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