This series implements NAT networks support for FreeBSD using the Packet Filter (pf) firewall.
The commit messages provide high-level details and limitations of the current implementation, and I'll use this cover letter to provide some more technical details and describe testing I have performed for this change. Libvirt FreeBSD/pf NAT testing For two networks: virsh # net-dumpxml default <network> <name>default</name> <uuid>68cd5419-9fda-4cf0-9ac6-2eb9c1ba41ed</uuid> <forward mode='nat'> <nat> <port start='1024' end='65535'/> </nat> </forward> <bridge name='virbr0' stp='on' delay='0'/> <mac address='52:54:00:db:0e:e5'/> <ip address='192.168.122.1' netmask='255.255.255.0'> <dhcp> <range start='192.168.122.2' end='192.168.122.254'/> </dhcp> </ip> </network> virsh # net-dumpxml natnet <network> <name>natnet</name> <uuid>d3c59659-3ceb-4482-a625-1f839a54429c</uuid> <forward mode='nat'> <nat> <port start='1024' end='65535'/> </nat> </forward> <bridge name='virbr1' stp='on' delay='0'/> <mac address='52:54:00:0a:fc:1d'/> <ip address='10.0.100.1' netmask='255.255.255.0'> <dhcp> <range start='10.0.100.2' end='10.0.100.254'/> </dhcp> </ip> </network> virsh # The following rules are generated: $ sudo pfctl -a '*' -sn nat-anchor "libvirt/*" all { nat-anchor "default" all { nat pass on re0 inet from 192.168.122.0/24 to <natdst> -> (re0) port 1024:65535 round-robin } nat-anchor "natnet" all { nat pass on re0 inet from 10.0.100.0/24 to <natdst> -> (re0) port 1024:65535 round-robin } } $ $ sudo pfctl -a 'libvirt/default' -t natdst -T show 0.0.0.0/0 !192.168.122.0/24 !224.0.0.0/24 !255.255.255.255 $ sudo pfctl -a 'libvirt/natnet' -t natdst -T show 0.0.0.0/0 !10.0.100.0/24 !224.0.0.0/24 !255.255.255.255 $ $ sudo pfctl -a '*' -sr scrub all fragment reassemble anchor "libvirt/*" all { anchor "default" all { pass quick on virbr0 inet from 192.168.122.0/24 to 192.168.122.0/24 flags S/SA keep state pass quick on virbr0 inet from 192.168.122.0/24 to 224.0.0.0/24 flags S/SA keep state pass quick on virbr0 inet from 192.168.122.0/24 to 255.255.255.255 flags S/SA keep state block drop on virbr0 all } anchor "natnet" all { pass quick on virbr1 inet from 10.0.100.0/24 to 10.0.100.0/24 flags S/SA keep state pass quick on virbr1 inet from 10.0.100.0/24 to 224.0.0.0/24 flags S/SA keep state pass quick on virbr1 inet from 10.0.100.0/24 to 255.255.255.255 flags S/SA keep state block drop on virbr1 all } } pass all flags S/SA keep state $ Create two guests attached to the "default" network, vmA and vmB. vmA $ ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host noprefixroute valid_lft forever preferred_lft forever 2: enp0s4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether 52:54:00:67:eb:de brd ff:ff:ff:ff:ff:ff inet 192.168.122.92/24 brd 192.168.122.255 scope global dynamic noprefixroute enp0s4 valid_lft 1082sec preferred_lft 1082sec inet6 fe80::5054:ff:fe67:ebde/64 scope link noprefixroute valid_lft forever preferred_lft forever vmA $ vmB $ ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host noprefixroute valid_lft forever preferred_lft forever 2: enp0s4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether 52:54:00:d2:8b:41 brd ff:ff:ff:ff:ff:ff inet 192.168.122.154/24 metric 100 brd 192.168.122.255 scope global dynamic enp0s4 valid_lft 1040sec preferred_lft 1040sec inet6 fe80::5054:ff:fed2:8b41/64 scope link valid_lft forever preferred_lft forever vmB $ Test NAT rules: vmA $ ping -c 3 8.8.8.8 PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data. 64 bytes from 8.8.8.8: icmp_seq=1 ttl=57 time=14.7 ms 64 bytes from 8.8.8.8: icmp_seq=2 ttl=57 time=10.7 ms 64 bytes from 8.8.8.8: icmp_seq=3 ttl=57 time=10.1 ms --- 8.8.8.8 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2006ms rtt min/avg/max/mdev = 10.099/11.835/14.710/2.047 ms vmA $ vmB $ ping -c 3 8.8.8.8 PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data. 64 bytes from 8.8.8.8: icmp_seq=1 ttl=57 time=15.1 ms 64 bytes from 8.8.8.8: icmp_seq=2 ttl=57 time=11.0 ms 64 bytes from 8.8.8.8: icmp_seq=3 ttl=57 time=10.4 ms --- 8.8.8.8 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2006ms rtt min/avg/max/mdev = 10.434/12.198/15.113/2.075 ms vmB $ vmA $ curl wttr.in/?0Q Fog _ - _ - _ - +4(1) °C _ - _ - _ ↙ 11 km/h _ - _ - _ - 0 km 0.0 mm vmA $ vmB $ curl wttr.in/?0Q Fog _ - _ - _ - +4(1) °C _ - _ - _ ↙ 11 km/h _ - _ - _ - 0 km 0.0 mm vmB $ Inter-VM connectivity: vmA $ ping -c 3 192.168.122.154 PING 192.168.122.154 (192.168.122.154) 56(84) bytes of data. 64 bytes from 192.168.122.154: icmp_seq=1 ttl=64 time=0.253 ms 64 bytes from 192.168.122.154: icmp_seq=2 ttl=64 time=0.226 ms 64 bytes from 192.168.122.154: icmp_seq=3 ttl=64 time=0.269 ms --- 192.168.122.154 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2042ms rtt min/avg/max/mdev = 0.226/0.249/0.269/0.017 ms vmA $ vmA $ ssh 192.168.122.154 uname novel@192.168.122.154's password: Linux vmA $ Multicast test: vmA $ iperf -s -u -B 224.0.0.1 -i 1 ------------------------------------------------------------ Server listening on UDP port 5001 Joining multicast group 224.0.0.1 Server set to single client traffic mode (per multicast receive) UDP buffer size: 208 KByte (default) ------------------------------------------------------------ [ 1] local 224.0.0.1 port 5001 connected with 192.168.122.154 port 36963 [ ID] Interval Transfer Bandwidth Jitter Lost/Total Datagrams [ 1] 0.00-1.00 sec 131 KBytes 1.07 Mbits/sec 0.030 ms 0/91 (0%) [ 1] 1.00-2.00 sec 128 KBytes 1.05 Mbits/sec 0.022 ms 0/89 (0%) [ 1] 2.00-3.00 sec 128 KBytes 1.05 Mbits/sec 0.021 ms 0/89 (0%) [ 1] 0.00-3.02 sec 389 KBytes 1.06 Mbits/sec 0.026 ms 0/271 (0%) vmB $ iperf -c 224.0.0.1 -u -T 32 -t 3 -i 1 ------------------------------------------------------------ Client connecting to 224.0.0.1, UDP port 5001 Sending 1470 byte datagrams, IPG target: 11215.21 us (kalman adjust) UDP buffer size: 208 KByte (default) ------------------------------------------------------------ [ 1] local 192.168.122.154 port 36963 connected with 224.0.0.1 port 5001 [ ID] Interval Transfer Bandwidth [ 1] 0.0000-1.0000 sec 131 KBytes 1.07 Mbits/sec [ 1] 1.0000-2.0000 sec 128 KBytes 1.05 Mbits/sec [ 1] 2.0000-3.0000 sec 128 KBytes 1.05 Mbits/sec [ 1] 0.0000-3.0173 sec 389 KBytes 1.06 Mbits/sec [ 1] Sent 272 datagrams vmB $ Broadcast test: vmA $ sudo sysctl -w net.ipv4.icmp_echo_ignore_broadcasts=0 net.ipv4.icmp_echo_ignore_broadcasts = 0 vmA $ vmB $ sudo sysctl -w net.ipv4.icmp_echo_ignore_broadcasts=0 net.ipv4.icmp_echo_ignore_broadcasts = 0 vmB $ host $ ping 192.168.122.255 PING 192.168.122.255 (192.168.122.255): 56 data bytes 64 bytes from 192.168.122.154: icmp_seq=0 ttl=64 time=0.199 ms 64 bytes from 192.168.122.92: icmp_seq=0 ttl=64 time=0.227 ms (DUP!) 64 bytes from 192.168.122.154: icmp_seq=1 ttl=64 time=0.209 ms 64 bytes from 192.168.122.92: icmp_seq=1 ttl=64 time=0.235 ms (DUP!) ^C --- 192.168.122.255 ping statistics --- 2 packets transmitted, 2 packets received, +2 duplicates, 0.0% packet loss round-trip min/avg/max/stddev = 0.199/0.218/0.235/0.014 ms This testing does not cover any negative scenarios which are probably not that important at this point. Roman Bogorodskiy (2): network: bridge_driver: add BSD implementation network: introduce Packet Filter firewall backend meson.build | 2 + po/POTFILES | 2 + src/network/bridge_driver_bsd.c | 107 +++++++++ src/network/bridge_driver_conf.c | 8 + src/network/bridge_driver_linux.c | 2 + src/network/bridge_driver_platform.c | 2 + src/network/meson.build | 1 + src/network/network_pf.c | 327 +++++++++++++++++++++++++++ src/network/network_pf.h | 26 +++ src/util/virfirewall.c | 4 +- src/util/virfirewall.h | 2 + 11 files changed, 482 insertions(+), 1 deletion(-) create mode 100644 src/network/bridge_driver_bsd.c create mode 100644 src/network/network_pf.c create mode 100644 src/network/network_pf.h -- 2.49.0