Masked set actions add a mask, immediately following the netlink
attribute data, within the netlink attribute itself.  Thus the key
attribute size for a masked set action is exactly double of the
non-masked set action.

Signed-off-by: Jarno Rajahalme <jrajaha...@nicira.com>
---
 lib/odp-execute.c |  241 ++++++++++++++++++++++++++++++++++++++++++++---------
 lib/odp-util.c    |   97 +++++++++++++--------
 lib/odp-util.h    |    3 +
 3 files changed, 268 insertions(+), 73 deletions(-)

diff --git a/lib/odp-execute.c b/lib/odp-execute.c
index 37e44e3..919b65e 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -18,6 +18,7 @@
 #include <config.h>
 #include "odp-execute.h"
 #include <linux/openvswitch.h>
+#include <netinet/ip6.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -29,15 +30,141 @@
 #include "unaligned.h"
 #include "util.h"
 
+/* Masked copy of an ethernet address. 'src' is already properly masked. */
 static void
-odp_eth_set_addrs(struct ofpbuf *packet,
-                  const struct ovs_key_ethernet *eth_key)
+ether_addr_copy_masked(uint8_t *dst, const uint8_t *src,
+                       const uint8_t *mask)
+{
+    int i;
+
+    for (i=0; i < ETH_ADDR_LEN; i++) {
+        dst[i] = src[i] | (dst[i] & ~mask[i]);
+    }
+}
+
+static void
+odp_eth_set_addrs(struct ofpbuf *packet, const struct ovs_key_ethernet *key,
+                  const struct ovs_key_ethernet *mask)
 {
     struct eth_header *eh = ofpbuf_l2(packet);
 
     if (eh) {
-        memcpy(eh->eth_src, eth_key->eth_src, sizeof eh->eth_src);
-        memcpy(eh->eth_dst, eth_key->eth_dst, sizeof eh->eth_dst);
+        if (!mask) {
+            memcpy(eh->eth_src, key->eth_src, sizeof eh->eth_src);
+            memcpy(eh->eth_dst, key->eth_dst, sizeof eh->eth_dst);
+        } else {
+            ether_addr_copy_masked(eh->eth_src, key->eth_src, mask->eth_src);
+            ether_addr_copy_masked(eh->eth_dst, key->eth_dst, mask->eth_dst);
+        }
+    }
+}
+
+static void
+odp_set_ipv4(struct ofpbuf *packet, const struct ovs_key_ipv4 *key,
+             const struct ovs_key_ipv4 *mask)
+{
+    if (!mask) {
+        packet_set_ipv4(packet, key->ipv4_src, key->ipv4_dst,
+                        key->ipv4_tos, key->ipv4_ttl);
+    } else {
+        struct ip_header *nh = ofpbuf_l3(packet);
+
+        packet_set_ipv4(packet,
+                        key->ipv4_src
+                        | (get_16aligned_be32(&nh->ip_src) & ~mask->ipv4_src),
+                        key->ipv4_dst
+                        | (get_16aligned_be32(&nh->ip_dst) & ~mask->ipv4_dst),
+                        key->ipv4_tos | (nh->ip_tos & ~mask->ipv4_tos),
+                        key->ipv4_ttl | (nh->ip_ttl & ~mask->ipv4_ttl));
+    }
+}
+
+static const ovs_be32 *
+mask_ipv6_addr(const ovs_be16 *old, const ovs_be32 *addr_,
+               const ovs_be32 *mask_, ovs_be32 *masked_)
+{
+    const ovs_be16 *addr = (const ovs_be16 *)addr_;
+    const ovs_be16 *mask = (const ovs_be16 *)mask_;
+    ovs_be16 *masked = (ovs_be16 *)masked_;
+
+    masked[0] = addr[0] | (old[0] & ~mask[0]);
+    masked[1] = addr[1] | (old[1] & ~mask[1]);
+    masked[2] = addr[2] | (old[2] & ~mask[2]);
+    masked[3] = addr[3] | (old[3] & ~mask[3]);
+    masked[4] = addr[4] | (old[4] & ~mask[4]);
+    masked[5] = addr[5] | (old[5] & ~mask[5]);
+    masked[6] = addr[6] | (old[6] & ~mask[6]);
+    masked[7] = addr[7] | (old[7] & ~mask[7]);
+
+    return masked_;
+}
+
+static void
+odp_set_ipv6(struct ofpbuf *packet, const struct ovs_key_ipv6 *key,
+             const struct ovs_key_ipv6 *mask)
+{
+    if (!mask) {
+        packet_set_ipv6(packet, key->ipv6_proto, key->ipv6_src, key->ipv6_dst,
+                        key->ipv6_tclass, key->ipv6_label, key->ipv6_hlimit);
+    } else {
+        struct ovs_16aligned_ip6_hdr *nh = ofpbuf_l3(packet);
+        ovs_be32 sbuf[4], dbuf[4];
+        uint8_t old_tc = ntohl(get_16aligned_be32(&nh->ip6_flow)) >> 20;
+        ovs_be32 old_fl = get_16aligned_be32(&nh->ip6_flow) & htonl(0xfffff);
+
+        packet_set_ipv6(packet, key->ipv6_proto,
+                        mask_ipv6_addr((const ovs_be16 *)nh->ip6_src.be32,
+                                       key->ipv6_src, mask->ipv6_src, sbuf),
+                        mask_ipv6_addr((const ovs_be16 *)nh->ip6_dst.be32,
+                                       key->ipv6_dst, mask->ipv6_dst, dbuf),
+                        key->ipv6_tclass | (old_tc & ~mask->ipv6_tclass),
+                        key->ipv6_label | (old_fl & ~mask->ipv6_label),
+                        key->ipv6_hlimit | (nh->ip6_hlim & 
~mask->ipv6_hlimit));
+    }
+}
+
+static void
+odp_set_tcp(struct ofpbuf *packet, const struct ovs_key_tcp *key,
+             const struct ovs_key_tcp *mask)
+{
+    if (!mask) {
+        packet_set_tcp_port(packet, key->tcp_src, key->tcp_dst);
+    } else {
+        struct tcp_header *th = ofpbuf_l4(packet);
+
+        packet_set_tcp_port(packet,
+                            key->tcp_src | (th->tcp_src & ~mask->tcp_src),
+                            key->tcp_dst | (th->tcp_dst & ~mask->tcp_dst));
+    }
+}
+
+static void
+odp_set_udp(struct ofpbuf *packet, const struct ovs_key_udp *key,
+             const struct ovs_key_udp *mask)
+{
+    if (!mask) {
+        packet_set_udp_port(packet, key->udp_src, key->udp_dst);
+    } else {
+        struct udp_header *uh = ofpbuf_l4(packet);
+
+        packet_set_udp_port(packet,
+                            key->udp_src | (uh->udp_src & ~mask->udp_src),
+                            key->udp_dst | (uh->udp_dst & ~mask->udp_dst));
+    }
+}
+
+static void
+odp_set_sctp(struct ofpbuf *packet, const struct ovs_key_sctp *key,
+             const struct ovs_key_sctp *mask)
+{
+    if (!mask) {
+        packet_set_sctp_port(packet, key->sctp_src, key->sctp_dst);
+    } else {
+        struct sctp_header *sh = ofpbuf_l4(packet);
+
+        packet_set_sctp_port(packet,
+                             key->sctp_src | (sh->sctp_src & ~mask->sctp_src),
+                             key->sctp_dst | (sh->sctp_dst & ~mask->sctp_dst));
     }
 }
 
