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