Merge from vendor branch LIBSTDC++:
[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.27 2002/12/06 11:40:27 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));
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 ipnat_t *natparse(line, linenum)
103 char *line;
104 int linenum;
105 {
106         static ipnat_t ipn;
107         struct protoent *pr;
108         char *dnetm = NULL, *dport = NULL;
109         char *s, *t, *cps[31], **cpp;
110         int i, cnt;
111         char *port1a = NULL, *port1b = NULL, *port2a = NULL;
112
113         proto = NULL;
114
115         /*
116          * Search for end of line and comment marker, advance of leading spaces
117          */
118         if ((s = strchr(line, '\n')))
119                 *s = '\0';
120         if ((s = strchr(line, '#')))
121                 *s = '\0';
122         while (*line && isspace(*line))
123                 line++;
124         if (!*line)
125                 return NULL;
126
127         bzero((char *)&ipn, sizeof(ipn));
128         cnt = 0;
129
130         /*
131          * split line upto into segments.
132          */
133         for (i = 0, *cps = strtok(line, " \b\t\r\n"); cps[i] && i < 30; cnt++)
134                 cps[++i] = strtok(NULL, " \b\t\r\n");
135
136         cps[i] = NULL;
137
138         if (cnt < 3) {
139                 fprintf(stderr, "%d: not enough segments in line\n", linenum);
140                 return NULL;
141         }
142
143         cpp = cps;
144
145         /*
146          * Check first word is a recognised keyword and then is the interface
147          */
148         if (!strcasecmp(*cpp, "map"))
149                 ipn.in_redir = NAT_MAP;
150         else if (!strcasecmp(*cpp, "map-block"))
151                 ipn.in_redir = NAT_MAPBLK;
152         else if (!strcasecmp(*cpp, "rdr"))
153                 ipn.in_redir = NAT_REDIRECT;
154         else if (!strcasecmp(*cpp, "bimap"))
155                 ipn.in_redir = NAT_BIMAP;
156         else {
157                 fprintf(stderr, "%d: unknown mapping: \"%s\"\n",
158                         linenum, *cpp);
159                 return NULL;
160         }
161
162         cpp++;
163
164         strncpy(ipn.in_ifname, *cpp, sizeof(ipn.in_ifname) - 1);
165         ipn.in_ifname[sizeof(ipn.in_ifname) - 1] = '\0';
166         cpp++;
167
168         /*
169          * If the first word after the interface is "from" or is a ! then
170          * the expanded syntax is being used so parse it differently.
171          */
172         if (!strcasecmp(*cpp, "from") || (**cpp == '!')) {
173                 if (!strcmp(*cpp, "!")) {
174                         cpp++;
175                         if (strcasecmp(*cpp, "from")) {
176                                 fprintf(stderr, "Missing from after !\n");
177                                 return NULL;
178                         }
179                         ipn.in_flags |= IPN_NOTSRC;
180                 } else if (**cpp == '!') {
181                         if (strcasecmp(*cpp + 1, "from")) {
182                                 fprintf(stderr, "Missing from after !\n");
183                                 return NULL;
184                         }
185                         ipn.in_flags |= IPN_NOTSRC;
186                 }
187                 if ((ipn.in_flags & IPN_NOTSRC) &&
188                     (ipn.in_redir & (NAT_MAP|NAT_MAPBLK))) {
189                         fprintf(stderr, "Cannot use '! from' with map\n");
190                         return NULL;
191                 }
192
193                 ipn.in_flags |= IPN_FILTER;
194                 cpp++;
195                 if (ipn.in_redir == NAT_REDIRECT) {
196                         if (hostmask(&cpp, (u_32_t *)&ipn.in_srcip,
197                                      (u_32_t *)&ipn.in_srcmsk, &ipn.in_sport,
198                                      &ipn.in_scmp, &ipn.in_stop, linenum)) {
199                                 return NULL;
200                         }
201                 } else {
202                         if (hostmask(&cpp, (u_32_t *)&ipn.in_inip,
203                                      (u_32_t *)&ipn.in_inmsk, &ipn.in_sport,
204                                      &ipn.in_scmp, &ipn.in_stop, linenum)) {
205                                 return NULL;
206                         }
207                 }
208
209                 if (!strcmp(*cpp, "!")) {
210                         cpp++;
211                         ipn.in_flags |= IPN_NOTDST;
212                 } else if (**cpp == '!') {
213                         (*cpp)++;
214                         ipn.in_flags |= IPN_NOTDST;
215                 }
216
217                 if (strcasecmp(*cpp, "to")) {
218                         fprintf(stderr, "%d: unexpected keyword (%s) - to\n",
219                                 linenum, *cpp);
220                         return NULL;
221                 }
222                 if ((ipn.in_flags & IPN_NOTDST) &&
223                     (ipn.in_redir & (NAT_REDIRECT))) {
224                         fprintf(stderr, "Cannot use '! to' with rdr\n");
225                         return NULL;
226                 }
227
228                 if (!*++cpp) {
229                         fprintf(stderr, "%d: missing host after to\n", linenum);
230                         return NULL;
231                 }
232                 if (ipn.in_redir == NAT_REDIRECT) {
233                         if (hostmask(&cpp, (u_32_t *)&ipn.in_outip,
234                                      (u_32_t *)&ipn.in_outmsk, &ipn.in_dport,
235                                      &ipn.in_dcmp, &ipn.in_dtop, linenum)) {
236                                 return NULL;
237                         }
238                         ipn.in_pmin = htons(ipn.in_dport);
239                 } else {
240                         if (hostmask(&cpp, (u_32_t *)&ipn.in_srcip,
241                                      (u_32_t *)&ipn.in_srcmsk, &ipn.in_dport,
242                                      &ipn.in_dcmp, &ipn.in_dtop, linenum)) {
243                                 return NULL;
244                         }
245                 }
246         } else {
247                 s = *cpp;
248                 if (!s) {
249                         fprintf(stderr, "%d: short line\n", linenum);
250                         return NULL;
251                 }
252                 t = strchr(s, '/');
253                 if (!t) {
254                         fprintf(stderr, "%d: no netmask on LHS\n", linenum);
255                         return NULL;
256                 }
257                 *t++ = '\0';
258                 if (ipn.in_redir == NAT_REDIRECT) {
259                         if (hostnum((u_32_t *)&ipn.in_outip, s, linenum) == -1)
260                                 return NULL;
261                         if (genmask(t, (u_32_t *)&ipn.in_outmsk) == -1) {
262                                 return NULL;
263                         }
264                 } else {
265                         if (hostnum((u_32_t *)&ipn.in_inip, s, linenum) == -1)
266                                 return NULL;
267                         if (genmask(t, (u_32_t *)&ipn.in_inmsk) == -1) {
268                                 return NULL;
269                         }
270                 }
271                 cpp++;
272                 if (!*cpp) {
273                         fprintf(stderr, "%d: short line\n", linenum);
274                         return NULL;
275                 }
276         }
277
278         /*
279          * If it is a standard redirect then we expect it to have a port
280          * match after the hostmask.
281          */
282         if ((ipn.in_redir == NAT_REDIRECT) && !(ipn.in_flags & IPN_FILTER)) {
283                 if (strcasecmp(*cpp, "port")) {
284                         fprintf(stderr, "%d: missing fields - 1st port\n",
285                                 linenum);
286                         return NULL;
287                 }
288
289                 cpp++;
290
291                 if (!*cpp) {
292                         fprintf(stderr,
293                                 "%d: missing fields (destination port)\n",
294                                 linenum);
295                         return NULL;
296                 }
297
298                 if (isdigit(**cpp) && (s = strchr(*cpp, '-')))
299                         *s++ = '\0';
300                 else
301                         s = NULL;
302
303                 port1a = *cpp++;
304
305                 if (!strcmp(*cpp, "-")) {
306                         cpp++;
307                         s = *cpp++;
308                 }
309
310                 if (s)
311                         port1b = s;
312                 else
313                         ipn.in_pmax = ipn.in_pmin;
314         }
315
316         /*
317          * In the middle of the NAT rule syntax is -> to indicate the
318          * direction of translation.
319          */
320         if (!*cpp) {
321                 fprintf(stderr, "%d: missing fields (->)\n", linenum);
322                 return NULL;
323         }
324         if (strcmp(*cpp, "->")) {
325                 fprintf(stderr, "%d: missing ->\n", linenum);
326                 return NULL;
327         }
328         cpp++;
329
330         if (!*cpp) {
331                 fprintf(stderr, "%d: missing fields (%s)\n",
332                         linenum, ipn.in_redir ? "destination" : "target");
333                 return NULL;
334         }
335
336         if (ipn.in_redir == NAT_MAP) {
337                 if (!strcasecmp(*cpp, "range")) {
338                         cpp++;
339                         ipn.in_flags |= IPN_IPRANGE;
340                         if (!*cpp) {
341                                 fprintf(stderr, "%d: missing fields (%s)\n",
342                                         linenum,
343                                         ipn.in_redir ? "destination":"target");
344                                 return NULL;
345                         }
346                 }
347         }
348
349         if (ipn.in_flags & IPN_IPRANGE) {
350                 dnetm = strrchr(*cpp, '-');
351                 if (dnetm == NULL) {
352                         cpp++;
353                         if (*cpp && !strcmp(*cpp, "-") && *(cpp + 1))
354                                         dnetm = *(cpp + 1);
355                 } else
356                         *dnetm++ = '\0';
357                 if (dnetm == NULL || *dnetm == '\0') {
358                         fprintf(stderr,
359                                 "%d: desination range not specified\n",
360                                 linenum);
361                         return NULL;
362                 }
363         } else if (ipn.in_redir != NAT_REDIRECT) {
364                 dnetm = strrchr(*cpp, '/');
365                 if (dnetm == NULL) {
366                         cpp++;
367                         if (*cpp && !strcasecmp(*cpp, "netmask"))
368                                 dnetm = *++cpp;
369                 }
370                 if (dnetm == NULL) {
371                         fprintf(stderr,
372                                 "%d: missing fields (dest netmask)\n",
373                                 linenum);
374                         return NULL;
375                 }
376                 if (*dnetm == '/')
377                         *dnetm++ = '\0';
378         }
379
380         if (ipn.in_redir == NAT_REDIRECT) {
381                 dnetm = strchr(*cpp, ',');
382                 if (dnetm != NULL) {
383                         ipn.in_flags |= IPN_SPLIT;
384                         *dnetm++ = '\0';
385                 }
386                 if (hostnum((u_32_t *)&ipn.in_inip, *cpp, linenum) == -1)
387                         return NULL;
388 #if SOLARIS
389                 if (ntohl(ipn.in_inip) == INADDR_LOOPBACK) {
390                         fprintf(stderr,
391                                 "localhost as destination not supported\n");
392                         return NULL;
393                 }
394 #endif
395         } else {
396                 if (!strcmp(*cpp, ipn.in_ifname))
397                         *cpp = "0";
398                 if (hostnum((u_32_t *)&ipn.in_outip, *cpp, linenum) == -1)
399                         return NULL;
400         }
401         cpp++;
402
403         if (ipn.in_redir & NAT_MAPBLK) {
404                 if (*cpp) {
405                         if (strcasecmp(*cpp, "ports")) {
406                                 fprintf(stderr,
407                                         "%d: expected \"ports\" - got \"%s\"\n",
408                                         linenum, *cpp);
409                                 return NULL;
410                         }
411                         cpp++;
412                         if (*cpp == NULL) {
413                                 fprintf(stderr,
414                                         "%d: missing argument to \"ports\"\n",
415                                         linenum);
416                                 return NULL;
417                         }
418                         if (!strcasecmp(*cpp, "auto"))
419                                 ipn.in_flags |= IPN_AUTOPORTMAP;
420                         else
421                                 ipn.in_pmin = atoi(*cpp);
422                         cpp++;
423                 } else
424                         ipn.in_pmin = 0;
425         } else if ((ipn.in_redir & NAT_BIMAP) == NAT_REDIRECT) {
426                 if (*cpp && (strrchr(*cpp, '/') != NULL)) {
427                         fprintf(stderr, "%d: No netmask supported in %s\n",
428                                 linenum, "destination host for redirect");
429                         return NULL;
430                 }
431
432                 if (!*cpp) {
433                         fprintf(stderr, "%d: Missing destination port %s\n",
434                                 linenum, "in redirect");
435                         return NULL;
436                 }
437
438                 /* If it's a in_redir, expect target port */
439
440                 if (strcasecmp(*cpp, "port")) {
441                         fprintf(stderr, "%d: missing fields - 2nd port (%s)\n",
442                                 linenum, *cpp);
443                         return NULL;
444                 }
445                 cpp++;
446                 if (!*cpp) {
447                         fprintf(stderr,
448                                 "%d: missing fields (destination port)\n",
449                                 linenum);
450                         return NULL;
451                 }
452
453                 port2a = *cpp++;
454         } 
455         if (dnetm && *dnetm == '/')
456                 *dnetm++ = '\0';
457
458         if (ipn.in_redir & (NAT_MAP|NAT_MAPBLK)) {
459                 if (ipn.in_flags & IPN_IPRANGE) {
460                         if (hostnum((u_32_t *)&ipn.in_outmsk, dnetm,
461                                     linenum) == -1)
462                                 return NULL;
463                 } else if (genmask(dnetm, (u_32_t *)&ipn.in_outmsk))
464                         return NULL;
465         } else {
466                 if (ipn.in_flags & IPN_SPLIT) {
467                         if (hostnum((u_32_t *)&ipn.in_inmsk, dnetm,
468                                     linenum) == -1)
469                                 return NULL;
470                 } else if (genmask("255.255.255.255", (u_32_t *)&ipn.in_inmsk))
471                         return NULL;
472                 if (!*cpp) {
473                         ipn.in_flags |= IPN_TCP; /* XXX- TCP only by default */
474                         proto = "tcp";
475                 } else {
476                         proto = *cpp++;
477                         if (!strcasecmp(proto, "tcp"))
478                                 ipn.in_flags |= IPN_TCP;
479                         else if (!strcasecmp(proto, "udp"))
480                                 ipn.in_flags |= IPN_UDP;
481                         else if (!strcasecmp(proto, "tcp/udp"))
482                                 ipn.in_flags |= IPN_TCPUDP;
483                         else if (!strcasecmp(proto, "tcpudp")) {
484                                 ipn.in_flags |= IPN_TCPUDP;
485                                 proto = "tcp/udp";
486                         } else if (!strcasecmp(proto, "ip"))
487                                 ipn.in_flags |= IPN_ANY;
488                         else {
489                                 ipn.in_flags |= IPN_ANY;
490                                 if ((pr = getprotobyname(proto)))
491                                         ipn.in_p = pr->p_proto;
492                                 else {
493                                         if (!isdigit(*proto)) {
494                                                 fprintf(stderr,
495                                                 "%d: Unknown protocol %s\n",
496                                                         linenum, proto);
497                                                 return NULL;
498                                         } else
499                                                 ipn.in_p = atoi(proto);
500                                 }
501                         }
502                         if ((ipn.in_flags & IPN_TCPUDP) == 0) {
503                                 port1a = "0";
504                                 port2a = "0";
505                         }
506
507                         if (*cpp && !strcasecmp(*cpp, "round-robin")) {
508                                 cpp++;
509                                 ipn.in_flags |= IPN_ROUNDR;
510                         }
511
512                         if (*cpp && !strcasecmp(*cpp, "frag")) {
513                                 cpp++;
514                                 ipn.in_flags |= IPN_FRAG;
515                         }
516
517                         if (*cpp && !strcasecmp(*cpp, "age")) {
518                                 cpp++;
519                                 if (!*cpp) {
520                                         fprintf(stderr,
521                                                 "%d: age with no parameters\n",
522                                                 linenum);
523                                         return NULL;
524                                 }
525
526                                 ipn.in_age[0] = atoi(*cpp);
527                                 s = index(*cpp, '/');
528                                 if (s != NULL)
529                                         ipn.in_age[1] = atoi(s + 1);
530                                 else
531                                         ipn.in_age[1] = ipn.in_age[0];
532                                 cpp++;
533                         }
534
535                         if (*cpp && !strcasecmp(*cpp, "mssclamp")) {
536                                 cpp++;
537                                 if (*cpp) {
538                                         ipn.in_mssclamp = atoi(*cpp);
539                                         cpp++;
540                                 } else {
541                                         fprintf(stderr,
542                                            "%d: mssclamp with no parameters\n",
543                                                 linenum);
544                                         return NULL;
545                                 }
546                         }
547
548                         if (*cpp) {
549                                 fprintf(stderr,
550                                 "%d: extra junk at the end of the line: %s\n",
551                                         linenum, *cpp);
552                                 return NULL;
553                         }
554                 }
555         }
556
557         if ((ipn.in_redir == NAT_REDIRECT) && !(ipn.in_flags & IPN_FILTER)) {
558                 if (!portnum(port1a, &ipn.in_pmin, linenum))
559                         return NULL;
560                 ipn.in_pmin = htons(ipn.in_pmin);
561                 if (port1b != NULL) {
562                         if (!portnum(port1b, &ipn.in_pmax, linenum))
563                                 return NULL;
564                         ipn.in_pmax = htons(ipn.in_pmax);
565                 } else
566                         ipn.in_pmax = ipn.in_pmin;
567         }
568
569         if ((ipn.in_redir & NAT_BIMAP) == NAT_REDIRECT) {
570                 if (!portnum(port2a, &ipn.in_pnext, linenum))
571                         return NULL;
572                 ipn.in_pnext = htons(ipn.in_pnext);
573         }
574
575         if (!(ipn.in_flags & IPN_SPLIT))
576                 ipn.in_inip &= ipn.in_inmsk;
577         if ((ipn.in_flags & IPN_IPRANGE) == 0)
578                 ipn.in_outip &= ipn.in_outmsk;
579         ipn.in_srcip &= ipn.in_srcmsk;
580
581         if ((ipn.in_redir & NAT_MAPBLK) != 0)
582                 nat_setgroupmap(&ipn);
583
584         if (*cpp && !*(cpp+1) && !strcasecmp(*cpp, "frag")) {
585                 cpp++;
586                 ipn.in_flags |= IPN_FRAG;
587         }
588
589         if (!*cpp)
590                 return &ipn;
591
592         if (ipn.in_redir != NAT_BIMAP && !strcasecmp(*cpp, "proxy")) {
593                 if (ipn.in_redir == NAT_BIMAP) {
594                         fprintf(stderr, "%d: cannot use proxy with bimap\n",
595                                 linenum);
596                         return NULL;
597                 }
598                 cpp++;
599                 if (!*cpp) {
600                         fprintf(stderr,
601                                 "%d: missing parameter for \"proxy\"\n",
602                                 linenum);
603                         return NULL;
604                 }
605                 dport = NULL;
606
607                 if (!strcasecmp(*cpp, "port")) {
608                         cpp++;
609                         if (!*cpp) {
610                                 fprintf(stderr,
611                                         "%d: missing parameter for \"port\"\n",
612                                         linenum);
613                                 return NULL;
614                         }
615
616                         dport = *cpp;
617                         cpp++;
618
619                         if (!*cpp) {
620                                 fprintf(stderr,
621                                         "%d: missing parameter for \"proxy\"\n",
622                                         linenum);
623                                 return NULL;
624                         }
625                 } else {
626                         fprintf(stderr,
627                                 "%d: missing keyword \"port\"\n", linenum);
628                         return NULL;
629                 }
630
631                 if ((proto = index(*cpp, '/'))) {
632                         *proto++ = '\0';
633                         if ((pr = getprotobyname(proto)))
634                                 ipn.in_p = pr->p_proto;
635                         else
636                                 ipn.in_p = atoi(proto);
637                 } else
638                         ipn.in_p = 0;
639
640                 if (dport && !portnum(dport, &ipn.in_dport, linenum))
641                         return NULL;
642                 ipn.in_dport = htons(ipn.in_dport);
643
644                 (void) strncpy(ipn.in_plabel, *cpp, sizeof(ipn.in_plabel));
645                 cpp++;
646
647         } else if (ipn.in_redir != NAT_BIMAP && !strcasecmp(*cpp, "portmap")) {
648                 if (ipn.in_redir == NAT_BIMAP) {
649                         fprintf(stderr, "%d: cannot use portmap with bimap\n",
650                                 linenum);
651                         return NULL;
652                 }
653                 cpp++;
654                 if (!*cpp) {
655                         fprintf(stderr,
656                                 "%d: missing expression following portmap\n",
657                                 linenum);
658                         return NULL;
659                 }
660
661                 if (!strcasecmp(*cpp, "tcp"))
662                         ipn.in_flags |= IPN_TCP;
663                 else if (!strcasecmp(*cpp, "udp"))
664                         ipn.in_flags |= IPN_UDP;
665                 else if (!strcasecmp(*cpp, "tcpudp"))
666                         ipn.in_flags |= IPN_TCPUDP;
667                 else if (!strcasecmp(*cpp, "tcp/udp"))
668                         ipn.in_flags |= IPN_TCPUDP;
669                 else {
670                         fprintf(stderr,
671                                 "%d: expected protocol name - got \"%s\"\n",
672                                 linenum, *cpp);
673                         return NULL;
674                 }
675                 proto = *cpp;
676                 cpp++;
677
678                 if (!*cpp) {
679                         fprintf(stderr, "%d: no port range found\n", linenum);
680                         return NULL;
681                 }
682
683                 if (!strcasecmp(*cpp, "auto")) {
684                         ipn.in_flags |= IPN_AUTOPORTMAP;
685                         ipn.in_pmin = htons(1024);
686                         ipn.in_pmax = htons(65535);
687                         nat_setgroupmap(&ipn);
688                         cpp++;
689                 } else {
690                         if (!(t = strchr(*cpp, ':'))) {
691                                 fprintf(stderr,
692                                         "%d: no port range in \"%s\"\n",
693                                         linenum, *cpp);
694                                 return NULL;
695                         }
696                         *t++ = '\0';
697                         if (!portnum(*cpp, &ipn.in_pmin, linenum) ||
698                             !portnum(t, &ipn.in_pmax, linenum))
699                                 return NULL;
700                         ipn.in_pmin = htons(ipn.in_pmin);
701                         ipn.in_pmax = htons(ipn.in_pmax);
702                         cpp++;
703                 }
704         }
705
706         if (*cpp && !strcasecmp(*cpp, "frag")) {
707                 cpp++;
708                 ipn.in_flags |= IPN_FRAG;
709         }
710
711         if (*cpp && !strcasecmp(*cpp, "age")) {
712                 cpp++;
713                 if (!*cpp) {
714                         fprintf(stderr, "%d: age with no parameters\n",
715                                 linenum);
716                         return NULL;
717                 }
718                 ipn.in_age[0] = atoi(*cpp);
719                 s = index(*cpp, '/');
720                 if (s != NULL)
721                         ipn.in_age[1] = atoi(s + 1);
722                 else
723                         ipn.in_age[1] = ipn.in_age[0];
724                 cpp++;
725         }
726
727         if (*cpp && !strcasecmp(*cpp, "mssclamp")) {
728                 cpp++;
729                 if (*cpp) {
730                         ipn.in_mssclamp = atoi(*cpp);
731                         cpp++;
732                 } else {
733                         fprintf(stderr, "%d: mssclamp with no parameters\n",
734                                 linenum);
735                         return NULL;
736                 }
737         }
738
739         if (*cpp) {
740                 fprintf(stderr, "%d: extra junk at the end of the line: %s\n",
741                         linenum, *cpp);
742                 return NULL;
743         }
744         return &ipn;
745 }
746
747
748 void natparsefile(fd, file, opts)
749 int fd;
750 char *file;
751 int opts;
752 {
753         char    line[512], *s;
754         ipnat_t *np;
755         FILE    *fp;
756         int     linenum = 0;
757
758         if (strcmp(file, "-")) {
759                 if (!(fp = fopen(file, "r"))) {
760                         fprintf(stderr, "%s: open: %s\n", file,
761                                 STRERROR(errno));
762                         exit(1);
763                 }
764         } else
765                 fp = stdin;
766
767         while (fgets(line, sizeof(line) - 1, fp)) {
768                 linenum++;
769                 line[sizeof(line) - 1] = '\0';
770                 if ((s = strchr(line, '\n')))
771                         *s = '\0';
772
773                 if (!(np = natparse(line, linenum))) {
774                         if (*line)
775                                 fprintf(stderr, "%d: syntax error in \"%s\"\n",
776                                         linenum, line);
777                 } else {
778                         if ((opts & OPT_VERBOSE) && np)
779                                 printnat(np, opts);
780                         if (!(opts & OPT_NODO)) {
781                                 if (!(opts & OPT_REMOVE)) {
782                                         if (ioctl(fd, SIOCADNAT, &np) == -1) {
783                                                 fprintf(stderr, "%d:",
784                                                         linenum);
785                                                 perror("ioctl(SIOCADNAT)");
786                                         }
787                                 } else if (ioctl(fd, SIOCRMNAT, &np) == -1) {
788                                         fprintf(stderr, "%d:", linenum);
789                                         perror("ioctl(SIOCRMNAT)");
790                                 }
791                         }
792                 }
793         }
794         if (fp != stdin)
795                 fclose(fp);
796 }