nicira-ext: Support matching IPv6 traffic.
authorJustin Pettit <jpettit@nicira.com>
Thu, 30 Dec 2010 03:03:46 +0000 (19:03 -0800)
committerJustin Pettit <jpettit@nicira.com>
Wed, 2 Feb 2011 20:53:26 +0000 (12:53 -0800)
Provides ability to match over IPv6 traffic in the same manner as IPv4.
Currently, the matching fields include:

    - IPv6 source and destination addresses (ipv6_src and ipv6_dst)
    - Traffic Class (nw_tos)
    - Next Header (nw_proto)
    - ICMPv6 Type and Code (icmp_type and icmp_code)
    - TCP and UDP Ports over IPv6 (tp_src and tp_dst)

When defining IPv6 rules, the Nicira Extensible Match (NXM) extension to
OVS must be used.

Signed-off-by: Justin Pettit <jpettit@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
27 files changed:
DESIGN [new file with mode: 0644]
Makefile.am
acinclude.m4
datapath/flow.c
datapath/flow.h
datapath/linux-2.6/Modules.mk
datapath/linux-2.6/compat-2.6/include/linux/icmpv6.h [new file with mode: 0644]
include/openflow/nicira-ext.h
include/openvswitch/datapath-protocol.h
lib/classifier.c
lib/classifier.h
lib/flow.c
lib/flow.h
lib/nx-match.c
lib/nx-match.def
lib/nx-match.h
lib/odp-util.c
lib/odp-util.h
lib/ofp-parse.c
lib/ofp-util.c
lib/packets.c
lib/packets.h
lib/socket-util.c
lib/socket-util.h
tests/ovs-ofctl.at
tests/test-packets.c
utilities/ovs-ofctl.8.in

diff --git a/DESIGN b/DESIGN
new file mode 100644 (file)
index 0000000..56e2605
--- /dev/null
+++ b/DESIGN
@@ -0,0 +1,76 @@
+                     Design Decisions In Open vSwitch
+                     ================================
+
+This document describes design decisions that went into implementing
+Open vSwitch.  While we believe these to be reasonable decisions, it is
+impossible to predict how Open vSwitch will be used in all environments.
+Understanding assumptions made by Open vSwitch is critical to a
+successful deployment.  The end of this document contains contact
+information that can be used to let us know how we can make Open vSwitch
+more generally useful.
+
+
+IPv6
+====
+
+Open vSwitch supports stateless handling of IPv6 packets.  Flows can be
+written to support matching TCP, UDP, and ICMPv6 headers within an IPv6
+packet.
+
+IPv6 was not designed to interact well with middle-boxes.  This,
+combined with Open vSwitch's stateless nature, have affected the
+processing of IPv6 traffic, which is detailed below.
+
+Extension Headers
+-----------------
+
+The base IPv6 header is incredibly simple with the intention of only
+containing information relevant for routing packets between two
+endpoints.  IPv6 relies heavily on the use of extension headers to
+provide any other functionality.  Unfortunately, the extension headers
+were designed in such a way that it is impossible to move to the next
+header (including the layer-4 payload) unless the current header is
+understood.
+
+Open vSwitch will process the following extension headers and continue
+to the next header:
+
+    * Fragment (see the next section)
+    * AH (Authentication Header)
+    * Hop-by-Hop Options
+    * Routing
+    * Destination Options
+
+When a header is encountered that is not in that list, it is considered
+"terminal".  A terminal header's IPv6 protocol value is stored in
+"nw_proto" for matching purposes.  If a terminal header is TCP, UDP, or
+ICMPv6, the packet will be further processed in an attempt to extract
+layer-4 information.
+
+Fragments
+---------
+
+IPv6 requires that every link in the internet have an MTU of 1280 octets
+or greater (RFC 2460).  As such, a terminal header (as described above in
+"Extension Headers") in the first fragment should generally be
+reachable.  In this case, the terminal header's IPv6 protocol type is
+stored in the "nw_proto" field for matching purposes.  If a terminal
+header cannot be found in the first fragment (one with a fragment offset
+of zero), the "nw_proto" field is set to 0.  Subsequent fragments (those
+with a non-zero fragment offset) have the "nw_proto" field set to the
+IPv6 protocol type for fragments (44).
+
+Jumbograms
+----------
+
+An IPv6 jumbogram (RFC 2675) is a packet containing a payload longer
+than 65,535 octets.  A jumbogram is only relevant in subnets with a link
+MTU greater than 65,575 octets, and are not required to be supported on
+nodes that do not connect to link with such large MTUs.  Currently, Open
+vSwitch doesn't process jumbograms.
+
+
+Suggestions
+===========
+
+Suggestions to improve Open vSwitch are welcome at discuss@openvswitch.org.
index deae5127d747f5f1dffa17a379692eaac8b1bcb9..71a0652c976e0ae9a22cc699c390d17a103685e1 100644 (file)
@@ -31,6 +31,7 @@ CLEAN_LOCAL =
 DISTCLEANFILES =
 EXTRA_DIST = \
        CodingStyle \
+       DESIGN \
        INSTALL.KVM \
        INSTALL.Linux \
        INSTALL.OpenFlow \
index 6fc1c7a1ee7c396b9bdd445039a2d0aeae91f4f0..0cd14272d72057a797b3d4f3b68528283f32456c 100644 (file)
@@ -184,6 +184,8 @@ AC_DEFUN([OVS_CHECK_LINUX26_COMPAT], [
   OVS_GREP_IFELSE([$KSRC26/include/linux/skbuff.h], [skb_cow_head])
   OVS_GREP_IFELSE([$KSRC26/include/linux/skbuff.h], [skb_transport_header],
                   [OVS_DEFINE([HAVE_SKBUFF_HEADER_HELPERS])])
+  OVS_GREP_IFELSE([$KSRC26/include/linux/icmpv6.h], [icmp6_hdr],
+                  [OVS_DEFINE([HAVE_ICMP6_HDR])])
   OVS_GREP_IFELSE([$KSRC26/include/linux/skbuff.h], [skb_warn_if_lro],
                   [OVS_DEFINE([HAVE_SKB_WARN_LRO])])
 
index d83c17d9a49ac2c9ac8b7611dd6aab195a7323ce..4365e22c3549c38136da373389157b4afe974811 100644 (file)
 #include <linux/if_arp.h>
 #include <linux/if_ether.h>
 #include <linux/ip.h>
+#include <linux/ipv6.h>
 #include <linux/tcp.h>
 #include <linux/udp.h>
 #include <linux/icmp.h>
+#include <linux/icmpv6.h>
 #include <net/inet_ecn.h>
 #include <net/ip.h>
+#include <net/ipv6.h>
 
 static struct kmem_cache *flow_cache;
 static unsigned int hash_seed __read_mostly;
@@ -95,6 +98,63 @@ u64 flow_used_time(unsigned long flow_jiffies)
        return cur_ms - idle_ms;
 }
 
+static int parse_ipv6hdr(struct sk_buff *skb, struct sw_flow_key *key)
+{
+       unsigned int nh_ofs = skb_network_offset(skb);
+       unsigned int nh_len;
+       int payload_ofs;
+       int payload_len;
+       struct ipv6hdr *nh;
+       uint8_t nexthdr;
+
+       if (unlikely(skb->len < nh_ofs + sizeof(*nh)))
+               return -EINVAL;
+
+       nh = ipv6_hdr(skb);
+       nexthdr = nh->nexthdr;
+       payload_ofs = (u8 *)(nh + 1) - skb->data;
+       payload_len = ntohs(nh->payload_len);
+
+       memcpy(key->ipv6_src, nh->saddr.in6_u.u6_addr8, sizeof(key->ipv6_src));
+       memcpy(key->ipv6_dst, nh->daddr.in6_u.u6_addr8, sizeof(key->ipv6_dst));
+       key->nw_tos = ipv6_get_dsfield(nh) & ~INET_ECN_MASK;
+       key->nw_proto = NEXTHDR_NONE;
+
+       /* We don't process jumbograms. */
+       if (!payload_len)
+               return -EINVAL;
+
+       if (unlikely(skb->len < nh_ofs + sizeof(*nh) + payload_len))
+               return -EINVAL;
+
+       payload_ofs = ipv6_skip_exthdr(skb, payload_ofs, &nexthdr);
+       if (payload_ofs < 0) {
+               return -EINVAL;
+       }
+       nh_len = payload_ofs - nh_ofs;
+
+       /* Ensure that the payload length claimed is at least large enough
+        * for the headers we've already processed. */
+       if (payload_len < nh_len - sizeof(*nh))
+               return -EINVAL;
+
+       /* Pull enough header bytes to account for the IP header plus the
+        * longest transport header that we parse, currently 20 bytes for TCP.
+        * To dig deeper than the transport header, transport parsers may need
+        * to pull more header bytes.
+        */
+       if (unlikely(!pskb_may_pull(skb, min(nh_ofs + nh_len + 20, skb->len))))
+               return -ENOMEM;
+
+       skb_set_transport_header(skb, nh_ofs + nh_len);
+       key->nw_proto = nexthdr;
+       return nh_len;
+}
+
+static bool icmp6hdr_ok(struct sk_buff *skb)
+{
+       return skb->len >= skb_transport_offset(skb) + sizeof(struct icmp6hdr);
+}
 
 #define TCP_FLAGS_OFFSET 13
 #define TCP_FLAG_MASK 0x3f
@@ -274,10 +334,10 @@ static __be16 parse_ethertype(struct sk_buff *skb)
  *    - skb->network_header: just past the Ethernet header, or just past the
  *      VLAN header, to the first byte of the Ethernet payload.
  *
- *    - skb->transport_header: If key->dl_type is ETH_P_IP on output, then just
- *      past the IPv4 header, if one is present and of a correct length,
- *      otherwise the same as skb->network_header.  For other key->dl_type
- *      values it is left untouched.
+ *    - skb->transport_header: If key->dl_type is ETH_P_IP or ETH_P_IPV6
+ *      on output, then just past the IP header, if one is present and
+ *      of a correct length, otherwise the same as skb->network_header.
+ *      For other key->dl_type values it is left untouched.
  */
 int flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key,
                 bool *is_frag)
@@ -291,7 +351,8 @@ int flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key,
 
        /*
         * We would really like to pull as many bytes as we could possibly
-        * want to parse into the linear data area.  Currently that is:
+        * want to parse into the linear data area.  Currently, for IPv4,
+        * that is:
         *
         *    14     Ethernet header
         *     4     VLAN header
@@ -339,8 +400,8 @@ int flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key,
                }
 
                nh = ip_hdr(skb);
-               key->nw_src = nh->saddr;
-               key->nw_dst = nh->daddr;
+               key->ipv4_src = nh->saddr;
+               key->ipv4_dst = nh->daddr;
                key->nw_tos = nh->tos & ~INET_ECN_MASK;
                key->nw_proto = nh->protocol;
 
@@ -388,12 +449,47 @@ int flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key,
 
                        if (key->nw_proto == ARPOP_REQUEST
                                        || key->nw_proto == ARPOP_REPLY) {
-                               memcpy(&key->nw_src, arp->ar_sip, sizeof(key->nw_src));
-                               memcpy(&key->nw_dst, arp->ar_tip, sizeof(key->nw_dst));
+                               memcpy(&key->ipv4_src, arp->ar_sip, sizeof(key->ipv4_src));
+                               memcpy(&key->ipv4_dst, arp->ar_tip, sizeof(key->ipv4_dst));
                                memcpy(key->arp_sha, arp->ar_sha, ETH_ALEN);
                                memcpy(key->arp_tha, arp->ar_tha, ETH_ALEN);
                        }
                }
+       } else if (key->dl_type == htons(ETH_P_IPV6)) {
+               int nh_len;             /* IPv6 Header + Extensions */
+
+               nh_len = parse_ipv6hdr(skb, key);
+               if (unlikely(nh_len < 0)) {
+                       if (nh_len == -EINVAL) {
+                               skb->transport_header = skb->network_header;
+                               return 0;
+                       }
+                       return nh_len;
+               }
+
+               /* Transport layer. */
+               if (key->nw_proto == NEXTHDR_TCP) {
+                       if (tcphdr_ok(skb)) {
+                               struct tcphdr *tcp = tcp_hdr(skb);
+                               key->tp_src = tcp->source;
+                               key->tp_dst = tcp->dest;
+                       }
+               } else if (key->nw_proto == NEXTHDR_UDP) {
+                       if (udphdr_ok(skb)) {
+                               struct udphdr *udp = udp_hdr(skb);
+                               key->tp_src = udp->source;
+                               key->tp_dst = udp->dest;
+                       }
+               } else if (key->nw_proto == NEXTHDR_ICMP) {
+                       if (icmp6hdr_ok(skb)) {
+                               struct icmp6hdr *icmp = icmp6_hdr(skb);
+                               /* The ICMPv6 type and code fields use the 16-bit
+                                * transport port fields, so we need to store them
+                                * in 16-bit network byte order. */
+                               key->tp_src = htons(icmp->icmp6_type);
+                               key->tp_dst = htons(icmp->icmp6_code);
+                       }
+               }
        }
        return 0;
 }
@@ -420,7 +516,8 @@ int flow_cmp(const struct tbl_node *node, void *key2_)
  * This state machine accepts the following forms, with [] for optional
  * elements and | for alternatives:
  *
- * [tun_id] in_port ethernet [8021q] [ethertype [IP [TCP|UDP|ICMP] | ARP]
+ * [tun_id] in_port ethernet [8021q] [ethertype \
+ *              [IPv4 [TCP|UDP|ICMP] | IPv6 [TCP|UDP|ICMPv6] | ARP]]
  */
 int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr)
 {
@@ -440,18 +537,22 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr)
                        [ODP_KEY_ATTR_8021Q] = sizeof(struct odp_key_8021q),
                        [ODP_KEY_ATTR_ETHERTYPE] = 2,
                        [ODP_KEY_ATTR_IPV4] = sizeof(struct odp_key_ipv4),
+                       [ODP_KEY_ATTR_IPV6] = sizeof(struct odp_key_ipv6),
                        [ODP_KEY_ATTR_TCP] = sizeof(struct odp_key_tcp),
                        [ODP_KEY_ATTR_UDP] = sizeof(struct odp_key_udp),
                        [ODP_KEY_ATTR_ICMP] = sizeof(struct odp_key_icmp),
+                       [ODP_KEY_ATTR_ICMPV6] = sizeof(struct odp_key_icmpv6),
                        [ODP_KEY_ATTR_ARP] = sizeof(struct odp_key_arp),
                };
 
                const struct odp_key_ethernet *eth_key;
                const struct odp_key_8021q *q_key;
                const struct odp_key_ipv4 *ipv4_key;
+               const struct odp_key_ipv6 *ipv6_key;
                const struct odp_key_tcp *tcp_key;
                const struct odp_key_udp *udp_key;
                const struct odp_key_icmp *icmp_key;
+               const struct odp_key_icmpv6 *icmpv6_key;
                const struct odp_key_arp *arp_key;
 
                 int type = nla_type(nla);
@@ -499,15 +600,30 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr)
                        if (swkey->dl_type != htons(ETH_P_IP))
                                return -EINVAL;
                        ipv4_key = nla_data(nla);
-                       swkey->nw_src = ipv4_key->ipv4_src;
-                       swkey->nw_dst = ipv4_key->ipv4_dst;
+                       swkey->ipv4_src = ipv4_key->ipv4_src;
+                       swkey->ipv4_dst = ipv4_key->ipv4_dst;
                        swkey->nw_proto = ipv4_key->ipv4_proto;
                        swkey->nw_tos = ipv4_key->ipv4_tos;
                        if (swkey->nw_tos & INET_ECN_MASK)
                                return -EINVAL;
                        break;
 
+               case TRANSITION(ODP_KEY_ATTR_ETHERTYPE, ODP_KEY_ATTR_IPV6):
+                       if (swkey->dl_type != htons(ETH_P_IPV6))
+                               return -EINVAL;
+                       ipv6_key = nla_data(nla);
+                       memcpy(swkey->ipv6_src, ipv6_key->ipv6_src,
+                                       sizeof(swkey->ipv6_src));
+                       memcpy(swkey->ipv6_dst, ipv6_key->ipv6_dst,
+                                       sizeof(swkey->ipv6_dst));
+                       swkey->nw_proto = ipv6_key->ipv6_proto;
+                       swkey->nw_tos = ipv6_key->ipv6_tos;
+                       if (swkey->nw_tos & INET_ECN_MASK)
+                               return -EINVAL;
+                       break;
+
                case TRANSITION(ODP_KEY_ATTR_IPV4, ODP_KEY_ATTR_TCP):
