Home | History | Annotate | Download | only in sockfs
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 /*
     27  * NL7C (Network Layer 7 Cache) as part of SOCKFS provides an in-kernel
     28  * gateway cache for the request/response message based L7 protocol HTTP
     29  * (Hypertext Transfer Protocol, see HTTP/1.1 RFC2616) in a semantically
     30  * transparent manner.
     31  *
     32  * Neither the requesting user agent (client, e.g. web browser) nor the
     33  * origin server (e.g. webserver) that provided the response cached by
     34  * NL7C are impacted in any way.
     35  *
     36  * Note, currently NL7C only processes HTTP messages via the embedded
     37  * URI of scheme http (not https nor any other), additional scheme are
     38  * intended to be supported as is practical such that much of the NL7C
     39  * framework may appear more general purpose then would be needed just
     40  * for an HTTP gateway cache.
     41  *
     42  * NL7C replaces NCA (Network Cache and Accelerator) and in the future
     43  * NCAS (NCA/SSL).
     44  *
     45  * Further, NL7C uses all NCA configuration files, see "/etc/nca/", the
     46  * NCA socket API, "AF_NCA", and "ndd /dev/nca" for backwards compatibility.
     47  */
     48 
     49 #include <sys/systm.h>
     50 #include <sys/strsun.h>
     51 #include <sys/strsubr.h>
     52 #include <inet/common.h>
     53 #include <inet/led.h>
     54 #include <inet/mi.h>
     55 #include <netinet/in.h>
     56 #include <fs/sockfs/nl7c.h>
     57 #include <fs/sockfs/nl7curi.h>
     58 #include <fs/sockfs/socktpi.h>
     59 
     60 #include <inet/nca/ncadoorhdr.h>
     61 #include <inet/nca/ncalogd.h>
     62 #include <inet/nca/ncandd.h>
     63 
     64 #include <sys/promif.h>
     65 
     66 /*
     67  * NL7C, NCA, NL7C logger enabled:
     68  */
     69 
     70 boolean_t	nl7c_enabled = B_FALSE;
     71 
     72 boolean_t	nl7c_logd_enabled = B_FALSE;
     73 boolean_t	nl7c_logd_started = B_FALSE;
     74 boolean_t	nl7c_logd_cycle = B_TRUE;
     75 
     76 /*
     77  * Some externs:
     78  */
     79 
     80 extern int	inet_pton(int, char *, void *);
     81 
     82 extern void	nl7c_uri_init(void);
     83 extern boolean_t nl7c_logd_init(int, caddr_t *);
     84 extern void	nl7c_nca_init(void);
     85 
     86 /*
     87  * nl7c_addr_t - a singly linked grounded list, pointed to by *nl7caddrs,
     88  * constructed at init time by parsing "/etc/nca/ncaport.conf".
     89  *
     90  * This list is searched at bind(3SOCKET) time when an application doesn't
     91  * explicitly set AF_NCA but instead uses AF_INET, if a match is found then
     92  * the underlying socket is marked sti_nl7c_flags NL7C_ENABLED.
     93  */
     94 
     95 typedef struct nl7c_addr_s {
     96 	struct nl7c_addr_s *next;	/* next entry */
     97 	sa_family_t	family;		/* addr type, only INET and INET6 */
     98 	uint16_t	port;		/* port */
     99 	union {
    100 		ipaddr_t	v4;	/* IPv4 address */
    101 		in6_addr_t	v6;	/* IPv6 address */
    102 		void		*align;	/* foce alignment */
    103 	}		addr;		/* address */
    104 
    105 	struct sonode	*listener;	/* listen()er's sonode */
    106 	boolean_t	temp;		/* temporary addr via add_addr() ? */
    107 } nl7c_addr_t;
    108 
    109 nl7c_addr_t	*nl7caddrs = NULL;
    110 
    111 /*
    112  * Called for an NL7C_ENABLED listen()er socket for the nl7c_addr_t
    113  * previously returned by nl7c_lookup_addr().
    114  */
    115 
    116 void
    117 nl7c_listener_addr(void *arg, struct sonode *so)
    118 {
    119 	nl7c_addr_t		*p = (nl7c_addr_t *)arg;
    120 
    121 	if (p->listener == NULL)
    122 		p->listener = so;
    123 	SOTOTPI(so)->sti_nl7c_addr = arg;
    124 }
    125 
    126 struct sonode *
    127 nl7c_addr2portso(void *arg)
    128 {
    129 	nl7c_addr_t		*p = (nl7c_addr_t *)arg;
    130 
    131 	return (p->listener);
    132 }
    133 
    134 void *
    135 nl7c_lookup_addr(void *addr, t_uscalar_t addrlen)
    136 {
    137 	struct sockaddr		*sap = addr;
    138 	struct sockaddr_in	*v4p = addr;
    139 	nl7c_addr_t		*p = nl7caddrs;
    140 
    141 	if (sap->sa_family != AF_INET || addrlen != sizeof (*v4p)) {
    142 		/* Only support IPv4 */
    143 		return (B_FALSE);
    144 	}
    145 	while (p) {
    146 		if (sap->sa_family == p->family &&
    147 		    v4p->sin_port == p->port &&
    148 		    (v4p->sin_addr.s_addr == p->addr.v4 ||
    149 		    p->addr.v4 == INADDR_ANY)) {
    150 			/* Match */
    151 			return (p);
    152 		}
    153 		p = p->next;
    154 	}
    155 	return (NULL);
    156 }
    157 
    158 void *
    159 nl7c_add_addr(void *addr, t_uscalar_t addrlen)
    160 {
    161 	struct sockaddr		*sap = addr;
    162 	struct sockaddr_in	*v4p = addr;
    163 	nl7c_addr_t		*new = NULL;
    164 	nl7c_addr_t		*old;
    165 	nl7c_addr_t		*p;
    166 	boolean_t		alloced;
    167 
    168 	if (sap->sa_family != AF_INET || addrlen != sizeof (*v4p)) {
    169 		/* Only support IPv4 */
    170 		return (NULL);
    171 	}
    172 again:
    173 	p = nl7caddrs;
    174 	while (p) {
    175 		if (new == NULL && p->port == 0)
    176 			new = p;
    177 		if (sap->sa_family == p->family &&
    178 		    v4p->sin_port == p->port &&
    179 		    (v4p->sin_addr.s_addr == p->addr.v4 ||
    180 		    p->addr.v4 == INADDR_ANY)) {
    181 			/* Match */
    182 			return (p);
    183 		}
    184 		p = p->next;
    185 	}
    186 	if (new == NULL) {
    187 		new = kmem_zalloc(sizeof (*new), KM_SLEEP);
    188 		alloced = B_TRUE;
    189 	} else
    190 		alloced = B_FALSE;
    191 
    192 	new->family = sap->sa_family;
    193 	new->port = v4p->sin_port;
    194 	new->addr.v4 = v4p->sin_addr.s_addr;
    195 	new->temp = B_TRUE;
    196 
    197 	if (alloced) {
    198 		old = nl7caddrs;
    199 		new->next = old;
    200 		if (atomic_cas_ptr(&nl7caddrs, old, new) != old) {
    201 			kmem_free(new, sizeof (*new));
    202 			goto again;
    203 		}
    204 	}
    205 
    206 	return (new);
    207 }
    208 
    209 boolean_t
    210 nl7c_close_addr(struct sonode *so)
    211 {
    212 	nl7c_addr_t	*p = nl7caddrs;
    213 
    214 	while (p) {
    215 		if (p->listener == so) {
    216 			if (p->temp)
    217 				p->port = (uint16_t)-1;
    218 			p->listener = NULL;
    219 			return (B_TRUE);
    220 		}
    221 		p = p->next;
    222 	}
    223 	return (B_FALSE);
    224 }
    225 
    226 static void
    227 nl7c_addr_add(nl7c_addr_t *p)
    228 {
    229 	p->next = nl7caddrs;
    230 	nl7caddrs = p;
    231 }
    232 
    233 void
    234 nl7c_mi_report_addr(mblk_t *mp)
    235 {
    236 	ipaddr_t	ip;
    237 	uint16_t	port;
    238 	nl7c_addr_t	*p = nl7caddrs;
    239 	struct sonode	*so;
    240 	char		addr[32];
    241 
    242 	(void) mi_mpprintf(mp, "Door  Up-Call-Queue IPaddr:TCPport Listenning");
    243 	while (p) {
    244 		if (p->port != (uint16_t)-1) {
    245 			/* Don't report freed slots */
    246 			ip = ntohl(p->addr.v4);
    247 			port = ntohs(p->port);
    248 
    249 			if (ip == INADDR_ANY) {
    250 				(void) strcpy(addr, "*");
    251 			} else {
    252 				int a1 = (ip >> 24) & 0xFF;
    253 				int a2 = (ip >> 16) & 0xFF;
    254 				int a3 = (ip >> 8) & 0xFF;
    255 				int a4 = ip & 0xFF;
    256 
    257 				(void) mi_sprintf(addr, "%d.%d.%d.%d",
    258 				    a1, a2, a3, a4);
    259 			}
    260 			so = p->listener;
    261 			(void) mi_mpprintf(mp, "%p  %s:%d  %d",
    262 			    so ? (void *)strvp2wq(SOTOV(so)) : NULL,
    263 			    addr, port, p->listener ? 1 : 0);
    264 		}
    265 		p = p->next;
    266 	}
    267 }
    268 
    269 /*
    270  * ASCII to unsigned.
    271  *
    272  * Note, it's assumed that *p is a valid zero byte terminated string.
    273  */
    274 
    275 static unsigned
    276 atou(const char *p)
    277 {
    278 	int c;
    279 	int v = 0;
    280 
    281 	/* Shift and add digit by digit */
    282 	while ((c = *p++) != NULL && isdigit(c)) {
    283 		v *= 10;
    284 		v += c - '0';
    285 	}
    286 	return (v);
    287 }
    288 
    289 /*
    290  * strdup(), yet another strdup() in the kernel.
    291  */
    292 
    293 static char *
    294 strdup(char *s)
    295 {
    296 	int	len = strlen(s) + 1;
    297 	char	*ret = kmem_alloc(len, KM_SLEEP);
    298 
    299 	bcopy(s, ret, len);
    300 
    301 	return (ret);
    302 }
    303 
    304 /*
    305  * Inet ASCII to binary.
    306  *
    307  * Note, it's assumed that *s is a valid zero byte terminated string, and
    308  * that *p is a zero initialized struct (this is important as the value of
    309  * INADDR_ANY and IN6ADDR_ANY is zero).
    310  */
    311 
    312 static int
    313 inet_atob(char *s, nl7c_addr_t *p)
    314 {
    315 	if (strcmp(s, "*") == 0) {
    316 		/* INADDR_ANY */
    317 		p->family = AF_INET;
    318 		return (0);
    319 	}
    320 	if (strcmp(s, "::") == 0) {
    321 		/* IN6ADDR_ANY */
    322 		p->family = AF_INET6;
    323 		return (0);
    324 	}
    325 	/* IPv4 address ? */
    326 	if (inet_pton(AF_INET, s, &p->addr.v4) != 1) {
    327 		/* Nop, IPv6 address ? */
    328 		if (inet_pton(AF_INET6, s, &p->addr.v6) != 1) {
    329 			/* Nop, return error */
    330 			return (1);
    331 		}
    332 		p->family = AF_INET6;
    333 	} else {
    334 		p->family = AF_INET;
    335 		p->addr.v4 = ntohl(p->addr.v4);
    336 	}
    337 	return (0);
    338 }
    339 
    340 /*
    341  * Open and read each line from "/etc/nca/ncaport.conf", the syntax of a
    342  * ncaport.conf file line is:
    343  *
    344  *	ncaport=IPaddr/Port[/Proxy]
    345  *
    346  * Where:
    347  *
    348  * ncaport - the only token recognized.
    349  *
    350  *  IPaddr - an IPv4 numeric dot address (e.g. 192.168.84.71) or '*' for
    351  *           INADDR_ANY, or an IPv6 numeric address or "::" for IN6ADDR_ANY.
    352  *
    353  *       / - IPaddr/Port separator.
    354  *
    355  *    Port - a TCP decimal port number.
    356  *
    357  * Note, all other lines will be ignored.
    358  */
    359 
    360 static void
    361 ncaportconf_read(void)
    362 {
    363 	int	ret;
    364 	struct vnode *vp;
    365 	char	c;
    366 	ssize_t resid;
    367 	char	buf[1024];
    368 	char	*ebp = &buf[sizeof (buf)];
    369 	char	*bp = ebp;
    370 	offset_t off = 0;
    371 	enum parse_e {START, TOK, ADDR, PORT, EOL} parse = START;
    372 	nl7c_addr_t *addrp = NULL;
    373 	char	*ncaport = "ncaport";
    374 	char	string[] = "XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX";
    375 	char	*stringp;
    376 	char	*tok;
    377 	char	*portconf = "/etc/nca/ncaport.conf";
    378 
    379 	ret = vn_open(portconf, UIO_SYSSPACE, FREAD, 0, &vp, 0, 0);
    380 	if (ret == ENOENT) {
    381 		/* No portconf file, nothing to do */
    382 		return;
    383 	}
    384 	if (ret != 0) {
    385 		/* Error of some sort, tell'm about it */
    386 		cmn_err(CE_WARN, "%s: open error %d", portconf, ret);
    387 		return;
    388 	}
    389 	/*
    390 	 * Read portconf one buf[] at a time, parse one char at a time.
    391 	 */
    392 	for (;;) {
    393 		if (bp == ebp) {
    394 			/* Nothing left in buf[], read another */
    395 			ret = vn_rdwr(UIO_READ, vp, buf, sizeof (buf), off,
    396 			    UIO_SYSSPACE, 0, (rlim64_t)0, CRED(), &resid);
    397 			if (ret != 0) {
    398 				/* Error of some sort, tell'm about it */
    399 				cmn_err(CE_WARN, "%s: read error %d",
    400 				    portconf, ret);
    401 				break;
    402 			}
    403 			if (resid == sizeof (buf)) {
    404 				/* EOF, done */
    405 				break;
    406 			}
    407 			/* Initilize per buf[] state */
    408 			bp = buf;
    409 			ebp = &buf[sizeof (buf) - resid];
    410 			off += sizeof (buf) - resid;
    411 		}
    412 		c = *bp++;
    413 		switch (parse) {
    414 		case START:
    415 			/* Initilize all per file line state */
    416 			if (addrp == NULL) {
    417 				addrp = kmem_zalloc(sizeof (*addrp),
    418 				    KM_NOSLEEP);
    419 			}
    420 			tok = ncaport;
    421 			stringp = string;
    422 			parse = TOK;
    423 			/*FALLTHROUGH*/
    424 		case TOK:
    425 			if (c == '#') {
    426 				/* Comment through end of line */
    427 				parse = EOL;
    428 				break;
    429 			}
    430 			if (isalpha(c)) {
    431 				if (c != *tok++) {
    432 					/* Only know one token, skip */
    433 					parse = EOL;
    434 				}
    435 			} else if (c == '=') {
    436 				if (*tok != NULL) {
    437 					/* Only know one token, skip */
    438 					parse = EOL;
    439 					break;
    440 				}
    441 				parse = ADDR;
    442 			} else if (c == '\n') {
    443 				/* Found EOL, empty line, next line */
    444 				parse = START;
    445 			} else {
    446 				/* Unexpected char, skip */
    447 				parse = EOL;
    448 			}
    449 			break;
    450 
    451 		case ADDR:
    452 			if (c == '/') {
    453 				/* addr/port separator, end of addr */
    454 				*stringp = NULL;
    455 				if (inet_atob(string, addrp)) {
    456 					/* Bad addr, skip */
    457 					parse = EOL;
    458 				} else {
    459 					stringp = string;
    460 					parse = PORT;
    461 				}
    462 			} else {
    463 				/* Save char to string */
    464 				if (stringp ==
    465 				    &string[sizeof (string) - 1]) {
    466 					/* Would overflow, skip */
    467 					parse = EOL;
    468 				} else {
    469 					/* Copy IP addr char */
    470 					*stringp++ = c;
    471 				}
    472 			}
    473 			break;
    474 
    475 		case PORT:
    476 			if (isdigit(c)) {
    477 				/* Save char to string */
    478 				if (stringp ==
    479 				    &string[sizeof (string) - 1]) {
    480 					/* Would overflow, skip */
    481 					parse = EOL;
    482 				} else {
    483 					/* Copy port digit char */
    484 					*stringp++ = c;
    485 				}
    486 				break;
    487 			} else if (c == '#' || isspace(c)) {
    488 				/* End of port number, convert */
    489 				*stringp = NULL;
    490 				addrp->port = ntohs(atou(string));
    491 
    492 				/* End of parse, add entry */
    493 				nl7c_addr_add(addrp);
    494 				addrp = NULL;
    495 				parse = EOL;
    496 			} else {
    497 				/* Unrecognized char, skip */
    498 				parse = EOL;
    499 				break;
    500 			}
    501 			if (c == '\n') {
    502 				/* Found EOL, start on next line */
    503 				parse = START;
    504 			}
    505 			break;
    506 
    507 		case EOL:
    508 			if (c == '\n') {
    509 				/* Found EOL, start on next line */
    510 				parse = START;
    511 			}
    512 			break;
    513 		}
    514 
    515 	}
    516 	if (addrp != NULL) {
    517 		kmem_free(addrp, sizeof (*addrp));
    518 	}
    519 	(void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED(), NULL);
    520 	VN_RELE(vp);
    521 }
    522 
    523 /*
    524  * Open and read each line from "/etc/nca/ncakmod.conf" and parse looking
    525  * for the NCA enabled, the syntax is: status=enabled, all other lines will
    526  * be ignored.
    527  */
    528 
    529 static void
    530 ncakmodconf_read(void)
    531 {
    532 	int	ret;
    533 	struct vnode *vp;
    534 	char	c;
    535 	ssize_t resid;
    536 	char	buf[1024];
    537 	char	*ebp = &buf[sizeof (buf)];
    538 	char	*bp = ebp;
    539 	offset_t off = 0;
    540 	enum parse_e {START, TOK, EOL} parse = START;
    541 	char	*status = "status=enabled";
    542 	char	*tok;
    543 	char	*ncakmod = "/etc/nca/ncakmod.conf";
    544 
    545 	ret = vn_open(ncakmod, UIO_SYSSPACE, FREAD, 0, &vp, 0, 0);
    546 	if (ret == ENOENT) {
    547 		/* No ncakmod file, nothing to do */
    548 		return;
    549 	}
    550 	if (ret != 0) {
    551 		/* Error of some sort, tell'm about it */
    552 		cmn_err(CE_WARN, "%s: open error %d", status, ret);
    553 		return;
    554 	}
    555 	/*
    556 	 * Read ncakmod one buf[] at a time, parse one char at a time.
    557 	 */
    558 	for (;;) {
    559 		if (bp == ebp) {
    560 			/* Nothing left in buf[], read another */
    561 			ret = vn_rdwr(UIO_READ, vp, buf, sizeof (buf), off,
    562 			    UIO_SYSSPACE, 0, (rlim64_t)0, CRED(), &resid);
    563 			if (ret != 0) {
    564 				/* Error of some sort, tell'm about it */
    565 				cmn_err(CE_WARN, "%s: read error %d",
    566 				    status, ret);
    567 				break;
    568 			}
    569 			if (resid == sizeof (buf)) {
    570 				/* EOF, done */
    571 				break;
    572 			}
    573 			/* Initilize per buf[] state */
    574 			bp = buf;
    575 			ebp = &buf[sizeof (buf) - resid];
    576 			off += sizeof (buf) - resid;
    577 		}
    578 		c = *bp++;
    579 		switch (parse) {
    580 		case START:
    581 			/* Initilize all per file line state */
    582 			tok = status;
    583 			parse = TOK;
    584 			/*FALLTHROUGH*/
    585 		case TOK:
    586 			if (c == '#') {
    587 				/* Comment through end of line */
    588 				parse = EOL;
    589 				break;
    590 			}
    591 			if (isalpha(c) || c == '=') {
    592 				if (c != *tok++) {
    593 					/* Only know one token, skip */
    594 					parse = EOL;
    595 				}
    596 			} else if (c == '\n') {
    597 				/*
    598 				 * Found EOL, if tok found done,
    599 				 * else start on next-line.
    600 				 */
    601 				if (*tok == NULL) {
    602 					nl7c_enabled = B_TRUE;
    603 					goto done;
    604 				}
    605 				parse = START;
    606 			} else {
    607 				/* Unexpected char, skip */
    608 				parse = EOL;
    609 			}
    610 			break;
    611 
    612 		case EOL:
    613 			if (c == '\n') {
    614 				/* Found EOL, start on next line */
    615 				parse = START;
    616 			}
    617 			break;
    618 		}
    619 
    620 	}
    621 done:
    622 	(void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED(), NULL);
    623 	VN_RELE(vp);
    624 }
    625 
    626 /*
    627  * Open and read each line from "/etc/nca/ncalogd.conf" and parse for
    628  * the tokens and token text (i.e. key and value ncalogd.conf(4)):
    629  *
    630  *	status=enabled
    631  *
    632  *	logd_file_size=[0-9]+
    633  *
    634  *	logd_file_name=["]filename( filename)*["]
    635  */
    636 
    637 static int	file_size = 1000000;
    638 static caddr_t	fnv[NCA_FIOV_SZ];
    639 
    640 static void
    641 ncalogdconf_read(void)
    642 {
    643 	int	ret;
    644 	struct vnode *vp;
    645 	char	c;
    646 	int	sz;
    647 	ssize_t resid;
    648 	char	buf[1024];
    649 	char	*ebp = &buf[sizeof (buf)];
    650 	char	*bp = ebp;
    651 	offset_t off = 0;
    652 	enum parse_e {START, TOK, TEXT, EOL} parse = START;
    653 	char	*tokstatus = "status\0enabled";
    654 	char	*toksize = "logd_file_size";
    655 	char	*tokfile = "logd_path_name";
    656 	char	*tokstatusp;
    657 	char	*toksizep;
    658 	char	*tokfilep;
    659 	char	*tok;
    660 	int	tokdelim = 0;
    661 	char	*ncalogd = "/etc/nca/ncalogd.conf";
    662 	char	*ncadeflog = "/var/nca/log";
    663 	char	file[TYPICALMAXPATHLEN] = {0};
    664 	char	*fp = file;
    665 	caddr_t	*fnvp = fnv;
    666 
    667 	ret = vn_open(ncalogd, UIO_SYSSPACE, FREAD, 0, &vp, 0, 0);
    668 	if (ret == ENOENT) {
    669 		/* No ncalogd file, nothing to do */
    670 		return;
    671 	}
    672 	if (ret != 0) {
    673 		/* Error of some sort, tell'm about it */
    674 		cmn_err(CE_WARN, "ncalogdconf_read: %s: open error(%d).",
    675 		    ncalogd, ret);
    676 		return;
    677 	}
    678 	/*
    679 	 * Read ncalogd.conf one buf[] at a time, parse one char at a time.
    680 	 */
    681 	for (;;) {
    682 		if (bp == ebp) {
    683 			/* Nothing left in buf[], read another */
    684 			ret = vn_rdwr(UIO_READ, vp, buf, sizeof (buf), off,
    685 			    UIO_SYSSPACE, 0, (rlim64_t)0, CRED(), &resid);
    686 			if (ret != 0) {
    687 				/* Error of some sort, tell'm about it */
    688 				cmn_err(CE_WARN, "%s: read error %d",
    689 				    ncalogd, ret);
    690 				break;
    691 			}
    692 			if (resid == sizeof (buf)) {
    693 				/* EOF, done */
    694 				break;
    695 			}
    696 			/* Initilize per buf[] state */
    697 			bp = buf;
    698 			ebp = &buf[sizeof (buf) - resid];
    699 			off += sizeof (buf) - resid;
    700 		}
    701 		c = *bp++;
    702 		switch (parse) {
    703 		case START:
    704 			/* Initilize all per file line state */
    705 			tokstatusp = tokstatus;
    706 			toksizep = toksize;
    707 			tokfilep = tokfile;
    708 			tok = NULL;
    709 			parse = TOK;
    710 			sz = 0;
    711 			/*FALLTHROUGH*/
    712 		case TOK:
    713 			if (isalpha(c) || c == '_') {
    714 				/*
    715 				 * Found a valid tok char, if matches
    716 				 * any of the tokens continue else NULL
    717 				 * then string pointer.
    718 				 */
    719 				if (tokstatusp != NULL && c != *tokstatusp++)
    720 					tokstatusp = NULL;
    721 				if (toksizep != NULL && c != *toksizep++)
    722 					toksizep = NULL;
    723 				if (tokfilep != NULL && c != *tokfilep++)
    724 					tokfilep = NULL;
    725 
    726 				if (tokstatusp == NULL &&
    727 				    toksizep == NULL &&
    728 				    tokfilep == NULL) {
    729 					/*
    730 					 * All tok string pointers are NULL
    731 					 * so skip rest of line.
    732 					 */
    733 					parse = EOL;
    734 				}
    735 			} else if (c == '=') {
    736 				/*
    737 				 * Found tok separator, if tok found get
    738 				 * tok text, else skip rest of line.
    739 				 */
    740 				if (tokstatusp != NULL && *tokstatusp == NULL)
    741 					tok = tokstatus;
    742 				else if (toksizep != NULL && *toksizep == NULL)
    743 					tok = toksize;
    744 				else if (tokfilep != NULL && *tokfilep == NULL)
    745 					tok = tokfile;
    746 				if (tok != NULL)
    747 					parse = TEXT;
    748 				else
    749 					parse = EOL;
    750 			} else if (c == '\n') {
    751 				/* Found EOL, start on next line */
    752 				parse = START;
    753 			} else {
    754 				/* Comment or unknown char, skip rest of line */
    755 				parse = EOL;
    756 			}
    757 			break;
    758 		case TEXT:
    759 			if (c == '\n') {
    760 				/*
    761 				 * Found EOL, finish up tok text processing
    762 				 * (if any) and start on next line.
    763 				 */
    764 				if (tok == tokstatus) {
    765 					if (*++tokstatusp == NULL)
    766 						nl7c_logd_enabled = B_TRUE;
    767 				} else if (tok == toksize) {
    768 					file_size = sz;
    769 				} else if (tok == tokfile) {
    770 					if (tokdelim == 0) {
    771 						/* Non delimited path name */
    772 						*fnvp++ = strdup(file);
    773 					} else if (fp != file) {
    774 						/* No closing delimiter */
    775 						/*EMPTY*/;
    776 					}
    777 				}
    778 				parse = START;
    779 			} else if (tok == tokstatus) {
    780 				if (! isalpha(c) || *++tokstatusp == NULL ||
    781 				    c != *tokstatusp) {
    782 					/* Not enabled, skip line */
    783 					parse = EOL;
    784 				}
    785 			} else if (tok == toksize) {
    786 				if (isdigit(c)) {
    787 					sz *= 10;
    788 					sz += c - '0';
    789 				} else {
    790 					/* Not a decimal digit, skip line */
    791 					parse = EOL;
    792 				}
    793 			} else {
    794 				/* File name */
    795 				if (c == '"' && tokdelim++ == 0) {
    796 					/* Opening delimiter, skip */
    797 					/*EMPTY*/;
    798 				} else if (c == '"' || c == ' ') {
    799 					/* List delim or filename separator */
    800 					*fnvp++ = strdup(file);
    801 					fp = file;
    802 				} else if (fp < &file[sizeof (file) - 1]) {
    803 					/* Filename char */
    804 					*fp++ = c;
    805 				} else {
    806 					/* Filename to long, skip line */
    807 					parse = EOL;
    808 				}
    809 			}
    810 			break;
    811 
    812 		case EOL:
    813 			if (c == '\n') {
    814 				/* Found EOL, start on next line */
    815 				parse = START;
    816 			}
    817 			break;
    818 		}
    819 
    820 	}
    821 done:
    822 	(void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED(), NULL);
    823 	VN_RELE(vp);
    824 
    825 	if (nl7c_logd_enabled) {
    826 		if (fnvp == fnv) {
    827 			/*
    828 			 * No logfile was specified and found so
    829 			 * so use defualt NCA log file path.
    830 			 */
    831 			*fnvp++ = strdup(ncadeflog);
    832 		}
    833 		if (fnvp < &fnv[NCA_FIOV_SZ]) {
    834 			/* NULL terminate list */
    835 			*fnvp = NULL;
    836 		}
    837 	}
    838 }
    839 
    840 void
    841 nl7clogd_startup(void)
    842 {
    843 	static kmutex_t startup;
    844 
    845 	/*
    846 	 * Called on the first log() attempt, have to wait until then to
    847 	 * initialize logd as at logdconf_read() the root fs is read-only.
    848 	 */
    849 	mutex_enter(&startup);
    850 	if (nl7c_logd_started) {
    851 		/* Lost the race, nothing todo */
    852 		mutex_exit(&startup);
    853 		return;
    854 	}
    855 	nl7c_logd_started = B_TRUE;
    856 	if (! nl7c_logd_init(file_size, fnv)) {
    857 		/* Failure, disable logging */
    858 		nl7c_logd_enabled = B_FALSE;
    859 		cmn_err(CE_WARN, "nl7clogd_startup: failed, disabling loggin");
    860 		mutex_exit(&startup);
    861 		return;
    862 	}
    863 	mutex_exit(&startup);
    864 }
    865 
    866 
    867 void
    868 nl7c_startup()
    869 {
    870 	/*
    871 	 * Open, read, and parse the NCA logd configuration file,
    872 	 * then initialize URI processing and NCA compat.
    873 	 */
    874 	ncalogdconf_read();
    875 	nl7c_uri_init();
    876 	nl7c_nca_init();
    877 }
    878 
    879 void
    880 nl7c_init()
    881 {
    882 	/* Open, read, and parse the NCA kmod configuration file */
    883 	ncakmodconf_read();
    884 
    885 	if (nl7c_enabled) {
    886 		/*
    887 		 * NL7C is enabled so open, read, and parse
    888 		 * the NCA address/port configuration file
    889 		 * and call startup() to finish config/init.
    890 		 */
    891 		ncaportconf_read();
    892 		nl7c_startup();
    893 	}
    894 }
    895 
    896 /*
    897  * The main processing function called by accept() on a newly created
    898  * socket prior to returning it to the caller of accept().
    899  *
    900  * Here data is read from the socket until a completed L7 request parse
    901  * is completed. Data will be read in the context of the user thread
    902  * which called accept(), when parse has been completed either B_TRUE
    903  * or B_FALSE will be returned.
    904  *
    905  * If NL7C successfully process the L7 protocol request, i.e. generates
    906  * a response, B_TRUE will be returned.
    907  *
    908  * Else, B_FALSE will be returned if NL7C can't process the request:
    909  *
    910  * 1) Couldn't locate a URI within the request.
    911  *
    912  * 2) URI scheme not reqcognized.
    913  *
    914  * 3) A request which can't be processed.
    915  *
    916  * 4) A request which could be processed but NL7C dosen't currently have
    917  *    the response data. In which case NL7C will parse the returned response
    918  *    from the application for possible caching for subsequent request(s).
    919  */
    920 
    921 volatile uint64_t nl7c_proc_cnt = 0;
    922 volatile uint64_t nl7c_proc_error = 0;
    923 volatile uint64_t nl7c_proc_ETIME = 0;
    924 volatile uint64_t nl7c_proc_again = 0;
    925 volatile uint64_t nl7c_proc_next = 0;
    926 volatile uint64_t nl7c_proc_rcv = 0;
    927 volatile uint64_t nl7c_proc_noLRI = 0;
    928 volatile uint64_t nl7c_proc_nodata = 0;
    929 volatile uint64_t nl7c_proc_parse = 0;
    930 
    931 boolean_t
    932 nl7c_process(struct sonode *so, boolean_t nonblocking)
    933 {
    934 	vnode_t	*vp = SOTOV(so);
    935 	sotpi_info_t *sti = SOTOTPI(so);
    936 	mblk_t	*rmp = sti->sti_nl7c_rcv_mp;
    937 	clock_t	timout;
    938 	rval_t	rval;
    939 	uchar_t pri;
    940 	int 	pflag;
    941 	int	error;
    942 	boolean_t more;
    943 	boolean_t ret = B_FALSE;
    944 	boolean_t first = B_TRUE;
    945 	boolean_t pollin = (sti->sti_nl7c_flags & NL7C_POLLIN);
    946 
    947 	nl7c_proc_cnt++;
    948 
    949 	/* Caller has so_lock enter()ed */
    950 	error = so_lock_read_intr(so, nonblocking ? FNDELAY|FNONBLOCK : 0);
    951 	if (error) {
    952 		/* Couldn't read lock, pass on this socket */
    953 		sti->sti_nl7c_flags = 0;
    954 		nl7c_proc_noLRI++;
    955 		return (B_FALSE);
    956 	}
    957 	/* Exit so_lock for now, will be reenter()ed prior to return */
    958 	mutex_exit(&so->so_lock);
    959 
    960 	if (pollin)
    961 		sti->sti_nl7c_flags &= ~NL7C_POLLIN;
    962 
    963 	/* Initialize some kstrgetmsg() constants */
    964 	pflag = MSG_ANY | MSG_DELAYERROR;
    965 	pri = 0;
    966 	if (nonblocking) {
    967 		/* Non blocking so don't block */
    968 		timout = 0;
    969 	} else if (sti->sti_nl7c_flags & NL7C_SOPERSIST) {
    970 		/* 2nd or more time(s) here so use keep-alive value */
    971 		timout = nca_http_keep_alive_timeout;
    972 	} else {
    973 		/* 1st time here so use connection value */
    974 		timout = nca_http_timeout;
    975 	}
    976 
    977 	rval.r_vals = 0;
    978 	do {
    979 		/*
    980 		 * First time through, if no data left over from a previous
    981 		 * kstrgetmsg() then try to get some, else just process it.
    982 		 *
    983 		 * Thereafter, rmp = NULL after the successful kstrgetmsg()
    984 		 * so try to get some new data and append to list (i.e. until
    985 		 * enough fragments are collected for a successful parse).
    986 		 */
    987 		if (rmp == NULL) {
    988 
    989 			error = kstrgetmsg(vp, &rmp, NULL, &pri, &pflag,
    990 			    timout, &rval);
    991 			if (error) {
    992 				if (error == ETIME) {
    993 					/* Timeout */
    994 					nl7c_proc_ETIME++;
    995 				} else if (error != EWOULDBLOCK) {
    996 					/* Error of some sort */
    997 					nl7c_proc_error++;
    998 					rval.r_v.r_v2 = error;
    999 					sti->sti_nl7c_flags = 0;
   1000 					break;
   1001 				}
   1002 				error = 0;
   1003 			}
   1004 			if (rmp != NULL) {
   1005 				mblk_t	*mp = sti->sti_nl7c_rcv_mp;
   1006 
   1007 
   1008 				if (mp == NULL) {
   1009 					/* Just new data, common case */
   1010 					sti->sti_nl7c_rcv_mp = rmp;
   1011 				} else {
   1012 					/* Add new data to tail */
   1013 					while (mp->b_cont != NULL)
   1014 						mp = mp->b_cont;
   1015 					mp->b_cont = rmp;
   1016 				}
   1017 			}
   1018 			if (sti->sti_nl7c_rcv_mp == NULL) {
   1019 				/* No data */
   1020 				nl7c_proc_nodata++;
   1021 				if (timout > 0 || (first && pollin)) {
   1022 					/* Expected data so EOF */
   1023 					ret = B_TRUE;
   1024 				} else if (sti->sti_nl7c_flags &
   1025 				    NL7C_SOPERSIST) {
   1026 					/* Persistent so just checking */
   1027 					ret = B_FALSE;
   1028 				}
   1029 				break;
   1030 			}
   1031 			rmp = NULL;
   1032 		}
   1033 		first = B_FALSE;
   1034 	again:
   1035 		nl7c_proc_parse++;
   1036 
   1037 		more = nl7c_parse(so, nonblocking, &ret);
   1038 
   1039 		if (ret == B_TRUE && (sti->sti_nl7c_flags & NL7C_SOPERSIST)) {
   1040 			/*
   1041 			 * Parse complete, cache hit, response on its way,
   1042 			 * socket is persistent so try to process the next
   1043 			 * request.
   1044 			 */
   1045 			if (nonblocking) {
   1046 				ret = B_FALSE;
   1047 				break;
   1048 			}
   1049 			if (sti->sti_nl7c_rcv_mp) {
   1050 				/* More recv-side data, pipelined */
   1051 				nl7c_proc_again++;
   1052 				goto again;
   1053 			}
   1054 			nl7c_proc_next++;
   1055 			if (nonblocking)
   1056 				timout = 0;
   1057 			else
   1058 				timout = nca_http_keep_alive_timeout;
   1059 
   1060 			more = B_TRUE;
   1061 		}
   1062 
   1063 	} while (more);
   1064 
   1065 	if (sti->sti_nl7c_rcv_mp) {
   1066 		nl7c_proc_rcv++;
   1067 	}
   1068 	sti->sti_nl7c_rcv_rval = rval.r_vals;
   1069 	/* Renter so_lock, caller called with it enter()ed */
   1070 	mutex_enter(&so->so_lock);
   1071 	so_unlock_read(so);
   1072 
   1073 	return (ret);
   1074 }
   1075