Score:1

Filtering traffic by MAC address with nftables

in flag

TL;DR: when spoofing MAC addresses, how can I make sure the real addresses won't be leaked to the rest of the network using nftables ?

Context

For network security training purposes I am currently building a network tap consisting of a transparent bridge with two interfaces (the tap).

The tap is a computer is running Debian Bullseye, that has 4 ethernet interfaces as follows:

Interface MAC
enp3s0 00:90:27:b4:40:58
enp4s0 00:90:27:b4:40:59
enp6s0 00:90:27:b4:40:5a
enp7s0 00:90:27:b4:40:5b
  • enp3s0 is connected to the switch and gets an IP address via DHCP.
  • Additionally I created a bridge br0 with nmcli, with two slave interfaces: enp6s0 and enp7s0
  • enp7s0 is connected to the switch

The bridge is created as follows:

nmcli connection add connection.type bridge ifname br0 con-name br0 ipv4.method disabled ipv6.method ignore bridge.stp no bridge.vlan-filtering yes connection.autoconnect-slaves 1 autoconnect no
nmcli connection add type ethernet ifname enp6s0 con-name br0-slave-enp6s0 slave-type bridge master br0 autoconnect no
nmcli connection add type ethernet ifname enp7s0 con-name br0-slave-enp7s0 slave-type bridge master br0 autoconnect no

Goal

I want the machine to remain stealth at all times on the network since it is a passive tap, so I want to drop outgoing traffic that could accidentally leak the MAC addresses of my machine. So I made an attempt with nftables as follows, with a named set for the MAC addresses:

table netdev bridge {
    set macs {
        type ether_addr
        elements = { 00:90:27:b4:40:59,
                 00:90:27:b4:40:5a, 00:90:27:b4:40:5b }
    }

    chain br0 {
        type filter hook ingress devices = { enp4s0, enp6s0, enp7s0 } priority filter; policy accept;
        ether saddr @macs drop
        log flags all prefix "myfilter " counter
    }
}

I also want to have counters and log details of the dropped packets.

Problem

Traffic is being dropped and logging is done to /var/log/syslog as expected. Counters are being updated. But the netfilter rule does not seem to be working as intended. Instead it seems to be dropping all traffic sent to enp7s0.

Sample:

Dec 20 01:23:47 testbox kernel: [32441.125971] myfilter IN=enp7s0 OUT= MACSRC=10:c2:5a:58:72:8f MACDST=01:00:5e:7f:ff:fa MACPROTO=0800 SRC=192.168.0.1 DST=239.255.255.250 LEN=367 TOS=0x00 PREC=0x00 TTL=4 ID=37026 PROTO=UDP SPT=1900 DPT=1900 LEN=347 
Dec 20 01:23:47 testbox kernel: [32441.126842] myfilter IN=enp7s0 OUT= MACSRC=10:c2:5a:58:72:8f MACDST=01:00:5e:7f:ff:fa MACPROTO=0800 SRC=192.168.0.1 DST=239.255.255.250 LEN=312 TOS=0x00 PREC=0x00 TTL=4 ID=37027 PROTO=UDP SPT=1900 DPT=1900 LEN=292 
Dec 20 01:23:47 testbox kernel: [32441.127554] myfilter IN=enp7s0 OUT= MACSRC=10:c2:5a:58:72:8f MACDST=01:00:5e:7f:ff:fa MACPROTO=0800 SRC=192.168.0.1 DST=239.255.255.250 LEN=303 TOS=0x00 PREC=0x00 TTL=4 ID=37028 PROTO=UDP SPT=1900 DPT=1900 LEN=283 
Dec 20 01:23:47 testbox kernel: [32441.128342] myfilter IN=enp7s0 OUT= MACSRC=10:c2:5a:58:72:8f MACDST=01:00:5e:7f:ff:fa MACPROTO=0800 SRC=192.168.0.1 DST=239.255.255.250 LEN=377 TOS=0x00 PREC=0x00 TTL=4 ID=37029 PROTO=UDP SPT=1900 DPT=1900 LEN=357 
Dec 20 01:23:53 testbox kernel: [32447.003559] myfilter IN=enp7s0 OUT= MACSRC=10:c2:5a:58:72:8f MACDST=33:33:00:00:00:01 MACPROTO=86dd SRC=fe80:0000:0000:0000:12c2:5aff:fe75:899d DST=ff02:0000:0000:0000:0000:0000:0000:0001 LEN=176 TC=0 HOPLIMIT=255 FLOWLBL=0 PROTO=ICMPv6 TYPE=134 CODE=0 
Dec 20 01:23:53 testbox kernel: [32447.015382] myfilter IN=enp7s0 OUT= MACSRC=00:90:27:e6:10:58 MACDST=33:33:00:00:00:16 MACPROTO=86dd SRC=fe80:0000:0000:0000:6d06:284e:2844:db03 DST=ff02:0000:0000:0000:0000:0000:0000:0016 LEN=136 TC=0 HOPLIMIT=1 FLOWLBL=0 OPT ( ) PROTO=ICMPv6 TYPE=143 CODE=0 
Dec 20 01:23:53 testbox kernel: [32447.619279] myfilter IN=enp7s0 OUT= MACSRC=00:90:27:e6:10:58 MACDST=33:33:00:00:00:16 MACPROTO=86dd SRC=fe80:0000:0000:0000:6d06:284e:2844:db03 DST=ff02:0000:0000:0000:0000:0000:0000:0016 LEN=136 TC=0 HOPLIMIT=1 FLOWLBL=0 OPT ( ) PROTO=ICMPv6 TYPE=143 CODE=0 

I believe I might need to use an egress hook instead of ingress. I understand the egress hook is available in nft from version 1.01. Current stable version of nft in Debian is 0.98, so I manually upgraded to v1.01 by downloading the packages manually. But replacing ingress with egress does not work: nft does not recognize my rule.

Questions

  • Does my approach make sense ? Can I use the ingress hook for my purpose ? Or is egress required ?
  • Alternatively, can this be done with tc ?
  • Or can the bridge be further isolated against traffic originating from other interfaces ?
A.B avatar
cl flag
A.B
As a remark, Linux kernel-side support for nftables netdev egress starts at Linux 5.16 (including "current" 5.16-rc6)
Kate avatar
in flag
@A.B Good point: my running kernel is evidently not recent enough: Linux testbox 5.10.0-9-amd64 #1 SMP Debian 5.10.70-1 (2021-09-30) x86_64 GNU/Linux
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.