This patch refactors the netlink flow handling code to make
the processing, error checking and prerequisite checking
more stream lined.

Signed-off-by: Andy Zhou <az...@nicira.com>
---
 datapath/datapath.c |   44 +++-
 datapath/flow.c     |  652 ++++++++++++++++++++++++++++-----------------------
 datapath/flow.h     |   16 +-
 3 files changed, 411 insertions(+), 301 deletions(-)

diff --git a/datapath/datapath.c b/datapath/datapath.c
index 42af315..8cf75bf 100644
--- a/datapath/datapath.c
+++ b/datapath/datapath.c
@@ -619,10 +619,14 @@ static int validate_tp_port(const struct sw_flow_key 
*flow_key)
 static int validate_and_copy_set_tun(const struct nlattr *attr,
                                     struct sw_flow_actions **sfa)
 {
-       struct ovs_key_ipv4_tunnel tun_key;
+       struct sw_flow_match match;
+       struct sw_flow_key key;
        int err, start;
 
-       err = ipv4_tun_from_nlattr(nla_data(attr), &tun_key);
+       memset(&key, 0, sizeof key);
+       ovs_match_init(&match, &key);
+
+       err = ipv4_tun_from_nlattr(nla_data(attr), &match);
        if (err)
                return err;
 
@@ -630,7 +634,8 @@ static int validate_and_copy_set_tun(const struct nlattr 
*attr,
        if (start < 0)
                return start;
 
-       err = add_action(sfa, OVS_KEY_ATTR_IPV4_TUNNEL, &tun_key, 
sizeof(tun_key));
+       err = add_action(sfa, OVS_KEY_ATTR_IPV4_TUNNEL, &match.key->tun_key,
+                        sizeof(match.key->tun_key));
        add_nested_action_end(*sfa, start);
 
        return err;
@@ -1234,6 +1239,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, 
struct genl_info *info)
        struct datapath *dp;
        struct flow_table *table;
        struct sw_flow_actions *acts = NULL;
+       struct sw_flow_match match;
        int error;
        int key_len;
 
@@ -1241,10 +1247,18 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, 
struct genl_info *info)
        error = -EINVAL;
        if (!a[OVS_FLOW_ATTR_KEY])
                goto error;
-       error = ovs_flow_from_nlattrs(&key, &key_len, a[OVS_FLOW_ATTR_KEY]);
+
+       memset(&key, 0, sizeof key);
+       ovs_match_init(&match, &key);
+       error = ovs_match_from_nlattrs(&match, a[OVS_FLOW_ATTR_KEY]);
        if (error)
                goto error;
 
+       if (ovs_match_check_prerequisite(&match) == false)
+               goto error;
+
+       key_len = ovs_match_get_len(&match);
+
        /* Validate actions. */
        if (a[OVS_FLOW_ATTR_ACTIONS]) {
                acts = 
ovs_flow_actions_alloc(nla_len(a[OVS_FLOW_ATTR_ACTIONS]));
@@ -1358,15 +1372,24 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct 
genl_info *info)
        struct sw_flow *flow;
        struct datapath *dp;
        struct flow_table *table;
+       struct sw_flow_match match;
        int err;
        int key_len;
 
+       memset(&key, 0, sizeof key);
+       ovs_match_init(&match, &key);
+
        if (!a[OVS_FLOW_ATTR_KEY])
                return -EINVAL;
-       err = ovs_flow_from_nlattrs(&key, &key_len, a[OVS_FLOW_ATTR_KEY]);
+       err = ovs_match_from_nlattrs(&match, a[OVS_FLOW_ATTR_KEY]);
        if (err)
                return err;
 
+       if (ovs_match_check_prerequisite(&match) == false)
+               return -EINVAL;
+
+       key_len = ovs_match_get_len(&match);
+
        ovs_lock();
        dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
        if (!dp) {
@@ -1404,6 +1427,7 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct 
genl_info *info)
        struct sw_flow *flow;
        struct datapath *dp;
        struct flow_table *table;
+       struct sw_flow_match match;
        int err;
        int key_len;
 
@@ -1418,10 +1442,18 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct 
genl_info *info)
                err = flush_flows(dp);
                goto unlock;
        }
-       err = ovs_flow_from_nlattrs(&key, &key_len, a[OVS_FLOW_ATTR_KEY]);
+
+       memset(&key, 0, sizeof key);
+       ovs_match_init(&match, &key);
+       err = ovs_match_from_nlattrs(&match, a[OVS_FLOW_ATTR_KEY]);
        if (err)
                goto unlock;
 
