PF's DIOCNATLOOK system call can not obtain correct return data in OpenBSD
7.3-7.5, but this call was normal before OpenBSD 7.3. I tested it on
OpenBSD 7.2 and OpenBSD 6.9 and both returned correct data.
The test code is at the end of the report (from man page of PF with a
little modification), and the following is the test process:
### [Didn't WORK] DIOCNATLOOK didn't work on OpenBSD 7.3, the following are
systeminfo, pf rules and test process
1. os infomation
openbsd# uname -a
OpenBSD openbsd.home.pro 7.3 GENERIC.MP#5 amd64
2. compile the test code
openbsd# cc test.c
3. the pf rdr rule
openbsd# pfctl -sr
pass in quick on em0 inet proto tcp from any to any port = 1234 flags S/SA
rdr-to 127.0.0.1 port 4000
4. connect from a client(192.168.11.74) to openbsd's port 1234, and print
the pf state table on openbsd.
openbsd# pfctl -ss|grep 1234
all tcp 127.0.0.1:4000 (192.168.11.4:1234) <- 192.168.11.74:26244
ESTABLISHED:ESTABLISHED
5. running test code with: client_ip client_port rdr_ip rdr_port, and
the code get an error message.
openbsd# ./a.out 192.168.11.74 26244 127.0.0.1 4000
a.out: DIOCNATLOOK: No such file or directory
### DIOCNATLOOK works on OpenBSD 7.2, the following are systeminfo, pf
rules and test process
1. os information
obsd# uname -a
OpenBSD obsd.my.domain 7.2 GENERIC.MP#758 amd64
2. compile the test code
openbsd# cc test.c
3. the pf rdr rule
openbsd# pfctl -sr
pass in quick on em0 inet proto tcp from any to any port = 1234 flags S/SA
rdr-to 127.0.0.1 port 4000
4. connect from a client(192.168.11.74) to openbsd's port 1234, and print
the pf state table on openbsd.
obsd# pfctl -ss | grep 1234
all tcp 127.0.0.1:4444 (192.168.11.43:1234) <- 192.168.11.74:38485
FIN_WAIT_2:ESTABLISHED
5. running test code with: client_ip client_port rdr_ip rdr_port, and
the code get corrent result.
obsd# ./a.out 192.168.11.74 38485 127.0.0.1 4444
internal host 192.168.11.43:1234
BTW: This code works on FreeBSD 14 and NetBSD 10
I looked at the source code of pf_ioctl.c and pf.c in both OpenBSD 7.2 and
OpenBSD 7.3, I noticed that the call changed from NB_FIND to NBT_FIND in
OpenBSD 7.3, I don't know if this is the cause.
--
xiangbo
Code of test.c:
//-------------------------------------------------------------------------------------------------------
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <net/pfvar.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
u_int32_t
read_address(const char *s)
{
int a, b, c, d;
sscanf(s, "%i.%i.%i.%i", &a, &b, &c, &d);
return htonl(a << 24 | b << 16 | c << 8 | d);
}
void
print_address(u_int32_t a)
{
a = ntohl(a);
printf("%d.%d.%d.%d", a >> 24 & 255, a >> 16 & 255,
a >> 8 & 255, a & 255);
}
int
main(int argc, char *argv[])
{
struct pfioc_natlook nl;
int dev;
if (argc != 5) {
printf("%s <client addr> <client port> <rdr addr> <rdr port>\n",
argv[0]);
return 1;
}
dev = open("/dev/pf", O_RDWR);
if (dev == -1)
err(1, "open(\"/dev/pf\") failed");
memset(&nl, 0, sizeof(struct pfioc_natlook));
nl.saddr.v4.s_addr = read_address(argv[1]);
nl.sport = htons(atoi(argv[2]));
nl.daddr.v4.s_addr = read_address(argv[3]);
nl.dport = htons(atoi(argv[4]));
nl.af = AF_INET;
nl.proto = IPPROTO_TCP;
nl.direction = PF_OUT;
if (ioctl(dev, DIOCNATLOOK, &nl))
err(1, "DIOCNATLOOK");
printf("internal host ");
print_address(nl.rdaddr.v4.s_addr);
printf(":%u\n", ntohs(nl.rdport));
return 0;
}
//-------------------------------------------------------------------------------------------------------