]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/ipfilter/natparse.c
This commit was generated by cvs2svn to compensate for changes in r96364,
[FreeBSD/FreeBSD.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 #ifdef __sgi
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.24 2002/04/24 17:30:51 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         } else {
389                 if (!strcmp(*cpp, ipn.in_ifname))
390                         *cpp = "0";
391                 if (hostnum((u_32_t *)&ipn.in_outip, *cpp, linenum) == -1)
392                         return NULL;
393         }
394         cpp++;
395
396         if (ipn.in_redir & NAT_MAPBLK) {
397                 if (*cpp) {
398                         if (strcasecmp(*cpp, "ports")) {
399                                 fprintf(stderr,
400                                         "%d: expected \"ports\" - got \"%s\"\n",
401                                         linenum, *cpp);
402                                 return NULL;
403                         }
404                         cpp++;
405                         if (*cpp == NULL) {
406                                 fprintf(stderr,
407                                         "%d: missing argument to \"ports\"\n",
408                                         linenum);
409                                 return NULL;
410                         }
411                         if (!strcasecmp(*cpp, "auto"))
412                                 ipn.in_flags |= IPN_AUTOPORTMAP;
413                         else
414                                 ipn.in_pmin = atoi(*cpp);
415                         cpp++;
416                 } else
417                         ipn.in_pmin = 0;
418         } else if ((ipn.in_redir & NAT_BIMAP) == NAT_REDIRECT) {
419                 if (*cpp && (strrchr(*cpp, '/') != NULL)) {
420                         fprintf(stderr, "%d: No netmask supported in %s\n",
421                                 linenum, "destination host for redirect");
422                         return NULL;
423                 }
424
425                 if (!*cpp) {
426                         fprintf(stderr, "%d: Missing destination port %s\n",
427                                 linenum, "in redirect");
428                         return NULL;
429                 }
430
431                 /* If it's a in_redir, expect target port */
432
433                 if (strcasecmp(*cpp, "port")) {
434                         fprintf(stderr, "%d: missing fields - 2nd port (%s)\n",
435                                 linenum, *cpp);
436                         return NULL;
437                 }
438                 cpp++;
439                 if (!*cpp) {
440                         fprintf(stderr,
441                                 "%d: missing fields (destination port)\n",
442                                 linenum);
443                         return NULL;
444                 }
445
446                 port2a = *cpp++;
447         } 
448         if (dnetm && *dnetm == '/')
449                 *dnetm++ = '\0';
450
451         if (ipn.in_redir & (NAT_MAP|NAT_MAPBLK)) {
452                 if (ipn.in_flags & IPN_IPRANGE) {
453                         if (hostnum((u_32_t *)&ipn.in_outmsk, dnetm,
454                                     linenum) == -1)
455                                 return NULL;
456                 } else if (genmask(dnetm, (u_32_t *)&ipn.in_outmsk))
457                         return NULL;
458         } else {
459                 if (ipn.in_flags & IPN_SPLIT) {
460                         if (hostnum((u_32_t *)&ipn.in_inmsk, dnetm,
461                                     linenum) == -1)
462                                 return NULL;
463                 } else if (genmask("255.255.255.255", (u_32_t *)&ipn.in_inmsk))
464                         return NULL;
465                 if (!*cpp) {
466                         ipn.in_flags |= IPN_TCP; /* XXX- TCP only by default */
467                         proto = "tcp";
468                 } else {
469                         proto = *cpp++;
470                         if (!strcasecmp(proto, "tcp"))
471                                 ipn.in_flags |= IPN_TCP;
472                         else if (!strcasecmp(proto, "udp"))
473                                 ipn.in_flags |= IPN_UDP;
474                         else if (!strcasecmp(proto, "tcp/udp"))
475                                 ipn.in_flags |= IPN_TCPUDP;
476                         else if (!strcasecmp(proto, "tcpudp")) {
477                                 ipn.in_flags |= IPN_TCPUDP;
478                                 proto = "tcp/udp";
479                         } else if (!strcasecmp(proto, "ip"))
480                                 ipn.in_flags |= IPN_ANY;
481                         else {
482                                 ipn.in_flags |= IPN_ANY;
483                                 if ((pr = getprotobyname(proto)))
484                                         ipn.in_p = pr->p_proto;
485                                 else {
486                                         if (!isdigit(*proto)) {
487                                                 fprintf(stderr,
488                                                 "%d: Unknown protocol %s\n",
489                                                         linenum, proto);
490                                                 return NULL;
491                                         } else
492                                                 ipn.in_p = atoi(proto);
493                                 }
494                         }
495                         if ((ipn.in_flags & IPN_TCPUDP) == 0) {
496                                 port1a = "0";
497                                 port2a = "0";
498                         }
499
500                         if (*cpp && !strcasecmp(*cpp, "round-robin")) {
501                                 cpp++;
502                                 ipn.in_flags |= IPN_ROUNDR;
503                         }
504
505                         if (*cpp && !strcasecmp(*cpp, "frag")) {
506                                 cpp++;
507                                 ipn.in_flags |= IPN_FRAG;
508                         }
509
510                         if (*cpp && !strcasecmp(*cpp, "age")) {
511                                 cpp++;
512                                 if (!*cpp) {
513                                         fprintf(stderr,
514                                                 "%d: age with no parameters\n",
515                                                 linenum);
516                                         return NULL;
517                                 }
518
519                                 ipn.in_age[0] = atoi(*cpp);
520                                 s = index(*cpp, '/');
521                                 if (s != NULL)
522                                         ipn.in_age[1] = atoi(s + 1);
523                                 else
524                                         ipn.in_age[1] = ipn.in_age[0];
525                                 cpp++;
526                         }
527
528                         if (*cpp) {
529                                 fprintf(stderr,
530                                 "%d: extra junk at the end of the line: %s\n",
531                                         linenum, *cpp);
532                                 return NULL;
533                         }
534                 }
535         }
536
537         if ((ipn.in_redir == NAT_REDIRECT) && !(ipn.in_flags & IPN_FILTER)) {
538                 if (!portnum(port1a, &ipn.in_pmin, linenum))
539                         return NULL;
540                 ipn.in_pmin = htons(ipn.in_pmin);
541                 if (port1b != NULL) {
542                         if (!portnum(port1b, &ipn.in_pmax, linenum))
543                                 return NULL;
544                         ipn.in_pmax = htons(ipn.in_pmax);
545                 } else
546                         ipn.in_pmax = ipn.in_pmin;
547         }
548
549         if ((ipn.in_redir & NAT_BIMAP) == NAT_REDIRECT) {
550                 if (!portnum(port2a, &ipn.in_pnext, linenum))
551                         return NULL;
552                 ipn.in_pnext = htons(ipn.in_pnext);
553         }
554
555         if (!(ipn.in_flags & IPN_SPLIT))
556                 ipn.in_inip &= ipn.in_inmsk;
557         if ((ipn.in_flags & IPN_IPRANGE) == 0)
558                 ipn.in_outip &= ipn.in_outmsk;
559         ipn.in_srcip &= ipn.in_srcmsk;
560
561         if ((ipn.in_redir & NAT_MAPBLK) != 0)
562                 nat_setgroupmap(&ipn);
563
564         if (*cpp && !*(cpp+1) && !strcasecmp(*cpp, "frag")) {
565                 cpp++;
566                 ipn.in_flags |= IPN_FRAG;
567         }
568
569         if (!*cpp)
570                 return &ipn;
571
572         if (ipn.in_redir == NAT_BIMAP) {
573                 fprintf(stderr,
574                         "%d: extra words at the end of bimap line: %s\n",
575                         linenum, *cpp);
576                 return NULL;
577         }
578
579         if (!strcasecmp(*cpp, "proxy")) {
580                 if (ipn.in_redir == NAT_BIMAP) {
581                         fprintf(stderr, "%d: cannot use proxy with bimap\n",
582                                 linenum);
583                         return NULL;
584                 }
585                 cpp++;
586                 if (!*cpp) {
587                         fprintf(stderr,
588                                 "%d: missing parameter for \"proxy\"\n",
589                                 linenum);
590                         return NULL;
591                 }
592                 dport = NULL;
593
594                 if (!strcasecmp(*cpp, "port")) {
595                         cpp++;
596                         if (!*cpp) {
597                                 fprintf(stderr,
598                                         "%d: missing parameter for \"port\"\n",
599                                         linenum);
600                                 return NULL;
601                         }
602
603                         dport = *cpp;
604                         cpp++;
605
606                         if (!*cpp) {
607                                 fprintf(stderr,
608                                         "%d: missing parameter for \"proxy\"\n",
609                                         linenum);
610                                 return NULL;
611                         }
612                 } else {
613                         fprintf(stderr,
614                                 "%d: missing keyword \"port\"\n", linenum);
615                         return NULL;
616                 }
617
618                 if ((proto = index(*cpp, '/'))) {
619                         *proto++ = '\0';
620                         if ((pr = getprotobyname(proto)))
621                                 ipn.in_p = pr->p_proto;
622                         else
623                                 ipn.in_p = atoi(proto);
624                 } else
625                         ipn.in_p = 0;
626
627                 if (dport && !portnum(dport, &ipn.in_dport, linenum))
628                         return NULL;
629                 ipn.in_dport = htons(ipn.in_dport);
630
631                 (void) strncpy(ipn.in_plabel, *cpp, sizeof(ipn.in_plabel));
632                 cpp++;
633
634         } else if (!strcasecmp(*cpp, "portmap")) {
635                 if (ipn.in_redir == NAT_BIMAP) {
636                         fprintf(stderr, "%d: cannot use portmap with bimap\n",
637                                 linenum);
638                         return NULL;
639                 }
640                 cpp++;
641                 if (!*cpp) {
642                         fprintf(stderr,
643                                 "%d: missing expression following portmap\n",
644                                 linenum);
645                         return NULL;
646                 }
647
648                 if (!strcasecmp(*cpp, "tcp"))
649                         ipn.in_flags |= IPN_TCP;
650                 else if (!strcasecmp(*cpp, "udp"))
651                         ipn.in_flags |= IPN_UDP;
652                 else if (!strcasecmp(*cpp, "tcpudp"))
653                         ipn.in_flags |= IPN_TCPUDP;
654                 else if (!strcasecmp(*cpp, "tcp/udp"))
655                         ipn.in_flags |= IPN_TCPUDP;
656                 else {
657                         fprintf(stderr,
658                                 "%d: expected protocol name - got \"%s\"\n",
659                                 linenum, *cpp);
660                         return NULL;
661                 }
662                 proto = *cpp;
663                 cpp++;
664
665                 if (!*cpp) {
666                         fprintf(stderr, "%d: no port range found\n", linenum);
667                         return NULL;
668                 }
669
670                 if (!strcasecmp(*cpp, "auto")) {
671                         ipn.in_flags |= IPN_AUTOPORTMAP;
672                         ipn.in_pmin = htons(1024);
673                         ipn.in_pmax = htons(65535);
674                         nat_setgroupmap(&ipn);
675                         cpp++;
676                 } else {
677                         if (!(t = strchr(*cpp, ':'))) {
678                                 fprintf(stderr,
679                                         "%d: no port range in \"%s\"\n",
680                                         linenum, *cpp);
681                                 return NULL;
682                         }
683                         *t++ = '\0';
684                         if (!portnum(*cpp, &ipn.in_pmin, linenum) ||
685                             !portnum(t, &ipn.in_pmax, linenum))
686                                 return NULL;
687                         ipn.in_pmin = htons(ipn.in_pmin);
688                         ipn.in_pmax = htons(ipn.in_pmax);
689                         cpp++;
690                 }
691         }
692
693         if (*cpp && !strcasecmp(*cpp, "frag")) {
694                 cpp++;
695                 ipn.in_flags |= IPN_FRAG;
696         }
697
698         if (*cpp && !strcasecmp(*cpp, "age")) {
699                 cpp++;
700                 if (!*cpp) {
701                         fprintf(stderr, "%d: age with no parameters\n",
702                                 linenum);
703                         return NULL;
704                 }
705                 ipn.in_age[0] = atoi(*cpp);
706                 s = index(*cpp, '/');
707                 if (s != NULL)
708                         ipn.in_age[1] = atoi(s + 1);
709                 else
710                         ipn.in_age[1] = ipn.in_age[0];
711                 cpp++;
712         }
713
714         if (*cpp) {
715                 fprintf(stderr, "%d: extra junk at the end of the line: %s\n",
716                         linenum, *cpp);
717                 return NULL;
718         }
719         return &ipn;
720 }
721
722
723 void natparsefile(fd, file, opts)
724 int fd;
725 char *file;
726 int opts;
727 {
728         char    line[512], *s;
729         ipnat_t *np;
730         FILE    *fp;
731         int     linenum = 0;
732
733         if (strcmp(file, "-")) {
734                 if (!(fp = fopen(file, "r"))) {
735                         fprintf(stderr, "%s: open: %s\n", file,
736                                 STRERROR(errno));
737                         exit(1);
738                 }
739         } else
740                 fp = stdin;
741
742         while (fgets(line, sizeof(line) - 1, fp)) {
743                 linenum++;
744                 line[sizeof(line) - 1] = '\0';
745                 if ((s = strchr(line, '\n')))
746                         *s = '\0';
747
748                 if (!(np = natparse(line, linenum))) {
749                         if (*line)
750                                 fprintf(stderr, "%d: syntax error in \"%s\"\n",
751                                         linenum, line);
752                 } else {
753                         if ((opts & OPT_VERBOSE) && np)
754                                 printnat(np, opts);
755                         if (!(opts & OPT_NODO)) {
756                                 if (!(opts & OPT_REMOVE)) {
757                                         if (ioctl(fd, SIOCADNAT, &np) == -1) {
758                                                 fprintf(stderr, "%d:",
759                                                         linenum);
760                                                 perror("ioctl(SIOCADNAT)");
761                                         }
762                                 } else if (ioctl(fd, SIOCRMNAT, &np) == -1) {
763                                         fprintf(stderr, "%d:", linenum);
764                                         perror("ioctl(SIOCRMNAT)");
765                                 }
766                         }
767                 }
768         }
769         if (fp != stdin)
770                 fclose(fp);
771 }