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 * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
27 */
28
29 /*
30 * NL7C (Network Layer 7 Cache) as part of SOCKFS provides an in-kernel
31 * gateway cache for the request/response message based L7 protocol HTTP
32 * (Hypertext Transfer Protocol, see HTTP/1.1 RFC2616) in a semantically
33 * transparent manner.
34 *
35 * Neither the requesting user agent (client, e.g. web browser) nor the
36 * origin server (e.g. webserver) that provided the response cached by
37 * NL7C are impacted in any way.
38 *
39 * Note, currently NL7C only processes HTTP messages via the embedded
40 * URI of scheme http (not https nor any other), additional scheme are
41 * intended to be supported as is practical such that much of the NL7C
42 * framework may appear more general purpose then would be needed just
43 * for an HTTP gateway cache.
44 *
45 * NL7C replaces NCA (Network Cache and Accelerator) and in the future
46 * NCAS (NCA/SSL).
47 *
48 * Further, NL7C uses all NCA configuration files, see "/etc/nca/", the
49 * NCA socket API, "AF_NCA", and "ndd /dev/nca" for backwards compatibility.
50 */
51
52 #include <sys/systm.h>
53 #include <sys/strsun.h>
54 #include <sys/strsubr.h>
55 #include <inet/common.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 #include <inet/ip.h>
67
68 #include <sys/promif.h>
69
70 /*
71 * NL7C, NCA, NL7C logger enabled:
72 */
73
74 boolean_t nl7c_enabled = B_FALSE;
75
76 boolean_t nl7c_logd_enabled = B_FALSE;
77 boolean_t nl7c_logd_started = B_FALSE;
78 boolean_t nl7c_logd_cycle = B_TRUE;
79
80 /*
81 * Some externs:
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 sti_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 SOTOTPI(so)->sti_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 }
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 sotpi_info_t *sti = SOTOTPI(so);
937 mblk_t *rmp = sti->sti_nl7c_rcv_mp;
938 clock_t timout;
939 rval_t rval;
940 uchar_t pri;
941 int pflag;
942 int error;
943 boolean_t more;
944 boolean_t ret = B_FALSE;
945 boolean_t first = B_TRUE;
946 boolean_t pollin = (sti->sti_nl7c_flags & NL7C_POLLIN);
947
948 nl7c_proc_cnt++;
949
950 /* Caller has so_lock enter()ed */
951 error = so_lock_read_intr(so, nonblocking ? FNDELAY|FNONBLOCK : 0);
952 if (error) {
953 /* Couldn't read lock, pass on this socket */
954 sti->sti_nl7c_flags = 0;
955 nl7c_proc_noLRI++;
956 return (B_FALSE);
957 }
958 /* Exit so_lock for now, will be reenter()ed prior to return */
959 mutex_exit(&so->so_lock);
960
961 if (pollin)
962 sti->sti_nl7c_flags &= ~NL7C_POLLIN;
963
964 /* Initialize some kstrgetmsg() constants */
965 pflag = MSG_ANY | MSG_DELAYERROR;
966 pri = 0;
967 if (nonblocking) {
968 /* Non blocking so don't block */
969 timout = 0;
970 } else if (sti->sti_nl7c_flags & NL7C_SOPERSIST) {
971 /* 2nd or more time(s) here so use keep-alive value */
972 timout = nca_http_keep_alive_timeout;
973 } else {
974 /* 1st time here so use connection value */
975 timout = nca_http_timeout;
976 }
977
978 rval.r_vals = 0;
979 do {
980 /*
981 * First time through, if no data left over from a previous
982 * kstrgetmsg() then try to get some, else just process it.
983 *
984 * Thereafter, rmp = NULL after the successful kstrgetmsg()
985 * so try to get some new data and append to list (i.e. until
986 * enough fragments are collected for a successful parse).
987 */
988 if (rmp == NULL) {
989
990 error = kstrgetmsg(vp, &rmp, NULL, &pri, &pflag,
991 timout, &rval);
992 if (error) {
993 if (error == ETIME) {
994 /* Timeout */
995 nl7c_proc_ETIME++;
996 } else if (error != EWOULDBLOCK) {
997 /* Error of some sort */
998 nl7c_proc_error++;
999 rval.r_v.r_v2 = error;
1000 sti->sti_nl7c_flags = 0;
1001 break;
1002 }
1003 error = 0;
1004 }
1005 if (rmp != NULL) {
1006 mblk_t *mp = sti->sti_nl7c_rcv_mp;
1007
1008
1009 if (mp == NULL) {
1010 /* Just new data, common case */
1011 sti->sti_nl7c_rcv_mp = rmp;
1012 } else {
1013 /* Add new data to tail */
1014 while (mp->b_cont != NULL)
1015 mp = mp->b_cont;
1016 mp->b_cont = rmp;
1017 }
1018 }
1019 if (sti->sti_nl7c_rcv_mp == NULL) {
1020 /* No data */
1021 nl7c_proc_nodata++;
1022 if (timout > 0 || (first && pollin)) {
1023 /* Expected data so EOF */
1024 ret = B_TRUE;
1025 } else if (sti->sti_nl7c_flags &
1026 NL7C_SOPERSIST) {
1027 /* Persistent so just checking */
1028 ret = B_FALSE;
1029 }
1030 break;
1031 }
1032 rmp = NULL;
1033 }
1034 first = B_FALSE;
1035 again:
1036 nl7c_proc_parse++;
1037
1038 more = nl7c_parse(so, nonblocking, &ret);
1039
1040 if (ret == B_TRUE && (sti->sti_nl7c_flags & NL7C_SOPERSIST)) {
1041 /*
1042 * Parse complete, cache hit, response on its way,
1043 * socket is persistent so try to process the next
1044 * request.
1045 */
1046 if (nonblocking) {
1047 ret = B_FALSE;
1048 break;
1049 }
1050 if (sti->sti_nl7c_rcv_mp) {
1051 /* More recv-side data, pipelined */
1052 nl7c_proc_again++;
1053 goto again;
1054 }
1055 nl7c_proc_next++;
1056 if (nonblocking)
1057 timout = 0;
1058 else
1059 timout = nca_http_keep_alive_timeout;
1060
1061 more = B_TRUE;
1062 }
1063
1064 } while (more);
1065
1066 if (sti->sti_nl7c_rcv_mp) {
1067 nl7c_proc_rcv++;
1068 }
1069 sti->sti_nl7c_rcv_rval = rval.r_vals;
1070 /* Renter so_lock, caller called with it enter()ed */
1071 mutex_enter(&so->so_lock);
1072 so_unlock_read(so);
1073
1074 return (ret);
1075 }