In the final code block of in_control_internal, remove ia from the hash table
[dragonfly.git] / contrib / ipfilter / parse.c
blob0d8a617d4aa330d5de9043740df74607a8132d20
1 /*
2 * Copyright (C) 1993-2001 by Darren Reed.
4 * See the IPFILTER.LICENCE file for details on licencing.
5 */
6 #if defined(__sgi) && (IRIX > 602)
7 # include <sys/ptimers.h>
8 #endif
9 #include <sys/types.h>
10 #if !defined(__SVR4) && !defined(__svr4__)
11 #include <strings.h>
12 #else
13 #include <sys/byteorder.h>
14 #endif
15 #include <sys/param.h>
16 #include <sys/time.h>
17 #include <sys/socket.h>
18 #include <netinet/in.h>
19 #include <netinet/in_systm.h>
20 #include <netinet/ip.h>
21 #include <netinet/tcp.h>
22 #include <net/if.h>
23 #if __FreeBSD_version >= 300000
24 # include <net/if_var.h>
25 #endif
26 #include <stdio.h>
27 #include <string.h>
28 #include <limits.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <stddef.h>
32 #include <netdb.h>
33 #include <arpa/nameser.h>
34 #include <arpa/inet.h>
35 #include <resolv.h>
36 #include <ctype.h>
37 #include <syslog.h>
38 #include "ip_compat.h"
39 #include "ip_fil.h"
40 #include "ipf.h"
41 #include "facpri.h"
43 #if !defined(lint)
44 static const char sccsid[] = "@(#)parse.c 1.44 6/5/96 (C) 1993-2000 Darren Reed";
45 static const char rcsid[] = "@(#)$IPFilter: parse.c,v 2.8 1999/12/28 10:49:46 darrenr Exp $";
46 #endif
48 extern struct ipopt_names ionames[], secclass[];
49 extern int opts;
50 extern int use_inet6;
52 int addicmp __P((char ***, struct frentry *, int));
53 int extras __P((char ***, struct frentry *, int));
55 int icmpcode __P((char *)), addkeep __P((char ***, struct frentry *, int));
56 int to_interface __P((frdest_t *, char *, int));
57 void print_toif __P((char *, frdest_t *));
58 void optprint __P((u_short *, u_long, u_long));
59 int loglevel __P((char **, u_int *, int));
60 void printlog __P((frentry_t *));
61 void printifname __P((char *, char *, void *));
63 extern char *proto;
64 extern char flagset[];
65 extern u_char flags[];
68 /* parse()
70 * parse a line read from the input filter rule file
72 * status:
73 * < 0 error
74 * = 0 OK
75 * > 0 programmer error
77 struct frentry *parse(line, linenum, status)
78 char *line;
79 int linenum;
80 int *status; /* good, bad, or indifferent */
82 static struct frentry fil;
83 char *cps[31], **cpp, *endptr, *s;
84 struct protoent *p = NULL;
85 int i, cnt = 1, j, ch;
86 u_int k;
88 *status = 100; /* default to error */
90 while (*line && isspace(*line))
91 line++;
92 if (!*line) {
93 *status = 0;
94 return NULL;
97 bzero((char *)&fil, sizeof(fil));
98 fil.fr_mip.fi_v = 0xf;
99 fil.fr_ip.fi_v = use_inet6 ? 6 : 4;
100 fil.fr_loglevel = 0xffff;
103 * break line up into max of 20 segments
105 if (opts & OPT_DEBUG)
106 fprintf(stderr, "parse [%s]\n", line);
107 for (i = 0, *cps = strtok(line, " \b\t\r\n"); cps[i] && i < 30; cnt++)
108 cps[++i] = strtok(NULL, " \b\t\r\n");
109 cps[i] = NULL;
111 if (cnt < 3) {
112 fprintf(stderr, "%d: not enough segments in line\n", linenum);
113 *status = -1;
114 return NULL;
117 cpp = cps;
119 * The presence of an '@' followed by a number gives the position in
120 * the current rule list to insert this one.
122 if (**cpp == '@')
123 fil.fr_hits = (U_QUAD_T)atoi(*cpp++ + 1) + 1;
127 * Check the first keyword in the rule and any options that are
128 * expected to follow it.
130 if (!strcasecmp("block", *cpp)) {
131 fil.fr_flags |= FR_BLOCK;
132 if (!strncasecmp(*(cpp+1), "return-icmp-as-dest", 19) &&
133 (i = 19))
134 fil.fr_flags |= FR_FAKEICMP;
135 else if (!strncasecmp(*(cpp+1), "return-icmp", 11) && (i = 11))
136 fil.fr_flags |= FR_RETICMP;
137 if (fil.fr_flags & FR_RETICMP) {
138 cpp++;
139 if (strlen(*cpp) == i) {
140 if (*(cpp + 1) && **(cpp +1) == '(') {
141 cpp++;
142 i = 0;
143 } else
144 i = -1;
148 * The ICMP code is not required to follow in ()'s
150 if ((i >= 0) && (*(*cpp + i) == '(')) {
151 i++;
152 j = icmpcode(*cpp + i);
153 if (j == -1) {
154 fprintf(stderr,
155 "%d: unrecognised icmp code %s\n",
156 linenum, *cpp + 20);
157 *status = -1;
158 return NULL;
160 fil.fr_icode = j;
162 } else if (!strcasecmp(*(cpp+1), "return-rst")) {
163 fil.fr_flags |= FR_RETRST;
164 cpp++;
166 } else if (!strcasecmp("count", *cpp)) {
167 fil.fr_flags |= FR_ACCOUNT;
168 } else if (!strcasecmp("pass", *cpp)) {
169 fil.fr_flags |= FR_PASS;
170 } else if (!strcasecmp("nomatch", *cpp)) {
171 fil.fr_flags |= FR_NOMATCH;
172 } else if (!strcasecmp("auth", *cpp)) {
173 fil.fr_flags |= FR_AUTH;
174 if (!strncasecmp(*(cpp+1), "return-rst", 10)) {
175 fil.fr_flags |= FR_RETRST;
176 cpp++;
178 } else if (!strcasecmp("preauth", *cpp)) {
179 fil.fr_flags |= FR_PREAUTH;
180 } else if (!strcasecmp("skip", *cpp)) {
181 cpp++;
182 if (ratoui(*cpp, &k, 0, UINT_MAX))
183 fil.fr_skip = k;
184 else {
185 fprintf(stderr, "%d: integer must follow skip\n",
186 linenum);
187 *status = -1;
188 return NULL;
190 } else if (!strcasecmp("log", *cpp)) {
191 fil.fr_flags |= FR_LOG;
192 if (!strcasecmp(*(cpp+1), "body")) {
193 fil.fr_flags |= FR_LOGBODY;
194 cpp++;
196 if (!strcasecmp(*(cpp+1), "first")) {
197 fil.fr_flags |= FR_LOGFIRST;
198 cpp++;
200 if (*cpp && !strcasecmp(*(cpp+1), "or-block")) {
201 fil.fr_flags |= FR_LOGORBLOCK;
202 cpp++;
204 if (!strcasecmp(*(cpp+1), "level")) {
205 cpp++;
206 if (loglevel(cpp, &fil.fr_loglevel, linenum) == -1) {
207 /* NB loglevel prints its own error message */
208 *status = -1;
209 return NULL;
211 cpp++;
213 } else {
215 * Doesn't start with one of the action words
217 fprintf(stderr, "%d: unknown keyword (%s)\n", linenum, *cpp);
218 *status = -1;
219 return NULL;
221 if (!*++cpp) {
222 fprintf(stderr, "%d: missing 'in'/'out' keyword\n", linenum);
223 *status = -1;
224 return NULL;
228 * Get the direction for filtering. Impose restrictions on direction
229 * if blocking with returning ICMP or an RST has been requested.
231 if (!strcasecmp("in", *cpp))
232 fil.fr_flags |= FR_INQUE;
233 else if (!strcasecmp("out", *cpp)) {
234 fil.fr_flags |= FR_OUTQUE;
235 if (fil.fr_flags & FR_RETICMP) {
236 fprintf(stderr,
237 "%d: Can only use return-icmp with 'in'\n",
238 linenum);
239 *status = -1;
240 return NULL;
241 } else if (fil.fr_flags & FR_RETRST) {
242 fprintf(stderr,
243 "%d: Can only use return-rst with 'in'\n",
244 linenum);
245 *status = -1;
246 return NULL;
249 if (!*++cpp) {
250 fprintf(stderr, "%d: missing source specification\n", linenum);
251 *status = -1;
252 return NULL;
255 if (!strcasecmp("log", *cpp)) {
256 if (!*++cpp) {
257 fprintf(stderr, "%d: missing source specification\n",
258 linenum);
259 *status = -1;
260 return NULL;
262 if (fil.fr_flags & FR_PASS)
263 fil.fr_flags |= FR_LOGP;
264 else if (fil.fr_flags & FR_BLOCK)
265 fil.fr_flags |= FR_LOGB;
266 if (*cpp && !strcasecmp(*cpp, "body")) {
267 fil.fr_flags |= FR_LOGBODY;
268 cpp++;
270 if (*cpp && !strcasecmp(*cpp, "first")) {
271 fil.fr_flags |= FR_LOGFIRST;
272 cpp++;
274 if (*cpp && !strcasecmp(*cpp, "or-block")) {
275 if (!(fil.fr_flags & FR_PASS)) {
276 fprintf(stderr,
277 "%d: or-block must be used with pass\n",
278 linenum);
279 *status = -1;
280 return NULL;
282 fil.fr_flags |= FR_LOGORBLOCK;
283 cpp++;
285 if (*cpp && !strcasecmp(*cpp, "level")) {
286 if (loglevel(cpp, &fil.fr_loglevel, linenum) == -1) {
287 *status = -1;
288 return NULL;
290 cpp++;
291 cpp++;
295 if (*cpp && !strcasecmp("quick", *cpp)) {
296 if (fil.fr_skip != 0) {
297 fprintf(stderr, "%d: cannot use skip with quick\n",
298 linenum);
299 *status = -1;
300 return NULL;
302 cpp++;
303 fil.fr_flags |= FR_QUICK;
307 * Parse rule options that are available if a rule is tied to an
308 * interface.
310 *fil.fr_ifname = '\0';
311 *fil.fr_oifname = '\0';
312 if (*cpp && !strcasecmp(*cpp, "on")) {
313 if (!*++cpp) {
314 fprintf(stderr, "%d: interface name missing\n",
315 linenum);
316 *status = -1;
317 return NULL;
320 s = index(*cpp, ',');
321 if (s != NULL) {
322 *s++ = '\0';
323 (void)strncpy(fil.fr_ifnames[1], s, IFNAMSIZ - 1);
324 fil.fr_ifnames[1][IFNAMSIZ - 1] = '\0';
325 } else
326 strcpy(fil.fr_ifnames[1], "*");
328 (void)strncpy(fil.fr_ifnames[0], *cpp, IFNAMSIZ - 1);
329 fil.fr_ifnames[0][IFNAMSIZ - 1] = '\0';
331 cpp++;
332 if (!*cpp) {
333 if ((fil.fr_flags & FR_RETMASK) == FR_RETRST) {
334 fprintf(stderr,
335 "%d: %s can only be used with TCP\n",
336 linenum, "return-rst");
337 *status = -1;
338 return NULL;
340 *status = 0;
341 return &fil;
344 if (*cpp) {
345 if (!strcasecmp(*cpp, "dup-to") && *(cpp + 1)) {
346 cpp++;
347 if (to_interface(&fil.fr_dif, *cpp, linenum)) {
348 *status = -1;
349 return NULL;
351 cpp++;
353 if (*cpp && !strcasecmp(*cpp, "to") && *(cpp + 1)) {
354 cpp++;
355 if (to_interface(&fil.fr_tif, *cpp, linenum)) {
356 *status = -1;
357 return NULL;
359 cpp++;
360 } else if (*cpp && !strcasecmp(*cpp, "fastroute")) {
361 if (!(fil.fr_flags & FR_INQUE)) {
362 fprintf(stderr,
363 "can only use %s with 'in'\n",
364 "fastroute");
365 *status = -1;
366 return NULL;
368 fil.fr_flags |= FR_FASTROUTE;
369 cpp++;
374 * Set the "other" interface name. Lets you specify both
375 * inbound and outbound interfaces for state rules. Do not
376 * prevent both interfaces from being the same.
378 strcpy(fil.fr_ifnames[3], "*");
379 if ((*cpp != NULL) && (*(cpp + 1) != NULL) &&
380 ((((fil.fr_flags & FR_INQUE) != 0) &&
381 (strcasecmp(*cpp, "out-via") == 0)) ||
382 (((fil.fr_flags & FR_OUTQUE) != 0) &&
383 (strcasecmp(*cpp, "in-via") == 0)))) {
384 cpp++;
386 s = index(*cpp, ',');
387 if (s != NULL) {
388 *s++ = '\0';
389 (void)strncpy(fil.fr_ifnames[3], s,
390 IFNAMSIZ - 1);
391 fil.fr_ifnames[3][IFNAMSIZ - 1] = '\0';
394 (void)strncpy(fil.fr_ifnames[2], *cpp, IFNAMSIZ - 1);
395 fil.fr_ifnames[2][IFNAMSIZ - 1] = '\0';
396 cpp++;
397 } else
398 strcpy(fil.fr_ifnames[2], "*");
400 if (*cpp && !strcasecmp(*cpp, "tos")) {
401 if (!*++cpp) {
402 fprintf(stderr, "%d: tos missing value\n", linenum);
403 *status = -1;
404 return NULL;
406 fil.fr_tos = strtol(*cpp, NULL, 0);
407 fil.fr_mip.fi_tos = 0xff;
408 cpp++;
411 if (*cpp && !strcasecmp(*cpp, "ttl")) {
412 if (!*++cpp) {
413 fprintf(stderr, "%d: ttl missing hopcount value\n",
414 linenum);
415 *status = -1;
416 return NULL;
418 if (ratoi(*cpp, &i, 0, 255))
419 fil.fr_ttl = i;
420 else {
421 fprintf(stderr, "%d: invalid ttl (%s)\n",
422 linenum, *cpp);
423 *status = -1;
424 return NULL;
426 fil.fr_mip.fi_ttl = 0xff;
427 cpp++;
431 * check for "proto <protoname>" only decode udp/tcp/icmp as protoname
433 proto = NULL;
434 if (*cpp && !strcasecmp(*cpp, "proto")) {
435 if (!*++cpp) {
436 fprintf(stderr, "%d: protocol name missing\n", linenum);
437 *status = -1;
438 return NULL;
440 proto = *cpp++;
441 if (!strcasecmp(proto, "tcp/udp")) {
442 fil.fr_ip.fi_fl |= FI_TCPUDP;
443 fil.fr_mip.fi_fl |= FI_TCPUDP;
444 } else if (use_inet6 && !strcasecmp(proto, "icmp")) {
445 fprintf(stderr,
446 "%d: use proto ipv6-icmp with IPv6 (or use proto 1 if you really mean icmp)\n",
447 linenum);
448 } else {
449 if (!(p = getprotobyname(proto)) && !isdigit(*proto)) {
450 fprintf(stderr,
451 "%d: unknown protocol (%s)\n",
452 linenum, proto);
453 *status = -1;
454 return NULL;
456 if (p)
457 fil.fr_proto = p->p_proto;
458 else if (isdigit(*proto)) {
459 i = (int)strtol(proto, &endptr, 0);
460 if (*endptr != '\0' || i < 0 || i > 255) {
461 fprintf(stderr,
462 "%d: unknown protocol (%s)\n",
463 linenum, proto);
464 *status = -1;
465 return NULL;
467 fil.fr_proto = i;
469 fil.fr_mip.fi_p = 0xff;
472 if ((fil.fr_proto != IPPROTO_TCP) &&
473 ((fil.fr_flags & FR_RETMASK) == FR_RETRST)) {
474 fprintf(stderr, "%d: %s can only be used with TCP\n",
475 linenum, "return-rst");
476 *status = -1;
477 return NULL;
481 * get the from host and bit mask to use against packets
484 if (!*cpp) {
485 fprintf(stderr, "%d: missing source specification\n", linenum);
486 *status = -1;
487 return NULL;
489 if (!strcasecmp(*cpp, "all")) {
490 cpp++;
491 if (!*cpp) {
492 *status = 0;
493 return &fil;
495 } else {
496 if (strcasecmp(*cpp, "from")) {
497 fprintf(stderr, "%d: unexpected keyword (%s) - from\n",
498 linenum, *cpp);
499 *status = -1;
500 return NULL;
502 if (!*++cpp) {
503 fprintf(stderr, "%d: missing host after from\n",
504 linenum);
505 *status = -1;
506 return NULL;
508 if (!strcmp(*cpp, "!")) {
509 fil.fr_flags |= FR_NOTSRCIP;
510 if (!*++cpp) {
511 fprintf(stderr,
512 "%d: missing host after from\n",
513 linenum);
514 *status = -1;
515 return NULL;
517 } else if (**cpp == '!') {
518 fil.fr_flags |= FR_NOTSRCIP;
519 (*cpp)++;
521 ch = 0;
522 if (hostmask(&cpp, (u_32_t *)&fil.fr_src,
523 (u_32_t *)&fil.fr_smsk, &fil.fr_sport, &ch,
524 &fil.fr_stop, linenum)) {
525 *status = -1;
526 return NULL;
529 if ((ch != 0) && (fil.fr_proto != IPPROTO_TCP) &&
530 (fil.fr_proto != IPPROTO_UDP) &&
531 !(fil.fr_ip.fi_fl & FI_TCPUDP)) {
532 fprintf(stderr,
533 "%d: cannot use port and neither tcp or udp\n",
534 linenum);
535 *status = -1;
536 return NULL;
539 fil.fr_scmp = ch;
540 if (!*cpp) {
541 fprintf(stderr, "%d: missing to fields\n", linenum);
542 *status = -1;
543 return NULL;
547 * do the same for the to field (destination host)
549 if (strcasecmp(*cpp, "to")) {
550 fprintf(stderr, "%d: unexpected keyword (%s) - to\n",
551 linenum, *cpp);
552 *status = -1;
553 return NULL;
555 if (!*++cpp) {
556 fprintf(stderr, "%d: missing host after to\n", linenum);
557 *status = -1;
558 return NULL;
560 ch = 0;
561 if (!strcmp(*cpp, "!")) {
562 fil.fr_flags |= FR_NOTDSTIP;
563 if (!*++cpp) {
564 fprintf(stderr,
565 "%d: missing host after from\n",
566 linenum);
567 *status = -1;
568 return NULL;
570 } else if (**cpp == '!') {
571 fil.fr_flags |= FR_NOTDSTIP;
572 (*cpp)++;
574 if (hostmask(&cpp, (u_32_t *)&fil.fr_dst,
575 (u_32_t *)&fil.fr_dmsk, &fil.fr_dport, &ch,
576 &fil.fr_dtop, linenum)) {
577 *status = -1;
578 return NULL;
580 if ((ch != 0) && (fil.fr_proto != IPPROTO_TCP) &&
581 (fil.fr_proto != IPPROTO_UDP) &&
582 !(fil.fr_ip.fi_fl & FI_TCPUDP)) {
583 fprintf(stderr,
584 "%d: cannot use port and neither tcp or udp\n",
585 linenum);
586 *status = -1;
587 return NULL;
590 fil.fr_dcmp = ch;
594 * check some sanity, make sure we don't have icmp checks with tcp
595 * or udp or visa versa.
597 if (fil.fr_proto && (fil.fr_dcmp || fil.fr_scmp) &&
598 fil.fr_proto != IPPROTO_TCP && fil.fr_proto != IPPROTO_UDP) {
599 fprintf(stderr, "%d: port operation on non tcp/udp\n", linenum);
600 *status = -1;
601 return NULL;
603 if (fil.fr_icmp && fil.fr_proto != IPPROTO_ICMP) {
604 fprintf(stderr, "%d: icmp comparisons on wrong protocol\n",
605 linenum);
606 *status = -1;
607 return NULL;
610 if (!*cpp) {
611 *status = 0;
612 return &fil;
615 if (*cpp && !strcasecmp(*cpp, "flags")) {
616 if (!*++cpp) {
617 fprintf(stderr, "%d: no flags present\n", linenum);
618 *status = -1;
619 return NULL;
621 fil.fr_tcpf = tcp_flags(*cpp, &fil.fr_tcpfm, linenum);
622 cpp++;
626 * extras...
628 if ((fil.fr_v == 4) && *cpp && (!strcasecmp(*cpp, "with") ||
629 !strcasecmp(*cpp, "and")))
630 if (extras(&cpp, &fil, linenum)) {
631 *status = -1;
632 return NULL;
636 * icmp types for use with the icmp protocol
638 if (*cpp && !strcasecmp(*cpp, "icmp-type")) {
639 if (fil.fr_proto != IPPROTO_ICMP &&
640 fil.fr_proto != IPPROTO_ICMPV6) {
641 fprintf(stderr,
642 "%d: icmp with wrong protocol (%d)\n",
643 linenum, fil.fr_proto);
644 *status = -1;
645 return NULL;
647 if (addicmp(&cpp, &fil, linenum)) {
648 *status = -1;
649 return NULL;
651 fil.fr_icmp = htons(fil.fr_icmp);
652 fil.fr_icmpm = htons(fil.fr_icmpm);
656 * Keep something...
658 while (*cpp && !strcasecmp(*cpp, "keep"))
659 if (addkeep(&cpp, &fil, linenum)) {
660 *status = -1;
661 return NULL;
665 * This is here to enforce the old interface binding behaviour.
666 * That is, "on X" is equivalent to "<dir> on X <!dir>-via -,X"
668 if (fil.fr_flags & FR_KEEPSTATE) {
669 if (*fil.fr_ifnames[0] && !*fil.fr_ifnames[3]) {
670 bcopy(fil.fr_ifnames[0], fil.fr_ifnames[3],
671 sizeof(fil.fr_ifnames[3]));
672 strncpy(fil.fr_ifnames[2], "*",
673 sizeof(fil.fr_ifnames[3]));
678 * head of a new group ?
680 if (*cpp && !strcasecmp(*cpp, "head")) {
681 if (fil.fr_skip != 0) {
682 fprintf(stderr, "%d: cannot use skip with head\n",
683 linenum);
684 *status = -1;
685 return NULL;
687 if (!*++cpp) {
688 fprintf(stderr, "%d: head without group #\n", linenum);
689 *status = -1;
690 return NULL;
692 if (ratoui(*cpp, &k, 0, UINT_MAX))
693 fil.fr_grhead = (u_32_t)k;
694 else {
695 fprintf(stderr, "%d: invalid group (%s)\n",
696 linenum, *cpp);
697 *status = -1;
698 return NULL;
700 cpp++;
704 * head of a new group ?
706 if (*cpp && !strcasecmp(*cpp, "group")) {
707 if (!*++cpp) {
708 fprintf(stderr, "%d: group without group #\n",
709 linenum);
710 *status = -1;
711 return NULL;
713 if (ratoui(*cpp, &k, 0, UINT_MAX))
714 fil.fr_group = k;
715 else {
716 fprintf(stderr, "%d: invalid group (%s)\n",
717 linenum, *cpp);
718 *status = -1;
719 return NULL;
721 cpp++;
725 * leftovers...yuck
727 if (*cpp && **cpp) {
728 fprintf(stderr, "%d: unknown words at end: [", linenum);
729 for (; *cpp; cpp++)
730 fprintf(stderr, "%s ", *cpp);
731 fprintf(stderr, "]\n");
732 *status = -1;
733 return NULL;
737 * lazy users...
739 if ((fil.fr_tcpf || fil.fr_tcpfm) && fil.fr_proto != IPPROTO_TCP) {
740 fprintf(stderr, "%d: TCP protocol not specified\n", linenum);
741 *status = -1;
742 return NULL;
744 if (!(fil.fr_ip.fi_fl & FI_TCPUDP) && (fil.fr_proto != IPPROTO_TCP) &&
745 (fil.fr_proto != IPPROTO_UDP) && (fil.fr_dcmp || fil.fr_scmp)) {
746 if (!fil.fr_proto) {
747 fil.fr_ip.fi_fl |= FI_TCPUDP;
748 fil.fr_mip.fi_fl |= FI_TCPUDP;
749 } else {
750 fprintf(stderr,
751 "%d: port comparisons for non-TCP/UDP\n",
752 linenum);
753 *status = -1;
754 return NULL;
758 if ((fil.fr_flags & FR_KEEPFRAG) &&
759 (!(fil.fr_ip.fi_fl & FI_FRAG) || !(fil.fr_ip.fi_fl & FI_FRAG))) {
760 fprintf(stderr,
761 "%d: must use 'with frags' with 'keep frags'\n",
762 linenum);
763 *status = -1;
764 return NULL;
767 *status = 0;
768 return &fil;
772 int loglevel(cpp, facpri, linenum)
773 char **cpp;
774 u_int *facpri;
775 int linenum;
777 int fac, pri;
778 char *s;
780 fac = 0;
781 pri = 0;
782 if (!*++cpp) {
783 fprintf(stderr, "%d: %s\n", linenum,
784 "missing identifier after level");
785 return -1;
788 s = index(*cpp, '.');
789 if (s) {
790 *s++ = '\0';
791 fac = fac_findname(*cpp);
792 if (fac == -1) {
793 fprintf(stderr, "%d: %s %s\n", linenum,
794 "Unknown facility", *cpp);
795 return -1;
797 pri = pri_findname(s);
798 if (pri == -1) {
799 fprintf(stderr, "%d: %s %s\n", linenum,
800 "Unknown priority", s);
801 return -1;
803 } else {
804 pri = pri_findname(*cpp);
805 if (pri == -1) {
806 fprintf(stderr, "%d: %s %s\n", linenum,
807 "Unknown priority", *cpp);
808 return -1;
811 *facpri = fac|pri;
812 return 0;
816 int to_interface(fdp, to, linenum)
817 frdest_t *fdp;
818 char *to;
819 int linenum;
821 char *s;
823 s = index(to, ':');
824 fdp->fd_ifp = NULL;
825 if (s) {
826 *s++ = '\0';
827 if (hostnum((u_32_t *)&fdp->fd_ip, s, linenum) == -1)
828 return -1;
830 (void) strncpy(fdp->fd_ifname, to, sizeof(fdp->fd_ifname) - 1);
831 fdp->fd_ifname[sizeof(fdp->fd_ifname) - 1] = '\0';
832 return 0;
836 void print_toif(tag, fdp)
837 char *tag;
838 frdest_t *fdp;
840 printf("%s %s%s", tag, fdp->fd_ifname,
841 (fdp->fd_ifp || (long)fdp->fd_ifp == -1) ? "" : "(!)");
842 #ifdef USE_INET6
843 if (use_inet6 && IP6_NOTZERO(&fdp->fd_ip6.in6)) {
844 char ipv6addr[80];
846 inet_ntop(AF_INET6, &fdp->fd_ip6, ipv6addr,
847 sizeof(fdp->fd_ip6));
848 printf(":%s", ipv6addr);
849 } else
850 #endif
851 if (fdp->fd_ip.s_addr)
852 printf(":%s", inet_ntoa(fdp->fd_ip));
853 putchar(' ');
858 * deal with extra bits on end of the line
860 int extras(cp, fr, linenum)
861 char ***cp;
862 struct frentry *fr;
863 int linenum;
865 u_short secmsk;
866 u_long opts;
867 int notopt;
868 char oflags;
870 opts = 0;
871 secmsk = 0;
872 notopt = 0;
873 (*cp)++;
874 if (!**cp)
875 return -1;
877 while (**cp && (!strncasecmp(**cp, "ipopt", 5) ||
878 !strcasecmp(**cp, "not") || !strncasecmp(**cp, "opt", 3) ||
879 !strncasecmp(**cp, "frag", 4) || !strcasecmp(**cp, "no") ||
880 !strcasecmp(**cp, "short"))) {
881 if (***cp == 'n' || ***cp == 'N') {
882 notopt = 1;
883 (*cp)++;
884 continue;
885 } else if (***cp == 'i' || ***cp == 'I') {
886 if (!notopt)
887 fr->fr_ip.fi_fl |= FI_OPTIONS;
888 fr->fr_mip.fi_fl |= FI_OPTIONS;
889 goto nextopt;
890 } else if (***cp == 'f' || ***cp == 'F') {
891 if (!notopt)
892 fr->fr_ip.fi_fl |= FI_FRAG;
893 fr->fr_mip.fi_fl |= FI_FRAG;
894 goto nextopt;
895 } else if (***cp == 'o' || ***cp == 'O') {
896 if (!*(*cp + 1)) {
897 fprintf(stderr,
898 "%d: opt missing arguements\n",
899 linenum);
900 return -1;
902 (*cp)++;
903 if (!(opts = optname(cp, &secmsk, linenum)))
904 return -1;
905 oflags = FI_OPTIONS;
906 } else if (***cp == 's' || ***cp == 'S') {
907 if (fr->fr_tcpf) {
908 fprintf(stderr,
909 "%d: short cannot be used with TCP flags\n",
910 linenum);
911 return -1;
914 if (!notopt)
915 fr->fr_ip.fi_fl |= FI_SHORT;
916 fr->fr_mip.fi_fl |= FI_SHORT;
917 goto nextopt;
918 } else
919 return -1;
921 if (!notopt || !opts)
922 fr->fr_mip.fi_fl |= oflags;
923 if (notopt) {
924 if (!secmsk) {
925 fr->fr_mip.fi_optmsk |= opts;
926 } else {
927 fr->fr_mip.fi_optmsk |= (opts & ~0x0100);
929 } else {
930 fr->fr_mip.fi_optmsk |= opts;
932 fr->fr_mip.fi_secmsk |= secmsk;
934 if (notopt) {
935 fr->fr_ip.fi_fl &= (~oflags & 0xf);
936 fr->fr_ip.fi_optmsk &= ~opts;
937 fr->fr_ip.fi_secmsk &= ~secmsk;
938 } else {
939 fr->fr_ip.fi_fl |= oflags;
940 fr->fr_ip.fi_optmsk |= opts;
941 fr->fr_ip.fi_secmsk |= secmsk;
943 nextopt:
944 notopt = 0;
945 opts = 0;
946 oflags = 0;
947 secmsk = 0;
948 (*cp)++;
950 return 0;
954 u_32_t optname(cp, sp, linenum)
955 char ***cp;
956 u_short *sp;
957 int linenum;
959 struct ipopt_names *io, *so;
960 u_long msk = 0;
961 u_short smsk = 0;
962 char *s;
963 int sec = 0;
965 for (s = strtok(**cp, ","); s; s = strtok(NULL, ",")) {
966 for (io = ionames; io->on_name; io++)
967 if (!strcasecmp(s, io->on_name)) {
968 msk |= io->on_bit;
969 break;
971 if (!io->on_name) {
972 fprintf(stderr, "%d: unknown IP option name %s\n",
973 linenum, s);
974 return 0;
976 if (!strcasecmp(s, "sec-class"))
977 sec = 1;
980 if (sec && !*(*cp + 1)) {
981 fprintf(stderr, "%d: missing security level after sec-class\n",
982 linenum);
983 return 0;
986 if (sec) {
987 (*cp)++;
988 for (s = strtok(**cp, ","); s; s = strtok(NULL, ",")) {
989 for (so = secclass; so->on_name; so++)
990 if (!strcasecmp(s, so->on_name)) {
991 smsk |= so->on_bit;
992 break;
994 if (!so->on_name) {
995 fprintf(stderr,
996 "%d: no such security level: %s\n",
997 linenum, s);
998 return 0;
1001 if (smsk)
1002 *sp = smsk;
1004 return msk;
1008 #ifdef __STDC__
1009 void optprint(u_short *sec, u_long optmsk, u_long optbits)
1010 #else
1011 void optprint(sec, optmsk, optbits)
1012 u_short *sec;
1013 u_long optmsk, optbits;
1014 #endif
1016 u_short secmsk = sec[0], secbits = sec[1];
1017 struct ipopt_names *io, *so;
1018 char *s;
1020 s = " opt ";
1021 for (io = ionames; io->on_name; io++)
1022 if ((io->on_bit & optmsk) &&
1023 ((io->on_bit & optmsk) == (io->on_bit & optbits))) {
1024 if ((io->on_value != IPOPT_SECURITY) ||
1025 (!secmsk && !secbits)) {
1026 printf("%s%s", s, io->on_name);
1027 if (io->on_value == IPOPT_SECURITY)
1028 io++;
1029 s = ",";
1034 if (secmsk & secbits) {
1035 printf("%ssec-class", s);
1036 s = " ";
1037 for (so = secclass; so->on_name; so++)
1038 if ((secmsk & so->on_bit) &&
1039 ((so->on_bit & secmsk) == (so->on_bit & secbits))) {
1040 printf("%s%s", s, so->on_name);
1041 s = ",";
1045 if ((optmsk && (optmsk != optbits)) ||
1046 (secmsk && (secmsk != secbits))) {
1047 s = " ";
1048 printf(" not opt");
1049 if (optmsk != optbits) {
1050 for (io = ionames; io->on_name; io++)
1051 if ((io->on_bit & optmsk) &&
1052 ((io->on_bit & optmsk) !=
1053 (io->on_bit & optbits))) {
1054 if ((io->on_value != IPOPT_SECURITY) ||
1055 (!secmsk && !secbits)) {
1056 printf("%s%s", s, io->on_name);
1057 s = ",";
1058 if (io->on_value ==
1059 IPOPT_SECURITY)
1060 io++;
1061 } else
1062 io++;
1066 if (secmsk != secbits) {
1067 printf("%ssec-class", s);
1068 s = " ";
1069 for (so = secclass; so->on_name; so++)
1070 if ((so->on_bit & secmsk) &&
1071 ((so->on_bit & secmsk) !=
1072 (so->on_bit & secbits))) {
1073 printf("%s%s", s, so->on_name);
1074 s = ",";
1080 char *icmptypes[] = {
1081 "echorep", (char *)NULL, (char *)NULL, "unreach", "squench",
1082 "redir", (char *)NULL, (char *)NULL, "echo", "routerad",
1083 "routersol", "timex", "paramprob", "timest", "timestrep",
1084 "inforeq", "inforep", "maskreq", "maskrep", "END"
1088 * set the icmp field to the correct type if "icmp" word is found
1090 int addicmp(cp, fp, linenum)
1091 char ***cp;
1092 struct frentry *fp;
1093 int linenum;
1095 char **t;
1096 int i;
1098 (*cp)++;
1099 if (!**cp)
1100 return -1;
1102 if (isdigit(***cp)) {
1103 if (!ratoi(**cp, &i, 0, 255)) {
1104 fprintf(stderr,
1105 "%d: Invalid icmp-type (%s) specified\n",
1106 linenum, **cp);
1107 return -1;
1109 } else if (fp->fr_proto == IPPROTO_ICMPV6) {
1110 fprintf(stderr, "%d: Unknown ICMPv6 type (%s) specified, %s",
1111 linenum, **cp, "(use numeric value instead)\n");
1112 return -1;
1113 } else {
1114 for (t = icmptypes, i = 0; ; t++, i++) {
1115 if (!*t)
1116 continue;
1117 if (!strcasecmp("END", *t)) {
1118 i = -1;
1119 break;
1121 if (!strcasecmp(*t, **cp))
1122 break;
1124 if (i == -1) {
1125 fprintf(stderr,
1126 "%d: Invalid icmp-type (%s) specified\n",
1127 linenum, **cp);
1128 return -1;
1131 fp->fr_icmp = (u_short)(i << 8);
1132 fp->fr_icmpm = (u_short)0xff00;
1133 (*cp)++;
1134 if (!**cp)
1135 return 0;
1137 if (**cp && strcasecmp("code", **cp))
1138 return 0;
1139 (*cp)++;
1140 if (isdigit(***cp)) {
1141 if (!ratoi(**cp, &i, 0, 255)) {
1142 fprintf(stderr,
1143 "%d: Invalid icmp code (%s) specified\n",
1144 linenum, **cp);
1145 return -1;
1147 } else {
1148 i = icmpcode(**cp);
1149 if (i == -1) {
1150 fprintf(stderr,
1151 "%d: Invalid icmp code (%s) specified\n",
1152 linenum, **cp);
1153 return -1;
1156 i &= 0xff;
1157 fp->fr_icmp |= (u_short)i;
1158 fp->fr_icmpm = (u_short)0xffff;
1159 (*cp)++;
1160 return 0;
1164 #define MAX_ICMPCODE 15
1166 char *icmpcodes[] = {
1167 "net-unr", "host-unr", "proto-unr", "port-unr", "needfrag",
1168 "srcfail", "net-unk", "host-unk", "isolate", "net-prohib",
1169 "host-prohib", "net-tos", "host-tos", "filter-prohib", "host-preced",
1170 "preced-cutoff", NULL };
1172 * Return the number for the associated ICMP unreachable code.
1174 int icmpcode(str)
1175 char *str;
1177 char *s;
1178 int i, len;
1180 if ((s = strrchr(str, ')')))
1181 *s = '\0';
1182 if (isdigit(*str)) {
1183 if (!ratoi(str, &i, 0, 255))
1184 return -1;
1185 else
1186 return i;
1188 len = strlen(str);
1189 for (i = 0; icmpcodes[i]; i++)
1190 if (!strncasecmp(str, icmpcodes[i], MIN(len,
1191 strlen(icmpcodes[i])) ))
1192 return i;
1193 return -1;
1198 * set the icmp field to the correct type if "icmp" word is found
1200 int addkeep(cp, fp, linenum)
1201 char ***cp;
1202 struct frentry *fp;
1203 int linenum;
1205 char *s;
1207 (*cp)++;
1208 if (!**cp) {
1209 fprintf(stderr, "%d: Missing keyword after keep\n",
1210 linenum);
1211 return -1;
1214 if (strcasecmp(**cp, "state") == 0)
1215 fp->fr_flags |= FR_KEEPSTATE;
1216 else if (strncasecmp(**cp, "frag", 4) == 0)
1217 fp->fr_flags |= FR_KEEPFRAG;
1218 else if (strcasecmp(**cp, "state-age") == 0) {
1219 if (fp->fr_ip.fi_p == IPPROTO_TCP) {
1220 fprintf(stderr, "%d: cannot use state-age with tcp\n",
1221 linenum);
1222 return -1;
1224 if ((fp->fr_flags & FR_KEEPSTATE) == 0) {
1225 fprintf(stderr, "%d: state-age with no 'keep state'\n",
1226 linenum);
1227 return -1;
1229 (*cp)++;
1230 if (!**cp) {
1231 fprintf(stderr, "%d: state-age with no arg\n",
1232 linenum);
1233 return -1;
1235 fp->fr_age[0] = atoi(**cp);
1236 s = index(**cp, '/');
1237 if (s != NULL) {
1238 s++;
1239 fp->fr_age[1] = atoi(s);
1240 } else
1241 fp->fr_age[1] = fp->fr_age[0];
1242 } else {
1243 fprintf(stderr, "%d: Unrecognised state keyword \"%s\"\n",
1244 linenum, **cp);
1245 return -1;
1247 (*cp)++;
1248 return 0;
1252 void printifname(format, name, ifp)
1253 char *format, *name;
1254 void *ifp;
1256 printf("%s%s", format, name);
1257 if ((ifp == NULL) && strcmp(name, "-") && strcmp(name, "*"))
1258 printf("(!)");
1263 * print the filter structure in a useful way
1265 void printfr(fp)
1266 struct frentry *fp;
1268 struct protoent *p;
1269 u_short sec[2];
1270 char *s;
1271 u_char *t;
1272 int pr;
1274 if (fp->fr_flags & FR_PASS)
1275 printf("pass");
1276 if (fp->fr_flags & FR_NOMATCH)
1277 printf("nomatch");
1278 else if (fp->fr_flags & FR_BLOCK) {
1279 printf("block");
1280 if (fp->fr_flags & FR_RETICMP) {
1281 if ((fp->fr_flags & FR_RETMASK) == FR_FAKEICMP)
1282 printf(" return-icmp-as-dest");
1283 else if ((fp->fr_flags & FR_RETMASK) == FR_RETICMP)
1284 printf(" return-icmp");
1285 if (fp->fr_icode) {
1286 if (fp->fr_icode <= MAX_ICMPCODE)
1287 printf("(%s)",
1288 icmpcodes[(int)fp->fr_icode]);
1289 else
1290 printf("(%d)", fp->fr_icode);
1292 } else if ((fp->fr_flags & FR_RETMASK) == FR_RETRST)
1293 printf(" return-rst");
1294 } else if ((fp->fr_flags & FR_LOGMASK) == FR_LOG) {
1295 printlog(fp);
1296 } else if (fp->fr_flags & FR_ACCOUNT)
1297 printf("count");
1298 else if (fp->fr_flags & FR_AUTH) {
1299 printf("auth");
1300 if ((fp->fr_flags & FR_RETMASK) == FR_RETRST)
1301 printf(" return-rst");
1302 } else if (fp->fr_flags & FR_PREAUTH)
1303 printf("preauth");
1304 else if (fp->fr_skip)
1305 printf("skip %hu", fp->fr_skip);
1307 if (fp->fr_flags & FR_OUTQUE)
1308 printf(" out ");
1309 else
1310 printf(" in ");
1312 if (((fp->fr_flags & FR_LOGB) == FR_LOGB) ||
1313 ((fp->fr_flags & FR_LOGP) == FR_LOGP)) {
1314 printlog(fp);
1315 putchar(' ');
1318 if (fp->fr_flags & FR_QUICK)
1319 printf("quick ");
1321 if (*fp->fr_ifname) {
1322 printifname("on ", fp->fr_ifname, fp->fr_ifa);
1323 if (*fp->fr_ifnames[1] && strcmp(fp->fr_ifnames[1], "*"))
1324 printifname(",", fp->fr_ifnames[1], fp->fr_ifas[1]);
1325 putchar(' ');
1327 if (*fp->fr_dif.fd_ifname)
1328 print_toif("dup-to", &fp->fr_dif);
1329 if (*fp->fr_tif.fd_ifname)
1330 print_toif("to", &fp->fr_tif);
1331 if (fp->fr_flags & FR_FASTROUTE)
1332 printf("fastroute ");
1334 if ((*fp->fr_ifnames[2] && strcmp(fp->fr_ifnames[2], "*")) ||
1335 (*fp->fr_ifnames[3] && strcmp(fp->fr_ifnames[3], "*"))) {
1336 if (fp->fr_flags & FR_OUTQUE)
1337 printf("in-via ");
1338 else
1339 printf("out-via ");
1341 if (*fp->fr_ifnames[2]) {
1342 printifname("", fp->fr_ifnames[2],
1343 fp->fr_ifas[2]);
1344 putchar(',');
1347 if (*fp->fr_ifnames[3])
1348 printifname("", fp->fr_ifnames[3],
1349 fp->fr_ifas[3]);
1350 putchar(' ');
1354 if (fp->fr_mip.fi_tos)
1355 printf("tos %#x ", fp->fr_tos);
1356 if (fp->fr_mip.fi_ttl)
1357 printf("ttl %d ", fp->fr_ttl);
1358 if (fp->fr_ip.fi_fl & FI_TCPUDP) {
1359 printf("proto tcp/udp ");
1360 pr = -1;
1361 } else if ((pr = fp->fr_mip.fi_p)) {
1362 if ((p = getprotobynumber(fp->fr_proto)))
1363 printf("proto %s ", p->p_name);
1364 else
1365 printf("proto %d ", fp->fr_proto);
1368 printf("from %s", fp->fr_flags & FR_NOTSRCIP ? "!" : "");
1369 printhostmask(fp->fr_v, (u_32_t *)&fp->fr_src.s_addr,
1370 (u_32_t *)&fp->fr_smsk.s_addr);
1371 if (fp->fr_scmp)
1372 printportcmp(pr, &fp->fr_tuc.ftu_src);
1374 printf(" to %s", fp->fr_flags & FR_NOTDSTIP ? "!" : "");
1375 printhostmask(fp->fr_v, (u_32_t *)&fp->fr_dst.s_addr,
1376 (u_32_t *)&fp->fr_dmsk.s_addr);
1377 if (fp->fr_dcmp)
1378 printportcmp(pr, &fp->fr_tuc.ftu_dst);
1380 if ((fp->fr_ip.fi_fl & ~FI_TCPUDP) ||
1381 (fp->fr_mip.fi_fl & ~FI_TCPUDP) ||
1382 fp->fr_ip.fi_optmsk || fp->fr_mip.fi_optmsk ||
1383 fp->fr_ip.fi_secmsk || fp->fr_mip.fi_secmsk) {
1384 printf(" with");
1385 if (fp->fr_ip.fi_optmsk || fp->fr_mip.fi_optmsk ||
1386 fp->fr_ip.fi_secmsk || fp->fr_mip.fi_secmsk) {
1387 sec[0] = fp->fr_mip.fi_secmsk;
1388 sec[1] = fp->fr_ip.fi_secmsk;
1389 optprint(sec,
1390 fp->fr_mip.fi_optmsk, fp->fr_ip.fi_optmsk);
1391 } else if (fp->fr_mip.fi_fl & FI_OPTIONS) {
1392 if (!(fp->fr_ip.fi_fl & FI_OPTIONS))
1393 printf(" not");
1394 printf(" ipopt");
1396 if (fp->fr_mip.fi_fl & FI_SHORT) {
1397 if (!(fp->fr_ip.fi_fl & FI_SHORT))
1398 printf(" not");
1399 printf(" short");
1401 if (fp->fr_mip.fi_fl & FI_FRAG) {
1402 if (!(fp->fr_ip.fi_fl & FI_FRAG))
1403 printf(" not");
1404 printf(" frag");
1407 if (fp->fr_proto == IPPROTO_ICMP && fp->fr_icmpm != 0) {
1408 int type = fp->fr_icmp, code;
1410 type = ntohs(fp->fr_icmp);
1411 code = type & 0xff;
1412 type /= 256;
1413 if (type < (sizeof(icmptypes) / sizeof(char *) - 1) &&
1414 icmptypes[type])
1415 printf(" icmp-type %s", icmptypes[type]);
1416 else
1417 printf(" icmp-type %d", type);
1418 if (ntohs(fp->fr_icmpm) & 0xff)
1419 printf(" code %d", code);
1421 if (fp->fr_proto == IPPROTO_ICMPV6 && fp->fr_icmpm != 0) {
1422 int type = fp->fr_icmp, code;
1424 type = ntohs(fp->fr_icmp);
1425 code = type & 0xff;
1426 type /= 256;
1427 printf(" icmp-type %d", type);
1428 if (ntohs(fp->fr_icmpm) & 0xff)
1429 printf(" code %d", code);
1431 if (fp->fr_proto == IPPROTO_TCP && (fp->fr_tcpf || fp->fr_tcpfm)) {
1432 printf(" flags ");
1433 if (fp->fr_tcpf & ~TCPF_ALL)
1434 printf("0x%x", fp->fr_tcpf);
1435 else
1436 for (s = flagset, t = flags; *s; s++, t++)
1437 if (fp->fr_tcpf & *t)
1438 (void)putchar(*s);
1439 if (fp->fr_tcpfm) {
1440 (void)putchar('/');
1441 if (fp->fr_tcpfm & ~TCPF_ALL)
1442 printf("0x%x", fp->fr_tcpfm);
1443 else
1444 for (s = flagset, t = flags; *s; s++, t++)
1445 if (fp->fr_tcpfm & *t)
1446 (void)putchar(*s);
1450 if (fp->fr_flags & FR_KEEPSTATE)
1451 printf(" keep state");
1452 if (fp->fr_flags & FR_KEEPFRAG)
1453 printf(" keep frags");
1454 if (fp->fr_age[0] != 0 || fp->fr_age[1]!= 0)
1455 printf(" state-age %u/%u", fp->fr_age[0], fp->fr_age[1]);
1456 if (fp->fr_grhead)
1457 printf(" head %d", fp->fr_grhead);
1458 if (fp->fr_group)
1459 printf(" group %d", fp->fr_group);
1460 (void)putchar('\n');
1463 void binprint(fp)
1464 struct frentry *fp;
1466 int i = sizeof(*fp), j = 0;
1467 u_char *s;
1469 for (s = (u_char *)fp; i; i--, s++) {
1470 j++;
1471 printf("%02x ", *s);
1472 if (j == 16) {
1473 printf("\n");
1474 j = 0;
1477 putchar('\n');
1478 (void)fflush(stdout);
1482 void printlog(fp)
1483 frentry_t *fp;
1485 char *s, *u;
1487 printf("log");
1488 if (fp->fr_flags & FR_LOGBODY)
1489 printf(" body");
1490 if (fp->fr_flags & FR_LOGFIRST)
1491 printf(" first");
1492 if (fp->fr_flags & FR_LOGORBLOCK)
1493 printf(" or-block");
1494 if (fp->fr_loglevel != 0xffff) {
1495 printf(" level ");
1496 if (fp->fr_loglevel & LOG_FACMASK) {
1497 s = fac_toname(fp->fr_loglevel);
1498 if (s == NULL)
1499 s = "!!!";
1500 } else
1501 s = "";
1502 u = pri_toname(fp->fr_loglevel);
1503 if (u == NULL)
1504 u = "!!!";
1505 if (*s)
1506 printf("%s.%s", s, u);
1507 else
1508 printf("%s", u);