datapath: Always allow tunnel mask to be specified in the netlink
[sliver-openvswitch.git] / datapath / flow.c
index 9ae94bc..29d3062 100644 (file)
@@ -82,8 +82,9 @@ static void update_range__(struct sw_flow_match *match,
        do { \
                update_range__(match, offsetof(struct sw_flow_key, field),  \
                                     sizeof((match)->key->field), is_mask); \
-               if (is_mask && match->mask != NULL) {                       \
-                       (match)->mask->key.field = value;                   \
+               if (is_mask) {                                              \
+                       if ((match)->mask)                                  \
+                               (match)->mask->key.field = value;           \
                } else {                                                    \
                        (match)->key->field = value;                        \
                }                                                           \
@@ -93,8 +94,9 @@ static void update_range__(struct sw_flow_match *match,
        do { \
                update_range__(match, offsetof(struct sw_flow_key, field),  \
                                len, is_mask);                              \
-               if (is_mask && match->mask != NULL) {                       \
-                       memcpy(&(match)->mask->key.field, value_p, len);    \
+               if (is_mask) {                                              \
+                       if ((match)->mask)                                  \
+                               memcpy(&(match)->mask->key.field, value_p, len);\
                } else {                                                    \
                        memcpy(&(match)->key->field, value_p, len);         \
                }                                                           \
@@ -133,6 +135,9 @@ static bool ovs_match_validate(const struct sw_flow_match *match,
                        | (1ULL << OVS_KEY_ATTR_ARP)
                        | (1ULL << OVS_KEY_ATTR_ND));
 
+       /* Tunnel mask is always allowed. */
+       mask_allowed |= (1ULL << OVS_KEY_ATTR_TUNNEL);
+
        if (match->key->phy.in_port == DP_MAX_PORTS &&
            match->mask && (match->mask->key.phy.in_port == 0xffff))
                mask_allowed |= (1ULL << OVS_KEY_ATTR_IN_PORT);
@@ -349,9 +354,8 @@ static bool icmp6hdr_ok(struct sk_buff *skb)
                                  sizeof(struct icmp6hdr));
 }
 
-static void flow_key_mask(struct sw_flow_key *dst,
-                         const struct sw_flow_key *src,
-                         const struct sw_flow_mask *mask)
+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;
@@ -430,7 +434,7 @@ static struct flex_array *alloc_buckets(unsigned int n_buckets)
        struct flex_array *buckets;
        int i, err;
 
-       buckets = flex_array_alloc(sizeof(struct hlist_head *),
+       buckets = flex_array_alloc(sizeof(struct hlist_head),
                                   n_buckets, GFP_KERNEL);
        if (!buckets)
                return NULL;
@@ -490,7 +494,7 @@ static void __flow_tbl_destroy(struct flow_table *table)
                int ver = table->node_ver;
 
                hlist_for_each_entry_safe(flow, n, head, hash_node[ver]) {
-                       hlist_del_rcu(&flow->hash_node[ver]);
+                       hlist_del(&flow->hash_node[ver]);
                        ovs_flow_free(flow, false);
                }
        }
@@ -639,8 +643,7 @@ void ovs_flow_free(struct sw_flow *flow, bool deferred)
        if (!flow)
                return;
 
-       ovs_sw_flow_mask_del_ref((struct sw_flow_mask __force *)flow->mask,
-                                deferred);
+       ovs_sw_flow_mask_del_ref(flow->mask, deferred);
 
        if (deferred)
                call_rcu(&flow->rcu, rcu_free_flow_callback);
@@ -1045,11 +1048,12 @@ static struct sw_flow *ovs_masked_flow_lookup(struct flow_table *table,
        u32 hash;
        struct sw_flow_key masked_key;
 
-       flow_key_mask(&masked_key, flow_key, mask);
+       ovs_flow_key_mask(&masked_key, flow_key, mask);
        hash = ovs_flow_hash(&masked_key, key_start, key_len);
        head = find_bucket(table, hash);
        hlist_for_each_entry_rcu(flow, head, hash_node[table->node_ver]) {
-               if (__flow_cmp_key(flow, &masked_key, key_start, key_len))
+               if (flow->mask == mask &&
+                   __flow_cmp_key(flow, &masked_key, key_start, key_len))
                        return flow;
        }
        return NULL;
@@ -1071,14 +1075,10 @@ struct sw_flow *ovs_flow_lookup(struct flow_table *tbl,
 }
 
 
-void ovs_flow_insert(struct flow_table *table, struct sw_flow *flow,
-                        const struct sw_flow_key *key, int key_len)
+void ovs_flow_insert(struct flow_table *table, struct sw_flow *flow)
 {
-       flow->unmasked_key = *key;
-       flow_key_mask(&flow->key, &flow->unmasked_key, ovsl_dereference(flow->mask));
-       flow->hash = ovs_flow_hash(&flow->key,
-                       ovsl_dereference(flow->mask)->range.start,
-                       ovsl_dereference(flow->mask)->range.end);
+       flow->hash = ovs_flow_hash(&flow->key, flow->mask->range.start,
+                       flow->mask->range.end);
        __tbl_insert(table, flow);
 }
 
@@ -1260,7 +1260,7 @@ int ipv4_tun_from_nlattr(const struct nlattr *attr,
        }
 
        if (!ttl) {
-               OVS_NLERR("IPv4 tunnel TTL is zero.\n");
+               OVS_NLERR("IPv4 tunnel TTL not specified.\n");
                return -EINVAL;
        }
 
@@ -1277,23 +1277,24 @@ int ipv4_tun_to_nlattr(struct sk_buff *skb,
        if (!nla)
                return -EMSGSIZE;
 
-       if (tun_key->tun_flags & TUNNEL_KEY &&
+       if (output->tun_flags & TUNNEL_KEY &&
            nla_put_be64(skb, OVS_TUNNEL_KEY_ATTR_ID, output->tun_id))
                return -EMSGSIZE;
-       if (tun_key->ipv4_src &&
-           nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_IPV4_SRC, output->ipv4_src))
+       if (output->ipv4_src &&
+               nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_IPV4_SRC, output->ipv4_src))
                return -EMSGSIZE;
-       if (nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_IPV4_DST, output->ipv4_dst))
+       if (output->ipv4_dst &&
+               nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_IPV4_DST, output->ipv4_dst))
                return -EMSGSIZE;
-       if (tun_key->ipv4_tos &&
-           nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_TOS, output->ipv4_tos))
+       if (output->ipv4_tos &&
+               nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_TOS, output->ipv4_tos))
                return -EMSGSIZE;
        if (nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_TTL, output->ipv4_ttl))
                return -EMSGSIZE;
