[libvirt] [PATCH v2] nwfilter: Add support for icmpv6 filtering

Stefan Berger stefanb at linux.vnet.ibm.com
Mon Dec 8 18:35:30 UTC 2014


Make use of the ebtables functionality to be able to filter certain
parameters of icmpv6 packets. Extend the XML parser for icmpv6 types,
type ranges, codes, and code ranges. Extend the nwfilter documentation,
schema, and test cases.

Being able to filter icmpv6 types and codes helps extending the DHCP
snooper for IPv6 and filtering at least some parameters of IPv6's NDP
(Neighbor Discovery Protocol) packets. However, the filtering will not
be as good as the filtering of ARP packets since we cannot
check on IP addresses in the payload of the NDP packets.

Signed-off-by: Stefan Berger stefanb at linux.vnet.ibm.com
---
 docs/formatnwfilter.html.in                    | 20 +++++++
 docs/schemas/nwfilter.rng                      | 26 +++++++++
 src/conf/nwfilter_conf.c                       | 26 +++++++++
 src/conf/nwfilter_conf.h                       |  4 ++
 src/nwfilter/nwfilter_ebiptables_driver.c      | 80 ++++++++++++++++++++++++++
 tests/nwfilterxml2firewalldata/ipv6-linux.args | 16 ++++++
 tests/nwfilterxml2firewalldata/ipv6.xml        | 38 ++++++++++++
 tests/nwfilterxml2xmlin/ipv6-test.xml          | 38 ++++++++++++
 tests/nwfilterxml2xmlout/ipv6-test.xml         | 12 ++++
 9 files changed, 260 insertions(+)

diff --git a/docs/formatnwfilter.html.in b/docs/formatnwfilter.html.in
index 073b852..e403e33 100644
--- a/docs/formatnwfilter.html.in
+++ b/docs/formatnwfilter.html.in
@@ -1197,6 +1197,26 @@
          <td>End of range of valid destination ports; requires <code>protocol</code></td>
        </tr>
        <tr>
+         <td>type<span class="since">(Since 1.2.11)</span></td>
+         <td>UINT8</td>
+         <td>ICMPv6 type; requires <code>protocol</code> to be set to <code>icmpv6</code></td>
+       </tr>
+       <tr>
+         <td>typeend<span class="since">(Since 1.2.11)</span></td>
+         <td>UINT8</td>
+         <td>ICMPv6 type end of range; requires <code>protocol</code> to be set to <code>icmpv6</code></td>
+       </tr>
+       <tr>
+         <td>code<span class="since">(Since 1.2.11)</span></td>
+         <td>UINT8</td>
+         <td>ICMPv6 code; requires <code>protocol</code> to be set to <code>icmpv6</code></td>
+       </tr>
+       <tr>
+         <td>code<span class="since">(Since 1.2.11)</span></td>
+         <td>UINT8</td>
+         <td>ICMPv6 code end of range; requires <code>protocol</code> to be set to <code>icmpv6</code></td>
+       </tr>
+       <tr>
          <td>comment <span class="since">(Since 0.8.5)</span></td>
          <td>STRING</td>
          <td>text with max. 256 characters</td>
diff --git a/docs/schemas/nwfilter.rng b/docs/schemas/nwfilter.rng
index 2b54fd5..9df39c0 100644
--- a/docs/schemas/nwfilter.rng
+++ b/docs/schemas/nwfilter.rng
@@ -90,6 +90,7 @@
                   <ref name="common-ipv6-attributes-p1"/>
                   <ref name="common-port-attributes"/>
                   <ref name="ip-attributes"/>
+                  <ref name="icmp-attribute-ranges"/>
                   <ref name="comment-attribute"/>
                 </element>
               </zeroOrMore>
@@ -588,6 +589,31 @@
     </interleave>
   </define>
 