+               case TRANSITION(ODP_KEY_ATTR_IPV6, ODP_KEY_ATTR_TCP):
                        if (swkey->nw_proto != IPPROTO_TCP)
                                return -EINVAL;
                        tcp_key = nla_data(nla);
@@ -516,6 +632,7 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr)
                        break;
 
                case TRANSITION(ODP_KEY_ATTR_IPV4, ODP_KEY_ATTR_UDP):
+               case TRANSITION(ODP_KEY_ATTR_IPV6, ODP_KEY_ATTR_UDP):
                        if (swkey->nw_proto != IPPROTO_UDP)
                                return -EINVAL;
                        udp_key = nla_data(nla);
@@ -531,12 +648,20 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr)
                        swkey->tp_dst = htons(icmp_key->icmp_code);
                        break;
 
+               case TRANSITION(ODP_KEY_ATTR_IPV6, ODP_KEY_ATTR_ICMPV6):
+                       if (swkey->nw_proto != IPPROTO_ICMPV6)
+                               return -EINVAL;
+                       icmpv6_key = nla_data(nla);
+                       swkey->tp_src = htons(icmpv6_key->icmpv6_type);
+                       swkey->tp_dst = htons(icmpv6_key->icmpv6_code);
+                       break;
+
                case TRANSITION(ODP_KEY_ATTR_ETHERTYPE, ODP_KEY_ATTR_ARP):
                        if (swkey->dl_type != htons(ETH_P_ARP))
                                return -EINVAL;
                        arp_key = nla_data(nla);
-                       swkey->nw_src = arp_key->arp_sip;
-                       swkey->nw_dst = arp_key->arp_tip;
+                       swkey->ipv4_src = arp_key->arp_sip;
+                       swkey->ipv4_dst = arp_key->arp_tip;
                        if (arp_key->arp_op & htons(0xff00))
                                return -EINVAL;
                        swkey->nw_proto = ntohs(arp_key->arp_op);
@@ -578,9 +703,17 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr)
                        return -EINVAL;
                return 0;
 
+       case ODP_KEY_ATTR_IPV6:
+               if (swkey->nw_proto == IPPROTO_TCP ||
+                   swkey->nw_proto == IPPROTO_UDP ||
+                   swkey->nw_proto == IPPROTO_ICMPV6)
+                       return -EINVAL;
+               return 0;
+
        case ODP_KEY_ATTR_TCP:
        case ODP_KEY_ATTR_UDP:
        case ODP_KEY_ATTR_ICMP:
+       case ODP_KEY_ATTR_ICMPV6:
        case ODP_KEY_ATTR_ARP:
                return 0;
        }
@@ -626,10 +759,39 @@ int flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb)
                if (!nla)
                        goto nla_put_failure;
                ipv4_key = nla_data(nla);
-               ipv4_key->ipv4_src = swkey->nw_src;
-               ipv4_key->ipv4_dst = swkey->nw_dst;
+               ipv4_key->ipv4_src = swkey->ipv4_src;
+               ipv4_key->ipv4_dst = swkey->ipv4_dst;
                ipv4_key->ipv4_proto = swkey->nw_proto;
                ipv4_key->ipv4_tos = swkey->nw_tos;
+       } else if (swkey->dl_type == htons(ETH_P_IPV6)) {
+               struct odp_key_ipv6 *ipv6_key;
+
+               nla = nla_reserve(skb, ODP_KEY_ATTR_IPV6, sizeof(*ipv6_key));
+               if (!nla)
+                       goto nla_put_failure;
+               ipv6_key = nla_data(nla);
+               memcpy(ipv6_key->ipv6_src, swkey->ipv6_src,
+                               sizeof(ipv6_key->ipv6_src));
+               memcpy(ipv6_key->ipv6_dst, swkey->ipv6_dst,
+                               sizeof(ipv6_key->ipv6_dst));
+               ipv6_key->ipv6_proto = swkey->nw_proto;
+               ipv6_key->ipv6_tos = swkey->nw_tos;
+       } else if (swkey->dl_type == htons(ETH_P_ARP)) {
+               struct odp_key_arp *arp_key;
+
+               nla = nla_reserve(skb, ODP_KEY_ATTR_ARP, sizeof(*arp_key));
+               if (!nla)
+                       goto nla_put_failure;
+               arp_key = nla_data(nla);
+               arp_key->arp_sip = swkey->ipv4_src;
+               arp_key->arp_tip = swkey->ipv4_dst;
+               arp_key->arp_op = htons(swkey->nw_proto);
+               memcpy(arp_key->arp_sha, swkey->arp_sha, ETH_ALEN);
+               memcpy(arp_key->arp_tha, swkey->arp_tha, ETH_ALEN);
+       }
+
+       if (swkey->dl_type == htons(ETH_P_IP)
+                       || swkey->dl_type == htons(ETH_P_IPV6)) {
 
                if (swkey->nw_proto == IPPROTO_TCP) {
                        struct odp_key_tcp *tcp_key;
@@ -649,7 +811,8 @@ int flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb)
                        udp_key = nla_data(nla);
                        udp_key->udp_src = swkey->tp_src;
                        udp_key->udp_dst = swkey->tp_dst;
-               } else if (swkey->nw_proto == IPPROTO_ICMP) {
+               } else if (swkey->dl_type == htons(ETH_P_IP)
+                               && swkey->nw_proto == IPPROTO_ICMP) {
                        struct odp_key_icmp *icmp_key;
 
                        nla = nla_reserve(skb, ODP_KEY_ATTR_ICMP, sizeof(*icmp_key));
@@ -658,19 +821,17 @@ int flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb)
                        icmp_key = nla_data(nla);
                        icmp_key->icmp_type = ntohs(swkey->tp_src);
                        icmp_key->icmp_code = ntohs(swkey->tp_dst);
-               }
-       } else if (swkey->dl_type == htons(ETH_P_ARP)) {
-               struct odp_key_arp *arp_key;
+               } else if (swkey->dl_type == htons(ETH_P_IPV6)
+                               && swkey->nw_proto == IPPROTO_ICMPV6) {
+                       struct odp_key_icmpv6 *icmpv6_key;
 
-               nla = nla_reserve(skb, ODP_KEY_ATTR_ARP, sizeof(*arp_key));
-               if (!nla)
-                       goto nla_put_failure;
-               arp_key = nla_data(nla);
-               arp_key->arp_sip = swkey->nw_src;
-               arp_key->arp_tip = swkey->nw_dst;
-               arp_key->arp_op = htons(swkey->nw_proto);
-               memcpy(arp_key->arp_sha, swkey->arp_sha, ETH_ALEN);
-               memcpy(arp_key->arp_tha, swkey->arp_tha, ETH_ALEN);
+                       nla = nla_reserve(skb, ODP_KEY_ATTR_ICMPV6, sizeof(*icmpv6_key));
+                       if (!nla)
+                               goto nla_put_failure;
+                       icmpv6_key = nla_data(nla);
+                       icmpv6_key->icmpv6_type = ntohs(swkey->tp_src);
+                       icmpv6_key->icmpv6_code = ntohs(swkey->tp_dst);
+               }
        }
 
        return 0;
index b9af27224f948b9b954ea44afa29be7ab151a469..ee1c4c92a7b1d36811917df2b0952d978ec250f3 100644 (file)
@@ -31,8 +31,16 @@ struct sw_flow_actions {
 
 struct sw_flow_key {
        __be64  tun_id;     /* Encapsulating tunnel ID. */
-       __be32  nw_src;     /* IP source address. */
-       __be32  nw_dst;     /* IP destination address. */
+       union {
+               struct {
+                       __be32  ipv4_src;        /* IPv4 source address. */
+                       __be32  ipv4_dst;        /* IPv4 destination address. */
+               };
+               struct {
+                       __be32  ipv6_src[4]; /* IPv6 source address. */
+                       __be32  ipv6_dst[4]; /* IPv6 source address. */
+               };
+       };
        u16     in_port;    /* Input switch port. */
        __be16  dl_tci;     /* 0 if no VLAN, VLAN_TAG_PRESENT set otherwise. */
        __be16  dl_type;    /* Ethernet frame type. */
index b7b952435eb314ed7fe031eecd10680ed44a2317..88c5769ddb291eeb4306bc1078e68ee9de832517 100644 (file)
@@ -17,6 +17,7 @@ openvswitch_headers += \
        linux-2.6/compat-2.6/include/linux/err.h \
        linux-2.6/compat-2.6/include/linux/genetlink.h \
        linux-2.6/compat-2.6/include/linux/icmp.h \
+       linux-2.6/compat-2.6/include/linux/icmpv6.h \
        linux-2.6/compat-2.6/include/linux/if.h \
        linux-2.6/compat-2.6/include/linux/if_arp.h \
        linux-2.6/compat-2.6/include/linux/if_ether.h \
diff --git a/datapath/linux-2.6/compat-2.6/include/linux/icmpv6.h b/datapath/linux-2.6/compat-2.6/include/linux/icmpv6.h
new file mode 100644 (file)
index 0000000..f005a48
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef __LINUX_ICMPV6_WRAPPER_H
+#define __LINUX_ICMPV6_WRAPPER_H 1
+
+#include_next <linux/icmpv6.h>
+
+#ifndef HAVE_ICMP6_HDR
+static inline struct icmp6hdr *icmp6_hdr(const struct sk_buff *skb)
+{
+        return (struct icmp6hdr *)skb_transport_header(skb);
+}
+#endif
+
+#endif
index c5b0a7ca84c295cae34effca9ac3e3efae7d4fe3..f67862639a14d93be70022afda347cd2166e04d0 100644 (file)
@@ -388,6 +388,8 @@ OFP_ASSERT(sizeof(struct nx_action_pop_queue) == 16);
  *   - NXM_NX_TUN_ID
  *   - NXM_NX_ARP_SHA
  *   - NXM_NX_ARP_THA
+ *   - NXM_NX_ICMPV6_TYPE
+ *   - NXM_NX_ICMPV6_CODE
  *   - NXM_NX_REG(idx) for idx in the switch's accepted range.
  *
  * The following nxm_header values are potentially acceptable as 'dst':
@@ -904,7 +906,7 @@ enum nx_mp_algorithm {
 
 /* The "type of service" byte of the IP header, with the ECN bits forced to 0.
  *
- * Prereqs: NXM_OF_ETH_TYPE must match 0x0800 exactly.
+ * Prereqs: NXM_OF_ETH_TYPE must be either 0x0800 or 0x86dd.
  *
  * Format: 8-bit integer with 2 least-significant bits forced to 0.
  *
@@ -913,7 +915,7 @@ enum nx_mp_algorithm {
 
 /* The "protocol" byte in the IP header.
  *
- * Prereqs: NXM_OF_ETH_TYPE must match 0x0800 exactly.
+ * Prereqs: NXM_OF_ETH_TYPE must be either 0x0800 or 0x86dd.
  *
  * Format: 8-bit integer.
  *
@@ -936,7 +938,7 @@ enum nx_mp_algorithm {
 /* The source or destination port in the TCP header.
  *
  * Prereqs:
- *   NXM_OF_ETH_TYPE must match 0x0800 exactly.
+ *   NXM_OF_ETH_TYPE must be either 0x0800 or 0x86dd.
  *   NXM_OF_IP_PROTO must match 6 exactly.
  *
  * Format: 16-bit integer in network byte order.
@@ -948,7 +950,7 @@ enum nx_mp_algorithm {
 /* The source or destination port in the UDP header.
  *
  * Prereqs:
- *   NXM_OF_ETH_TYPE must match 0x0800 exactly.
+ *   NXM_OF_ETH_TYPE must match either 0x0800 or 0x86dd.
  *   NXM_OF_IP_PROTO must match 17 exactly.
  *
  * Format: 16-bit integer in network byte order.
@@ -1051,6 +1053,32 @@ enum nx_mp_algorithm {
 #define NXM_NX_ARP_SHA    NXM_HEADER  (0x0001, 17, 6)
 #define NXM_NX_ARP_THA    NXM_HEADER  (0x0001, 18, 6)
 
+/* The source or destination address in the IPv6 header.
+ *
+ * Prereqs: NXM_OF_ETH_TYPE must match 0x86dd exactly.
+ *
+ * Format: 128-bit IPv6 address.
+ *
+ * Masking: Only CIDR masks are allowed, that is, masks that consist of N
+ *   high-order bits set to 1 and the other 128-N bits set to 0. */
+#define NXM_NX_IPV6_SRC    NXM_HEADER  (0x0001, 19, 16)
+#define NXM_NX_IPV6_SRC_W  NXM_HEADER_W(0x0001, 19, 16)
+#define NXM_NX_IPV6_DST    NXM_HEADER  (0x0001, 20, 16)
+#define NXM_NX_IPV6_DST_W  NXM_HEADER_W(0x0001, 20, 16)
+
+/* The type or code in the ICMPv6 header.
+ *
+ * Prereqs:
+ *   NXM_OF_ETH_TYPE must match 0x86dd exactly.
+ *   NXM_OF_IP_PROTO must match 58 exactly.
+ *
+ * Format: 8-bit integer.
+ *
+ * Masking: Not maskable. */
+#define NXM_NX_ICMPV6_TYPE NXM_HEADER  (0x0001, 21, 1)
+#define NXM_NX_ICMPV6_CODE NXM_HEADER  (0x0001, 22, 1)
+
+
 /* ## --------------------- ## */
 /* ## Requests and replies. ## */
 /* ## --------------------- ## */
index fdd225db606ac72f3038d84acd36a2816ff81355..13b7d9d1a05412501e1674f8cc6cc6f930dab2a3 100644 (file)
@@ -311,9 +311,11 @@ enum odp_key_type {
        ODP_KEY_ATTR_8021Q,     /* struct odp_key_8021q */
        ODP_KEY_ATTR_ETHERTYPE, /* 16-bit Ethernet type */
        ODP_KEY_ATTR_IPV4,      /* struct odp_key_ipv4 */
+       ODP_KEY_ATTR_IPV6,      /* struct odp_key_ipv6 */
        ODP_KEY_ATTR_TCP,       /* struct odp_key_tcp */
        ODP_KEY_ATTR_UDP,       /* struct odp_key_udp */
        ODP_KEY_ATTR_ICMP,      /* struct odp_key_icmp */
+       ODP_KEY_ATTR_ICMPV6,    /* struct odp_key_icmpv6 */
        ODP_KEY_ATTR_ARP,       /* struct odp_key_arp */
        __ODP_KEY_ATTR_MAX
 };
@@ -337,6 +339,13 @@ struct odp_key_ipv4 {
        uint8_t  ipv4_tos;
 };
 
+struct odp_key_ipv6 {
+       ovs_be32 ipv6_src[4];
+       ovs_be32 ipv6_dst[4];
+       uint8_t  ipv6_proto;
+       uint8_t  ipv6_tos;
+};
+
 struct odp_key_tcp {
        ovs_be16 tcp_src;
        ovs_be16 tcp_dst;
@@ -352,6 +361,11 @@ struct odp_key_icmp {
        uint8_t icmp_code;
 };
 
+struct odp_key_icmpv6 {
+       uint8_t icmpv6_type;
+       uint8_t icmpv6_code;
+};
+
 struct odp_key_arp {
        ovs_be32 arp_sip;
        ovs_be32 arp_tip;
index a4532a61349c6bb803b87881f1c648c0c0b85303..658be8086395105c7c26fffaa484c7a7e65a2e01 100644 (file)
@@ -333,6 +333,42 @@ cls_rule_set_arp_tha(struct cls_rule *rule, const uint8_t tha[ETH_ADDR_LEN])
     memcpy(rule->flow.arp_tha, tha, ETH_ADDR_LEN);
 }
 
+void
+cls_rule_set_ipv6_src(struct cls_rule *rule, const struct in6_addr *src)
+{
+    cls_rule_set_ipv6_src_masked(rule, src, &in6addr_exact);
+}
+
+bool
+cls_rule_set_ipv6_src_masked(struct cls_rule *rule, const struct in6_addr *src,
+                             const struct in6_addr *mask)
+{
+    if (flow_wildcards_set_ipv6_src_mask(&rule->wc, mask)) {
+        rule->flow.ipv6_src = ipv6_addr_bitand(src, mask);
+        return true;
+    } else {
+        return false;
+    }
+}
+
+void
+cls_rule_set_ipv6_dst(struct cls_rule *rule, const struct in6_addr *dst)
+{
+    cls_rule_set_ipv6_dst_masked(rule, dst, &in6addr_exact);
+}
+
+bool
+cls_rule_set_ipv6_dst_masked(struct cls_rule *rule, const struct in6_addr *dst,
+                             const struct in6_addr *mask)
+{
+    if (flow_wildcards_set_ipv6_dst_mask(&rule->wc, mask)) {
+        rule->flow.ipv6_dst = ipv6_addr_bitand(dst, mask);
+        return true;
+    } else {
+        return false;
+    }
+}
+
 /* Returns true if 'a' and 'b' have the same priority, wildcard the same
  * fields, and have the same values for fixed fields, otherwise false. */
 bool
@@ -361,6 +397,27 @@ format_ip_netmask(struct ds *s, const char *name, ovs_be32 ip,
     }
 }
 
+static void
+format_ipv6_netmask(struct ds *s, const char *name,
+                    const struct in6_addr *addr,
+                    const struct in6_addr *netmask)
+{
+    if (!ipv6_mask_is_any(netmask)) {
+        ds_put_format(s, "%s=", name);
+        print_ipv6_addr(s, addr);
+        if (!ipv6_mask_is_exact(netmask)) {
+            if (ipv6_is_cidr(netmask)) {
+                int cidr_bits = ipv6_count_cidr_bits(netmask);
+                ds_put_format(s, "/%d", cidr_bits);
+            } else {
+                ds_put_char(s, '/');
+                print_ipv6_addr(s, netmask);
+            }
+        }
+        ds_put_char(s, ',');
+    }
+}
+
 void
 cls_rule_format(const struct cls_rule *rule, struct ds *s)
 {
@@ -395,6 +452,22 @@ cls_rule_format(const struct cls_rule *rule, struct ds *s)
             } else {
                 ds_put_cstr(s, "ip,");
             }
+        } else if (f->dl_type == htons(ETH_TYPE_IPV6)) {
+            if (!(w & FWW_NW_PROTO)) {
+                skip_proto = true;
+                if (f->nw_proto == IPPROTO_ICMPV6) {
+                    ds_put_cstr(s, "icmp6,");
+                } else if (f->nw_proto == IPPROTO_TCP) {
+                    ds_put_cstr(s, "tcp6,");
+                } else if (f->nw_proto == IPPROTO_UDP) {
+                    ds_put_cstr(s, "udp6,");
+                } else {
+                    ds_put_cstr(s, "ipv6,");
+                    skip_proto = false;
+                }
+            } else {
+                ds_put_cstr(s, "ipv6,");
+            }
         } else if (f->dl_type == htons(ETH_TYPE_ARP)) {
             ds_put_cstr(s, "arp,");
         } else {
@@ -472,8 +545,13 @@ cls_rule_format(const struct cls_rule *rule, struct ds *s)
     if (!skip_type && !(w & FWW_DL_TYPE)) {
         ds_put_format(s, "dl_type=0x%04"PRIx16",", ntohs(f->dl_type));
     }
-    format_ip_netmask(s, "nw_src", f->nw_src, wc->nw_src_mask);
-    format_ip_netmask(s, "nw_dst", f->nw_dst, wc->nw_dst_mask);
+    if (f->dl_type == htons(ETH_TYPE_IPV6)) {
+        format_ipv6_netmask(s, "ipv6_src", &f->ipv6_src, &wc->ipv6_src_mask);
+        format_ipv6_netmask(s, "ipv6_dst", &f->ipv6_dst, &wc->ipv6_dst_mask);
+    } else {
+        format_ip_netmask(s, "nw_src", f->nw_src, wc->nw_src_mask);
+        format_ip_netmask(s, "nw_dst", f->nw_dst, wc->nw_dst_mask);
+    }
     if (!skip_proto && !(w & FWW_NW_PROTO)) {
         if (f->dl_type == htons(ETH_TYPE_ARP)) {
             ds_put_format(s, "opcode=%"PRIu8",", f->nw_proto);
@@ -501,6 +579,13 @@ cls_rule_format(const struct cls_rule *rule, struct ds *s)
         if (!(w & FWW_TP_DST)) {
             ds_put_format(s, "icmp_code=%"PRIu16",", ntohs(f->tp_dst));
         }
+    } else if (f->nw_proto == IPPROTO_ICMPV6) {
+        if (!(w & FWW_TP_SRC)) {
+            ds_put_format(s, "icmp_type=%"PRIu16",", ntohs(f->tp_src));
+        }
+        if (!(w & FWW_TP_DST)) {
+            ds_put_format(s, "icmp_code=%"PRIu16",", ntohs(f->tp_dst));
+        }
     } else {
         if (!(w & FWW_TP_SRC)) {
             ds_put_format(s, "tp_src=%"PRIu16",", ntohs(f->tp_src));
@@ -964,6 +1049,30 @@ next_rule_in_list(struct cls_rule *rule)
     return next->priority < rule->priority ? next : NULL;
 }
 
+static bool
+ipv6_equal_except(const struct in6_addr *a, const struct in6_addr *b,
+                  const struct in6_addr *mask)
+{
+    int i;
+
+#ifdef s6_addr32
+    for (i=0; i<4; i++) {
+        if ((a->s6_addr32[i] ^ b->s6_addr32[i]) & mask->s6_addr32[i]) {
+            return false;
+        }
+    }
+#else
+    for (i=0; i<16; i++) {
+        if ((a->s6_addr[i] ^ b->s6_addr[i]) & mask->s6_addr[i]) {
+            return false;
+        }
+    }
+#endif
+
+    return true;
+}
+
+
 static bool
 flow_equal_except(const struct flow *a, const struct flow *b,
                   const struct flow_wildcards *wildcards)
@@ -971,7 +1080,7 @@ flow_equal_except(const struct flow *a, const struct flow *b,
     const flow_wildcards_t wc = wildcards->wildcards;
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 52 + FLOW_N_REGS * 4);
+    BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 84 + FLOW_N_REGS * 4);
 
     for (i = 0; i < FLOW_N_REGS; i++) {
         if ((a->regs[i] ^ b->regs[i]) & wildcards->reg_masks[i]) {
@@ -1000,7 +1109,11 @@ flow_equal_except(const struct flow *a, const struct flow *b,
             && (wc & FWW_NW_PROTO || a->nw_proto == b->nw_proto)
             && (wc & FWW_NW_TOS || a->nw_tos == b->nw_tos)
             && (wc & FWW_ARP_SHA || eth_addr_equals(a->arp_sha, b->arp_sha))
-            && (wc & FWW_ARP_THA || eth_addr_equals(a->arp_tha, b->arp_tha)));
+            && (wc & FWW_ARP_THA || eth_addr_equals(a->arp_tha, b->arp_tha))
+            && ipv6_equal_except(&a->ipv6_src, &b->ipv6_src,
+                    &wildcards->ipv6_src_mask)
+            && ipv6_equal_except(&a->ipv6_dst, &b->ipv6_dst,
+                    &wildcards->ipv6_dst_mask));
 }
 
 static void
@@ -1009,7 +1122,7 @@ zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards)
     const flow_wildcards_t wc = wildcards->wildcards;
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 52 + 4 * FLOW_N_REGS);
+    BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 84 + 4 * FLOW_N_REGS);
 
     for (i = 0; i < FLOW_N_REGS; i++) {
         flow->regs[i] &= wildcards->reg_masks[i];
@@ -1052,4 +1165,8 @@ zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards)
     if (wc & FWW_ARP_THA) {
         memset(flow->arp_tha, 0, sizeof flow->arp_tha);
     }
+    flow->ipv6_src = ipv6_addr_bitand(&flow->ipv6_src,
+            &wildcards->ipv6_src_mask);
+    flow->ipv6_dst = ipv6_addr_bitand(&flow->ipv6_dst,
+            &wildcards->ipv6_dst_mask);
 }