+       if (ovs_match_check_prerequisite(&match) == false)
+               goto unlock;
+
+       key_len = ovs_match_get_len(&match);
+
        table = ovsl_dereference(dp->table);
        flow = ovs_flow_tbl_lookup(table, &key, key_len);
        if (!flow) {
diff --git a/datapath/flow.c b/datapath/flow.c
index 3ce926e..914051d 100644
--- a/datapath/flow.c
+++ b/datapath/flow.c
@@ -47,6 +47,110 @@
 
 static struct kmem_cache *flow_cache;
 
+static void field_add_writes__(struct sw_flow_match *match,
+               size_t offset, size_t size)
+{
+       int i;
+       u8 *op = &(match->n_writes[offset]);
+
+       for (i = 0; i < size; i++, op++) {
+               ++*op;
+       }
+}
+
+#define SW_FLOW_KEY_PUT(match, field, value) \
+       do { \
+               field_add_writes__(match, offsetof(struct sw_flow_key, field), \
+                                    sizeof (match)->key->field );             \
+               (match)->key->field = value;                                   \
+       } while (0)
+
+#define SW_FLOW_KEY_MEMCPY(match, field, value_p, len) \
+       do { \
+               field_add_writes__(match, \
+                             offsetof(struct sw_flow_key, field), len); \
+               memcpy(&(match)->key->field, value_p, len); \
+       } while (0)
+
+void ovs_match_init(struct sw_flow_match *match, struct sw_flow_key *key)
+{
+       memset(match, 0, sizeof *match);
+       match->key = key;
+}
+
+bool ovs_match_check_prerequisite(const struct sw_flow_match *match)
+{
+       int i;
+       u64 expected = 0;
+
+       /* Any duplicate writes to the matching key is invalid. */
+       for (i=0; i< sizeof *(match->key); i++)
+               if (match->n_writes[i] >= 2) {
+                       return false;
+        }
+
+       if (match->key->eth.type == htons(ETH_P_ARP))
+               expected |= 1ULL << OVS_KEY_ATTR_ARP;
+
+       if (match->key->eth.type == htons(ETH_P_RARP))
+               expected |= 1ULL << OVS_KEY_ATTR_ARP;
+
+       if (match->key->eth.type == htons(ETH_P_IP)) {
+               expected |= 1ULL << OVS_KEY_ATTR_IPV4;
+
+               if (match->key->ip.frag != OVS_FRAG_TYPE_LATER) {
+                       if (match->key->ip.proto == IPPROTO_UDP)
+                               expected |= 1ULL << OVS_KEY_ATTR_UDP;
+
+                       if (match->key->ip.proto == IPPROTO_TCP)
+                               expected |= 1ULL << OVS_KEY_ATTR_TCP;
+
+                       if (match->key->ip.proto == IPPROTO_ICMP)
+                               expected |= 1ULL << OVS_KEY_ATTR_ICMP;
+               }
+       }
+
+       if (match->key->eth.type == htons(ETH_P_IPV6)) {
+               expected |= 1ULL << OVS_KEY_ATTR_IPV6;
+
+               if (match->key->ip.frag != OVS_FRAG_TYPE_LATER) {
+                       if (match->key->ip.proto == IPPROTO_UDP)
+                               expected |= 1ULL << OVS_KEY_ATTR_UDP;
+
+                       if (match->key->ip.proto == IPPROTO_TCP)
+                               expected |= 1ULL << OVS_KEY_ATTR_TCP;
+
+                       if (match->key->ip.proto == IPPROTO_ICMPV6) {
+                               expected |= 1ULL << OVS_KEY_ATTR_ICMPV6;
+
+                               if (match->key->ipv6.tp.src == 
htons(NDISC_NEIGHBOUR_SOLICITATION))
+                                       expected |= 1ULL << OVS_KEY_ATTR_ND;
+
+                               if (match->key->ipv6.tp.src == 
htons(NDISC_NEIGHBOUR_ADVERTISEMENT))
+                                       expected |= 1ULL << OVS_KEY_ATTR_ND;
+                       }
+               }
+       }
+
+       if ((match->key_attrs & expected) != expected)
+               return false;
+
+       return true;
+}
+
+int  ovs_match_get_len(struct sw_flow_match *match)
+{
+       int len;
+
+       for (len = sizeof *(match->key); len >0; len--) {
+               if (match->n_writes[len-1] > 0) {
+                       return len;
+               }
+       }
+
+       return 0;
+}
+
 static int check_header(struct sk_buff *skb, int len)
 {
        if (unlikely(skb->len < len))
@@ -850,112 +954,6 @@ const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = {
        [OVS_KEY_ATTR_TUNNEL] = -1,
 };
 
-static int ipv4_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_len,
-                                 const struct nlattr *a[], u64 *attrs)
-{
-       const struct ovs_key_icmp *icmp_key;
-       const struct ovs_key_tcp *tcp_key;
-       const struct ovs_key_udp *udp_key;
-
-       switch (swkey->ip.proto) {
-       case IPPROTO_TCP:
-               if (!(*attrs & (1 << OVS_KEY_ATTR_TCP)))
-                       return -EINVAL;
-               *attrs &= ~(1 << OVS_KEY_ATTR_TCP);
-
-               *key_len = SW_FLOW_KEY_OFFSET(ipv4.tp);
-               tcp_key = nla_data(a[OVS_KEY_ATTR_TCP]);
-               swkey->ipv4.tp.src = tcp_key->tcp_src;
-               swkey->ipv4.tp.dst = tcp_key->tcp_dst;
-               break;
-
-       case IPPROTO_UDP:
-               if (!(*attrs & (1 << OVS_KEY_ATTR_UDP)))
-                       return -EINVAL;
-               *attrs &= ~(1 << OVS_KEY_ATTR_UDP);
-
-               *key_len = SW_FLOW_KEY_OFFSET(ipv4.tp);
-               udp_key = nla_data(a[OVS_KEY_ATTR_UDP]);
-               swkey->ipv4.tp.src = udp_key->udp_src;
-               swkey->ipv4.tp.dst = udp_key->udp_dst;
-               break;
-
-       case IPPROTO_ICMP:
-               if (!(*attrs & (1 << OVS_KEY_ATTR_ICMP)))
-                       return -EINVAL;
-               *attrs &= ~(1 << OVS_KEY_ATTR_ICMP);
-
-               *key_len = SW_FLOW_KEY_OFFSET(ipv4.tp);
-               icmp_key = nla_data(a[OVS_KEY_ATTR_ICMP]);
-               swkey->ipv4.tp.src = htons(icmp_key->icmp_type);
-               swkey->ipv4.tp.dst = htons(icmp_key->icmp_code);
-               break;
-       }
-
-       return 0;
-}
-
-static int ipv6_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_len,
-                                 const struct nlattr *a[], u64 *attrs)
-{
-       const struct ovs_key_icmpv6 *icmpv6_key;
-       const struct ovs_key_tcp *tcp_key;
-       const struct ovs_key_udp *udp_key;
-
-       switch (swkey->ip.proto) {
-       case IPPROTO_TCP:
-               if (!(*attrs & (1 << OVS_KEY_ATTR_TCP)))
-                       return -EINVAL;
-               *attrs &= ~(1 << OVS_KEY_ATTR_TCP);
-
-               *key_len = SW_FLOW_KEY_OFFSET(ipv6.tp);
-               tcp_key = nla_data(a[OVS_KEY_ATTR_TCP]);
-               swkey->ipv6.tp.src = tcp_key->tcp_src;
-               swkey->ipv6.tp.dst = tcp_key->tcp_dst;
-               break;
-
-       case IPPROTO_UDP:
-               if (!(*attrs & (1 << OVS_KEY_ATTR_UDP)))
-                       return -EINVAL;
-               *attrs &= ~(1 << OVS_KEY_ATTR_UDP);
-
-               *key_len = SW_FLOW_KEY_OFFSET(ipv6.tp);
-               udp_key = nla_data(a[OVS_KEY_ATTR_UDP]);
-               swkey->ipv6.tp.src = udp_key->udp_src;
-               swkey->ipv6.tp.dst = udp_key->udp_dst;
-               break;
-
-       case IPPROTO_ICMPV6:
-               if (!(*attrs & (1 << OVS_KEY_ATTR_ICMPV6)))
-                       return -EINVAL;
-               *attrs &= ~(1 << OVS_KEY_ATTR_ICMPV6);
-
-               *key_len = SW_FLOW_KEY_OFFSET(ipv6.tp);
-               icmpv6_key = nla_data(a[OVS_KEY_ATTR_ICMPV6]);
-               swkey->ipv6.tp.src = htons(icmpv6_key->icmpv6_type);
-               swkey->ipv6.tp.dst = htons(icmpv6_key->icmpv6_code);
-
-               if (swkey->ipv6.tp.src == htons(NDISC_NEIGHBOUR_SOLICITATION) ||
-                   swkey->ipv6.tp.src == htons(NDISC_NEIGHBOUR_ADVERTISEMENT)) 
{
-                       const struct ovs_key_nd *nd_key;
-
-                       if (!(*attrs & (1 << OVS_KEY_ATTR_ND)))
-                               return -EINVAL;
-                       *attrs &= ~(1 << OVS_KEY_ATTR_ND);
-
-                       *key_len = SW_FLOW_KEY_OFFSET(ipv6.nd);
-                       nd_key = nla_data(a[OVS_KEY_ATTR_ND]);
-                       memcpy(&swkey->ipv6.nd.target, nd_key->nd_target,
-                              sizeof(swkey->ipv6.nd.target));
-                       memcpy(swkey->ipv6.nd.sll, nd_key->nd_sll, ETH_ALEN);
-                       memcpy(swkey->ipv6.nd.tll, nd_key->nd_tll, ETH_ALEN);
-               }
-               break;
-       }
-
-       return 0;
-}
-
 static int parse_flow_nlattrs(const struct nlattr *attr,
                              const struct nlattr *a[], u64 *attrsp)
 {
@@ -986,13 +984,12 @@ static int parse_flow_nlattrs(const struct nlattr *attr,
 }
 
 int ipv4_tun_from_nlattr(const struct nlattr *attr,
-                        struct ovs_key_ipv4_tunnel *tun_key)
+                        struct sw_flow_match *match)
 {
        struct nlattr *a;
        int rem;
        bool ttl = false;
-
-       memset(tun_key, 0, sizeof(*tun_key));
+       u16 tun_flags = 0;
 
        nla_for_each_nested(a, attr, rem) {
                int type = nla_type(a);
@@ -1012,37 +1009,39 @@ int ipv4_tun_from_nlattr(const struct nlattr *attr,
 
                switch (type) {
                case OVS_TUNNEL_KEY_ATTR_ID:
-                       tun_key->tun_id = nla_get_be64(a);
-                       tun_key->tun_flags |= OVS_TNL_F_KEY;
+                       SW_FLOW_KEY_PUT(match, tun_key.tun_id, nla_get_be64(a));
+                       tun_flags |= OVS_TNL_F_KEY;
                        break;
                case OVS_TUNNEL_KEY_ATTR_IPV4_SRC:
-                       tun_key->ipv4_src = nla_get_be32(a);
+                       SW_FLOW_KEY_PUT(match, tun_key.ipv4_src, 
nla_get_be32(a));
                        break;
                case OVS_TUNNEL_KEY_ATTR_IPV4_DST:
-                       tun_key->ipv4_dst = nla_get_be32(a);
+                       SW_FLOW_KEY_PUT(match, tun_key.ipv4_dst, 
nla_get_be32(a));
                        break;
                case OVS_TUNNEL_KEY_ATTR_TOS:
-                       tun_key->ipv4_tos = nla_get_u8(a);
+                       SW_FLOW_KEY_PUT(match, tun_key.ipv4_tos, nla_get_u8(a));
                        break;
                case OVS_TUNNEL_KEY_ATTR_TTL:
-                       tun_key->ipv4_ttl = nla_get_u8(a);
+                       SW_FLOW_KEY_PUT(match, tun_key.ipv4_ttl, nla_get_u8(a));
                        ttl = true;
                        break;
                case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT:
-                       tun_key->tun_flags |= OVS_TNL_F_DONT_FRAGMENT;
+                       tun_flags |= OVS_TNL_F_DONT_FRAGMENT;
                        break;
                case OVS_TUNNEL_KEY_ATTR_CSUM:
-                       tun_key->tun_flags |= OVS_TNL_F_CSUM;
+                       tun_flags |= OVS_TNL_F_CSUM;
                        break;
                default:
                        return -EINVAL;
 
                }
        }
+       SW_FLOW_KEY_PUT(match, tun_key.tun_flags,  tun_flags);
+
        if (rem > 0)
                return -EINVAL;
 
-       if (!tun_key->ipv4_dst)
+       if (!match->key->tun_key.ipv4_dst)
                return -EINVAL;
 
        if (!ttl)
@@ -1086,180 +1085,267 @@ int ipv4_tun_to_nlattr(struct sk_buff *skb,
 
 /**
  * ovs_flow_from_nlattrs - parses Netlink attributes into a flow key.
- * @swkey: receives the extracted flow key.
- * @key_lenp: number of bytes used in @swkey.
+ * @match: receives the extracted flow match information.
  * @attr: Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink attribute
  * sequence.
  */
-int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
-                     const struct nlattr *attr)
+int ovs_match_from_nlattrs(struct sw_flow_match *match,
+               const struct nlattr *attr)
 {
        const struct nlattr *a[OVS_KEY_ATTR_MAX + 1];
-       const struct ovs_key_ethernet *eth_key;
-       int key_len;
+       const struct nlattr *encap;
        u64 attrs;
        int err;
-
-       memset(swkey, 0, sizeof(struct sw_flow_key));
-       key_len = SW_FLOW_KEY_OFFSET(eth);
+       int k;
 
        err = parse_flow_nlattrs(attr, a, &attrs);
        if (err)
                return err;
 
-       /* Metadata attributes. */
-       if (attrs & (1 << OVS_KEY_ATTR_PRIORITY)) {
-               swkey->phy.priority = nla_get_u32(a[OVS_KEY_ATTR_PRIORITY]);
-               attrs &= ~(1 << OVS_KEY_ATTR_PRIORITY);
-       }
-       if (attrs & (1 << OVS_KEY_ATTR_IN_PORT)) {
-               u32 in_port = nla_get_u32(a[OVS_KEY_ATTR_IN_PORT]);
-               if (in_port >= DP_MAX_PORTS)
-                       return -EINVAL;
-               swkey->phy.in_port = in_port;
-               attrs &= ~(1 << OVS_KEY_ATTR_IN_PORT);
-       } else {
-               swkey->phy.in_port = DP_MAX_PORTS;
+       if (attrs & 1ULL << OVS_KEY_ATTR_ENCAP) {
+               encap = a[OVS_KEY_ATTR_ENCAP];
+               attrs &= ~(1ULL << OVS_KEY_ATTR_ENCAP);
+               if (nla_len(encap)) {
+                       __be16 tci = 0;
+                       __be16 eth_type = 0; /* ETH_P_8021Q */
+
+                       if (a[OVS_KEY_ATTR_ETHERTYPE])
+                               eth_type = 
nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]);
+                       if (a[OVS_KEY_ATTR_VLAN])
+                               tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]);
+
+                       if  ( (eth_type == htons(ETH_P_8021Q))
+                                       && (tci & htons(VLAN_TAG_PRESENT)) )
+                               err = parse_flow_nlattrs(encap, a, &attrs);
+                       else
+                               err = -EINVAL;
+
+                       if (err)
+                               return err;
+               }
        }