+  <define name="icmp-attribute-ranges">
+    <interleave>
+      <optional>
+        <attribute name="type">
+          <ref name="uint8range"/>
+        </attribute>
+      </optional>
+      <optional>
+        <attribute name="typeend">
+          <ref name="uint8range"/>
+        </attribute>
+      </optional>
+      <optional>
+        <attribute name="code">
+          <ref name="uint8range"/>
+        </attribute>
+      </optional>
+      <optional>
+        <attribute name="codeend">
+          <ref name="uint8range"/>
+        </attribute>
+      </optional>
+    </interleave>
+  </define>
+
   <define name="mac-attributes">
     <interleave>
       <optional>
diff --git a/src/conf/nwfilter_conf.c b/src/conf/nwfilter_conf.c
index 317792e..aed82ad 100644
--- a/src/conf/nwfilter_conf.c
+++ b/src/conf/nwfilter_conf.c
@@ -1445,6 +1445,26 @@ static const virXMLAttr2Struct ipv6Attributes[] = {
         .datatype = DATATYPE_UINT16 | DATATYPE_UINT16_HEX,
         .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.portData.dataDstPortEnd),
     },
+    {
+        .name = "type",
+        .datatype = DATATYPE_UINT8 | DATATYPE_UINT8_HEX,
+        .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.dataICMPTypeStart),
+    },
+    {
+        .name = "typeend",
+        .datatype = DATATYPE_UINT8 | DATATYPE_UINT8_HEX,
+        .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.dataICMPTypeEnd),
+    },
+    {
+        .name = "code",
+        .datatype = DATATYPE_UINT8 | DATATYPE_UINT8_HEX,
+        .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.dataICMPCodeStart),
+    },
+    {
+        .name = "codeend",
+        .datatype = DATATYPE_UINT8 | DATATYPE_UINT8_HEX,
+        .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.dataICMPCodeEnd),
+    },
     COMMENT_PROP_IPHDR(ipv6HdrFilter),
     {
         .name = NULL,
@@ -2219,6 +2239,12 @@ virNWFilterRuleDefFixup(virNWFilterRuleDefPtr rule)
                       rule->p.ipv6HdrFilter.ipHdr.dataSrcIPAddr);
         COPY_NEG_SIGN(rule->p.ipv6HdrFilter.ipHdr.dataDstIPMask,
                       rule->p.ipv6HdrFilter.ipHdr.dataDstIPAddr);
+        COPY_NEG_SIGN(rule->p.ipv6HdrFilter.dataICMPTypeEnd,
+                      rule->p.ipv6HdrFilter.dataICMPTypeStart);
+        COPY_NEG_SIGN(rule->p.ipv6HdrFilter.dataICMPCodeStart,
+                      rule->p.ipv6HdrFilter.dataICMPTypeStart);
+        COPY_NEG_SIGN(rule->p.ipv6HdrFilter.dataICMPCodeEnd,
+                      rule->p.ipv6HdrFilter.dataICMPTypeStart);
         virNWFilterRuleDefFixupIPSet(&rule->p.ipv6HdrFilter.ipHdr);
     break;
 
diff --git a/src/conf/nwfilter_conf.h b/src/conf/nwfilter_conf.h
index f81df60..6e68ecc 100644
--- a/src/conf/nwfilter_conf.h
+++ b/src/conf/nwfilter_conf.h
@@ -265,6 +265,10 @@ struct _ipv6HdrFilterDef {
     ethHdrDataDef  ethHdr;
     ipHdrDataDef   ipHdr;
     portDataDef    portData;
+    nwItemDesc     dataICMPTypeStart;
+    nwItemDesc     dataICMPTypeEnd;
+    nwItemDesc     dataICMPCodeStart;
+    nwItemDesc     dataICMPCodeEnd;
 };
 
 
