[ovs-dev] [IPv6 IV: A New Hope 5/6] nicira-ext: Support matching IPv6 traffic.
Justin Pettit
jpettit at nicira.com
Tue Feb 1 08:53:52 UTC 2011
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 at nicira.com>
---
DESIGN | 76 +++++++
Makefile.am | 1 +
acinclude.m4 | 2 +
datapath/flow.c | 206 +++++++++++++++---
datapath/flow.h | 12 +-
datapath/linux-2.6/Modules.mk | 1 +
.../linux-2.6/compat-2.6/include/linux/icmpv6.h | 13 +
include/openflow/nicira-ext.h | 36 +++-
include/openvswitch/datapath-protocol.h | 8 +
lib/classifier.c | 127 ++++++++++-
lib/classifier.h | 6 +
lib/flow.c | 232 +++++++++++++++++++-
lib/flow.h | 18 +-
lib/nx-match.c | 163 +++++++++++++-
lib/nx-match.def | 72 ++++---
lib/nx-match.h | 8 +-
lib/odp-util.c | 87 +++++++-
lib/odp-util.h | 4 +-
lib/ofp-parse.c | 66 ++++++-
lib/ofp-util.c | 9 +
lib/packets.c | 120 ++++++++++
lib/packets.h | 32 +++
lib/socket-util.c | 14 ++
lib/socket-util.h | 1 +
tests/ovs-ofctl.at | 48 ++++
tests/test-packets.c | 116 ++++++++++
utilities/ovs-ofctl.8.in | 84 +++++--
27 files changed, 1417 insertions(+), 145 deletions(-)
create mode 100644 DESIGN
create mode 100644 datapath/linux-2.6/compat-2.6/include/linux/icmpv6.h
diff --git a/DESIGN b/DESIGN
new file mode 100644
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 at openvswitch.org.
diff --git a/Makefile.am b/Makefile.am
index deae512..71a0652 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -31,6 +31,7 @@ CLEAN_LOCAL =
DISTCLEANFILES =
EXTRA_DIST = \
CodingStyle \
+ DESIGN \
INSTALL.KVM \
INSTALL.Linux \
INSTALL.OpenFlow \
diff --git a/acinclude.m4 b/acinclude.m4
index 6fc1c7a..0cd1427 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -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])])
diff --git a/datapath/flow.c b/datapath/flow.c
index d83c17d..a0688ec 100644
--- a/datapath/flow.c
+++ b/datapath/flow.c
@@ -24,11 +24,14 @@
#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 - skb_network_offset(skb);
+
+ /* 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;
}
@@ -440,6 +536,7 @@ 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),
@@ -449,6 +546,7 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr)
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;
@@ -499,15 +597,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 +629,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);
@@ -524,7 +638,9 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr)
break;
case TRANSITION(ODP_KEY_ATTR_IPV4, ODP_KEY_ATTR_ICMP):
- if (swkey->nw_proto != IPPROTO_ICMP)
+ case TRANSITION(ODP_KEY_ATTR_IPV6, ODP_KEY_ATTR_ICMP):
+ if (swkey->nw_proto != IPPROTO_ICMP
+ && swkey->nw_proto != IPPROTO_ICMPV6)
return -EINVAL;
icmp_key = nla_data(nla);
swkey->tp_src = htons(icmp_key->icmp_type);
@@ -535,8 +651,8 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr)
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);
@@ -572,9 +688,11 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr)
return 0;
case ODP_KEY_ATTR_IPV4:
+ case ODP_KEY_ATTR_IPV6:
if (swkey->nw_proto == IPPROTO_TCP ||
swkey->nw_proto == IPPROTO_UDP ||
- swkey->nw_proto == IPPROTO_ICMP)
+ swkey->nw_proto == IPPROTO_ICMP ||
+ swkey->nw_proto == IPPROTO_ICMPV6)
return -EINVAL;
return 0;
@@ -626,10 +744,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 +796,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 +806,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_icmp *icmp_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_ICMP, sizeof(*icmp_key));
+ if (!nla)
+ goto nla_put_failure;
+ icmp_key = nla_data(nla);
+ icmp_key->icmp_type = ntohs(swkey->tp_src);
+ icmp_key->icmp_code = ntohs(swkey->tp_dst);
+ }
}
return 0;
diff --git a/datapath/flow.h b/datapath/flow.h
index b9af272..ee1c4c9 100644
--- a/datapath/flow.h
+++ b/datapath/flow.h
@@ -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. */
diff --git a/datapath/linux-2.6/Modules.mk b/datapath/linux-2.6/Modules.mk
index b7b9524..88c5769 100644
--- a/datapath/linux-2.6/Modules.mk
+++ b/datapath/linux-2.6/Modules.mk
@@ -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
index 0000000..f005a48
--- /dev/null
+++ b/datapath/linux-2.6/compat-2.6/include/linux/icmpv6.h
@@ -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
diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h
index 01e735d..f136ba5 100644
--- a/include/openflow/nicira-ext.h
+++ b/include/openflow/nicira-ext.h
@@ -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. ## */
/* ## --------------------- ## */
diff --git a/include/openvswitch/datapath-protocol.h b/include/openvswitch/datapath-protocol.h
index 4d1b17c..921d6a2 100644
--- a/include/openvswitch/datapath-protocol.h
+++ b/include/openvswitch/datapath-protocol.h
@@ -311,6 +311,7 @@ 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 */
@@ -337,6 +338,13 @@ struct odp_key_ipv4 {
uint8_t ipv4_tos;
};
+struct odp_key_ipv6 {
+ uint32_t ipv6_src[4];
+ uint32_t ipv6_dst[4];
+ uint8_t ipv6_proto;
+ uint8_t ipv6_tos;
+};
+
struct odp_key_tcp {
ovs_be16 tcp_src;
ovs_be16 tcp_dst;
diff --git a/lib/classifier.c b/lib/classifier.c
index a4d53c6..b606f6f 100644
--- a/lib/classifier.c
+++ b/lib/classifier.c
@@ -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));
@@ -965,13 +1050,37 @@ next_rule_in_list(struct cls_rule *rule)
}
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)
{
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);
}
diff --git a/lib/classifier.h b/lib/classifier.h
index 4014972..c82a484 100644
--- a/lib/classifier.h
+++ b/lib/classifier.h
@@ -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 *);
diff --git a/lib/flow.c b/lib/flow.c
index 684ec0f..75efbdd 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -16,8 +16,11 @@
#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
diff --git a/lib/flow.h b/lib/flow.h
index a4cf60e..83830e6 100644
--- a/lib/flow.h
+++ b/lib/flow.h
@@ -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);
diff --git a/lib/nx-match.c b/lib/nx-match.c
index afa88e2..f962e5c 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -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();
}
diff --git a/lib/nx-match.def b/lib/nx-match.def
index d32b94e..e4ed43f 100644
--- a/lib/nx-match.def
+++ b/lib/nx-match.def
@@ -14,43 +14,55 @@
* 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, IP_TYPE_TCP, false)
-DEFINE_FIELD (OF_TCP_DST, FWW_TP_DST, ETH_TYPE_IP, IP_TYPE_TCP, false)
-DEFINE_FIELD (OF_UDP_SRC, FWW_TP_SRC, ETH_TYPE_IP, IP_TYPE_UDP, false)
-DEFINE_FIELD (OF_UDP_DST, FWW_TP_DST, ETH_TYPE_IP, IP_TYPE_UDP, false)
-DEFINE_FIELD (OF_ICMP_TYPE, FWW_TP_SRC, ETH_TYPE_IP, IP_TYPE_ICMP, false)
-DEFINE_FIELD (OF_ICMP_CODE, FWW_TP_DST, ETH_TYPE_IP, IP_TYPE_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, IP_TYPE_TCP, false)
+DEFINE_FIELD (OF_TCP_DST, FWW_TP_DST, NXM_DL_IP_ANY, IP_TYPE_TCP, false)
+DEFINE_FIELD (OF_UDP_SRC, FWW_TP_SRC, NXM_DL_IP_ANY, IP_TYPE_UDP, false)
+DEFINE_FIELD (OF_UDP_DST, FWW_TP_DST, NXM_DL_IP_ANY, IP_TYPE_UDP, false)
+DEFINE_FIELD (OF_ICMP_TYPE, FWW_TP_SRC, NXM_DL_IP, IP_TYPE_ICMP, false)
+DEFINE_FIELD (OF_ICMP_CODE, FWW_TP_DST, NXM_DL_IP, IP_TYPE_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
diff --git a/lib/nx-match.h b/lib/nx-match.h
index bd4fea2..aefcb65 100644
--- a/lib/nx-match.h
+++ b/lib/nx-match.h
@@ -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. */
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 89f67b0..6270f04 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <arpa/inet.h>
#include <config.h>
#include "odp-util.h"
#include <errno.h>
@@ -194,6 +195,7 @@ 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);
@@ -233,6 +235,7 @@ 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;
@@ -287,6 +290,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")",
@@ -394,6 +411,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 == IP_TYPE_TCP) {
struct odp_key_tcp *tcp_key;
@@ -409,24 +449,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 == IP_TYPE_ICMP) {
+ } else if (flow->dl_type == htons(ETH_TYPE_IP)
+ && flow->nw_proto == IP_TYPE_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_ARP)) {
- struct odp_key_arp *arp_key;
+ } else if (flow->dl_type == htons(ETH_TYPE_IPV6)
+ && flow->nw_proto == IPPROTO_ICMPV6) {
+ struct odp_key_icmp *icmp_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);
+ 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);
+ }
}
}
@@ -448,6 +487,7 @@ 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;
@@ -514,7 +554,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 != IP_TYPE_TCP) {
return EINVAL;
}
@@ -524,6 +579,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 != IP_TYPE_UDP) {
return EINVAL;
}
@@ -533,7 +589,9 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len,
break;
case TRANSITION(ODP_KEY_ATTR_IPV4, ODP_KEY_ATTR_ICMP):
- if (flow->nw_proto != IP_TYPE_ICMP) {
+ case TRANSITION(ODP_KEY_ATTR_IPV6, ODP_KEY_ATTR_ICMP):
+ if (flow->nw_proto != IP_TYPE_ICMP
+ && flow->nw_proto != IPPROTO_ICMPV6) {
return EINVAL;
}
icmp_key = nl_attr_get(nla);
@@ -584,15 +642,18 @@ 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;
}
return 0;
case ODP_KEY_ATTR_IPV4:
+ case ODP_KEY_ATTR_IPV6:
if (flow->nw_proto == IP_TYPE_TCP
|| flow->nw_proto == IP_TYPE_UDP
- || flow->nw_proto == IP_TYPE_ICMP) {
+ || flow->nw_proto == IP_TYPE_ICMP
+ || flow->nw_proto == IPPROTO_ICMPV6) {
return EINVAL;
}
return 0;
diff --git a/lib/odp-util.h b/lib/odp-util.h
index 1a0d58d..8ec09f3 100644
--- a/lib/odp-util.h
+++ b/lib/odp-util.h
@@ -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 *);
diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
index 2ab684c..7349c40 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -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, IP_TYPE_ICMP },
{ "tcp", ETH_TYPE_IP, IP_TYPE_TCP },
{ "udp", ETH_TYPE_IP, IP_TYPE_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) {
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index 59a5fc4..4d89e0a 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -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. */
diff --git a/lib/packets.c b/lib/packets.c
index 2dc82fe..05148fe 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -16,10 +16,16 @@
#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;
+}
diff --git a/lib/packets.h b/lib/packets.h
index 96e23e1..4d4a967 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -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
@@ -375,4 +377,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 */
diff --git a/lib/socket-util.c b/lib/socket-util.c
index e97e7c9..469131d 100644
--- a/lib/socket-util.c
+++ b/lib/socket-util.c
@@ -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
diff --git a/lib/socket-util.h b/lib/socket-util.h
index 40a9614..f4e617a 100644
--- a/lib/socket-util.h
+++ b/lib/socket-util.h
@@ -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);
diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at
index 7eecf28..a86588b 100644
--- a/tests/ovs-ofctl.at
+++ b/tests/ovs-ofctl.at
@@ -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)
diff --git a/tests/test-packets.c b/tests/test-packets.c
index 464a8eb..dda4797 100644
--- a/tests/test-packets.c
+++ b/tests/test-packets.c
@@ -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;
}
diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
index 135e705..3742552 100644
--- a/utilities/ovs-ofctl.8.in
+++ b/utilities/ovs-ofctl.8.in
@@ -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
--
1.7.1
More information about the dev
mailing list