index 401497279130263de8ee1aabdac83b3ac0725c2d..c82a4844978cd1d53a67d7d20f721ffd1bd2bf11 100644 (file)
@@ -103,6 +103,12 @@ void cls_rule_set_icmp_type(struct cls_rule *, uint8_t);
 void cls_rule_set_icmp_code(struct cls_rule *, uint8_t);
 void cls_rule_set_arp_sha(struct cls_rule *, const uint8_t[6]);
 void cls_rule_set_arp_tha(struct cls_rule *, const uint8_t[6]);
+void cls_rule_set_ipv6_src(struct cls_rule *, const struct in6_addr *);
+bool cls_rule_set_ipv6_src_masked(struct cls_rule *, const struct in6_addr *,
+                                  const struct in6_addr *);
+void cls_rule_set_ipv6_dst(struct cls_rule *, const struct in6_addr *);
+bool cls_rule_set_ipv6_dst_masked(struct cls_rule *, const struct in6_addr *,
+                                  const struct in6_addr *);
 
 bool cls_rule_equal(const struct cls_rule *, const struct cls_rule *);
 
index ff06318c2115b810ca44ada808976868dffc2077..41f13b88c46f78299bc94d035f9d78cfb7b3a30c 100644 (file)
 #include <config.h>
 #include <sys/types.h>
 #include "flow.h"
+#include <errno.h>
 #include <inttypes.h>
 #include <netinet/in.h>
+#include <netinet/icmp6.h>
+#include <netinet/ip6.h>
 #include <stdlib.h>
 #include <string.h>
 #include "byte-order.h"
@@ -80,6 +83,12 @@ pull_icmp(struct ofpbuf *packet)
     return ofpbuf_try_pull(packet, ICMP_HEADER_LEN);
 }
 
+static struct icmp6_hdr *
+pull_icmpv6(struct ofpbuf *packet)
+{
+    return ofpbuf_try_pull(packet, sizeof(struct icmp6_hdr));
+}
+
 static void
 parse_vlan(struct ofpbuf *b, struct flow *flow)
 {
@@ -122,6 +131,105 @@ parse_ethertype(struct ofpbuf *b)
     return llc->snap.snap_type;
 }
 
+static int
+parse_ipv6(struct ofpbuf *packet, struct flow *flow)
+{
+    struct ip6_hdr *nh;
+    int nh_len = sizeof(struct ip6_hdr);
+    int payload_len;
+    ovs_be32 tc_flow;
+    int nexthdr;
+
+    if (packet->size < sizeof *nh) {
+        return -EINVAL;
+    }
+
+    nh = packet->data;
+    nexthdr = nh->ip6_nxt;
+    payload_len = ntohs(nh->ip6_plen);
+
+    flow->ipv6_src = nh->ip6_src;
+    flow->ipv6_dst = nh->ip6_dst;
+
+    tc_flow = get_unaligned_be32(&nh->ip6_flow);
+    flow->nw_tos = (ntohl(tc_flow) >> 4) & IP_DSCP_MASK;
+    flow->nw_proto = IPPROTO_NONE;
+
+    /* We don't process jumbograms. */
+    if (!payload_len) {
+        return -EINVAL;
+    }
+
+    if (packet->size < sizeof *nh + payload_len) {
+        return -EINVAL;
+    }
+
+    while (1) {
+        if ((nexthdr != IPPROTO_HOPOPTS)
+                && (nexthdr != IPPROTO_ROUTING)
+                && (nexthdr != IPPROTO_DSTOPTS)
+                && (nexthdr != IPPROTO_AH)
+                && (nexthdr != IPPROTO_FRAGMENT)) {
+            /* It's either a terminal header (e.g., TCP, UDP) or one we
+             * don't understand.  In either case, we're done with the
+             * packet, so use it to fill in 'nw_proto'. */
+            break;
+        }
+
+        /* We only verify that at least 8 bytes of the next header are
+         * available, but many of these headers are longer.  Ensure that
+         * accesses within the extension header are within those first 8
+         * bytes. */
+        if (packet->size < nh_len + 8) {
+            return -EINVAL;
+        }
+
+        if ((nexthdr == IPPROTO_HOPOPTS)
+                || (nexthdr == IPPROTO_ROUTING)
+                || (nexthdr == IPPROTO_DSTOPTS)) {
+            /* These headers, while different, have the fields we care about
+             * in the same location and with the same interpretation. */
+            struct ip6_ext *ext_hdr;
+
+            ext_hdr = (struct ip6_ext *)((char *)packet->data + nh_len);
+            nexthdr = ext_hdr->ip6e_nxt;
+            nh_len += (ext_hdr->ip6e_len + 1) * 8;
+        } else if (nexthdr == IPPROTO_AH) {
+            /* A standard AH definition isn't available, but the fields
+             * we care about are in the same location as the generic
+             * option header--only the header length is calculated
+             * differently. */
+            struct ip6_ext *ext_hdr;
+
+            ext_hdr = (struct ip6_ext *)((char *)packet->data + nh_len);
+            nexthdr = ext_hdr->ip6e_nxt;
+            nh_len += (ext_hdr->ip6e_len + 2) * 4;
+        } else if (nexthdr == IPPROTO_FRAGMENT) {
+            struct ip6_frag *frag_hdr;
+
+            frag_hdr = (struct ip6_frag *)((char *)packet->data + nh_len);
+
+            nexthdr = frag_hdr->ip6f_nxt;
+            nh_len += sizeof *frag_hdr;
+
+            /* We only process the first fragment. */
+            if ((frag_hdr->ip6f_offlg & IP6F_OFF_MASK) != htons(0)) {
+                nexthdr = IPPROTO_FRAGMENT;
+                break;
+            }
+        }
+    }
+
+    /* The payload length claims to be smaller than the size of the
+     * headers we've already processed. */
+    if (payload_len < nh_len - sizeof *nh) {
+        return -EINVAL;
+    }
+
+    flow->nw_proto = nexthdr;
+    return nh_len;
+}
+
 /* Initializes 'flow' members from 'packet', 'tun_id', and 'in_port.
  * Initializes 'packet' header pointers as follows:
  *
@@ -209,6 +317,41 @@ flow_extract(struct ofpbuf *packet, ovs_be64 tun_id, uint16_t in_port,
                 retval = 1;
             }
         }
+    } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
+        int nh_len;
+        const struct ip6_hdr *nh;
+
+        nh_len = parse_ipv6(&b, flow);
+        if (nh_len < 0) {
+            return 0;
+        }
+
+        nh = ofpbuf_pull(&b, nh_len);
+        if (nh) {
+            packet->l4 = b.data;
+            if (flow->nw_proto == IPPROTO_TCP) {
+                const struct tcp_header *tcp = pull_tcp(&b);
+                if (tcp) {
+                    flow->tp_src = tcp->tcp_src;
+                    flow->tp_dst = tcp->tcp_dst;
+                    packet->l7 = b.data;
+                }
+            } else if (flow->nw_proto == IPPROTO_UDP) {
+                const struct udp_header *udp = pull_udp(&b);
+                if (udp) {
+                    flow->tp_src = udp->udp_src;
+                    flow->tp_dst = udp->udp_dst;
+                    packet->l7 = b.data;
+                }
+            } else if (flow->nw_proto == IPPROTO_ICMPV6) {
+                const struct icmp6_hdr *icmp = pull_icmpv6(&b);
+                if (icmp) {
+                    flow->icmp_type = htons(icmp->icmp6_type);
+                    flow->icmp_code = htons(icmp->icmp6_code);
+                    packet->l7 = b.data;
+                }
+            }
+        }
     } else if (flow->dl_type == htons(ETH_TYPE_ARP)) {
         const struct arp_eth_header *arp = pull_arp(&b);
         if (arp && arp->ar_hrd == htons(1)
@@ -229,6 +372,7 @@ flow_extract(struct ofpbuf *packet, ovs_be64 tun_id, uint16_t in_port,
             }
         }
     }
+
     return retval;
 }
 
@@ -273,17 +417,27 @@ flow_format(struct ds *ds, const struct flow *flow)
         ds_put_char(ds, '0');
     }
     ds_put_format(ds, ") mac"ETH_ADDR_FMT"->"ETH_ADDR_FMT
-                      " type%04"PRIx16
-                      " proto%"PRIu8
-                      " tos%"PRIu8
-                      " ip"IP_FMT"->"IP_FMT,
+                      " type%04"PRIx16,
                   ETH_ADDR_ARGS(flow->dl_src),
                   ETH_ADDR_ARGS(flow->dl_dst),
-                  ntohs(flow->dl_type),
-                  flow->nw_proto,
-                  flow->nw_tos,
-                  IP_ARGS(&flow->nw_src),
-                  IP_ARGS(&flow->nw_dst));
+                  ntohs(flow->dl_type));
+
+    if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
+        ds_put_format(ds, " proto%"PRIu8" tos%"PRIu8" ipv6",
+                      flow->nw_proto, flow->nw_tos);
+        print_ipv6_addr(ds, &flow->ipv6_src);
+        ds_put_cstr(ds, "->");
+        print_ipv6_addr(ds, &flow->ipv6_dst);
+       
+    } else {
+        ds_put_format(ds, " proto%"PRIu8
+                          " tos%"PRIu8
+                          " ip"IP_FMT"->"IP_FMT,
+                      flow->nw_proto,
+                      flow->nw_tos,
+                      IP_ARGS(&flow->nw_src),
+                      IP_ARGS(&flow->nw_dst));
+    }
     if (flow->tp_src || flow->tp_dst) {
         ds_put_format(ds, " port%"PRIu16"->%"PRIu16,
                 ntohs(flow->tp_src), ntohs(flow->tp_dst));
@@ -313,6 +467,8 @@ flow_wildcards_init_catchall(struct flow_wildcards *wc)
     wc->tun_id_mask = htonll(0);
     wc->nw_src_mask = htonl(0);
     wc->nw_dst_mask = htonl(0);
+    wc->ipv6_src_mask = in6addr_any;
+    wc->ipv6_dst_mask = in6addr_any;
     memset(wc->reg_masks, 0, sizeof wc->reg_masks);
     wc->vlan_tci_mask = htons(0);
     wc->zero = 0;
@@ -327,6 +483,8 @@ flow_wildcards_init_exact(struct flow_wildcards *wc)
     wc->tun_id_mask = htonll(UINT64_MAX);
     wc->nw_src_mask = htonl(UINT32_MAX);
     wc->nw_dst_mask = htonl(UINT32_MAX);
+    wc->ipv6_src_mask = in6addr_exact;
+    wc->ipv6_dst_mask = in6addr_exact;
     memset(wc->reg_masks, 0xff, sizeof wc->reg_masks);
     wc->vlan_tci_mask = htons(UINT16_MAX);
     wc->zero = 0;
@@ -343,7 +501,9 @@ flow_wildcards_is_exact(const struct flow_wildcards *wc)
         || wc->tun_id_mask != htonll(UINT64_MAX)
         || wc->nw_src_mask != htonl(UINT32_MAX)
         || wc->nw_dst_mask != htonl(UINT32_MAX)
-        || wc->vlan_tci_mask != htons(UINT16_MAX)) {
+        || wc->vlan_tci_mask != htons(UINT16_MAX)
+        || !ipv6_mask_is_exact(&wc->ipv6_src_mask)
+        || !ipv6_mask_is_exact(&wc->ipv6_dst_mask)) {
         return false;
     }
 
@@ -370,6 +530,10 @@ flow_wildcards_combine(struct flow_wildcards *dst,
     dst->tun_id_mask = src1->tun_id_mask & src2->tun_id_mask;
     dst->nw_src_mask = src1->nw_src_mask & src2->nw_src_mask;
     dst->nw_dst_mask = src1->nw_dst_mask & src2->nw_dst_mask;
+    dst->ipv6_src_mask = ipv6_addr_bitand(&src1->ipv6_src_mask,
+                                        &src2->ipv6_src_mask);
+    dst->ipv6_dst_mask = ipv6_addr_bitand(&src1->ipv6_dst_mask,
+                                        &src2->ipv6_dst_mask);
     for (i = 0; i < FLOW_N_REGS; i++) {
         dst->reg_masks[i] = src1->reg_masks[i] & src2->reg_masks[i];
     }
@@ -383,7 +547,7 @@ flow_wildcards_hash(const struct flow_wildcards *wc)
     /* If you change struct flow_wildcards and thereby trigger this
      * assertion, please check that the new struct flow_wildcards has no holes
      * in it before you update the assertion. */