-       if (attrs & (1 << OVS_KEY_ATTR_SKB_MARK)) {
-               uint32_t mark = nla_get_u32(a[OVS_KEY_ATTR_SKB_MARK]);
+
+       /* Save all attributes specified for prerequisite check */
+       match->key_attrs = attrs;
+
+       /* Set default values if not specified. */
+       if ((attrs & (1 << OVS_KEY_ATTR_IN_PORT)) == 0)
+               SW_FLOW_KEY_PUT(match, phy.in_port, DP_MAX_PORTS);
+
+       if ((attrs & (1 << OVS_KEY_ATTR_ETHERTYPE)) == 0)
+               SW_FLOW_KEY_PUT(match, eth.type, htons(ETH_P_802_2));
+
+       while (attrs) {
+               k = fls64(attrs) -1;
+               attrs &= ~(1ULL << k);
+
+               switch(k) {
+               case OVS_KEY_ATTR_PRIORITY:
+                       SW_FLOW_KEY_PUT(match, phy.priority,
+                                 nla_get_u32(a[OVS_KEY_ATTR_PRIORITY]));
+                       break;
+
+               case OVS_KEY_ATTR_IN_PORT:
+                       {
+                               u32 in_port = 
nla_get_u32(a[OVS_KEY_ATTR_IN_PORT]);
+
+                               if (in_port >= DP_MAX_PORTS)
+                                       return -EINVAL;
+                               SW_FLOW_KEY_PUT(match, phy.in_port, in_port);
+                       }
+                       break;
+
+               case 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 (mark != 0)
-                       return -EINVAL;
+                               if (mark != 0)
+                                       return -EINVAL;
 #endif
-               swkey->phy.skb_mark = mark;
-               attrs &= ~(1 << OVS_KEY_ATTR_SKB_MARK);
-       }
+                               SW_FLOW_KEY_PUT(match, phy.skb_mark, mark);
+                       }
+                       break;
 
-       if (attrs & (1ULL << OVS_KEY_ATTR_TUNNEL)) {
-               err = ipv4_tun_from_nlattr(a[OVS_KEY_ATTR_TUNNEL], 
&swkey->tun_key);
-               if (err)
-                       return err;
+               case OVS_KEY_ATTR_TUNNEL:
+                       if (ipv4_tun_from_nlattr(a[OVS_KEY_ATTR_TUNNEL], match))
+                               return -EINVAL;
 
-               attrs &= ~(1ULL << OVS_KEY_ATTR_TUNNEL);
-       }
+                       break;
 
-       /* Data attributes. */
-       if (!(attrs & (1 << OVS_KEY_ATTR_ETHERNET)))
-               return -EINVAL;
-       attrs &= ~(1 << OVS_KEY_ATTR_ETHERNET);
+               case OVS_KEY_ATTR_ETHERNET:
+                       {
+                               const struct ovs_key_ethernet *eth_key;
 
-       eth_key = nla_data(a[OVS_KEY_ATTR_ETHERNET]);
-       memcpy(swkey->eth.src, eth_key->eth_src, ETH_ALEN);
-       memcpy(swkey->eth.dst, eth_key->eth_dst, ETH_ALEN);
+                               eth_key = nla_data(a[OVS_KEY_ATTR_ETHERNET]);
+                               SW_FLOW_KEY_MEMCPY(match, eth.src,
+                                               eth_key->eth_src, ETH_ALEN);
+                               SW_FLOW_KEY_MEMCPY(match, eth.dst,
+                                               eth_key->eth_dst, ETH_ALEN);
+                       }
+                       break;
 
-       if (attrs & (1u << OVS_KEY_ATTR_ETHERTYPE) &&
-           nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]) == htons(ETH_P_8021Q)) {
-               const struct nlattr *encap;
-               __be16 tci;
+               case OVS_KEY_ATTR_VLAN:
+                       {
+                               __be16 tci;
 
-               if (attrs != ((1 << OVS_KEY_ATTR_VLAN) |
-                             (1 << OVS_KEY_ATTR_ETHERTYPE) |
-                             (1 << OVS_KEY_ATTR_ENCAP)))
-                       return -EINVAL;
+                               tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]);
+                               SW_FLOW_KEY_PUT(match, eth.tci, tci);
+                       }
+                       break;
 
