Merge from vendor branch HEIMDAL:
[dragonfly.git] / contrib / ipfilter / parse.c
1 /*
2  * Copyright (C) 1993-2001 by Darren Reed.
3  *
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"
42
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
47
48 extern  struct  ipopt_names     ionames[], secclass[];
49 extern  int     opts;
50 extern  int     use_inet6;
51
52 int     addicmp __P((char ***, struct frentry *, int));
53 int     extras __P((char ***, struct frentry *, int));
54
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 *));
62
63 extern  char    *proto;
64 extern  char    flagset[];
65 extern  u_char  flags[];
66
67
68 /* parse()
69  *
70  * parse a line read from the input filter rule file
71  *
72  * status:
73  *      < 0     error
74  *      = 0     OK
75  *      > 0     programmer error
76  */
77 struct  frentry *parse(line, linenum, status)
78 char    *line;
79 int     linenum;
80 int     *status;        /* good, bad, or indifferent */
81 {
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;
87
88         *status = 100;  /* default to error */
89
90         while (*line && isspace(*line))
91                 line++;
92         if (!*line) {
93                 *status = 0;
94                 return NULL;
95         }
96
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;
101
102         /*
103          * break line up into max of 20 segments
104          */
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;
110
111         if (cnt < 3) {
112                 fprintf(stderr, "%d: not enough segments in line\n", linenum);
113                 *status = -1;
114                 return NULL;
115         }
116
117         cpp = cps;
118         /*
119          * The presence of an '@' followed by a number gives the position in
120          * the current rule list to insert this one.
121          */
122         if (**cpp == '@')
123                 fil.fr_hits = (U_QUAD_T)atoi(*cpp++ + 1) + 1;
124
125
126         /*
127          * Check the first keyword in the rule and any options that are
128          * expected to follow it.
129          */
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;
145                         }
146
147                         /*
148                          * The ICMP code is not required to follow in ()'s
149                          */
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;
159                                 }
160                                 fil.fr_icode = j;
161                         }
162                 } else if (!strcasecmp(*(cpp+1), "return-rst")) {
163                         fil.fr_flags |= FR_RETRST;
164                         cpp++;
165                 }
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++;
177                 }
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;
189                 }
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++;
195                 }
196                 if (!strcasecmp(*(cpp+1), "first")) {
197                         fil.fr_flags |= FR_LOGFIRST;
198                         cpp++;
199                 }
200                 if (*cpp && !strcasecmp(*(cpp+1), "or-block")) {
201                         fil.fr_flags |= FR_LOGORBLOCK;
202                         cpp++;
203                 }
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;
210                         }
211                         cpp++;
212                 }
213         } else {
214                 /*
215                  * Doesn't start with one of the action words
216                  */
217                 fprintf(stderr, "%d: unknown keyword (%s)\n", linenum, *cpp);
218                 *status = -1;
219                 return NULL;
220         }
221         if (!*++cpp) {
222                 fprintf(stderr, "%d: missing 'in'/'out' keyword\n", linenum);
223                 *status = -1;
224                 return NULL;
225         }
226
227         /*
228          * Get the direction for filtering.  Impose restrictions on direction
229          * if blocking with returning ICMP or an RST has been requested.
230          */
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;
247                 }
248         }
249         if (!*++cpp) {
250                 fprintf(stderr, "%d: missing source specification\n", linenum);
251                 *status = -1;
252                 return NULL;
253         }
254
255         if (!strcasecmp("log", *cpp)) {
256                 if (!*++cpp) {
257                         fprintf(stderr, "%d: missing source specification\n",
258                                 linenum);
259                         *status = -1;
260                         return NULL;
261                 }
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++;
269                 }
270                 if (*cpp && !strcasecmp(*cpp, "first")) {
271                         fil.fr_flags |= FR_LOGFIRST;
272                         cpp++;
273                 }
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;
281                         }
282                         fil.fr_flags |= FR_LOGORBLOCK;
283                         cpp++;
284                 }
285                 if (*cpp && !strcasecmp(*cpp, "level")) {
286                         if (loglevel(cpp, &fil.fr_loglevel, linenum) == -1) {
287                                 *status = -1;
288                                 return NULL;
289                         }
290                         cpp++;
291                         cpp++;
292                 }
293         }
294
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;
301                 }
302                 cpp++;
303                 fil.fr_flags |= FR_QUICK;
304         }
305
306         /*
307          * Parse rule options that are available if a rule is tied to an
308          * interface.
309          */
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;
318                 }
319
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], "*");
327
328                 (void)strncpy(fil.fr_ifnames[0], *cpp, IFNAMSIZ - 1);
329                 fil.fr_ifnames[0][IFNAMSIZ - 1] = '\0';
330
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;
339                         }
340                         *status = 0;
341                         return &fil;
342                 }
343
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;
350                                 }
351                                 cpp++;
352                         }
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;
358                                 }
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;
367                                 }
368                                 fil.fr_flags |= FR_FASTROUTE;
369                                 cpp++;
370                         }
371                 }
372
373                 /*
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.
377                  */
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++;
385
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';
392                         }
393
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], "*");
399         }
400         if (*cpp && !strcasecmp(*cpp, "tos")) {
401                 if (!*++cpp) {
402                         fprintf(stderr, "%d: tos missing value\n", linenum);
403                         *status = -1;
404                         return NULL;
405                 }
406                 fil.fr_tos = strtol(*cpp, NULL, 0);
407                 fil.fr_mip.fi_tos = 0xff;
408                 cpp++;
409         }
410
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;
417                 }
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;
425                 }
426                 fil.fr_mip.fi_ttl = 0xff;
427                 cpp++;
428         }
429
430         /*
431          * check for "proto <protoname>" only decode udp/tcp/icmp as protoname
432          */
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;
439                 }
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;
455                         }
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;            
466                                 }
467                                 fil.fr_proto = i;
468                         }
469                         fil.fr_mip.fi_p = 0xff;
470                 }
471         }
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;
478         }
479
480         /*
481          * get the from host and bit mask to use against packets
482          */
483
484         if (!*cpp) {
485                 fprintf(stderr, "%d: missing source specification\n", linenum);
486                 *status = -1;
487                 return NULL;
488         }
489         if (!strcasecmp(*cpp, "all")) {
490                 cpp++;
491                 if (!*cpp) {
492                         *status = 0;
493                         return &fil;
494                 }
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;
501                 }
502                 if (!*++cpp) {
503                         fprintf(stderr, "%d: missing host after from\n",
504                                 linenum);
505                         *status = -1;
506                         return NULL;
507                 }
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;
516                         }
517                 } else if (**cpp == '!') {
518                         fil.fr_flags |= FR_NOTSRCIP;
519                         (*cpp)++;
520                 }
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;
527                 }
528
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;
537                 }
538
539                 fil.fr_scmp = ch;
540                 if (!*cpp) {
541                         fprintf(stderr, "%d: missing to fields\n", linenum);
542                         *status = -1;
543                         return NULL;
544                 }
545
546                 /*
547                  * do the same for the to field (destination host)
548                  */
549                 if (strcasecmp(*cpp, "to")) {
550                         fprintf(stderr, "%d: unexpected keyword (%s) - to\n",
551                                 linenum, *cpp);
552                         *status = -1;
553                         return NULL;
554                 }
555                 if (!*++cpp) {
556                         fprintf(stderr, "%d: missing host after to\n", linenum);
557                         *status = -1;
558                         return NULL;
559                 }
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;
569                         }
570                 } else if (**cpp == '!') {
571                         fil.fr_flags |= FR_NOTDSTIP;
572                         (*cpp)++;
573                 }
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;
579                 }
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;
588                 }
589
590                 fil.fr_dcmp = ch;
591         }
592
593         /*
594          * check some sanity, make sure we don't have icmp checks with tcp
595          * or udp or visa versa.
596          */
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;
602         }
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;
608         }
609
610         if (!*cpp) {
611                 *status = 0;
612                 return &fil;
613         }
614
615         if (*cpp && !strcasecmp(*cpp, "flags")) {
616                 if (!*++cpp) {
617                         fprintf(stderr, "%d: no flags present\n", linenum);
618                         *status = -1;
619                         return NULL;
620                 }
621                 fil.fr_tcpf = tcp_flags(*cpp, &fil.fr_tcpfm, linenum);
622                 cpp++;
623         }
624
625         /*
626          * extras...
627          */
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;
633                 }
634
635         /*
636          * icmp types for use with the icmp protocol
637          */
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;
646                 }
647                 if (addicmp(&cpp, &fil, linenum)) {
648                         *status = -1;
649                         return NULL;
650                 }
651                 fil.fr_icmp = htons(fil.fr_icmp);
652                 fil.fr_icmpm = htons(fil.fr_icmpm);
653         }
654
655         /*
656          * Keep something...
657          */
658         while (*cpp && !strcasecmp(*cpp, "keep"))
659                 if (addkeep(&cpp, &fil, linenum)) {
660                         *status = -1;
661                         return NULL;
662                 }
663
664         /*
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"
667          */
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]));
674                 }
675         }
676
677         /*
678          * head of a new group ?
679          */
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;
686                 }
687                 if (!*++cpp) {
688                         fprintf(stderr, "%d: head without group #\n", linenum);
689                         *status = -1;
690                         return NULL;
691                 }
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;
699                 }
700                 cpp++;
701         }
702
703         /*
704          * head of a new group ?
705          */
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;
712                 }
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;
720                 }
721                 cpp++;
722         }
723
724         /*
725          * leftovers...yuck
726          */
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;
734         }
735
736         /*
737          * lazy users...
738          */
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;
743         }
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;
755                 }
756         }
757 /*
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;
765         }
766 */
767         *status = 0;
768         return &fil;
769 }
770
771
772 int loglevel(cpp, facpri, linenum)
773 char **cpp;
774 u_int *facpri;
775 int linenum;
776 {
777         int fac, pri;
778         char *s;
779
780         fac = 0;
781         pri = 0;
782         if (!*++cpp) {
783                 fprintf(stderr, "%d: %s\n", linenum,
784                         "missing identifier after level");
785                 return -1;
786         }
787
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;
796                 }
797                 pri = pri_findname(s);
798                 if (pri == -1) {
799                         fprintf(stderr, "%d: %s %s\n", linenum,
800                                 "Unknown priority", s);
801                         return -1;
802                 }
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;
809                 }
810         }
811         *facpri = fac|pri;
812         return 0;
813 }
814
815
816 int to_interface(fdp, to, linenum)
817 frdest_t *fdp;
818 char *to;
819 int linenum;
820 {
821         char *s;
822
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;
829         }
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;
833 }
834
835
836 void print_toif(tag, fdp)
837 char *tag;
838 frdest_t *fdp;
839 {
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];
845
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(' ');
854 }
855
856
857 /*
858  * deal with extra bits on end of the line
859  */
860 int     extras(cp, fr, linenum)
861 char    ***cp;
862 struct  frentry *fr;
863 int     linenum;
864 {
865         u_short secmsk;
866         u_long  opts;
867         int     notopt;
868         char    oflags;
869
870         opts = 0;
871         secmsk = 0;
872         notopt = 0;
873         (*cp)++;
874         if (!**cp)
875                 return -1;
876
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;
901                         }
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;
912                         }
913
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;
920
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);
928                   }
929                 } else {
930                                 fr->fr_mip.fi_optmsk |= opts;
931                 }
932                 fr->fr_mip.fi_secmsk |= secmsk;
933
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;
942                 }
943 nextopt:
944                 notopt = 0;
945                 opts = 0;
946                 oflags = 0;
947                 secmsk = 0;
948                 (*cp)++;
949         }
950         return 0;
951 }
952
953
954 u_32_t optname(cp, sp, linenum)
955 char ***cp;
956 u_short *sp;
957 int linenum;
958 {
959         struct ipopt_names *io, *so;
960         u_long msk = 0;
961         u_short smsk = 0;
962         char *s;
963         int sec = 0;
964
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;
970                         }
971                 if (!io->on_name) {
972                         fprintf(stderr, "%d: unknown IP option name %s\n",
973                                 linenum, s);
974                         return 0;
975                 }
976                 if (!strcasecmp(s, "sec-class"))
977                         sec = 1;
978         }
979
980         if (sec && !*(*cp + 1)) {
981                 fprintf(stderr, "%d: missing security level after sec-class\n",
982                         linenum);
983                 return 0;
984         }
985
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;
993                                 }
994                         if (!so->on_name) {
995                                 fprintf(stderr,
996                                         "%d: no such security level: %s\n",
997                                         linenum, s);
998                                 return 0;
999                         }
1000                 }
1001                 if (smsk)
1002                         *sp = smsk;
1003         }
1004         return msk;
1005 }
1006
1007
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
1015 {
1016         u_short secmsk = sec[0], secbits = sec[1];
1017         struct ipopt_names *io, *so;
1018         char *s;
1019
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 = ",";
1030                         }
1031                 }
1032
1033
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 = ",";
1042                         }
1043         }
1044
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++;
1063                                 }
1064                 }
1065
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 = ",";
1075                                 }
1076                 }
1077         }
1078 }
1079
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"
1085 };
1086
1087 /*
1088  * set the icmp field to the correct type if "icmp" word is found
1089  */
1090 int addicmp(cp, fp, linenum)
1091 char ***cp;
1092 struct frentry  *fp;
1093 int linenum;
1094 {
1095         char    **t;
1096         int     i;
1097
1098         (*cp)++;
1099         if (!**cp)
1100                 return -1;
1101
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;
1108                 }
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;
1120                         }
1121                         if (!strcasecmp(*t, **cp))
1122                                 break;
1123                 }
1124                 if (i == -1) {
1125                         fprintf(stderr,
1126                                 "%d: Invalid icmp-type (%s) specified\n",
1127                                 linenum, **cp);
1128                         return -1;
1129                 }
1130         }
1131         fp->fr_icmp = (u_short)(i << 8);
1132         fp->fr_icmpm = (u_short)0xff00;
1133         (*cp)++;
1134         if (!**cp)
1135                 return 0;
1136
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;
1146                 }
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;
1154                 }
1155         }
1156         i &= 0xff;
1157         fp->fr_icmp |= (u_short)i;
1158         fp->fr_icmpm = (u_short)0xffff;
1159         (*cp)++;
1160         return 0;
1161 }
1162
1163
1164 #define MAX_ICMPCODE    15
1165
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 };
1171 /*
1172  * Return the number for the associated ICMP unreachable code.
1173  */
1174 int icmpcode(str)
1175 char *str;
1176 {
1177         char    *s;
1178         int     i, len;
1179
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;
1187         }
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;
1194 }
1195
1196
1197 /*
1198  * set the icmp field to the correct type if "icmp" word is found
1199  */
1200 int addkeep(cp, fp, linenum)
1201 char ***cp;
1202 struct frentry  *fp;
1203 int linenum; 
1204 {
1205         char *s;
1206
1207         (*cp)++;
1208         if (!**cp) {
1209                 fprintf(stderr, "%d: Missing keyword after keep\n",
1210                         linenum);
1211                 return -1;
1212         }
1213
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;
1223                 }
1224                 if ((fp->fr_flags & FR_KEEPSTATE) == 0) {
1225                         fprintf(stderr, "%d: state-age with no 'keep state'\n",
1226                                 linenum);
1227                         return -1;
1228                 }
1229                 (*cp)++;
1230                 if (!**cp) {
1231                         fprintf(stderr, "%d: state-age with no arg\n",
1232                                 linenum);
1233                         return -1;
1234                 }
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;
1246         }
1247         (*cp)++;
1248         return 0;
1249 }
1250
1251
1252 void printifname(format, name, ifp)
1253 char *format, *name;
1254 void *ifp;
1255 {
1256         printf("%s%s", format, name);
1257         if ((ifp == NULL) && strcmp(name, "-") && strcmp(name, "*"))
1258                 printf("(!)");
1259 }
1260
1261
1262 /*
1263  * print the filter structure in a useful way
1264  */
1265 void printfr(fp)
1266 struct frentry  *fp;
1267 {
1268         struct protoent *p;
1269         u_short sec[2];
1270         char *s;
1271         u_char *t;
1272         int pr;
1273
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);
1291                         }
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);
1306
1307         if (fp->fr_flags & FR_OUTQUE)
1308                 printf(" out ");
1309         else
1310                 printf(" in ");
1311
1312         if (((fp->fr_flags & FR_LOGB) == FR_LOGB) ||
1313             ((fp->fr_flags & FR_LOGP) == FR_LOGP)) {
1314                 printlog(fp);
1315                 putchar(' ');
1316         }
1317
1318         if (fp->fr_flags & FR_QUICK)
1319                 printf("quick ");
1320
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(' ');
1326
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 ");
1333
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 ");
1340
1341                         if (*fp->fr_ifnames[2]) {
1342                                 printifname("", fp->fr_ifnames[2],
1343                                             fp->fr_ifas[2]);
1344                                 putchar(',');
1345                         }
1346
1347                         if (*fp->fr_ifnames[3])
1348                                 printifname("", fp->fr_ifnames[3],
1349                                             fp->fr_ifas[3]);
1350                         putchar(' ');
1351                 }
1352         }
1353
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);
1366         }
1367
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);
1373
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);
1379
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");
1395                 }
1396                 if (fp->fr_mip.fi_fl & FI_SHORT) {
1397                         if (!(fp->fr_ip.fi_fl & FI_SHORT))
1398                                 printf(" not");
1399                         printf(" short");
1400                 }
1401                 if (fp->fr_mip.fi_fl & FI_FRAG) {
1402                         if (!(fp->fr_ip.fi_fl & FI_FRAG))
1403                                 printf(" not");
1404                         printf(" frag");
1405                 }
1406         }
1407         if (fp->fr_proto == IPPROTO_ICMP && fp->fr_icmpm != 0) {
1408                 int     type = fp->fr_icmp, code;
1409
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);
1420         }
1421         if (fp->fr_proto == IPPROTO_ICMPV6 && fp->fr_icmpm != 0) {
1422                 int     type = fp->fr_icmp, code;
1423
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);
1430         }
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);
1447                 }
1448         }
1449
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');
1461 }
1462
1463 void    binprint(fp)
1464 struct frentry *fp;
1465 {
1466         int i = sizeof(*fp), j = 0;
1467         u_char *s;
1468
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;
1475                 }
1476         }
1477         putchar('\n');
1478         (void)fflush(stdout);
1479 }
1480
1481
1482 void printlog(fp)
1483 frentry_t *fp;
1484 {
1485         char *s, *u;
1486
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);
1509         }
1510 }