kern/77195: [patch] Ipfilter ioctl SIOCGNATL does not match active sessions properly

Martin Beran mb at
Mon Feb 7 00:40:26 PST 2005

>Number:         77195
>Category:       kern
>Synopsis:       [patch] Ipfilter ioctl SIOCGNATL does not match active sessions properly
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Mon Feb 07 08:40:25 GMT 2005
>Originator:     Martin Beran <mb at>
>Release:        FreeBSD 5.3-RELEASE i386
Trusted Network Solutions, a. s.
System: FreeBSD 5.3-RELEASE FreeBSD 5.3-RELEASE #1: Tue Dec 28 17:56:21 CET 2004 root at i386

Ipfilter provides ioctl SIOCGNATL which can be used to obtain the original
destination IP address of a redirected connection. Matching with the table of
active sessions is not performed properly. The type of protocol is ignored and
port numbers are used as an input to the hash function, but records from the
hash table are not checked whether they have the correct ports. As a result,
ipfilter sometimes reports the original destination of a redirected connection
I have tested this problem on 5.3-RELEASE, but according to the source code
(/usr/src/sys/contrib/ipfilter/netinet/ip_nat.c, function nat_lookupredir), it
is present also in 4.11-RELEASE and in CURRENT.
Create a redirection rule, e.g.,
rdr lo0 port 53 -> port 53538 udp
Create many (several hundred) active sessions by sending UDP datagrams from
localhost, various source ports, to localhost, port 53. Then start opening TCP
connections from localhost, various source ports, to localhost, some
arbitrarily chosen port (other than 53). For each connection, try to find the
original destination by SIOCGNATL ioctl. From time to time, the ioctl responds
that the destination is port 53.
--- /usr/src/sys/contrib/ipfilter/netinet/ip_nat.c	2005/02/04 16:07:35
+++ /usr/src/sys/contrib/ipfilter/netinet/ip_nat.c	2005/02/06 13:54:31
@@ -2239,7 +2239,20 @@
 	nat_t *nat;
 	fr_info_t fi;
+	int proto;
+	switch(np->nl_flags & IPN_TCPUDP) {
+	case IPN_TCP:
+		proto = IPPROTO_TCP;
+		break;
+	case IPN_UDP:
+		proto = IPPROTO_UDP;
+		break;
+	default:
+		proto = 0;
+		break;
+	}
 	bzero((char *)&fi, sizeof(fi));
 	fi.fin_data[0] = ntohs(np->nl_inport);
 	fi.fin_data[1] = ntohs(np->nl_outport);
@@ -2248,7 +2261,7 @@
 	 * If nl_inip is non null, this is a lookup based on the real
 	 * ip address. Else, we use the fake.
-	if ((nat = nat_outlookup(&fi, np->nl_flags, 0, np->nl_inip,
+	if ((nat = nat_outlookup(&fi, np->nl_flags, proto, np->nl_inip,
 				 np->nl_outip, 0))) {
 		np->nl_realip = nat->nat_outip;
 		np->nl_realport = nat->nat_outport;

More information about the freebsd-bugs mailing list