-               encap = a[OVS_KEY_ATTR_ENCAP];
-               tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]);
-               if (tci & htons(VLAN_TAG_PRESENT)) {
-                       swkey->eth.tci = tci;
+               case OVS_KEY_ATTR_ETHERTYPE:
+                       {
+                               __be16 eth_type;
 
-                       err = parse_flow_nlattrs(encap, a, &attrs);
-                       if (err)
-                               return err;
-               } else if (!tci) {
-                       /* Corner case for truncated 802.1Q header. */
-                       if (nla_len(encap))
-                               return -EINVAL;
+                               eth_type = 
nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]);
 
-                       swkey->eth.type = htons(ETH_P_8021Q);
-                       *key_lenp = key_len;
-                       return 0;
-               } else {
-                       return -EINVAL;
-               }
-       }
+                               if (ntohs(eth_type) < ETH_P_802_3_MIN)
+                                       return -EINVAL;
 
-       if (attrs & (1 << OVS_KEY_ATTR_ETHERTYPE)) {
-               swkey->eth.type = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]);
-               if (ntohs(swkey->eth.type) < ETH_P_802_3_MIN)
-                       return -EINVAL;
-               attrs &= ~(1 << OVS_KEY_ATTR_ETHERTYPE);
-       } else {
-               swkey->eth.type = htons(ETH_P_802_2);
-       }
+                               SW_FLOW_KEY_PUT(match, eth.type, eth_type);
+                       }
+                       break;
 
