From 0bc844ba02ae460d4a895878b9136ba5d8e09b37 Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Tue, 25 Jan 2022 23:12:20 +0100 Subject: [PATCH] ruleset: properly deal with wildcards in zone device selectors Translate iptables style wildcards (`name+`) to nftables ones (`name*`) and ensure that such wildcards are not used as anonymous set items but that they're tested by separate expressions. Also move redundant zone device/subnet selection expressions into a common template and include it where applicable. Finally add a new testcase which covers various device name wildcard corner- cases and rule permutation requirements. Signed-off-by: Jo-Philipp Wich --- root/usr/share/firewall4/templates/ruleset.uc | 10 +- .../share/firewall4/templates/zone-jump.uc | 6 + .../share/firewall4/templates/zone-match.uc | 22 +- .../share/firewall4/templates/zone-mssfix.uc | 9 +- .../share/firewall4/templates/zone-verdict.uc | 10 +- root/usr/share/ucode/fw4.uc | 81 +++- tests/02_zones/04_wildcard_devices | 351 ++++++++++++++++++ 7 files changed, 444 insertions(+), 45 deletions(-) create mode 100644 root/usr/share/firewall4/templates/zone-jump.uc create mode 100644 tests/02_zones/04_wildcard_devices diff --git a/root/usr/share/firewall4/templates/ruleset.uc b/root/usr/share/firewall4/templates/ruleset.uc index dadb1fd..4e519bf 100644 --- a/root/usr/share/firewall4/templates/ruleset.uc +++ b/root/usr/share/firewall4/templates/ruleset.uc @@ -77,7 +77,7 @@ table inet fw4 { {%+ include("rule.uc", { fw4, rule }) %} {% endfor %} {% for (let zone in fw4.zones()): for (let rule in zone.match_rules): %} - {%+ include("zone-match.uc", { fw4, zone, rule, direction: "input" }) %} + {%+ include("zone-jump.uc", { fw4, zone, rule, direction: "input" }) %} {% endfor; endfor %} {% if (fw4.input_policy() == "reject"): %} jump handle_reject @@ -98,7 +98,7 @@ table inet fw4 { {%+ include("rule.uc", { fw4, rule }) %} {% endfor %} {% for (let zone in fw4.zones()): for (let rule in zone.match_rules): %} - {%+ include("zone-match.uc", { fw4, zone, rule, direction: "forward" }) %} + {%+ include("zone-jump.uc", { fw4, zone, rule, direction: "forward" }) %} {% endfor; endfor %} {% if (fw4.forward_policy() == "reject"): %} jump handle_reject @@ -118,7 +118,7 @@ table inet fw4 { {%+ include("rule.uc", { fw4, rule }) %} {% endfor %} {% for (let zone in fw4.zones()): for (let rule in zone.match_rules): %} - {%+ include("zone-match.uc", { fw4, zone, rule, direction: "output" }) %} + {%+ include("zone-jump.uc", { fw4, zone, rule, direction: "output" }) %} {% endfor; endfor %} {% if (fw4.output_policy() == "reject"): %} jump handle_reject @@ -207,7 +207,7 @@ table inet fw4 { {% for (let zone in fw4.zones()): %} {% if (zone.dflags.dnat): %} {% for (let rule in zone.match_rules): %} - {%+ include("zone-match.uc", { fw4, zone, rule, direction: "dstnat" }) %} + {%+ include("zone-jump.uc", { fw4, zone, rule, direction: "dstnat" }) %} {% endfor %} {% endif %} {% endfor %} @@ -221,7 +221,7 @@ table inet fw4 { {% for (let zone in fw4.zones()): %} {% if (zone.dflags.snat): %} {% for (let rule in zone.match_rules): %} - {%+ include("zone-match.uc", { fw4, zone, rule, direction: "srcnat" }) %} + {%+ include("zone-jump.uc", { fw4, zone, rule, direction: "srcnat" }) %} {% endfor %} {% endif %} {% endfor %} diff --git a/root/usr/share/firewall4/templates/zone-jump.uc b/root/usr/share/firewall4/templates/zone-jump.uc new file mode 100644 index 0000000..902321f --- /dev/null +++ b/root/usr/share/firewall4/templates/zone-jump.uc @@ -0,0 +1,6 @@ +{%+ if (rule.family): -%} + meta nfproto {{ fw4.nfproto(rule.family) }} {%+ endif -%} +{%+ include("zone-match.uc", { egress: (direction in ["output", "srcnat"]), rule }) -%} +jump {{ direction }}_{{ zone.name }} comment "!fw4: Handle {{ zone.name }} {{ + fw4.nfproto(rule.family, true) +}} {{ direction }} traffic" diff --git a/root/usr/share/firewall4/templates/zone-match.uc b/root/usr/share/firewall4/templates/zone-match.uc index 656c568..f336447 100644 --- a/root/usr/share/firewall4/templates/zone-match.uc +++ b/root/usr/share/firewall4/templates/zone-match.uc @@ -1,20 +1,10 @@ -{%+ if (rule.family): -%} - meta nfproto {{ fw4.nfproto(rule.family) }} {%+ endif -%} {%+ if (rule.devices_pos): -%} - {{ (direction in ["output", "srcnat"]) - ? "oifname" : "iifname" }} {{ fw4.set(rule.devices_pos) }} {%+ endif -%} + {{ egress ? "oifname" : "iifname" }} {{ fw4.set(rule.devices_pos) }} {%+ endif -%} {%+ if (rule.devices_neg): -%} - {{ (direction in ["output", "srcnat"]) - ? "oifname" : "iifname" - }} != {{ fw4.set(rule.devices_neg) }} {%+ endif -%} + {{ egress ? "oifname" : "iifname" }} != {{ fw4.set(rule.devices_neg) }} {%+ endif -%} +{%+ for (let wcndev in rule.devices_neg_wildcard): -%} + {{ egress ? "oifname" : "iifname" }} != {{ fw4.quote(wcndev) }} {%+ endfor -%} {%+ if (rule.subnets_pos): -%} - {{ fw4.ipproto(rule.family) }} {{ - (direction in ["output", "srcnat"]) ? "daddr" : "saddr" - }} {{ fw4.set(rule.subnets_pos) }} {%+ endif -%} + {{ fw4.ipproto(rule.family) }} {{ egress ? "daddr" : "saddr" }} {{ fw4.set(rule.subnets_pos) }} {%+ endif -%} {%+ if (rule.subnets_neg): -%} - {{ fw4.ipproto(rule.family) }} {{ - (direction in ["output", "srcnat"]) ? "daddr" : "saddr" - }} != {{ fw4.set(rule.subnets_neg) }} {%+ endif -%} -jump {{ direction }}_{{ zone.name }} comment "!fw4: Handle {{ zone.name }} {{ - fw4.nfproto(rule.family, true) -}} {{ direction }} traffic" + {{ fw4.ipproto(rule.family) }} {{ egress ? "daddr" : "saddr" }} != {{ fw4.set(rule.subnets_neg) }} {%+ endif -%} diff --git a/root/usr/share/firewall4/templates/zone-mssfix.uc b/root/usr/share/firewall4/templates/zone-mssfix.uc index dd5766b..b76cfb6 100644 --- a/root/usr/share/firewall4/templates/zone-mssfix.uc +++ b/root/usr/share/firewall4/templates/zone-mssfix.uc @@ -1,13 +1,6 @@ {%+ if (rule.family): -%} meta nfproto {{ fw4.nfproto(rule.family) }} {%+ endif -%} -{%+ if (rule.devices_pos): -%} - {{ egress ? "oifname" : "iifname" }} {{ fw4.set(rule.devices_pos) }} {%+ endif -%} -{%+ if (rule.devices_neg): -%} - {{ egress ? "oifname" : "iifname" }} != {{ fw4.set(rule.devices_neg) }} {%+ endif -%} -{%+ if (rule.subnets_pos): -%} - {{ fw4.ipproto(rule.family) }} {{ egress ? "daddr" : "saddr" }} {{ fw4.set(rule.subnets_pos) }} {%+ endif -%} -{%+ if (rule.subnets_neg): -%} - {{ fw4.ipproto(rule.family) }} {{ egress ? "daddr" : "saddr" }} != {{ fw4.set(rule.subnets_neg) }} {%+ endif -%} +{%+ include("zone-match.uc", { egress, rule }) -%} tcp flags syn tcp option maxseg size set rt mtu {%+ if (zone.log & 2): -%} log prefix "MSSFIX {{ zone.name }} out: " {%+ endif -%} comment "!fw4: Zone {{ zone.name }} {{ diff --git a/root/usr/share/firewall4/templates/zone-verdict.uc b/root/usr/share/firewall4/templates/zone-verdict.uc index c8f5667..6a9c8f6 100644 --- a/root/usr/share/firewall4/templates/zone-verdict.uc +++ b/root/usr/share/firewall4/templates/zone-verdict.uc @@ -1,14 +1,6 @@ {%+ if (rule.family): -%} meta nfproto {{ fw4.nfproto(rule.family) }} {%+ endif -%} -{%+ if (rule.devices_pos): -%} - {{ egress ? "oifname" : "iifname" }} {{ fw4.set(rule.devices_pos) }} {%+ endif -%} -{%+ if (rule.devices_neg): -%} - {{ egress ? "oifname" : "iifname" - }} != {{ fw4.set(rule.devices_neg) }} {%+ endif -%} -{%+ if (rule.subnets_pos): -%} - {{ fw4.ipproto(rule.family) }} {{ egress ? "daddr" : "saddr" }} {{ fw4.set(rule.subnets_pos) }} {%+ endif -%} -{%+ if (rule.subnets_neg): -%} - {{ fw4.ipproto(rule.family) }} {{ egress ? "daddr" : "saddr" }} != {{ fw4.set(rule.subnets_neg) }} {%+ endif -%} +{%+ include("zone-match.uc", { egress, rule }) -%} {%+ if (zone.counter): -%} counter {%+ endif -%} {%+ if (verdict != "accept" && (zone.log & 1)): -%} diff --git a/root/usr/share/ucode/fw4.uc b/root/usr/share/ucode/fw4.uc index ab6fbdf..6973eae 100644 --- a/root/usr/share/ucode/fw4.uc +++ b/root/usr/share/ucode/fw4.uc @@ -220,6 +220,16 @@ function filter_neg(x) { return length(rv) ? rv : null; } +function filter_neg_nonwildcard(x) { + let rv = filter(x, e => e.invert && !e.wildcard); + return length(rv) ? rv : null; +} + +function filter_neg_wildcard(x) { + let rv = filter(x, e => e.invert && e.wildcard); + return length(rv) ? rv : null; +} + function subnets_split_af(x) { let rv = []; @@ -1760,7 +1770,8 @@ return { r.family = family; r.devices_pos = map(filter_pos(devices), d => d.device); - r.devices_neg = map(filter_neg(devices), d => d.device); + r.devices_neg = map(filter_neg_nonwildcard(devices), d => d.device); + r.devices_neg_wildcard = map(filter_neg_wildcard(devices), d => d.device); r.subnets_pos = map(filter_pos(subnets), this.cidr); r.subnets_neg = map(filter_neg(subnets), this.cidr); @@ -1772,18 +1783,74 @@ return { zone.helper, "ct helper" ]); + // group non-inverted device matches into wildcard and non-wildcard ones + let wildcard_devices = [], plain_devices = [], match_all_devices = false; + + for (let device in match_devices) { + let m = match(device.device, /^([^+]*)(\+)?$/); + + if (!m) { + this.warn_section(data, "skipping invalid wildcard pattern '" + device.device + '"'); + continue; + } + + // filter `+` (match any device) since nftables does not support + // wildcard only matches + if (!device.invert && m[0] == '+') { + match_all_devices = true; + continue; + } + + // replace inverted `+` (match no device) with invalid pattern + if (device.invert && m[0] == '+') { + device.device = '/never/'; + device.invert = false; + } + + // replace "name+" matches with "name*" + else if (m[2] == '+') + device.device = m[1] + '*'; + + device.wildcard = !!m[2]; + + if (!device.invert && device.wildcard) + push(wildcard_devices, device); + else + push(plain_devices, device); + } + + // loop wildcard devices + for (let wildcard_device in wildcard_devices) { + // cover this wildcard and any inverted wildcard or non-wildcard interface + let devices = [ wildcard_device, ...(filter_neg(plain_devices) || []) ]; + + // check if there's no AF specific bits, in this case we can do AF agnostic matching + if (!family && length(devices) && !length(match_subnets[0]) && !length(match_subnets[1])) { + add_rule(0, devices, null, zone); + } + + // we need to emit one or two AF specific rules + else { + if (family_is_ipv4(zone) && (length(devices) || length(match_subnets[0]))) + add_rule(4, devices, match_subnets[0], zone); + + if (family_is_ipv6(zone) && (length(devices) || length(match_subnets[1]))) + add_rule(6, devices, match_subnets[1], zone); + } + } + // check if there's no AF specific bits, in this case we can do AF agnostic matching - if (!family && length(match_devices) && !length(match_subnets[0]) && !length(match_subnets[1])) { - add_rule(0, match_devices, null, zone); + if (!family && (match_all_devices || length(plain_devices)) && !length(match_subnets[0]) && !length(match_subnets[1])) { + add_rule(0, plain_devices, null, zone); } // we need to emit one or two AF specific rules else { - if (family_is_ipv4(zone) && (length(match_devices) || length(match_subnets[0]))) - add_rule(4, match_devices, match_subnets[0], zone); + if (family_is_ipv4(zone) && (match_all_devices || length(plain_devices) || length(match_subnets[0]))) + add_rule(4, plain_devices, match_subnets[0], zone); - if (family_is_ipv6(zone) && (length(match_devices) || length(match_subnets[1]))) - add_rule(6, match_devices, match_subnets[1], zone); + if (family_is_ipv6(zone) && (match_all_devices || length(plain_devices) || length(match_subnets[1]))) + add_rule(6, plain_devices, match_subnets[1], zone); } zone.match_rules = match_rules; diff --git a/tests/02_zones/04_wildcard_devices b/tests/02_zones/04_wildcard_devices new file mode 100644 index 0000000..aea5853 --- /dev/null +++ b/tests/02_zones/04_wildcard_devices @@ -0,0 +1,351 @@ +Test that wildcard devices are properly handled. + +-- Testcase -- +{% + include("./root/usr/share/firewall4/main.uc", { + getenv: function(varname) { + switch (varname) { + case 'ACTION': + return 'print'; + } + } + }) +%} +-- End -- + +-- File uci/helpers.json -- +{} +-- End -- + +-- File fs/open~_sys_class_net_never_flags.txt -- +0x0 +-- End -- + +-- File fs/open~_sys_class_net_test_flags.txt -- +0x0 +-- End -- + +-- File fs/open~_sys_class_net_foo_flags.txt -- +0x0 +-- End -- + +-- File fs/open~_sys_class_net_bar_flags.txt -- +0x0 +-- End -- + +-- File fs/open~_sys_class_net_baz_flags.txt -- +0x0 +-- End -- + +-- File fs/open~_sys_class_net_qrx_flags.txt -- +0x0 +-- End -- + +-- File fs/open~_sys_class_net_test1_flags.txt -- +0x1103 +-- End -- + +-- File fs/open~_sys_class_net_test2_flags.txt -- +0x1103 +-- End -- + +-- File uci/firewall.json -- +{ + "zone": [ + { + ".description": "A '+' device match should translate to no ifname match at all", + "name": "test1", + "device": [ "+" ] + }, + { + ".description": "An inverted '+' device match should result in a match that always fails", + "name": "test2", + "device": [ "!+" ] + }, + { + ".description": "A 'name+' device match should translate to an nft wildcard pattern", + "name": "test3", + "device": [ "test+" ] + }, + { + ".description": "Wildcard matches must not be grouped into sets", + "name": "test4", + "device": [ "foo+", "bar+", "test1", "test2" ] + }, + { + ".description": "Multiple inverted wildcard matches may be grouped into one rule", + "name": "test5", + "device": [ "foo+", "bar+", "!baz+", "!qrx+", "test1", "test2", "!test3", "!test4" ] + } + ] +} +-- End -- + +-- Expect stdout -- +table inet fw4 +flush table inet fw4 + +table inet fw4 { + # + # Set definitions + # + + + # + # Defines + # + + define test1_devices = { "+" } + define test2_devices = { "/never/" } + define test3_devices = { "test*" } + define test4_devices = { "foo*", "bar*", "test1", "test2" } + define test5_devices = { "foo*", "bar*", "test1", "test2" } + + # + # User includes + # + + include "/etc/nftables.d/*.nft" + + + # + # Filter rules + # + + chain input { + type filter hook input priority filter; policy drop; + + iifname "lo" accept comment "!fw4: Accept traffic from loopback" + + ct state established,related accept comment "!fw4: Allow inbound established and related flows" + jump input_test1 comment "!fw4: Handle test1 IPv4/IPv6 input traffic" + iifname "/never/" jump input_test2 comment "!fw4: Handle test2 IPv4/IPv6 input traffic" + iifname "test*" jump input_test3 comment "!fw4: Handle test3 IPv4/IPv6 input traffic" + iifname "foo*" jump input_test4 comment "!fw4: Handle test4 IPv4/IPv6 input traffic" + iifname "bar*" jump input_test4 comment "!fw4: Handle test4 IPv4/IPv6 input traffic" + iifname { "test1", "test2" } jump input_test4 comment "!fw4: Handle test4 IPv4/IPv6 input traffic" + iifname "foo*" iifname != { "test3", "test4" } iifname != "baz*" iifname != "qrx*" jump input_test5 comment "!fw4: Handle test5 IPv4/IPv6 input traffic" + iifname "bar*" iifname != { "test3", "test4" } iifname != "baz*" iifname != "qrx*" jump input_test5 comment "!fw4: Handle test5 IPv4/IPv6 input traffic" + iifname { "test1", "test2" } iifname != { "test3", "test4" } iifname != "baz*" iifname != "qrx*" jump input_test5 comment "!fw4: Handle test5 IPv4/IPv6 input traffic" + } + + chain forward { + type filter hook forward priority filter; policy drop; + + ct state established,related accept comment "!fw4: Allow forwarded established and related flows" + jump forward_test1 comment "!fw4: Handle test1 IPv4/IPv6 forward traffic" + iifname "/never/" jump forward_test2 comment "!fw4: Handle test2 IPv4/IPv6 forward traffic" + iifname "test*" jump forward_test3 comment "!fw4: Handle test3 IPv4/IPv6 forward traffic" + iifname "foo*" jump forward_test4 comment "!fw4: Handle test4 IPv4/IPv6 forward traffic" + iifname "bar*" jump forward_test4 comment "!fw4: Handle test4 IPv4/IPv6 forward traffic" + iifname { "test1", "test2" } jump forward_test4 comment "!fw4: Handle test4 IPv4/IPv6 forward traffic" + iifname "foo*" iifname != { "test3", "test4" } iifname != "baz*" iifname != "qrx*" jump forward_test5 comment "!fw4: Handle test5 IPv4/IPv6 forward traffic" + iifname "bar*" iifname != { "test3", "test4" } iifname != "baz*" iifname != "qrx*" jump forward_test5 comment "!fw4: Handle test5 IPv4/IPv6 forward traffic" + iifname { "test1", "test2" } iifname != { "test3", "test4" } iifname != "baz*" iifname != "qrx*" jump forward_test5 comment "!fw4: Handle test5 IPv4/IPv6 forward traffic" + } + + chain output { + type filter hook output priority filter; policy drop; + + oifname "lo" accept comment "!fw4: Accept traffic towards loopback" + + ct state established,related accept comment "!fw4: Allow outbound established and related flows" + jump output_test1 comment "!fw4: Handle test1 IPv4/IPv6 output traffic" + oifname "/never/" jump output_test2 comment "!fw4: Handle test2 IPv4/IPv6 output traffic" + oifname "test*" jump output_test3 comment "!fw4: Handle test3 IPv4/IPv6 output traffic" + oifname "foo*" jump output_test4 comment "!fw4: Handle test4 IPv4/IPv6 output traffic" + oifname "bar*" jump output_test4 comment "!fw4: Handle test4 IPv4/IPv6 output traffic" + oifname { "test1", "test2" } jump output_test4 comment "!fw4: Handle test4 IPv4/IPv6 output traffic" + oifname "foo*" oifname != { "test3", "test4" } oifname != "baz*" oifname != "qrx*" jump output_test5 comment "!fw4: Handle test5 IPv4/IPv6 output traffic" + oifname "bar*" oifname != { "test3", "test4" } oifname != "baz*" oifname != "qrx*" jump output_test5 comment "!fw4: Handle test5 IPv4/IPv6 output traffic" + oifname { "test1", "test2" } oifname != { "test3", "test4" } oifname != "baz*" oifname != "qrx*" jump output_test5 comment "!fw4: Handle test5 IPv4/IPv6 output traffic" + } + + chain handle_reject { + meta l4proto tcp reject with tcp reset comment "!fw4: Reject TCP traffic" + reject with icmpx type port-unreachable comment "!fw4: Reject any other traffic" + } + + chain input_test1 { + jump drop_from_test1 + } + + chain output_test1 { + jump drop_to_test1 + } + + chain forward_test1 { + jump drop_to_test1 + } + + chain drop_from_test1 { + counter drop comment "!fw4: drop test1 IPv4/IPv6 traffic" + } + + chain drop_to_test1 { + counter drop comment "!fw4: drop test1 IPv4/IPv6 traffic" + } + + chain input_test2 { + jump drop_from_test2 + } + + chain output_test2 { + jump drop_to_test2 + } + + chain forward_test2 { + jump drop_to_test2 + } + + chain drop_from_test2 { + iifname "/never/" counter drop comment "!fw4: drop test2 IPv4/IPv6 traffic" + } + + chain drop_to_test2 { + oifname "/never/" counter drop comment "!fw4: drop test2 IPv4/IPv6 traffic" + } + + chain input_test3 { + jump drop_from_test3 + } + + chain output_test3 { + jump drop_to_test3 + } + + chain forward_test3 { + jump drop_to_test3 + } + + chain drop_from_test3 { + iifname "test*" counter drop comment "!fw4: drop test3 IPv4/IPv6 traffic" + } + + chain drop_to_test3 { + oifname "test*" counter drop comment "!fw4: drop test3 IPv4/IPv6 traffic" + } + + chain input_test4 { + jump drop_from_test4 + } + + chain output_test4 { + jump drop_to_test4 + } + + chain forward_test4 { + jump drop_to_test4 + } + + chain drop_from_test4 { + iifname "foo*" counter drop comment "!fw4: drop test4 IPv4/IPv6 traffic" + iifname "bar*" counter drop comment "!fw4: drop test4 IPv4/IPv6 traffic" + iifname { "test1", "test2" } counter drop comment "!fw4: drop test4 IPv4/IPv6 traffic" + } + + chain drop_to_test4 { + oifname "foo*" counter drop comment "!fw4: drop test4 IPv4/IPv6 traffic" + oifname "bar*" counter drop comment "!fw4: drop test4 IPv4/IPv6 traffic" + oifname { "test1", "test2" } counter drop comment "!fw4: drop test4 IPv4/IPv6 traffic" + } + + chain input_test5 { + jump drop_from_test5 + } + + chain output_test5 { + jump drop_to_test5 + } + + chain forward_test5 { + jump drop_to_test5 + } + + chain drop_from_test5 { + iifname "foo*" iifname != { "test3", "test4" } iifname != "baz*" iifname != "qrx*" counter drop comment "!fw4: drop test5 IPv4/IPv6 traffic" + iifname "bar*" iifname != { "test3", "test4" } iifname != "baz*" iifname != "qrx*" counter drop comment "!fw4: drop test5 IPv4/IPv6 traffic" + iifname { "test1", "test2" } iifname != { "test3", "test4" } iifname != "baz*" iifname != "qrx*" counter drop comment "!fw4: drop test5 IPv4/IPv6 traffic" + } + + chain drop_to_test5 { + oifname "foo*" oifname != { "test3", "test4" } oifname != "baz*" oifname != "qrx*" counter drop comment "!fw4: drop test5 IPv4/IPv6 traffic" + oifname "bar*" oifname != { "test3", "test4" } oifname != "baz*" oifname != "qrx*" counter drop comment "!fw4: drop test5 IPv4/IPv6 traffic" + oifname { "test1", "test2" } oifname != { "test3", "test4" } oifname != "baz*" oifname != "qrx*" counter drop comment "!fw4: drop test5 IPv4/IPv6 traffic" + } + + + # + # NAT rules + # + + chain dstnat { + type nat hook prerouting priority dstnat; policy accept; + } + + chain srcnat { + type nat hook postrouting priority srcnat; policy accept; + } + + + # + # Raw rules (notrack & helper) + # + + chain raw_prerouting { + type filter hook prerouting priority raw; policy accept; + iifname "/never/" jump helper_test2 comment "!fw4: test2 IPv4/IPv6 CT helper assignment" + iifname "test*" jump helper_test3 comment "!fw4: test3 IPv4/IPv6 CT helper assignment" + iifname "foo*" jump helper_test4 comment "!fw4: test4 IPv4/IPv6 CT helper assignment" + iifname "bar*" jump helper_test4 comment "!fw4: test4 IPv4/IPv6 CT helper assignment" + iifname { "test1", "test2" } jump helper_test4 comment "!fw4: test4 IPv4/IPv6 CT helper assignment" + iifname "foo*" iifname != { "test3", "test4" } jump helper_test5 comment "!fw4: test5 IPv4/IPv6 CT helper assignment" + iifname "bar*" iifname != { "test3", "test4" } jump helper_test5 comment "!fw4: test5 IPv4/IPv6 CT helper assignment" + iifname { "test1", "test2" } iifname != { "test3", "test4" } jump helper_test5 comment "!fw4: test5 IPv4/IPv6 CT helper assignment" + } + + chain raw_output { + type filter hook output priority raw; policy accept; + } + + chain helper_test1 { + } + + chain helper_test2 { + } + + chain helper_test3 { + } + + chain helper_test4 { + } + + chain helper_test5 { + } + + + # + # Mangle rules + # + + chain mangle_prerouting { + type filter hook prerouting priority mangle; policy accept; + } + + chain mangle_postrouting { + type filter hook postrouting priority mangle; policy accept; + } + + chain mangle_input { + type filter hook input priority mangle; policy accept; + } + + chain mangle_output { + type filter hook output priority mangle; policy accept; + } + + chain mangle_forward { + type filter hook forward priority mangle; policy accept; + } +} +-- End -- -- 2.30.2