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