X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=datapath%2Fflow.c;h=7a697a42fdf474e244abab6da25f6d09da5b1a18;hb=7061a497b10b0c830cebcd463ceac053ba81dea5;hp=0a6e040ad64097c39fa5887c00864e6c61f18a67;hpb=33fd9f59afeda7955a8d63e57ac81cc2a0760531;p=sliver-openvswitch.git diff --git a/datapath/flow.c b/datapath/flow.c index 0a6e040ad..7a697a42f 100644 --- a/datapath/flow.c +++ b/datapath/flow.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -54,8 +55,8 @@ static void update_range__(struct sw_flow_match *match, 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; @@ -102,6 +103,11 @@ static void update_range__(struct sw_flow_match *match, } \ } 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) @@ -130,6 +136,7 @@ static bool ovs_match_validate(const struct sw_flow_match *match, | (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) @@ -138,7 +145,7 @@ static bool ovs_match_validate(const struct sw_flow_match *match, /* Always allowed mask fields. */ mask_allowed |= ((1ULL << OVS_KEY_ATTR_TUNNEL) | (1ULL << OVS_KEY_ATTR_IN_PORT) - | (11ULL << OVS_KEY_ATTR_ETHERTYPE)); + | (1ULL << OVS_KEY_ATTR_ETHERTYPE)); /* Check key attributes. */ if (match->key->eth.type == htons(ETH_P_ARP) @@ -160,6 +167,12 @@ static bool ovs_match_validate(const struct sw_flow_match *match, 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)) @@ -186,6 +199,12 @@ static bool ovs_match_validate(const struct sw_flow_match *match, 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)) @@ -281,6 +300,12 @@ static bool udphdr_ok(struct sk_buff *skb) 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) + @@ -351,16 +376,17 @@ static bool icmp6hdr_ok(struct sk_buff *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 @@ -799,7 +825,6 @@ invalid: * 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. * @@ -900,6 +925,12 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key) 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); @@ -962,6 +993,12 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key) 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); @@ -974,10 +1011,16 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key) 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) @@ -985,35 +1028,43 @@ 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); } @@ -1021,33 +1072,34 @@ struct sw_flow *ovs_flow_lookup_unmasked_key(struct flow_table *table, 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; @@ -1096,6 +1148,7 @@ const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = { [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), @@ -1174,8 +1227,8 @@ static int parse_flow_nlattrs(const struct nlattr *attr, return __parse_flow_nlattrs(attr, a, attrsp, false); } -int ipv4_tun_from_nlattr(const struct nlattr *attr, - struct sw_flow_match *match, bool is_mask) +int ovs_ipv4_tun_from_nlattr(const struct nlattr *attr, + struct sw_flow_match *match, bool is_mask) { struct nlattr *a; int rem; @@ -1202,7 +1255,7 @@ int ipv4_tun_from_nlattr(const struct nlattr *attr, 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; } @@ -1248,22 +1301,24 @@ int ipv4_tun_from_nlattr(const struct nlattr *attr, return -EINVAL; } - if (!match->key->tun_key.ipv4_dst) { - OVS_NLERR("IPv4 tunnel destination address is zero.\n"); - return -EINVAL; - } + if (!is_mask) { + if (!match->key->tun_key.ipv4_dst) { + OVS_NLERR("IPv4 tunnel destination address is zero.\n"); + return -EINVAL; + } - if (!ttl) { - OVS_NLERR("IPv4 tunnel TTL not specified.\n"); - return -EINVAL; + if (!ttl) { + OVS_NLERR("IPv4 tunnel TTL not specified.\n"); + return -EINVAL; + } } return 0; } -int ipv4_tun_to_nlattr(struct sk_buff *skb, - const struct ovs_key_ipv4_tunnel *tun_key, - const struct ovs_key_ipv4_tunnel *output) +int ovs_ipv4_tun_to_nlattr(struct sk_buff *skb, + const struct ovs_key_ipv4_tunnel *tun_key, + const struct ovs_key_ipv4_tunnel *output) { struct nlattr *nla; @@ -1332,7 +1387,7 @@ static int metadata_from_nlattrs(struct sw_flow_match *match, u64 *attrs, *attrs &= ~(1ULL << OVS_KEY_ATTR_SKB_MARK); } if (*attrs & (1ULL << OVS_KEY_ATTR_TUNNEL)) { - if (ipv4_tun_from_nlattr(a[OVS_KEY_ATTR_TUNNEL], match, + if (ovs_ipv4_tun_from_nlattr(a[OVS_KEY_ATTR_TUNNEL], match, is_mask)) return -EINVAL; *attrs &= ~(1ULL << OVS_KEY_ATTR_TUNNEL); @@ -1387,7 +1442,7 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs, /* 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; } @@ -1513,6 +1568,24 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs, 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; @@ -1583,26 +1656,36 @@ int ovs_match_from_nlattrs(struct sw_flow_match *match, 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; } } @@ -1615,25 +1698,36 @@ int ovs_match_from_nlattrs(struct sw_flow_match *match, 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); @@ -1696,21 +1790,22 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, { struct ovs_key_ethernet *eth_key; struct nlattr *nla, *encap; + bool is_mask = (swkey != output); 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)) + if ((swkey->tun_key.ipv4_dst || is_mask) && + ovs_ipv4_tun_to_nlattr(skb, &swkey->tun_key, &output->tun_key)) goto nla_put_failure; if (swkey->phy.in_port == DP_MAX_PORTS) { - if ((swkey != output) && (output->phy.in_port == 0xffff)) + if (is_mask && (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; + upper_u16 = !is_mask ? 0 : 0xffff; if (nla_put_u32(skb, OVS_KEY_ATTR_IN_PORT, (upper_u16 << 16) | output->phy.in_port)) @@ -1730,7 +1825,7 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, if (swkey->eth.tci || swkey->eth.type == htons(ETH_P_8021Q)) { __be16 eth_type; - eth_type = (swkey == output) ? htons(ETH_P_8021Q) : htons(0xffff) ; + eth_type = !is_mask ? htons(ETH_P_8021Q) : htons(0xffff); if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, eth_type) || nla_put_be16(skb, OVS_KEY_ATTR_VLAN, output->eth.tci)) goto nla_put_failure; @@ -1747,7 +1842,7 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, * 0xffff in the mask attribute. Ethertype can also * be wildcarded. */ - if (swkey != output && output->eth.type) + if (is_mask && output->eth.type) if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, output->eth.type)) goto nla_put_failure; @@ -1834,6 +1929,20 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, 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; @@ -1886,6 +1995,8 @@ nla_put_failure: * Returns zero if successful or a negative error code. */ int ovs_flow_init(void) { + 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) @@ -1948,7 +2059,7 @@ static bool ovs_sw_flow_mask_equal(const struct sw_flow_mask *a, 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, @@ -1985,5 +2096,5 @@ static void ovs_sw_flow_mask_set(struct sw_flow_mask *mask, 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)); }