@@ -51,88 +178,124 @@ odp_set_tunnel_action(const struct nlattr *a, struct 
flow_tnl *tun_key)
 }
 
 static void
-set_arp(struct ofpbuf *packet, const struct ovs_key_arp *arp_key)
+set_arp(struct ofpbuf *packet, const struct ovs_key_arp *key,
+        const struct ovs_key_arp *mask)
 {
     struct arp_eth_header *arp = ofpbuf_l3(packet);
 
-    arp->ar_op = arp_key->arp_op;
-    memcpy(arp->ar_sha, arp_key->arp_sha, ETH_ADDR_LEN);
-    put_16aligned_be32(&arp->ar_spa, arp_key->arp_sip);
-    memcpy(arp->ar_tha, arp_key->arp_tha, ETH_ADDR_LEN);
-    put_16aligned_be32(&arp->ar_tpa, arp_key->arp_tip);
+    if (!mask) {
+        arp->ar_op = key->arp_op;
+        memcpy(arp->ar_sha, key->arp_sha, ETH_ADDR_LEN);
+        put_16aligned_be32(&arp->ar_spa, key->arp_sip);
+        memcpy(arp->ar_tha, key->arp_tha, ETH_ADDR_LEN);
+        put_16aligned_be32(&arp->ar_tpa, key->arp_tip);
+    } else {
+        ovs_be32 ar_spa = get_16aligned_be32(&arp->ar_spa);
+        ovs_be32 ar_tpa = get_16aligned_be32(&arp->ar_tpa);
+
+        arp->ar_op = key->arp_op | (arp->ar_op & ~mask->arp_op);
+        ether_addr_copy_masked(arp->ar_sha, key->arp_sha, mask->arp_sha);
+        put_16aligned_be32(&arp->ar_spa,
+                           key->arp_sip | (ar_spa & ~mask->arp_sip));
+        ether_addr_copy_masked(arp->ar_tha, key->arp_tha, mask->arp_tha);
+        put_16aligned_be32(&arp->ar_tpa,
+                           key->arp_tip | (ar_tpa & ~mask->arp_tip));
+    }
 }
 