diff --git a/src/nwfilter/nwfilter_ebiptables_driver.c b/src/nwfilter/nwfilter_ebiptables_driver.c
index 377b59b..423d069 100644
--- a/src/nwfilter/nwfilter_ebiptables_driver.c
+++ b/src/nwfilter/nwfilter_ebiptables_driver.c
@@ -1826,6 +1826,7 @@ ebtablesCreateRuleInstance(virFirewallPtr fw,
     bool hasMask = false;
     virFirewallRulePtr fwrule;
     int ret = -1;
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
 
     if (STREQ(chainSuffix,
               virNWFilterChainSuffixTypeToString(
@@ -2342,6 +2343,83 @@ ebtablesCreateRuleInstance(virFirewallPtr fw,
                 virFirewallRuleAddArg(fw, fwrule, number);
             }
         }
+
+        if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.dataICMPTypeStart)  ||
+            HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.dataICMPTypeEnd) ||
+            HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.dataICMPCodeStart) ||
+            HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.dataICMPCodeEnd)) {
+            bool lo = false;
+            char *r;
+
+            virFirewallRuleAddArg(fw, fwrule,
+                                  "--ip6-icmp-type");
+
+            if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.dataICMPTypeStart)) {
+                if (printDataType(vars,
+                                  number, sizeof(number),
+                                  &rule->p.ipv6HdrFilter.dataICMPTypeStart) < 0)
+                    goto cleanup;
+                lo = true;
+            } else {
+                ignore_value(virStrcpyStatic(number, "0"));
+            }
+
+            virBufferStrcat(&buf, number, ":", NULL);
+
+            if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.dataICMPTypeEnd)) {
+                if (printDataType(vars,
+                                  numberalt, sizeof(numberalt),
+                                  &rule->p.ipv6HdrFilter.dataICMPTypeEnd) < 0)
+                    goto cleanup;
+            } else {
+                if (lo)
+                    ignore_value(virStrcpyStatic(numberalt, number));
+                else
+                    ignore_value(virStrcpyStatic(numberalt, "255"));
+            }
+
+            virBufferStrcat(&buf, numberalt, "/", NULL);
+
+            lo = false;
+
+            if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.dataICMPCodeStart)) {
+                if (printDataType(vars,
+                                  number, sizeof(number),
+                                  &rule->p.ipv6HdrFilter.dataICMPCodeStart) < 0)
+                    goto cleanup;
+                lo = true;
+            } else {
+                ignore_value(virStrcpyStatic(number, "0"));
+            }
+
+            virBufferStrcat(&buf, number, ":", NULL);
+
+            if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.dataICMPCodeEnd)) {
+                if (printDataType(vars,
+                                  numberalt, sizeof(numberalt),
+                                  &rule->p.ipv6HdrFilter.dataICMPCodeEnd) < 0)
+                    goto cleanup;
+            } else {
+                if (lo)
+                    ignore_value(virStrcpyStatic(numberalt, number));
+                else
+                    ignore_value(virStrcpyStatic(numberalt, "255"));
+            }
+
+            virBufferStrcat(&buf, numberalt, NULL);
+
+            if (ENTRY_WANT_NEG_SIGN(&rule->p.ipv6HdrFilter.dataICMPTypeStart))
+                virFirewallRuleAddArg(fw, fwrule, "!");
+
+            if (virBufferCheckError(&buf) < 0)
+                 goto cleanup;
+
+            r = virBufferContentAndReset(&buf);
+
+            virFirewallRuleAddArg(fw, fwrule, r);
+
+            VIR_FREE(r);
+        }
         break;
 
     case VIR_NWFILTER_RULE_PROTOCOL_NONE:
@@ -2376,6 +2454,8 @@ ebtablesCreateRuleInstance(virFirewallPtr fw,
 
     ret = 0;
  cleanup:
+    virBufferFreeAndReset(&buf);
+
     return ret;
 }
 
