NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
kern/50198: SIOCGNATL broken in IPFilter 5
>Number: 50198
>Category: kern
>Synopsis: SIOCGNATL broken in IPFilter 5
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: kern-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Thu Sep 03 17:15:00 +0000 2015
>Originator: Patrick Welche
>Release: NetBSD 7.0_RC3
>Organization:
>Environment:
Tested 5.2_STABLE/i386 vs 7.0_RC3/i386 (and -current/i386)
>Description:
ipnat SIOCGNATL from IPFilter v5 doesn't look up redirected destination
IP addresses correctly, which breaks transparent proxies such as squid.
IPFilter 4 in NetBSD 5 returns:
real: 192.168.204.87:80
IPFilter 5 in NetBSD 7 returns:
real: 127.0.0.1:1234
>How-To-Repeat:
Set up a trivial redirect rule:
# cat /etc/ipnat.conf
rdr xennet0 0/0 port 80 -> 127.0.0.1 port 1234 tcp
and run the following server, which listens on port 1234, and does a
SIOCGNATL call:
================================================================================
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ipl.h>
#include <netinet/ip_fil.h>
#include <netinet/ip_nat.h>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define MYADDR "127.0.0.1"
#define MYPORT 1234
int main()
{
int i, sock, con, natfd;
struct sockaddr_storage connstore;
struct sockaddr_in lo, *conn;
socklen_t connlen;
ipfobj_t obj;
natlookup_t nat;
connlen = sizeof(connstore);
conn = (struct sockaddr_in *)&connstore;
natfd = open("/dev/ipnat", O_RDONLY);
if (natfd == -1)
err(1, "/dev/ipnat");
if (inet_aton(MYADDR, &lo.sin_addr) == 0)
errx(1,"inet_aton received invalid string");
lo.sin_len = sizeof(lo);
lo.sin_family = AF_INET;
lo.sin_port = htons(MYPORT);
memset(lo.sin_zero, 0, sizeof(lo.sin_zero));
sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (bind(sock, (struct sockaddr *)&lo, sizeof(lo)) == -1)
err(1, "bind");
if (listen(sock, 10) == -1)
err(1, "listen");
printf("IPFilter version %u\n", IPFILTER_VERSION);
printf("listening on : %s:%d\n", MYADDR, MYPORT);
for (i = 0; i < 1; ++i) {
con = accept(sock, (struct sockaddr *)conn, &connlen);
if (con == -1)
err(1, "accept");
printf("connection from: %s:%hu\n", inet_ntoa(conn->sin_addr),
ntohs(conn->sin_port));
memset(&nat, 0, sizeof(nat));
nat.nl_outip = conn->sin_addr;
nat.nl_outport = conn->sin_port;
nat.nl_inip = lo.sin_addr;
nat.nl_inport = lo.sin_port;
nat.nl_flags = IPN_TCP;
#if IPFILTER_VERSION >= 5000003
nat.nl_v = 4;
#endif
memset(&obj, 0, sizeof(obj));
obj.ipfo_rev = IPFILTER_VERSION;
obj.ipfo_size = sizeof(nat);
obj.ipfo_ptr = &nat;
obj.ipfo_type = IPFOBJ_NATLOOKUP;
if (ioctl(natfd, SIOCGNATL, &obj) == -1)
err(1, "SIOCGNATL");
printf(" real: %s:%d\n", inet_ntoa(nat.nl_realip),
ntohs(nat.nl_realport));
close(con);
}
if (close(natfd) == -1)
err(1, "close nat");
if (close(sock) == -1)
err(1, "close server");
return 0;
}
================================================================================
Add a few debug printfs in ip_nat.c:
================================================================================
NetBSD 5 patch:
diff --git a/sys/dist/ipf/netinet/ip_nat.c b/sys/dist/ipf/netinet/ip_nat.c
index 9b30e07..5c5f2a8 100644
--- a/sys/dist/ipf/netinet/ip_nat.c
+++ b/sys/dist/ipf/netinet/ip_nat.c
@@ -3599,6 +3599,15 @@ natlookup_t *np;
np->nl_realip, np->nl_outip))) {
np->nl_inip = nat->nat_inip;
np->nl_inport = nat->nat_inport;
+printf("%s IPN_IN:\n", __func__);
+printf(" inip: %s:%d\n",
+ inet_ntoa(nat->nat_inip), ntohs(nat->nat_inport));
+printf(" outip: %s:%d\n",
+ inet_ntoa(nat->nat_outip), ntohs(nat->nat_outport));
+printf(" oip: %s:%d\n",
+ inet_ntoa(nat->nat_oip), ntohs(nat->nat_oport));
+printf("==> in: %s:%d\n",
+ inet_ntoa(np->nl_inip), ntohs(np->nl_inport));
}
} else {
/*
@@ -3623,6 +3632,15 @@ natlookup_t *np;
np->nl_realip = nat->nat_outip;
np->nl_realport = nat->nat_outport;
+printf("%s !IPN_IN:\n", __func__);
+printf(" inip: %s:%d\n",
+ inet_ntoa(nat->nat_inip), ntohs(nat->nat_inport));
+printf(" outip: %s:%d\n",
+ inet_ntoa(nat->nat_outip), ntohs(nat->nat_outport));
+printf(" oip: %s:%d\n",
+ inet_ntoa(nat->nat_oip), ntohs(nat->nat_oport));
+printf("> real: %s:%d\n",
+ inet_ntoa(np->nl_realip), ntohs(np->nl_realport));
}
}
================================================================================
NetBSD 7 patch:
diff --git a/sys/external/bsd/ipf/netinet/ip_nat.c b/sys/external/bsd/ipf/netinet/ip_nat.c
index 0fa9033..b202a8e 100644
--- a/sys/external/bsd/ipf/netinet/ip_nat.c
+++ b/sys/external/bsd/ipf/netinet/ip_nat.c
@@ -4605,6 +4606,17 @@ ipf_nat_lookupredir(ipf_main_softc_t *softc, natlookup_t *np)
np->nl_realip, np->nl_outip))) {
np->nl_inip = nat->nat_odstip;
np->nl_inport = nat->nat_odport;
+printf("%s IPN_IN:\n", __func__);
+printf(" osrc: %s:%d",
+ intoa(nat->nat_osrcaddr), ntohs(nat->nat_osport));
+printf(" odst: %s:%d\n",
+ intoa(nat->nat_odstaddr), ntohs(nat->nat_odport));
+printf(" nsrc: %s:%d",
+ intoa(nat->nat_nsrcaddr), ntohs(nat->nat_nsport));
+printf(" ndst: %s:%d\n",
+ intoa(nat->nat_ndstaddr), ntohs(nat->nat_ndport));
+printf("===> in : %s:%d\n",
+ intoa(np->nl_inip.s_addr), ntohs(np->nl_inport));
}
} else {
/*
@@ -4627,8 +4639,25 @@ ipf_nat_lookupredir(ipf_main_softc_t *softc, natlookup_t *np)
}
}
+#define XXXPWHACK 0
+#if XXXPWHACK
+ np->nl_realip = nat->nat_odstip;
+ np->nl_realport = nat->nat_odport;
+#else
np->nl_realip = nat->nat_ndstip;
np->nl_realport = nat->nat_ndport;
+#endif
+printf("%s !IPN_IN:\n", __func__);
+printf(" osrc: %s:%d",
+ intoa(nat->nat_osrcaddr), ntohs(nat->nat_osport));
+printf(" odst: %s:%d\n",
+ intoa(nat->nat_odstaddr), ntohs(nat->nat_odport));
+printf(" nsrc: %s:%d",
+ intoa(nat->nat_nsrcaddr), ntohs(nat->nat_nsport));
+printf(" ndst: %s:%d\n",
+ intoa(nat->nat_ndstaddr), ntohs(nat->nat_ndport));
+printf("===> real: %s:%d\n",
+ intoa(np->nl_realip.s_addr), ntohs(np->nl_realport));
}
}
================================================================================
Now telnet in to port 80 from another computer (192.168.204.1), and
observe the different results (running on console, hence kernel printfs
are interleaved):
================================================================================
netbsd5# uname -rm
5.2_STABLE i386
netbsd5# ./ipfbug
IPFilter version 4012900
listening on : 127.0.0.1:1234
connection from: 192.168.204.1:65392
nat_lookupredir !IPN_IN:
inip: 127.0.0.1:1234
outip: 192.168.204.87:80
oip: 192.168.204.1:65392
> real: 192.168.204.87:80
real: 192.168.204.87:80
netbsd5# ipnat -l
List of active MAP/Redirect filters:
rdr xennet0 0.0.0.0/0 port 80 -> 127.0.0.1 port 1234 tcp
List of active sessions:
RDR 127.0.0.1 1234 <- -> 192.168.204.87 80 [192.168.204.1 65392]
================================================================================
netbsd7# uname -rm
7.0_RC3 i386
netbsd7# ./ipfbug
IPFilter version 5010200
listening on : 127.0.0.1:1234
connection from: 192.168.204.1:65386
ipf_nat_lookupredir !IPN_IN:
osrc: 192.168.204.1:65386 odst: 192.168.204.86:80
nsrc: 192.168.204.1:65386 ndst: 127.0.0.1:1234
===> real: 127.0.0.1:1234
real: 127.0.0.1:1234
netbsd7# ipnat -l
List of active MAP/Redirect filters:
rdr xennet0 0/0 port 80 -> 127.0.0.1/32 port 1234 tcp
List of active sessions:
RDR 127.0.0.1 1234 <- -> 192.168.204.86 80 [192.168.204.1 65386]
================================================================================
>Fix:
I think ipf_nat_lookupredir() is simply returning the wrong variables.
Flipping XXXPWHACK to 1 should give the correct answer.
Something similar will be necessary in the untested IPN_IN case.
There is also an upgrade issue: IPFilter 5 grew a nl_v member, hence the
necessity of
#if IPFILTER_VERSION >= 5000003
nat.nl_v = 4;
#endif
in my toy server above. This means that any transparent proxy servers
written against IPFilter 4 will stop working on an upgrade to IPFilter 5.
It might be worth adding a "case 0:" to print a warning and fall through
to "case 4:" in ip_nat.c "switch (nl.nl_v)", rather than assume IPv0.
Home |
Main Index |
Thread Index |
Old Index