[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[libvirt] [PATCH 13/14] Add IPv6 support for ebtables layer



This patch adds IPv6 support for the ebtables layer. Since the parser
etc. are all parameterized, it was fairly easy to add this...

Signed-off-by: Stefan Berger <stefanb us ibm com>

---
 src/conf/nwfilter_conf.c                  |  229 +++++++++++++++++++++++++++++-
 src/conf/nwfilter_conf.h                  |   18 ++
 src/nwfilter/nwfilter_ebiptables_driver.c |  155 ++++++++++++++++++++
 3 files changed, 398 insertions(+), 4 deletions(-)

Index: libvirt-acl/src/conf/nwfilter_conf.c
===================================================================
--- libvirt-acl.orig/src/conf/nwfilter_conf.c
+++ libvirt-acl/src/conf/nwfilter_conf.c
@@ -73,7 +73,8 @@ VIR_ENUM_IMPL(virNWFilterEbtablesTable, 
 VIR_ENUM_IMPL(virNWFilterChainSuffix, VIR_NWFILTER_CHAINSUFFIX_LAST,
               "root",
               "arp",
-              "ipv4");
+              "ipv4",
+              "ipv6");
 
 
 /*
@@ -366,6 +367,9 @@ static const struct int_map macProtoMap[
       .attr = ETHERTYPE_IP,
       .val  = "ipv4",
     }, {
+      .attr = ETHERTYPE_IPV6,
+      .val  = "ipv6",
+    }, {
       .val  = NULL,
     }
 };
@@ -449,6 +453,13 @@ checkIPv4Mask(enum attrDatatype datatype
     return checkValidMask(maskptr, 4);
 }
 
+static bool
+checkIPv6Mask(enum attrDatatype datatype ATTRIBUTE_UNUSED, void *maskptr,
+              virNWFilterRuleDefPtr nwf ATTRIBUTE_UNUSED)
+{
+    return checkValidMask(maskptr, 16);
+}
+
 
 static bool
 checkMACMask(enum attrDatatype datatype ATTRIBUTE_UNUSED,
@@ -765,6 +776,61 @@ static const virXMLAttr2Struct ipAttribu
 };
 
 
+static const virXMLAttr2Struct ipv6Attributes[] = {
+    COMMON_MAC_PROPS(ipv6HdrFilter),
+    {
+        .name = SRCIPADDR,
+        .datatype = DATATYPE_IPV6ADDR,
+        .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.ipHdr.dataSrcIPAddr),
+    },
+    {
+        .name = DSTIPADDR,
+        .datatype = DATATYPE_IPV6ADDR,
+        .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.ipHdr.dataDstIPAddr),
+    },
+    {
+        .name = SRCIPMASK,
+        .datatype = DATATYPE_IPV6MASK,
+        .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.ipHdr.dataSrcIPMask),
+    },
+    {
+        .name = DSTIPMASK,
+        .datatype = DATATYPE_IPV6MASK,
+        .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.ipHdr.dataDstIPMask),
+    },
+    {
+        .name = "protocol",
+        .datatype = DATATYPE_STRING,
+        .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.ipHdr.dataProtocolID),
+        .validator= checkIPProtocolID,
+        .formatter= formatIPProtocolID,
+    },
+    {
+        .name = SRCPORTSTART,
+        .datatype = DATATYPE_UINT16,
+        .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.portData.dataSrcPortStart),
+    },
+    {
+        .name = SRCPORTEND,
+        .datatype = DATATYPE_UINT16,
+        .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.portData.dataSrcPortEnd),
+    },
+    {
+        .name = DSTPORTSTART,
+        .datatype = DATATYPE_UINT16,
+        .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.portData.dataDstPortStart),
+    },
+    {
+        .name = DSTPORTEND,
+        .datatype = DATATYPE_UINT16,
+        .dataIdx = offsetof(virNWFilterRuleDef, p.ipv6HdrFilter.portData.dataDstPortEnd),
+    },
+    {
+        .name = NULL,
+    }
+};
+
+
 typedef struct _virAttributes virAttributes;
 struct _virAttributes {
     const char *id;
@@ -787,6 +853,10 @@ static const virAttributes virAttr[] = {
         .att = ipAttributes,
         .prtclType = VIR_NWFILTER_RULE_PROTOCOL_IP,
     }, {
+        .id = "ipv6",
+        .att = ipv6Attributes,
+        .prtclType = VIR_NWFILTER_RULE_PROTOCOL_IPV6,
+    }, {
         .id = NULL,
     }
 };
@@ -825,6 +895,89 @@ virNWIPv4AddressParser(const char *input
 }
 
 
+static bool
+virNWIPv6AddressParser(const char *input,
+                       nwIPAddressPtr output)
+{
+    int i, j, pos;
+    uint16_t n;
+    int shiftpos = -1;
+    char prevchar;
+    char base;
+
+    memset(output, 0x0, sizeof(*output));
+
+    output->isIPv6 = 1;
+
+    pos = 0;
+    i = 0;
+
+    while (i < 8) {
+        j = 0;
+        n = 0;
+        while (1) {
+            prevchar = input[pos++];
+            if (prevchar == ':' || prevchar == 0) {
+                if (j > 0) {
+                    output->addr.ipv6Addr[i * 2 + 0] = n >> 8;
+                    output->addr.ipv6Addr[i * 2 + 1] = n;
+                    i++;
+                }
+                break;
+            }
+
+            if (j >= 4)
+                return 0;
+
+            if (prevchar >= '0' && prevchar <= '9')
+                base = '0';
+            else if (prevchar >= 'a' && prevchar <= 'f')
+                base = 'a' - 10;
+            else if (prevchar >= 'A' && prevchar <= 'F')
+                base = 'A' - 10;
+            else
+                return 0;
+            n <<= 4;
+            n |= (prevchar - base);
+            j++;
+        }
+
+        if (prevchar == 0)
+            break;
+
+        if (input[pos] == ':') {
+            pos ++;
+            // sequence of zeros
+            if (prevchar != ':')
+                return 0;
+
+            if (shiftpos != -1)
+                return 0;
+
+            shiftpos = i;
+        }
+    }
+
+    if (shiftpos != -1) {
+        if (i >= 7)
+            return 0;
+        i--;
+        j = 0;
+        while (i >= shiftpos) {
+            output->addr.ipv6Addr[15 - (j*2) - 1] =
+                              output->addr.ipv6Addr[i * 2 + 0];
+            output->addr.ipv6Addr[15 - (j*2) - 0] =
+                              output->addr.ipv6Addr[i * 2 + 1];
+            output->addr.ipv6Addr[i * 2 + 0] = 0;
+            output->addr.ipv6Addr[i * 2 + 1] = 0;
+            i--;
+            j++;
+        }
+    }
+    return 1;
+}
+
+
 static int
 virNWFilterRuleDetailsParse(virConnectPtr conn ATTRIBUTE_UNUSED,
                             xmlNodePtr node,
@@ -969,6 +1122,42 @@ virNWFilterRuleDetailsParse(virConnectPt
                             found = 1;
                         break;
 
+                        case DATATYPE_IPV6ADDR:
+                            storage_ptr = &item->u.ipaddr;
+                            if (!virNWIPv6AddressParser(prop,
+                                       (nwIPAddressPtr)storage_ptr)) {
+                                rc = -1;
+                            }
+                            found = 1;
+                        break;
+
+                        case DATATYPE_IPV6MASK:
+                            // parse as dotted IPv4, CIDR mask, then IPv6
+                            storage_ptr = &item->u.u8;
+                            if (!virNWIPv6AddressParser(prop, &ipaddr)) {
+                                if (sscanf(prop, "%d", &int_val) == 1) {
+                                    if (int_val >= 0 && int_val <= 128) {
+                                        if (!validator)
+                                            *(uint8_t *)storage_ptr =
+                                                   (uint8_t)int_val;
+                                        found = 1;
+                                        data_ptr = &int_val;
+                                    } else
+                                        rc = -1;
+                                } else
+                                    rc = -1;
+                            } else {
+                                if (checkIPv6Mask(datatype,
+                                                  ipaddr.addr.ipv6Addr, nwf))
+                                    *(uint8_t *)storage_ptr =
+                                       getMaskNumBits(ipaddr.addr.ipv6Addr,
+                                                 sizeof(ipaddr.addr.ipv6Addr));
+                                else
+                                    rc = -1;
+                                found = 1;
+                            }
+                        break;
+
                         case DATATYPE_STRING:
                             if (!validator) {
                                 // not supported
@@ -1076,6 +1265,13 @@ virNWFilterRuleDefFixup(virNWFilterRuleD
                       rule->p.ipHdrFilter.ipHdr.dataDstIPAddr);
     break;
 
+    case VIR_NWFILTER_RULE_PROTOCOL_IPV6:
+        COPY_NEG_SIGN(rule->p.ipv6HdrFilter.ipHdr.dataSrcIPMask,
+                      rule->p.ipv6HdrFilter.ipHdr.dataSrcIPAddr);
+        COPY_NEG_SIGN(rule->p.ipv6HdrFilter.ipHdr.dataDstIPMask,
+                      rule->p.ipv6HdrFilter.ipHdr.dataDstIPAddr);
+    break;
+
     case VIR_NWFILTER_RULE_PROTOCOL_ARP:
     case VIR_NWFILTER_RULE_PROTOCOL_NONE:
     break;
@@ -1930,7 +2126,34 @@ virNWIPAddressFormat(virBufferPtr buf, n
                           ipaddr->addr.ipv4Addr[2],
                           ipaddr->addr.ipv4Addr[3]);
     } else {
-        virBufferAddLit(buf, "MISSING IPv6 ADDRESS FORMATTER");
+        int i;
+        int dcshown = 0, in_dc = 0;
+        unsigned short n;
+        while (i < 8) {
+            n = (ipaddr->addr.ipv6Addr[i * 2 + 0] << 8) |
+                 ipaddr->addr.ipv6Addr[i * 2 + 1];
+            if (n == 0) {
+                if (!dcshown) {
+                    in_dc = 1;
+                    if (i == 0)
+                        virBufferAddLit(buf, ":");
+                    dcshown = 1;
+                }
+                i++;
+                continue;
+            }
+            if (in_dc) {
+                dcshown = 1;
+                virBufferAddLit(buf, ":");
+                in_dc = 0;
+            }
+            i++;
+            virBufferVSprintf(buf, "%x", n);
+            if (i < 8)
+                virBufferAddLit(buf, ":");
+        }
+        if (in_dc)
+            virBufferAddLit(buf, ":");
     }
 }
 
@@ -1999,6 +2222,7 @@ virNWFilterRuleDefDetailsFormat(virConne
                switch (att[i].datatype) {
 
                case DATATYPE_IPMASK:
+               case DATATYPE_IPV6MASK:
                    // display all masks in CIDR format
                case DATATYPE_UINT8:
                    storage_ptr = &item->u.u8;
@@ -2011,6 +2235,7 @@ virNWFilterRuleDefDetailsFormat(virConne
                break;
 
                case DATATYPE_IPADDR:
+               case DATATYPE_IPV6ADDR:
                    storage_ptr = &item->u.ipaddr;
                    virNWIPAddressFormat(buf,
                                         (nwIPAddressPtr)storage_ptr);
Index: libvirt-acl/src/conf/nwfilter_conf.h
===================================================================
--- libvirt-acl.orig/src/conf/nwfilter_conf.h
+++ libvirt-acl/src/conf/nwfilter_conf.h
@@ -68,8 +68,10 @@ enum attrDatatype {
     DATATYPE_IPADDR           = (1 << 4),
     DATATYPE_IPMASK           = (1 << 5),
     DATATYPE_STRING           = (1 << 6),
+    DATATYPE_IPV6ADDR         = (1 << 7),
+    DATATYPE_IPV6MASK         = (1 << 8),
 
-    DATATYPE_LAST             = (1 << 7),
+    DATATYPE_LAST             = (1 << 9),
 };
 
 
@@ -86,7 +88,7 @@ struct _nwIPAddress {
     int isIPv6;
     union {
         unsigned char ipv4Addr[4];
-        /* unsigned char ipv6Addr[16]; future :-) */
+        unsigned char ipv6Addr[16];
     } addr;
 };
 