diff --git a/tests/nwfilterxml2firewalldata/ipv6-linux.args b/tests/nwfilterxml2firewalldata/ipv6-linux.args
index a42566c..735f663 100644
--- a/tests/nwfilterxml2firewalldata/ipv6-linux.args
+++ b/tests/nwfilterxml2firewalldata/ipv6-linux.args
@@ -18,3 +18,19 @@ ebtables -t nat -A libvirt-J-vnet0 -p ipv6 --ip6-destination 1::2/128 \
 --ip6-source a:b:c::/65 --ip6-protocol 18 -j ACCEPT
 ebtables -t nat -A libvirt-P-vnet0 -p ipv6 --ip6-source 1::2/128 \
 --ip6-destination a:b:c::/65 --ip6-protocol 18 -j ACCEPT
+ebtables -t nat -A libvirt-J-vnet0 -p ipv6 --ip6-destination 1::2/128 \
+--ip6-source a:b:c::/65 --ip6-protocol 58 --ip6-icmp-type 1:11/10:11 -j ACCEPT
+ebtables -t nat -A libvirt-P-vnet0 -p ipv6 --ip6-source 1::2/128 \
+--ip6-destination a:b:c::/65 --ip6-protocol 58 --ip6-icmp-type 1:11/10:11 -j ACCEPT
+ebtables -t nat -A libvirt-J-vnet0 -p ipv6 --ip6-destination 1::2/128 \
+--ip6-source a:b:c::/65 --ip6-protocol 58 --ip6-icmp-type 1:1/10:10 -j ACCEPT
+ebtables -t nat -A libvirt-P-vnet0 -p ipv6 --ip6-source 1::2/128 \
+--ip6-destination a:b:c::/65 --ip6-protocol 58 --ip6-icmp-type 1:1/10:10 -j ACCEPT
+ebtables -t nat -A libvirt-J-vnet0 -p ipv6 --ip6-destination 1::2/128 \
+--ip6-source a:b:c::/65 --ip6-protocol 58 --ip6-icmp-type 0:255/10:10 -j ACCEPT
+ebtables -t nat -A libvirt-P-vnet0 -p ipv6 --ip6-source 1::2/128 \
+--ip6-destination a:b:c::/65 --ip6-protocol 58 --ip6-icmp-type 0:255/10:10 -j ACCEPT
+ebtables -t nat -A libvirt-J-vnet0 -p ipv6 --ip6-destination 1::2/128 \
+--ip6-source a:b:c::/65 --ip6-protocol 58 --ip6-icmp-type 1:1/0:255 -j ACCEPT
+ebtables -t nat -A libvirt-P-vnet0 -p ipv6 --ip6-source 1::2/128 \
+--ip6-destination a:b:c::/65 --ip6-protocol 58 --ip6-icmp-type 1:1/0:255 -j ACCEPT
diff --git a/tests/nwfilterxml2firewalldata/ipv6.xml b/tests/nwfilterxml2firewalldata/ipv6.xml
index 9f67bea..2400958 100644
--- a/tests/nwfilterxml2firewalldata/ipv6.xml
+++ b/tests/nwfilterxml2firewalldata/ipv6.xml
@@ -40,4 +40,42 @@
       />
   </rule>
 
+  <rule action='accept' direction='inout'>
+     <ipv6 srcipaddr='1::2' srcipmask='128'
+           dstipaddr='a:b:c::'
+           dstipmask='ffff:ffff:ffff:ffff:8000::'
+           protocol='icmpv6'
+           type='1' typeend='11'
+           code='10' codeend='11'
+      />
+  </rule>
+
+  <rule action='accept' direction='inout'>
+     <ipv6 srcipaddr='1::2' srcipmask='128'
+           dstipaddr='a:b:c::'
+           dstipmask='ffff:ffff:ffff:ffff:8000::'
+           protocol='icmpv6'
+           type='1'
+           code='10'
+      />
+  </rule>
+
+  <rule action='accept' direction='inout'>
+     <ipv6 srcipaddr='1::2' srcipmask='128'
+           dstipaddr='a:b:c::'
+           dstipmask='ffff:ffff:ffff:ffff:8000::'
+           protocol='icmpv6'
+           code='10'
+      />
+  </rule>
+
+  <rule action='accept' direction='inout'>
+     <ipv6 srcipaddr='1::2' srcipmask='128'
+           dstipaddr='a:b:c::'
+           dstipmask='ffff:ffff:ffff:ffff:8000::'
+           protocol='icmpv6'
+           type='1'
+      />
+  </rule>
+
 </filter>
