OK so I have learnt that vmaps can be weird from my earlier ct state established or related accept # right ct state {established: accept, related: accept} # right ct state vmap {established or related: accept} # wrong This error message is confusing me, though: # nft 'flush ruleset;table a;chain a b;a b iifname vmap {"ppp0": drop}' [no error] # nft 'flush ruleset;table a;chain a b;a b iifname vmap {"ppp*": drop}' Error: Byteorder mismatch: expected big endian, got host endian flush ruleset;table a;chain a b;a b iifname vmap {"ppp*": drop} ^^^^^^^ # nft 'flush ruleset;table a;chain a b;a b iifname {eth0, ppp0}' [no error] # nft 'flush ruleset;table a;chain a b;a b iifname {eth0, ppp*}' Error: Byteorder mismatch: expected big endian, got host endian flush ruleset;table a;chain a b;a b iifname {eth0, ppp*} ^^^^^^^ Is the actual problem "you can't glob in set/vmap" ? My actual end goal is to translate this gateway policy iptables-save rules: # Allow arbitrary flows to be initiated from <more trusted> to <less trusted>. # Allow limited flows to be initiated from the internet (ADSL) to the DMZ. # NOTE: the internet is on ppp0 and ppp1; # we cannot give them logical names due to a design flaw in pppd. # NOTE: return packets are handled elsewhere; everything below is --ctstate NEW. # NOTE: There are 5 networks, so 5P2 = 20 permutations. # For speed, the -j REJECT permutations are left implicit. -A FORWARD -i dmz -o ppp+ -j ACCEPT -A FORWARD -i scratch -o ppp+ -j ACCEPT -A FORWARD -i unmanaged -o ppp+ -j ACCEPT -A FORWARD -i managed -o ppp+ -j ACCEPT -A FORWARD -i managed -o dmz -j ACCEPT -A FORWARD -i managed -o scratch -j ACCEPT -A FORWARD -i managed -o unmanaged -j ACCEPT -A FORWARD -i unmanaged -o scratch -j ACCEPT -A FORWARD -i ppp+ -o dmz -j LIMITED -A FORWARD -i scratch -o dmz -j LIMITED -A FORWARD -i unmanaged -o dmz -j LIMITED # In theory DMZ hosts must fend for themselves; # in practice their competence is suspect. # Thus, limit DMZ access to "typical" ports (plus some per-host exceptions). # Within those typical ports, DMZ hosts still fend for themselves. -A LIMITED -p tcp -m multiport --dports ssh,http,https,smtp,submission,imaps,domain -j ACCEPT -A LIMITED -p udp --dport domain -j ACCEPT -A LIMITED -p udp --dport collectd -d collectd -j ACCEPT ...to a SINGLE nftables rule like: iifname . oifname vmap { * . ppp* : accept, # LANs can start arbitrary flows to the internet * . dmz : jump limited, # LANs & internet can start SPECIFIC flows to DMZ managed . * : accept, # managed LAN (most trusted) can start arbitrary flows to anyone unmanaged . scratch : accept, # the only other thing } If I can't use globs, I guess I have to do this using separate rules, which is annoying because then the order matters for performance reasons. This looks sensible (apart from managed->dmz, which I don't care about): oifname ppp* accept oifname dmz jump limited iifname managed accept iifname unmanaged oifname scratch accept For maximum paranoia it makes sense to restrict to KNOWN ifaces, so if someone e.g. adds an OpenVPN later, the existing rules won't inadvertently allow traffic to/from tun0: # ppp* becomes {ppp0, ppp1} because we can't glob in a set. # Thus, we now have 6 networks (6P2 = 30 permutations). iifname {managed, unmanaged, scratch, dmz} oifname {ppp0, ppp1} accept iifname {unmanaged, scratch, ppp0, ppp1} oifname dmz jump limited iifname managed oifname {unmanaged, scratch, dmz, ppp0, ppp1} accept iifname unmanaged oifname scratch accept # And these are the implicit -j REJECT permutations #iifname {dmz, ppp0, ppp1} oifname {managed, unmanaged, scratch} reject #iifname scratch oifname {managed, unmanaged} reject #iifname unmanaged oifname managed reject #iifname {ppp0, ppp1} oifname {ppp0, ppp1} reject Apart from having to expand out ppp*, and having to optimize the rules so the high-count rule is first, I think that's tolerable. Does that look sensible? Is there any way to make it more "elegant"?