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:
76
DESIGN
Normal file
76
DESIGN
Normal 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.
|
@@ -31,6 +31,7 @@ CLEAN_LOCAL =
|
||||
DISTCLEANFILES =
|
||||
EXTRA_DIST = \
|
||||
CodingStyle \
|
||||
DESIGN \
|
||||
INSTALL.KVM \
|
||||
INSTALL.Linux \
|
||||
INSTALL.OpenFlow \
|
||||
|
@@ -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])])
|
||||
|
||||
|
215
datapath/flow.c
215
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 - 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;
|
||||
|
@@ -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. */
|
||||
|
@@ -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 \
|
||||
|
13
datapath/linux-2.6/compat-2.6/include/linux/icmpv6.h
Normal file
13
datapath/linux-2.6/compat-2.6/include/linux/icmpv6.h
Normal 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
|
@@ -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. ## */
|
||||
/* ## --------------------- ## */
|
||||
|
@@ -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;
|
||||
|
123
lib/classifier.c
123
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));
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
@@ -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 *);
|
||||
|
||||
|
249
lib/flow.c
249
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),
|
||||
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);
|
||||
}
|
||||
|
18
lib/flow.h
18
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);
|
||||
|
||||
|
151
lib/nx-match.c
151
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
|
||||
@@ -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();
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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. */
|
||||
|
106
lib/odp-util.c
106
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,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;
|
||||
|
||||
|
@@ -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 *);
|
||||
|
@@ -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) {
|
||||
|
@@ -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. */
|
||||
|
120
lib/packets.c
120
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;
|
||||
}
|
||||
|
@@ -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 */
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
@@ -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)
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user