-       if (swkey->eth.type == htons(ETH_P_IP)) {
-               const struct ovs_key_ipv4 *ipv4_key;
+               case OVS_KEY_ATTR_IPV4:
+                       {
+                               const struct ovs_key_ipv4 *ipv4_key;
 
-               if (!(attrs & (1 << OVS_KEY_ATTR_IPV4)))
-                       return -EINVAL;
-               attrs &= ~(1 << OVS_KEY_ATTR_IPV4);
+                               ipv4_key = nla_data(a[OVS_KEY_ATTR_IPV4]);
+                               if (ipv4_key->ipv4_frag > OVS_FRAG_TYPE_MAX)
+                                       return -EINVAL;
+                               SW_FLOW_KEY_PUT(match, ip.proto,
+                                               ipv4_key->ipv4_proto);
+                               SW_FLOW_KEY_PUT(match, ip.tos,
+                                               ipv4_key->ipv4_tos);
+                               SW_FLOW_KEY_PUT(match, ip.ttl,
+                                               ipv4_key->ipv4_ttl);
+                               SW_FLOW_KEY_PUT(match, ip.frag,
+                                               ipv4_key->ipv4_frag);
+                               SW_FLOW_KEY_PUT(match, ipv4.addr.src,
+                                               ipv4_key->ipv4_src);
+                               SW_FLOW_KEY_PUT(match, ipv4.addr.dst,
+                                               ipv4_key->ipv4_dst);
+                       }
+                       break;
 
-               key_len = SW_FLOW_KEY_OFFSET(ipv4.addr);
-               ipv4_key = nla_data(a[OVS_KEY_ATTR_IPV4]);
-               if (ipv4_key->ipv4_frag > OVS_FRAG_TYPE_MAX)
-                       return -EINVAL;
-               swkey->ip.proto = ipv4_key->ipv4_proto;
-               swkey->ip.tos = ipv4_key->ipv4_tos;
-               swkey->ip.ttl = ipv4_key->ipv4_ttl;
-               swkey->ip.frag = ipv4_key->ipv4_frag;
-               swkey->ipv4.addr.src = ipv4_key->ipv4_src;
-               swkey->ipv4.addr.dst = ipv4_key->ipv4_dst;
-
-               if (swkey->ip.frag != OVS_FRAG_TYPE_LATER) {
-                       err = ipv4_flow_from_nlattrs(swkey, &key_len, a, 
&attrs);
-                       if (err)
-                               return err;
-               }
-       } else if (swkey->eth.type == htons(ETH_P_IPV6)) {
-               const struct ovs_key_ipv6 *ipv6_key;
+               case OVS_KEY_ATTR_IPV6:
+                       {
+                               const struct ovs_key_ipv6 *ipv6_key;
 
-               if (!(attrs & (1 << OVS_KEY_ATTR_IPV6)))
-                       return -EINVAL;
-               attrs &= ~(1 << OVS_KEY_ATTR_IPV6);
+                               ipv6_key = nla_data(a[OVS_KEY_ATTR_IPV6]);
+                               if (ipv6_key->ipv6_frag > OVS_FRAG_TYPE_MAX)
+                                       return -EINVAL;
+                               SW_FLOW_KEY_PUT(match, ipv6.label,
+                                               ipv6_key->ipv6_label);
+                               SW_FLOW_KEY_PUT(match, ip.proto,
+                                               ipv6_key->ipv6_proto);
+                               SW_FLOW_KEY_PUT(match, ip.tos,
+                                               ipv6_key->ipv6_tclass);
+                               SW_FLOW_KEY_PUT(match, ip.ttl,
+                                               ipv6_key->ipv6_hlimit);
+                               SW_FLOW_KEY_PUT(match, ip.frag,
+                                               ipv6_key->ipv6_frag);
+                               SW_FLOW_KEY_MEMCPY(match, ipv6.addr.src,
+                                               ipv6_key->ipv6_src,
+                                               
sizeof(match->key->ipv6.addr.src));
+                               SW_FLOW_KEY_MEMCPY(match, ipv6.addr.dst,
+                                               ipv6_key->ipv6_dst,
+                                               
sizeof(match->key->ipv6.addr.dst));
 
-               key_len = SW_FLOW_KEY_OFFSET(ipv6.label);
-               ipv6_key = nla_data(a[OVS_KEY_ATTR_IPV6]);
-               if (ipv6_key->ipv6_frag > OVS_FRAG_TYPE_MAX)
-                       return -EINVAL;
-               swkey->ipv6.label = ipv6_key->ipv6_label;
-               swkey->ip.proto = ipv6_key->ipv6_proto;
-               swkey->ip.tos = ipv6_key->ipv6_tclass;
-               swkey->ip.ttl = ipv6_key->ipv6_hlimit;
-               swkey->ip.frag = ipv6_key->ipv6_frag;
-               memcpy(&swkey->ipv6.addr.src, ipv6_key->ipv6_src,
-                      sizeof(swkey->ipv6.addr.src));
-               memcpy(&swkey->ipv6.addr.dst, ipv6_key->ipv6_dst,
-                      sizeof(swkey->ipv6.addr.dst));
-
-               if (swkey->ip.frag != OVS_FRAG_TYPE_LATER) {
-                       err = ipv6_flow_from_nlattrs(swkey, &key_len, a, 
&attrs);
-                       if (err)
-                               return err;
-               }
-       } else if (swkey->eth.type == htons(ETH_P_ARP) ||
-                  swkey->eth.type == htons(ETH_P_RARP)) {
-               const struct ovs_key_arp *arp_key;
+                       }
+                       break;
 
-               if (!(attrs & (1 << OVS_KEY_ATTR_ARP)))
-                       return -EINVAL;
-               attrs &= ~(1 << OVS_KEY_ATTR_ARP);
+               case OVS_KEY_ATTR_ARP:
+                       {
+                               const struct ovs_key_arp *arp_key;
 
-               key_len = SW_FLOW_KEY_OFFSET(ipv4.arp);
-               arp_key = nla_data(a[OVS_KEY_ATTR_ARP]);
-               swkey->ipv4.addr.src = arp_key->arp_sip;
-               swkey->ipv4.addr.dst = arp_key->arp_tip;
-               if (arp_key->arp_op & htons(0xff00))
+                               arp_key = nla_data(a[OVS_KEY_ATTR_ARP]);
+                               SW_FLOW_KEY_PUT(match, ipv4.addr.src,
+                                               arp_key->arp_sip);
+                               SW_FLOW_KEY_PUT(match, ipv4.addr.dst,
+                                               arp_key->arp_tip);
+                               if (arp_key->arp_op & htons(0xff00))
+                                       return -EINVAL;
+                               SW_FLOW_KEY_PUT(match, ip.proto,
+                                               ntohs(arp_key->arp_op));
+                               SW_FLOW_KEY_MEMCPY(match, ipv4.arp.sha,
+                                               arp_key->arp_sha,
+                                               ETH_ALEN);
+                               SW_FLOW_KEY_MEMCPY(match, ipv4.arp.tha,
+                                               arp_key->arp_tha,
+                                               ETH_ALEN);
+                       }
+                       break;
+
+               case OVS_KEY_ATTR_TCP:
+                       {
+                               const struct ovs_key_tcp *tcp_key;
+
+                               tcp_key = nla_data(a[OVS_KEY_ATTR_TCP]);
+                               SW_FLOW_KEY_PUT(match, ipv4.tp.src,
+                                               tcp_key->tcp_src);
+                               SW_FLOW_KEY_PUT(match, ipv4.tp.dst,
+                                               tcp_key->tcp_dst);
+                       }
+                       break;
+
+               case OVS_KEY_ATTR_UDP:
+                       {
+                               const struct ovs_key_udp *udp_key;
+
+                               udp_key = nla_data(a[OVS_KEY_ATTR_UDP]);
+                               SW_FLOW_KEY_PUT(match, ipv4.tp.src,
+                                               udp_key->udp_src);
+                               SW_FLOW_KEY_PUT(match, ipv4.tp.dst,
+                                               udp_key->udp_dst);
+                       }
+                       break;
+
+               case OVS_KEY_ATTR_ICMP:
+                       {
+                               const struct ovs_key_icmp *icmp_key;
+
+                               icmp_key = nla_data(a[OVS_KEY_ATTR_ICMP]);
+                               SW_FLOW_KEY_PUT(match, ipv4.tp.src,
+                                               htons(icmp_key->icmp_type));
+                               SW_FLOW_KEY_PUT(match, ipv4.tp.dst,
+                                               htons(icmp_key->icmp_code));
+                       }
+                       break;
+
+               case OVS_KEY_ATTR_ICMPV6:
+                       {
+                               const struct ovs_key_icmpv6 *icmpv6_key;
+
+                               icmpv6_key = nla_data(a[OVS_KEY_ATTR_ICMPV6]);
+                               SW_FLOW_KEY_PUT(match, ipv6.tp.src,
+                                               htons(icmpv6_key->icmpv6_type));
+                               SW_FLOW_KEY_PUT(match, ipv6.tp.dst,
+                                               htons(icmpv6_key->icmpv6_code));
+                       }
+                       break;
+
+               case OVS_KEY_ATTR_ND:
+                       {
+                               const struct ovs_key_nd *nd_key;
+
+                               nd_key = nla_data(a[OVS_KEY_ATTR_ND]);
+                               SW_FLOW_KEY_MEMCPY(match, ipv6.nd.target,
+                                       nd_key->nd_target,
+                                       sizeof(match->key->ipv6.nd.target));
+                               SW_FLOW_KEY_MEMCPY(match, ipv6.nd.sll,
+                                               nd_key->nd_sll, ETH_ALEN);
+                               SW_FLOW_KEY_MEMCPY(match, ipv6.nd.tll,
+                                               nd_key->nd_tll, ETH_ALEN);
+                       }
+                       break;
+
+               default:
                        return -EINVAL;
-               swkey->ip.proto = ntohs(arp_key->arp_op);
-               memcpy(swkey->ipv4.arp.sha, arp_key->arp_sha, ETH_ALEN);
-               memcpy(swkey->ipv4.arp.tha, arp_key->arp_tha, ETH_ALEN);
+               }
        }
 
