xref: /titanic_44/usr/src/uts/common/fs/sockfs/nl7c.c (revision 5c51f1241dbbdf2656d0e10011981411ed0c9673)
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