2 * Copyright (C) 1993-2002 by Darren Reed.
4 * See the IPFILTER.LICENCE file for details on licencing.
6 #if defined(__sgi) && (IRIX > 602)
7 # include <sys/ptimers.h>
13 #include <sys/types.h>
14 #if !defined(__SVR4) && !defined(__svr4__)
17 # include <sys/byteorder.h>
20 #include <sys/param.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>
30 #include <netinet/in.h>
31 #include <netinet/in_systm.h>
32 #include <netinet/ip.h>
33 #include <netinet/tcp.h>
35 #if __FreeBSD_version >= 300000
36 # include <net/if_var.h>
39 #include <arpa/nameser.h>
40 #include <arpa/inet.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"
50 #if defined(sun) && !SOLARIS2
51 # define STRERROR(x) sys_errlist[x]
52 extern char *sys_errlist
[];
54 # define STRERROR(x) strerror(x)
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 $";
64 #define bzero(a,b) memset(a,0,b)
67 extern void printnat
__P((ipnat_t
*, int));
68 extern int countbits
__P((u_32_t
));
71 ipnat_t
*natparse
__P((char *, int, int *));
72 void natparsefile
__P((int, char *, int));
73 void nat_setgroupmap
__P((struct ipnat
*));
76 void nat_setgroupmap(n
)
79 if (n
->in_outmsk
== n
->in_inmsk
)
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);
88 n
->in_ppip
= USABLE_PORTS
/ n
->in_ippip
;
90 n
->in_space
= USABLE_PORTS
* ~ntohl(n
->in_outmsk
);
92 if (!(n
->in_ppip
= n
->in_pmin
))
94 n
->in_ippip
= USABLE_PORTS
/ n
->in_ppip
;
100 * Parse a line of input from the ipnat configuration file
105 * > 0 programmer error
107 ipnat_t
*natparse(line
, linenum
, status
)
114 char *dnetm
= NULL
, *dport
= NULL
;
115 char *s
, *t
, *cps
[31], **cpp
;
117 char *port1a
= NULL
, *port1b
= NULL
, *port2a
= NULL
;
119 *status
= 100; /* default to error */
123 * Search for end of line and comment marker, advance of leading spaces
125 if ((s
= strchr(line
, '\n')))
127 if ((s
= strchr(line
, '#')))
129 while (*line
&& isspace(*line
))
136 bzero((char *)&ipn
, sizeof(ipn
));
140 * split line upto into segments.
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");
148 fprintf(stderr
, "%d: not enough segments in line\n", linenum
);
156 * Check first word is a recognised keyword and then is the interface
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
;
167 fprintf(stderr
, "%d: unknown mapping: \"%s\"\n",
175 strncpy(ipn
.in_ifname
, *cpp
, sizeof(ipn
.in_ifname
) - 1);
176 ipn
.in_ifname
[sizeof(ipn
.in_ifname
) - 1] = '\0';
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.
183 if (!strcasecmp(*cpp
, "from") || (**cpp
== '!')) {
184 if (!strcmp(*cpp
, "!")) {
186 if (strcasecmp(*cpp
, "from")) {
187 fprintf(stderr
, "Missing from after !\n");
191 ipn
.in_flags
|= IPN_NOTSRC
;
192 } else if (**cpp
== '!') {
193 if (strcasecmp(*cpp
+ 1, "from")) {
194 fprintf(stderr
, "Missing from after !\n");
198 ipn
.in_flags
|= IPN_NOTSRC
;
200 if ((ipn
.in_flags
& IPN_NOTSRC
) &&
201 (ipn
.in_redir
& (NAT_MAP
|NAT_MAPBLK
))) {
202 fprintf(stderr
, "Cannot use '! from' with map\n");
207 ipn
.in_flags
|= IPN_FILTER
;
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
)) {
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
)) {
225 if (!strcmp(*cpp
, "!")) {
227 ipn
.in_flags
|= IPN_NOTDST
;
228 } else if (**cpp
== '!') {
230 ipn
.in_flags
|= IPN_NOTDST
;
233 if (strcasecmp(*cpp
, "to")) {
234 fprintf(stderr
, "%d: unexpected keyword (%s) - to\n",
239 if ((ipn
.in_flags
& IPN_NOTDST
) &&
240 (ipn
.in_redir
& (NAT_REDIRECT
))) {
241 fprintf(stderr
, "Cannot use '! to' with rdr\n");
247 fprintf(stderr
, "%d: missing host after to\n", linenum
);
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
)) {
258 ipn
.in_pmin
= htons(ipn
.in_dport
);
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
)) {
270 fprintf(stderr
, "%d: short line\n", linenum
);
276 fprintf(stderr
, "%d: no netmask on LHS\n", linenum
);
281 if (ipn
.in_redir
== NAT_REDIRECT
) {
282 if (hostnum((u_32_t
*)&ipn
.in_outip
, s
, linenum
) == -1){
286 if (genmask(t
, (u_32_t
*)&ipn
.in_outmsk
) == -1) {
291 if (hostnum((u_32_t
*)&ipn
.in_inip
, s
, linenum
) == -1) {
295 if (genmask(t
, (u_32_t
*)&ipn
.in_inmsk
) == -1) {
302 fprintf(stderr
, "%d: short line\n", linenum
);
309 * If it is a standard redirect then we expect it to have a port
310 * match after the hostmask.
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",
324 "%d: missing fields (destination port)\n",
330 if (isdigit(**cpp
) && (s
= strchr(*cpp
, '-')))
337 if (!strcmp(*cpp
, "-")) {
345 ipn
.in_pmax
= ipn
.in_pmin
;
349 * In the middle of the NAT rule syntax is -> to indicate the
350 * direction of translation.
353 fprintf(stderr
, "%d: missing fields (->)\n", linenum
);
357 if (strcmp(*cpp
, "->")) {
358 fprintf(stderr
, "%d: missing ->\n", linenum
);
365 fprintf(stderr
, "%d: missing fields (%s)\n",
366 linenum
, ipn
.in_redir
? "destination" : "target");
371 if (ipn
.in_redir
== NAT_MAP
) {
372 if (!strcasecmp(*cpp
, "range")) {
374 ipn
.in_flags
|= IPN_IPRANGE
;
376 fprintf(stderr
, "%d: missing fields (%s)\n",
378 ipn
.in_redir
? "destination":"target");
385 if (ipn
.in_flags
& IPN_IPRANGE
) {
386 dnetm
= strrchr(*cpp
, '-');
389 if (*cpp
&& !strcmp(*cpp
, "-") && *(cpp
+ 1))
393 if (dnetm
== NULL
|| *dnetm
== '\0') {
395 "%d: desination range not specified\n",
400 } else if (ipn
.in_redir
!= NAT_REDIRECT
) {
401 dnetm
= strrchr(*cpp
, '/');
404 if (*cpp
&& !strcasecmp(*cpp
, "netmask"))
409 "%d: missing fields (dest netmask)\n",
418 if (ipn
.in_redir
== NAT_REDIRECT
) {
419 dnetm
= strchr(*cpp
, ',');
421 ipn
.in_flags
|= IPN_SPLIT
;
424 if (hostnum((u_32_t
*)&ipn
.in_inip
, *cpp
, linenum
) == -1) {
429 if (ntohl(ipn
.in_inip
) == INADDR_LOOPBACK
) {
431 "localhost as destination not supported\n");
437 if (!strcmp(*cpp
, ipn
.in_ifname
))
439 if (hostnum((u_32_t
*)&ipn
.in_outip
, *cpp
, linenum
) == -1) {
446 if (ipn
.in_redir
& NAT_MAPBLK
) {
448 if (strcasecmp(*cpp
, "ports")) {
450 "%d: expected \"ports\" - got \"%s\"\n",
458 "%d: missing argument to \"ports\"\n",
463 if (!strcasecmp(*cpp
, "auto"))
464 ipn
.in_flags
|= IPN_AUTOPORTMAP
;
466 ipn
.in_pmin
= atoi(*cpp
);
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");
479 fprintf(stderr
, "%d: Missing destination port %s\n",
480 linenum
, "in redirect");
485 /* If it's a in_redir, expect target port */
487 if (strcasecmp(*cpp
, "port")) {
488 fprintf(stderr
, "%d: missing fields - 2nd port (%s)\n",
496 "%d: missing fields (destination port)\n",
504 if (dnetm
&& *dnetm
== '/')
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
,
514 } else if (genmask(dnetm
, (u_32_t
*)&ipn
.in_outmsk
)) {
519 if (ipn
.in_flags
& IPN_SPLIT
) {
520 if (hostnum((u_32_t
*)&ipn
.in_inmsk
, dnetm
,
525 } else if (genmask("255.255.255.255", (u_32_t
*)&ipn
.in_inmsk
)){
530 ipn
.in_flags
|= IPN_TCP
; /* XXX- TCP only by default */
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
;
543 } else if (!strcasecmp(proto
, "ip"))
544 ipn
.in_flags
|= IPN_ANY
;
546 ipn
.in_flags
|= IPN_ANY
;
547 if ((pr
= getprotobyname(proto
)))
548 ipn
.in_p
= pr
->p_proto
;
550 if (!isdigit(*proto
)) {
552 "%d: Unknown protocol %s\n",
557 ipn
.in_p
= atoi(proto
);
560 if ((ipn
.in_flags
& IPN_TCPUDP
) == 0) {
565 if (*cpp
&& !strcasecmp(*cpp
, "round-robin")) {
567 ipn
.in_flags
|= IPN_ROUNDR
;
570 if (*cpp
&& !strcasecmp(*cpp
, "frag")) {
572 ipn
.in_flags
|= IPN_FRAG
;
575 if (*cpp
&& !strcasecmp(*cpp
, "age")) {
579 "%d: age with no parameters\n",
585 ipn
.in_age
[0] = atoi(*cpp
);
586 s
= index(*cpp
, '/');
588 ipn
.in_age
[1] = atoi(s
+ 1);
590 ipn
.in_age
[1] = ipn
.in_age
[0];
594 if (*cpp
&& !strcasecmp(*cpp
, "mssclamp")) {
597 ipn
.in_mssclamp
= atoi(*cpp
);
601 "%d: mssclamp with no parameters\n",
610 "%d: extra junk at the end of the line: %s\n",
618 if ((ipn
.in_redir
== NAT_REDIRECT
) && !(ipn
.in_flags
& IPN_FILTER
)) {
619 if (!portnum(port1a
, &ipn
.in_pmin
, linenum
)) {
623 ipn
.in_pmin
= htons(ipn
.in_pmin
);
624 if (port1b
!= NULL
) {
625 if (!portnum(port1b
, &ipn
.in_pmax
, linenum
)) {
629 ipn
.in_pmax
= htons(ipn
.in_pmax
);
631 ipn
.in_pmax
= ipn
.in_pmin
;
634 if ((ipn
.in_redir
& NAT_BIMAP
) == NAT_REDIRECT
) {
635 if (!portnum(port2a
, &ipn
.in_pnext
, linenum
)) {
639 ipn
.in_pnext
= htons(ipn
.in_pnext
);
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
;
648 if ((ipn
.in_redir
& NAT_MAPBLK
) != 0)
649 nat_setgroupmap(&ipn
);
651 if (*cpp
&& !*(cpp
+1) && !strcasecmp(*cpp
, "frag")) {
653 ipn
.in_flags
|= IPN_FRAG
;
661 if (ipn
.in_redir
!= NAT_BIMAP
&& !strcasecmp(*cpp
, "proxy")) {
664 if (ipn
.in_redir
== NAT_BIMAP
) {
665 fprintf(stderr
, "%d: cannot use proxy with bimap\n",
673 "%d: missing parameter for \"proxy\"\n",
680 if (!strcasecmp(*cpp
, "port")) {
684 "%d: missing parameter for \"port\"\n",
695 "%d: missing parameter for \"proxy\"\n",
702 "%d: missing keyword \"port\"\n", linenum
);
707 if ((proto
= index(*cpp
, '/'))) {
709 if ((pr
= getprotobyname(proto
)))
710 ipn
.in_p
= pr
->p_proto
;
712 ipn
.in_p
= atoi(proto
);
716 if (dport
&& !portnum(dport
, &pport
, linenum
))
718 if (ipn
.in_dcmp
!= 0) {
719 if (pport
!= ipn
.in_dport
) {
721 "%d: mismatch in port numbers\n",
726 ipn
.in_dport
= htons(pport
);
728 (void) strncpy(ipn
.in_plabel
, *cpp
, sizeof(ipn
.in_plabel
));
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",
741 "%d: missing expression following portmap\n",
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
;
757 "%d: expected protocol name - got \"%s\"\n",
766 fprintf(stderr
, "%d: no port range found\n", linenum
);
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
);
778 if (!(t
= strchr(*cpp
, ':'))) {
780 "%d: no port range in \"%s\"\n",
786 if (!portnum(*cpp
, &ipn
.in_pmin
, linenum
) ||
787 !portnum(t
, &ipn
.in_pmax
, linenum
)) {
791 ipn
.in_pmin
= htons(ipn
.in_pmin
);
792 ipn
.in_pmax
= htons(ipn
.in_pmax
);
797 if (*cpp
&& !strcasecmp(*cpp
, "frag")) {
799 ipn
.in_flags
|= IPN_FRAG
;
802 if (*cpp
&& !strcasecmp(*cpp
, "age")) {
805 fprintf(stderr
, "%d: age with no parameters\n",
810 ipn
.in_age
[0] = atoi(*cpp
);
811 s
= index(*cpp
, '/');
813 ipn
.in_age
[1] = atoi(s
+ 1);
815 ipn
.in_age
[1] = ipn
.in_age
[0];
819 if (*cpp
&& !strcasecmp(*cpp
, "mssclamp")) {
822 ipn
.in_mssclamp
= atoi(*cpp
);
825 fprintf(stderr
, "%d: mssclamp with no parameters\n",
833 fprintf(stderr
, "%d: extra junk at the end of the line: %s\n",
844 void natparsefile(fd
, file
, opts
)
855 if (strcmp(file
, "-")) {
856 if (!(fp
= fopen(file
, "r"))) {
857 fprintf(stderr
, "%s: open: %s\n", file
,
864 while (fgets(line
, sizeof(line
) - 1, fp
)) {
866 line
[sizeof(line
) - 1] = '\0';
867 if ((s
= strchr(line
, '\n')))
871 np
= natparse(line
, linenum
, &parsestatus
);
872 if (parsestatus
!= 0) {
874 fprintf(stderr
, "%d: syntax error in \"%s\"\n",
877 fprintf(stderr
, "%s: %s error (%d), quitting\n",
879 ((parsestatus
< 0)? "parse": "internal"),
884 if ((opts
& OPT_VERBOSE
) && np
)
886 if (!(opts
& OPT_NODO
)) {
887 if (!(opts
& OPT_REMOVE
)) {
888 if (ioctl(fd
, SIOCADNAT
, &np
) == -1) {
889 fprintf(stderr
, "%d:",
891 perror("ioctl(SIOCADNAT)");
893 } else if (ioctl(fd
, SIOCRMNAT
, &np
) == -1) {
894 fprintf(stderr
, "%d:", linenum
);
895 perror("ioctl(SIOCRMNAT)");