xref: /titanic_52/usr/src/uts/common/fs/sockfs/nl7c.c (revision 4a5d661a82b942b6538acd26209d959ce98b593a)
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 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
26  */
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/ip.h>
56 #include <inet/led.h>
57 #include <inet/mi.h>
58 #include <netinet/in.h>
59 #include <fs/sockfs/nl7c.h>
60 #include <fs/sockfs/nl7curi.h>
61 #include <fs/sockfs/socktpi.h>
62 
63 #include <inet/nca/ncadoorhdr.h>
64 #include <inet/nca/ncalogd.h>
65 #include <inet/nca/ncandd.h>
66 
67 #include <sys/promif.h>
68 
69 /*
70  * NL7C, NCA, NL7C logger enabled:
71  */
72 
73 boolean_t	nl7c_enabled = B_FALSE;
74 
75 boolean_t	nl7c_logd_enabled = B_FALSE;
76 boolean_t	nl7c_logd_started = B_FALSE;
77 boolean_t	nl7c_logd_cycle = B_TRUE;
78 
79 /*
80  * Some externs:
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  * Inet ASCII to binary.
291  *
292  * Note, it's assumed that *s is a valid zero byte terminated string, and
293  * that *p is a zero initialized struct (this is important as the value of
294  * INADDR_ANY and IN6ADDR_ANY is zero).
295  */
296 
297 static int
298 inet_atob(char *s, nl7c_addr_t *p)
299 {
300 	if (strcmp(s, "*") == 0) {
301 		/* INADDR_ANY */
302 		p->family = AF_INET;
303 		return (0);
304 	}
305 	if (strcmp(s, "::") == 0) {
306 		/* IN6ADDR_ANY */
307 		p->family = AF_INET6;
308 		return (0);
309 	}
310 	/* IPv4 address ? */
311 	if (inet_pton(AF_INET, s, &p->addr.v4) != 1) {
312 		/* Nop, IPv6 address ? */
313 		if (inet_pton(AF_INET6, s, &p->addr.v6) != 1) {
314 			/* Nop, return error */
315 			return (1);
316 		}
317 		p->family = AF_INET6;
318 	} else {
319 		p->family = AF_INET;
320 	}
321 	return (0);
322 }
323 
324 /*
325  * Open and read each line from "/etc/nca/ncaport.conf", the syntax of a
326  * ncaport.conf file line is:
327  *
328  *	ncaport=IPaddr/Port[/Proxy]
329  *
330  * Where:
331  *
332  * ncaport - the only token recognized.
333  *
334  *  IPaddr - an IPv4 numeric dot address (e.g. 192.168.84.71) or '*' for
335  *           INADDR_ANY, or an IPv6 numeric address or "::" for IN6ADDR_ANY.
336  *
337  *       / - IPaddr/Port separator.
338  *
339  *    Port - a TCP decimal port number.
340  *
341  * Note, all other lines will be ignored.
342  */
343 
344 static void
345 ncaportconf_read(void)
346 {
347 	int	ret;
348 	struct vnode *vp;
349 	char	c;
350 	ssize_t resid;
351 	char	buf[1024];
352 	char	*ebp = &buf[sizeof (buf)];
353 	char	*bp = ebp;
354 	offset_t off = 0;
355 	enum parse_e {START, TOK, ADDR, PORT, EOL} parse = START;
356 	nl7c_addr_t *addrp = NULL;
357 	char	*ncaport = "ncaport";
358 	char	string[] = "XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX";
359 	char	*stringp;
360 	char	*tok;
361 	char	*portconf = "/etc/nca/ncaport.conf";
362 
363 	ret = vn_open(portconf, UIO_SYSSPACE, FREAD, 0, &vp, 0, 0);
364 	if (ret == ENOENT) {
365 		/* No portconf file, nothing to do */
366 		return;
367 	}
368 	if (ret != 0) {
369 		/* Error of some sort, tell'm about it */
370 		cmn_err(CE_WARN, "%s: open error %d", portconf, ret);
371 		return;
372 	}
373 	/*
374 	 * Read portconf one buf[] at a time, parse one char at a time.
375 	 */
376 	for (;;) {
377 		if (bp == ebp) {
378 			/* Nothing left in buf[], read another */
379 			ret = vn_rdwr(UIO_READ, vp, buf, sizeof (buf), off,
380 			    UIO_SYSSPACE, 0, (rlim64_t)0, CRED(), &resid);
381 			if (ret != 0) {
382 				/* Error of some sort, tell'm about it */
383 				cmn_err(CE_WARN, "%s: read error %d",
384 				    portconf, ret);
385 				break;
386 			}
387 			if (resid == sizeof (buf)) {
388 				/* EOF, done */
389 				break;
390 			}
391 			/* Initilize per buf[] state */
392 			bp = buf;
393 			ebp = &buf[sizeof (buf) - resid];
394 			off += sizeof (buf) - resid;
395 		}
396 		c = *bp++;
397 		switch (parse) {
398 		case START:
399 			/* Initilize all per file line state */
400 			if (addrp == NULL) {
401 				addrp = kmem_zalloc(sizeof (*addrp),
402 				    KM_NOSLEEP);
403 			}
404 			tok = ncaport;
405 			stringp = string;
406 			parse = TOK;
407 			/*FALLTHROUGH*/
408 		case TOK:
409 			if (c == '#') {
410 				/* Comment through end of line */
411 				parse = EOL;
412 				break;
413 			}
414 			if (isalpha(c)) {
415 				if (c != *tok++) {
416 					/* Only know one token, skip */
417 					parse = EOL;
418 				}
419 			} else if (c == '=') {
420 				if (*tok != NULL) {
421 					/* Only know one token, skip */
422 					parse = EOL;
423 					break;
424 				}
425 				parse = ADDR;
426 			} else if (c == '\n') {
427 				/* Found EOL, empty line, next line */
428 				parse = START;
429 			} else {
430 				/* Unexpected char, skip */
431 				parse = EOL;
432 			}
433 			break;
434 
435 		case ADDR:
436 			if (c == '/') {
437 				/* addr/port separator, end of addr */
438 				*stringp = NULL;
439 				if (inet_atob(string, addrp)) {
440 					/* Bad addr, skip */
441 					parse = EOL;
442 				} else {
443 					stringp = string;
444 					parse = PORT;
445 				}
446 			} else {
447 				/* Save char to string */
448 				if (stringp ==
449 				    &string[sizeof (string) - 1]) {
450 					/* Would overflow, skip */
451 					parse = EOL;
452 				} else {
453 					/* Copy IP addr char */
454 					*stringp++ = c;
455 				}
456 			}
457 			break;
458 
459 		case PORT:
460 			if (isdigit(c)) {
461 				/* Save char to string */
462 				if (stringp ==
463 				    &string[sizeof (string) - 1]) {
464 					/* Would overflow, skip */
465 					parse = EOL;
466 				} else {
467 					/* Copy port digit char */
468 					*stringp++ = c;
469 				}
470 				break;
471 			} else if (c == '#' || isspace(c)) {
472 				/* End of port number, convert */
473 				*stringp = NULL;
474 				addrp->port = ntohs(atou(string));
475 
476 				/* End of parse, add entry */
477 				nl7c_addr_add(addrp);
478 				addrp = NULL;
479 				parse = EOL;
480 			} else {
481 				/* Unrecognized char, skip */
482 				parse = EOL;
483 				break;
484 			}
485 			if (c == '\n') {
486 				/* Found EOL, start on next line */
487 				parse = START;
488 			}
489 			break;
490 
491 		case EOL:
492 			if (c == '\n') {
493 				/* Found EOL, start on next line */
494 				parse = START;
495 			}
496 			break;
497 		}
498 
499 	}
500 	if (addrp != NULL) {
501 		kmem_free(addrp, sizeof (*addrp));
502 	}
503 	(void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED(), NULL);
504 	VN_RELE(vp);
505 }
506 
507 /*
508  * Open and read each line from "/etc/nca/ncakmod.conf" and parse looking
509  * for the NCA enabled, the syntax is: status=enabled, all other lines will
510  * be ignored.
511  */
512 
513 static void
514 ncakmodconf_read(void)
515 {
516 	int	ret;
517 	struct vnode *vp;
518 	char	c;
519 	ssize_t resid;
520 	char	buf[1024];
521 	char	*ebp = &buf[sizeof (buf)];
522 	char	*bp = ebp;
523 	offset_t off = 0;
524 	enum parse_e {START, TOK, EOL} parse = START;
525 	char	*status = "status=enabled";
526 	char	*tok;
527 	char	*ncakmod = "/etc/nca/ncakmod.conf";
528 
529 	ret = vn_open(ncakmod, UIO_SYSSPACE, FREAD, 0, &vp, 0, 0);
530 	if (ret == ENOENT) {
531 		/* No ncakmod file, nothing to do */
532 		return;
533 	}
534 	if (ret != 0) {
535 		/* Error of some sort, tell'm about it */
536 		cmn_err(CE_WARN, "%s: open error %d", status, ret);
537 		return;
538 	}
539 	/*
540 	 * Read ncakmod one buf[] at a time, parse one char at a time.
541 	 */
542 	for (;;) {
543 		if (bp == ebp) {
544 			/* Nothing left in buf[], read another */
545 			ret = vn_rdwr(UIO_READ, vp, buf, sizeof (buf), off,
546 			    UIO_SYSSPACE, 0, (rlim64_t)0, CRED(), &resid);
547 			if (ret != 0) {
548 				/* Error of some sort, tell'm about it */
549 				cmn_err(CE_WARN, "%s: read error %d",
550 				    status, ret);
551 				break;
552 			}
553 			if (resid == sizeof (buf)) {
554 				/* EOF, done */
555 				break;
556 			}
557 			/* Initilize per buf[] state */
558 			bp = buf;
559 			ebp = &buf[sizeof (buf) - resid];
560 			off += sizeof (buf) - resid;
561 		}
562 		c = *bp++;
563 		switch (parse) {
564 		case START:
565 			/* Initilize all per file line state */
566 			tok = status;
567 			parse = TOK;
568 			/*FALLTHROUGH*/
569 		case TOK:
570 			if (c == '#') {
571 				/* Comment through end of line */
572 				parse = EOL;
573 				break;
574 			}
575 			if (isalpha(c) || c == '=') {
576 				if (c != *tok++) {
577 					/* Only know one token, skip */
578 					parse = EOL;
579 				}
580 			} else if (c == '\n') {
581 				/*
582 				 * Found EOL, if tok found done,
583 				 * else start on next-line.
584 				 */
585 				if (*tok == NULL) {
586 					nl7c_enabled = B_TRUE;
587 					goto done;
588 				}
589 				parse = START;
590 			} else {
591 				/* Unexpected char, skip */
592 				parse = EOL;
593 			}
594 			break;
595 
596 		case EOL:
597 			if (c == '\n') {
598 				/* Found EOL, start on next line */
599 				parse = START;
600 			}
601 			break;
602 		}
603 
604 	}
605 done:
606 	(void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED(), NULL);
607 	VN_RELE(vp);
608 }
609 
610 /*
611  * Open and read each line from "/etc/nca/ncalogd.conf" and parse for
612  * the tokens and token text (i.e. key and value ncalogd.conf(4)):
613  *
614  *	status=enabled
615  *
616  *	logd_file_size=[0-9]+
617  *
618  *	logd_file_name=["]filename( filename)*["]
619  */
620 
621 static int	file_size = 1000000;
622 static caddr_t	fnv[NCA_FIOV_SZ];
623 
624 static void
625 ncalogdconf_read(void)
626 {
627 	int	ret;
628 	struct vnode *vp;
629 	char	c;
630 	int	sz;
631 	ssize_t resid;
632 	char	buf[1024];
633 	char	*ebp = &buf[sizeof (buf)];
634 	char	*bp = ebp;
635 	offset_t off = 0;
636 	enum parse_e {START, TOK, TEXT, EOL} parse = START;
637 	char	*tokstatus = "status\0enabled";
638 	char	*toksize = "logd_file_size";
639 	char	*tokfile = "logd_path_name";
640 	char	*tokstatusp;
641 	char	*toksizep;
642 	char	*tokfilep;
643 	char	*tok;
644 	int	tokdelim = 0;
645 	char	*ncalogd = "/etc/nca/ncalogd.conf";
646 	char	*ncadeflog = "/var/nca/log";
647 	char	file[TYPICALMAXPATHLEN] = {0};
648 	char	*fp = file;
649 	caddr_t	*fnvp = fnv;
650 
651 	ret = vn_open(ncalogd, UIO_SYSSPACE, FREAD, 0, &vp, 0, 0);
652 	if (ret == ENOENT) {
653 		/* No ncalogd file, nothing to do */
654 		return;
655 	}
656 	if (ret != 0) {
657 		/* Error of some sort, tell'm about it */
658 		cmn_err(CE_WARN, "ncalogdconf_read: %s: open error(%d).",
659 		    ncalogd, ret);
660 		return;
661 	}
662 	/*
663 	 * Read ncalogd.conf one buf[] at a time, parse one char at a time.
664 	 */
665 	for (;;) {
666 		if (bp == ebp) {
667 			/* Nothing left in buf[], read another */
668 			ret = vn_rdwr(UIO_READ, vp, buf, sizeof (buf), off,
669 			    UIO_SYSSPACE, 0, (rlim64_t)0, CRED(), &resid);
670 			if (ret != 0) {
671 				/* Error of some sort, tell'm about it */
672 				cmn_err(CE_WARN, "%s: read error %d",
673 				    ncalogd, ret);
674 				break;
675 			}
676 			if (resid == sizeof (buf)) {
677 				/* EOF, done */
678 				break;
679 			}
680 			/* Initilize per buf[] state */
681 			bp = buf;
682 			ebp = &buf[sizeof (buf) - resid];
683 			off += sizeof (buf) - resid;
684 		}
685 		c = *bp++;
686 		switch (parse) {
687 		case START:
688 			/* Initilize all per file line state */
689 			tokstatusp = tokstatus;
690 			toksizep = toksize;
691 			tokfilep = tokfile;
692 			tok = NULL;
693 			parse = TOK;
694 			sz = 0;
695 			/*FALLTHROUGH*/
696 		case TOK:
697 			if (isalpha(c) || c == '_') {
698 				/*
699 				 * Found a valid tok char, if matches
700 				 * any of the tokens continue else NULL
701 				 * then string pointer.
702 				 */
703 				if (tokstatusp != NULL && c != *tokstatusp++)
704 					tokstatusp = NULL;
705 				if (toksizep != NULL && c != *toksizep++)
706 					toksizep = NULL;
707 				if (tokfilep != NULL && c != *tokfilep++)
708 					tokfilep = NULL;
709 
710 				if (tokstatusp == NULL &&
711 				    toksizep == NULL &&
712 				    tokfilep == NULL) {
713 					/*
714 					 * All tok string pointers are NULL
715 					 * so skip rest of line.
716 					 */
717 					parse = EOL;
718 				}
719 			} else if (c == '=') {
720 				/*
721 				 * Found tok separator, if tok found get
722 				 * tok text, else skip rest of line.
723 				 */
724 				if (tokstatusp != NULL && *tokstatusp == NULL)
725 					tok = tokstatus;
726 				else if (toksizep != NULL && *toksizep == NULL)
727 					tok = toksize;
728 				else if (tokfilep != NULL && *tokfilep == NULL)
729 					tok = tokfile;
730 				if (tok != NULL)
731 					parse = TEXT;
732 				else
733 					parse = EOL;
734 			} else if (c == '\n') {
735 				/* Found EOL, start on next line */
736 				parse = START;
737 			} else {
738 				/* Comment or unknown char, skip rest of line */
739 				parse = EOL;
740 			}
741 			break;
742 		case TEXT:
743 			if (c == '\n') {
744 				/*
745 				 * Found EOL, finish up tok text processing
746 				 * (if any) and start on next line.
747 				 */
748 				if (tok == tokstatus) {
749 					if (*++tokstatusp == NULL)
750 						nl7c_logd_enabled = B_TRUE;
751 				} else if (tok == toksize) {
752 					file_size = sz;
753 				} else if (tok == tokfile) {
754 					if (tokdelim == 0) {
755 						/* Non delimited path name */
756 						*fnvp++ = strdup(file);
757 					} else if (fp != file) {
758 						/* No closing delimiter */
759 						/*EMPTY*/;
760 					}
761 				}
762 				parse = START;
763 			} else if (tok == tokstatus) {
764 				if (! isalpha(c) || *++tokstatusp == NULL ||
765 				    c != *tokstatusp) {
766 					/* Not enabled, skip line */
767 					parse = EOL;
768 				}
769 			} else if (tok == toksize) {
770 				if (isdigit(c)) {
771 					sz *= 10;
772 					sz += c - '0';
773 				} else {
774 					/* Not a decimal digit, skip line */
775 					parse = EOL;
776 				}
777 			} else {
778 				/* File name */
779 				if (c == '"' && tokdelim++ == 0) {
780 					/* Opening delimiter, skip */
781 					/*EMPTY*/;
782 				} else if (c == '"' || c == ' ') {
783 					/* List delim or filename separator */
784 					*fnvp++ = strdup(file);
785 					fp = file;
786 				} else if (fp < &file[sizeof (file) - 1]) {
787 					/* Filename char */
788 					*fp++ = c;
789 				} else {
790 					/* Filename to long, skip line */
791 					parse = EOL;
792 				}
793 			}
794 			break;
795 
796 		case EOL:
797 			if (c == '\n') {
798 				/* Found EOL, start on next line */
799 				parse = START;
800 			}
801 			break;
802 		}
803 
804 	}
805 done:
806 	(void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED(), NULL);
807 	VN_RELE(vp);
808 
809 	if (nl7c_logd_enabled) {
810 		if (fnvp == fnv) {
811 			/*
812 			 * No logfile was specified and found so
813 			 * so use defualt NCA log file path.
814 			 */
815 			*fnvp++ = strdup(ncadeflog);
816 		}
817 		if (fnvp < &fnv[NCA_FIOV_SZ]) {
818 			/* NULL terminate list */
819 			*fnvp = NULL;
820 		}
821 	}
822 }
823 
824 void
825 nl7clogd_startup(void)
826 {
827 	static kmutex_t startup;
828 
829 	/*
830 	 * Called on the first log() attempt, have to wait until then to
831 	 * initialize logd as at logdconf_read() the root fs is read-only.
832 	 */
833 	mutex_enter(&startup);
834 	if (nl7c_logd_started) {
835 		/* Lost the race, nothing todo */
836 		mutex_exit(&startup);
837 		return;
838 	}
839 	nl7c_logd_started = B_TRUE;
840 	if (! nl7c_logd_init(file_size, fnv)) {
841 		/* Failure, disable logging */
842 		nl7c_logd_enabled = B_FALSE;
843 		cmn_err(CE_WARN, "nl7clogd_startup: failed, disabling loggin");
844 		mutex_exit(&startup);
845 		return;
846 	}
847 	mutex_exit(&startup);
848 }
849 
850 
851 void
852 nl7c_startup()
853 {
854 	/*
855 	 * Open, read, and parse the NCA logd configuration file,
856 	 * then initialize URI processing and NCA compat.
857 	 */
858 	ncalogdconf_read();
859 	nl7c_uri_init();
860 	nl7c_nca_init();
861 }
862 
863 void
864 nl7c_init()
865 {
866 	/* Open, read, and parse the NCA kmod configuration file */
867 	ncakmodconf_read();
868 
869 	if (nl7c_enabled) {
870 		/*
871 		 * NL7C is enabled so open, read, and parse
872 		 * the NCA address/port configuration file
873 		 * and call startup() to finish config/init.
874 		 */
875 		ncaportconf_read();
876 		nl7c_startup();
877 	}
878 }
879 
880 /*
881  * The main processing function called by accept() on a newly created
882  * socket prior to returning it to the caller of accept().
883  *
884  * Here data is read from the socket until a completed L7 request parse
885  * is completed. Data will be read in the context of the user thread
886  * which called accept(), when parse has been completed either B_TRUE
887  * or B_FALSE will be returned.
888  *
889  * If NL7C successfully process the L7 protocol request, i.e. generates
890  * a response, B_TRUE will be returned.
891  *
892  * Else, B_FALSE will be returned if NL7C can't process the request:
893  *
894  * 1) Couldn't locate a URI within the request.
895  *
896  * 2) URI scheme not reqcognized.
897  *
898  * 3) A request which can't be processed.
899  *
900  * 4) A request which could be processed but NL7C dosen't currently have
901  *    the response data. In which case NL7C will parse the returned response
902  *    from the application for possible caching for subsequent request(s).
903  */
904 
905 volatile uint64_t nl7c_proc_cnt = 0;
906 volatile uint64_t nl7c_proc_error = 0;
907 volatile uint64_t nl7c_proc_ETIME = 0;
908 volatile uint64_t nl7c_proc_again = 0;
909 volatile uint64_t nl7c_proc_next = 0;
910 volatile uint64_t nl7c_proc_rcv = 0;
911 volatile uint64_t nl7c_proc_noLRI = 0;
912 volatile uint64_t nl7c_proc_nodata = 0;
913 volatile uint64_t nl7c_proc_parse = 0;
914 
915 boolean_t
916 nl7c_process(struct sonode *so, boolean_t nonblocking)
917 {
918 	vnode_t	*vp = SOTOV(so);
919 	sotpi_info_t *sti = SOTOTPI(so);
920 	mblk_t	*rmp = sti->sti_nl7c_rcv_mp;
921 	clock_t	timout;
922 	rval_t	rval;
923 	uchar_t pri;
924 	int 	pflag;
925 	int	error;
926 	boolean_t more;
927 	boolean_t ret = B_FALSE;
928 	boolean_t first = B_TRUE;
929 	boolean_t pollin = (sti->sti_nl7c_flags & NL7C_POLLIN);
930 
931 	nl7c_proc_cnt++;
932 
933 	/* Caller has so_lock enter()ed */
934 	error = so_lock_read_intr(so, nonblocking ? FNDELAY|FNONBLOCK : 0);
935 	if (error) {
936 		/* Couldn't read lock, pass on this socket */
937 		sti->sti_nl7c_flags = 0;
938 		nl7c_proc_noLRI++;
939 		return (B_FALSE);
940 	}
941 	/* Exit so_lock for now, will be reenter()ed prior to return */
942 	mutex_exit(&so->so_lock);
943 
944 	if (pollin)
945 		sti->sti_nl7c_flags &= ~NL7C_POLLIN;
946 
947 	/* Initialize some kstrgetmsg() constants */
948 	pflag = MSG_ANY | MSG_DELAYERROR;
949 	pri = 0;
950 	if (nonblocking) {
951 		/* Non blocking so don't block */
952 		timout = 0;
953 	} else if (sti->sti_nl7c_flags & NL7C_SOPERSIST) {
954 		/* 2nd or more time(s) here so use keep-alive value */
955 		timout = nca_http_keep_alive_timeout;
956 	} else {
957 		/* 1st time here so use connection value */
958 		timout = nca_http_timeout;
959 	}
960 
961 	rval.r_vals = 0;
962 	do {
963 		/*
964 		 * First time through, if no data left over from a previous
965 		 * kstrgetmsg() then try to get some, else just process it.
966 		 *
967 		 * Thereafter, rmp = NULL after the successful kstrgetmsg()
968 		 * so try to get some new data and append to list (i.e. until
969 		 * enough fragments are collected for a successful parse).
970 		 */
971 		if (rmp == NULL) {
972 
973 			error = kstrgetmsg(vp, &rmp, NULL, &pri, &pflag,
974 			    timout, &rval);
975 			if (error) {
976 				if (error == ETIME) {
977 					/* Timeout */
978 					nl7c_proc_ETIME++;
979 				} else if (error != EWOULDBLOCK) {
980 					/* Error of some sort */
981 					nl7c_proc_error++;
982 					rval.r_v.r_v2 = error;
983 					sti->sti_nl7c_flags = 0;
984 					break;
985 				}
986 				error = 0;
987 			}
988 			if (rmp != NULL) {
989 				mblk_t	*mp = sti->sti_nl7c_rcv_mp;
990 
991 
992 				if (mp == NULL) {
993 					/* Just new data, common case */
994 					sti->sti_nl7c_rcv_mp = rmp;
995 				} else {
996 					/* Add new data to tail */
997 					while (mp->b_cont != NULL)
998 						mp = mp->b_cont;
999 					mp->b_cont = rmp;
1000 				}
1001 			}
1002 			if (sti->sti_nl7c_rcv_mp == NULL) {
1003 				/* No data */
1004 				nl7c_proc_nodata++;
1005 				if (timout > 0 || (first && pollin)) {
1006 					/* Expected data so EOF */
1007 					ret = B_TRUE;
1008 				} else if (sti->sti_nl7c_flags &
1009 				    NL7C_SOPERSIST) {
1010 					/* Persistent so just checking */
1011 					ret = B_FALSE;
1012 				}
1013 				break;
1014 			}
1015 			rmp = NULL;
1016 		}
1017 		first = B_FALSE;
1018 	again:
1019 		nl7c_proc_parse++;
1020 
1021 		more = nl7c_parse(so, nonblocking, &ret);
1022 
1023 		if (ret == B_TRUE && (sti->sti_nl7c_flags & NL7C_SOPERSIST)) {
1024 			/*
1025 			 * Parse complete, cache hit, response on its way,
1026 			 * socket is persistent so try to process the next
1027 			 * request.
1028 			 */
1029 			if (nonblocking) {
1030 				ret = B_FALSE;
1031 				break;
1032 			}
1033 			if (sti->sti_nl7c_rcv_mp) {
1034 				/* More recv-side data, pipelined */
1035 				nl7c_proc_again++;
1036 				goto again;
1037 			}
1038 			nl7c_proc_next++;
1039 			if (nonblocking)
1040 				timout = 0;
1041 			else
1042 				timout = nca_http_keep_alive_timeout;
1043 
1044 			more = B_TRUE;
1045 		}
1046 
1047 	} while (more);
1048 
1049 	if (sti->sti_nl7c_rcv_mp) {
1050 		nl7c_proc_rcv++;
1051 	}
1052 	sti->sti_nl7c_rcv_rval = rval.r_vals;
1053 	/* Renter so_lock, caller called with it enter()ed */
1054 	mutex_enter(&so->so_lock);
1055 	so_unlock_read(so);
1056 
1057 	return (ret);
1058 }
1059