@@ -171,6 +173,15 @@ struct _ipHdrFilterDef {
 };
 
 
+typedef struct _ipv6HdrFilterDef  ipv6HdrFilterDef;
+typedef ipv6HdrFilterDef *ipv6HdrFilterDefPtr;
+struct _ipv6HdrFilterDef {
+    ethHdrDataDef  ethHdr;
+    ipHdrDataDef   ipHdr;
+    portDataDef    portData;
+};
+
+
 enum virNWFilterRuleActionType {
     VIR_NWFILTER_RULE_ACTION_DROP = 0,
     VIR_NWFILTER_RULE_ACTION_ACCEPT,
@@ -198,6 +209,7 @@ enum virNWFilterRuleProtocolType {
     VIR_NWFILTER_RULE_PROTOCOL_MAC,
     VIR_NWFILTER_RULE_PROTOCOL_ARP,
     VIR_NWFILTER_RULE_PROTOCOL_IP,
+    VIR_NWFILTER_RULE_PROTOCOL_IPV6,
 };
 
 enum virNWFilterEbtablesTableType {
@@ -223,6 +235,7 @@ struct _virNWFilterRuleDef {
         ethHdrFilterDef  ethHdrFilter;
         arpHdrFilterDef  arpHdrFilter;
         ipHdrFilterDef   ipHdrFilter;
+        ipv6HdrFilterDef ipv6HdrFilter;
     } p;
 
     int nvars;
@@ -249,6 +262,7 @@ enum virNWFilterChainSuffixType {
     VIR_NWFILTER_CHAINSUFFIX_ROOT = 0,
     VIR_NWFILTER_CHAINSUFFIX_ARP,
     VIR_NWFILTER_CHAINSUFFIX_IPv4,
+    VIR_NWFILTER_CHAINSUFFIX_IPv6,
 
     VIR_NWFILTER_CHAINSUFFIX_LAST,
 };
Index: libvirt-acl/src/nwfilter/nwfilter_ebiptables_driver.c
===================================================================
--- libvirt-acl.orig/src/nwfilter/nwfilter_ebiptables_driver.c
+++ libvirt-acl/src/nwfilter/nwfilter_ebiptables_driver.c
@@ -73,6 +73,7 @@
 
 static const char *supported_protocols[] = {
     "ipv4",
+    "ipv6",
     "arp",
     NULL,
 };
@@ -117,6 +118,8 @@ printDataType(virConnectPtr conn,
               nwItemDescPtr item)
 {
     int done;
+    int i, pos, s;
+
     if (printVar(conn, vars, buf, bufsize, item, &done))
         return 1;
 
@@ -136,6 +139,21 @@ printDataType(virConnectPtr conn,
         }
     break;
 
+    case DATATYPE_IPV6ADDR:
+        pos = 0;
+        for (i = 0; i < 16; i++) {
+            s = snprintf(&buf[pos], bufsize - pos, "%x%s",
+                         (unsigned int)item->u.ipaddr.addr.ipv6Addr[i],
+                         ((i & 1) && (i < 15)) ? ":" : "" );
+            if (s >= bufsize - pos) {
+                virNWFilterReportError(conn, VIR_ERR_INVALID_NWFILTER,
+                                       _("Buffer too small for IPv6 address"));
+                return 1;
+            }
+            pos += s;
+        }
+    break;
+
     case DATATYPE_MACADDR:
         if (bufsize < VIR_MAC_STRING_BUFLEN) {
             virNWFilterReportError(conn, VIR_ERR_INVALID_NWFILTER,
@@ -155,6 +173,7 @@ printDataType(virConnectPtr conn,
         }
     break;
 
+    case DATATYPE_IPV6MASK:
     case DATATYPE_IPMASK:
     case DATATYPE_UINT8:
         if (snprintf(buf, bufsize, "%d",
@@ -304,6 +323,7 @@ ebtablesCreateRuleInstance(virConnectPtr
 {
     char macaddr[VIR_MAC_STRING_BUFLEN],
          ipaddr[INET_ADDRSTRLEN],
+         ipv6addr[INET6_ADDRSTRLEN],
          number[20];
     char chain[MAX_CHAINNAME_LENGTH];
     virBuffer buf = VIR_BUFFER_INITIALIZER;
@@ -587,6 +607,135 @@ ebtablesCreateRuleInstance(virConnectPtr
         }
     break;
 
+    case VIR_NWFILTER_RULE_PROTOCOL_IPV6:
+        virBufferVSprintf(&buf,
+                          CMD_DEF_PRE EBTABLES_CMD " -t %s -%%c %s %%s",
+                          EBTABLES_DEFAULT_TABLE, chain);
+
+        if (ebtablesHandleEthHdr(conn,
+                                 &buf,
+                                 vars,
+                                 &rule->p.ipv6HdrFilter.ethHdr))
+            goto err_exit;
+
+        virBufferAddLit(&buf,
+                        " -p ipv6");
+
+        if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.ipHdr.dataSrcIPAddr)) {
+            if (printDataType(conn,
+                              vars,
+                              ipv6addr, sizeof(ipv6addr),
+                              &rule->p.ipv6HdrFilter.ipHdr.dataSrcIPAddr))
+                goto err_exit;
+
+            virBufferVSprintf(&buf,
+                          " --ip6-source %s %s",
+                          ENTRY_GET_NEG_SIGN(&rule->p.ipv6HdrFilter.ipHdr.dataSrcIPAddr),
+                          ipv6addr);
+
+            if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.ipHdr.dataSrcIPMask)) {
+                if (printDataType(conn,
+                                  vars,
+                                  number, sizeof(number),
+                                  &rule->p.ipv6HdrFilter.ipHdr.dataSrcIPMask))
+                    goto err_exit;
+                virBufferVSprintf(&buf,
+                             "/%s",
+                             number);
+            }
+        }
+
+        if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.ipHdr.dataDstIPAddr)) {
+
+            if (printDataType(conn,
+                              vars,
+                              ipv6addr, sizeof(ipv6addr),
+                              &rule->p.ipv6HdrFilter.ipHdr.dataDstIPAddr))
+                goto err_exit;
+
+            virBufferVSprintf(&buf,
+                          " --ip6-destination %s %s",
+                          ENTRY_GET_NEG_SIGN(&rule->p.ipv6HdrFilter.ipHdr.dataDstIPAddr),
+                          ipv6addr);
+
+            if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.ipHdr.dataDstIPMask)) {
+                if (printDataType(conn,
+                                  vars,
+                                  number, sizeof(number),
+                                  &rule->p.ipv6HdrFilter.ipHdr.dataDstIPMask))
+                    goto err_exit;
+                virBufferVSprintf(&buf,
+                                  "/%s",
+                                  number);
+            }
+        }
+
+        if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.ipHdr.dataProtocolID)) {
+            if (printDataType(conn,
+                              vars,
+                              number, sizeof(number),
+                              &rule->p.ipv6HdrFilter.ipHdr.dataProtocolID))
+                goto err_exit;
+
+            virBufferVSprintf(&buf,
+                 " --ip6-protocol %s %s",
+                 ENTRY_GET_NEG_SIGN(&rule->p.ipv6HdrFilter.ipHdr.dataProtocolID),
+                 number);
+        }
+
+        if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.portData.dataSrcPortStart)) {
+
+            if (printDataType(conn,
+                              vars,
+                              number, sizeof(number),
+                              &rule->p.ipv6HdrFilter.portData.dataSrcPortStart))
+                goto err_exit;
+
+            virBufferVSprintf(&buf,
+                          " --ip6-source-port %s %s",
+                          ENTRY_GET_NEG_SIGN(&rule->p.ipv6HdrFilter.portData.dataSrcPortStart),
+                          number);
+
+            if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.portData.dataSrcPortEnd)) {
+                if (printDataType(conn,
+                                  vars,
+                                  number, sizeof(number),
+                                  &rule->p.ipv6HdrFilter.portData.dataSrcPortEnd))
+                    goto err_exit;
+
+                virBufferVSprintf(&buf,
+                                  ":%s",
+                                  number);
+            }
+        }
+
+        if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.portData.dataDstPortStart)) {
+
+            if (printDataType(conn,
+                              vars,
+                              number, sizeof(number),
+                              &rule->p.ipv6HdrFilter.portData.dataDstPortStart))
+                goto err_exit;
+
+            virBufferVSprintf(&buf,
+                          " --ip6-destination-port %s %s",
+                          ENTRY_GET_NEG_SIGN(&rule->p.ipv6HdrFilter.portData.dataDstPortStart),
+                          number);
+
+            if (HAS_ENTRY_ITEM(&rule->p.ipv6HdrFilter.portData.dataDstPortEnd)) {
+                if (printDataType(conn,
+                                vars,
+                                number, sizeof(number),
+                                &rule->p.ipv6HdrFilter.portData.dataDstPortEnd))
+                    goto err_exit;
+
+                virBufferVSprintf(&buf,
+                                  ":%s",
+                                  number);
+            }
+        }
+    break;
+
     case VIR_NWFILTER_RULE_PROTOCOL_NONE:
         virBufferVSprintf(&buf,
                           CMD_DEF_PRE EBTABLES_CMD " -t %s -%%c %s %%s",
@@ -650,6 +799,7 @@ ebiptablesCreateRuleInstance(virConnectP
     case VIR_NWFILTER_RULE_PROTOCOL_MAC:
     case VIR_NWFILTER_RULE_PROTOCOL_ARP:
     case VIR_NWFILTER_RULE_PROTOCOL_NONE:
+    case VIR_NWFILTER_RULE_PROTOCOL_IPV6:
 
         if (rule->tt == VIR_NWFILTER_RULE_DIRECTION_OUT ||
             rule->tt == VIR_NWFILTER_RULE_DIRECTION_INOUT) {
@@ -1230,6 +1380,11 @@ ebiptablesApplyRules(virConnectPtr conn,
     if (chains_out & (1 << VIR_NWFILTER_CHAINSUFFIX_IPv4))
         ebtablesCreateTmpSubChain(conn, &buf, 0, ifname, "ipv4", 1);
 
+    if (chains_in  & (1 << VIR_NWFILTER_CHAINSUFFIX_IPv6))
+        ebtablesCreateTmpSubChain(conn, &buf, 1, ifname, "ipv6", 1);
+    if (chains_out & (1 << VIR_NWFILTER_CHAINSUFFIX_IPv6))
+        ebtablesCreateTmpSubChain(conn, &buf, 0, ifname, "ipv6", 1);
+
     // keep arp as last
     if (chains_in  & (1 << VIR_NWFILTER_CHAINSUFFIX_ARP))
         ebtablesCreateTmpSubChain(conn, &buf, 1, ifname, "arp", 1);

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]