aboutsummaryrefslogtreecommitdiff
path: root/sbin/ipf/ipnat
diff options
context:
space:
mode:
authorCy Schubert <cy@FreeBSD.org>2021-12-15 21:45:47 +0000
committerCy Schubert <cy@FreeBSD.org>2021-12-20 14:16:33 +0000
commit41edb306f05651fcaf6c74f9e3557f59f80292e1 (patch)
tree27e88c81aa6f5219d79ccd2c24e11d5cddac6d29 /sbin/ipf/ipnat
parent3b9b51fe464ebb91e894742a6a0e6417e256f03a (diff)
downloadsrc-41edb306f05651fcaf6c74f9e3557f59f80292e1.tar.gz
src-41edb306f05651fcaf6c74f9e3557f59f80292e1.zip
Diffstat (limited to 'sbin/ipf/ipnat')
-rw-r--r--sbin/ipf/ipnat/ipnat.148
-rw-r--r--sbin/ipf/ipnat/ipnat.497
-rw-r--r--sbin/ipf/ipnat/ipnat.5728
-rw-r--r--sbin/ipf/ipnat/ipnat.876
-rw-r--r--sbin/ipf/ipnat/ipnat.c842
-rw-r--r--sbin/ipf/ipnat/ipnat_y.y1774
6 files changed, 3565 insertions, 0 deletions
diff --git a/sbin/ipf/ipnat/ipnat.1 b/sbin/ipf/ipnat/ipnat.1
new file mode 100644
index 000000000000..f24141546171
--- /dev/null
+++ b/sbin/ipf/ipnat/ipnat.1
@@ -0,0 +1,48 @@
+.TH IPNAT 1
+.SH NAME
+ipnat \- user interface to the NAT
+.SH SYNOPSIS
+.B ipnat
+[
+.B \-lnrsvCF
+]
+.B \-f <\fIfilename\fP>
+.SH DESCRIPTION
+.PP
+\fBipnat\fP opens the filename given (treating "\-" as stdin) and parses the
+file for a set of rules which are to be added or removed from the IP NAT.
+.PP
+Each rule processed by \fBipnat\fP
+is added to the kernels internal lists if there are no parsing problems.
+Rules are added to the end of the internal lists, matching the order in
+which they appear when given to \fBipnat\fP.
+.SH OPTIONS
+.TP
+.B \-C
+delete all entries in the current NAT rule listing (NAT rules)
+.TP
+.B \-F
+delete all active entries in the current NAT translation table (currently
+active NAT mappings)
+.TP
+.B \-l
+Show the list of current NAT table entry mappings.
+.TP
+.B \-n
+This flag (no-change) prevents \fBipf\fP from actually making any ioctl
+calls or doing anything which would alter the currently running kernel.
+.TP
+.B \-s
+Retrieve and display NAT statistics
+.TP
+.B \-r
+Remove matching NAT rules rather than add them to the internal lists
+.TP
+.B \-v
+Turn verbose mode on. Displays information relating to rule processing
+and active rules/table entries.
+.DT
+.SH FILES
+/dev/ipnat
+.SH SEE ALSO
+ipnat(5), ipf(8), ipfstat(8)
diff --git a/sbin/ipf/ipnat/ipnat.4 b/sbin/ipf/ipnat/ipnat.4
new file mode 100644
index 000000000000..80c5ba444708
--- /dev/null
+++ b/sbin/ipf/ipnat/ipnat.4
@@ -0,0 +1,97 @@
+.\" $FreeBSD$
+.TH IPNAT 4
+.SH NAME
+ipnat \- Network Address Translation kernel interface
+.SH SYNOPSIS
+#include <netinet/ip_compat.h>
+.br
+#include <netinet/ip_fil.h>
+.br
+#include <netinet/ip_proxy.h>
+.br
+#include <netinet/ip_nat.h>
+.SH IOCTLS
+.PP
+To add and delete rules to the NAT list, two 'basic' ioctls are provided
+for use. The ioctl's are called as:
+.LP
+.nf
+ ioctl(fd, SIOCADNAT, struct ipnat **)
+ ioctl(fd, SIOCRMNAT, struct ipnat **)
+ ioctl(fd, SIOCGNATS, struct natstat **)
+ ioctl(fd, SIOCGNATL, struct natlookup **)
+.fi
+.PP
+Unlike \fBipf(4)\fP, there is only a single list supported by the kernel NAT
+interface. An inactive list which can be swapped to is not currently
+supported.
+
+These ioctl's are implemented as being routing ioctls and thus the same rules
+for the various routing ioctls and the file descriptor are employed, mainly
+being that the fd must be that of the device associated with the module
+(i.e., /dev/ipl).
+.PP
+The structure used with the NAT interface is described below:
+.LP
+.nf
+typedef struct ipnat {
+ struct ipnat *in_next;
+ void *in_ifp;
+ u_short in_flags;
+ u_short in_pnext;
+ u_short in_port[2];
+ struct in_addr in_in[2];
+ struct in_addr in_out[2];
+ struct in_addr in_nextip;
+ int in_space;
+ int in_redir; /* 0 if it's a mapping, 1 if it's a hard redir */
+ char in_ifname[IFNAMSIZ];
+} ipnat_t;
+
+#define in_pmin in_port[0] /* Also holds static redir port */
+#define in_pmax in_port[1]
+#define in_nip in_nextip.s_addr
+#define in_inip in_in[0].s_addr
+#define in_inmsk in_in[1].s_addr
+#define in_outip in_out[0].s_addr
+#define in_outmsk in_out[1].s_addr
+
+.fi
+.PP
+Recognised values for in_redir:
+.LP
+.nf
+#define NAT_MAP 0
+#define NAT_REDIRECT 1
+.fi
+.LP
+\fBNAT statistics\fP
+Statistics on the number of packets mapped, going in and out are kept,
+the number of times a new entry is added and deleted (through expiration) to
+the NAT table and the current usage level of the NAT table.
+.PP
+Pointers to the NAT table inside the kernel, as well as to the top of the
+internal NAT lists constructed with the \fBSIOCADNAT\fP ioctls. The table
+itself is a hash table of size NAT_SIZE (default size is 367).
+.PP
+To retrieve the statistics, the \fBSIOCGNATS\fP ioctl must be used, with
+the appropriate structure passed by reference, as follows:
+.nf
+ ioctl(fd, SIOCGNATS, struct natstat *)
+
+typedef struct natstat {
+ u_long ns_mapped[2];
+ u_long ns_added;
+ u_long ns_expire;
+ u_long ns_inuse;
+ nat_t ***ns_table;
+ ipnat_t *ns_list;
+} natstat_t;
+.fi
+.SH BUGS
+It would be nice if there were more flexibility when adding and deleting
+filter rules.
+.SH FILES
+/dev/ipnat
+.SH SEE ALSO
+ipf(4), ipnat(5), ipf(8), ipnat(8), ipfstat(8)
diff --git a/sbin/ipf/ipnat/ipnat.5 b/sbin/ipf/ipnat/ipnat.5
new file mode 100644
index 000000000000..ab56573d79ea
--- /dev/null
+++ b/sbin/ipf/ipnat/ipnat.5
@@ -0,0 +1,728 @@
+.\" $FreeBSD$
+.\"
+.TH IPNAT 5
+.SH NAME
+ipnat, ipnat.conf \- IPFilter NAT file format
+.SH DESCRIPTION
+.PP
+The
+.B ipnat.conf
+file is used to specify rules for the Network Address Translation (NAT)
+component of IPFilter. To load rules specified in the
+.B ipnat.conf
+file, the
+.B ipnat(8)
+program is used.
+.PP
+For standard NAT functionality, a rule should start with \fBmap\fP and then
+proceeds to specify the interface for which outgoing packets will have their
+source address rewritten. Following this it is expected that the old source
+address, and optionally port number, will be specified.
+.PP
+In general, all NAT rules conform to the following layout:
+the first word indicates what type of NAT rule is present, this is followed
+by some stanzas to match a packet, followed by a "->" and this is then
+followed by several more stanzas describing the new data to be put in the
+packet.
+.PP
+In this text and in others,
+use of the term "left hand side" (LHS) when talking about a NAT rule refers
+to text that appears before the "->" and the "right hand side" (RHS) for text
+that appears after it. In essence, the LHS is the packet matching and the
+RHS is the new data to be used.
+.SH VARIABLES
+.PP
+This configuration file, like all others used with IPFilter, supports the
+use of variable substitution throughout the text.
+.nf
+
+nif="ppp0";
+map $nif 0/0 -> 0/32
+.fi
+.PP
+would become
+.nf
+
+map ppp0 0/0 -> 0/32
+.fi
+.PP
+Variables can be used recursively, such as 'foo="$bar baz";', so long as
+$bar exists when the parser reaches the assignment for foo.
+.PP
+See
+.B ipnat(8)
+for instructions on how to define variables to be used from a shell
+environment.
+.SH OUTBOUND SOURCE TRANSLATION (map'ing)
+Changing the source address of a packet is traditionally performed using
+.B map
+rules. Both the source address and optionally port number can be changed
+according to various controls.
+.PP
+To start out with, a common rule used is of the form:
+.nf
+
+map le0 0/0 -> 0/32
+.fi
+.PP
+Here we're saying change the source address of all packets going out of
+le0 (the address/mask pair of 0/0 matching all packets) to that of the
+interface le0 (0/32 is a synonym for the interface's own address at
+the current point in time.) If we wanted to pass the packet through
+with no change in address, we would write it as:
+.nf
+
+map le0 0/0 -> 0/0
+.fi
+.PP
+If we only want to change a portion of our internal network and to a
+different address that is routed back through this host, we might do:
+.nf
+
+map le0 10.1.1.0/24 -> 192.168.55.3/32
+.fi
+.PP
+In some instances, we may have an entire subnet to map internal addresses
+out onto, in which case we can express the translation as this:
+.nf
+
+map le0 10.0.0.0/8 -> 192.168.55.0/24
+.fi
+.PP
+IPFilter will cycle through each of the 256 addresses in the 192.168.55.0/24
+address space to ensure that they all get used.
+.PP
+Of course this poses a problem for TCP and UDP, with many connections made,
+each with its own port number pair. If we're unlucky, translations can be
+dropped because the new address/port pair mapping already exists. To
+mitigate this problem, we add in port translation or port mapping:
+.nf
+
+map le0 10.0.0.0/8 -> 192.168.55.0/24 portmap tcp/udp auto
+.fi
+.PP
+In this instance, the word "auto" tells IPFilter to calculate a private
+range of port numbers for each address on the LHS to use without fear
+of them being trampled by others. This can lead to problems if there are
+connections being generated more quickly than IPFilter can expire them.
+In this instance, and if we want to get away from a private range of
+port numbers, we can say:
+.nf
+
+map le0 10.0.0.0/8 -> 192.168.55.0/24 portmap tcp/udp 5000:65000
+.fi
+.PP
+And now each connection through le0 will add to the enumeration of
+the port number space 5000-65000 as well as the IP address subnet
+of 192.168.55.0/24.
+.PP
+If the new addresses to be used are in a consecutive range, rather
+than a complete subnet, we can express this as:
+.nf
+
+map le0 10.0.0.0/8 -> range 192.168.55.10-192.168.55.249
+ portmap tcp/udp 5000:65000
+.fi
+.PP
+This tells IPFilter that it has a range of 240 IP address to use, from
+192.168.55.10 to 192.168.55.249, inclusive.
+.PP
+If there were several ranges of addresses for use, we can use each one
+in a round-robin fashion as followed:
+.nf
+
+map le0 10.0.0.0/8 -> range 192.168.55.10-192.168.55.29
+ portmap tcp/udp 5000:65000 round-robin
+map le0 10.0.0.0/8 -> range 192.168.55.40-192.168.55.49
+ portmap tcp/udp 5000:65000 round-robin
+.fi
+.PP
+To specify translation rules that impact a specific IP protocol,
+the protocol name or number is appended to the rule like this:
+.nf
+
+map le0 10.0.0.0/8 -> 192.168.55.0/24 tcp/udp
+map le0 10.0.0.0/8 -> 192.168.55.1/32 icmp
+map le0 10.0.0.0/8 -> 192.168.55.2/32 gre
+.fi
+.PP
+For TCP connections exiting a connection such as PPPoE where the MTU is
+slightly smaller than normal ethernet, it can be useful to reduce the
+Maximum Segment Size (MSS) offered by the internal machines to match,
+reducing the liklihood that the either end will attempt to send packets
+that are too big and result in fragmentation. This is acheived using the
+.B mssclamp
+option with TCP
+.B map
+rules like this:
+.nf
+
+map pppoe0 0/0 -> 0/32 mssclamp 1400 tcp
+.fi
+.PP
+For ICMP packets, we can map the ICMP id space in query packets:
+.nf
+
+map le0 10.0.0.0/8 -> 192.168.55.1/32 icmpidmap icmp 1000:20000
+.fi
+.PP
+If we wish to be more specific about our initial matching criteria on the
+LHS, we can expand to using a syntax more similar to that in
+.B ipf.conf(5)
+:
+.nf
+
+map le0 from 10.0.0.0/8 to 26.0.0.0/8 ->
+ 192.168.55.1
+map le0 from 10.0.0.0/8 port > 1024 to 26.0.0.0/8 ->
+ 192.168.55.2 portmap 5000:9999 tcp/udp
+map le0 from 10.0.0.0/8 ! to 26.0.0.0/8 ->
+ 192.168.55.3 portmap 5000:9999 tcp/udp
+.fi
+.TP
+.B NOTE:
+negation matching with source addresses is
+.B NOT
+possible with
+.B map
+/
+.B map-block
+rules.
+.PP
+The NAT code has builtin default timeouts for TCP, UDP, ICMP and another
+for all other protocols. In general, the timeout for an entry to be
+deleted shrinks once a reply packet has been seen (excluding TCP.)
+If you wish to specify your own timeouts, this can be achieved either
+by setting one timeout for both directions:
+.nf
+
+map le0 0/0 -> 0/32 gre age 30
+.fi
+.PP
+or setting a different timeout for the reply:
+.nf
+
+map le0 from any to any port = 53 -> 0/32 age 60/10 udp
+.fi
+.PP
+A pressing problem that many people encounter when using NAT is that the
+address protocol can be embedded inside an application's communication.
+To address this problem, IPFilter provides a number of built-in proxies
+for the more common trouble makers, such as FTP. These proxies can be
+used as follows:
+.nf
+
+map le0 0/0 -> 0/32 proxy port 21 ftp/tcp
+.fi
+.PP
+In this rule, the word "proxy" tells us that we want to connect up this
+translation with an internal proxy. The "port 21" is an extra restriction
+that requires the destination port number to be 21 if this rule is to be
+activated. The word "ftp" is the proxy identifier that the kernel will
+try and resolve internally, "tcp" the protocol that packets must match.
+.PP
+See below for a list of proxies and their relative staus.
+.PP
+To associate NAT rules with filtering rules, it is possible to set and
+match tags during either inbound or outbound processing. At present the
+tags for forwarded packets are not preserved by forwarding, so once the
+packet leaves IPFilter, the tag is forgotten. For
+.B map
+rules, we can match tags set by filter rules like this:
+.nf
+
+map le0 0/0 -> 0/32 proxy portmap 5000:5999 tag lan1 tcp
+.fi
+.PP
+This would be used with "pass out" rules that includes a stanza such
+as "set-tag (nat = lan1)".
+.PP
+If the interface in which packets are received is different from the
+interface on which packets are sent out, then the translation rule needs
+to be written to take this into account:
+.nf
+
+map hme0,le0 0/0 -> 0/32
+.fi
+.PP
+Although this might seem counterintuitive, the interfaces when listed
+in rules for
+.B ipnat.conf
+are always in the
+.I inbound
+,
+.I outbound
+order. In this case, hme0 would be the return interface and le0 would be
+the outgoing interface. If you wish to allow return packets on any
+interface, the correct syntax to use would be:
+.nf
+
+map *,le0 0/0 -> 0/32
+.fi
+.LP
+A special variant of
+.B map
+rules exists, called
+.B map-block.
+This command is intended for use when there is a large network to be mapped
+onto a smaller network, where the difference in netmasks is upto 14 bits
+difference in size. This is achieved by dividing the address space and
+port space up to ensure that each source address has its own private range
+of ports to use. For example, this rule:
+.nf
+
+map-block ppp0 172.192.0.0/16 -> 209.1.2.0/24 ports auto
+.fi
+.PP
+would result in 172.192.0.0/24 being mapped to 209.1.2.0/32
+with each address, from 172.192.0.0 to 172.192.0.255 having 252 ports of its
+own. As opposed to the above use of \fBmap\fP, if for some reason the user
+of (say) 172.192.0.2 wanted 260 simultaneous connections going out, they would
+be limited to 252 with \fBmap-block\fP but would just \fImove on\fP to the next
+IP address with the \fBmap\fP command.
+.SS Extended matching
+.PP
+If it is desirable to match on both the source and destination of a packet
+before applying an address translation to it, this can be achieved by using
+the same from-to syntax as is used in \fBipf.conf\fP(5). What follows
+applies equally to the
+.B map
+rules discussed above and
+.B rdr
+rules discussed below. A simple example is as follows:
+.nf
+
+map bge0 from 10.1.0.0/16 to 192.168.1.0/24 -> 172.12.1.4
+.fi
+.PP
+This would only match packets that are coming from hosts that have a source
+address matching 10.1.0.0/16 and a destination matching 192.168.1.0/24.
+This can be expanded upon with ports for TCP like this:
+.nf
+
+rdr bge0 from 10.1.0.0/16 to any port = 25 -> 127.0.0.1 port 2501 tcp
+.fi
+.PP
+Where only TCP packets from 10.1.0.0/16 to port 25 will be redirected to
+port 2501.
+.PP
+As with \fBipf.conf\fR(5), if we have a large set of networks or addresses
+that we would like to match up with then we can define a pool using
+\fBippool\fR(8) in \fBippool.conf\fR(5) and then refer to it in an
+\fBipnat\fR rule like this:
+.nf
+
+map bge0 from pool/100 to any port = 25 -> 127.0.0.1 port 2501 tcp
+.fi
+.TP
+.B NOTE:
+In this situation, the rule is considered to have a netmask of "0" and
+thus is looked at last, after any rules with /16's or /24's in them,
+.I even if
+the defined pool only has /24's or /32's. Pools may also be used
+.I wherever
+the from-to syntax in \fBipnat.conf\fR(5) is allowed.
+.SH INBOUND DESTINATION TRANSLATION (redirection)
+.PP
+Redirection of packets is used to change the destination fields in a packet
+and is supported for packets that are moving \fIin\fP on a network interface.
+While the same general syntax for
+.B map
+rules is supported, there are differences and limitations.
+.PP
+Firstly, by default all redirection rules target a single IP address, not
+a network or range of network addresses, so a rule written like this:
+.nf
+
+rdr le0 0/0 -> 192.168.1.0
+.fi
+.PP
+Will not spread packets across all 256 IP addresses in that class C network.
+If you were to try a rule like this:
+.nf
+
+rdr le0 0/0 -> 192.168.1.0/24
+.fi
+.PP
+then you will receive a parsing error.
+.PP
+The from-to source-destination matching used with
+.B map
+rules can be used with rdr rules, along with negation, however the
+restriction moves - only a source address match can be negated:
+.nf
+
+rdr le0 from 1.1.0.0/16 to any -> 192.168.1.3
+rdr le0 ! from 1.1.0.0/16 to any -> 192.168.1.4
+.fi
+.PP
+If there is a consective set of addresses you wish to spread the packets
+over, then this can be done in one of two ways, the word "range" optional
+to preserve:
+.nf
+
+rdr le0 0/0 -> 192.168.1.1 - 192.168.1.5
+rdr le0 0/0 -> range 192.168.1.1 - 192.168.1.5
+.fi
+.PP
+If there are only two addresses to split the packets across, the
+recommended method is to use a comma (",") like this:
+.nf
+
+rdr le0 0/0 -> 192.168.1.1,192.168.1.2
+.fi
+.PP
+If there is a large group of destination addresses that are somewhat
+disjoint in nature, we can cycle through them using a
+.B round-robin
+technique like this:
+.nf
+
+rdr le0 0/0 -> 192.168.1.1,192.168.1.2 round-robin
+rdr le0 0/0 -> 192.168.1.5,192.168.1.7 round-robin
+rdr le0 0/0 -> 192.168.1.9 round-robin
+.fi
+.PP
+If there are a large number of redirect rules and hosts being targetted
+then it may be desirable to have all those from a single source address
+be targetted at the same destination address. To achieve this, the
+word
+.B sticky
+is appended to the rule like this:
+.nf
+
+rdr le0 0/0 -> 192.168.1.1,192.168.1.2 sticky
+rdr le0 0/0 -> 192.168.1.5,192.168.1.7 round-robin sticky
+rdr le0 0/0 -> 192.168.1.9 round-robin sticky
+.fi
+.PP
+The
+.B sticky
+feature can only be combined with
+.B round-robin
+and the use of comma.
+.PP
+For TCP and UDP packets, it is possible to both match on the destiantion
+port number and to modify it. For example, to change the destination port
+from 80 to 3128, we would use a rule like this:
+.nf
+
+rdr de0 0/0 port 80 -> 127.0.0.1 port 3128 tcp
+.fi
+.PP
+If a range of ports is given on the LHS and a single port is given on the
+RHS, the entire range of ports is moved. For example, if we had this:
+.nf
+
+rdr le0 0/0 port 80-88 -> 127.0.0.1 port 3128 tcp
+.fi
+.PP
+then port 80 would become 3128, port 81 would become 3129, etc. If we
+want to redirect a number of different pots to just a single port, an
+equals sign ("=") is placed before the port number on the RHS like this:
+.nf
+
+rdr le0 0/0 port 80-88 -> 127.0.0.1 port = 3128 tcp
+.fi
+.PP
+In this case, port 80 goes to 3128, port 81 to 3128, etc.
+.PP
+As with
+.B map
+rules, it is possible to manually set a timeout using the
+.B age
+option, like this:
+.nf
+
+rdr le0 0/0 port 53 -> 127.0.0.1 port 10053 udp age 5/5
+.fi
+.PP
+The use of proxies is not restricted to
+.B map
+rules and outbound sessions. Proxies can also be used with redirect
+rules, although the syntax is slightly different:
+.nf
+
+rdr ge0 0/0 port 21 -> 127.0.0.1 port 21 tcp proxy ftp
+.fi
+.PP
+For
+.B rdr
+rules, the interfaces supplied are in the same order as
+.B map
+rules - input first, then output. In situations where the outgoing interface
+is not certain, it is also possible to use a wildcard ("*") to effect a match
+on any interface.
+.nf
+
+rdr le0,* 0/0 -> 192.168.1.0
+.fi
+.PP
+A single rule, with as many options set as possible would look something like
+this:
+.nf
+
+rdr le0,ppp0 9.8.7.6/32 port 80 -> 1.1.1.1,1.1.1.2 port 80 tcp
+ round-robin frag age 40/40 sticky mssclamp 1000 tag tagged
+.fi
+.SH REWRITING SOURCE AND DESTINATION
+.PP
+Whilst the above two commands provide a lot of flexibility in changing
+addressing fields in packets, often it can be of benefit to translate
+\fIboth\fP source \fBand\fR destination at the same time or to change
+the source address on input or the destination address on output.
+Doing all of these things can be accomplished using
+.B rewrite
+NAT rules.
+.PP
+A
+.B rewrite
+rule requires the same level of packet matching as before, protocol and
+source/destination information but in addition allows either
+.B in
+or
+.B out
+to be specified like this:
+.nf
+
+rewrite in on ppp0 proto tcp from any to any port = 80 ->
+ src 0/0 dst 127.0.0.1,3128;
+rewrite out on ppp0 from any to any ->
+ src 0/32 dst 10.1.1.0/24;
+.fi
+.PP
+On the RHS we can specify both new source and destination information to place
+into the packet being sent out. As with other rules used in
+\fBipnat.conf\fR, there are shortcuts syntaxes available to use the original
+address information (\fB0/0\fR) and the address associated with the network
+interface (\fB0/32\fR.) For TCP and UDP, both address and port information
+can be changed. At present it is only possible to specify either a range of
+port numbers to be used (\fBX-Y\fR) or a single port number (\fB= X\fR) as
+follows:
+.nf
+
+rewrite in on le0 proto tcp from any to any port = 80 ->
+ src 0/0,2000-20000 dst 127.0.0.1,port = 3128;
+.fi
+.PP
+There are four fields that are stepped through in enumerating the number
+space available for creating a new destination:
+.LP
+source address
+.LP
+source port
+.LP
+destination address
+.LP
+destination port
+.PP
+If one of these happens to be a static then it will be skipped and the next
+one incremented. As an example:
+.nf
+
+rewrite out on le0 proto tcp from any to any port = 80 ->
+ src 1.0.0.0/8,5000-5999 dst 2.0.0.0/24,6000-6999;
+.fi
+.PP
+The translated packets would be:
+.LP
+1st src=1.0.0.1,5000 dst=2.0.0.1,6000
+.LP
+2nd src=1.0.0.2,5000 dst=2.0.0.1,6000
+.LP
+3rd src=1.0.0.2,5001 dst=2.0.0.1,6000
+.LP
+4th src=1.0.0.2,5001 dst=2.0.0.2,6000
+.LP
+5th src=1.0.0.2,5001 dst=2.0.0.2,6001
+.LP
+6th src=1.0.0.3,5001 dst=2.0.0.2,6001
+.PP
+and so on.
+.PP
+As with
+.B map
+rules, it is possible to specify a range of addresses by including the word
+\fIrange\fR before the addresses:
+.nf
+
+rewrite from any to any port = 80 ->
+ src 1.1.2.3 - 1.1.2.6 dst 2.2.3.4 - 2.2.3.6;
+.fi
+.SH DIVERTING PACKETS
+.PP
+If you'd like to send packets to a UDP socket rather than just another
+computer to be decapsulated, this can be achieved using a
+.B divert
+rule.
+.PP
+Divert rules can be be used with both inbound and outbound packet
+matching however the rule
+.B must
+specify host addresses for the outer packet, not ranges of addresses
+or netmasks, just single addresses.
+Additionally the syntax must supply required information for UDP.
+An example of what a divert rule looks ike is as follows:
+.nf
+
+divert in on le0 proto udp from any to any port = 53 ->
+ src 192.1.1.1,54 dst 192.168.1.22.1,5300;
+.fi
+.PP
+On the LHS is a normal set of matching capabilities but on the RHS it is
+a requirement to specify both the source and destination addresses and
+ports.
+.PP
+As this feature is intended to be used with targetting packets at sockets
+and not IPFilter running on other systems, there is no rule provided to
+\fIundivert\fR packets.
+.TP
+.B NOTE:
+Diverted packets \fImay\fP be fragmented if the addition of the
+encapsulating IP header plus UDP header causes the packet to exceed
+the size allowed by the outbound network interface. At present it is
+not possible to cause Path MTU discovery to happen as this feature
+is intended to be transparent to both endpoints.
+.B Path MTU Discovery
+If Path MTU discovery is being used and the "do not fragment" flag
+is set in packets to be encapsulated, an ICMP error message will
+be sent back to the sender if the new packet would need to be
+fragmented.
+.SH COMMON OPTIONS
+This section deals with options that are available with all rules.
+.TP
+.B purge
+When the purge keyword is added to the end of a NAT rule, it will
+cause all of the active NAT sessions to be removed when the rule
+is removed as an individual operation. If all of the NAT rules
+are flushed out, it is expected that the operator will similarly
+flush the NAT table and thus NAT sessions are not removed when the
+NAT rules are flushed out.
+.SH RULE ORDERING
+.PP
+.B NOTE:
+Rules in
+.B ipnat.conf
+are read in sequentially as listed and loaded into the kernel in this
+fashion
+.B BUT
+packet matching is done on \fBnetmask\fR, going from 32 down to 0.
+If a rule uses
+.B pool
+or
+.B hash
+to reference a set of addresses or networks, the netmask value for
+these fields is considered to be "0".
+So if your
+.B ipnat.conf
+has the following rules:
+.nf
+
+rdr le0 192.0.0.0/8 port 80 -> 127.0.0.1 3132 tcp
+rdr le0 192.2.0.0/16 port 80 -> 127.0.0.1 3131 tcp
+rdr le0 from any to pool/100 port 80 -> 127.0.0.1 port 3130 tcp
+rdr le0 192.2.2.0/24 port 80 -> 127.0.0.1 3129 tcp
+rdr le0 192.2.2.1 port 80 -> 127.0.0.1 3128 tcp
+.fi
+.PP
+then the rule with 192.2.2.1 will match \fBfirst\fR, regardless of where
+it appears in the ordering of the above rules. In fact, the order in
+which they would be used to match a packet is:
+.nf
+
+rdr le0 192.2.2.1 port 80 -> 127.0.0.1 3128 tcp
+rdr le0 192.2.2.0/24 port 80 -> 127.0.0.1 3129 tcp
+rdr le0 192.2.0.0/16 port 80 -> 127.0.0.1 3131 tcp
+rdr le0 192.0.0.0/8 port 80 -> 127.0.0.1 3132 tcp
+rdr le0 from any to pool/100 port 80 -> 127.0.0.1 port 3130 tcp
+.fi
+.PP
+where the first line is actually a /32.
+.PP
+If your
+.B ipnat.conf
+file has entries with matching target fields (source address for
+.B map
+rules and destination address for
+.B rdr
+rules), then the ordering in the
+.B ipnat.conf
+file does matter. So if you had the following:
+.nf
+
+rdr le0 from 1.1.0.0/16 to 192.2.2.1 port 80 -> 127.0.0.1 3129 tcp
+rdr le0 from 1.1.1.0/24 to 192.2.2.1 port 80 -> 127.0.0.1 3128 tcp
+.fi
+.PP
+Then no packets will match the 2nd rule, they'll all match the first.
+.SH IPv6
+.PP
+In all of the examples above, where an IPv4 address is present, an IPv6
+address can also be used. All rules must use either IPv4 addresses with
+both halves of the NAT rule or IPv6 addresses for both halves. Mixing
+IPv6 addresses with IPv4 addresses, in a single rule, will result in an
+error.
+.PP
+For shorthand notations such as "0/32", the equivalent for IPv6 is
+"0/128". IPFilter will treat any netmask greater than 32 as an
+implicit direction that the address should be IPv6, not IPv4.
+To be unambiguous with 0/0, for IPv6 use ::0/0.
+.SH KERNEL PROXIES
+.PP
+IP Filter comes with a few, simple, proxies built into the code that is loaded
+into the kernel to allow secondary channels to be opened without forcing the
+packets through a user program. The current state of the proxies is listed
+below, as one of three states:
+.HP
+Aging - protocol is roughly understood from
+the time at which the proxy was written but it is not well tested or
+maintained;
+.HP
+Developmental - basic functionality exists, works most of the time but
+may be problematic in extended real use;
+.HP
+Experimental - rough support for the protocol at best, may or may not
+work as testing has been at best sporadic, possible large scale changes
+to the code in order to properly support the protocol.
+.HP
+Mature - well tested, protocol is properly
+understood by the proxy;
+.PP
+The currently compiled in proxy list is as follows:
+.TP
+FTP - Mature
+(map ... proxy port ftp ftp/tcp)
+.TP
+IRC - Experimental
+(proxy port 6667 irc/tcp)
+.TP
+rpcbind - Experimental
+.TP
+PPTP - Experimental
+.TP
+H.323 - Experimental
+(map ... proxy port 1720 h323/tcp)
+.TP
+Real Audio (PNA) - Aging
+.TP
+DNS - Developmental
+(map ... proxy port 53 dns/udp { block .cnn.com; })
+.TP
+IPsec - Developmental
+(map ... proxy port 500 ipsec/tcp)
+.TP
+netbios - Experimental
+.TP
+R-command - Mature
+(map ... proxy port shell rcmd/tcp)
+.SH KERNEL PROXIES
+.SH FILES
+/dev/ipnat
+.br
+/etc/protocols
+.br
+/etc/services
+.br
+/etc/hosts
+.SH SEE ALSO
+ipnat(4), hosts(5), ipf(5), services(5), ipf(8), ipnat(8)
diff --git a/sbin/ipf/ipnat/ipnat.8 b/sbin/ipf/ipnat/ipnat.8
new file mode 100644
index 000000000000..a49f33736b40
--- /dev/null
+++ b/sbin/ipf/ipnat/ipnat.8
@@ -0,0 +1,76 @@
+.\" $FreeBSD$
+.\"
+.TH IPNAT 8
+.SH NAME
+ipnat \- user interface to the NAT subsystem
+.SH SYNOPSIS
+.B ipnat
+[
+.B \-dhlnrsvCF
+]
+[
+.B \-M core
+]
+[
+.B \-N system
+]
+.B \-f <\fIfilename\fP>
+.SH DESCRIPTION
+.PP
+\fBipnat\fP opens the filename given (treating "\-" as stdin) and parses the
+file for a set of rules which are to be added or removed from the IP NAT.
+.PP
+Each rule processed by \fBipnat\fP
+is added to the kernels internal lists if there are no parsing problems.
+Rules are added to the end of the internal lists, matching the order in
+which they appear when given to \fBipnat\fP.
+.PP
+Note that if
+\fBipf(8)\fP
+is not enabled when NAT is configured, it will be enabled
+automatically, as the same kernel facilities are used for
+NAT functionality. In addition, packet forwarding must be
+enabled.
+.SH OPTIONS
+.TP
+.B \-C
+delete all entries in the current NAT rule listing (NAT rules)
+.TP
+.B \-d
+Enable printing of some extra debugging information.
+.TP
+.B \-F
+delete all active entries in the current NAT translation table (currently
+active NAT mappings)
+.TP
+.B \-h
+Print number of hits for each MAP/Redirect filter.
+.TP
+.B \-l
+Show the list of current NAT table entry mappings.
+.TP
+.B \-n
+This flag (no-change) prevents \fBipf\fP from actually making any ioctl
+calls or doing anything which would alter the currently running kernel.
+.TP
+.B \-p
+This flag is used with the \fB-r\fP flag to cause any active NAT
+sessions that were created by the rules being removed and that are
+currently active to also be removed.
+.TP
+.B \-r
+Remove matching NAT rules rather than add them to the internal lists.
+.TP
+.B \-s
+Retrieve and display NAT statistics.
+.TP
+.B \-v
+Turn verbose mode on. Displays information relating to rule processing
+and active rules/table entries.
+.DT
+.SH FILES
+/dev/ipnat
+.br
+/usr/share/examples/ipfilter Directory with examples.
+.SH SEE ALSO
+ipnat(5), ipf(8), ipfstat(8)
diff --git a/sbin/ipf/ipnat/ipnat.c b/sbin/ipf/ipnat/ipnat.c
new file mode 100644
index 000000000000..1ca6e776ffdf
--- /dev/null
+++ b/sbin/ipf/ipnat/ipnat.c
@@ -0,0 +1,842 @@
+/* $FreeBSD$ */
+
+/*
+ * Copyright (C) 2012 by Darren Reed.
+ *
+ * See the IPFILTER.LICENCE file for details on licencing.
+ *
+ * Added redirect stuff and a variety of bug fixes. (mcn@EnGarde.com)
+ */
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#if !defined(__SVR4)
+#include <strings.h>
+#else
+#include <sys/byteorder.h>
+#endif
+#include <sys/time.h>
+#include <sys/param.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <sys/file.h>
+#define _KERNEL
+#include <sys/uio.h>
+#undef _KERNEL
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#if defined(sun) && defined(__SVR4)
+# include <sys/ioccom.h>
+# include <sys/sysmacros.h>
+#endif
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <net/if.h>
+#include <netdb.h>
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+#include <resolv.h>
+#include <ctype.h>
+# include <nlist.h>
+#include "ipf.h"
+#include "netinet/ipl.h"
+#include "kmem.h"
+
+
+# define STRERROR(x) strerror(x)
+
+#if !defined(lint)
+static const char sccsid[] ="@(#)ipnat.c 1.9 6/5/96 (C) 1993 Darren Reed";
+static const char rcsid[] = "@(#)$Id$";
+#endif
+
+
+#if SOLARIS
+#define bzero(a,b) memset(a,0,b)
+#endif
+int use_inet6 = 0;
+
+extern char *optarg;
+
+void dostats(int, natstat_t *, int, int, int *);
+void dotable(natstat_t *, int, int, int, char *);
+void flushtable(int, int, int *);
+void usage(char *);
+int main(int, char*[]);
+void showhostmap(natstat_t *nsp);
+void natstat_dead(natstat_t *, char *);
+void dostats_live(int, natstat_t *, int, int *);
+void showhostmap_dead(natstat_t *);
+void showhostmap_live(int, natstat_t *);
+void dostats_dead(natstat_t *, int, int *);
+int nat_matcharray(nat_t *, int *);
+
+int opts;
+int nohdrfields = 0;
+wordtab_t *nat_fields = NULL;
+
+void usage(name)
+ char *name;
+{
+ fprintf(stderr, "Usage: %s [-CFhlnrRsv] [-f filename]\n", name);
+ exit(1);
+}
+
+
+int main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int fd, c, mode, *natfilter;
+ char *file, *core, *kernel;
+ natstat_t ns, *nsp;
+ ipfobj_t obj;
+
+ fd = -1;
+ opts = 0;
+ nsp = &ns;
+ file = NULL;
+ core = NULL;
+ kernel = NULL;
+ mode = O_RDWR;
+ natfilter = NULL;
+
+ assigndefined(getenv("IPNAT_PREDEFINED"));
+
+ while ((c = getopt(argc, argv, "CdFf:hlm:M:N:nO:prRsv")) != -1)
+ switch (c)
+ {
+ case 'C' :
+ opts |= OPT_CLEAR;
+ break;
+ case 'd' :
+ opts |= OPT_DEBUG;
+ break;
+ case 'f' :
+ file = optarg;
+ break;
+ case 'F' :
+ opts |= OPT_FLUSH;
+ break;
+ case 'h' :
+ opts |=OPT_HITS;
+ break;
+ case 'l' :
+ opts |= OPT_LIST;
+ mode = O_RDONLY;
+ break;
+ case 'm' :
+ natfilter = parseipfexpr(optarg, NULL);
+ break;
+ case 'M' :
+ core = optarg;
+ break;
+ case 'N' :
+ kernel = optarg;
+ break;
+ case 'n' :
+ opts |= OPT_DONOTHING|OPT_DONTOPEN;
+ mode = O_RDONLY;
+ break;
+ case 'O' :
+ nat_fields = parsefields(natfields, optarg);
+ break;
+ case 'p' :
+ opts |= OPT_PURGE;
+ break;
+ case 'R' :
+ opts |= OPT_NORESOLVE;
+ break;
+ case 'r' :
+ opts |= OPT_REMOVE;
+ break;
+ case 's' :
+ opts |= OPT_STAT;
+ mode = O_RDONLY;
+ break;
+ case 'v' :
+ opts |= OPT_VERBOSE;
+ break;
+ default :
+ usage(argv[0]);
+ }
+
+ if (((opts & OPT_PURGE) != 0) && ((opts & OPT_REMOVE) == 0)) {
+ (void) fprintf(stderr, "%s: -p must be used with -r\n",
+ argv[0]);
+ exit(1);
+ }
+
+ initparse();
+
+ if ((kernel != NULL) || (core != NULL)) {
+ (void) setgid(getgid());
+ (void) setuid(getuid());
+ }
+
+ if (!(opts & OPT_DONOTHING)) {
+ if (((fd = open(IPNAT_NAME, mode)) == -1) &&
+ ((fd = open(IPNAT_NAME, O_RDONLY)) == -1)) {
+ (void) fprintf(stderr, "%s: open: %s\n", IPNAT_NAME,
+ STRERROR(errno));
+ exit(1);
+ }
+ }
+
+ bzero((char *)&ns, sizeof(ns));
+
+ if ((opts & OPT_DONOTHING) == 0) {
+ if (checkrev(IPL_NAME) == -1) {
+ fprintf(stderr, "User/kernel version check failed\n");
+ exit(1);
+ }
+ }
+
+ if (!(opts & OPT_DONOTHING) && (kernel == NULL) && (core == NULL)) {
+ bzero((char *)&obj, sizeof(obj));
+ obj.ipfo_rev = IPFILTER_VERSION;
+ obj.ipfo_type = IPFOBJ_NATSTAT;
+ obj.ipfo_size = sizeof(*nsp);
+ obj.ipfo_ptr = (void *)nsp;
+ if (ioctl(fd, SIOCGNATS, &obj) == -1) {
+ ipferror(fd, "ioctl(SIOCGNATS)");
+ exit(1);
+ }
+ (void) setgid(getgid());
+ (void) setuid(getuid());
+ } else if ((kernel != NULL) || (core != NULL)) {
+ if (openkmem(kernel, core) == -1)
+ exit(1);
+
+ natstat_dead(nsp, kernel);
+ if (opts & (OPT_LIST|OPT_STAT))
+ dostats(fd, nsp, opts, 0, natfilter);
+ exit(0);
+ }
+
+ if (opts & (OPT_FLUSH|OPT_CLEAR))
+ flushtable(fd, opts, natfilter);
+ if (file) {
+ return ipnat_parsefile(fd, ipnat_addrule, ioctl, file);
+ }
+ if (opts & (OPT_LIST|OPT_STAT))
+ dostats(fd, nsp, opts, 1, natfilter);
+ return 0;
+}
+
+
+/*
+ * Read NAT statistic information in using a symbol table and memory file
+ * rather than doing ioctl's.
+ */
+void natstat_dead(nsp, kernel)
+ natstat_t *nsp;
+ char *kernel;
+{
+ struct nlist nat_nlist[10] = {
+ { "nat_table" }, /* 0 */
+ { "nat_list" },
+ { "maptable" },
+ { "ipf_nattable_sz" },
+ { "ipf_natrules_sz" },
+ { "ipf_rdrrules_sz" }, /* 5 */
+ { "ipf_hostmap_sz" },
+ { "nat_instances" },
+ { NULL }
+ };
+ void *tables[2];
+
+ if (nlist(kernel, nat_nlist) == -1) {
+ fprintf(stderr, "nlist error\n");
+ return;
+ }
+
+ /*
+ * Normally the ioctl copies all of these values into the structure
+ * for us, before returning it to userland, so here we must copy each
+ * one in individually.
+ */
+ kmemcpy((char *)&tables, nat_nlist[0].n_value, sizeof(tables));
+ nsp->ns_side[0].ns_table = tables[0];
+ nsp->ns_side[1].ns_table = tables[1];
+
+ kmemcpy((char *)&nsp->ns_list, nat_nlist[1].n_value,
+ sizeof(nsp->ns_list));
+ kmemcpy((char *)&nsp->ns_maptable, nat_nlist[2].n_value,
+ sizeof(nsp->ns_maptable));
+ kmemcpy((char *)&nsp->ns_nattab_sz, nat_nlist[3].n_value,
+ sizeof(nsp->ns_nattab_sz));
+ kmemcpy((char *)&nsp->ns_rultab_sz, nat_nlist[4].n_value,
+ sizeof(nsp->ns_rultab_sz));
+ kmemcpy((char *)&nsp->ns_rdrtab_sz, nat_nlist[5].n_value,
+ sizeof(nsp->ns_rdrtab_sz));
+ kmemcpy((char *)&nsp->ns_hostmap_sz, nat_nlist[6].n_value,
+ sizeof(nsp->ns_hostmap_sz));
+ kmemcpy((char *)&nsp->ns_instances, nat_nlist[7].n_value,
+ sizeof(nsp->ns_instances));
+}
+
+
+/*
+ * Issue an ioctl to flush either the NAT rules table or the active mapping
+ * table or both.
+ */
+void flushtable(fd, opts, match)
+ int fd, opts, *match;
+{
+ int n = 0;
+
+ if (opts & OPT_FLUSH) {
+ n = 0;
+ if (!(opts & OPT_DONOTHING)) {
+ if (match != NULL) {
+ ipfobj_t obj;
+
+ obj.ipfo_rev = IPFILTER_VERSION;
+ obj.ipfo_size = match[0] * sizeof(int);
+ obj.ipfo_type = IPFOBJ_IPFEXPR;
+ obj.ipfo_ptr = match;
+ if (ioctl(fd, SIOCMATCHFLUSH, &obj) == -1) {
+ ipferror(fd, "ioctl(SIOCMATCHFLUSH)");
+ n = -1;
+ } else {
+ n = obj.ipfo_retval;
+ }
+ } else if (ioctl(fd, SIOCIPFFL, &n) == -1) {
+ ipferror(fd, "ioctl(SIOCIPFFL)");
+ n = -1;
+ }
+ }
+ if (n >= 0)
+ printf("%d entries flushed from NAT table\n", n);
+ }
+
+ if (opts & OPT_CLEAR) {
+ n = 1;
+ if (!(opts & OPT_DONOTHING) && ioctl(fd, SIOCIPFFL, &n) == -1)
+ ipferror(fd, "ioctl(SIOCCNATL)");
+ else
+ printf("%d entries flushed from NAT list\n", n);
+ }
+}
+
+
+/*
+ * Display NAT statistics.
+ */
+void dostats_dead(nsp, opts, filter)
+ natstat_t *nsp;
+ int opts, *filter;
+{
+ nat_t *np, nat;
+ ipnat_t ipn;
+ int i;
+
+ if (nat_fields == NULL) {
+ printf("List of active MAP/Redirect filters:\n");
+ while (nsp->ns_list) {
+ if (kmemcpy((char *)&ipn, (long)nsp->ns_list,
+ sizeof(ipn))) {
+ perror("kmemcpy");
+ break;
+ }
+ if (opts & OPT_HITS)
+ printf("%lu ", ipn.in_hits);
+ printnat(&ipn, opts & (OPT_DEBUG|OPT_VERBOSE));
+ nsp->ns_list = ipn.in_next;
+ }
+ }
+
+ if (nat_fields == NULL) {
+ printf("\nList of active sessions:\n");
+
+ } else if (nohdrfields == 0) {
+ for (i = 0; nat_fields[i].w_value != 0; i++) {
+ printfieldhdr(natfields, nat_fields + i);
+ if (nat_fields[i + 1].w_value != 0)
+ printf("\t");
+ }
+ printf("\n");
+ }
+
+ for (np = nsp->ns_instances; np; np = nat.nat_next) {
+ if (kmemcpy((char *)&nat, (long)np, sizeof(nat)))
+ break;
+ if ((filter != NULL) && (nat_matcharray(&nat, filter) == 0))
+ continue;
+ if (nat_fields != NULL) {
+ for (i = 0; nat_fields[i].w_value != 0; i++) {
+ printnatfield(&nat, nat_fields[i].w_value);
+ if (nat_fields[i + 1].w_value != 0)
+ printf("\t");
+ }
+ printf("\n");
+ } else {
+ printactivenat(&nat, opts, nsp->ns_ticks);
+ if (nat.nat_aps) {
+ int proto;
+
+ if (nat.nat_dir & NAT_OUTBOUND)
+ proto = nat.nat_pr[1];
+ else
+ proto = nat.nat_pr[0];
+ printaps(nat.nat_aps, opts, proto);
+ }
+ }
+ }
+
+ if (opts & OPT_VERBOSE)
+ showhostmap_dead(nsp);
+}
+
+
+void dotable(nsp, fd, alive, which, side)
+ natstat_t *nsp;
+ int fd, alive, which;
+ char *side;
+{
+ int sz, i, used, maxlen, minlen, totallen;
+ ipftable_t table;
+ u_int *buckets;
+ ipfobj_t obj;
+
+ sz = sizeof(*buckets) * nsp->ns_nattab_sz;
+ buckets = (u_int *)malloc(sz);
+ if (buckets == NULL) {
+ fprintf(stderr,
+ "cannot allocate memory (%d) for buckets\n", sz);
+ return;
+ }
+
+ obj.ipfo_rev = IPFILTER_VERSION;
+ obj.ipfo_type = IPFOBJ_GTABLE;
+ obj.ipfo_size = sizeof(table);
+ obj.ipfo_ptr = &table;
+
+ if (which == 0) {
+ table.ita_type = IPFTABLE_BUCKETS_NATIN;
+ } else if (which == 1) {
+ table.ita_type = IPFTABLE_BUCKETS_NATOUT;
+ }
+ table.ita_table = buckets;
+
+ if (alive) {
+ if (ioctl(fd, SIOCGTABL, &obj) != 0) {
+ ipferror(fd, "SIOCFTABL");
+ free(buckets);
+ return;
+ }
+ } else {
+ if (kmemcpy((char *)buckets, (u_long)nsp->ns_nattab_sz, sz)) {
+ free(buckets);
+ return;
+ }
+ }
+
+ minlen = nsp->ns_side[which].ns_inuse;
+ totallen = 0;
+ maxlen = 0;
+ used = 0;
+
+ for (i = 0; i < nsp->ns_nattab_sz; i++) {
+ if (buckets[i] > maxlen)
+ maxlen = buckets[i];
+ if (buckets[i] < minlen)
+ minlen = buckets[i];
+ if (buckets[i] != 0)
+ used++;
+ totallen += buckets[i];
+ }
+
+ printf("%d%%\thash efficiency %s\n",
+ totallen ? used * 100 / totallen : 0, side);
+ printf("%2.2f%%\tbucket usage %s\n",
+ ((float)used / nsp->ns_nattab_sz) * 100.0, side);
+ printf("%d\tminimal length %s\n", minlen, side);
+ printf("%d\tmaximal length %s\n", maxlen, side);
+ printf("%.3f\taverage length %s\n",
+ used ? ((float)totallen / used) : 0.0, side);
+
+ free(buckets);
+}
+
+
+void dostats(fd, nsp, opts, alive, filter)
+ natstat_t *nsp;
+ int fd, opts, alive, *filter;
+{
+ /*
+ * Show statistics ?
+ */
+ if (opts & OPT_STAT) {
+ printnatside("in", &nsp->ns_side[0]);
+ dotable(nsp, fd, alive, 0, "in");
+
+ printnatside("out", &nsp->ns_side[1]);
+ dotable(nsp, fd, alive, 1, "out");
+
+ printf("%lu\tlog successes\n", nsp->ns_side[0].ns_log);
+ printf("%lu\tlog failures\n", nsp->ns_side[1].ns_log);
+ printf("%lu\tadded in\n%lu\tadded out\n",
+ nsp->ns_side[0].ns_added,
+ nsp->ns_side[1].ns_added);
+ printf("%u\tactive\n", nsp->ns_active);
+ printf("%lu\ttransparent adds\n", nsp->ns_addtrpnt);
+ printf("%lu\tdivert build\n", nsp->ns_divert_build);
+ printf("%lu\texpired\n", nsp->ns_expire);
+ printf("%lu\tflush all\n", nsp->ns_flush_all);
+ printf("%lu\tflush closing\n", nsp->ns_flush_closing);
+ printf("%lu\tflush queue\n", nsp->ns_flush_queue);
+ printf("%lu\tflush state\n", nsp->ns_flush_state);
+ printf("%lu\tflush timeout\n", nsp->ns_flush_timeout);
+ printf("%lu\thostmap new\n", nsp->ns_hm_new);
+ printf("%lu\thostmap fails\n", nsp->ns_hm_newfail);
+ printf("%lu\thostmap add\n", nsp->ns_hm_addref);
+ printf("%lu\thostmap NULL rule\n", nsp->ns_hm_nullnp);
+ printf("%lu\tlog ok\n", nsp->ns_log_ok);
+ printf("%lu\tlog fail\n", nsp->ns_log_fail);
+ printf("%u\torphan count\n", nsp->ns_orphans);
+ printf("%u\trule count\n", nsp->ns_rules);
+ printf("%u\tmap rules\n", nsp->ns_rules_map);
+ printf("%u\trdr rules\n", nsp->ns_rules_rdr);
+ printf("%u\twilds\n", nsp->ns_wilds);
+ if (opts & OPT_VERBOSE)
+ printf("list %p\n", nsp->ns_list);
+ }
+
+ if (opts & OPT_LIST) {
+ if (alive)
+ dostats_live(fd, nsp, opts, filter);
+ else
+ dostats_dead(nsp, opts, filter);
+ }
+}
+
+
+/*
+ * Display NAT statistics.
+ */
+void dostats_live(fd, nsp, opts, filter)
+ natstat_t *nsp;
+ int fd, opts, *filter;
+{
+ ipfgeniter_t iter;
+ char buffer[2000];
+ ipfobj_t obj;
+ ipnat_t *ipn;
+ nat_t nat;
+ int i;
+
+ bzero((char *)&obj, sizeof(obj));
+ obj.ipfo_rev = IPFILTER_VERSION;
+ obj.ipfo_type = IPFOBJ_GENITER;
+ obj.ipfo_size = sizeof(iter);
+ obj.ipfo_ptr = &iter;
+
+ iter.igi_type = IPFGENITER_IPNAT;
+ iter.igi_nitems = 1;
+ iter.igi_data = buffer;
+ ipn = (ipnat_t *)buffer;
+
+ /*
+ * Show list of NAT rules and NAT sessions ?
+ */
+ if (nat_fields == NULL) {
+ printf("List of active MAP/Redirect filters:\n");
+ while (nsp->ns_list) {
+ if (ioctl(fd, SIOCGENITER, &obj) == -1)
+ break;
+ if (opts & OPT_HITS)
+ printf("%lu ", ipn->in_hits);
+ printnat(ipn, opts & (OPT_DEBUG|OPT_VERBOSE));
+ nsp->ns_list = ipn->in_next;
+ }
+ }
+
+ if (nat_fields == NULL) {
+ printf("\nList of active sessions:\n");
+
+ } else if (nohdrfields == 0) {
+ for (i = 0; nat_fields[i].w_value != 0; i++) {
+ printfieldhdr(natfields, nat_fields + i);
+ if (nat_fields[i + 1].w_value != 0)
+ printf("\t");
+ }
+ printf("\n");
+ }
+
+ i = IPFGENITER_IPNAT;
+ (void) ioctl(fd,SIOCIPFDELTOK, &i);
+
+
+ iter.igi_type = IPFGENITER_NAT;
+ iter.igi_nitems = 1;
+ iter.igi_data = &nat;
+
+ while (nsp->ns_instances != NULL) {
+ if (ioctl(fd, SIOCGENITER, &obj) == -1)
+ break;
+ if ((filter != NULL) && (nat_matcharray(&nat, filter) == 0))
+ continue;
+ if (nat_fields != NULL) {
+ for (i = 0; nat_fields[i].w_value != 0; i++) {
+ printnatfield(&nat, nat_fields[i].w_value);
+ if (nat_fields[i + 1].w_value != 0)
+ printf("\t");
+ }
+ printf("\n");
+ } else {
+ printactivenat(&nat, opts, nsp->ns_ticks);
+ if (nat.nat_aps) {
+ int proto;
+
+ if (nat.nat_dir & NAT_OUTBOUND)
+ proto = nat.nat_pr[1];
+ else
+ proto = nat.nat_pr[0];
+ printaps(nat.nat_aps, opts, proto);
+ }
+ }
+ nsp->ns_instances = nat.nat_next;
+ }
+
+ if (opts & OPT_VERBOSE)
+ showhostmap_live(fd, nsp);
+
+ i = IPFGENITER_NAT;
+ (void) ioctl(fd,SIOCIPFDELTOK, &i);
+}
+
+
+/*
+ * Display the active host mapping table.
+ */
+void showhostmap_dead(nsp)
+ natstat_t *nsp;
+{
+ hostmap_t hm, *hmp, **maptable;
+ u_int hv;
+
+ printf("\nList of active host mappings:\n");
+
+ maptable = (hostmap_t **)malloc(sizeof(hostmap_t *) *
+ nsp->ns_hostmap_sz);
+ if (kmemcpy((char *)maptable, (u_long)nsp->ns_maptable,
+ sizeof(hostmap_t *) * nsp->ns_hostmap_sz)) {
+ perror("kmemcpy (maptable)");
+ return;
+ }
+
+ for (hv = 0; hv < nsp->ns_hostmap_sz; hv++) {
+ hmp = maptable[hv];
+
+ while (hmp) {
+ if (kmemcpy((char *)&hm, (u_long)hmp, sizeof(hm))) {
+ perror("kmemcpy (hostmap)");
+ return;
+ }
+
+ printhostmap(&hm, hv);
+ hmp = hm.hm_next;
+ }
+ }
+ free(maptable);
+}
+
+
+/*
+ * Display the active host mapping table.
+ */
+void showhostmap_live(fd, nsp)
+ int fd;
+ natstat_t *nsp;
+{
+ ipfgeniter_t iter;
+ hostmap_t hm;
+ ipfobj_t obj;
+ int i;
+
+ bzero((char *)&obj, sizeof(obj));
+ obj.ipfo_rev = IPFILTER_VERSION;
+ obj.ipfo_type = IPFOBJ_GENITER;
+ obj.ipfo_size = sizeof(iter);
+ obj.ipfo_ptr = &iter;
+
+ iter.igi_type = IPFGENITER_HOSTMAP;
+ iter.igi_nitems = 1;
+ iter.igi_data = &hm;
+
+ printf("\nList of active host mappings:\n");
+
+ while (nsp->ns_maplist != NULL) {
+ if (ioctl(fd, SIOCGENITER, &obj) == -1)
+ break;
+ printhostmap(&hm, hm.hm_hv);
+ nsp->ns_maplist = hm.hm_next;
+ }
+
+ i = IPFGENITER_HOSTMAP;
+ (void) ioctl(fd,SIOCIPFDELTOK, &i);
+}
+
+
+int nat_matcharray(nat, array)
+ nat_t *nat;
+ int *array;
+{
+ int i, n, *x, rv, p;
+ ipfexp_t *e;
+
+ rv = 0;
+ n = array[0];
+ x = array + 1;
+
+ for (; n > 0; x += 3 + x[3], rv = 0) {
+ e = (ipfexp_t *)x;
+ if (e->ipfe_cmd == IPF_EXP_END)
+ break;
+ n -= e->ipfe_size;
+
+ p = e->ipfe_cmd >> 16;
+ if ((p != 0) && (p != nat->nat_pr[1]))
+ break;
+
+ switch (e->ipfe_cmd)
+ {
+ case IPF_EXP_IP_PR :
+ for (i = 0; !rv && i < e->ipfe_narg; i++) {
+ rv |= (nat->nat_pr[1] == e->ipfe_arg0[i]);
+ }
+ break;
+
+ case IPF_EXP_IP_SRCADDR :
+ if (nat->nat_v[0] != 4)
+ break;
+ for (i = 0; !rv && i < e->ipfe_narg; i++) {
+ rv |= ((nat->nat_osrcaddr &
+ e->ipfe_arg0[i * 2 + 1]) ==
+ e->ipfe_arg0[i * 2]) ||
+ ((nat->nat_nsrcaddr &
+ e->ipfe_arg0[i * 2 + 1]) ==
+ e->ipfe_arg0[i * 2]);
+ }
+ break;
+
+ case IPF_EXP_IP_DSTADDR :
+ if (nat->nat_v[0] != 4)
+ break;
+ for (i = 0; !rv && i < e->ipfe_narg; i++) {
+ rv |= ((nat->nat_odstaddr &
+ e->ipfe_arg0[i * 2 + 1]) ==
+ e->ipfe_arg0[i * 2]) ||
+ ((nat->nat_ndstaddr &
+ e->ipfe_arg0[i * 2 + 1]) ==
+ e->ipfe_arg0[i * 2]);
+ }
+ break;
+
+ case IPF_EXP_IP_ADDR :
+ if (nat->nat_v[0] != 4)
+ break;
+ for (i = 0; !rv && i < e->ipfe_narg; i++) {
+ rv |= ((nat->nat_osrcaddr &
+ e->ipfe_arg0[i * 2 + 1]) ==
+ e->ipfe_arg0[i * 2]) ||
+ ((nat->nat_nsrcaddr &
+ e->ipfe_arg0[i * 2 + 1]) ==
+ e->ipfe_arg0[i * 2]) ||
+ ((nat->nat_odstaddr &
+ e->ipfe_arg0[i * 2 + 1]) ==
+ e->ipfe_arg0[i * 2]) ||
+ ((nat->nat_ndstaddr &
+ e->ipfe_arg0[i * 2 + 1]) ==
+ e->ipfe_arg0[i * 2]);
+ }
+ break;
+
+#ifdef USE_INET6
+ case IPF_EXP_IP6_SRCADDR :
+ if (nat->nat_v[0] != 6)
+ break;
+ for (i = 0; !rv && i < e->ipfe_narg; i++) {
+ rv |= IP6_MASKEQ(&nat->nat_osrc6,
+ &e->ipfe_arg0[i * 8 + 4],
+ &e->ipfe_arg0[i * 8]) ||
+ IP6_MASKEQ(&nat->nat_nsrc6,
+ &e->ipfe_arg0[i * 8 + 4],
+ &e->ipfe_arg0[i * 8]);
+ }
+ break;
+
+ case IPF_EXP_IP6_DSTADDR :
+ if (nat->nat_v[0] != 6)
+ break;
+ for (i = 0; !rv && i < e->ipfe_narg; i++) {
+ rv |= IP6_MASKEQ(&nat->nat_odst6,
+ &e->ipfe_arg0[i * 8 + 4],
+ &e->ipfe_arg0[i * 8]) ||
+ IP6_MASKEQ(&nat->nat_ndst6,
+ &e->ipfe_arg0[i * 8 + 4],
+ &e->ipfe_arg0[i * 8]);
+ }
+ break;
+
+ case IPF_EXP_IP6_ADDR :
+ if (nat->nat_v[0] != 6)
+ break;
+ for (i = 0; !rv && i < e->ipfe_narg; i++) {
+ rv |= IP6_MASKEQ(&nat->nat_osrc6,
+ &e->ipfe_arg0[i * 8 + 4],
+ &e->ipfe_arg0[i * 8]) ||
+ IP6_MASKEQ(&nat->nat_nsrc6,
+ &e->ipfe_arg0[i * 8 + 4],
+ &e->ipfe_arg0[i * 8]) ||
+ IP6_MASKEQ(&nat->nat_odst6,
+ &e->ipfe_arg0[i * 8 + 4],
+ &e->ipfe_arg0[i * 8]) ||
+ IP6_MASKEQ(&nat->nat_ndst6,
+ &e->ipfe_arg0[i * 8 + 4],
+ &e->ipfe_arg0[i * 8]);
+ }
+ break;
+#endif
+
+ case IPF_EXP_UDP_PORT :
+ case IPF_EXP_TCP_PORT :
+ for (i = 0; !rv && i < e->ipfe_narg; i++) {
+ rv |= (nat->nat_osport == e->ipfe_arg0[i]) ||
+ (nat->nat_nsport == e->ipfe_arg0[i]) ||
+ (nat->nat_odport == e->ipfe_arg0[i]) ||
+ (nat->nat_ndport == e->ipfe_arg0[i]);
+ }
+ break;
+
+ case IPF_EXP_UDP_SPORT :
+ case IPF_EXP_TCP_SPORT :
+ for (i = 0; !rv && i < e->ipfe_narg; i++) {
+ rv |= (nat->nat_osport == e->ipfe_arg0[i]) ||
+ (nat->nat_nsport == e->ipfe_arg0[i]);
+ }
+ break;
+
+ case IPF_EXP_UDP_DPORT :
+ case IPF_EXP_TCP_DPORT :
+ for (i = 0; !rv && i < e->ipfe_narg; i++) {
+ rv |= (nat->nat_odport == e->ipfe_arg0[i]) ||
+ (nat->nat_ndport == e->ipfe_arg0[i]);
+ }
+ break;
+ }
+ rv ^= e->ipfe_not;
+
+ if (rv == 0)
+ break;
+ }
+
+ return rv;
+}
diff --git a/sbin/ipf/ipnat/ipnat_y.y b/sbin/ipf/ipnat/ipnat_y.y
new file mode 100644
index 000000000000..a6a5a0e49d76
--- /dev/null
+++ b/sbin/ipf/ipnat/ipnat_y.y
@@ -0,0 +1,1774 @@
+/* $FreeBSD$ */
+
+/*
+ * Copyright (C) 2012 by Darren Reed.
+ *
+ * See the IPFILTER.LICENCE file for details on licencing.
+ */
+%{
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#if !defined(__SVR4) && !defined(__GNUC__)
+#include <strings.h>
+#endif
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/file.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <sys/time.h>
+#include <syslog.h>
+#include <net/if.h>
+#include <netdb.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include "ipf.h"
+#include "netinet/ipl.h"
+#include "ipnat_l.h"
+
+#define YYDEBUG 1
+
+extern void yyerror(char *);
+extern int yyparse(void);
+extern int yylex(void);
+extern int yydebug;
+extern FILE *yyin;
+extern int yylineNum;
+
+static ipnat_t *nattop = NULL;
+static ipnat_t *nat = NULL;
+static int natfd = -1;
+static ioctlfunc_t natioctlfunc = NULL;
+static addfunc_t nataddfunc = NULL;
+static int suggest_port = 0;
+static proxyrule_t *prules = NULL;
+static int parser_error = 0;
+
+static void newnatrule(void);
+static void setnatproto(int);
+static void setmapifnames(void);
+static void setrdrifnames(void);
+static void proxy_setconfig(int);
+static void proxy_unsetconfig(void);
+static namelist_t *proxy_dns_add_pass(char *, char *);
+static namelist_t *proxy_dns_add_block(char *, char *);
+static void proxy_addconfig(char *, int, char *, namelist_t *);
+static void proxy_loadconfig(int, ioctlfunc_t, char *, int,
+ char *, namelist_t *);
+static void proxy_loadrules(int, ioctlfunc_t, proxyrule_t *);
+static void setmapifnames(void);
+static void setrdrifnames(void);
+static void setifname(ipnat_t **, int, char *);
+static int addname(ipnat_t **, char *);
+%}
+%union {
+ char *str;
+ u_32_t num;
+ struct {
+ i6addr_t a;
+ int f;
+ } ipa;
+ frentry_t fr;
+ frtuc_t *frt;
+ u_short port;
+ struct {
+ int p1;
+ int p2;
+ int pc;
+ } pc;
+ struct {
+ i6addr_t a;
+ i6addr_t m;
+ int t; /* Address type */
+ int u;
+ int f; /* Family */
+ int v; /* IP version */
+ int s; /* 0 = number, 1 = text */
+ int n; /* number */
+ } ipp;
+ union i6addr ip6;
+ namelist_t *names;
+};
+
+%token <num> YY_NUMBER YY_HEX
+%token <str> YY_STR
+%token YY_COMMENT
+%token YY_CMP_EQ YY_CMP_NE YY_CMP_LE YY_CMP_GE YY_CMP_LT YY_CMP_GT
+%token YY_RANGE_OUT YY_RANGE_IN
+%token <ip6> YY_IPV6
+
+%token IPNY_MAPBLOCK IPNY_RDR IPNY_PORT IPNY_PORTS IPNY_AUTO IPNY_RANGE
+%token IPNY_MAP IPNY_BIMAP IPNY_FROM IPNY_TO IPNY_MASK IPNY_PORTMAP IPNY_ANY
+%token IPNY_ROUNDROBIN IPNY_FRAG IPNY_AGE IPNY_ICMPIDMAP IPNY_PROXY
+%token IPNY_TCP IPNY_UDP IPNY_TCPUDP IPNY_STICKY IPNY_MSSCLAMP IPNY_TAG
+%token IPNY_TLATE IPNY_POOL IPNY_HASH IPNY_NO IPNY_REWRITE IPNY_PROTO
+%token IPNY_ON IPNY_SRC IPNY_DST IPNY_IN IPNY_OUT IPNY_DIVERT
+%token IPNY_CONFIG IPNY_ALLOW IPNY_DENY IPNY_DNS IPNY_INET IPNY_INET6
+%token IPNY_SEQUENTIAL IPNY_DSTLIST IPNY_PURGE
+%type <port> portspec
+%type <num> hexnumber compare range proto
+%type <num> saddr daddr sobject dobject mapfrom rdrfrom dip
+%type <ipa> hostname ipv4 ipaddr
+%type <ipp> addr rhsaddr rhdaddr erhdaddr
+%type <pc> portstuff portpair comaports srcports dstports
+%type <names> dnslines dnsline
+%%
+file: line
+ | assign
+ | file line
+ | file assign
+ | file pconf ';'
+ ;
+
+line: xx rule { int err;
+ while ((nat = nattop) != NULL) {
+ if (nat->in_v[0] == 0)
+ nat->in_v[0] = 4;
+ if (nat->in_v[1] == 0)
+ nat->in_v[1] = nat->in_v[0];
+ nattop = nat->in_next;
+ err = (*nataddfunc)(natfd, natioctlfunc, nat);
+ free(nat);
+ if (err != 0) {
+ parser_error = err;
+ break;
+ }
+ }
+ if (parser_error == 0 && prules != NULL) {
+ proxy_loadrules(natfd, natioctlfunc, prules);
+ prules = NULL;
+ }
+ resetlexer();
+ }
+ | YY_COMMENT
+ ;
+
+assign: YY_STR assigning YY_STR ';' { set_variable($1, $3);
+ resetlexer();
+ free($1);
+ free($3);
+ yyvarnext = 0;
+ }
+ ;
+
+assigning:
+ '=' { yyvarnext = 1; }
+ ;
+
+xx: { newnatrule(); }
+ ;
+
+rule: map eol
+ | mapblock eol
+ | redir eol
+ | rewrite ';'
+ | divert ';'
+ ;
+
+no: IPNY_NO { nat->in_flags |= IPN_NO; }
+ ;
+
+eol: | ';'
+ ;
+
+map: mapit ifnames addr tlate rhsaddr proxy mapoptions
+ { if ($3.f != 0 && $3.f != $5.f && $5.f != 0)
+ yyerror("3.address family mismatch");
+ if (nat->in_v[0] == 0 && $5.v != 0)
+ nat->in_v[0] = $5.v;
+ else if (nat->in_v[0] == 0 && $3.v != 0)
+ nat->in_v[0] = $3.v;
+ if (nat->in_v[1] == 0 && $5.v != 0)
+ nat->in_v[1] = $5.v;
+ else if (nat->in_v[1] == 0 && $3.v != 0)
+ nat->in_v[1] = $3.v;
+ nat->in_osrcatype = $3.t;
+ bcopy(&$3.a, &nat->in_osrc.na_addr[0],
+ sizeof($3.a));
+ bcopy(&$3.m, &nat->in_osrc.na_addr[1],
+ sizeof($3.a));
+ nat->in_nsrcatype = $5.t;
+ nat->in_nsrcafunc = $5.u;
+ bcopy(&$5.a, &nat->in_nsrc.na_addr[0],
+ sizeof($5.a));
+ bcopy(&$5.m, &nat->in_nsrc.na_addr[1],
+ sizeof($5.a));
+
+ setmapifnames();
+ }
+ | mapit ifnames addr tlate rhsaddr mapport mapoptions
+ { if ($3.f != $5.f && $3.f != 0 && $5.f != 0)
+ yyerror("4.address family mismatch");
+ if (nat->in_v[1] == 0 && $5.v != 0)
+ nat->in_v[1] = $5.v;
+ else if (nat->in_v[0] == 0 && $3.v != 0)
+ nat->in_v[0] = $3.v;
+ if (nat->in_v[0] == 0 && $5.v != 0)
+ nat->in_v[0] = $5.v;
+ else if (nat->in_v[1] == 0 && $3.v != 0)
+ nat->in_v[1] = $3.v;
+ nat->in_osrcatype = $3.t;
+ bcopy(&$3.a, &nat->in_osrc.na_addr[0],
+ sizeof($3.a));
+ bcopy(&$3.m, &nat->in_osrc.na_addr[1],
+ sizeof($3.a));
+ nat->in_nsrcatype = $5.t;
+ nat->in_nsrcafunc = $5.u;
+ bcopy(&$5.a, &nat->in_nsrc.na_addr[0],
+ sizeof($5.a));
+ bcopy(&$5.m, &nat->in_nsrc.na_addr[1],
+ sizeof($5.a));
+
+ setmapifnames();
+ }
+ | no mapit ifnames addr setproto ';'
+ { if (nat->in_v[0] == 0)
+ nat->in_v[0] = $4.v;
+ nat->in_osrcatype = $4.t;
+ bcopy(&$4.a, &nat->in_osrc.na_addr[0],
+ sizeof($4.a));
+ bcopy(&$4.m, &nat->in_osrc.na_addr[1],
+ sizeof($4.a));
+
+ setmapifnames();
+ }
+ | mapit ifnames mapfrom tlate rhsaddr proxy mapoptions
+ { if ($3 != 0 && $5.f != 0 && $3 != $5.f)
+ yyerror("5.address family mismatch");
+ if (nat->in_v[0] == 0 && $5.v != 0)
+ nat->in_v[0] = $5.v;
+ else if (nat->in_v[0] == 0 && $3 != 0)
+ nat->in_v[0] = ftov($3);
+ if (nat->in_v[1] == 0 && $5.v != 0)
+ nat->in_v[1] = $5.v;
+ else if (nat->in_v[1] == 0 && $3 != 0)
+ nat->in_v[1] = ftov($3);
+ nat->in_nsrcatype = $5.t;
+ nat->in_nsrcafunc = $5.u;
+ bcopy(&$5.a, &nat->in_nsrc.na_addr[0],
+ sizeof($5.a));
+ bcopy(&$5.m, &nat->in_nsrc.na_addr[1],
+ sizeof($5.a));
+
+ setmapifnames();
+ }
+ | no mapit ifnames mapfrom setproto ';'
+ { nat->in_v[0] = ftov($4);
+ setmapifnames();
+ }
+ | mapit ifnames mapfrom tlate rhsaddr mapport mapoptions
+ { if ($3 != 0 && $5.f != 0 && $3 != $5.f)
+ yyerror("6.address family mismatch");
+ if (nat->in_v[0] == 0 && $5.v != 0)
+ nat->in_v[0] = $5.v;
+ else if (nat->in_v[0] == 0 && $3 != 0)
+ nat->in_v[0] = ftov($3);
+ if (nat->in_v[1] == 0 && $5.v != 0)
+ nat->in_v[1] = $5.v;
+ else if (nat->in_v[1] == 0 && $3 != 0)
+ nat->in_v[1] = ftov($3);
+ nat->in_nsrcatype = $5.t;
+ nat->in_nsrcafunc = $5.u;
+ bcopy(&$5.a, &nat->in_nsrc.na_addr[0],
+ sizeof($5.a));
+ bcopy(&$5.m, &nat->in_nsrc.na_addr[1],
+ sizeof($5.a));
+
+ setmapifnames();
+ }
+ ;
+
+mapblock:
+ mapblockit ifnames addr tlate addr ports mapoptions
+ { if ($3.f != 0 && $5.f != 0 && $3.f != $5.f)
+ yyerror("7.address family mismatch");
+ if (nat->in_v[0] == 0 && $5.v != 0)
+ nat->in_v[0] = $5.v;
+ else if (nat->in_v[0] == 0 && $3.v != 0)
+ nat->in_v[0] = $3.v;
+ if (nat->in_v[1] == 0 && $5.v != 0)
+ nat->in_v[1] = $5.v;
+ else if (nat->in_v[1] == 0 && $3.v != 0)
+ nat->in_v[1] = $3.v;
+ nat->in_osrcatype = $3.t;
+ bcopy(&$3.a, &nat->in_osrc.na_addr[0],
+ sizeof($3.a));
+ bcopy(&$3.m, &nat->in_osrc.na_addr[1],
+ sizeof($3.a));
+ nat->in_nsrcatype = $5.t;
+ nat->in_nsrcafunc = $5.u;
+ bcopy(&$5.a, &nat->in_nsrc.na_addr[0],
+ sizeof($5.a));
+ bcopy(&$5.m, &nat->in_nsrc.na_addr[1],
+ sizeof($5.a));
+
+ setmapifnames();
+ }
+ | no mapblockit ifnames { yyexpectaddr = 1; } addr setproto ';'
+ { if (nat->in_v[0] == 0)
+ nat->in_v[0] = $5.v;
+ if (nat->in_v[1] == 0)
+ nat->in_v[1] = $5.v;
+ nat->in_osrcatype = $5.t;
+ bcopy(&$5.a, &nat->in_osrc.na_addr[0],
+ sizeof($5.a));
+ bcopy(&$5.m, &nat->in_osrc.na_addr[1],
+ sizeof($5.a));
+
+ setmapifnames();
+ }
+ ;
+
+redir: rdrit ifnames addr dport tlate dip nport setproto rdroptions
+ { if ($6 != 0 && $3.f != 0 && $6 != $3.f)
+ yyerror("21.address family mismatch");
+ if (nat->in_v[0] == 0) {
+ if ($3.v != AF_UNSPEC)
+ nat->in_v[0] = ftov($3.f);
+ else
+ nat->in_v[0] = ftov($6);
+ }
+ nat->in_odstatype = $3.t;
+ bcopy(&$3.a, &nat->in_odst.na_addr[0],
+ sizeof($3.a));
+ bcopy(&$3.m, &nat->in_odst.na_addr[1],
+ sizeof($3.a));
+
+ setrdrifnames();
+ }
+ | no rdrit ifnames addr dport setproto ';'
+ { if (nat->in_v[0] == 0)
+ nat->in_v[0] = ftov($4.f);
+ nat->in_odstatype = $4.t;
+ bcopy(&$4.a, &nat->in_odst.na_addr[0],
+ sizeof($4.a));
+ bcopy(&$4.m, &nat->in_odst.na_addr[1],
+ sizeof($4.a));
+
+ setrdrifnames();
+ }
+ | rdrit ifnames rdrfrom tlate dip nport setproto rdroptions
+ { if ($5 != 0 && $3 != 0 && $5 != $3)
+ yyerror("20.address family mismatch");
+ if (nat->in_v[0] == 0) {
+ if ($3 != AF_UNSPEC)
+ nat->in_v[0] = ftov($3);
+ else
+ nat->in_v[0] = ftov($5);
+ }
+ setrdrifnames();
+ }
+ | no rdrit ifnames rdrfrom setproto ';'
+ { nat->in_v[0] = ftov($4);
+
+ setrdrifnames();
+ }
+ ;
+
+rewrite:
+ IPNY_REWRITE oninout rwrproto mapfrom tlate newdst newopts
+ { if (nat->in_v[0] == 0)
+ nat->in_v[0] = ftov($4);
+ if (nat->in_redir & NAT_MAP)
+ setmapifnames();
+ else
+ setrdrifnames();
+ nat->in_redir |= NAT_REWRITE;
+ }
+ ;
+
+divert: IPNY_DIVERT oninout rwrproto mapfrom tlate divdst newopts
+ { if (nat->in_v[0] == 0)
+ nat->in_v[0] = ftov($4);
+ if (nat->in_redir & NAT_MAP) {
+ setmapifnames();
+ nat->in_pr[0] = IPPROTO_UDP;
+ } else {
+ setrdrifnames();
+ nat->in_pr[1] = IPPROTO_UDP;
+ }
+ nat->in_flags &= ~IPN_TCP;
+ }
+ ;
+
+tlate: IPNY_TLATE { yyexpectaddr = 1; }
+ ;
+
+pconf: IPNY_PROXY { yysetdict(proxies); }
+ IPNY_DNS '/' proto IPNY_CONFIG YY_STR '{'
+ { proxy_setconfig(IPNY_DNS); }
+ dnslines ';' '}'
+ { proxy_addconfig("dns", $5, $7, $10);
+ proxy_unsetconfig();
+ }
+ ;
+
+dnslines:
+ dnsline { $$ = $1; }
+ | dnslines ';' dnsline { $$ = $1; $1->na_next = $3; }
+ ;
+
+dnsline:
+ IPNY_ALLOW YY_STR { $$ = proxy_dns_add_pass(NULL, $2); }
+ | IPNY_DENY YY_STR { $$ = proxy_dns_add_block(NULL, $2); }
+ | IPNY_ALLOW '.' YY_STR { $$ = proxy_dns_add_pass(".", $3); }
+ | IPNY_DENY '.' YY_STR { $$ = proxy_dns_add_block(".", $3); }
+ ;
+
+oninout:
+ inout IPNY_ON ifnames { ; }
+ ;
+
+inout: IPNY_IN { nat->in_redir = NAT_REDIRECT; }
+ | IPNY_OUT { nat->in_redir = NAT_MAP; }
+ ;
+
+rwrproto:
+ | IPNY_PROTO setproto
+ ;
+
+newdst: src rhsaddr srcports dst erhdaddr dstports
+ { nat->in_nsrc.na_addr[0] = $2.a;
+ nat->in_nsrc.na_addr[1] = $2.m;
+ nat->in_nsrc.na_atype = $2.t;
+ if ($2.t == FRI_LOOKUP) {
+ nat->in_nsrc.na_type = $2.u;
+ nat->in_nsrc.na_subtype = $2.s;
+ nat->in_nsrc.na_num = $2.n;
+ }
+ nat->in_nsports[0] = $3.p1;
+ nat->in_nsports[1] = $3.p2;
+ nat->in_ndst.na_addr[0] = $5.a;
+ nat->in_ndst.na_addr[1] = $5.m;
+ nat->in_ndst.na_atype = $5.t;
+ if ($5.t == FRI_LOOKUP) {
+ nat->in_ndst.na_type = $5.u;
+ nat->in_ndst.na_subtype = $5.s;
+ nat->in_ndst.na_num = $5.n;
+ }
+ nat->in_ndports[0] = $6.p1;
+ nat->in_ndports[1] = $6.p2;
+ }
+ ;
+
+divdst: src addr ',' portspec dst addr ',' portspec IPNY_UDP
+ { nat->in_nsrc.na_addr[0] = $2.a;
+ if ($2.m.in4.s_addr != 0xffffffff)
+ yyerror("divert must have /32 dest");
+ nat->in_nsrc.na_addr[1] = $2.m;
+ nat->in_nsports[0] = $4;
+ nat->in_nsports[1] = $4;
+
+ nat->in_ndst.na_addr[0] = $6.a;
+ nat->in_ndst.na_addr[1] = $6.m;
+ if ($6.m.in4.s_addr != 0xffffffff)
+ yyerror("divert must have /32 dest");
+ nat->in_ndports[0] = $8;
+ nat->in_ndports[1] = $8;
+
+ nat->in_redir |= NAT_DIVERTUDP;
+ }
+ ;
+
+src: IPNY_SRC { yyexpectaddr = 1; }
+ ;
+
+dst: IPNY_DST { yyexpectaddr = 1; }
+ ;
+
+srcports:
+ comaports { $$.p1 = $1.p1;
+ $$.p2 = $1.p2;
+ }
+ | IPNY_PORT '=' portspec
+ { $$.p1 = $3;
+ $$.p2 = $3;
+ nat->in_flags |= IPN_FIXEDSPORT;
+ }
+ ;
+
+dstports:
+ comaports { $$.p1 = $1.p1;
+ $$.p2 = $1.p2;
+ }
+ | IPNY_PORT '=' portspec
+ { $$.p1 = $3;
+ $$.p2 = $3;
+ nat->in_flags |= IPN_FIXEDDPORT;
+ }
+ ;
+
+comaports:
+ { $$.p1 = 0;
+ $$.p2 = 0;
+ }
+ | ',' { if (!(nat->in_flags & IPN_TCPUDP))
+ yyerror("must be TCP/UDP for ports");
+ }
+ portpair { $$.p1 = $3.p1;
+ $$.p2 = $3.p2;
+ }
+ ;
+
+proxy: | IPNY_PROXY port portspec YY_STR '/' proto
+ { int pos;
+ pos = addname(&nat, $4);
+ nat->in_plabel = pos;
+ if (nat->in_dcmp == 0) {
+ nat->in_odport = $3;
+ } else if ($3 != nat->in_odport) {
+ yyerror("proxy port numbers not consistant");
+ }
+ nat->in_ndport = $3;
+ setnatproto($6);
+ free($4);
+ }
+ | IPNY_PROXY port YY_STR YY_STR '/' proto
+ { int pnum, pos;
+ pos = addname(&nat, $4);
+ nat->in_plabel = pos;
+ pnum = getportproto($3, $6);
+ if (pnum == -1)
+ yyerror("invalid port number");
+ nat->in_odport = ntohs(pnum);
+ nat->in_ndport = ntohs(pnum);
+ setnatproto($6);
+ free($3);
+ free($4);
+ }
+ | IPNY_PROXY port portspec YY_STR '/' proto IPNY_CONFIG YY_STR
+ { int pos;
+ pos = addname(&nat, $4);
+ nat->in_plabel = pos;
+ if (nat->in_dcmp == 0) {
+ nat->in_odport = $3;
+ } else if ($3 != nat->in_odport) {
+ yyerror("proxy port numbers not consistant");
+ }
+ nat->in_ndport = $3;
+ setnatproto($6);
+ nat->in_pconfig = addname(&nat, $8);
+ free($4);
+ free($8);
+ }
+ | IPNY_PROXY port YY_STR YY_STR '/' proto IPNY_CONFIG YY_STR
+ { int pnum, pos;
+ pos = addname(&nat, $4);
+ nat->in_plabel = pos;
+ pnum = getportproto($3, $6);
+ if (pnum == -1)
+ yyerror("invalid port number");
+ nat->in_odport = ntohs(pnum);
+ nat->in_ndport = ntohs(pnum);
+ setnatproto($6);
+ pos = addname(&nat, $8);
+ nat->in_pconfig = pos;
+ free($3);
+ free($4);
+ free($8);
+ }
+ ;
+setproto:
+ | proto { if (nat->in_pr[0] != 0 ||
+ nat->in_pr[1] != 0 ||
+ nat->in_flags & IPN_TCPUDP)
+ yyerror("protocol set twice");
+ setnatproto($1);
+ }
+ | IPNY_TCPUDP { if (nat->in_pr[0] != 0 ||
+ nat->in_pr[1] != 0 ||
+ nat->in_flags & IPN_TCPUDP)
+ yyerror("protocol set twice");
+ nat->in_flags |= IPN_TCPUDP;
+ nat->in_pr[0] = 0;
+ nat->in_pr[1] = 0;
+ }
+ | IPNY_TCP '/' IPNY_UDP { if (nat->in_pr[0] != 0 ||
+ nat->in_pr[1] != 0 ||
+ nat->in_flags & IPN_TCPUDP)
+ yyerror("protocol set twice");
+ nat->in_flags |= IPN_TCPUDP;
+ nat->in_pr[0] = 0;
+ nat->in_pr[1] = 0;
+ }
+ ;
+
+rhsaddr:
+ addr { $$ = $1;
+ yyexpectaddr = 0;
+ }
+ | hostname '-' { yyexpectaddr = 1; } hostname
+ { $$.t = FRI_RANGE;
+ if ($1.f != $4.f)
+ yyerror("8.address family "
+ "mismatch");
+ $$.f = $1.f;
+ $$.v = ftov($1.f);
+ $$.a = $1.a;
+ $$.m = $4.a;
+ nat->in_flags |= IPN_SIPRANGE;
+ yyexpectaddr = 0;
+ }
+ | IPNY_RANGE hostname '-' { yyexpectaddr = 1; } hostname
+ { $$.t = FRI_RANGE;
+ if ($2.f != $5.f)
+ yyerror("9.address family "
+ "mismatch");
+ $$.f = $2.f;
+ $$.v = ftov($2.f);
+ $$.a = $2.a;
+ $$.m = $5.a;
+ nat->in_flags |= IPN_SIPRANGE;
+ yyexpectaddr = 0;
+ }
+ ;
+
+dip:
+ hostname ',' { yyexpectaddr = 1; } hostname
+ { nat->in_flags |= IPN_SPLIT;
+ if ($1.f != $4.f)
+ yyerror("10.address family "
+ "mismatch");
+ $$ = $1.f;
+ nat->in_ndstip6 = $1.a;
+ nat->in_ndstmsk6 = $4.a;
+ nat->in_ndstatype = FRI_SPLIT;
+ yyexpectaddr = 0;
+ }
+ | rhdaddr { int bits;
+ nat->in_ndstip6 = $1.a;
+ nat->in_ndstmsk6 = $1.m;
+ nat->in_ndst.na_atype = $1.t;
+ yyexpectaddr = 0;
+ if ($1.f == AF_INET)
+ bits = count4bits($1.m.in4.s_addr);
+ else
+ bits = count6bits($1.m.i6);
+ if (($1.f == AF_INET) && (bits != 0) &&
+ (bits != 32)) {
+ yyerror("dest ip bitmask not /32");
+ } else if (($1.f == AF_INET6) &&
+ (bits != 0) && (bits != 128)) {
+ yyerror("dest ip bitmask not /128");
+ }
+ $$ = $1.f;
+ }
+ ;
+
+rhdaddr:
+ addr { $$ = $1;
+ yyexpectaddr = 0;
+ }
+ | hostname '-' hostname { bzero(&$$, sizeof($$));
+ $$.t = FRI_RANGE;
+ if ($1.f != 0 && $3.f != 0 &&
+ $1.f != $3.f)
+ yyerror("11.address family "
+ "mismatch");
+ $$.a = $1.a;
+ $$.m = $3.a;
+ nat->in_flags |= IPN_DIPRANGE;
+ yyexpectaddr = 0;
+ }
+ | IPNY_RANGE hostname '-' hostname
+ { bzero(&$$, sizeof($$));
+ $$.t = FRI_RANGE;
+ if ($2.f != 0 && $4.f != 0 &&
+ $2.f != $4.f)
+ yyerror("12.address family "
+ "mismatch");
+ $$.a = $2.a;
+ $$.m = $4.a;
+ nat->in_flags |= IPN_DIPRANGE;
+ yyexpectaddr = 0;
+ }
+ ;
+
+erhdaddr:
+ rhdaddr { $$ = $1; }
+ | IPNY_DSTLIST '/' YY_NUMBER { $$.t = FRI_LOOKUP;
+ $$.u = IPLT_DSTLIST;
+ $$.s = 0;
+ $$.n = $3;
+ }
+ | IPNY_DSTLIST '/' YY_STR { $$.t = FRI_LOOKUP;
+ $$.u = IPLT_DSTLIST;
+ $$.s = 1;
+ $$.n = addname(&nat, $3);
+ }
+ ;
+
+port: IPNY_PORT { suggest_port = 1; }
+ ;
+
+portspec:
+ YY_NUMBER { if ($1 > 65535) /* Unsigned */
+ yyerror("invalid port number");
+ else
+ $$ = $1;
+ }
+ | YY_STR { if (getport(NULL, $1,
+ &($$), NULL) == -1)
+ yyerror("invalid port number");
+ $$ = ntohs($$);
+ }
+ ;
+
+portpair:
+ portspec { $$.p1 = $1; $$.p2 = $1; }
+ | portspec '-' portspec { $$.p1 = $1; $$.p2 = $3; }
+ | portspec ':' portspec { $$.p1 = $1; $$.p2 = $3; }
+ ;
+
+dport: | port portpair { nat->in_odport = $2.p1;
+ if ($2.p2 == 0)
+ nat->in_dtop = $2.p1;
+ else
+ nat->in_dtop = $2.p2;
+ }
+ ;
+
+nport: | port portpair { nat->in_dpmin = $2.p1;
+ nat->in_dpnext = $2.p1;
+ nat->in_dpmax = $2.p2;
+ nat->in_ndport = $2.p1;
+ if (nat->in_dtop == 0)
+ nat->in_dtop = $2.p2;
+ }
+ | port '=' portspec { nat->in_dpmin = $3;
+ nat->in_dpnext = $3;
+ nat->in_ndport = $3;
+ if (nat->in_dtop == 0)
+ nat->in_dtop = nat->in_odport;
+ nat->in_flags |= IPN_FIXEDDPORT;
+ }
+ ;
+
+ports: | IPNY_PORTS YY_NUMBER { nat->in_spmin = $2; }
+ | IPNY_PORTS IPNY_AUTO { nat->in_flags |= IPN_AUTOPORTMAP; }
+ ;
+
+mapit: IPNY_MAP { nat->in_redir = NAT_MAP; }
+ | IPNY_BIMAP { nat->in_redir = NAT_BIMAP; }
+ ;
+
+rdrit: IPNY_RDR { nat->in_redir = NAT_REDIRECT; }
+ ;
+
+mapblockit:
+ IPNY_MAPBLOCK { nat->in_redir = NAT_MAPBLK; }
+ ;
+
+mapfrom:
+ from sobject to dobject { if ($2 != 0 && $4 != 0 && $2 != $4)
+ yyerror("13.address family "
+ "mismatch");
+ $$ = $2;
+ }
+ | from sobject '!' to dobject
+ { if ($2 != 0 && $5 != 0 && $2 != $5)
+ yyerror("14.address family "
+ "mismatch");
+ nat->in_flags |= IPN_NOTDST;
+ $$ = $2;
+ }
+ | from sobject to '!' dobject
+ { if ($2 != 0 && $5 != 0 && $2 != $5)
+ yyerror("15.address family "
+ "mismatch");
+ nat->in_flags |= IPN_NOTDST;
+ $$ = $2;
+ }
+ ;
+
+rdrfrom:
+ from sobject to dobject { if ($2 != 0 && $4 != 0 && $2 != $4)
+ yyerror("16.address family "
+ "mismatch");
+ $$ = $2;
+ }
+ | '!' from sobject to dobject
+ { if ($3 != 0 && $5 != 0 && $3 != $5)
+ yyerror("17.address family "
+ "mismatch");
+ nat->in_flags |= IPN_NOTSRC;
+ $$ = $3;
+ }
+ | from '!' sobject to dobject
+ { if ($3 != 0 && $5 != 0 && $3 != $5)
+ yyerror("18.address family "
+ "mismatch");
+ nat->in_flags |= IPN_NOTSRC;
+ $$ = $3;
+ }
+ ;
+
+from: IPNY_FROM { nat->in_flags |= IPN_FILTER;
+ yyexpectaddr = 1;
+ }
+ ;
+
+to: IPNY_TO { yyexpectaddr = 1; }
+ ;
+
+ifnames:
+ ifname family { yyexpectaddr = 1; }
+ | ifname ',' otherifname family { yyexpectaddr = 1; }
+ ;
+
+ifname: YY_STR { setifname(&nat, 0, $1);
+ free($1);
+ }
+ ;
+
+family: | IPNY_INET { nat->in_v[0] = 4; nat->in_v[1] = 4; }
+ | IPNY_INET6 { nat->in_v[0] = 6; nat->in_v[1] = 6; }
+ ;
+
+otherifname:
+ YY_STR { setifname(&nat, 1, $1);
+ free($1);
+ }
+ ;
+
+mapport:
+ IPNY_PORTMAP tcpudp portpair sequential
+ { nat->in_spmin = $3.p1;
+ nat->in_spmax = $3.p2;
+ }
+ | IPNY_PORTMAP portpair tcpudp sequential
+ { nat->in_spmin = $2.p1;
+ nat->in_spmax = $2.p2;
+ }
+ | IPNY_PORTMAP tcpudp IPNY_AUTO sequential
+ { nat->in_flags |= IPN_AUTOPORTMAP;
+ nat->in_spmin = 1024;
+ nat->in_spmax = 65535;
+ }
+ | IPNY_ICMPIDMAP YY_STR portpair sequential
+ { if (strcmp($2, "icmp") != 0 &&
+ strcmp($2, "ipv6-icmp") != 0) {
+ yyerror("icmpidmap not followed by icmp");
+ }
+ free($2);
+ if ($3.p1 < 0 || $3.p1 > 65535)
+ yyerror("invalid 1st ICMP Id number");
+ if ($3.p2 < 0 || $3.p2 > 65535)
+ yyerror("invalid 2nd ICMP Id number");
+ if (strcmp($2, "ipv6-icmp") == 0) {
+ nat->in_pr[0] = IPPROTO_ICMPV6;
+ nat->in_pr[1] = IPPROTO_ICMPV6;
+ } else {
+ nat->in_pr[0] = IPPROTO_ICMP;
+ nat->in_pr[1] = IPPROTO_ICMP;
+ }
+ nat->in_flags = IPN_ICMPQUERY;
+ nat->in_spmin = $3.p1;
+ nat->in_spmax = $3.p2;
+ }
+ ;
+
+sobject:
+ saddr { $$ = $1; }
+ | saddr port portstuff { nat->in_osport = $3.p1;
+ nat->in_stop = $3.p2;
+ nat->in_scmp = $3.pc;
+ $$ = $1;
+ }
+ ;
+
+saddr: addr { nat->in_osrcatype = $1.t;
+ bcopy(&$1.a,
+ &nat->in_osrc.na_addr[0],
+ sizeof($1.a));
+ bcopy(&$1.m,
+ &nat->in_osrc.na_addr[1],
+ sizeof($1.m));
+ $$ = $1.f;
+ }
+ ;
+
+dobject:
+ daddr { $$ = $1; }
+ | daddr port portstuff { nat->in_odport = $3.p1;
+ nat->in_dtop = $3.p2;
+ nat->in_dcmp = $3.pc;
+ $$ = $1;
+ }
+ ;
+
+daddr: addr { nat->in_odstatype = $1.t;
+ bcopy(&$1.a,
+ &nat->in_odst.na_addr[0],
+ sizeof($1.a));
+ bcopy(&$1.m,
+ &nat->in_odst.na_addr[1],
+ sizeof($1.m));
+ $$ = $1.f;
+ }
+ ;
+
+addr: IPNY_ANY { yyexpectaddr = 0;
+ bzero(&$$, sizeof($$));
+ $$.t = FRI_NORMAL;
+ }
+ | hostname { bzero(&$$, sizeof($$));
+ $$.a = $1.a;
+ $$.t = FRI_NORMAL;
+ $$.v = ftov($1.f);
+ $$.f = $1.f;
+ if ($$.f == AF_INET) {
+ $$.m.in4.s_addr = 0xffffffff;
+ } else if ($$.f == AF_INET6) {
+ $$.m.i6[0] = 0xffffffff;
+ $$.m.i6[1] = 0xffffffff;
+ $$.m.i6[2] = 0xffffffff;
+ $$.m.i6[3] = 0xffffffff;
+ }
+ yyexpectaddr = 0;
+ }
+ | hostname slash YY_NUMBER
+ { bzero(&$$, sizeof($$));
+ $$.a = $1.a;
+ $$.f = $1.f;
+ $$.v = ftov($1.f);
+ $$.t = FRI_NORMAL;
+ ntomask($$.f, $3, (u_32_t *)&$$.m);
+ $$.a.i6[0] &= $$.m.i6[0];
+ $$.a.i6[1] &= $$.m.i6[1];
+ $$.a.i6[2] &= $$.m.i6[2];
+ $$.a.i6[3] &= $$.m.i6[3];
+ yyexpectaddr = 0;
+ }
+ | hostname slash ipaddr { bzero(&$$, sizeof($$));
+ if ($1.f != $3.f) {
+ yyerror("1.address family "
+ "mismatch");
+ }
+ $$.a = $1.a;
+ $$.m = $3.a;
+ $$.t = FRI_NORMAL;
+ $$.a.i6[0] &= $$.m.i6[0];
+ $$.a.i6[1] &= $$.m.i6[1];
+ $$.a.i6[2] &= $$.m.i6[2];
+ $$.a.i6[3] &= $$.m.i6[3];
+ $$.f = $1.f;
+ $$.v = ftov($1.f);
+ yyexpectaddr = 0;
+ }
+ | hostname slash hexnumber { bzero(&$$, sizeof($$));
+ $$.a = $1.a;
+ $$.m.in4.s_addr = htonl($3);
+ $$.t = FRI_NORMAL;
+ $$.a.in4.s_addr &= $$.m.in4.s_addr;
+ $$.f = $1.f;
+ $$.v = ftov($1.f);
+ if ($$.f == AF_INET6)
+ yyerror("incorrect inet6 mask");
+ }
+ | hostname mask ipaddr { bzero(&$$, sizeof($$));
+ if ($1.f != $3.f) {
+ yyerror("2.address family "
+ "mismatch");
+ }
+ $$.a = $1.a;
+ $$.m = $3.a;
+ $$.t = FRI_NORMAL;
+ $$.a.i6[0] &= $$.m.i6[0];
+ $$.a.i6[1] &= $$.m.i6[1];
+ $$.a.i6[2] &= $$.m.i6[2];
+ $$.a.i6[3] &= $$.m.i6[3];
+ $$.f = $1.f;
+ $$.v = ftov($1.f);
+ yyexpectaddr = 0;
+ }
+ | hostname mask hexnumber { bzero(&$$, sizeof($$));
+ $$.a = $1.a;
+ $$.m.in4.s_addr = htonl($3);
+ $$.t = FRI_NORMAL;
+ $$.a.in4.s_addr &= $$.m.in4.s_addr;
+ $$.f = AF_INET;
+ $$.v = 4;
+ }
+ | pool slash YY_NUMBER { bzero(&$$, sizeof($$));
+ $$.a.iplookupnum = $3;
+ $$.a.iplookuptype = IPLT_POOL;
+ $$.a.iplookupsubtype = 0;
+ $$.t = FRI_LOOKUP;
+ }
+ | pool slash YY_STR { bzero(&$$, sizeof($$));
+ $$.a.iplookupname = addname(&nat,$3);
+ $$.a.iplookuptype = IPLT_POOL;
+ $$.a.iplookupsubtype = 1;
+ $$.t = FRI_LOOKUP;
+ }
+ | hash slash YY_NUMBER { bzero(&$$, sizeof($$));
+ $$.a.iplookupnum = $3;
+ $$.a.iplookuptype = IPLT_HASH;
+ $$.a.iplookupsubtype = 0;
+ $$.t = FRI_LOOKUP;
+ }
+ | hash slash YY_STR { bzero(&$$, sizeof($$));
+ $$.a.iplookupname = addname(&nat,$3);
+ $$.a.iplookuptype = IPLT_HASH;
+ $$.a.iplookupsubtype = 1;
+ $$.t = FRI_LOOKUP;
+ }
+ ;
+
+slash: '/' { yyexpectaddr = 0; }
+ ;
+
+mask: IPNY_MASK { yyexpectaddr = 0; }
+ ;
+
+pool: IPNY_POOL { if (!(nat->in_flags & IPN_FILTER)) {
+ yyerror("Can only use pool with from/to rules\n");
+ }
+ yyexpectaddr = 0;
+ yyresetdict();
+ }
+ ;
+
+hash: IPNY_HASH { if (!(nat->in_flags & IPN_FILTER)) {
+ yyerror("Can only use hash with from/to rules\n");
+ }
+ yyexpectaddr = 0;
+ yyresetdict();
+ }
+ ;
+
+portstuff:
+ compare portspec { $$.pc = $1; $$.p1 = $2; $$.p2 = 0; }
+ | portspec range portspec { $$.pc = $2; $$.p1 = $1; $$.p2 = $3; }
+ ;
+
+mapoptions:
+ rr frag age mssclamp nattag setproto purge
+ ;
+
+rdroptions:
+ rr frag age sticky mssclamp rdrproxy nattag purge
+ ;
+
+nattag: | IPNY_TAG YY_STR { strncpy(nat->in_tag.ipt_tag, $2,
+ sizeof(nat->in_tag.ipt_tag));
+ }
+rr: | IPNY_ROUNDROBIN { nat->in_flags |= IPN_ROUNDR; }
+ ;
+
+frag: | IPNY_FRAG { nat->in_flags |= IPN_FRAG; }
+ ;
+
+age: | IPNY_AGE YY_NUMBER { nat->in_age[0] = $2;
+ nat->in_age[1] = $2; }
+ | IPNY_AGE YY_NUMBER '/' YY_NUMBER { nat->in_age[0] = $2;
+ nat->in_age[1] = $4; }
+ ;
+
+sticky: | IPNY_STICKY { if (!(nat->in_flags & IPN_ROUNDR) &&
+ !(nat->in_flags & IPN_SPLIT)) {
+ FPRINTF(stderr,
+ "'sticky' for use with round-robin/IP splitting only\n");
+ } else
+ nat->in_flags |= IPN_STICKY;
+ }
+ ;
+
+mssclamp:
+ | IPNY_MSSCLAMP YY_NUMBER { nat->in_mssclamp = $2; }
+ ;
+
+tcpudp: IPNY_TCP { setnatproto(IPPROTO_TCP); }
+ | IPNY_UDP { setnatproto(IPPROTO_UDP); }
+ | IPNY_TCPUDP { nat->in_flags |= IPN_TCPUDP;
+ nat->in_pr[0] = 0;
+ nat->in_pr[1] = 0;
+ }
+ | IPNY_TCP '/' IPNY_UDP { nat->in_flags |= IPN_TCPUDP;
+ nat->in_pr[0] = 0;
+ nat->in_pr[1] = 0;
+ }
+ ;
+
+sequential:
+ | IPNY_SEQUENTIAL { nat->in_flags |= IPN_SEQUENTIAL; }
+ ;
+
+purge:
+ | IPNY_PURGE { nat->in_flags |= IPN_PURGE; }
+ ;
+
+rdrproxy:
+ IPNY_PROXY YY_STR
+ { int pos;
+ pos = addname(&nat, $2);
+ nat->in_plabel = pos;
+ nat->in_odport = nat->in_dpnext;
+ nat->in_dtop = nat->in_odport;
+ free($2);
+ }
+ | proxy { if (nat->in_plabel != -1) {
+ nat->in_ndport = nat->in_odport;
+ nat->in_dpmin = nat->in_odport;
+ nat->in_dpmax = nat->in_dpmin;
+ nat->in_dtop = nat->in_dpmin;
+ nat->in_dpnext = nat->in_dpmin;
+ }
+ }
+ ;
+
+newopts:
+ | IPNY_PURGE { nat->in_flags |= IPN_PURGE; }
+ ;
+
+proto: YY_NUMBER { $$ = $1;
+ if ($$ != IPPROTO_TCP &&
+ $$ != IPPROTO_UDP)
+ suggest_port = 0;
+ }
+ | IPNY_TCP { $$ = IPPROTO_TCP; }
+ | IPNY_UDP { $$ = IPPROTO_UDP; }
+ | YY_STR { $$ = getproto($1);
+ free($1);
+ if ($$ == -1)
+ yyerror("unknown protocol");
+ if ($$ != IPPROTO_TCP &&
+ $$ != IPPROTO_UDP)
+ suggest_port = 0;
+ }
+ ;
+
+hexnumber:
+ YY_HEX { $$ = $1; }
+ ;
+
+hostname:
+ YY_STR { i6addr_t addr;
+ int family;
+
+#ifdef USE_INET6
+ if (nat->in_v[0] == 6)
+ family = AF_INET6;
+ else
+#endif
+ family = AF_INET;
+ memset(&($$), 0, sizeof($$));
+ memset(&addr, 0, sizeof(addr));
+ $$.f = family;
+ if (gethost(family, $1,
+ &addr) == 0) {
+ $$.a = addr;
+ } else {
+ FPRINTF(stderr,
+ "Unknown host '%s'\n",
+ $1);
+ }
+ free($1);
+ }
+ | YY_NUMBER { memset(&($$), 0, sizeof($$));
+ $$.a.in4.s_addr = htonl($1);
+ if ($$.a.in4.s_addr != 0)
+ $$.f = AF_INET;
+ }
+ | ipv4 { $$ = $1; }
+ | YY_IPV6 { memset(&($$), 0, sizeof($$));
+ $$.a = $1;
+ $$.f = AF_INET6;
+ }
+ | YY_NUMBER YY_IPV6 { memset(&($$), 0, sizeof($$));
+ $$.a = $2;
+ $$.f = AF_INET6;
+ }
+ ;
+
+compare:
+ '=' { $$ = FR_EQUAL; }
+ | YY_CMP_EQ { $$ = FR_EQUAL; }
+ | YY_CMP_NE { $$ = FR_NEQUAL; }
+ | YY_CMP_LT { $$ = FR_LESST; }
+ | YY_CMP_LE { $$ = FR_LESSTE; }
+ | YY_CMP_GT { $$ = FR_GREATERT; }
+ | YY_CMP_GE { $$ = FR_GREATERTE; }
+
+range:
+ YY_RANGE_OUT { $$ = FR_OUTRANGE; }
+ | YY_RANGE_IN { $$ = FR_INRANGE; }
+ | ':' { $$ = FR_INCRANGE; }
+ ;
+
+ipaddr: ipv4 { $$ = $1; }
+ | YY_IPV6 { $$.a = $1;
+ $$.f = AF_INET6;
+ }
+ ;
+
+ipv4: YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER
+ { if ($1 > 255 || $3 > 255 || $5 > 255 || $7 > 255) {
+ yyerror("Invalid octet string for IP address");
+ return 0;
+ }
+ bzero((char *)&$$, sizeof($$));
+ $$.a.in4.s_addr = ($1 << 24) | ($3 << 16) | ($5 << 8) | $7;
+ $$.a.in4.s_addr = htonl($$.a.in4.s_addr);
+ $$.f = AF_INET;
+ }
+ ;
+
+%%
+
+
+static wordtab_t proxies[] = {
+ { "dns", IPNY_DNS }
+};
+
+static wordtab_t dnswords[] = {
+ { "allow", IPNY_ALLOW },
+ { "block", IPNY_DENY },
+ { "deny", IPNY_DENY },
+ { "drop", IPNY_DENY },
+ { "pass", IPNY_ALLOW },
+
+};
+
+static wordtab_t yywords[] = {
+ { "age", IPNY_AGE },
+ { "any", IPNY_ANY },
+ { "auto", IPNY_AUTO },
+ { "bimap", IPNY_BIMAP },
+ { "config", IPNY_CONFIG },
+ { "divert", IPNY_DIVERT },
+ { "dst", IPNY_DST },
+ { "dstlist", IPNY_DSTLIST },
+ { "frag", IPNY_FRAG },
+ { "from", IPNY_FROM },
+ { "hash", IPNY_HASH },
+ { "icmpidmap", IPNY_ICMPIDMAP },
+ { "in", IPNY_IN },
+ { "inet", IPNY_INET },
+ { "inet6", IPNY_INET6 },
+ { "mask", IPNY_MASK },
+ { "map", IPNY_MAP },
+ { "map-block", IPNY_MAPBLOCK },
+ { "mssclamp", IPNY_MSSCLAMP },
+ { "netmask", IPNY_MASK },
+ { "no", IPNY_NO },
+ { "on", IPNY_ON },
+ { "out", IPNY_OUT },
+ { "pool", IPNY_POOL },
+ { "port", IPNY_PORT },
+ { "portmap", IPNY_PORTMAP },
+ { "ports", IPNY_PORTS },
+ { "proto", IPNY_PROTO },
+ { "proxy", IPNY_PROXY },
+ { "purge", IPNY_PURGE },
+ { "range", IPNY_RANGE },
+ { "rewrite", IPNY_REWRITE },
+ { "rdr", IPNY_RDR },
+ { "round-robin",IPNY_ROUNDROBIN },
+ { "sequential", IPNY_SEQUENTIAL },
+ { "src", IPNY_SRC },
+ { "sticky", IPNY_STICKY },
+ { "tag", IPNY_TAG },
+ { "tcp", IPNY_TCP },
+ { "tcpudp", IPNY_TCPUDP },
+ { "to", IPNY_TO },
+ { "udp", IPNY_UDP },
+ { "-", '-' },
+ { "->", IPNY_TLATE },
+ { "eq", YY_CMP_EQ },
+ { "ne", YY_CMP_NE },
+ { "lt", YY_CMP_LT },
+ { "gt", YY_CMP_GT },
+ { "le", YY_CMP_LE },
+ { "ge", YY_CMP_GE },
+ { NULL, 0 }
+};
+
+
+int
+ipnat_parsefile(fd, addfunc, ioctlfunc, filename)
+ int fd;
+ addfunc_t addfunc;
+ ioctlfunc_t ioctlfunc;
+ char *filename;
+{
+ FILE *fp = NULL;
+ int rval;
+ char *s;
+
+ yylineNum = 1;
+
+ (void) yysettab(yywords);
+
+ s = getenv("YYDEBUG");
+ if (s)
+ yydebug = atoi(s);
+ else
+ yydebug = 0;
+
+ if (strcmp(filename, "-")) {
+ fp = fopen(filename, "r");
+ if (!fp) {
+ FPRINTF(stderr, "fopen(%s) failed: %s\n", filename,
+ STRERROR(errno));
+ return -1;
+ }
+ } else
+ fp = stdin;
+
+ while ((rval = ipnat_parsesome(fd, addfunc, ioctlfunc, fp)) == 0)
+ ;
+ if (fp != NULL)
+ fclose(fp);
+ if (rval == -1)
+ rval = 0;
+ else if (rval != 0)
+ rval = 1;
+ return rval;
+}
+
+
+int
+ipnat_parsesome(fd, addfunc, ioctlfunc, fp)
+ int fd;
+ addfunc_t addfunc;
+ ioctlfunc_t ioctlfunc;
+ FILE *fp;
+{
+ char *s;
+ int i;
+
+ natfd = fd;
+ parser_error = 0;
+ nataddfunc = addfunc;
+ natioctlfunc = ioctlfunc;
+
+ if (feof(fp))
+ return -1;
+ i = fgetc(fp);
+ if (i == EOF)
+ return -1;
+ if (ungetc(i, fp) == EOF)
+ return -1;
+ if (feof(fp))
+ return -1;
+ s = getenv("YYDEBUG");
+ if (s)
+ yydebug = atoi(s);
+ else
+ yydebug = 0;
+
+ yyin = fp;
+ yyparse();
+ return parser_error;
+}
+
+
+static void
+newnatrule()
+{
+ ipnat_t *n;
+
+ n = calloc(1, sizeof(*n));
+ if (n == NULL)
+ return;
+
+ if (nat == NULL) {
+ nattop = nat = n;
+ n->in_pnext = &nattop;
+ } else {
+ nat->in_next = n;
+ n->in_pnext = &nat->in_next;
+ nat = n;
+ }
+
+ n->in_flineno = yylineNum;
+ n->in_ifnames[0] = -1;
+ n->in_ifnames[1] = -1;
+ n->in_plabel = -1;
+ n->in_pconfig = -1;
+ n->in_size = sizeof(*n);
+
+ suggest_port = 0;
+}
+
+
+static void
+setnatproto(p)
+ int p;
+{
+ nat->in_pr[0] = p;
+ nat->in_pr[1] = p;
+
+ switch (p)
+ {
+ case IPPROTO_TCP :
+ nat->in_flags |= IPN_TCP;
+ nat->in_flags &= ~IPN_UDP;
+ break;
+ case IPPROTO_UDP :
+ nat->in_flags |= IPN_UDP;
+ nat->in_flags &= ~IPN_TCP;
+ break;
+#ifdef USE_INET6
+ case IPPROTO_ICMPV6 :
+#endif
+ case IPPROTO_ICMP :
+ nat->in_flags &= ~IPN_TCPUDP;
+ if (!(nat->in_flags & IPN_ICMPQUERY) &&
+ !(nat->in_redir & NAT_DIVERTUDP)) {
+ nat->in_dcmp = 0;
+ nat->in_scmp = 0;
+ nat->in_dpmin = 0;
+ nat->in_dpmax = 0;
+ nat->in_dpnext = 0;
+ nat->in_spmin = 0;
+ nat->in_spmax = 0;
+ nat->in_spnext = 0;
+ }
+ break;
+ default :
+ if ((nat->in_redir & NAT_MAPBLK) == 0) {
+ nat->in_flags &= ~IPN_TCPUDP;
+ nat->in_dcmp = 0;
+ nat->in_scmp = 0;
+ nat->in_dpmin = 0;
+ nat->in_dpmax = 0;
+ nat->in_dpnext = 0;
+ nat->in_spmin = 0;
+ nat->in_spmax = 0;
+ nat->in_spnext = 0;
+ }
+ break;
+ }
+
+ if ((nat->in_flags & (IPN_TCP|IPN_UDP)) == 0) {
+ nat->in_stop = 0;
+ nat->in_dtop = 0;
+ nat->in_osport = 0;
+ nat->in_odport = 0;
+ nat->in_stop = 0;
+ nat->in_osport = 0;
+ nat->in_dtop = 0;
+ nat->in_odport = 0;
+ }
+ if ((nat->in_flags & (IPN_TCPUDP|IPN_FIXEDDPORT)) == IPN_FIXEDDPORT)
+ nat->in_flags &= ~IPN_FIXEDDPORT;
+}
+
+
+int
+ipnat_addrule(fd, ioctlfunc, ptr)
+ int fd;
+ ioctlfunc_t ioctlfunc;
+ void *ptr;
+{
+ ioctlcmd_t add, del;
+ ipfobj_t obj;
+ ipnat_t *ipn;
+
+ ipn = ptr;
+ bzero((char *)&obj, sizeof(obj));
+ obj.ipfo_rev = IPFILTER_VERSION;
+ obj.ipfo_size = ipn->in_size;
+ obj.ipfo_type = IPFOBJ_IPNAT;
+ obj.ipfo_ptr = ptr;
+
+ if ((opts & OPT_DONOTHING) != 0)
+ fd = -1;
+
+ if (opts & OPT_ZERORULEST) {
+ add = SIOCZRLST;
+ del = 0;
+ } else if (opts & OPT_PURGE) {
+ add = 0;
+ del = SIOCPURGENAT;
+ } else {
+ add = SIOCADNAT;
+ del = SIOCRMNAT;
+ }
+
+ if ((opts & OPT_VERBOSE) != 0)
+ printnat(ipn, opts);
+
+ if (opts & OPT_DEBUG)
+ binprint(ipn, ipn->in_size);
+
+ if ((opts & OPT_ZERORULEST) != 0) {
+ if ((*ioctlfunc)(fd, add, (void *)&obj) == -1) {
+ if ((opts & OPT_DONOTHING) == 0) {
+ char msg[80];
+
+ snprintf(msg, sizeof(msg), "%d:ioctl(zero nat rule)",
+ ipn->in_flineno);
+ return ipf_perror_fd(fd, ioctlfunc, msg);
+ }
+ } else {
+ PRINTF("hits %lu ", ipn->in_hits);
+#ifdef USE_QUAD_T
+ PRINTF("bytes %"PRIu64" ",
+ ipn->in_bytes[0] + ipn->in_bytes[1]);
+#else
+ PRINTF("bytes %lu ",
+ ipn->in_bytes[0] + ipn->in_bytes[1]);
+#endif
+ printnat(ipn, opts);
+ }
+ } else if ((opts & OPT_REMOVE) != 0) {
+ if ((*ioctlfunc)(fd, del, (void *)&obj) == -1) {
+ if ((opts & OPT_DONOTHING) == 0) {
+ char msg[80];
+
+ snprintf(msg, sizeof(msg), "%d:ioctl(delete nat rule)",
+ ipn->in_flineno);
+ return ipf_perror_fd(fd, ioctlfunc, msg);
+ }
+ }
+ } else {
+ if ((*ioctlfunc)(fd, add, (void *)&obj) == -1) {
+ if ((opts & OPT_DONOTHING) == 0) {
+ char msg[80];
+
+ snprintf(msg, sizeof(msg), "%d:ioctl(add/insert nat rule)",
+ ipn->in_flineno);
+ if (errno == EEXIST) {
+ int strlen_msg = strlen(msg);
+ snprintf(msg + strlen_msg, sizeof(msg) -strlen_msg, "(line %d)",
+ ipn->in_flineno);
+ }
+ return ipf_perror_fd(fd, ioctlfunc, msg);
+ }
+ }
+ }
+ return 0;
+}
+
+
+static void
+setmapifnames()
+{
+ if (nat->in_ifnames[1] == -1)
+ nat->in_ifnames[1] = nat->in_ifnames[0];
+
+ if ((suggest_port == 1) && (nat->in_flags & IPN_TCPUDP) == 0)
+ nat->in_flags |= IPN_TCPUDP;
+
+ if ((nat->in_flags & IPN_TCPUDP) == 0)
+ setnatproto(nat->in_pr[1]);
+
+ if (((nat->in_redir & NAT_MAPBLK) != 0) ||
+ ((nat->in_flags & IPN_AUTOPORTMAP) != 0))
+ nat_setgroupmap(nat);
+}
+
+
+static void
+setrdrifnames()
+{
+ if ((suggest_port == 1) && (nat->in_flags & IPN_TCPUDP) == 0)
+ nat->in_flags |= IPN_TCPUDP;
+
+ if ((nat->in_pr[0] == 0) && ((nat->in_flags & IPN_TCPUDP) == 0) &&
+ (nat->in_dpmin != 0 || nat->in_dpmax != 0 || nat->in_dpnext != 0))
+ setnatproto(IPPROTO_TCP);
+
+ if (nat->in_ifnames[1] == -1)
+ nat->in_ifnames[1] = nat->in_ifnames[0];
+}
+
+
+static void
+proxy_setconfig(proxy)
+ int proxy;
+{
+ if (proxy == IPNY_DNS) {
+ yysetfixeddict(dnswords);
+ }
+}
+
+
+static void
+proxy_unsetconfig()
+{
+ yyresetdict();
+}
+
+
+static namelist_t *
+proxy_dns_add_pass(prefix, name)
+ char *prefix, *name;
+{
+ namelist_t *n;
+
+ n = calloc(1, sizeof(*n));
+ if (n != NULL) {
+ if (prefix == NULL || *prefix == '\0') {
+ n->na_name = strdup(name);
+ } else {
+ n->na_name = malloc(strlen(name) + strlen(prefix) + 1);
+ strcpy(n->na_name, prefix);
+ strcat(n->na_name, name);
+ }
+ }
+ return n;
+}
+
+
+static namelist_t *
+proxy_dns_add_block(prefix, name)
+ char *prefix, *name;
+{
+ namelist_t *n;
+
+ n = calloc(1, sizeof(*n));
+ if (n != NULL) {
+ if (prefix == NULL || *prefix == '\0') {
+ n->na_name = strdup(name);
+ } else {
+ n->na_name = malloc(strlen(name) + strlen(prefix) + 1);
+ strcpy(n->na_name, prefix);
+ strcat(n->na_name, name);
+ }
+ n->na_value = 1;
+ }
+ return n;
+}
+
+
+static void
+proxy_addconfig(proxy, proto, conf, list)
+ char *proxy, *conf;
+ int proto;
+ namelist_t *list;
+{
+ proxyrule_t *pr;
+
+ pr = calloc(1, sizeof(*pr));
+ if (pr != NULL) {
+ pr->pr_proto = proto;
+ pr->pr_proxy = proxy;
+ pr->pr_conf = conf;
+ pr->pr_names = list;
+ pr->pr_next = prules;
+ prules = pr;
+ }
+}
+
+
+static void
+proxy_loadrules(fd, ioctlfunc, rules)
+ int fd;
+ ioctlfunc_t ioctlfunc;
+ proxyrule_t *rules;
+{
+ proxyrule_t *pr;
+
+ while ((pr = rules) != NULL) {
+ proxy_loadconfig(fd, ioctlfunc, pr->pr_proxy, pr->pr_proto,
+ pr->pr_conf, pr->pr_names);
+ rules = pr->pr_next;
+ free(pr->pr_conf);
+ free(pr);
+ }
+}
+
+
+static void
+proxy_loadconfig(fd, ioctlfunc, proxy, proto, conf, list)
+ int fd;
+ ioctlfunc_t ioctlfunc;
+ char *proxy, *conf;
+ int proto;
+ namelist_t *list;
+{
+ namelist_t *na;
+ ipfobj_t obj;
+ ap_ctl_t pcmd;
+
+ obj.ipfo_rev = IPFILTER_VERSION;
+ obj.ipfo_type = IPFOBJ_PROXYCTL;
+ obj.ipfo_size = sizeof(pcmd);
+ obj.ipfo_ptr = &pcmd;
+
+ while ((na = list) != NULL) {
+ if ((opts & OPT_REMOVE) != 0)
+ pcmd.apc_cmd = APC_CMD_DEL;
+ else
+ pcmd.apc_cmd = APC_CMD_ADD;
+ pcmd.apc_dsize = strlen(na->na_name) + 1;
+ pcmd.apc_data = na->na_name;
+ pcmd.apc_arg = na->na_value;
+ pcmd.apc_p = proto;
+
+ strncpy(pcmd.apc_label, proxy, APR_LABELLEN);
+ pcmd.apc_label[APR_LABELLEN - 1] = '\0';
+
+ strncpy(pcmd.apc_config, conf, APR_LABELLEN);
+ pcmd.apc_config[APR_LABELLEN - 1] = '\0';
+
+ if ((*ioctlfunc)(fd, SIOCPROXY, (void *)&obj) == -1) {
+ if ((opts & OPT_DONOTHING) == 0) {
+ char msg[80];
+
+ snprintf(msg, sizeof(msg), "%d:ioctl(add/remove proxy rule)",
+ yylineNum);
+ ipf_perror_fd(fd, ioctlfunc, msg);
+ return;
+ }
+ }
+
+ list = na->na_next;
+ free(na->na_name);
+ free(na);
+ }
+}
+
+
+static void
+setifname(np, idx, name)
+ ipnat_t **np;
+ int idx;
+ char *name;
+{
+ int pos;
+
+ pos = addname(np, name);
+ if (pos == -1)
+ return;
+ (*np)->in_ifnames[idx] = pos;
+}
+
+
+static int
+addname(np, name)
+ ipnat_t **np;
+ char *name;
+{
+ ipnat_t *n;
+ int nlen;
+ int pos;
+
+ nlen = strlen(name) + 1;
+ n = realloc(*np, (*np)->in_size + nlen);
+ if (*np == nattop)
+ nattop = n;
+ *np = n;
+ if (n == NULL)
+ return -1;
+ if (n->in_pnext != NULL)
+ *n->in_pnext = n;
+ n->in_size += nlen;
+ pos = n->in_namelen;
+ n->in_namelen += nlen;
+ strcpy(n->in_names + pos, name);
+ n->in_names[n->in_namelen] = '\0';
+ return pos;
+}