Add -O and -T to the SYNOPSIS.
[dragonfly.git] / contrib / ipfilter / natparse.c
1 /*
2  * Copyright (C) 1993-2002 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 <stdio.h>
10 #include <string.h>
11 #include <fcntl.h>
12 #include <errno.h>
13 #include <sys/types.h>
14 #if !defined(__SVR4) && !defined(__svr4__)
15 # include <strings.h>
16 #else
17 # include <sys/byteorder.h>
18 #endif
19 #include <sys/time.h>
20 #include <sys/param.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <stddef.h>
24 #include <sys/socket.h>
25 #include <sys/ioctl.h>
26 #if defined(sun) && (defined(__svr4__) || defined(__SVR4))
27 # include <sys/ioccom.h>
28 # include <sys/sysmacros.h>
29 #endif
30 #include <netinet/in.h>
31 #include <netinet/in_systm.h>
32 #include <netinet/ip.h>
33 #include <netinet/tcp.h>
34 #include <net/if.h>
35 #if __FreeBSD_version >= 300000
36 # include <net/if_var.h>
37 #endif
38 #include <netdb.h>
39 #include <arpa/nameser.h>
40 #include <arpa/inet.h>
41 #include <resolv.h>
42 #include <ctype.h>
43 #include "netinet/ip_compat.h"
44 #include "netinet/ip_fil.h"
45 #include "netinet/ip_nat.h"
46 #include "netinet/ip_state.h"
47 #include "netinet/ip_proxy.h"
48 #include "ipf.h"
49
50 #if     defined(sun) && !SOLARIS2
51 # define        STRERROR(x)     sys_errlist[x]
52 extern  char    *sys_errlist[];
53 #else
54 # define        STRERROR(x)     strerror(x)
55 #endif
56
57 #if !defined(lint)
58 static const char sccsid[] ="@(#)ipnat.c        1.9 6/5/96 (C) 1993 Darren Reed";
59 static const char rcsid[] = "@(#)$Id: natparse.c,v 1.17.2.29 2003/05/15 17:45:34 darrenr Exp $";
60 #endif
61
62
63 #if     SOLARIS
64 #define bzero(a,b)      memset(a,0,b)
65 #endif
66
67 extern  void    printnat __P((ipnat_t *, int));
68 extern  int     countbits __P((u_32_t));
69 extern  char    *proto;
70
71 ipnat_t *natparse __P((char *, int, int *));
72 void    natparsefile __P((int, char *, int));
73 void    nat_setgroupmap __P((struct ipnat *));
74
75
76 void nat_setgroupmap(n)
77 ipnat_t *n;
78 {
79         if (n->in_outmsk == n->in_inmsk)
80                 n->in_ippip = 1;
81         else if (n->in_flags & IPN_AUTOPORTMAP) {
82                 n->in_ippip = ~ntohl(n->in_inmsk);
83                 if (n->in_outmsk != 0xffffffff)
84                         n->in_ippip /= (~ntohl(n->in_outmsk) + 1);
85                 n->in_ippip++;
86                 if (n->in_ippip == 0)
87                         n->in_ippip = 1;
88                 n->in_ppip = USABLE_PORTS / n->in_ippip;
89         } else {
90                 n->in_space = USABLE_PORTS * ~ntohl(n->in_outmsk);
91                 n->in_nip = 0;
92                 if (!(n->in_ppip = n->in_pmin))
93                         n->in_ppip = 1;
94                 n->in_ippip = USABLE_PORTS / n->in_ppip;
95         }
96 }
97
98
99 /*
100  * Parse a line of input from the ipnat configuration file
101  *
102  * status:
103  *      < 0     error
104  *      = 0     OK
105  *      > 0     programmer error
106  */
107 ipnat_t *natparse(line, linenum, status)
108 char *line;
109 int linenum;
110 int *status;
111 {
112         static ipnat_t ipn;
113         struct protoent *pr;
114         char *dnetm = NULL, *dport = NULL;
115         char *s, *t, *cps[31], **cpp;
116         int i, cnt;
117         char *port1a = NULL, *port1b = NULL, *port2a = NULL;
118
119         *status = 100;          /* default to error */
120         proto = NULL;
121
122         /*
123          * Search for end of line and comment marker, advance of leading spaces
124          */
125         if ((s = strchr(line, '\n')))
126                 *s = '\0';
127         if ((s = strchr(line, '#')))
128                 *s = '\0';
129         while (*line && isspace(*line))
130                 line++;
131         if (!*line) {
132                 *status = 0;
133                 return NULL;
134         }
135
136         bzero((char *)&ipn, sizeof(ipn));
137         cnt = 0;
138
139         /*
140          * split line upto into segments.
141          */
142         for (i = 0, *cps = strtok(line, " \b\t\r\n"); cps[i] && i < 30; cnt++)
143                 cps[++i] = strtok(NULL, " \b\t\r\n");
144
145         cps[i] = NULL;
146
147         if (cnt < 3) {
148                 fprintf(stderr, "%d: not enough segments in line\n", linenum);
149                 *status = -1;
150                 return NULL;
151         }
152
153         cpp = cps;
154
155         /*
156          * Check first word is a recognised keyword and then is the interface
157          */
158         if (!strcasecmp(*cpp, "map"))
159                 ipn.in_redir = NAT_MAP;
160         else if (!strcasecmp(*cpp, "map-block"))
161                 ipn.in_redir = NAT_MAPBLK;
162         else if (!strcasecmp(*cpp, "rdr"))
163                 ipn.in_redir = NAT_REDIRECT;
164         else if (!strcasecmp(*cpp, "bimap"))
165                 ipn.in_redir = NAT_BIMAP;
166         else {
167                 fprintf(stderr, "%d: unknown mapping: \"%s\"\n",
168                         linenum, *cpp);
169                 *status = -1;
170                 return NULL;
171         }
172
173         cpp++;
174
175         strncpy(ipn.in_ifname, *cpp, sizeof(ipn.in_ifname) - 1);
176         ipn.in_ifname[sizeof(ipn.in_ifname) - 1] = '\0';
177         cpp++;
178
179         /*
180          * If the first word after the interface is "from" or is a ! then
181          * the expanded syntax is being used so parse it differently.
182          */
183         if (!strcasecmp(*cpp, "from") || (**cpp == '!')) {
184                 if (!strcmp(*cpp, "!")) {
185                         cpp++;
186                         if (strcasecmp(*cpp, "from")) {
187                                 fprintf(stderr, "Missing from after !\n");
188                                 *status = -1;
189                                 return NULL;
190                         }
191                         ipn.in_flags |= IPN_NOTSRC;
192                 } else if (**cpp == '!') {
193                         if (strcasecmp(*cpp + 1, "from")) {
194                                 fprintf(stderr, "Missing from after !\n");
195                                 *status = -1;
196                                 return NULL;
197                         }
198                         ipn.in_flags |= IPN_NOTSRC;
199                 }
200                 if ((ipn.in_flags & IPN_NOTSRC) &&
201                     (ipn.in_redir & (NAT_MAP|NAT_MAPBLK))) {
202                         fprintf(stderr, "Cannot use '! from' with map\n");
203                         *status = -1;
204                         return NULL;
205                 }
206
207                 ipn.in_flags |= IPN_FILTER;
208                 cpp++;
209                 if (ipn.in_redir == NAT_REDIRECT) {
210                         if (hostmask(&cpp, (u_32_t *)&ipn.in_srcip,
211                                      (u_32_t *)&ipn.in_srcmsk, &ipn.in_sport,
212                                      &ipn.in_scmp, &ipn.in_stop, linenum)) {
213                                 *status = -1;
214                                 return NULL;
215                         }
216                 } else {
217                         if (hostmask(&cpp, (u_32_t *)&ipn.in_inip,
218                                      (u_32_t *)&ipn.in_inmsk, &ipn.in_sport,
219                                      &ipn.in_scmp, &ipn.in_stop, linenum)) {
220                                 *status = -1;
221                                 return NULL;
222                         }
223                 }
224
225                 if (!strcmp(*cpp, "!")) {
226                         cpp++;
227                         ipn.in_flags |= IPN_NOTDST;
228                 } else if (**cpp == '!') {
229                         (*cpp)++;
230                         ipn.in_flags |= IPN_NOTDST;
231                 }
232
233                 if (strcasecmp(*cpp, "to")) {
234                         fprintf(stderr, "%d: unexpected keyword (%s) - to\n",
235                                 linenum, *cpp);
236                         *status = -1;
237                         return NULL;
238                 }
239                 if ((ipn.in_flags & IPN_NOTDST) &&
240                     (ipn.in_redir & (NAT_REDIRECT))) {
241                         fprintf(stderr, "Cannot use '! to' with rdr\n");
242                         *status = -1;
243                         return NULL;
244                 }
245
246                 if (!*++cpp) {
247                         fprintf(stderr, "%d: missing host after to\n", linenum);
248                         *status = -1;
249                         return NULL;
250                 }
251                 if (ipn.in_redir == NAT_REDIRECT) {
252                         if (hostmask(&cpp, (u_32_t *)&ipn.in_outip,
253                                      (u_32_t *)&ipn.in_outmsk, &ipn.in_dport,
254                                      &ipn.in_dcmp, &ipn.in_dtop, linenum)) {
255                                 *status = -1;
256                                 return NULL;
257                         }
258                         ipn.in_pmin = htons(ipn.in_dport);
259                 } else {
260                         if (hostmask(&cpp, (u_32_t *)&ipn.in_srcip,
261                                      (u_32_t *)&ipn.in_srcmsk, &ipn.in_dport,
262                                      &ipn.in_dcmp, &ipn.in_dtop, linenum)) {
263                                 *status = -1;
264                                 return NULL;
265                         }
266                 }
267         } else {
268                 s = *cpp;
269                 if (!s) {
270                         fprintf(stderr, "%d: short line\n", linenum);
271                         *status = -1;
272                         return NULL;
273                 }
274                 t = strchr(s, '/');
275                 if (!t) {
276                         fprintf(stderr, "%d: no netmask on LHS\n", linenum);
277                         *status = -1;
278                         return NULL;
279                 }
280                 *t++ = '\0';
281                 if (ipn.in_redir == NAT_REDIRECT) {
282                         if (hostnum((u_32_t *)&ipn.in_outip, s, linenum) == -1){
283                                 *status = -1;
284                                 return NULL;
285                         }
286                         if (genmask(t, (u_32_t *)&ipn.in_outmsk) == -1) {
287                                 *status = -1;
288                                 return NULL;
289                         }
290                 } else {
291                         if (hostnum((u_32_t *)&ipn.in_inip, s, linenum) == -1) {
292                                 *status = -1;
293                                 return NULL;
294                         }
295                         if (genmask(t, (u_32_t *)&ipn.in_inmsk) == -1) {
296                                 *status = -1;
297                                 return NULL;
298                         }
299                 }
300                 cpp++;
301                 if (!*cpp) {
302                         fprintf(stderr, "%d: short line\n", linenum);
303                         *status = -1;
304                         return NULL;
305                 }
306         }
307
308         /*
309          * If it is a standard redirect then we expect it to have a port
310          * match after the hostmask.
311          */
312         if ((ipn.in_redir == NAT_REDIRECT) && !(ipn.in_flags & IPN_FILTER)) {
313                 if (strcasecmp(*cpp, "port")) {
314                         fprintf(stderr, "%d: missing fields - 1st port\n",
315                                 linenum);
316                         *status = -1;
317                         return NULL;
318                 }
319
320                 cpp++;
321
322                 if (!*cpp) {
323                         fprintf(stderr,
324                                 "%d: missing fields (destination port)\n",
325                                 linenum);
326                         *status = -1;
327                         return NULL;
328                 }
329
330                 if (isdigit(**cpp) && (s = strchr(*cpp, '-')))
331                         *s++ = '\0';
332                 else
333                         s = NULL;
334
335                 port1a = *cpp++;
336
337                 if (!strcmp(*cpp, "-")) {
338                         cpp++;
339                         s = *cpp++;
340                 }
341
342                 if (s)
343                         port1b = s;
344                 else
345                         ipn.in_pmax = ipn.in_pmin;
346         }
347
348         /*
349          * In the middle of the NAT rule syntax is -> to indicate the
350          * direction of translation.
351          */
352         if (!*cpp) {
353                 fprintf(stderr, "%d: missing fields (->)\n", linenum);
354                 *status = -1;
355                 return NULL;
356         }
357         if (strcmp(*cpp, "->")) {
358                 fprintf(stderr, "%d: missing ->\n", linenum);
359                 *status = -1;
360                 return NULL;
361         }
362         cpp++;
363
364         if (!*cpp) {
365                 fprintf(stderr, "%d: missing fields (%s)\n",
366                         linenum, ipn.in_redir ? "destination" : "target");
367                 *status = -1;
368                 return NULL;
369         }
370
371         if (ipn.in_redir == NAT_MAP) {
372                 if (!strcasecmp(*cpp, "range")) {
373                         cpp++;
374                         ipn.in_flags |= IPN_IPRANGE;
375                         if (!*cpp) {
376                                 fprintf(stderr, "%d: missing fields (%s)\n",
377                                         linenum,
378                                         ipn.in_redir ? "destination":"target");
379                                 *status = -1;
380                                 return NULL;
381                         }
382                 }
383         }
384
385         if (ipn.in_flags & IPN_IPRANGE) {
386                 dnetm = strrchr(*cpp, '-');
387                 if (dnetm == NULL) {
388                         cpp++;
389                         if (*cpp && !strcmp(*cpp, "-") && *(cpp + 1))
390                                         dnetm = *(cpp + 1);
391                 } else
392                         *dnetm++ = '\0';
393                 if (dnetm == NULL || *dnetm == '\0') {
394                         fprintf(stderr,
395                                 "%d: desination range not specified\n",
396                                 linenum);
397                         *status = -1;
398                         return NULL;
399                 }
400         } else if (ipn.in_redir != NAT_REDIRECT) {
401                 dnetm = strrchr(*cpp, '/');
402                 if (dnetm == NULL) {
403                         cpp++;
404                         if (*cpp && !strcasecmp(*cpp, "netmask"))
405                                 dnetm = *++cpp;
406                 }
407                 if (dnetm == NULL) {
408                         fprintf(stderr,
409                                 "%d: missing fields (dest netmask)\n",
410                                 linenum);
411                         *status = -1;
412                         return NULL;
413                 }
414                 if (*dnetm == '/')
415                         *dnetm++ = '\0';
416         }
417
418         if (ipn.in_redir == NAT_REDIRECT) {
419                 dnetm = strchr(*cpp, ',');
420                 if (dnetm != NULL) {
421                         ipn.in_flags |= IPN_SPLIT;
422                         *dnetm++ = '\0';
423                 }
424                 if (hostnum((u_32_t *)&ipn.in_inip, *cpp, linenum) == -1) {
425                         *status = -1;
426                         return NULL;
427                 }
428 #if SOLARIS
429                 if (ntohl(ipn.in_inip) == INADDR_LOOPBACK) {
430                         fprintf(stderr,
431                                 "localhost as destination not supported\n");
432                         *status = -1;
433                         return NULL;
434                 }
435 #endif
436         } else {
437                 if (!strcmp(*cpp, ipn.in_ifname))
438                         *cpp = "0";
439                 if (hostnum((u_32_t *)&ipn.in_outip, *cpp, linenum) == -1) {
440                         *status = -1;
441                         return NULL;
442                 }
443         }
444         cpp++;
445
446         if (ipn.in_redir & NAT_MAPBLK) {
447                 if (*cpp) {
448                         if (strcasecmp(*cpp, "ports")) {
449                                 fprintf(stderr,
450                                         "%d: expected \"ports\" - got \"%s\"\n",
451                                         linenum, *cpp);
452                                 *status = -1;
453                                 return NULL;
454                         }
455                         cpp++;
456                         if (*cpp == NULL) {
457                                 fprintf(stderr,
458                                         "%d: missing argument to \"ports\"\n",
459                                         linenum);
460                                 *status = -1;
461                                 return NULL;
462                         }
463                         if (!strcasecmp(*cpp, "auto"))
464                                 ipn.in_flags |= IPN_AUTOPORTMAP;
465                         else
466                                 ipn.in_pmin = atoi(*cpp);
467                         cpp++;
468                 } else
469                         ipn.in_pmin = 0;
470         } else if ((ipn.in_redir & NAT_BIMAP) == NAT_REDIRECT) {
471                 if (*cpp && (strrchr(*cpp, '/') != NULL)) {
472                         fprintf(stderr, "%d: No netmask supported in %s\n",
473                                 linenum, "destination host for redirect");
474                         *status = -1;
475                         return NULL;
476                 }
477
478                 if (!*cpp) {
479                         fprintf(stderr, "%d: Missing destination port %s\n",
480                                 linenum, "in redirect");
481                         *status = -1;
482                         return NULL;
483                 }
484
485                 /* If it's a in_redir, expect target port */
486
487                 if (strcasecmp(*cpp, "port")) {
488                         fprintf(stderr, "%d: missing fields - 2nd port (%s)\n",
489                                 linenum, *cpp);
490                         *status = -1;
491                         return NULL;
492                 }
493                 cpp++;
494                 if (!*cpp) {
495                         fprintf(stderr,
496                                 "%d: missing fields (destination port)\n",
497                                 linenum);
498                         *status = -1;
499                         return NULL;
500                 }
501
502                 port2a = *cpp++;
503         } 
504         if (dnetm && *dnetm == '/')
505                 *dnetm++ = '\0';
506
507         if (ipn.in_redir & (NAT_MAP|NAT_MAPBLK)) {
508                 if (ipn.in_flags & IPN_IPRANGE) {
509                         if (hostnum((u_32_t *)&ipn.in_outmsk, dnetm,
510                                     linenum) == -1) {
511                                 *status = -1;
512                                 return NULL;
513                         }
514                 } else if (genmask(dnetm, (u_32_t *)&ipn.in_outmsk)) {
515                         *status = -1;
516                         return NULL;
517                 }
518         } else {
519                 if (ipn.in_flags & IPN_SPLIT) {
520                         if (hostnum((u_32_t *)&ipn.in_inmsk, dnetm,
521                                     linenum) == -1) {
522                                 *status = -1;
523                                 return NULL;
524                         }
525                 } else if (genmask("255.255.255.255", (u_32_t *)&ipn.in_inmsk)){
526                         *status = -1;
527                         return NULL;
528                 }
529                 if (!*cpp) {
530                         ipn.in_flags |= IPN_TCP; /* XXX- TCP only by default */
531                         proto = "tcp";
532                 } else {
533                         proto = *cpp++;
534                         if (!strcasecmp(proto, "tcp"))
535                                 ipn.in_flags |= IPN_TCP;
536                         else if (!strcasecmp(proto, "udp"))
537                                 ipn.in_flags |= IPN_UDP;
538                         else if (!strcasecmp(proto, "tcp/udp"))
539                                 ipn.in_flags |= IPN_TCPUDP;
540                         else if (!strcasecmp(proto, "tcpudp")) {
541                                 ipn.in_flags |= IPN_TCPUDP;
542                                 proto = "tcp/udp";
543                         } else if (!strcasecmp(proto, "ip"))
544                                 ipn.in_flags |= IPN_ANY;
545                         else {
546                                 ipn.in_flags |= IPN_ANY;
547                                 if ((pr = getprotobyname(proto)))
548                                         ipn.in_p = pr->p_proto;
549                                 else {
550                                         if (!isdigit(*proto)) {
551                                                 fprintf(stderr,
552                                                 "%d: Unknown protocol %s\n",
553                                                         linenum, proto);
554                                                 *status = -1;
555                                                 return NULL;
556                                         } else
557                                                 ipn.in_p = atoi(proto);
558                                 }
559                         }
560                         if ((ipn.in_flags & IPN_TCPUDP) == 0) {
561                                 port1a = "0";
562                                 port2a = "0";
563                         }
564
565                         if (*cpp && !strcasecmp(*cpp, "round-robin")) {
566                                 cpp++;
567                                 ipn.in_flags |= IPN_ROUNDR;
568                         }
569
570                         if (*cpp && !strcasecmp(*cpp, "frag")) {
571                                 cpp++;
572                                 ipn.in_flags |= IPN_FRAG;
573                         }
574
575                         if (*cpp && !strcasecmp(*cpp, "age")) {
576                                 cpp++;
577                                 if (!*cpp) {
578                                         fprintf(stderr,
579                                                 "%d: age with no parameters\n",
580                                                 linenum);
581                                         *status = -1;
582                                         return NULL;
583                                 }
584
585                                 ipn.in_age[0] = atoi(*cpp);
586                                 s = index(*cpp, '/');
587                                 if (s != NULL)
588                                         ipn.in_age[1] = atoi(s + 1);
589                                 else
590                                         ipn.in_age[1] = ipn.in_age[0];
591                                 cpp++;
592                         }
593
594                         if (*cpp && !strcasecmp(*cpp, "mssclamp")) {
595                                 cpp++;
596                                 if (*cpp) {
597                                         ipn.in_mssclamp = atoi(*cpp);
598                                         cpp++;
599                                 } else {
600                                         fprintf(stderr,
601                                            "%d: mssclamp with no parameters\n",
602                                                 linenum);
603                                         *status = -1;
604                                         return NULL;
605                                 }
606                         }
607
608                         if (*cpp) {
609                                 fprintf(stderr,
610                                 "%d: extra junk at the end of the line: %s\n",
611                                         linenum, *cpp);
612                                 *status = -1;
613                                 return NULL;
614                         }
615                 }
616         }
617
618         if ((ipn.in_redir == NAT_REDIRECT) && !(ipn.in_flags & IPN_FILTER)) {
619                 if (!portnum(port1a, &ipn.in_pmin, linenum)) {
620                         *status = -1;
621                         return NULL;
622                 }
623                 ipn.in_pmin = htons(ipn.in_pmin);
624                 if (port1b != NULL) {
625                         if (!portnum(port1b, &ipn.in_pmax, linenum)) {
626                                 *status = -1;
627                                 return NULL;
628                         }
629                         ipn.in_pmax = htons(ipn.in_pmax);
630                 } else
631                         ipn.in_pmax = ipn.in_pmin;
632         }
633
634         if ((ipn.in_redir & NAT_BIMAP) == NAT_REDIRECT) {
635                 if (!portnum(port2a, &ipn.in_pnext, linenum)) {
636                         *status = -1;
637                         return NULL;
638                 }
639                 ipn.in_pnext = htons(ipn.in_pnext);
640         }
641
642         if (!(ipn.in_flags & IPN_SPLIT))
643                 ipn.in_inip &= ipn.in_inmsk;
644         if ((ipn.in_flags & IPN_IPRANGE) == 0)
645                 ipn.in_outip &= ipn.in_outmsk;
646         ipn.in_srcip &= ipn.in_srcmsk;
647
648         if ((ipn.in_redir & NAT_MAPBLK) != 0)
649                 nat_setgroupmap(&ipn);
650
651         if (*cpp && !*(cpp+1) && !strcasecmp(*cpp, "frag")) {
652                 cpp++;
653                 ipn.in_flags |= IPN_FRAG;
654         }
655
656         if (!*cpp) {
657                 *status = 0;
658                 return &ipn;
659         }
660
661         if (ipn.in_redir != NAT_BIMAP && !strcasecmp(*cpp, "proxy")) {
662                 u_short pport;
663
664                 if (ipn.in_redir == NAT_BIMAP) {
665                         fprintf(stderr, "%d: cannot use proxy with bimap\n",
666                                 linenum);
667                         *status = -1;
668                         return NULL;
669                 }
670                 cpp++;
671                 if (!*cpp) {
672                         fprintf(stderr,
673                                 "%d: missing parameter for \"proxy\"\n",
674                                 linenum);
675                         *status = -1;
676                         return NULL;
677                 }
678                 dport = NULL;
679
680                 if (!strcasecmp(*cpp, "port")) {
681                         cpp++;
682                         if (!*cpp) {
683                                 fprintf(stderr,
684                                         "%d: missing parameter for \"port\"\n",
685                                         linenum);
686                                 *status = -1;
687                                 return NULL;
688                         }
689
690                         dport = *cpp;
691                         cpp++;
692
693                         if (!*cpp) {
694                                 fprintf(stderr,
695                                         "%d: missing parameter for \"proxy\"\n",
696                                         linenum);
697                                 *status = -1;
698                                 return NULL;
699                         }
700                 } else {
701                         fprintf(stderr,
702                                 "%d: missing keyword \"port\"\n", linenum);
703                         *status = -1;
704                         return NULL;
705                 }
706
707                 if ((proto = index(*cpp, '/'))) {
708                         *proto++ = '\0';
709                         if ((pr = getprotobyname(proto)))
710                                 ipn.in_p = pr->p_proto;
711                         else
712                                 ipn.in_p = atoi(proto);
713                 } else
714                         ipn.in_p = 0;
715
716                 if (dport && !portnum(dport, &pport, linenum))
717                         return NULL;
718                 if (ipn.in_dcmp != 0) {
719                         if (pport != ipn.in_dport) {
720                                 fprintf(stderr,
721                                         "%d: mismatch in port numbers\n",
722                                         linenum);
723                                 return NULL;
724                         }
725                 } else
726                         ipn.in_dport = htons(pport);
727
728                 (void) strncpy(ipn.in_plabel, *cpp, sizeof(ipn.in_plabel));
729                 cpp++;
730
731         } else if (ipn.in_redir != NAT_BIMAP && !strcasecmp(*cpp, "portmap")) {
732                 if (ipn.in_redir == NAT_BIMAP) {
733                         fprintf(stderr, "%d: cannot use portmap with bimap\n",
734                                 linenum);
735                         *status = -1;
736                         return NULL;
737                 }
738                 cpp++;
739                 if (!*cpp) {
740                         fprintf(stderr,
741                                 "%d: missing expression following portmap\n",
742                                 linenum);
743                         *status = -1;
744                         return NULL;
745                 }
746
747                 if (!strcasecmp(*cpp, "tcp"))
748                         ipn.in_flags |= IPN_TCP;
749                 else if (!strcasecmp(*cpp, "udp"))
750                         ipn.in_flags |= IPN_UDP;
751                 else if (!strcasecmp(*cpp, "tcpudp"))
752                         ipn.in_flags |= IPN_TCPUDP;
753                 else if (!strcasecmp(*cpp, "tcp/udp"))
754                         ipn.in_flags |= IPN_TCPUDP;
755                 else {
756                         fprintf(stderr,
757                                 "%d: expected protocol name - got \"%s\"\n",
758                                 linenum, *cpp);
759                         *status = -1;
760                         return NULL;
761                 }
762                 proto = *cpp;
763                 cpp++;
764
765                 if (!*cpp) {
766                         fprintf(stderr, "%d: no port range found\n", linenum);
767                         *status = -1;
768                         return NULL;
769                 }
770
771                 if (!strcasecmp(*cpp, "auto")) {
772                         ipn.in_flags |= IPN_AUTOPORTMAP;
773                         ipn.in_pmin = htons(1024);
774                         ipn.in_pmax = htons(65535);
775                         nat_setgroupmap(&ipn);
776                         cpp++;
777                 } else {
778                         if (!(t = strchr(*cpp, ':'))) {
779                                 fprintf(stderr,
780                                         "%d: no port range in \"%s\"\n",
781                                         linenum, *cpp);
782                                 *status = -1;
783                                 return NULL;
784                         }
785                         *t++ = '\0';
786                         if (!portnum(*cpp, &ipn.in_pmin, linenum) ||
787                             !portnum(t, &ipn.in_pmax, linenum)) {
788                                 *status = -1;
789                                 return NULL;
790                         }
791                         ipn.in_pmin = htons(ipn.in_pmin);
792                         ipn.in_pmax = htons(ipn.in_pmax);
793                         cpp++;
794                 }
795         }
796
797         if (*cpp && !strcasecmp(*cpp, "frag")) {
798                 cpp++;
799                 ipn.in_flags |= IPN_FRAG;
800         }
801
802         if (*cpp && !strcasecmp(*cpp, "age")) {
803                 cpp++;
804                 if (!*cpp) {
805                         fprintf(stderr, "%d: age with no parameters\n",
806                                 linenum);
807                         *status = -1;
808                         return NULL;
809                 }
810                 ipn.in_age[0] = atoi(*cpp);
811                 s = index(*cpp, '/');
812                 if (s != NULL)
813                         ipn.in_age[1] = atoi(s + 1);
814                 else
815                         ipn.in_age[1] = ipn.in_age[0];
816                 cpp++;
817         }
818
819         if (*cpp && !strcasecmp(*cpp, "mssclamp")) {
820                 cpp++;
821                 if (*cpp) {
822                         ipn.in_mssclamp = atoi(*cpp);
823                         cpp++;
824                 } else {
825                         fprintf(stderr, "%d: mssclamp with no parameters\n",
826                                 linenum);
827                         *status = -1;
828                         return NULL;
829                 }
830         }
831
832         if (*cpp) {
833                 fprintf(stderr, "%d: extra junk at the end of the line: %s\n",
834                         linenum, *cpp);
835                 *status = -1;
836                 return NULL;
837         }
838
839         *status = 0;
840         return &ipn;
841 }
842
843
844 void natparsefile(fd, file, opts)
845 int fd;
846 char *file;
847 int opts;
848 {
849         char    line[512], *s;
850         ipnat_t *np;
851         FILE    *fp;
852         int     linenum = 0;
853         int     parsestatus;
854
855         if (strcmp(file, "-")) {
856                 if (!(fp = fopen(file, "r"))) {
857                         fprintf(stderr, "%s: open: %s\n", file,
858                                 STRERROR(errno));
859                         exit(1);
860                 }
861         } else
862                 fp = stdin;
863
864         while (fgets(line, sizeof(line) - 1, fp)) {
865                 linenum++;
866                 line[sizeof(line) - 1] = '\0';
867                 if ((s = strchr(line, '\n')))
868                         *s = '\0';
869
870                 parsestatus = 1;
871                 np = natparse(line, linenum, &parsestatus);
872                 if (parsestatus != 0) {
873                         if (*line) {
874                                 fprintf(stderr, "%d: syntax error in \"%s\"\n",
875                                         linenum, line);
876                         }
877                         fprintf(stderr, "%s: %s error (%d), quitting\n",
878                             file,
879                             ((parsestatus < 0)? "parse": "internal"),
880                             parsestatus);
881                         exit(1);
882                 }
883                 if (np) {
884                         if ((opts & OPT_VERBOSE) && np)
885                                 printnat(np, opts);
886                         if (!(opts & OPT_NODO)) {
887                                 if (!(opts & OPT_REMOVE)) {
888                                         if (ioctl(fd, SIOCADNAT, &np) == -1) {
889                                                 fprintf(stderr, "%d:",
890                                                         linenum);
891                                                 perror("ioctl(SIOCADNAT)");
892                                         }
893                                 } else if (ioctl(fd, SIOCRMNAT, &np) == -1) {
894                                         fprintf(stderr, "%d:", linenum);
895                                         perror("ioctl(SIOCRMNAT)");
896                                 }
897                         }
898                 }
899         }
900         if (fp != stdin)
901                 fclose(fp);
902 }