-    BUILD_ASSERT_DECL(sizeof *wc == 24 + FLOW_N_REGS * 4);
+    BUILD_ASSERT_DECL(sizeof *wc == 56 + FLOW_N_REGS * 4);
     return hash_bytes(wc, sizeof *wc, 0);
 }
 
@@ -399,7 +563,9 @@ flow_wildcards_equal(const struct flow_wildcards *a,
         || a->tun_id_mask != b->tun_id_mask
         || a->nw_src_mask != b->nw_src_mask
         || a->nw_dst_mask != b->nw_dst_mask
-        || a->vlan_tci_mask != b->vlan_tci_mask) {
+        || a->vlan_tci_mask != b->vlan_tci_mask 
+        || !ipv6_addr_equals(&a->ipv6_src_mask, &b->ipv6_src_mask)
+        || !ipv6_addr_equals(&a->ipv6_dst_mask, &b->ipv6_dst_mask)) {
         return false;
     }
 
@@ -419,6 +585,7 @@ flow_wildcards_has_extra(const struct flow_wildcards *a,
                          const struct flow_wildcards *b)
 {
     int i;
+    struct in6_addr ipv6_masked;
 
     for (i = 0; i < FLOW_N_REGS; i++) {
         if ((a->reg_masks[i] & b->reg_masks[i]) != b->reg_masks[i]) {
@@ -426,6 +593,16 @@ flow_wildcards_has_extra(const struct flow_wildcards *a,
         }
     }
 
+    ipv6_masked = ipv6_addr_bitand(&a->ipv6_src_mask, &b->ipv6_src_mask);
+    if (!ipv6_addr_equals(&ipv6_masked, &b->ipv6_src_mask)) {
+        return true;
+    }
+
+    ipv6_masked = ipv6_addr_bitand(&a->ipv6_dst_mask, &b->ipv6_dst_mask);
+    if (!ipv6_addr_equals(&ipv6_masked, &b->ipv6_dst_mask)) {
+        return true;
+    }
+
     return (a->wildcards & ~b->wildcards
             || (a->tun_id_mask & b->tun_id_mask) != b->tun_id_mask
             || (a->nw_src_mask & b->nw_src_mask) != b->nw_src_mask
@@ -462,6 +639,37 @@ flow_wildcards_set_nw_dst_mask(struct flow_wildcards *wc, ovs_be32 mask)
     return set_nw_mask(&wc->nw_dst_mask, mask);
 }
 
+static bool
+set_ipv6_mask(struct in6_addr *maskp, const struct in6_addr *mask)
+{
+    if (ipv6_is_cidr(mask)) {
+        *maskp = *mask;
+        return true;
+    } else {
+        return false;
+    }
+}
+
+/* Sets the IPv6 source wildcard mask to CIDR 'mask' (consisting of N
+ * high-order 1-bit and 128-N low-order 0-bits).  Returns true if successful,
+ * false if 'mask' is not a CIDR mask.  */
+bool
+flow_wildcards_set_ipv6_src_mask(struct flow_wildcards *wc,
+                                 const struct in6_addr *mask)
+{
+    return set_ipv6_mask(&wc->ipv6_src_mask, mask);
+}
+
+/* Sets the IPv6 destination wildcard mask to CIDR 'mask' (consisting of
+ * N high-order 1-bit and 128-N low-order 0-bits).  Returns true if
+ * successful, false if 'mask' is not a CIDR mask.  */
+bool
+flow_wildcards_set_ipv6_dst_mask(struct flow_wildcards *wc,
+                                 const struct in6_addr *mask)
+{
+    return set_ipv6_mask(&wc->ipv6_dst_mask, mask);
+}
+
 /* Sets the wildcard mask for register 'idx' in 'wc' to 'mask'.
  * (A 0-bit indicates a wildcard bit.) */
 void
@@ -475,7 +683,10 @@ uint32_t
 flow_hash_symmetric_l4(const struct flow *flow, uint32_t basis)
 {
     struct {
-        ovs_be32 ip_addr;
+        union {
+            ovs_be32 ipv4_addr;
+            struct in6_addr ipv6_addr;
+        };
         ovs_be16 eth_type;
         ovs_be16 vlan_tci;
         ovs_be16 tp_addr;
@@ -492,17 +703,23 @@ flow_hash_symmetric_l4(const struct flow *flow, uint32_t basis)
     fields.vlan_tci = flow->vlan_tci & htons(VLAN_VID_MASK);
     fields.eth_type = flow->dl_type;
     if (fields.eth_type == htons(ETH_TYPE_IP)) {
-        fields.ip_addr = flow->nw_src ^ flow->nw_dst;
+        fields.ipv4_addr = flow->nw_src ^ flow->nw_dst;
+        fields.ip_proto = flow->nw_proto;
+        if (fields.ip_proto == IPPROTO_TCP || fields.ip_proto == IPPROTO_UDP) {
+            fields.tp_addr = flow->tp_src ^ flow->tp_dst;
+        }
+    } else if (fields.eth_type == htons(ETH_TYPE_IPV6)) {
+        const uint8_t *a = &flow->ipv6_src.s6_addr[0];
+        const uint8_t *b = &flow->ipv6_dst.s6_addr[0];
+        uint8_t *ipv6_addr = &fields.ipv6_addr.s6_addr[0];
+
+        for (i=0; i<16; i++) {
+            ipv6_addr[i] = a[i] ^ b[i];
+        }
         fields.ip_proto = flow->nw_proto;
         if (fields.ip_proto == IPPROTO_TCP || fields.ip_proto == IPPROTO_UDP) {
             fields.tp_addr = flow->tp_src ^ flow->tp_dst;
-        } else {
-            fields.tp_addr = htons(0);
         }
-    } else {
-        fields.ip_addr = htonl(0);
-        fields.ip_proto = 0;
-        fields.tp_addr = htons(0);
     }
     return hash_bytes(&fields, sizeof fields, basis);
 }
index a5b271ee5bfe0e79ddaa50bd9f17830133cc5104..d331aa3613785b596580bc2cdb5bad134c49f1d9 100644 (file)
@@ -43,8 +43,8 @@ BUILD_ASSERT_DECL(FLOW_N_REGS <= NXM_NX_MAX_REGS);
 struct flow {
     ovs_be64 tun_id;            /* Encapsulating tunnel ID. */
     uint32_t regs[FLOW_N_REGS]; /* Registers. */
-    ovs_be32 nw_src;            /* IP source address. */
-    ovs_be32 nw_dst;            /* IP destination address. */
+    ovs_be32 nw_src;            /* IPv4 source address. */
+    ovs_be32 nw_dst;            /* IPv4 destination address. */
     uint16_t in_port;           /* Input switch port. */
     ovs_be16 vlan_tci;          /* If 802.1Q, TCI | VLAN_CFI; otherwise 0. */
     ovs_be16 dl_type;           /* Ethernet frame type. */
@@ -56,15 +56,17 @@ struct flow {
     uint8_t nw_tos;             /* IP ToS (DSCP field, 6 bits). */
     uint8_t arp_sha[6];         /* ARP source hardware address. */
     uint8_t arp_tha[6];         /* ARP target hardware address. */
+    struct in6_addr ipv6_src;   /* IPv6 source address. */
+    struct in6_addr ipv6_dst;   /* IPv6 destination address. */
     uint32_t reserved;          /* Reserved for 64-bit packing. */
 };
 
 /* Assert that there are FLOW_SIG_SIZE bytes of significant data in "struct
  * flow", followed by FLOW_PAD_SIZE bytes of padding. */
-#define FLOW_SIG_SIZE (52 + FLOW_N_REGS * 4)
+#define FLOW_SIG_SIZE (84 + FLOW_N_REGS * 4)
 #define FLOW_PAD_SIZE 4
-BUILD_ASSERT_DECL(offsetof(struct flow, arp_tha) == FLOW_SIG_SIZE - 6);
-BUILD_ASSERT_DECL(sizeof(((struct flow *)0)->arp_tha) == 6);
+BUILD_ASSERT_DECL(offsetof(struct flow, ipv6_dst) == FLOW_SIG_SIZE - 16);
+BUILD_ASSERT_DECL(sizeof(((struct flow *)0)->ipv6_dst) == 16);
 BUILD_ASSERT_DECL(sizeof(struct flow) == FLOW_SIG_SIZE + FLOW_PAD_SIZE);
 
 int flow_extract(struct ofpbuf *, uint64_t tun_id, uint16_t in_port,
@@ -132,6 +134,8 @@ struct flow_wildcards {
     uint32_t reg_masks[FLOW_N_REGS]; /* 1-bit in each significant regs bit. */
     ovs_be32 nw_src_mask;       /* 1-bit in each significant nw_src bit. */
     ovs_be32 nw_dst_mask;       /* 1-bit in each significant nw_dst bit. */
+    struct in6_addr ipv6_src_mask; /* 1-bit in each signficant ipv6_src bit. */
+    struct in6_addr ipv6_dst_mask; /* 1-bit in each signficant ipv6_dst bit. */
     ovs_be16 vlan_tci_mask;     /* 1-bit in each significant vlan_tci bit. */
     uint16_t zero;              /* Padding field set to zero. */
 };
@@ -143,6 +147,10 @@ bool flow_wildcards_is_exact(const struct flow_wildcards *);
 
 bool flow_wildcards_set_nw_src_mask(struct flow_wildcards *, ovs_be32);
 bool flow_wildcards_set_nw_dst_mask(struct flow_wildcards *, ovs_be32);
+bool flow_wildcards_set_ipv6_src_mask(struct flow_wildcards *,
+                                      const struct in6_addr *);
+bool flow_wildcards_set_ipv6_dst_mask(struct flow_wildcards *,
+                                      const struct in6_addr *);
 void flow_wildcards_set_reg_mask(struct flow_wildcards *,
                                  int idx, uint32_t mask);
 
index d43372b8f99844f36b7688ab0c2e951f0b3b6f58..5fc6aa24942f1253b95e1099ef70a14956790589 100644 (file)
@@ -46,7 +46,7 @@ enum {
 /* For each NXM_* field, define NFI_NXM_* as consecutive integers starting from
  * zero. */
 enum nxm_field_index {
-#define DEFINE_FIELD(HEADER, WILDCARD, DL_TYPE, NW_PROTO, WRITABLE) \
+#define DEFINE_FIELD(HEADER, WILDCARD, DL_TYPES, NW_PROTO, WRITABLE) \
         NFI_NXM_##HEADER,
 #include "nx-match.def"
     N_NXM_FIELDS
@@ -54,20 +54,22 @@ enum nxm_field_index {
 
 struct nxm_field {
     struct hmap_node hmap_node;
-    enum nxm_field_index index; /* NFI_* value. */
-    uint32_t header;            /* NXM_* value. */
-    flow_wildcards_t wildcard;  /* FWW_* bit, if exactly one. */
-    ovs_be16 dl_type;           /* dl_type prerequisite, if nonzero. */
-    uint8_t nw_proto;           /* nw_proto prerequisite, if nonzero. */
-    const char *name;           /* "NXM_*" string. */
-    bool writable;              /* Writable with NXAST_REG_{MOVE,LOAD}? */
+    enum nxm_field_index index;       /* NFI_* value. */
+    uint32_t header;                  /* NXM_* value. */
+    flow_wildcards_t wildcard;        /* FWW_* bit, if exactly one. */
+    ovs_be16 dl_type[N_NXM_DL_TYPES]; /* dl_type prerequisites. */
+    uint8_t nw_proto;                 /* nw_proto prerequisite, if nonzero. */
+    const char *name;                 /* "NXM_*" string. */
+    bool writable;                    /* Writable with NXAST_REG_{MOVE,LOAD}? */
 };
 
+
 /* All the known fields. */
 static struct nxm_field nxm_fields[N_NXM_FIELDS] = {
-#define DEFINE_FIELD(HEADER, WILDCARD, DL_TYPE, NW_PROTO, WRITABLE)     \
+#define DEFINE_FIELD(HEADER, WILDCARD, DL_TYPES, NW_PROTO, WRITABLE)     \
     { HMAP_NODE_NULL_INITIALIZER, NFI_NXM_##HEADER, NXM_##HEADER, WILDCARD, \
-      CONSTANT_HTONS(DL_TYPE), NW_PROTO, "NXM_" #HEADER, WRITABLE },
+        DL_CONVERT DL_TYPES, NW_PROTO, "NXM_" #HEADER, WRITABLE },
+#define DL_CONVERT(T1, T2) { CONSTANT_HTONS(T1), CONSTANT_HTONS(T2) }
 #include "nx-match.def"
 };
 
@@ -285,6 +287,50 @@ parse_nxm_entry(struct cls_rule *rule, const struct nxm_field *f,
             return 0;
         }
 
+        /* IPv6 addresses. */
+    case NFI_NXM_NX_IPV6_SRC:
+        if (!ipv6_mask_is_any(&wc->ipv6_src_mask)) {
+            return NXM_DUP_TYPE;
+        } else {
+            struct in6_addr ipv6;
+            memcpy(&ipv6, value, sizeof ipv6);
+            cls_rule_set_ipv6_src(rule, &ipv6);
+            return 0;
+        }
+    case NFI_NXM_NX_IPV6_SRC_W:
+        if (!ipv6_mask_is_any(&wc->ipv6_src_mask)) {
+            return NXM_DUP_TYPE;
+        } else {
+            struct in6_addr ipv6, netmask;
+            memcpy(&ipv6, value, sizeof ipv6);
+            memcpy(&netmask, mask, sizeof netmask);
+            if (!cls_rule_set_ipv6_src_masked(rule, &ipv6, &netmask)) {
+                return NXM_BAD_MASK;
+            }
+            return 0;
+        }
+    case NFI_NXM_NX_IPV6_DST:
+        if (!ipv6_mask_is_any(&wc->ipv6_dst_mask)) {
+            return NXM_DUP_TYPE;
+        } else {
+            struct in6_addr ipv6;
+            memcpy(&ipv6, value, sizeof ipv6);
+            cls_rule_set_ipv6_dst(rule, &ipv6);
+            return 0;
+        }
+    case NFI_NXM_NX_IPV6_DST_W:
+        if (!ipv6_mask_is_any(&wc->ipv6_dst_mask)) {
+            return NXM_DUP_TYPE;
+        } else {
+            struct in6_addr ipv6, netmask;
+            memcpy(&ipv6, value, sizeof ipv6);
+            memcpy(&netmask, mask, sizeof netmask);
+            if (!cls_rule_set_ipv6_dst_masked(rule, &ipv6, &netmask)) {
+                return NXM_BAD_MASK;
+            }
+            return 0;
+        }
+
         /* TCP header. */
     case NFI_NXM_OF_TCP_SRC:
         flow->tp_src = get_unaligned_be16(value);
@@ -309,6 +355,14 @@ parse_nxm_entry(struct cls_rule *rule, const struct nxm_field *f,
         flow->tp_dst = htons(*(uint8_t *) value);
         return 0;
 
+        /* ICMPv6 header. */
+    case NFI_NXM_NX_ICMPV6_TYPE:
+        flow->tp_src = htons(*(uint8_t *) value);
+        return 0;
+    case NFI_NXM_NX_ICMPV6_CODE:
+        flow->tp_dst = htons(*(uint8_t *) value);
+        return 0;
+
         /* ARP header. */
     case NFI_NXM_OF_ARP_OP:
         if (ntohs(get_unaligned_be16(value)) > 255) {
@@ -372,9 +426,19 @@ parse_nxm_entry(struct cls_rule *rule, const struct nxm_field *f,
 static bool
 nxm_prereqs_ok(const struct nxm_field *field, const struct flow *flow)
 {
-    return (!field->dl_type
-            || (field->dl_type == flow->dl_type
-                && (!field->nw_proto || field->nw_proto == flow->nw_proto)));
+    if (field->nw_proto && field->nw_proto != flow->nw_proto) {
+        return false;
+    }
+
+    if (!field->dl_type[0]) {
+        return true;
+    } else if (field->dl_type[0] == flow->dl_type) {
+        return true;
+    } else if (field->dl_type[1] && field->dl_type[1] == flow->dl_type) {
+        return true;
+    }
+
+    return false;
 }
 
 static uint32_t
@@ -609,6 +673,22 @@ nxm_put_eth_dst(struct ofpbuf *b,
     }
 }
 
+static void
+nxm_put_ipv6(struct ofpbuf *b, uint32_t header,
+             const struct in6_addr *value, const struct in6_addr *mask)
+{
+    if (ipv6_mask_is_any(mask)) {
+        return;
+    } else if (ipv6_mask_is_exact(mask)) {
+        nxm_put_header(b, header);
+        ofpbuf_put(b, value, sizeof *value);
+    } else {
+        nxm_put_header(b, NXM_MAKE_WILD_HEADER(header));
+        ofpbuf_put(b, value, sizeof *value);
+        ofpbuf_put(b, mask, sizeof *mask);
+    }
+}
+
 /* Appends to 'b' the nx_match format that expresses 'cr' (except for
  * 'cr->priority', because priority is not part of nx_match), plus enough
  * zero bytes to pad the nx_match out to a multiple of 8.
@@ -693,6 +773,51 @@ nx_put_match(struct ofpbuf *b, const struct cls_rule *cr)
                 break;
             }
         }
+    } else if (!(wc & FWW_DL_TYPE) && flow->dl_type == htons(ETH_TYPE_IPV6)) {
+        /* IPv6. */
+
+        if (!(wc & FWW_NW_TOS)) {
+            nxm_put_8(b, NXM_OF_IP_TOS, flow->nw_tos & 0xfc);
+        }
+        nxm_put_ipv6(b, NXM_NX_IPV6_SRC, &flow->ipv6_src,
+                &cr->wc.ipv6_src_mask);
+        nxm_put_ipv6(b, NXM_NX_IPV6_DST, &flow->ipv6_dst,
+                &cr->wc.ipv6_dst_mask);
+
+        if (!(wc & FWW_NW_PROTO)) {
+            nxm_put_8(b, NXM_OF_IP_PROTO, flow->nw_proto);
+            switch (flow->nw_proto) {
+                /* TCP. */
+            case IPPROTO_TCP:
+                if (!(wc & FWW_TP_SRC)) {
+                    nxm_put_16(b, NXM_OF_TCP_SRC, flow->tp_src);
+                }
+                if (!(wc & FWW_TP_DST)) {
+                    nxm_put_16(b, NXM_OF_TCP_DST, flow->tp_dst);
+                }
+                break;
+
+                /* UDP. */
+            case IPPROTO_UDP:
+                if (!(wc & FWW_TP_SRC)) {
+                    nxm_put_16(b, NXM_OF_UDP_SRC, flow->tp_src);
+                }
+                if (!(wc & FWW_TP_DST)) {
+                    nxm_put_16(b, NXM_OF_UDP_DST, flow->tp_dst);
+                }
+                break;
+
+                /* ICMPv6. */
+            case IPPROTO_ICMPV6:
+                if (!(wc & FWW_TP_SRC)) {
+                    nxm_put_8(b, NXM_NX_ICMPV6_TYPE, ntohs(flow->tp_src));
+                }
+                if (!(wc & FWW_TP_DST)) {
+                    nxm_put_8(b, NXM_NX_ICMPV6_CODE, ntohs(flow->tp_dst));
+                }
+                break;
+            }
+        }
     } else if (!(wc & FWW_DL_TYPE) && flow->dl_type == htons(ETH_TYPE_ARP)) {
         /* ARP. */
         if (!(wc & FWW_NW_PROTO)) {
@@ -1147,9 +1272,11 @@ nxm_read_field(const struct nxm_field *src, const struct flow *flow)
         return ntohs(flow->tp_dst);
 
     case NFI_NXM_OF_ICMP_TYPE:
+    case NFI_NXM_NX_ICMPV6_TYPE:
         return ntohs(flow->tp_src) & 0xff;
 
     case NFI_NXM_OF_ICMP_CODE:
+    case NFI_NXM_NX_ICMPV6_CODE:
         return ntohs(flow->tp_dst) & 0xff;
 
     case NFI_NXM_NX_TUN_ID:
@@ -1188,6 +1315,10 @@ nxm_read_field(const struct nxm_field *src, const struct flow *flow)
     case NFI_NXM_OF_IP_DST_W:
     case NFI_NXM_OF_ARP_SPA_W:
     case NFI_NXM_OF_ARP_TPA_W:
+    case NFI_NXM_NX_IPV6_SRC:
+    case NFI_NXM_NX_IPV6_SRC_W:
+    case NFI_NXM_NX_IPV6_DST:
+    case NFI_NXM_NX_IPV6_DST_W:
     case N_NXM_FIELDS:
         NOT_REACHED();
     }
@@ -1255,6 +1386,12 @@ nxm_write_field(const struct nxm_field *dst, struct flow *flow,
     case NFI_NXM_OF_ARP_TPA_W:
     case NFI_NXM_NX_ARP_SHA:
     case NFI_NXM_NX_ARP_THA:
+    case NFI_NXM_NX_IPV6_SRC:
+    case NFI_NXM_NX_IPV6_SRC_W:
+    case NFI_NXM_NX_IPV6_DST:
+    case NFI_NXM_NX_IPV6_DST_W:
+    case NFI_NXM_NX_ICMPV6_TYPE:
+    case NFI_NXM_NX_ICMPV6_CODE:
     case N_NXM_FIELDS:
         NOT_REACHED();
     }
index 966143dccdc617c4448efb40660942dd91e18c4a..41e76d6568ac1d1c6808cb0e7879ca54a33b93a2 100644 (file)
  * limitations under the License.
  */
 
-#define DEFINE_FIELD_M(HEADER, WILDCARD, DL_TYPE, NW_PROTO, WRITABLE)   \
-    DEFINE_FIELD(HEADER, WILDCARD, DL_TYPE, NW_PROTO, WRITABLE)         \
-    DEFINE_FIELD(HEADER##_W, WILDCARD, DL_TYPE, NW_PROTO, false)
+#define N_NXM_DL_TYPES 2
 
-/*             NXM_ suffix   FWW_* bit     dl_type       nw_proto      rw? */
-/*             ------------  ------------  -----------   ------------- --- */
-DEFINE_FIELD  (OF_IN_PORT,   FWW_IN_PORT,  0,            0,            false)
-DEFINE_FIELD_M(OF_ETH_DST,   0,            0,            0,            false)
-DEFINE_FIELD  (OF_ETH_SRC,   FWW_DL_SRC,   0,            0,            false)
-DEFINE_FIELD  (OF_ETH_TYPE,  FWW_DL_TYPE,  0,            0,            false)
-DEFINE_FIELD_M(OF_VLAN_TCI,  0,            0,            0,            true)
-DEFINE_FIELD  (OF_IP_TOS,    FWW_NW_TOS,   ETH_TYPE_IP,  0,            false)
-DEFINE_FIELD  (OF_IP_PROTO,  FWW_NW_PROTO, ETH_TYPE_IP,  0,            false)
-DEFINE_FIELD_M(OF_IP_SRC,    0,            ETH_TYPE_IP,  0,            false)
-DEFINE_FIELD_M(OF_IP_DST,    0,            ETH_TYPE_IP,  0,            false)
-DEFINE_FIELD  (OF_TCP_SRC,   FWW_TP_SRC,   ETH_TYPE_IP,  IPPROTO_TCP,  false)
-DEFINE_FIELD  (OF_TCP_DST,   FWW_TP_DST,   ETH_TYPE_IP,  IPPROTO_TCP,  false)
-DEFINE_FIELD  (OF_UDP_SRC,   FWW_TP_SRC,   ETH_TYPE_IP,  IPPROTO_UDP,  false)
-DEFINE_FIELD  (OF_UDP_DST,   FWW_TP_DST,   ETH_TYPE_IP,  IPPROTO_UDP,  false)
-DEFINE_FIELD  (OF_ICMP_TYPE, FWW_TP_SRC,   ETH_TYPE_IP,  IPPROTO_ICMP, false)
-DEFINE_FIELD  (OF_ICMP_CODE, FWW_TP_DST,   ETH_TYPE_IP,  IPPROTO_ICMP, false)
-DEFINE_FIELD  (OF_ARP_OP,    FWW_NW_PROTO, ETH_TYPE_ARP, 0,            false)
-DEFINE_FIELD_M(OF_ARP_SPA,   0,            ETH_TYPE_ARP, 0,            false)
-DEFINE_FIELD_M(OF_ARP_TPA,   0,            ETH_TYPE_ARP, 0,            false)
-DEFINE_FIELD_M(NX_TUN_ID,    0,            0,            0,            true)
-DEFINE_FIELD  (NX_ARP_SHA,   FWW_ARP_SHA,  ETH_TYPE_ARP, 0,            false)
-DEFINE_FIELD  (NX_ARP_THA,   FWW_ARP_THA,  ETH_TYPE_ARP, 0,            false)
+#define NXM_DL_NONE   (0, 0)
+#define NXM_DL_ARP    (ETH_TYPE_ARP, 0)
+#define NXM_DL_IP     (ETH_TYPE_IP, 0)
+#define NXM_DL_IPV6   (ETH_TYPE_IPV6, 0)
+#define NXM_DL_IP_ANY (ETH_TYPE_IP, ETH_TYPE_IPV6)
 
-DEFINE_FIELD_M(NX_REG0,      0,            0,            0,            true)
+#define DEFINE_FIELD_M(HEADER, WILDCARD, DL_TYPES, NW_PROTO, WRITABLE)  \
+    DEFINE_FIELD(HEADER, WILDCARD, DL_TYPES, NW_PROTO, WRITABLE)        \
+    DEFINE_FIELD(HEADER##_W, WILDCARD, DL_TYPES, NW_PROTO, false)
+
+/*             NXM_ suffix     FWW_* bit     dl_types       nw_proto      rw? */
+/*             ------------    ------------  -----------    ------------- --- */
+DEFINE_FIELD_M(NX_TUN_ID,      0,            NXM_DL_NONE,   0,            true)
+DEFINE_FIELD  (OF_IN_PORT,     FWW_IN_PORT,  NXM_DL_NONE,   0,            false)
+DEFINE_FIELD_M(OF_ETH_DST,     0,            NXM_DL_NONE,   0,            false)
+DEFINE_FIELD  (OF_ETH_SRC,     FWW_DL_SRC,   NXM_DL_NONE,   0,            false)
+DEFINE_FIELD  (OF_ETH_TYPE,    FWW_DL_TYPE,  NXM_DL_NONE,   0,            false)
+DEFINE_FIELD_M(OF_VLAN_TCI,    0,            NXM_DL_NONE,   0,            true)
+DEFINE_FIELD  (OF_IP_TOS,      FWW_NW_TOS,   NXM_DL_IP_ANY, 0,            false)
+DEFINE_FIELD  (OF_IP_PROTO,    FWW_NW_PROTO, NXM_DL_IP_ANY, 0,            false)
+DEFINE_FIELD_M(OF_IP_SRC,      0,            NXM_DL_IP,     0,            false)
+DEFINE_FIELD_M(OF_IP_DST,      0,            NXM_DL_IP,     0,            false)
+DEFINE_FIELD  (OF_TCP_SRC,     FWW_TP_SRC,   NXM_DL_IP_ANY, IPPROTO_TCP,  false)
+DEFINE_FIELD  (OF_TCP_DST,     FWW_TP_DST,   NXM_DL_IP_ANY, IPPROTO_TCP,  false)
+DEFINE_FIELD  (OF_UDP_SRC,     FWW_TP_SRC,   NXM_DL_IP_ANY, IPPROTO_UDP,  false)
+DEFINE_FIELD  (OF_UDP_DST,     FWW_TP_DST,   NXM_DL_IP_ANY, IPPROTO_UDP,  false)
+DEFINE_FIELD  (OF_ICMP_TYPE,   FWW_TP_SRC,   NXM_DL_IP,     IPPROTO_ICMP, false)
+DEFINE_FIELD  (OF_ICMP_CODE,   FWW_TP_DST,   NXM_DL_IP,     IPPROTO_ICMP, false)
+DEFINE_FIELD  (OF_ARP_OP,      FWW_NW_PROTO, NXM_DL_ARP,    0,            false)
+DEFINE_FIELD_M(OF_ARP_SPA,     0,            NXM_DL_ARP,    0,            false)
+DEFINE_FIELD_M(OF_ARP_TPA,     0,            NXM_DL_ARP,    0,            false)
+DEFINE_FIELD  (NX_ARP_SHA,     FWW_ARP_SHA,  NXM_DL_ARP,    0,            false)
+DEFINE_FIELD  (NX_ARP_THA,     FWW_ARP_THA,  NXM_DL_ARP,    0,            false)
+DEFINE_FIELD_M(NX_IPV6_SRC,    0,            NXM_DL_IPV6,   0,            false)
+DEFINE_FIELD_M(NX_IPV6_DST,    0,            NXM_DL_IPV6,   0,            false)
+DEFINE_FIELD  (NX_ICMPV6_TYPE, FWW_TP_SRC,   NXM_DL_IPV6, IPPROTO_ICMPV6, false)
+DEFINE_FIELD  (NX_ICMPV6_CODE, FWW_TP_DST,   NXM_DL_IPV6, IPPROTO_ICMPV6, false)
+
+DEFINE_FIELD_M(NX_REG0,        0,            NXM_DL_NONE,   0,            true)
 #if FLOW_N_REGS >= 2
-DEFINE_FIELD_M(NX_REG1,      0,            0,            0,            true)
+DEFINE_FIELD_M(NX_REG1,        0,            NXM_DL_NONE,   0,            true)
 #endif
 #if FLOW_N_REGS >= 3
-DEFINE_FIELD_M(NX_REG2,      0,            0,            0,            true)
+DEFINE_FIELD_M(NX_REG2,        0,            NXM_DL_NONE,   0,            true)
 #endif
 #if FLOW_N_REGS >= 4
-DEFINE_FIELD_M(NX_REG3,      0,            0,            0,            true)
+DEFINE_FIELD_M(NX_REG3,        0,            NXM_DL_NONE,   0,            true)
 #endif
 #if FLOW_N_REGS > 4
 #error
index bd4fea27e092b79478d7a50468fe538a2a5865b0..aefcb653bb9a068d681948dcf57162d75ecbc917 100644 (file)
@@ -93,8 +93,8 @@ nxm_decode_n_bits(ovs_be16 ofs_nbits)
  *  NXM_OF_VLAN_TCI     4       2     2      8
  *  NXM_OF_IP_TOS       4       1    --      5
  *  NXM_OF_IP_PROTO     4       2    --      6
- *  NXM_OF_IP_SRC_W     4       4     4     12
- *  NXM_OF_IP_DST_W     4       4     4     12
+ *  NXM_OF_IPV6_SRC_W   4      16    16     36
+ *  NXM_OF_IPV6_DST_W   4      16    16     36
  *  NXM_OF_TCP_SRC      4       2    --      6
  *  NXM_OF_TCP_DST      4       2    --      6
  *  NXM_NX_REG_W(0)     4       4     4     12
@@ -103,11 +103,11 @@ nxm_decode_n_bits(ovs_be16 ofs_nbits)
  *  NXM_NX_REG_W(3)     4       4     4     12
  *  NXM_NX_TUN_ID_W     4       8     8     20
  *  -------------------------------------------
- *  total                                  161
+ *  total                                  209
  *
  * So this value is conservative.
  */
-#define NXM_MAX_LEN 192
+#define NXM_MAX_LEN 256
 
 /* This is my guess at the length of a "typical" nx_match, for use in
  * predicting space requirements. */
index bacb1c04908342456d466b1aac800071d82b236d..e7acaad85ffc075ac3e5764ea51c403c0ad2c8be 100644 (file)
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <arpa/inet.h>
 #include <config.h>
 #include "odp-util.h"
 #include <errno.h>
@@ -194,9 +195,11 @@ odp_flow_key_attr_len(uint16_t type)
     case ODP_KEY_ATTR_8021Q: return sizeof(struct odp_key_8021q);
     case ODP_KEY_ATTR_ETHERTYPE: return 2;
     case ODP_KEY_ATTR_IPV4: return sizeof(struct odp_key_ipv4);
+    case ODP_KEY_ATTR_IPV6: return sizeof(struct odp_key_ipv6);
     case ODP_KEY_ATTR_TCP: return sizeof(struct odp_key_tcp);
     case ODP_KEY_ATTR_UDP: return sizeof(struct odp_key_udp);
     case ODP_KEY_ATTR_ICMP: return sizeof(struct odp_key_icmp);
+    case ODP_KEY_ATTR_ICMPV6: return sizeof(struct odp_key_icmpv6);
     case ODP_KEY_ATTR_ARP: return sizeof(struct odp_key_arp);
 
     case ODP_KEY_ATTR_UNSPEC:
@@ -233,9 +236,11 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds)
     const struct odp_key_ethernet *eth_key;
     const struct odp_key_8021q *q_key;
     const struct odp_key_ipv4 *ipv4_key;
+    const struct odp_key_ipv6 *ipv6_key;
     const struct odp_key_tcp *tcp_key;
     const struct odp_key_udp *udp_key;
     const struct odp_key_icmp *icmp_key;
+    const struct odp_key_icmpv6 *icmpv6_key;
     const struct odp_key_arp *arp_key;
 
     if (nl_attr_get_size(a) != odp_flow_key_attr_len(nl_attr_type(a))) {
@@ -287,6 +292,20 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds)
                       ipv4_key->ipv4_proto, ipv4_key->ipv4_tos);
         break;
 
+    case ODP_KEY_ATTR_IPV6: {
+        char src_str[INET6_ADDRSTRLEN];
+        char dst_str[INET6_ADDRSTRLEN];
+
+        ipv6_key = nl_attr_get(a);
+        inet_ntop(AF_INET6, ipv6_key->ipv6_src, src_str, sizeof src_str);
+        inet_ntop(AF_INET6, ipv6_key->ipv6_dst, dst_str, sizeof dst_str);
+
+        ds_put_format(ds, "ipv6(src=%s,dst=%s,proto=%"PRId8",tos=%"PRIu8")",
+                      src_str, dst_str, ipv6_key->ipv6_proto,
+                      ipv6_key->ipv6_tos);
+        break;
+    }
+
     case ODP_KEY_ATTR_TCP:
         tcp_key = nl_attr_get(a);
         ds_put_format(ds, "tcp(src=%"PRIu16",dst=%"PRIu16")",
@@ -305,6 +324,12 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds)
                       icmp_key->icmp_type, icmp_key->icmp_code);
         break;
 
+    case ODP_KEY_ATTR_ICMPV6:
+        icmpv6_key = nl_attr_get(a);
+        ds_put_format(ds, "icmpv6(type=%"PRIu8",code=%"PRIu8")",
+                      icmpv6_key->icmpv6_type, icmpv6_key->icmpv6_code);
+        break;
+
     case ODP_KEY_ATTR_ARP:
         arp_key = nl_attr_get(a);
         ds_put_format(ds, "arp(sip="IP_FMT",tip="IP_FMT",op=%"PRIu16","
@@ -387,6 +412,29 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow)
         ipv4_key->ipv4_dst = flow->nw_dst;
         ipv4_key->ipv4_proto = flow->nw_proto;
         ipv4_key->ipv4_tos = flow->nw_tos;
+    } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
+        struct odp_key_ipv6 *ipv6_key;
+
+        ipv6_key = nl_msg_put_unspec_uninit(buf, ODP_KEY_ATTR_IPV6,
+                                            sizeof *ipv6_key);
+        memcpy(ipv6_key->ipv6_src, &flow->ipv6_src, sizeof ipv6_key->ipv6_src);
+        memcpy(ipv6_key->ipv6_dst, &flow->ipv6_dst, sizeof ipv6_key->ipv6_dst);
+        ipv6_key->ipv6_proto = flow->nw_proto;
+        ipv6_key->ipv6_tos = flow->nw_tos;
+    } else if (flow->dl_type == htons(ETH_TYPE_ARP)) {
+        struct odp_key_arp *arp_key;
+
+        arp_key = nl_msg_put_unspec_uninit(buf, ODP_KEY_ATTR_ARP,
+                                           sizeof *arp_key);
+        arp_key->arp_sip = flow->nw_src;
+        arp_key->arp_tip = flow->nw_dst;
+        arp_key->arp_op = htons(flow->nw_proto);
+        memcpy(arp_key->arp_sha, flow->arp_sha, ETH_ADDR_LEN);
+        memcpy(arp_key->arp_tha, flow->arp_tha, ETH_ADDR_LEN);
+    }
+    
+    if (flow->dl_type == htons(ETH_TYPE_IP)
+            || flow->dl_type == htons(ETH_TYPE_IPV6)) {
 
         if (flow->nw_proto == IPPROTO_TCP) {
             struct odp_key_tcp *tcp_key;
@@ -402,24 +450,23 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow)
                                                sizeof *udp_key);
             udp_key->udp_src = flow->tp_src;
             udp_key->udp_dst = flow->tp_dst;
-        } else if (flow->nw_proto == IPPROTO_ICMP) {
+        } else if (flow->dl_type == htons(ETH_TYPE_IP)
+                && flow->nw_proto == IPPROTO_ICMP) {
             struct odp_key_icmp *icmp_key;
 
             icmp_key = nl_msg_put_unspec_uninit(buf, ODP_KEY_ATTR_ICMP,
                                                 sizeof *icmp_key);
             icmp_key->icmp_type = ntohs(flow->tp_src);
             icmp_key->icmp_code = ntohs(flow->tp_dst);
+        } else if (flow->dl_type == htons(ETH_TYPE_IPV6)
+                && flow->nw_proto == IPPROTO_ICMPV6) {
+            struct odp_key_icmpv6 *icmpv6_key;
+
+            icmpv6_key = nl_msg_put_unspec_uninit(buf, ODP_KEY_ATTR_ICMPV6,
+                                                  sizeof *icmpv6_key);
+            icmpv6_key->icmpv6_type = ntohs(flow->tp_src);
+            icmpv6_key->icmpv6_code = ntohs(flow->tp_dst);
         }
-    } else if (flow->dl_type == htons(ETH_TYPE_ARP)) {
-        struct odp_key_arp *arp_key;
-
-        arp_key = nl_msg_put_unspec_uninit(buf, ODP_KEY_ATTR_ARP,
-                                           sizeof *arp_key);
-        arp_key->arp_sip = flow->nw_src;
-        arp_key->arp_tip = flow->nw_dst;
-        arp_key->arp_op = htons(flow->nw_proto);
-        memcpy(arp_key->arp_sha, flow->arp_sha, ETH_ADDR_LEN);
-        memcpy(arp_key->arp_tha, flow->arp_tha, ETH_ADDR_LEN);
     }
 }
 
@@ -441,9 +488,11 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len,
         const struct odp_key_ethernet *eth_key;
         const struct odp_key_8021q *q_key;
         const struct odp_key_ipv4 *ipv4_key;
+        const struct odp_key_ipv6 *ipv6_key;
         const struct odp_key_tcp *tcp_key;
         const struct odp_key_udp *udp_key;
         const struct odp_key_icmp *icmp_key;
+        const struct odp_key_icmpv6 *icmpv6_key;
         const struct odp_key_arp *arp_key;
 
         uint16_t type = nl_attr_type(nla);
@@ -507,7 +556,22 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len,
             }
             break;
 
+        case TRANSITION(ODP_KEY_ATTR_ETHERTYPE, ODP_KEY_ATTR_IPV6):
+            if (flow->dl_type != htons(ETH_TYPE_IPV6)) {
+                return EINVAL;
+            }
+            ipv6_key = nl_attr_get(nla);
+            memcpy(&flow->ipv6_src, ipv6_key->ipv6_src, sizeof flow->ipv6_src);
+            memcpy(&flow->ipv6_dst, ipv6_key->ipv6_dst, sizeof flow->ipv6_dst);
+            flow->nw_proto = ipv6_key->ipv6_proto;
+            flow->nw_tos = ipv6_key->ipv6_tos;
+            if (flow->nw_tos & IP_ECN_MASK) {
+                return EINVAL;
+            }
+            break;
+
         case TRANSITION(ODP_KEY_ATTR_IPV4, ODP_KEY_ATTR_TCP):
+        case TRANSITION(ODP_KEY_ATTR_IPV6, ODP_KEY_ATTR_TCP):
             if (flow->nw_proto != IPPROTO_TCP) {
                 return EINVAL;
             }
@@ -517,6 +581,7 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len,
             break;
 
         case TRANSITION(ODP_KEY_ATTR_IPV4, ODP_KEY_ATTR_UDP):
+        case TRANSITION(ODP_KEY_ATTR_IPV6, ODP_KEY_ATTR_UDP):
             if (flow->nw_proto != IPPROTO_UDP) {
                 return EINVAL;
             }
@@ -534,6 +599,15 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len,
             flow->tp_dst = htons(icmp_key->icmp_code);
             break;
 
+        case TRANSITION(ODP_KEY_ATTR_IPV6, ODP_KEY_ATTR_ICMPV6):
+            if (flow->nw_proto != IPPROTO_ICMPV6) {
+                return EINVAL;
+            }
+            icmpv6_key = nl_attr_get(nla);
+            flow->tp_src = htons(icmpv6_key->icmpv6_type);
+            flow->tp_dst = htons(icmpv6_key->icmpv6_code);
+            break;
+
         case TRANSITION(ODP_KEY_ATTR_ETHERTYPE, ODP_KEY_ATTR_ARP):
             if (flow->dl_type != htons(ETH_TYPE_ARP)) {
                 return EINVAL;
@@ -577,6 +651,7 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len,
 
     case ODP_KEY_ATTR_ETHERTYPE:
         if (flow->dl_type == htons(ETH_TYPE_IP)
+            || flow->dl_type == htons(ETH_TYPE_IPV6)
             || flow->dl_type == htons(ETH_TYPE_ARP)) {
             return EINVAL;
         }
@@ -590,9 +665,18 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len,
         }
         return 0;
 
+    case ODP_KEY_ATTR_IPV6:
+        if (flow->nw_proto == IPPROTO_TCP
+            || flow->nw_proto == IPPROTO_UDP
+            || flow->nw_proto == IPPROTO_ICMPV6) {
+            return EINVAL;
+        }
+        return 0;
+
     case ODP_KEY_ATTR_TCP:
     case ODP_KEY_ATTR_UDP:
     case ODP_KEY_ATTR_ICMP:
+    case ODP_KEY_ATTR_ICMPV6:
     case ODP_KEY_ATTR_ARP:
         return 0;
 
index 1a0d58df62eae484dabae3520536b23069a44867..8ec09f3cf1430029bea8cfe414e8b1f18f3b3470 100644 (file)
@@ -64,13 +64,13 @@ void format_odp_actions(struct ds *, const struct nlattr *odp_actions,
                         size_t actions_len);
 
 /* By my calculations currently the longest valid nlattr-formatted flow key is
- * 80 bytes long, so this leaves some safety margin.
+ * 92 bytes long, so this leaves some safety margin.
  *
  * We allocate temporary on-stack buffers for flow keys as arrays of uint32_t
  * to ensure proper 32-bit alignment for Netlink attributes.  (An array of
  * "struct nlattr" might not, in theory, be sufficiently aligned because it
  * only contains 16-bit types.) */
-#define ODPUTIL_FLOW_KEY_BYTES 96
+#define ODPUTIL_FLOW_KEY_BYTES 112
 #define ODPUTIL_FLOW_KEY_U32S DIV_ROUND_UP(ODPUTIL_FLOW_KEY_BYTES, 4)
 
 void odp_flow_key_format(const struct nlattr *, size_t, struct ds *);
index 366664e304c2020bba2333762c652acb463c34bd..e77453e06b11f7ecbb365509c0f1b856734fc5d1 100644 (file)
@@ -157,6 +157,46 @@ error:
     ovs_fatal(0, "%s: bad syntax for tunnel id", str);
 }
 
+static void
+str_to_ipv6(const char *str_, struct in6_addr *addrp, struct in6_addr *maskp)
+{
+    char *str = xstrdup(str_);
+    char *save_ptr = NULL;
+    const char *name, *netmask;
+    struct in6_addr addr, mask;
+    int retval;
+
+    name = strtok_r(str, "/", &save_ptr);
+    retval = name ? lookup_ipv6(name, &addr) : EINVAL;
+    if (retval) {
+        ovs_fatal(0, "%s: could not convert to IPv6 address", str);
+    }
+
+    netmask = strtok_r(NULL, "/", &save_ptr);
+    if (netmask) {
+        int prefix = atoi(netmask);
+        if (prefix <= 0 || prefix > 128) {
+            ovs_fatal(0, "%s: network prefix bits not between 1 and 128",
+                      str);
+        } else {
+            mask = ipv6_create_mask(prefix);
+        }
+    } else {
+        mask = in6addr_exact;
+    }
+    *addrp = ipv6_addr_bitand(&addr, &mask);
+
+    if (maskp) {
+        *maskp = mask;
+    } else {
+        if (!ipv6_mask_is_exact(&mask)) {
+            ovs_fatal(0, "%s: netmask not allowed here", str_);
+        }
+    }
+
+    free(str);
+}
+
 static void *
 put_action(struct ofpbuf *b, size_t size, uint16_t type)
 {
@@ -463,6 +503,11 @@ parse_protocol(const char *name, const struct protocol **p_out)
         { "icmp", ETH_TYPE_IP, IPPROTO_ICMP },
         { "tcp", ETH_TYPE_IP, IPPROTO_TCP },
         { "udp", ETH_TYPE_IP, IPPROTO_UDP },
+        { "ipv6", ETH_TYPE_IPV6, 0 },
+        { "ip6", ETH_TYPE_IPV6, 0 },
+        { "icmp6", ETH_TYPE_IPV6, IPPROTO_ICMPV6 },
+        { "tcp6", ETH_TYPE_IPV6, IPPROTO_TCP },
+        { "udp6", ETH_TYPE_IPV6, IPPROTO_UDP },
     };
     const struct protocol *p;
 
@@ -493,7 +538,9 @@ parse_protocol(const char *name, const struct protocol **p_out)
     FIELD(F_ICMP_TYPE,   "icmp_type",   FWW_TP_SRC)         \
     FIELD(F_ICMP_CODE,   "icmp_code",   FWW_TP_DST)         \
     FIELD(F_ARP_SHA,     "arp_sha",     FWW_ARP_SHA)        \
-    FIELD(F_ARP_THA,     "arp_tha",     FWW_ARP_THA)
+    FIELD(F_ARP_THA,     "arp_tha",     FWW_ARP_THA)        \
+    FIELD(F_IPV6_SRC,    "ipv6_src",    0)                  \
+    FIELD(F_IPV6_DST,    "ipv6_dst",    0)
 
 enum field_index {
 #define FIELD(ENUM, NAME, WILDCARD) ENUM,
@@ -535,6 +582,7 @@ parse_field_value(struct cls_rule *rule, enum field_index index,
     uint8_t mac[ETH_ADDR_LEN];
     ovs_be64 tun_id, tun_mask;
     ovs_be32 ip, mask;
+    struct in6_addr ipv6, ipv6_mask;
     uint16_t port_no;
 
     switch (index) {
@@ -619,6 +667,16 @@ parse_field_value(struct cls_rule *rule, enum field_index index,
         cls_rule_set_arp_tha(rule, mac);
         break;
 
+    case F_IPV6_SRC:
+        str_to_ipv6(value, &ipv6, &ipv6_mask);
+        cls_rule_set_ipv6_src_masked(rule, &ipv6, &ipv6_mask);
+        break;
+
+    case F_IPV6_DST:
+        str_to_ipv6(value, &ipv6, &ipv6_mask);
+        cls_rule_set_ipv6_dst_masked(rule, &ipv6, &ipv6_mask);
+        break;
+
     case N_FIELDS:
         NOT_REACHED();
     }
@@ -723,6 +781,12 @@ parse_ofp_str(struct flow_mod *fm, uint8_t *table_idx,
                         cls_rule_set_nw_src_masked(&fm->cr, 0, 0);
                     } else if (f->index == F_NW_DST) {
                         cls_rule_set_nw_dst_masked(&fm->cr, 0, 0);
+                    } else if (f->index == F_IPV6_SRC) {
+                        cls_rule_set_ipv6_src_masked(&fm->cr,
+                                &in6addr_any, &in6addr_any);
+                    } else if (f->index == F_IPV6_DST) {
+                        cls_rule_set_ipv6_dst_masked(&fm->cr,
+                                &in6addr_any, &in6addr_any);
                     } else if (f->index == F_DL_VLAN) {
                         cls_rule_set_any_vid(&fm->cr);
                     } else if (f->index == F_DL_VLAN_PCP) {
index 59a5fc43f29be903064327cafe272e921da6d04e..4d89e0ae4af2334206b22642b4794978fd6eeeac 100644 (file)
@@ -868,6 +868,12 @@ is_nxm_required(const struct cls_rule *rule, bool cookie_support,
         return true;
     }
 
+    /* Only NXM supports matching IPv6 traffic. */
+    if (!(wc->wildcards & FWW_DL_TYPE)
+            && (rule->flow.dl_type == htons(ETH_TYPE_IPV6))) {
+        return true;
+    }
+
     /* Only NXM supports matching registers. */
     if (!regs_fully_wildcarded(wc)) {
         return true;
@@ -2041,6 +2047,9 @@ normalize_match(struct ofp_match *m)
             m->nw_dst &= ofputil_wcbits_to_netmask(wc >> OFPFW_NW_DST_SHIFT);
         }
         m->tp_src = m->tp_dst = m->nw_tos = 0;
+    } else if (m->dl_type == htons(ETH_TYPE_IPV6)) {
+        /* Don't normalize IPv6 traffic, since OpenFlow doesn't have a
+         * way to express it. */
     } else {
         /* Network and transport layer fields will always be extracted as
          * zeros, so we can do an exact-match on those values. */
index 2dc82fe12e0d138cf821218fd7633cbfa24f58a4..05148fe0f3c9a9e670a0eb71f4282bdb100a5dda 100644 (file)
 
 #include <config.h>
 #include "packets.h"
+#include <assert.h>
+#include <arpa/inet.h>
 #include <netinet/in.h>
 #include <stdlib.h>
+#include "byte-order.h"
+#include "dynamic-string.h"
 #include "ofpbuf.h"
 
+const struct in6_addr in6addr_exact = IN6ADDR_EXACT_INIT;
+
 /* Parses 's' as a 16-digit hexadecimal number representing a datapath ID.  On
  * success stores the dpid into '*dpidp' and returns true, on failure stores 0
  * into '*dpidp' and returns false.
@@ -83,3 +89,117 @@ compose_benign_packet(struct ofpbuf *b, const char *tag, uint16_t snap_type,
     memcpy(llc_snap->snap.snap_org, "\x00\x23\x20", 3);
     llc_snap->snap.snap_type = htons(snap_type);
 }
+
+/* Stores the string representation of the IPv6 address 'addr' into the
+ * character array 'addr_str', which must be at least INET6_ADDRSTRLEN
+ * bytes long. */
+void
+format_ipv6_addr(char *addr_str, const struct in6_addr *addr)
+{
+    inet_ntop(AF_INET6, addr, addr_str, INET6_ADDRSTRLEN);
+}
+
+void
+print_ipv6_addr(struct ds *string, const struct in6_addr *addr)
+{
+    char addr_str[INET6_ADDRSTRLEN];
+
+    format_ipv6_addr(addr_str, addr);
+    ds_put_format(string, "%s", addr_str);
+}
+
+struct in6_addr ipv6_addr_bitand(const struct in6_addr *a,
+                                 const struct in6_addr *b)
+{
+    int i;
+    struct in6_addr dst;
+
+#ifdef s6_addr32
+    for (i=0; i<4; i++) {
+        dst.s6_addr32[i] = a->s6_addr32[i] & b->s6_addr32[i];
+    }
+#else
+    for (i=0; i<16; i++) {
+        dst.s6_addr[i] = a->s6_addr[i] & b->s6_addr[i];
+    }
+#endif
+
+    return dst;
+}
+
+/* Returns an in6_addr consisting of 'mask' high-order 1-bits and 128-N
+ * low-order 0-bits. */
+struct in6_addr
+ipv6_create_mask(int mask)
+{
+    struct in6_addr netmask;
+    uint8_t *netmaskp = &netmask.s6_addr[0];
+
+    memset(&netmask, 0, sizeof netmask);
+    while (mask > 8) {
+        *netmaskp = 0xff;
+        netmaskp++;
+        mask -= 8;
+    }
+
+    if (mask) {
+        *netmaskp = 0xff << (8 - mask);
+    }
+
+    return netmask;
+}
+
+/* Given the IPv6 netmask 'netmask', returns the number of bits of the
+ * IPv6 address that it wildcards.  'netmask' must be a CIDR netmask (see
+ * ipv6_is_cidr()). */
+int
+ipv6_count_cidr_bits(const struct in6_addr *netmask)
+{
+    int i;
+    int count = 0;
+    const uint8_t *netmaskp = &netmask->s6_addr[0];
+
+    assert(ipv6_is_cidr(netmask));
+
+    for (i=0; i<16; i++) {
+        if (netmaskp[i] == 0xff) {
+            count += 8;
+        } else {
+            uint8_t nm;
+
+            for(nm = netmaskp[i]; nm; nm <<= 1) {
+                count++;
+            }
+            break;
+        }
+
+    }
+
+    return count;
+}
+
+
+/* Returns true if 'netmask' is a CIDR netmask, that is, if it consists of N
+ * high-order 1-bits and 128-N low-order 0-bits. */
+bool
+ipv6_is_cidr(const struct in6_addr *netmask)
+{
+    const uint8_t *netmaskp = &netmask->s6_addr[0];
+    int i;
+
+    for (i=0; i<16; i++) {
+        if (netmaskp[i] != 0xff) {
+            uint8_t x = ~netmaskp[i];
+            if (x & (x + 1)) {
+                return false;
+            }
+            while (++i < 16) {
+                if (netmaskp[i]) {
+                    return false;
+                }
+            }
+        }
+    }
+
+    return true;
+}
index c0480db7ec73c580f118c46c2bda286135856c33..51121108deac5e572263c5166860db198b62ab8f 100644 (file)
@@ -28,6 +28,7 @@
 #include "util.h"
 
 struct ofpbuf;
+struct ds;
 
 bool dpid_from_string(const char *s, uint64_t *dpidp);
 
@@ -153,6 +154,7 @@ void compose_benign_packet(struct ofpbuf *, const char *tag,
 #define ETH_TYPE_IP            0x0800
 #define ETH_TYPE_ARP           0x0806
 #define ETH_TYPE_VLAN          0x8100
+#define ETH_TYPE_IPV6          0x86dd
 #define ETH_TYPE_CFM           0x8902
 
 /* Minimum value for an Ethernet type.  Values below this are IEEE 802.2 frame
@@ -371,4 +373,34 @@ struct arp_eth_header {
 } __attribute__((packed));
 BUILD_ASSERT_DECL(ARP_ETH_HEADER_LEN == sizeof(struct arp_eth_header));
 
+extern const struct in6_addr in6addr_exact;
+#define IN6ADDR_EXACT_INIT { { { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, \
+                                 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff } } }
+
+static inline bool ipv6_addr_equals(const struct in6_addr *a,
+                                    const struct in6_addr *b)
+{
+#ifdef IN6_ARE_ADDR_EQUAL
+    return IN6_ARE_ADDR_EQUAL(a, b);
+#else
+    return !memcmp(a, b, sizeof(*a));
+#endif
+}
+
+static inline bool ipv6_mask_is_any(const struct in6_addr *mask) {
+    return ipv6_addr_equals(mask, &in6addr_any);
+}
+
+static inline bool ipv6_mask_is_exact(const struct in6_addr *mask) {
+    return ipv6_addr_equals(mask, &in6addr_exact);
+}
+
+void format_ipv6_addr(char *addr_str, const struct in6_addr *addr);
+void print_ipv6_addr(struct ds *string, const struct in6_addr *addr);
+struct in6_addr ipv6_addr_bitand(const struct in6_addr *src,
+                                 const struct in6_addr *mask);
+struct in6_addr ipv6_create_mask(int mask);
+int ipv6_count_cidr_bits(const struct in6_addr *netmask);
+bool ipv6_is_cidr(const struct in6_addr *netmask);
+
 #endif /* packets.h */
index e97e7c90ebd037cb4d488e14038f06946ce69e13..469131d4cf4ff8ae294281a1eb46aeb12ef2c606 100644 (file)
@@ -121,6 +121,20 @@ lookup_ip(const char *host_name, struct in_addr *addr)
     return 0;
 }
 
+/* Translates 'host_name', which must be a string representation of an IPv6
+ * address, into a numeric IPv6 address in '*addr'.  Returns 0 if successful,
+ * otherwise a positive errno value. */
+int
+lookup_ipv6(const char *host_name, struct in6_addr *addr)
+{
+    if (inet_pton(AF_INET6, host_name, addr) != 1) {
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+        VLOG_ERR_RL(&rl, "\"%s\" is not a valid IPv6 address", host_name);
+        return ENOENT;
+    }
+    return 0;
+}
+
 /* Returns the error condition associated with socket 'fd' and resets the
  * socket's error status. */
 int
index 40a96144d9b21e7fd411c72be4da58bf129a1fc1..f4e617a6561fcecbad5b142456351314d540c937 100644 (file)
@@ -24,6 +24,7 @@
 int set_nonblocking(int fd);
 int get_max_fds(void);
 int lookup_ip(const char *host_name, struct in_addr *address);
+int lookup_ipv6(const char *host_name, struct in6_addr *address);
 int get_socket_error(int sock);
 int check_connection_completion(int fd);
 int drain_rcvbuf(int fd);
index 7eecf2879d7328a8967a6ef3f27b4e974ff9fe68..a86588b9fc9b49562a3428675453f2b446c0e0ba 100644 (file)
@@ -62,6 +62,12 @@ tcp,tp_src=123,actions=flood
 in_port=LOCAL dl_vlan=9 dl_src=00:0A:E4:25:6B:B0 actions=drop
 arp,nw_src=192.168.0.1 actions=drop_spoofed_arp,NORMAL
 arp,dl_src=00:0A:E4:25:6B:B0,arp_sha=00:0A:E4:25:6B:B0 actions=drop
+ipv6,ipv6_src=2001:db8:3c4d:1:2:3:4:5 actions=3
+ipv6,ipv6_src=2001:db8:3c4d:1:2:3:4:5/64 actions=4
+ipv6,ipv6_dst=2001:db8:3c4d:1:2:3:4:5/127 actions=5
+tcp6,ipv6_src=2001:db8:3c4d:1::1,tp_dst=80 actions=drop 
+udp6,ipv6_src=2001:db8:3c4d:1::3,tp_dst=53 actions=drop 
+in_port=3 icmp6,ipv6_src=2001:db8:3c4d:1::1,icmp_type=134 actions=drop
 udp dl_vlan_pcp=7 idle_timeout=5 actions=strip_vlan output:0
 tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1
 udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1
@@ -77,6 +83,12 @@ NXT_FLOW_MOD: ADD tcp,tp_src=123 actions=FLOOD
 NXT_FLOW_MOD: ADD in_port=65534,dl_vlan=9,dl_src=00:0a:e4:25:6b:b0 actions=drop
 NXT_FLOW_MOD: ADD arp,nw_src=192.168.0.1 actions=drop_spoofed_arp,NORMAL
 NXT_FLOW_MOD: ADD arp,dl_src=00:0a:e4:25:6b:b0,arp_sha=00:0a:e4:25:6b:b0 actions=drop
+NXT_FLOW_MOD: ADD ipv6,ipv6_src=2001:db8:3c4d:1:2:3:4:5 actions=output:3
+NXT_FLOW_MOD: ADD ipv6,ipv6_src=2001:db8:3c4d:1::/64 actions=output:4
+NXT_FLOW_MOD: ADD ipv6,ipv6_dst=2001:db8:3c4d:1:2:3:4:4/127 actions=output:5
+NXT_FLOW_MOD: ADD tcp6,ipv6_src=2001:db8:3c4d:1::1,tp_dst=80 actions=drop
+NXT_FLOW_MOD: ADD udp6,ipv6_src=2001:db8:3c4d:1::3,tp_dst=53 actions=drop
+NXT_FLOW_MOD: ADD icmp6,in_port=3,ipv6_src=2001:db8:3c4d:1::1,icmp_type=134 actions=drop
 NXT_FLOW_MOD: ADD udp,dl_vlan_pcp=7 idle:5 actions=strip_vlan,output:0
 NXT_FLOW_MOD: ADD tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1
 NXT_FLOW_MOD: ADD udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1
@@ -95,6 +107,12 @@ tcp,tp_src=123,actions=flood
 in_port=LOCAL dl_vlan=9 dl_src=00:0A:E4:25:6B:B0 actions=drop
 arp,nw_src=192.168.0.1 actions=drop_spoofed_arp,NORMAL
 arp,dl_src=00:0A:E4:25:6B:B0,arp_sha=00:0A:E4:25:6B:B0 actions=drop
+ipv6,ipv6_src=2001:db8:3c4d:1:2:3:4:5 actions=3
+ipv6,ipv6_src=2001:db8:3c4d:1:2:3:4:5/64 actions=4
+ipv6,ipv6_dst=2001:db8:3c4d:1:2:3:4:5/127 actions=5
+tcp6,ipv6_src=2001:db8:3c4d:1::1,tp_dst=80 actions=drop 
+udp6,ipv6_src=2001:db8:3c4d:1::3,tp_dst=53 actions=drop 
+in_port=3 icmp6,ipv6_src=2001:db8:3c4d:1::1,icmp_type=134 actions=drop
 udp dl_vlan_pcp=7 idle_timeout=5 actions=strip_vlan output:0
 tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1
 udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1
@@ -110,6 +128,12 @@ AT_CHECK([[sed 's/ (xid=0x[0-9a-fA-F]*)//' stdout]], [0],
 NXT_FLOW_MOD: ADD NXM_OF_IN_PORT(fffe), NXM_OF_ETH_SRC(000ae4256bb0), NXM_OF_VLAN_TCI_W(1009/1fff) actions=drop
 NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_SPA(c0a80001) actions=drop_spoofed_arp,NORMAL
 NXT_FLOW_MOD: ADD NXM_OF_ETH_SRC(000ae4256bb0), NXM_OF_ETH_TYPE(0806), NXM_NX_ARP_SHA(000ae4256bb0) actions=drop
+NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_SRC(20010db83c4d00010002000300040005) actions=output:3
+NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_SRC_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000) actions=output:4
+NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_DST_W(20010db83c4d00010002000300040004/fffffffffffffffffffffffffffffffe) actions=output:5
+NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_SRC(20010db83c4d00010000000000000001), NXM_OF_IP_PROTO(06), NXM_OF_TCP_DST(0050) actions=drop
+NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_SRC(20010db83c4d00010000000000000003), NXM_OF_IP_PROTO(11), NXM_OF_UDP_DST(0035) actions=drop
+NXT_FLOW_MOD: ADD NXM_OF_IN_PORT(0003), NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_SRC(20010db83c4d00010000000000000001), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(86) actions=drop
 NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800), NXM_OF_VLAN_TCI_W(f000/f000), NXM_OF_IP_PROTO(11) idle:5 actions=strip_vlan,output:0
 NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800), NXM_OF_IP_SRC(c0a80003), NXM_OF_IP_PROTO(06), NXM_OF_TCP_DST(0050) actions=set_queue:37,output:1
 NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800), NXM_OF_IP_SRC(c0a80003), NXM_OF_IP_PROTO(11), NXM_OF_UDP_DST(0035) actions=pop_queue,output:1
@@ -231,6 +255,18 @@ NXM_OF_ETH_TYPE(0806) NXM_NX_ARP_THA(0002e30f80a4)
 NXM_OF_ETH_TYPE(0800) NXM_NX_ARP_THA(0002e30f80a4)
 NXM_NX_ARP_THA(0002e30f80a4)
 
+# IPv6 source
+NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_SRC(20010db83c4d00010002000300040005)
+NXM_OF_ETH_TYPE(0800) NXM_NX_IPV6_SRC(20010db83c4d00010002000300040005)
+NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_SRC_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
+NXM_OF_ETH_TYPE(0800) NXM_NX_IPV6_SRC_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
+
+# IPv6 destination
+NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_DST(20010db83c4d00010002000300040005)
+NXM_OF_ETH_TYPE(0800) NXM_NX_IPV6_DST(20010db83c4d00010002000300040005)
+NXM_OF_ETH_TYPE(86dd) NXM_NX_IPV6_DST_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
+NXM_OF_ETH_TYPE(0800) NXM_NX_IPV6_DST_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
+
 # Tunnel ID.
 NXM_NX_TUN_ID(00000000abcdef01)
 NXM_NX_TUN_ID_W(84200000abcdef01/84200000FFFFFFFF)
@@ -359,6 +395,18 @@ NXM_OF_ETH_TYPE(0806), NXM_NX_ARP_THA(0002e30f80a4)
 nx_pull_match() returned error 44010104
 nx_pull_match() returned error 44010104
 
+# IPv6 source
+NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_SRC(20010db83c4d00010002000300040005)
+nx_pull_match() returned error 44010104
+NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_SRC_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
+nx_pull_match() returned error 44010104
+
+# IPv6 destination
+NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_DST(20010db83c4d00010002000300040005)
+nx_pull_match() returned error 44010104
+NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_DST_W(20010db83c4d00010000000000000000/ffffffffffffffff0000000000000000)
+nx_pull_match() returned error 44010104
+
 # Tunnel ID.
 NXM_NX_TUN_ID(00000000abcdef01)
 NXM_NX_TUN_ID_W(84200000abcdef01/84200000ffffffff)
index 464a8eb71ad3c8408fa5b87fa704d551f2512328..dda4797f56288cdff8fa75556bc6c92ebc6ab5d4 100644 (file)
@@ -39,10 +39,126 @@ test_ipv4_cidr(void)
     assert(!ip_is_cidr(htonl(0xffffffd0)));
 }
 
+static void
+test_ipv6_static_masks(void)
+{
+    /* The 'exact' and 'any' addresses should be identical to
+     * 'in6addr_exact' and  'in6addr_any' definitions, but we redefine
+     * them here since the pre-defined ones are used in the functions
+     * we're testing. */
+    struct in6_addr exact   = {{{ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, \
+                                  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff }}};
+
+    struct in6_addr any     = {{{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, \
+                                  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }}};
+
+    struct in6_addr neither = {{{ 0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef, \
+                                  0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef }}};
+
+    assert(ipv6_mask_is_exact(&exact));
+    assert(!ipv6_mask_is_exact(&any));
+    assert(!ipv6_mask_is_exact(&neither));
+
+    assert(!ipv6_mask_is_any(&exact));
+    assert(ipv6_mask_is_any(&any));
+    assert(!ipv6_mask_is_any(&neither));
+
+}
+
+static void
+test_ipv6_cidr(void)
+{
+    struct in6_addr dest;
+
+    struct in6_addr src   = {{{ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, \
+                                0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }}};
+
+    dest = ipv6_create_mask(0);
+    assert(ipv6_mask_is_any(&dest));
+    assert(ipv6_count_cidr_bits(&dest) == 0);
+    assert(ipv6_is_cidr(&dest));
+
+    dest = ipv6_create_mask(128);
+    assert(ipv6_mask_is_exact(&dest));
+    assert(ipv6_count_cidr_bits(&dest) == 128);
+    assert(ipv6_is_cidr(&dest));
+
+    dest = ipv6_create_mask(1);
+    assert(ipv6_count_cidr_bits(&dest) == 1);
+    assert(ipv6_is_cidr(&dest));
+
+    dest = ipv6_create_mask(13);
+    assert(ipv6_count_cidr_bits(&dest) == 13);
+    assert(ipv6_is_cidr(&dest));
+
+    dest = ipv6_create_mask(64);
+    assert(ipv6_count_cidr_bits(&dest) == 64);
+    assert(ipv6_is_cidr(&dest));
+
+    dest = ipv6_create_mask(95);
+    assert(ipv6_count_cidr_bits(&dest) == 95);
+    assert(ipv6_is_cidr(&dest));
+
+    dest = ipv6_create_mask(96);
+    assert(ipv6_count_cidr_bits(&dest) == 96);
+    assert(ipv6_is_cidr(&dest));
+
+    dest = ipv6_create_mask(97);
+    assert(ipv6_count_cidr_bits(&dest) == 97);
+    assert(ipv6_is_cidr(&dest));
+
+    dest = ipv6_create_mask(127);
+    assert(ipv6_count_cidr_bits(&dest) == 127);
+    assert(ipv6_is_cidr(&dest));
+
+    src.s6_addr[8] = 0xf0;
+    assert(ipv6_is_cidr(&src));
+    assert(ipv6_count_cidr_bits(&src) == 68);
+
+    src.s6_addr[15] = 0x01;
+    assert(!ipv6_is_cidr(&src));
+    src.s6_addr[15] = 0x00;
+    assert(ipv6_is_cidr(&src));
+
+    src.s6_addr[8] = 0x0f;
+    assert(!ipv6_is_cidr(&src));
+}
+
+
+static void
+test_ipv6_masking(void)
+{
+    struct in6_addr dest;
+    struct in6_addr mask;
+
+    mask = ipv6_create_mask(0);
+    dest = ipv6_addr_bitand(&in6addr_exact, &mask);
+    assert(ipv6_count_cidr_bits(&dest) == 0);
+
+    mask = ipv6_create_mask(1);
+    dest = ipv6_addr_bitand(&in6addr_exact, &mask);
+    assert(ipv6_count_cidr_bits(&dest) == 1);
+
+    mask = ipv6_create_mask(13);
+    dest = ipv6_addr_bitand(&in6addr_exact, &mask);
+    assert(ipv6_count_cidr_bits(&dest) == 13);
+
+    mask = ipv6_create_mask(127);
+    dest = ipv6_addr_bitand(&in6addr_exact, &mask);
+    assert(ipv6_count_cidr_bits(&dest) == 127);
+
+    mask = ipv6_create_mask(128);
+    dest = ipv6_addr_bitand(&in6addr_exact, &mask);
+    assert(ipv6_count_cidr_bits(&dest) == 128);
+}
+
 int
 main(void)
 {
     test_ipv4_cidr();
+    test_ipv6_static_masks();
+    test_ipv6_cidr();
+    test_ipv6_masking();
 
     return 0;
 }
index 135e705c20df7e6ef91e542d87f1d682c57e2ddd..37425529eaec227c48bb721ac01c1e8b7604143d 100644 (file)
@@ -299,24 +299,31 @@ or 0x0806, the values of \fBnw_src\fR and \fBnw_dst\fR are ignored
 .IP \fBnw_proto=\fIproto\fR
 When \fBip\fR or \fBdl_type=0x0800\fR is specified, matches IP
 protocol type \fIproto\fR, which is specified as a decimal number
-between 0 and 255, inclusive (e.g. 6 to match TCP packets).
+between 0 and 255, inclusive (e.g. 1 to match ICMP packets or 6 to match
+TCP packets).
+.IP
+When \fBipv6\fR or \fBdl_type=0x86dd\fR is specified, matches IPv6
+header type \fIproto\fR, which is specified as a decimal number between
+0 and 255, inclusive (e.g. 58 to match ICMPv6 packets or 6 to match
+TCP).  The header type is the terminal header as described in the
+\fBDESIGN\fR document.
 .IP
 When \fBarp\fR or \fBdl_type=0x0806\fR is specified, matches the lower
 8 bits of the ARP opcode.  ARP opcodes greater than 255 are treated as
 0.
 .IP
-When \fBdl_type\fR is wildcarded or set to a value other than 0x0800
-or 0x0806, the value of \fBnw_proto\fR is ignored (see \fBFlow
+When \fBdl_type\fR is wildcarded or set to a value other than 0x0800,
+0x0806, or 0x86dd, the value of \fBnw_proto\fR is ignored (see \fBFlow
 Syntax\fR above).
 .
 .IP \fBnw_tos=\fItos\fR
-Matches IP ToS/DSCP field \fItos\fR, which is specified as a decimal 
-number between 0 and 255, inclusive.  Note that the two lower reserved
-bits are ignored for matching purposes.
+Matches IP ToS/DSCP or IPv6 traffic class field \fItos\fR, which is
+specified as a decimal number between 0 and 255, inclusive.  Note that
+the two lower reserved bits are ignored for matching purposes.
 .IP
-The value of \fBnw_tos\fR is ignored unless \fBdl_type=0x0800\fR,
-\fBip\fR, \fBicmp\fR, \fBtcp\fR, or \fBudp\fR is also specified (see
-\fBFlow Syntax\fR above).
+When \fBdl_type\fR is wildcarded or set to a value other than 0x0800,
+0x0806, or 0x86dd, the value of \fBnw_tos\fR is ignored (see \fBFlow
+Syntax\fR above).
 .
 .IP \fBtp_src=\fIport\fR
 .IQ \fBtp_dst=\fIport\fR
@@ -331,14 +338,32 @@ these settings are ignored (see \fBFlow Syntax\fR above).
 .
 .IP \fBicmp_type=\fItype\fR
 .IQ \fBicmp_code=\fIcode\fR
-When \fBdl_type\fR and \fBnw_proto\fR specify ICMP, \fItype\fR matches
-the ICMP type and \fIcode\fR matches the ICMP code.  Each is specified
-as a decimal number between 0 and 255, inclusive.
+When \fBdl_type\fR and \fBnw_proto\fR specify ICMP or ICMPv6, \fItype\fR
+matches the ICMP type and \fIcode\fR matches the ICMP code.  Each is
+specified as a decimal number between 0 and 255, inclusive.
 .IP
 When \fBdl_type\fR and \fBnw_proto\fR take other values, the values of
 these settings are ignored (see \fBFlow Syntax\fR above).
 .
 .PP
+The following shorthand notations are also available:
+.
+.IP \fBip\fR
+Same as \fBdl_type=0x0800\fR.
+.
+.IP \fBicmp\fR
+Same as \fBdl_type=0x0800,nw_proto=1\fR.
+.
+.IP \fBtcp\fR
+Same as \fBdl_type=0x0800,nw_proto=6\fR.
+.
+.IP \fBudp\fR
+Same as \fBdl_type=0x0800,nw_proto=17\fR.
+.
+.IP \fBarp\fR
+Same as \fBdl_type=0x0806\fR.
+.
+.PP
 The following field assignments require support for the NXM (Nicira
 Extended Match) extension to OpenFlow.  When one of these is specified,
 \fBovs\-ofctl\fR will automatically attempt to negotiate use of this
@@ -351,6 +376,18 @@ When \fBdl_type\fR specifies ARP, \fBarp_sha\fR and \fBarp_tha\fR match
 the source and target hardware address, respectively.  An address is
 specified as 6 pairs of hexadecimal digits delimited by colons.
 .
+.IP \fBipv6_src=\fIipv6\fR[\fB/\fInetmask\fR]
+.IQ \fBipv6_dst=\fIipv6\fR[\fB/\fInetmask\fR]
+When \fBdl_type\fR is 0x86dd (possibly via shorthand, e.g., \fBipv6\fR
+or \fBtcp6\fR), matches IPv6 source (or destination) address \fIipv6\fR,
+which may be specified as defined in RFC 2373.  The preferred format is 
+\fIx\fB:\fIx\fB:\fIx\fB:\fIx\fB:\fIx\fB:\fIx\fB:\fIx\fB:\fIx\fR, where
+\fIx\fR are the hexadecimal values of the eight 16-bit pieces of the
+address.  A single instance of \fB::\fR may be used to indicate multiple
+groups of 16-bits of zeros.  The optional \fInetmask\fR allows
+restricting a match to an IPv6 address prefix.  A netmask is specified
+as a CIDR block (e.g. \fB2001:db8:3c4d:1::/64\fR).
+.
 .IP \fBtun_id=\fItunnel-id\fR[\fB/\fImask\fR]
 Matches tunnel identifier \fItunnel-id\fR.  Only packets that arrive
 over a tunnel that carries a key (e.g. GRE with the RFC 2890 key
@@ -381,22 +418,21 @@ When a packet enters an OpenFlow switch, all of the registers are set
 to 0.  Only explicit Nicira extension actions change register values.
 .
 .PP
-The following shorthand notations are also available:
+Defining IPv6 flows (those with \fBdl_type\fR equal to 0x86dd) requires
+support for NXM.  The following shorthand notations are available for
+IPv6-related flows:
 .
-.IP \fBip\fR
-Same as \fBdl_type=0x0800\fR.
+.IP \fBipv6\fR
+Same as \fBdl_type=0x86dd\fR.
 .
-.IP \fBicmp\fR
-Same as \fBdl_type=0x0800,nw_proto=1\fR.
-.
-.IP \fBtcp\fR
-Same as \fBdl_type=0x0800,nw_proto=6\fR.
+.IP \fBtcp6\fR
+Same as \fBdl_type=0x86dd,nw_proto=6\fR.
 .
-.IP \fBudp\fR
-Same as \fBdl_type=0x0800,nw_proto=17\fR.
+.IP \fBudp6\fR
+Same as \fBdl_type=0x86dd,nw_proto=17\fR.
 .
-.IP \fBarp\fR
-Same as \fBdl_type=0x0806\fR.
+.IP \fBicmp6\fR
+Same as \fBdl_type=0x86dd,nw_proto=58\fR.
 .
 .PP
 The \fBadd\-flow\fR and \fBadd\-flows\fR commands require an additional