+#define get_mask(a, type) ((const type *)nl_attr_get(a) + 1)
+
 static void
-odp_execute_set_action(struct ofpbuf *packet, const struct nlattr *a,
-                       struct pkt_metadata *md)
+odp_execute_set_action(struct pkt_metadata *md, struct ofpbuf *packet,
+                       const struct nlattr *a)
 {
     enum ovs_key_attr type = nl_attr_type(a);
-    const struct ovs_key_ipv4 *ipv4_key;
-    const struct ovs_key_ipv6 *ipv6_key;
-    const struct ovs_key_tcp *tcp_key;
-    const struct ovs_key_udp *udp_key;
-    const struct ovs_key_sctp *sctp_key;
+    size_t len = nl_attr_get_size(a);
 
     switch (type) {
     case OVS_KEY_ATTR_PRIORITY:
-        md->skb_priority = nl_attr_get_u32(a);
+        md->skb_priority = (len != 2 * sizeof(uint32_t))
+            ? nl_attr_get_u32(a)
+            : nl_attr_get_u32(a) | (md->skb_priority & ~*get_mask(a, 
uint32_t));
         break;
 
     case OVS_KEY_ATTR_TUNNEL:
+        /* Masked data not supported for tunnel. */
         odp_set_tunnel_action(a, &md->tunnel);
         break;
 
     case OVS_KEY_ATTR_SKB_MARK:
-        md->pkt_mark = nl_attr_get_u32(a);
+        md->pkt_mark = (len != 2 * sizeof(uint32_t))
+            ? nl_attr_get_u32(a)
+            : nl_attr_get_u32(a) | (md->pkt_mark & ~*get_mask(a, uint32_t));
         break;
 
     case OVS_KEY_ATTR_ETHERNET:
         odp_eth_set_addrs(packet,
-                          nl_attr_get_unspec(a, sizeof(struct 
ovs_key_ethernet)));
+                          nl_attr_get_unspec(a, sizeof(struct 
ovs_key_ethernet)),
+                          (len == 2 * sizeof(struct ovs_key_ethernet))
+                          ? get_mask(a, struct ovs_key_ethernet) : NULL);
         break;
 
     case OVS_KEY_ATTR_IPV4:
-        ipv4_key = nl_attr_get_unspec(a, sizeof(struct ovs_key_ipv4));
-        packet_set_ipv4(packet, ipv4_key->ipv4_src, ipv4_key->ipv4_dst,
-                        ipv4_key->ipv4_tos, ipv4_key->ipv4_ttl);
+        odp_set_ipv4(packet, nl_attr_get_unspec(a, sizeof(struct 
ovs_key_ipv4)),
+                     (len == 2 * sizeof(struct ovs_key_ipv4))
+                     ? get_mask(a, struct ovs_key_ipv4) : NULL);
         break;
 
     case OVS_KEY_ATTR_IPV6:
-        ipv6_key = nl_attr_get_unspec(a, sizeof(struct ovs_key_ipv6));
-        packet_set_ipv6(packet, ipv6_key->ipv6_proto, ipv6_key->ipv6_src,
-                        ipv6_key->ipv6_dst, ipv6_key->ipv6_tclass,
-                        ipv6_key->ipv6_label, ipv6_key->ipv6_hlimit);
+        odp_set_ipv6(packet, nl_attr_get_unspec(a, sizeof(struct 
ovs_key_ipv6)),
+                     (len == 2 * sizeof(struct ovs_key_ipv6))
+                     ? get_mask(a, struct ovs_key_ipv6) : NULL);
         break;
 
     case OVS_KEY_ATTR_TCP:
-        tcp_key = nl_attr_get_unspec(a, sizeof(struct ovs_key_tcp));
-        packet_set_tcp_port(packet, tcp_key->tcp_src, tcp_key->tcp_dst);
+        odp_set_tcp(packet, nl_attr_get_unspec(a, sizeof(struct ovs_key_tcp)),
+                    (len == 2 * sizeof(struct ovs_key_tcp))
+                    ? get_mask(a, struct ovs_key_tcp) : NULL);
         break;
 
     case OVS_KEY_ATTR_UDP:
-        udp_key = nl_attr_get_unspec(a, sizeof(struct ovs_key_udp));
-        packet_set_udp_port(packet, udp_key->udp_src, udp_key->udp_dst);
+        odp_set_udp(packet, nl_attr_get_unspec(a, sizeof(struct ovs_key_udp)),
+                    (len == 2 * sizeof(struct ovs_key_udp))
+                    ? get_mask(a, struct ovs_key_udp) : NULL);
         break;
 
     case OVS_KEY_ATTR_SCTP:
-        sctp_key = nl_attr_get_unspec(a, sizeof(struct ovs_key_sctp));
-        packet_set_sctp_port(packet, sctp_key->sctp_src, sctp_key->sctp_dst);
+        odp_set_sctp(packet, nl_attr_get_unspec(a, sizeof(struct 
ovs_key_sctp)),
+                     (len == 2 * sizeof(struct ovs_key_sctp))
+                     ? get_mask(a, struct ovs_key_sctp) : NULL);
         break;
 
     case OVS_KEY_ATTR_MPLS:
-         set_mpls_lse(packet, nl_attr_get_be32(a));
-         break;
+        if (len == 2 * sizeof(ovs_be32)) {
+            struct mpls_hdr *mh = ofpbuf_l2_5(packet);
+            if (mh) {
+                put_16aligned_be32(&mh->mpls_lse, nl_attr_get_be32(a)
+                                   | (get_16aligned_be32(&mh->mpls_lse)
+                                      & ~*get_mask(a, ovs_be32)));
+            }
+        } else {
+            set_mpls_lse(packet, nl_attr_get_be32(a));
+        }
+        break;
 
     case OVS_KEY_ATTR_ARP:
-        set_arp(packet, nl_attr_get_unspec(a, sizeof(struct ovs_key_arp)));
+        set_arp(packet, nl_attr_get_unspec(a, sizeof(struct ovs_key_arp)),
+                (len == 2 * sizeof(struct ovs_key_arp))
+                ? get_mask(a, struct ovs_key_arp) : NULL);
         break;
 
     case OVS_KEY_ATTR_DP_HASH:
-        md->dp_hash = nl_attr_get_u32(a);
+        md->dp_hash = (len != 2 * sizeof(uint32_t))
+            ? nl_attr_get_u32(a)
+            : nl_attr_get_u32(a) | (md->dp_hash & ~*get_mask(a, uint32_t));
         break;
 
     case OVS_KEY_ATTR_RECIRC_ID:
-        md->recirc_id = nl_attr_get_u32(a);
+        md->recirc_id = (len != 2 * sizeof(uint32_t))
+            ? nl_attr_get_u32(a)
+            : nl_attr_get_u32(a) | (md->recirc_id & ~*get_mask(a, uint32_t));
         break;
 
     case OVS_KEY_ATTR_UNSPEC:
@@ -239,7 +402,7 @@ odp_execute_actions__(void *dp, struct ofpbuf *packet, bool 
steal,
             break;
 
         case OVS_ACTION_ATTR_SET:
-            odp_execute_set_action(packet, nl_attr_get(a), md);
+            odp_execute_set_action(md, packet, nl_attr_get(a));
             break;
 
         case OVS_ACTION_ATTR_SAMPLE:
diff --git a/lib/odp-util.c b/lib/odp-util.c
index b58f1c0..f71b74d 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -1000,47 +1000,63 @@ format_odp_key_attr(const struct nlattr *a, const 
struct nlattr *ma,
     char namebuf[OVS_KEY_ATTR_BUFSIZE];
     int expected_len;
     bool is_exact;
-
-    is_exact = ma ? odp_mask_attr_is_exact(ma) : true;
+    bool bad_key_len;
+    bool bad_mask_len;
+    size_t size = nl_attr_get_size(a);
+    struct nlattr mask[1 + DIV_ROUND_UP(sizeof(struct ovs_key_ipv6),
+                                        sizeof(struct nlattr))];
 
     ds_put_cstr(ds, ovs_key_attr_to_string(attr, namebuf, sizeof namebuf));
 
-    {
-        expected_len = odp_flow_key_attr_len(nl_attr_type(a));
-        if (expected_len != -2) {
-            bool bad_key_len = nl_attr_get_size(a) != expected_len;
-            bool bad_mask_len = ma && nl_attr_get_size(a) != expected_len;
-
-            if (bad_key_len || bad_mask_len) {
-                if (bad_key_len) {
-                    ds_put_format(ds, "(bad key length %"PRIuSIZE", expected 
%d)(",
-                                  nl_attr_get_size(a),
-                                  odp_flow_key_attr_len(nl_attr_type(a)));
-                }
-                format_generic_odp_key(a, ds);
-                if (bad_mask_len) {
-                    ds_put_char(ds, '/');
-                    ds_put_format(ds, "(bad mask length %"PRIuSIZE", expected 
%d)(",
-                                  nl_attr_get_size(ma),
-                                  odp_flow_key_attr_len(nl_attr_type(ma)));
-                }
-                format_generic_odp_key(ma, ds);
-                ds_put_char(ds, ')');
-                return;
+    expected_len = odp_flow_key_attr_len(nl_attr_type(a));
+    if (expected_len != -2) {
+        bad_key_len = size != expected_len;
+        bad_mask_len = ma && nl_attr_get_size(ma) != expected_len;
+
+        /* Set actions may have an inline mask, that immediately follows the
+         * data within the attribute.  In this case the size is twice the
+         * expected length for a set without a mask.  Copy the mask to 'mask'
+         * so that we can print it out using the existing code below. */
+        if (bad_key_len && !ma && expected_len > 0) {
+            if (size == 2 * expected_len) {
+                bad_key_len = false;
+                ovs_assert(expected_len <= sizeof(struct ovs_key_ipv6));
+                mask->nla_type = attr;
+                mask->nla_len = NLA_HDRLEN + expected_len;
+                memcpy(mask + 1, (char *)(a + 1) + expected_len, expected_len);
+                ma = mask;
+            }
+        }
+
+        if (bad_key_len || bad_mask_len) {
+            if (bad_key_len) {
+                ds_put_format(ds, "(bad key length %"PRIuSIZE", expected %d)(",
+                              size, odp_flow_key_attr_len(nl_attr_type(a)));
+            }
+            format_generic_odp_key(a, ds);
+            if (bad_mask_len) {
+                ds_put_char(ds, '/');
+                ds_put_format(ds, "(bad mask length %"PRIuSIZE", expected 
%d)(",
+                              nl_attr_get_size(ma),
+                              odp_flow_key_attr_len(nl_attr_type(ma)));
             }
+            format_generic_odp_key(ma, ds);
+            ds_put_char(ds, ')');
+            return;
         }
     }
 
+    is_exact = ma ? odp_mask_attr_is_exact(ma) : true;
+
     ds_put_char(ds, '(');
     switch (attr) {
     case OVS_KEY_ATTR_ENCAP:
-        if (ma && nl_attr_get_size(ma) && nl_attr_get_size(a)) {
-            odp_flow_format(nl_attr_get(a), nl_attr_get_size(a),
+        if (ma && nl_attr_get_size(ma) && size) {
+            odp_flow_format(nl_attr_get(a), size,
                             nl_attr_get(ma), nl_attr_get_size(ma), NULL, ds,
                             verbose);
-        } else if (nl_attr_get_size(a)) {
-            odp_flow_format(nl_attr_get(a), nl_attr_get_size(a), NULL, 0, NULL,
-                            ds, verbose);
+        } else if (size) {
+            odp_flow_format(nl_attr_get(a), size, NULL, 0, NULL, ds, verbose);
         }
         break;
 
@@ -1153,19 +1169,17 @@ format_odp_key_attr(const struct nlattr *a, const 
struct nlattr *ma,
     case OVS_KEY_ATTR_MPLS: {
         const struct ovs_key_mpls *mpls_key = nl_attr_get(a);
         const struct ovs_key_mpls *mpls_mask = NULL;
-        size_t size = nl_attr_get_size(a);
 
         if (!size || size % sizeof *mpls_key) {
-            ds_put_format(ds, "(bad key length %"PRIuSIZE")",
-                          nl_attr_get_size(a));
+            ds_put_format(ds, "(bad key length %"PRIuSIZE")", size);
             return;
         }
         if (!is_exact) {
             mpls_mask = nl_attr_get(ma);
-            if (nl_attr_get_size(a) != nl_attr_get_size(ma)) {
+            if (size != nl_attr_get_size(ma)) {
                 ds_put_format(ds, "(key length %"PRIuSIZE" != "
                               "mask length %"PRIuSIZE")",
-                              nl_attr_get_size(a), nl_attr_get_size(ma));
+                              size, nl_attr_get_size(ma));
                 return;
             }
         }
@@ -3491,6 +3505,21 @@ commit_set_action(struct ofpbuf *odp_actions, enum 
ovs_key_attr key_type,
     nl_msg_end_nested(odp_actions, offset);
 }
 
+/* Masked set actions have a mask following the data within the netlink
+ * attribute.  The data may have only masked bits set, i.e., the data bits
+ * corresponding to zero bits in the mask must also be zeroes. */
+void
+commit_masked_set_action(struct ofpbuf *odp_actions,
+                         enum ovs_key_attr key_type,
+                         const void *key, const void *mask, size_t key_size)
+{
+    size_t offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SET);
+    char *data = nl_msg_put_unspec_uninit(odp_actions, key_type, key_size * 2);
+    memcpy(data, key, key_size);
+    memcpy(data + key_size, mask, key_size);
+    nl_msg_end_nested(odp_actions, offset);
+}
+
 void
 odp_put_pkt_mark_action(const uint32_t pkt_mark,
                         struct ofpbuf *odp_actions)
diff --git a/lib/odp-util.h b/lib/odp-util.h
index 7bc64c7..fbd2984 100644
--- a/lib/odp-util.h
+++ b/lib/odp-util.h
@@ -179,6 +179,9 @@ const char *odp_key_fitness_to_string(enum odp_key_fitness);
 
 void commit_odp_tunnel_action(const struct flow *, struct flow *base,
                               struct ofpbuf *odp_actions);
+void commit_masked_set_action(struct ofpbuf *odp_actions,
+                              enum ovs_key_attr key_type, const void *key,
+                              const void *mask, size_t key_size);
 enum slow_path_reason commit_odp_actions(const struct flow *,
                                          struct flow *base,
                                          struct ofpbuf *odp_actions,
-- 
1.7.10.4

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

Reply via email to