-       if ((tun_key->tun_flags & TUNNEL_DONT_FRAGMENT) &&
+       if ((output->tun_flags & TUNNEL_DONT_FRAGMENT) &&
                nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT))
                return -EMSGSIZE;
-       if ((tun_key->tun_flags & TUNNEL_CSUM) &&
+       if ((output->tun_flags & TUNNEL_CSUM) &&
                nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_CSUM))
                return -EMSGSIZE;
 
@@ -1367,15 +1368,19 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match,  u64 attrs,
                __be16 tci;
 
                tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]);
-               if (!is_mask)
-                       if (!(tci & htons(VLAN_TAG_PRESENT))) {
+               if (!(tci & htons(VLAN_TAG_PRESENT))) {
+                       if (is_mask)
+                               OVS_NLERR("VLAN TCI mask does not have exact match for VLAN_TAG_PRESENT bit.\n");
+                       else
                                OVS_NLERR("VLAN TCI does not have VLAN_TAG_PRESENT bit set.\n");
-                               return -EINVAL;
-                       }
+
+                       return -EINVAL;
+               }
 
                SW_FLOW_KEY_PUT(match, eth.tci, tci, is_mask);
                attrs &= ~(1ULL << OVS_KEY_ATTR_VLAN);
-       }
+       } else if (!is_mask)
+               SW_FLOW_KEY_PUT(match, eth.tci, htons(0xffff), true);
 
        if (attrs & (1ULL << OVS_KEY_ATTR_ETHERTYPE)) {
                __be16 eth_type;
@@ -1692,26 +1697,27 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey,
        struct ovs_key_ethernet *eth_key;
        struct nlattr *nla, *encap;
 
-       if (swkey->phy.priority &&
-           nla_put_u32(skb, OVS_KEY_ATTR_PRIORITY, output->phy.priority))
+       if (nla_put_u32(skb, OVS_KEY_ATTR_PRIORITY, output->phy.priority))
                goto nla_put_failure;
 
        if (swkey->tun_key.ipv4_dst &&
            ipv4_tun_to_nlattr(skb, &swkey->tun_key, &output->tun_key))
                goto nla_put_failure;
 
-       if (swkey->phy.in_port != DP_MAX_PORTS) {
-               /* Exact match upper 16 bits. */
+       if (swkey->phy.in_port == DP_MAX_PORTS) {
+               if ((swkey != output) && (output->phy.in_port == 0xffff))
+                       if (nla_put_u32(skb, OVS_KEY_ATTR_IN_PORT, 0xffffffff))
+                               goto nla_put_failure;
+       } else {
                u16 upper_u16;
                upper_u16 = (swkey == output) ? 0 : 0xffff;
 
                if (nla_put_u32(skb, OVS_KEY_ATTR_IN_PORT,
-                                       (upper_u16 << 16) | output->phy.in_port))
+                               (upper_u16 << 16) | output->phy.in_port))
                        goto nla_put_failure;
        }
 
-       if (swkey->phy.skb_mark &&
-           nla_put_u32(skb, OVS_KEY_ATTR_SKB_MARK, output->phy.skb_mark))
+       if (nla_put_u32(skb, OVS_KEY_ATTR_SKB_MARK, output->phy.skb_mark))
                goto nla_put_failure;
 
        nla = nla_reserve(skb, OVS_KEY_ATTR_ETHERNET, sizeof(*eth_key));
@@ -1734,12 +1740,22 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey,
        } else
                encap = NULL;
 
-       if ((swkey == output) && (swkey->eth.type == htons(ETH_P_802_2)))
+       if (swkey->eth.type == htons(ETH_P_802_2)) {
+               /*
+                * Ethertype 802.2 is represented in the netlink with omitted
+                * OVS_KEY_ATTR_ETHERTYPE in the flow key attribute, and
+                * 0xffff in the mask attribute.  Ethertype can also
+                * be wildcarded.
+                */
+               if (swkey != output && output->eth.type)
+                       if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE,
+                                               output->eth.type))
+                               goto nla_put_failure;
                goto unencap;
+       }
 
-       if (output->eth.type != 0)
-               if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, output->eth.type))
-                       goto nla_put_failure;
+       if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, output->eth.type))
+               goto nla_put_failure;
 
        if (swkey->eth.type == htons(ETH_P_IP)) {
                struct ovs_key_ipv4 *ipv4_key;