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

Reply via email to