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

[libvirt] [PATCH v4 13/15] Add IPv6 support for the 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                  |  230 +++++++++++++++++++++++++++++-
 src/conf/nwfilter_conf.h                  |   18 ++
 src/nwfilter/nwfilter_ebiptables_driver.c |  155 ++++++++++++++++++++
 3 files changed, 399 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,41 @@ 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:
+                            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 +1264,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;
@@ -1952,7 +2147,36 @@ 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;
+                }
+                if (in_dc) {
+                    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, ":");
     }
 }
 
@@ -2021,6 +2245,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;
@@ -2033,6 +2258,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 @@ ebiptablesApplyNewRules(virConnectPtr co
     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]