-       if (attrs)
-               return -EINVAL;
-       *key_lenp = key_len;
-
        return 0;
 }
 
@@ -1279,51 +1365,31 @@ int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, 
int *key_lenp,
 int ovs_flow_metadata_from_nlattrs(struct sw_flow *flow, int key_len, const 
struct nlattr *attr)
 {
        struct ovs_key_ipv4_tunnel *tun_key = &flow->key.tun_key;
-       const struct nlattr *nla;
-       int rem;
+       const struct nlattr *a[OVS_KEY_ATTR_MAX + 1];
+       u64 attrs;
+       int err;
+       struct sw_flow_match match;
 
        flow->key.phy.in_port = DP_MAX_PORTS;
        flow->key.phy.priority = 0;
        flow->key.phy.skb_mark = 0;
        memset(tun_key, 0, sizeof(flow->key.tun_key));
 
-       nla_for_each_nested(nla, attr, rem) {
-               int type = nla_type(nla);
-
-               if (type <= OVS_KEY_ATTR_MAX && ovs_key_lens[type] > 0) {
-                       int err;
-
-                       if (nla_len(nla) != ovs_key_lens[type])
-                               return -EINVAL;
-
-                       switch (type) {
-                       case OVS_KEY_ATTR_PRIORITY:
-                               flow->key.phy.priority = nla_get_u32(nla);
-                               break;
+       err = parse_flow_nlattrs(attr, a, &attrs);
+       if (err)
+               return -EINVAL;
 
-                       case OVS_KEY_ATTR_TUNNEL:
-                               err = ipv4_tun_from_nlattr(nla, tun_key);
-                               if (err)
-                                       return err;
-                               break;
+       attrs &= (1 << OVS_KEY_ATTR_PRIORITY);
+       attrs &= (1 << OVS_KEY_ATTR_TUNNEL);
+       attrs &= (1 << OVS_KEY_ATTR_IN_PORT);
+       attrs &= (1 << OVS_KEY_ATTR_SKB_MARK);
 
-                       case OVS_KEY_ATTR_IN_PORT:
-                               if (nla_get_u32(nla) >= DP_MAX_PORTS)
-                                       return -EINVAL;
-                               flow->key.phy.in_port = nla_get_u32(nla);
-                               break;
+       if (attrs)
+               return -EINVAL;
 
-                       case OVS_KEY_ATTR_SKB_MARK:
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) && !defined(CONFIG_NETFILTER)
-                               if (nla_get_u32(nla) != 0)
-                                       return -EINVAL;
-#endif
-                               flow->key.phy.skb_mark = nla_get_u32(nla);
-                               break;
-                       }
-               }
-       }
-       if (rem)
+       ovs_match_init(&match, &flow->key);
+       err = ovs_match_from_nlattrs(&match, attr);
+       if (err)
                return -EINVAL;
 
        flow->hash = ovs_flow_hash(&flow->key,
diff --git a/datapath/flow.h b/datapath/flow.h
index dba66cf..09b1482 100644
--- a/datapath/flow.h
+++ b/datapath/flow.h
@@ -129,6 +129,16 @@ struct sw_flow {
        u8 tcp_flags;           /* Union of seen TCP flags. */
 };
 
+struct sw_flow_match {
+       struct sw_flow_key *key;
+       u8 n_writes[sizeof(struct sw_flow_key)];  /* Per byte write count. */
+       u64  key_attrs;   /* OVS_KEY_ATTR_XXXs used to populate the keys */
+};
+
+void ovs_match_init(struct sw_flow_match *match, struct sw_flow_key *key);
+bool ovs_match_check_prerequisite(const struct sw_flow_match *match);
+int  ovs_match_get_len(struct sw_flow_match *match);
+
 struct arp_eth_header {
        __be16      ar_hrd;     /* format of hardware address   */
        __be16      ar_pro;     /* format of protocol address   */
@@ -159,7 +169,7 @@ void ovs_flow_used(struct sw_flow *, struct sk_buff *);
 u64 ovs_flow_used_time(unsigned long flow_jiffies);
 
 int ovs_flow_to_nlattrs(const struct sw_flow_key *, struct sk_buff *);
-int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
+int ovs_match_from_nlattrs(struct sw_flow_match *match,
                      const struct nlattr *);
 int ovs_flow_metadata_from_nlattrs(struct sw_flow *flow, int key_len,
                                   const struct nlattr *attr);
@@ -176,6 +186,8 @@ struct flow_table {
        bool keep_flows;
 };
 
+
+
 static inline int ovs_flow_tbl_count(struct flow_table *table)
 {
        return table->count;
@@ -200,7 +212,7 @@ void ovs_flow_tbl_remove(struct flow_table *table, struct 
sw_flow *flow);
 struct sw_flow *ovs_flow_tbl_next(struct flow_table *table, u32 *bucket, u32 
*idx);
 extern const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1];
 int ipv4_tun_from_nlattr(const struct nlattr *attr,
-                        struct ovs_key_ipv4_tunnel *tun_key);
+                        struct sw_flow_match *match);
 int ipv4_tun_to_nlattr(struct sk_buff *skb,
                        const struct ovs_key_ipv4_tunnel *tun_key);
 
-- 
1.7.9.5

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to