diff --git a/tests/nwfilterxml2xmlin/ipv6-test.xml b/tests/nwfilterxml2xmlin/ipv6-test.xml
index 556796f..2daa3b9 100644
--- a/tests/nwfilterxml2xmlin/ipv6-test.xml
+++ b/tests/nwfilterxml2xmlin/ipv6-test.xml
@@ -40,4 +40,42 @@
       />
   </rule>
 
+  <rule action='accept' direction='inout'>
+     <ipv6 srcipaddr='1::2' srcipmask='128'
+           dstipaddr='a:b:c::'
+           dstipmask='ffff:ffff:ffff:ffff:8000::'
+           protocol='icmpv6'
+           type='1' typeend='11'
+           code='10' codeend='11'
+      />
+  </rule>
+
+  <rule action='accept' direction='inout'>
+     <ipv6 srcipaddr='1::2' srcipmask='128'
+           dstipaddr='a:b:c::'
+           dstipmask='ffff:ffff:ffff:ffff:8000::'
+           protocol='icmpv6'
+           type='1'
+           code='10'
+      />
+  </rule>
+
+  <rule action='accept' direction='inout'>
+     <ipv6 srcipaddr='1::2' srcipmask='128'
+           dstipaddr='a:b:c::'
+           dstipmask='ffff:ffff:ffff:ffff:8000::'
+           protocol='icmpv6'
+           code='10'
+      />
+  </rule>
+
+  <rule action='accept' direction='inout'>
+     <ipv6 srcipaddr='1::2' srcipmask='128'
+           dstipaddr='a:b:c::'
+           dstipmask='ffff:ffff:ffff:ffff:8000::'
+           protocol='icmpv6'
+           type='1'
+      />
+  </rule>
+
 </filter>
diff --git a/tests/nwfilterxml2xmlout/ipv6-test.xml b/tests/nwfilterxml2xmlout/ipv6-test.xml
index fcc5c0d..ce9dd06 100644
--- a/tests/nwfilterxml2xmlout/ipv6-test.xml
+++ b/tests/nwfilterxml2xmlout/ipv6-test.xml
@@ -12,4 +12,16 @@
   <rule action='accept' direction='inout' priority='500'>
     <ipv6 srcipaddr='1::2' srcipmask='128' dstipaddr='a:b:c::' dstipmask='65' protocol='18'/>
   </rule>
+  <rule action='accept' direction='inout'>
+     <ipv6 srcipaddr='1::2' srcipmask='128' dstipaddr='a:b:c::' dstipmask='65' protocol='icmpv6' type='1' typeend='11' code='10' codeend='11'/>
+  </rule>
+  <rule action='accept' direction='inout'>
+     <ipv6 srcipaddr='1::2' srcipmask='128' dstipaddr='a:b:c::' dstipmask='65' protocol='icmpv6' type='1' code='10'/>
+  </rule>
+  <rule action='accept' direction='inout'>
+     <ipv6 srcipaddr='1::2' srcipmask='128' dstipaddr='a:b:c::' dstipmask='65' protocol='icmpv6' code='10'/>
+  </rule>
+  <rule action='accept' direction='inout'>
+     <ipv6 srcipaddr='1::2' srcipmask='128' dstipaddr='a:b:c::' dstipmask='65' protocol='icmpv6' type='1'/>
+  </rule>
 </filter>
-- 
1.9.3




More information about the libvir-list mailing list