X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=lib%2Fflow.c;h=7084079ea778da6dc310b9e242c9a2d0a5a9ea03;hb=038341d1c1c33107a7f2ad06a5acb47c5fff7967;hp=d7765676f02c971615e1784cf93cb0fbcd5f7b84;hpb=659c23467978f40200686695c48b09582f2c534b;p=openvswitch diff --git a/lib/flow.c b/lib/flow.c index d7765676..7084079e 100644 --- a/lib/flow.c +++ b/lib/flow.c @@ -19,9 +19,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include "byte-order.h" @@ -29,6 +31,7 @@ #include "csum.h" #include "dynamic-string.h" #include "hash.h" +#include "match.h" #include "ofpbuf.h" #include "openflow/openflow.h" #include "packets.h" @@ -38,6 +41,7 @@ VLOG_DEFINE_THIS_MODULE(flow); COVERAGE_DEFINE(flow_extract); +COVERAGE_DEFINE(miniflow_malloc); static struct arp_eth_header * pull_arp(struct ofpbuf *packet) @@ -313,7 +317,7 @@ invalid: } -/* Initializes 'flow' members from 'packet', 'skb_priority', 'tun_id', and +/* Initializes 'flow' members from 'packet', 'skb_priority', 'tnl', and * 'ofp_in_port'. * * Initializes 'packet' header pointers as follows: @@ -331,8 +335,9 @@ invalid: * present and has a correct length, and otherwise NULL. */ void -flow_extract(struct ofpbuf *packet, uint32_t skb_priority, ovs_be64 tun_id, - uint16_t ofp_in_port, struct flow *flow) +flow_extract(struct ofpbuf *packet, uint32_t skb_priority, + const struct flow_tnl *tnl, uint16_t ofp_in_port, + struct flow *flow) { struct ofpbuf b = *packet; struct eth_header *eth; @@ -340,7 +345,11 @@ flow_extract(struct ofpbuf *packet, uint32_t skb_priority, ovs_be64 tun_id, COVERAGE_INC(flow_extract); memset(flow, 0, sizeof *flow); - flow->tun_id = tun_id; + + if (tnl) { + assert(tnl != &flow->tunnel); + flow->tunnel = *tnl; + } flow->in_port = ofp_in_port; flow->skb_priority = skb_priority; @@ -415,7 +424,8 @@ flow_extract(struct ofpbuf *packet, uint32_t skb_priority, ovs_be64 tun_id, packet->l7 = b.data; } } - } else if (flow->dl_type == htons(ETH_TYPE_ARP)) { + } else if (flow->dl_type == htons(ETH_TYPE_ARP) || + flow->dl_type == htons(ETH_TYPE_RARP)) { const struct arp_eth_header *arp = pull_arp(&b); if (arp && arp->ar_hrd == htons(1) && arp->ar_pro == htons(ETH_TYPE_IP) @@ -426,13 +436,10 @@ flow_extract(struct ofpbuf *packet, uint32_t skb_priority, ovs_be64 tun_id, flow->nw_proto = ntohs(arp->ar_op); } - if ((flow->nw_proto == ARP_OP_REQUEST) - || (flow->nw_proto == ARP_OP_REPLY)) { - flow->nw_src = arp->ar_spa; - flow->nw_dst = arp->ar_tpa; - memcpy(flow->arp_sha, arp->ar_sha, ETH_ADDR_LEN); - memcpy(flow->arp_tha, arp->ar_tha, ETH_ADDR_LEN); - } + flow->nw_src = arp->ar_spa; + flow->nw_dst = arp->ar_tpa; + memcpy(flow->arp_sha, arp->ar_sha, ETH_ADDR_LEN); + memcpy(flow->arp_tha, arp->ar_tha, ETH_ADDR_LEN); } } } @@ -457,7 +464,7 @@ flow_get_metadata(const struct flow *flow, struct flow_metadata *fmd) { BUILD_ASSERT_DECL(FLOW_WC_SEQ == 17); - fmd->tun_id = flow->tun_id; + fmd->tun_id = flow->tunnel.tun_id; fmd->metadata = flow->metadata; memcpy(fmd->regs, flow->regs, sizeof fmd->regs); fmd->in_port = flow->in_port; @@ -474,59 +481,10 @@ flow_to_string(const struct flow *flow) void flow_format(struct ds *ds, const struct flow *flow) { - ds_put_format(ds, "priority:%"PRIu32 - ",tunnel:%#"PRIx64 - ",metadata:%#"PRIx64 - ",in_port:%04"PRIx16, - flow->skb_priority, - ntohll(flow->tun_id), - ntohll(flow->metadata), - flow->in_port); - - ds_put_format(ds, ",tci("); - if (flow->vlan_tci) { - ds_put_format(ds, "vlan:%"PRIu16",pcp:%d", - vlan_tci_to_vid(flow->vlan_tci), - vlan_tci_to_pcp(flow->vlan_tci)); - } else { - ds_put_char(ds, '0'); - } - ds_put_format(ds, ") mac("ETH_ADDR_FMT"->"ETH_ADDR_FMT - ") type:%04"PRIx16, - ETH_ADDR_ARGS(flow->dl_src), - ETH_ADDR_ARGS(flow->dl_dst), - ntohs(flow->dl_type)); - - if (flow->dl_type == htons(ETH_TYPE_IPV6)) { - ds_put_format(ds, " label:%#"PRIx32" proto:%"PRIu8" tos:%#"PRIx8 - " ttl:%"PRIu8" ipv6(", - ntohl(flow->ipv6_label), flow->nw_proto, - flow->nw_tos, flow->nw_ttl); - print_ipv6_addr(ds, &flow->ipv6_src); - ds_put_cstr(ds, "->"); - print_ipv6_addr(ds, &flow->ipv6_dst); - ds_put_char(ds, ')'); - } else { - ds_put_format(ds, " proto:%"PRIu8" tos:%#"PRIx8" ttl:%"PRIu8 - " ip("IP_FMT"->"IP_FMT")", - flow->nw_proto, flow->nw_tos, flow->nw_ttl, - IP_ARGS(&flow->nw_src), IP_ARGS(&flow->nw_dst)); - } - if (flow->nw_frag) { - ds_put_format(ds, " frag(%s)", - flow->nw_frag == FLOW_NW_FRAG_ANY ? "first" - : flow->nw_frag == (FLOW_NW_FRAG_ANY | FLOW_NW_FRAG_LATER) - ? "later" : ""); - } - if (flow->tp_src || flow->tp_dst) { - ds_put_format(ds, " port(%"PRIu16"->%"PRIu16")", - ntohs(flow->tp_src), ntohs(flow->tp_dst)); - } - if (!eth_addr_is_zero(flow->arp_sha) || !eth_addr_is_zero(flow->arp_tha)) { - ds_put_format(ds, " arp_ha("ETH_ADDR_FMT"->"ETH_ADDR_FMT")", - ETH_ADDR_ARGS(flow->arp_sha), - ETH_ADDR_ARGS(flow->arp_tha)); - } + struct match match; + + match_wc_init(&match, flow); + match_format(&match, ds, flow->skb_priority); } void @@ -552,7 +510,6 @@ void flow_wildcards_init_exact(struct flow_wildcards *wc) { memset(&wc->masks, 0xff, sizeof wc->masks); - memset(wc->masks.zeros, 0, sizeof wc->masks.zeros); } /* Returns true if 'wc' matches every packet, false if 'wc' fixes any bits or @@ -810,6 +767,7 @@ flow_compose(struct ofpbuf *b, const struct flow *flow) b->l3 = ip = ofpbuf_put_zeros(b, sizeof *ip); ip->ip_ihl_ver = IP_IHL_VER(5, 4); ip->ip_tos = flow->nw_tos; + ip->ip_ttl = flow->nw_ttl; ip->ip_proto = flow->nw_proto; ip->ip_src = flow->nw_src; ip->ip_dst = flow->nw_dst; @@ -851,7 +809,8 @@ flow_compose(struct ofpbuf *b, const struct flow *flow) ip->ip_csum = csum(ip, sizeof *ip); } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) { /* XXX */ - } else if (flow->dl_type == htons(ETH_TYPE_ARP)) { + } else if (flow->dl_type == htons(ETH_TYPE_ARP) || + flow->dl_type == htons(ETH_TYPE_RARP)) { struct arp_eth_header *arp; b->l3 = arp = ofpbuf_put_zeros(b, sizeof *arp); @@ -870,3 +829,416 @@ flow_compose(struct ofpbuf *b, const struct flow *flow) } } } + +/* Compressed flow. */ + +static int +miniflow_n_values(const struct miniflow *flow) +{ + int n, i; + + n = 0; + for (i = 0; i < MINI_N_MAPS; i++) { + n += popcount(flow->map[i]); + } + return n; +} + +static uint32_t * +miniflow_alloc_values(struct miniflow *flow, int n) +{ + if (n <= MINI_N_INLINE) { + return flow->inline_values; + } else { + COVERAGE_INC(miniflow_malloc); + return xmalloc(n * sizeof *flow->values); + } +} + +/* Initializes 'dst' as a copy of 'src'. The caller must eventually free 'dst' + * with miniflow_destroy(). */ +void +miniflow_init(struct miniflow *dst, const struct flow *src) +{ + const uint32_t *src_u32 = (const uint32_t *) src; + unsigned int ofs; + unsigned int i; + int n; + + /* Initialize dst->map, counting the number of nonzero elements. */ + n = 0; + memset(dst->map, 0, sizeof dst->map); + for (i = 0; i < FLOW_U32S; i++) { + if (src_u32[i]) { + dst->map[i / 32] |= 1u << (i % 32); + n++; + } + } + + /* Initialize dst->values. */ + dst->values = miniflow_alloc_values(dst, n); + ofs = 0; + for (i = 0; i < MINI_N_MAPS; i++) { + uint32_t map; + + for (map = dst->map[i]; map; map = zero_rightmost_1bit(map)) { + dst->values[ofs++] = src_u32[raw_ctz(map) + i * 32]; + } + } +} + +/* Initializes 'dst' as a copy of 'src'. The caller must eventually free 'dst' + * with miniflow_destroy(). */ +void +miniflow_clone(struct miniflow *dst, const struct miniflow *src) +{ + int n = miniflow_n_values(src); + memcpy(dst->map, src->map, sizeof dst->map); + dst->values = miniflow_alloc_values(dst, n); + memcpy(dst->values, src->values, n * sizeof *dst->values); +} + +/* Frees any memory owned by 'flow'. Does not free the storage in which 'flow' + * itself resides; the caller is responsible for that. */ +void +miniflow_destroy(struct miniflow *flow) +{ + if (flow->values != flow->inline_values) { + free(flow->values); + } +} + +/* Initializes 'dst' as a copy of 'src'. */ +void +miniflow_expand(const struct miniflow *src, struct flow *dst) +{ + uint32_t *dst_u32 = (uint32_t *) dst; + int ofs; + int i; + + memset(dst_u32, 0, sizeof *dst); + + ofs = 0; + for (i = 0; i < MINI_N_MAPS; i++) { + uint32_t map; + + for (map = src->map[i]; map; map = zero_rightmost_1bit(map)) { + dst_u32[raw_ctz(map) + i * 32] = src->values[ofs++]; + } + } +} + +static const uint32_t * +miniflow_get__(const struct miniflow *flow, unsigned int u32_ofs) +{ + if (!(flow->map[u32_ofs / 32] & (1u << (u32_ofs % 32)))) { + static const uint32_t zero = 0; + return &zero; + } else { + const uint32_t *p = flow->values; + + BUILD_ASSERT(MINI_N_MAPS == 2); + if (u32_ofs < 32) { + p += popcount(flow->map[0] & ((1u << u32_ofs) - 1)); + } else { + p += popcount(flow->map[0]); + p += popcount(flow->map[1] & ((1u << (u32_ofs - 32)) - 1)); + } + return p; + } +} + +/* Returns the uint32_t that would be at byte offset '4 * u32_ofs' if 'flow' + * were expanded into a "struct flow". */ +uint32_t +miniflow_get(const struct miniflow *flow, unsigned int u32_ofs) +{ + return *miniflow_get__(flow, u32_ofs); +} + +/* Returns the ovs_be16 that would be at byte offset 'u8_ofs' if 'flow' were + * expanded into a "struct flow". */ +static ovs_be16 +miniflow_get_be16(const struct miniflow *flow, unsigned int u8_ofs) +{ + const uint32_t *u32p = miniflow_get__(flow, u8_ofs / 4); + const ovs_be16 *be16p = (const ovs_be16 *) u32p; + return be16p[u8_ofs % 4 != 0]; +} + +/* Returns the VID within the vlan_tci member of the "struct flow" represented + * by 'flow'. */ +uint16_t +miniflow_get_vid(const struct miniflow *flow) +{ + ovs_be16 tci = miniflow_get_be16(flow, offsetof(struct flow, vlan_tci)); + return vlan_tci_to_vid(tci); +} + +/* Returns true if 'a' and 'b' are the same flow, false otherwise. */ +bool +miniflow_equal(const struct miniflow *a, const struct miniflow *b) +{ + int i; + + for (i = 0; i < MINI_N_MAPS; i++) { + if (a->map[i] != b->map[i]) { + return false; + } + } + + return !memcmp(a->values, b->values, + miniflow_n_values(a) * sizeof *a->values); +} + +/* Returns true if 'a' and 'b' are equal at the places where there are 1-bits + * in 'mask', false if they differ. */ +bool +miniflow_equal_in_minimask(const struct miniflow *a, const struct miniflow *b, + const struct minimask *mask) +{ + const uint32_t *p; + int i; + + p = mask->masks.values; + for (i = 0; i < MINI_N_MAPS; i++) { + uint32_t map; + + for (map = mask->masks.map[i]; map; map = zero_rightmost_1bit(map)) { + int ofs = raw_ctz(map) + i * 32; + + if ((miniflow_get(a, ofs) ^ miniflow_get(b, ofs)) & *p) { + return false; + } + p++; + } + } + + return true; +} + +/* Returns true if 'a' and 'b' are equal at the places where there are 1-bits + * in 'mask', false if they differ. */ +bool +miniflow_equal_flow_in_minimask(const struct miniflow *a, const struct flow *b, + const struct minimask *mask) +{ + const uint32_t *b_u32 = (const uint32_t *) b; + const uint32_t *p; + int i; + + p = mask->masks.values; + for (i = 0; i < MINI_N_MAPS; i++) { + uint32_t map; + + for (map = mask->masks.map[i]; map; map = zero_rightmost_1bit(map)) { + int ofs = raw_ctz(map) + i * 32; + + if ((miniflow_get(a, ofs) ^ b_u32[ofs]) & *p) { + return false; + } + p++; + } + } + + return true; +} + +/* Returns a hash value for 'flow', given 'basis'. */ +uint32_t +miniflow_hash(const struct miniflow *flow, uint32_t basis) +{ + BUILD_ASSERT_DECL(MINI_N_MAPS == 2); + return hash_3words(flow->map[0], flow->map[1], + hash_words(flow->values, miniflow_n_values(flow), + basis)); +} + +/* Returns a hash value for the bits of 'flow' where there are 1-bits in + * 'mask', given 'basis'. + * + * The hash values returned by this function are the same as those returned by + * flow_hash_in_minimask(), only the form of the arguments differ. */ +uint32_t +miniflow_hash_in_minimask(const struct miniflow *flow, + const struct minimask *mask, uint32_t basis) +{ + const uint32_t *p = mask->masks.values; + uint32_t hash; + int i; + + hash = basis; + for (i = 0; i < MINI_N_MAPS; i++) { + uint32_t map; + + for (map = mask->masks.map[i]; map; map = zero_rightmost_1bit(map)) { + int ofs = raw_ctz(map) + i * 32; + + hash = mhash_add(hash, miniflow_get(flow, ofs) & *p); + p++; + } + } + + return mhash_finish(hash, p - mask->masks.values); +} + +/* Returns a hash value for the bits of 'flow' where there are 1-bits in + * 'mask', given 'basis'. + * + * The hash values returned by this function are the same as those returned by + * miniflow_hash_in_minimask(), only the form of the arguments differ. */ +uint32_t +flow_hash_in_minimask(const struct flow *flow, const struct minimask *mask, + uint32_t basis) +{ + const uint32_t *flow_u32 = (const uint32_t *) flow; + const uint32_t *p = mask->masks.values; + uint32_t hash; + int i; + + hash = basis; + for (i = 0; i < MINI_N_MAPS; i++) { + uint32_t map; + + for (map = mask->masks.map[i]; map; map = zero_rightmost_1bit(map)) { + int ofs = raw_ctz(map) + i * 32; + + hash = mhash_add(hash, flow_u32[ofs] & *p); + p++; + } + } + + return mhash_finish(hash, p - mask->masks.values); +} + +/* Initializes 'dst' as a copy of 'src'. The caller must eventually free 'dst' + * with minimask_destroy(). */ +void +minimask_init(struct minimask *mask, const struct flow_wildcards *wc) +{ + miniflow_init(&mask->masks, &wc->masks); +} + +/* Initializes 'dst' as a copy of 'src'. The caller must eventually free 'dst' + * with minimask_destroy(). */ +void +minimask_clone(struct minimask *dst, const struct minimask *src) +{ + miniflow_clone(&dst->masks, &src->masks); +} + +/* Initializes 'dst_' as the bit-wise "and" of 'a_' and 'b_'. + * + * The caller must provide room for FLOW_U32S "uint32_t"s in 'storage', for use + * by 'dst_'. The caller must *not* free 'dst_' with minimask_destroy(). */ +void +minimask_combine(struct minimask *dst_, + const struct minimask *a_, const struct minimask *b_, + uint32_t storage[FLOW_U32S]) +{ + struct miniflow *dst = &dst_->masks; + const struct miniflow *a = &a_->masks; + const struct miniflow *b = &b_->masks; + int i, n; + + n = 0; + dst->values = storage; + for (i = 0; i < MINI_N_MAPS; i++) { + uint32_t map; + + dst->map[i] = 0; + for (map = a->map[i] & b->map[i]; map; + map = zero_rightmost_1bit(map)) { + int ofs = raw_ctz(map) + i * 32; + uint32_t mask = miniflow_get(a, ofs) & miniflow_get(b, ofs); + + if (mask) { + dst->map[i] |= rightmost_1bit(map); + dst->values[n++] = mask; + } + } + } +} + +/* Frees any memory owned by 'mask'. Does not free the storage in which 'mask' + * itself resides; the caller is responsible for that. */ +void +minimask_destroy(struct minimask *mask) +{ + miniflow_destroy(&mask->masks); +} + +/* Initializes 'dst' as a copy of 'src'. */ +void +minimask_expand(const struct minimask *mask, struct flow_wildcards *wc) +{ + miniflow_expand(&mask->masks, &wc->masks); +} + +/* Returns the uint32_t that would be at byte offset '4 * u32_ofs' if 'mask' + * were expanded into a "struct flow_wildcards". */ +uint32_t +minimask_get(const struct minimask *mask, unsigned int u32_ofs) +{ + return miniflow_get(&mask->masks, u32_ofs); +} + +/* Returns the VID mask within the vlan_tci member of the "struct + * flow_wildcards" represented by 'mask'. */ +uint16_t +minimask_get_vid_mask(const struct minimask *mask) +{ + return miniflow_get_vid(&mask->masks); +} + +/* Returns true if 'a' and 'b' are the same flow mask, false otherwise. */ +bool +minimask_equal(const struct minimask *a, const struct minimask *b) +{ + return miniflow_equal(&a->masks, &b->masks); +} + +/* Returns a hash value for 'mask', given 'basis'. */ +uint32_t +minimask_hash(const struct minimask *mask, uint32_t basis) +{ + return miniflow_hash(&mask->masks, basis); +} + +/* Returns true if at least one bit is wildcarded in 'a_' but not in 'b_', + * false otherwise. */ +bool +minimask_has_extra(const struct minimask *a_, const struct minimask *b_) +{ + const struct miniflow *a = &a_->masks; + const struct miniflow *b = &b_->masks; + int i; + + for (i = 0; i < MINI_N_MAPS; i++) { + uint32_t map; + + for (map = a->map[i] | b->map[i]; map; + map = zero_rightmost_1bit(map)) { + int ofs = raw_ctz(map) + i * 32; + uint32_t a_u32 = miniflow_get(a, ofs); + uint32_t b_u32 = miniflow_get(b, ofs); + + if ((a_u32 & b_u32) != b_u32) { + return true; + } + } + } + + return false; +} + +/* Returns true if 'mask' matches every packet, false if 'mask' fixes any bits + * or fields. */ +bool +minimask_is_catchall(const struct minimask *mask_) +{ + const struct miniflow *mask = &mask_->masks; + + BUILD_ASSERT(MINI_N_MAPS == 2); + return !(mask->map[0] | mask->map[1]); +}