2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-31 14:25:26 +00:00

nicira-ext: Support matching IPv6 traffic.

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>
This commit is contained in:
Justin Pettit
2010-12-29 19:03:46 -08:00
parent bad68a9965
commit d31f1109f1
27 changed files with 1475 additions and 150 deletions

76
DESIGN Normal file
View File

@@ -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.

View File

@@ -31,6 +31,7 @@ CLEAN_LOCAL =
DISTCLEANFILES =
EXTRA_DIST = \
CodingStyle \
DESIGN \
INSTALL.KVM \
INSTALL.Linux \
INSTALL.OpenFlow \

View 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])])

View File

@@ -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 - 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));
nla = nla_reserve(skb, ODP_KEY_ATTR_ICMPV6, sizeof(*icmpv6_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);
icmpv6_key = nla_data(nla);
icmpv6_key->icmpv6_type = ntohs(swkey->tp_src);
icmpv6_key->icmpv6_code = ntohs(swkey->tp_dst);
}
}
return 0;

View 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. */

View 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 \

View File

@@ -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

View 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. ## */
/* ## --------------------- ## */

View 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;

View 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));
}
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);
}

View 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 *);

View File

@@ -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),
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);
}

View 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);

View 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
@@ -57,17 +57,19 @@ struct nxm_field {
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. */
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();
}

View File

@@ -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 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 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 (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_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, 0, 0, true)
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

View 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. */

View 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_ARP)) {
struct odp_key_arp *arp_key;
} else if (flow->dl_type == htons(ETH_TYPE_IPV6)
&& flow->nw_proto == IPPROTO_ICMPV6) {
struct odp_key_icmpv6 *icmpv6_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);
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);
}
}
}
@@ -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;

View 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 *);

View 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) {

View 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. */

View File

@@ -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;
}

View 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 */

View 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

View 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);

View 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)

View 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;
}

View 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 \fBtcp6\fR
Same as \fBdl_type=0x86dd,nw_proto=6\fR.
.
.IP \fBtcp\fR
Same as \fBdl_type=0x0800,nw_proto=6\fR.
.IP \fBudp6\fR
Same as \fBdl_type=0x86dd,nw_proto=17\fR.
.
.IP \fBudp\fR
Same as \fBdl_type=0x0800,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