#include <linux/if_arp.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
+#include <linux/sctp.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/icmp.h>
size_t offset, size_t size, bool is_mask)
{
struct sw_flow_key_range *range = NULL;
- size_t start = offset;
- size_t end = offset + size;
+ size_t start = rounddown(offset, sizeof(long));
+ size_t end = roundup(offset + size, sizeof(long));
if (!is_mask)
range = &match->range;
} \
} while (0)
+static u16 range_n_bytes(const struct sw_flow_key_range *range)
+{
+ return range->end - range->start;
+}
+
void ovs_match_init(struct sw_flow_match *match,
struct sw_flow_key *key,
struct sw_flow_mask *mask)
| (1ULL << OVS_KEY_ATTR_IPV6)
| (1ULL << OVS_KEY_ATTR_TCP)
| (1ULL << OVS_KEY_ATTR_UDP)
+ | (1ULL << OVS_KEY_ATTR_SCTP)
| (1ULL << OVS_KEY_ATTR_ICMP)
| (1ULL << OVS_KEY_ATTR_ICMPV6)
| (1ULL << OVS_KEY_ATTR_ARP)
mask_allowed |= 1ULL << OVS_KEY_ATTR_UDP;
}
+ if (match->key->ip.proto == IPPROTO_SCTP) {
+ key_expected |= 1ULL << OVS_KEY_ATTR_SCTP;
+ if (match->mask && (match->mask->key.ip.proto == 0xff))
+ mask_allowed |= 1ULL << OVS_KEY_ATTR_SCTP;
+ }
+
if (match->key->ip.proto == IPPROTO_TCP) {
key_expected |= 1ULL << OVS_KEY_ATTR_TCP;
if (match->mask && (match->mask->key.ip.proto == 0xff))
mask_allowed |= 1ULL << OVS_KEY_ATTR_UDP;
}
+ if (match->key->ip.proto == IPPROTO_SCTP) {
+ key_expected |= 1ULL << OVS_KEY_ATTR_SCTP;
+ if (match->mask && (match->mask->key.ip.proto == 0xff))
+ mask_allowed |= 1ULL << OVS_KEY_ATTR_SCTP;
+ }
+
if (match->key->ip.proto == IPPROTO_TCP) {
key_expected |= 1ULL << OVS_KEY_ATTR_TCP;
if (match->mask && (match->mask->key.ip.proto == 0xff))
sizeof(struct udphdr));
}
+static bool sctphdr_ok(struct sk_buff *skb)
+{
+ return pskb_may_pull(skb, skb_transport_offset(skb) +
+ sizeof(struct sctphdr));
+}
+
static bool icmphdr_ok(struct sk_buff *skb)
{
return pskb_may_pull(skb, skb_transport_offset(skb) +
void ovs_flow_key_mask(struct sw_flow_key *dst, const struct sw_flow_key *src,
const struct sw_flow_mask *mask)
{
- u8 *m = (u8 *)&mask->key + mask->range.start;
- u8 *s = (u8 *)src + mask->range.start;
- u8 *d = (u8 *)dst + mask->range.start;
+ const long *m = (long *)((u8 *)&mask->key + mask->range.start);
+ const long *s = (long *)((u8 *)src + mask->range.start);
+ long *d = (long *)((u8 *)dst + mask->range.start);
int i;
- memset(dst, 0, sizeof(*dst));
- for (i = 0; i < ovs_sw_flow_mask_size_roundup(mask); i++) {
- *d = *s & *m;
- d++, s++, m++;
- }
+ /* The memory outside of the 'mask->range' are not set since
+ * further operations on 'dst' only uses contents within
+ * 'mask->range'.
+ */
+ for (i = 0; i < range_n_bytes(&mask->range); i += sizeof(long))
+ *d++ = *s++ & *m++;
}
#define TCP_FLAGS_OFFSET 13
* Ethernet header
* @in_port: port number on which @skb was received.
* @key: output flow key
- * @key_lenp: length of output flow key
*
* The caller must ensure that skb->len >= ETH_HLEN.
*
if (OVS_CB(skb)->tun_key)
memcpy(&key->tun_key, OVS_CB(skb)->tun_key, sizeof(key->tun_key));
key->phy.in_port = in_port;
- key->phy.skb_mark = skb_get_mark(skb);
+ key->phy.skb_mark = skb->mark;
skb_reset_mac_header(skb);
key->ipv4.tp.src = udp->source;
key->ipv4.tp.dst = udp->dest;
}
+ } else if (key->ip.proto == IPPROTO_SCTP) {
+ if (sctphdr_ok(skb)) {
+ struct sctphdr *sctp = sctp_hdr(skb);
+ key->ipv4.tp.src = sctp->source;
+ key->ipv4.tp.dst = sctp->dest;
+ }
} else if (key->ip.proto == IPPROTO_ICMP) {
if (icmphdr_ok(skb)) {
struct icmphdr *icmp = icmp_hdr(skb);
key->ipv6.tp.src = udp->source;
key->ipv6.tp.dst = udp->dest;
}
+ } else if (key->ip.proto == NEXTHDR_SCTP) {
+ if (sctphdr_ok(skb)) {
+ struct sctphdr *sctp = sctp_hdr(skb);
+ key->ipv6.tp.src = sctp->source;
+ key->ipv6.tp.dst = sctp->dest;
+ }
} else if (key->ip.proto == NEXTHDR_ICMP) {
if (icmp6hdr_ok(skb)) {
error = parse_icmpv6(skb, key, nh_len);
return 0;
}
-static u32 ovs_flow_hash(const struct sw_flow_key *key, int key_start, int key_len)
+static u32 ovs_flow_hash(const struct sw_flow_key *key, int key_start,
+ int key_end)
{
- return jhash2((u32 *)((u8 *)key + key_start),
- DIV_ROUND_UP(key_len - key_start, sizeof(u32)), 0);
+ u32 *hash_key = (u32 *)((u8 *)key + key_start);
+ int hash_u32s = (key_end - key_start) >> 2;
+
+ /* Make sure number of hash bytes are multiple of u32. */
+ BUILD_BUG_ON(sizeof(long) % sizeof(u32));
+
+ return jhash2(hash_key, hash_u32s, 0);
}
static int flow_key_start(const struct sw_flow_key *key)
if (key->tun_key.ipv4_dst)
return 0;
else
- return offsetof(struct sw_flow_key, phy);
+ return rounddown(offsetof(struct sw_flow_key, phy),
+ sizeof(long));
}
static bool __cmp_key(const struct sw_flow_key *key1,
- const struct sw_flow_key *key2, int key_start, int key_len)
+ const struct sw_flow_key *key2, int key_start, int key_end)
{
- return !memcmp((u8 *)key1 + key_start,
- (u8 *)key2 + key_start, (key_len - key_start));
+ const long *cp1 = (long *)((u8 *)key1 + key_start);
+ const long *cp2 = (long *)((u8 *)key2 + key_start);
+ long diffs = 0;
+ int i;
+
+ for (i = key_start; i < key_end; i += sizeof(long))
+ diffs |= *cp1++ ^ *cp2++;
+
+ return diffs == 0;
}
-static bool __flow_cmp_key(const struct sw_flow *flow,
- const struct sw_flow_key *key, int key_start, int key_len)
+static bool __flow_cmp_masked_key(const struct sw_flow *flow,
+ const struct sw_flow_key *key, int key_start, int key_end)
{
- return __cmp_key(&flow->key, key, key_start, key_len);
+ return __cmp_key(&flow->key, key, key_start, key_end);
}
static bool __flow_cmp_unmasked_key(const struct sw_flow *flow,
- const struct sw_flow_key *key, int key_start, int key_len)
+ const struct sw_flow_key *key, int key_start, int key_end)
{
- return __cmp_key(&flow->unmasked_key, key, key_start, key_len);
+ return __cmp_key(&flow->unmasked_key, key, key_start, key_end);
}
bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow,
- const struct sw_flow_key *key, int key_len)
+ const struct sw_flow_key *key, int key_end)
{
int key_start;
key_start = flow_key_start(key);
- return __flow_cmp_unmasked_key(flow, key, key_start, key_len);
+ return __flow_cmp_unmasked_key(flow, key, key_start, key_end);
}
struct sw_flow_match *match)
{
struct sw_flow_key *unmasked = match->key;
- int key_len = match->range.end;
+ int key_end = match->range.end;
struct sw_flow *flow;
flow = ovs_flow_lookup(table, unmasked);
- if (flow && (!ovs_flow_cmp_unmasked_key(flow, unmasked, key_len)))
+ if (flow && (!ovs_flow_cmp_unmasked_key(flow, unmasked, key_end)))
flow = NULL;
return flow;
}
static struct sw_flow *ovs_masked_flow_lookup(struct flow_table *table,
- const struct sw_flow_key *flow_key,
+ const struct sw_flow_key *unmasked,
struct sw_flow_mask *mask)
{
struct sw_flow *flow;
struct hlist_head *head;
int key_start = mask->range.start;
- int key_len = mask->range.end;
+ int key_end = mask->range.end;
u32 hash;
struct sw_flow_key masked_key;
- ovs_flow_key_mask(&masked_key, flow_key, mask);
- hash = ovs_flow_hash(&masked_key, key_start, key_len);
+ ovs_flow_key_mask(&masked_key, unmasked, mask);
+ hash = ovs_flow_hash(&masked_key, key_start, key_end);
head = find_bucket(table, hash);
hlist_for_each_entry_rcu(flow, head, hash_node[table->node_ver]) {
if (flow->mask == mask &&
- __flow_cmp_key(flow, &masked_key, key_start, key_len))
+ __flow_cmp_masked_key(flow, &masked_key,
+ key_start, key_end))
return flow;
}
return NULL;
[OVS_KEY_ATTR_IPV6] = sizeof(struct ovs_key_ipv6),
[OVS_KEY_ATTR_TCP] = sizeof(struct ovs_key_tcp),
[OVS_KEY_ATTR_UDP] = sizeof(struct ovs_key_udp),
+ [OVS_KEY_ATTR_SCTP] = sizeof(struct ovs_key_sctp),
[OVS_KEY_ATTR_ICMP] = sizeof(struct ovs_key_icmp),
[OVS_KEY_ATTR_ICMPV6] = sizeof(struct ovs_key_icmpv6),
[OVS_KEY_ATTR_ARP] = sizeof(struct ovs_key_arp),
if (ovs_tunnel_key_lens[type] != nla_len(a)) {
OVS_NLERR("IPv4 tunnel attribute type has unexpected "
- " legnth (type=%d, length=%d, expected=%d).\n",
+ " length (type=%d, length=%d, expected=%d).\n",
type, nla_len(a), ovs_tunnel_key_lens[type]);
return -EINVAL;
}
if (*attrs & (1ULL << OVS_KEY_ATTR_SKB_MARK)) {
uint32_t mark = nla_get_u32(a[OVS_KEY_ATTR_SKB_MARK]);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) && !defined(CONFIG_NETFILTER)
- if (!is_mask && mark != 0) {
- OVS_NLERR("skb->mark must be zero on this kernel (mark=%d).\n", mark);
- return -EINVAL;
- }
-#endif
+
SW_FLOW_KEY_PUT(match, phy.skb_mark, mark, is_mask);
*attrs &= ~(1ULL << OVS_KEY_ATTR_SKB_MARK);
}
/* Always exact match EtherType. */
eth_type = htons(0xffff);
} else if (ntohs(eth_type) < ETH_P_802_3_MIN) {
- OVS_NLERR("EtherType is less than mimimum (type=%x, min=%x).\n",
+ OVS_NLERR("EtherType is less than minimum (type=%x, min=%x).\n",
ntohs(eth_type), ETH_P_802_3_MIN);
return -EINVAL;
}
attrs &= ~(1ULL << OVS_KEY_ATTR_UDP);
}
+ if (attrs & (1ULL << OVS_KEY_ATTR_SCTP)) {
+ const struct ovs_key_sctp *sctp_key;
+
+ sctp_key = nla_data(a[OVS_KEY_ATTR_SCTP]);
+ if (orig_attrs & (1ULL << OVS_KEY_ATTR_IPV4)) {
+ SW_FLOW_KEY_PUT(match, ipv4.tp.src,
+ sctp_key->sctp_src, is_mask);
+ SW_FLOW_KEY_PUT(match, ipv4.tp.dst,
+ sctp_key->sctp_dst, is_mask);
+ } else {
+ SW_FLOW_KEY_PUT(match, ipv6.tp.src,
+ sctp_key->sctp_src, is_mask);
+ SW_FLOW_KEY_PUT(match, ipv6.tp.dst,
+ sctp_key->sctp_dst, is_mask);
+ }
+ attrs &= ~(1ULL << OVS_KEY_ATTR_SCTP);
+ }
+
if (attrs & (1ULL << OVS_KEY_ATTR_ICMP)) {
const struct ovs_key_icmp *icmp_key;
if (err)
return err;
- if (key_attrs & 1ULL << OVS_KEY_ATTR_ENCAP) {
- encap = a[OVS_KEY_ATTR_ENCAP];
- key_attrs &= ~(1ULL << OVS_KEY_ATTR_ENCAP);
- if (nla_len(encap)) {
- __be16 eth_type = 0; /* ETH_P_8021Q */
+ if ((key_attrs & (1ULL << OVS_KEY_ATTR_ETHERNET)) &&
+ (key_attrs & (1ULL << OVS_KEY_ATTR_ETHERTYPE)) &&
+ (nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]) == htons(ETH_P_8021Q))) {
+ __be16 tci;
- if (a[OVS_KEY_ATTR_ETHERTYPE])
- eth_type = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]);
+ if (!((key_attrs & (1ULL << OVS_KEY_ATTR_VLAN)) &&
+ (key_attrs & (1ULL << OVS_KEY_ATTR_ENCAP)))) {
+ OVS_NLERR("Invalid Vlan frame.\n");
+ return -EINVAL;
+ }
- if ((eth_type == htons(ETH_P_8021Q)) && (a[OVS_KEY_ATTR_VLAN])) {
- encap_valid = true;
- key_attrs &= ~(1ULL << OVS_KEY_ATTR_ETHERTYPE);
- err = parse_flow_nlattrs(encap, a, &key_attrs);
- } else {
- OVS_NLERR("Encap attribute is set for a non-VLAN frame.\n");
- err = -EINVAL;
- }
+ key_attrs &= ~(1ULL << OVS_KEY_ATTR_ETHERTYPE);
+ tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]);
+ encap = a[OVS_KEY_ATTR_ENCAP];
+ key_attrs &= ~(1ULL << OVS_KEY_ATTR_ENCAP);
+ encap_valid = true;
+ if (tci & htons(VLAN_TAG_PRESENT)) {
+ err = parse_flow_nlattrs(encap, a, &key_attrs);
if (err)
return err;
+ } else if (!tci) {
+ /* Corner case for truncated 802.1Q header. */
+ if (nla_len(encap)) {
+ OVS_NLERR("Truncated 802.1Q header has non-zero encap attribute.\n");
+ return -EINVAL;
+ }
+ } else {
+ OVS_NLERR("Encap attribute is set for a non-VLAN frame.\n");
+ return -EINVAL;
}
}
if (err)
return err;
- if ((mask_attrs & 1ULL << OVS_KEY_ATTR_ENCAP) && encap_valid) {
+ if (mask_attrs & 1ULL << OVS_KEY_ATTR_ENCAP) {
__be16 eth_type = 0;
+ __be16 tci = 0;
+
+ if (!encap_valid) {
+ OVS_NLERR("Encap mask attribute is set for non-VLAN frame.\n");
+ return -EINVAL;
+ }
mask_attrs &= ~(1ULL << OVS_KEY_ATTR_ENCAP);
if (a[OVS_KEY_ATTR_ETHERTYPE])
eth_type = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]);
+
if (eth_type == htons(0xffff)) {
mask_attrs &= ~(1ULL << OVS_KEY_ATTR_ETHERTYPE);
encap = a[OVS_KEY_ATTR_ENCAP];
err = parse_flow_mask_nlattrs(encap, a, &mask_attrs);
} else {
- OVS_NLERR("VLAN frames must have an exact match"
- " on the TPID (mask=%x).\n",
- ntohs(eth_type));
- err = -EINVAL;
+ OVS_NLERR("VLAN frames must have an exact match on the TPID (mask=%x).\n",
+ ntohs(eth_type));
+ return -EINVAL;
}
- if (err)
- return err;
+ if (a[OVS_KEY_ATTR_VLAN])
+ tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]);
+
+ if (!(tci & htons(VLAN_TAG_PRESENT))) {
+ OVS_NLERR("VLAN tag present bit must have an exact match (tci_mask=%x).\n", ntohs(tci));
+ return -EINVAL;
+ }
}
err = ovs_key_from_nlattrs(match, mask_attrs, a, true);
udp_key->udp_src = output->ipv6.tp.src;
udp_key->udp_dst = output->ipv6.tp.dst;
}
+ } else if (swkey->ip.proto == IPPROTO_SCTP) {
+ struct ovs_key_sctp *sctp_key;
+
+ nla = nla_reserve(skb, OVS_KEY_ATTR_SCTP, sizeof(*sctp_key));
+ if (!nla)
+ goto nla_put_failure;
+ sctp_key = nla_data(nla);
+ if (swkey->eth.type == htons(ETH_P_IP)) {
+ sctp_key->sctp_src = swkey->ipv4.tp.src;
+ sctp_key->sctp_dst = swkey->ipv4.tp.dst;
+ } else if (swkey->eth.type == htons(ETH_P_IPV6)) {
+ sctp_key->sctp_src = swkey->ipv6.tp.src;
+ sctp_key->sctp_dst = swkey->ipv6.tp.dst;
+ }
} else if (swkey->eth.type == htons(ETH_P_IP) &&
swkey->ip.proto == IPPROTO_ICMP) {
struct ovs_key_icmp *icmp_key;
* Returns zero if successful or a negative error code. */
int ovs_flow_init(void)
{
+ BUILD_BUG_ON(__alignof__(struct sw_flow_key) % __alignof__(long));
+ BUILD_BUG_ON(sizeof(struct sw_flow_key) % sizeof(long));
+
flow_cache = kmem_cache_create("sw_flow", sizeof(struct sw_flow), 0,
0, NULL);
if (flow_cache == NULL)
return (a->range.end == b->range.end)
&& (a->range.start == b->range.start)
- && (memcmp(a_, b_, ovs_sw_flow_mask_actual_size(a)) == 0);
+ && (memcmp(a_, b_, range_n_bytes(&a->range)) == 0);
}
struct sw_flow_mask *ovs_sw_flow_mask_find(const struct flow_table *tbl,
u8 *m = (u8 *)&mask->key + range->start;
mask->range = *range;
- memset(m, val, ovs_sw_flow_mask_size_roundup(mask));
+ memset(m, val, range_n_bytes(range));
}