I'm using a Raspberry Pi 3B+ (192.168.1.6 / VLAN 1 / br0 on eth0) as a VPN gateway (OpenVPN tun0) for Client A (192.168.5.10 / VLAN 2 / eth0.2). I split the single Pi interface with a managed L2 switch and VLANs. I am trying to duplicate traffic destined for Client A to Monitoring PC on the VLAN 1 (192.168.1.107) I've installed the require kmod (kmod-nft-dup-inet) and confirmed with modprobe and lsmod that it is infact loaded.
However when trying to create the rule from the command line I get syntax errors, I've read through the forums for similar thread and also consulted the nft wiki (https://wiki.nftables.org/wiki-nftables/index.php/Duplicating_packets) but I am still not sure what I am doing wrong, I would be very grateful if anyone could point me in the correct direction.
I'm using the latest OpenWRT snapshot for the Pi 3B+
┌──(root@rpi-owrt)-[00:08:56]-[~]
└─# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN 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
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master br0 state UP qlen 1000
link/ether b8:27:eb:27:4b:d5 brd ff:ff:ff:ff:ff:ff
3: teql0: <NOARP> mtu 1500 qdisc noop state DOWN qlen 100
link/void
4: wlan0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000
link/ether b8:27:eb:72:1e:80 brd ff:ff:ff:ff:ff:ff
5: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP qlen 1000
link/ether b8:27:eb:27:4b:d5 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.6/24 brd 192.168.1.255 scope global br0
valid_lft forever preferred_lft forever
inet6 fe80::ba27:ebff:fe27:4bd5/64 scope link
valid_lft forever preferred_lft forever
6: eth0.2@eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc htb state UP qlen 1000
link/ether b8:27:eb:27:4b:d5 brd ff:ff:ff:ff:ff:ff
inet 192.168.5.1/24 brd 192.168.5.255 scope global eth0.2
valid_lft forever preferred_lft forever
inet6 fe80::ba27:ebff:fe27:4bd5/64 scope link
valid_lft forever preferred_lft forever
7: eth0.3@eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc htb state UP qlen 1000
link/ether b8:27:eb:27:4b:d5 brd ff:ff:ff:ff:ff:ff
inet 192.168.6.1/24 brd 192.168.6.255 scope global eth0.3
valid_lft forever preferred_lft forever
inet6 fe80::ba27:ebff:fe27:4bd5/64 scope link
valid_lft forever preferred_lft forever
8: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN qlen 500
link/[65534]
inet 10.8.0.6/24 scope global tun0
valid_lft forever preferred_lft forever
inet6 fe80::a466:c6ac:c325:3bf1/64 scope link flags 800
valid_lft forever preferred_lft forever
9: ifb0: <BROADCAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc htb state UNKNOWN qlen 32
link/ether 2e:2d:13:75:85:04 brd ff:ff:ff:ff:ff:ff
inet6 fe80::2c2d:13ff:fe75:8504/64 scope link
valid_lft forever preferred_lft forever
10: ifb1: <BROADCAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc htb state UNKNOWN qlen 32
link/ether 42:81:fb:59:4f:62 brd ff:ff:ff:ff:ff:ff
inet6 fe80::4081:fbff:fe59:4f62/64 scope link
valid_lft forever preferred_lft forever
┌──(root@rpi-owrt)-[00:08:58]-[~]
└─# nft add rule inet fw4 mangle prerouting dup to ip saddr map { 192.168.5.10 : 192.168.1.107 }
Error: syntax error, unexpected dup
add rule inet fw4 mangle prerouting dup to ip saddr map { 192.168.5.10 : 192.168.1.107 }
^^^
┌──(root@rpi-owrt)-[00:09:10]-[~]
└─# nft add rule inet fw4 mangle prerouting dup to br0
Error: syntax error, unexpected dup
add rule inet fw4 mangle prerouting dup to br0
^^^
┌──(root@rpi-owrt)-[00:10:02]-[~]
└─# nft add rule mangle prerouting dup to br0
Error: Could not resolve hostname: Name does not resolve
add rule mangle prerouting dup to br0
^^^
┌──(root@rpi-owrt)-[00:10:32]-[~]
└─# nft add rule mangle prerouting dup to 192.168.1.107
Error: No such file or directory; did you mean table ‘fw4’ in family inet?
add rule mangle prerouting dup to 192.168.1.107
^^^^^^
┌──(root@rpi-owrt)-[00:10:45]-[~]
└─#
Hi, thanks for your reply, I did install the kernel module and checked it with modprobe/lsmod, it does seem to be installed and available, and I am indeed running the latest snapshot.
It appears that you can only create rules with a dup action in a single family table. This is very odd, as everything else in nftables is dual-stack (with, of course, the caveat that you can't mix up the families in a given expression, set or map). Since this expression is "pure" IPv4, seems like it should work.
$ nft create table ip test4
$ nft create chain ip test4 input '{ type filter hook input priority 0; policy accept ; }'
$ nft add rule ip test4 input 'dup to ip saddr map { 192.168.5.10 : 192.168.1.107 }'
$ nft list table ip test4
table ip test4 {
chain input {
type filter hook input priority filter; policy accept;
dup to ip saddr map { 192.168.5.10 : 192.168.1.107 }
}
}
But, change the table to dual stack...
$ nft create table inet test46
$ nft create chain inet test46 input '{ type filter hook input priority 0; policy accept ; }'
$ nft add rule inet test46 input 'dup to ip saddr map { 192.168.5.10 : 192.168.1.107 }'
Error: unsupported family
add rule inet test46 input dup to ip saddr map { 192.168.5.10 : 192.168.1.107 }
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
thank you very much for your time in testing that and the response, your reply is full of information, I just need to wrap my head around it will test it out and update this thread
Tried adding a redundant family selector on the rule meta nfproto ipv4, which is almost never necessary and should not be in this case, but I thought "Why not?"
Here's the script I used, in case it saves your some time:
#!/bin/sh -x
#$ nft add rule inet fw4 mangle_prerouting 'dup to ip saddr map { 192.168.5.10 : 192.168.1.107 }'
#Error: unsupported family
#add rule inet fw4 mangle_prerouting dup to ip saddr map { 192.168.5.10 : 192.168.1.107 }
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
if false ; then
nft create table ip test4
nft create chain ip test4 input '{ type filter hook input priority 0; policy accept ; }'
nft add rule ip test4 input 'dup to ip saddr map { 192.168.5.10 : 192.168.1.107 }'
nft list table ip test4
nft delete table ip test4
fi
if false ; then
nft create table ip6 test6
nft create chain ip6 test6 input '{ type filter hook input priority 0; policy accept ; }'
nft add rule ip6 test6 input 'dup to ip6 saddr map { fe80::1:2:3:4 : fe80::4:3:2:1 }'
nft list table ip6 test6
nft delete table ip6 test6
fi
if true ; then
nft create table inet test46
nft add map inet test46 dup_v4 '{ type ipv4_addr : ipv4_addr; elements = { 192.168.4.10 : 192.168.1.107 } }'
nft add map inet test46 dup_v6 '{ type ipv6_addr : ipv6_addr; elements = { fe80::1:2:3:4 : fe80::4:3:2:1 } }'
nft create chain inet test46 input '{ type filter hook input priority 0; policy accept ; }'
nft add rule inet test46 input 'meta nfproto ipv4 dup to ip saddr map { 192.168.5.10 : 192.168.1.107 }'
nft add rule inet test46 input 'meta nfproto ipv4 dup to ip saddr map @dup_v4'
nft add rule inet test46 input 'dup to ip6 saddr map { fe80::1:2:3:4 : fe80::4:3:2:1 }'
nft list table inet test46
nft delete table inet test46
fi
Thank you again for your detailed answer and for scripting the required commands, I'm still trying to wrap my head around nftables in general. I should also say that my raspberrry pi vpn box is ipv4 only. And the firewall zones have also been set to restrict to ipv4 only. My first area of confusion is that my error is not the same as yours mine says "Error: syntax error, unexpected dup" whereas yours says "Error: unsupported family" I did try your suggestion from your first answer, I created a new table then the chain and then the rule, however it didnt seem to actually do anything, opening wireshark on the monitoring computer didnt show any of the traffic.
Atleast following your steps from the first answer, I was able to create the table/chain and add the dup rule.
Yeah, my tests were non-functional, hence the input hook. I should have mentioned that (I wrote the script off the top of my head and just typed whatever came to mind first).
One nitpick that has no bearing on functionality, just style: by convention, chain names are usually based on the hook to which they are assigned (and often suffixed with the priority, if it's not the "default 0" == filter).
If you want to keep it in one file, here's a trick I use to "pretty format" the table, yet keep everything together (the -f - says "read stdin", which then is redirected from the "heredoc" delimited by TABLE).
#!/bin/sh
# First, delete the table if it exists.
nft list tables | grep -q 'inet snort' && nft -e delete table inet snort
# Now we rebuild it from scratch.
nft -f - <<TABLE
table inet snort {
chain prerouting_ips {
type filter hook prerouting priority raw; policy accept;
counter queue flags bypass to 4-6
}
}
TABLE