X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fflow.c;h=82d6729313a95339623caf51a1d84b118ea83857;hb=7685b7a9e5b3f6db6832e52e111000ff36d3acb4;hp=3633dffa3cfd05876c2b386659189a309050778b;hpb=c11c6faa557fb7f363375f988731fff19562dee5;p=sliver-openvswitch.git diff --git a/lib/flow.c b/lib/flow.c index 3633dffa3..82d672931 100644 --- a/lib/flow.c +++ b/lib/flow.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc. + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,6 +35,7 @@ #include "ofpbuf.h" #include "openflow/openflow.h" #include "packets.h" +#include "odp-util.h" #include "random.h" #include "unaligned.h" @@ -109,12 +110,11 @@ static void parse_mpls(struct ofpbuf *b, struct flow *flow) { struct mpls_hdr *mh; - bool top = true; + int idx = 0; while ((mh = ofpbuf_try_pull(b, sizeof *mh))) { - if (top) { - top = false; - flow->mpls_lse = mh->mpls_lse; + if (idx < FLOW_MAX_MPLS_LABELS) { + flow->mpls_lse[idx++] = mh->mpls_lse; } if (mh->mpls_lse & htonl(MPLS_BOS_MASK)) { break; @@ -362,8 +362,7 @@ invalid: } -/* Initializes 'flow' members from 'packet', 'skb_priority', 'tnl', and - * 'in_port'. +/* Initializes 'flow' members from 'packet' and 'md' * * Initializes 'packet' header pointers as follows: * @@ -382,8 +381,7 @@ invalid: * present and has a correct length, and otherwise NULL. */ void -flow_extract(struct ofpbuf *packet, uint32_t skb_priority, uint32_t pkt_mark, - const struct flow_tnl *tnl, const union flow_in_port *in_port, +flow_extract(struct ofpbuf *packet, const struct pkt_metadata *md, struct flow *flow) { struct ofpbuf b = *packet; @@ -393,15 +391,14 @@ flow_extract(struct ofpbuf *packet, uint32_t skb_priority, uint32_t pkt_mark, memset(flow, 0, sizeof *flow); - if (tnl) { - ovs_assert(tnl != &flow->tunnel); - flow->tunnel = *tnl; - } - if (in_port) { - flow->in_port = *in_port; + if (md) { + flow->tunnel = md->tunnel; + if (md->in_port.odp_port != ODPP_NONE) { + flow->in_port = md->in_port; + }; + flow->skb_priority = md->skb_priority; + flow->pkt_mark = md->pkt_mark; } - flow->skb_priority = skb_priority; - flow->pkt_mark = pkt_mark; packet->l2 = b.data; packet->l2_5 = NULL; @@ -519,11 +516,23 @@ flow_zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards) } } +void +flow_unwildcard_tp_ports(const struct flow *flow, struct flow_wildcards *wc) +{ + if (flow->nw_proto != IPPROTO_ICMP) { + memset(&wc->masks.tp_src, 0xff, sizeof wc->masks.tp_src); + memset(&wc->masks.tp_dst, 0xff, sizeof wc->masks.tp_dst); + } else { + wc->masks.tp_src = htons(0xff); + wc->masks.tp_dst = htons(0xff); + } +} + /* Initializes 'fmd' with the metadata found in 'flow'. */ void flow_get_metadata(const struct flow *flow, struct flow_metadata *fmd) { - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 23); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 24); fmd->tun_id = flow->tunnel.tun_id; fmd->tun_src = flow->tunnel.ip_src; @@ -713,24 +722,22 @@ flow_wildcards_fold_minimask(struct flow_wildcards *wc, flow_union_with_miniflow(&wc->masks, &mask->masks); } -inline uint64_t +uint64_t miniflow_get_map_in_range(const struct miniflow *miniflow, - uint8_t start, uint8_t end, const uint32_t **data) + uint8_t start, uint8_t end, unsigned int *offset) { uint64_t map = miniflow->map; - uint32_t *p = miniflow->values; + *offset = 0; if (start > 0) { uint64_t msk = (UINT64_C(1) << start) - 1; /* 'start' LSBs set */ - p += count_1bits(map & msk); /* Skip to start. */ + *offset = count_1bits(map & msk); map &= ~msk; } if (end < FLOW_U32S) { uint64_t msk = (UINT64_C(1) << end) - 1; /* 'end' LSBs set */ map &= msk; } - - *data = p; return map; } @@ -742,8 +749,10 @@ flow_wildcards_fold_minimask_range(struct flow_wildcards *wc, uint8_t start, uint8_t end) { uint32_t *dst_u32 = (uint32_t *)&wc->masks; - const uint32_t *p; - uint64_t map = miniflow_get_map_in_range(&mask->masks, start, end, &p); + unsigned int offset; + uint64_t map = miniflow_get_map_in_range(&mask->masks, start, end, + &offset); + const uint32_t *p = mask->masks.values + offset; for (; map; map = zero_rightmost_1bit(map)) { dst_u32[raw_ctz(map)] |= *p++; @@ -922,14 +931,13 @@ flow_mask_hash_fields(const struct flow *flow, struct flow_wildcards *wc, } if (is_ip_any(flow)) { memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto); - memset(&wc->masks.tp_src, 0xff, sizeof wc->masks.tp_src); - memset(&wc->masks.tp_dst, 0xff, sizeof wc->masks.tp_dst); + flow_unwildcard_tp_ports(flow, wc); } wc->masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI); break; default: - NOT_REACHED(); + OVS_NOT_REACHED(); } } @@ -947,7 +955,7 @@ flow_hash_fields(const struct flow *flow, enum nx_hash_fields fields, return flow_hash_symmetric_l4(flow, basis); } - NOT_REACHED(); + OVS_NOT_REACHED(); } /* Returns a string representation of 'fields'. */ @@ -1036,37 +1044,212 @@ flow_set_vlan_pcp(struct flow *flow, uint8_t pcp) flow->vlan_tci |= htons((pcp << VLAN_PCP_SHIFT) | VLAN_CFI); } +/* Returns the number of MPLS LSEs present in 'flow' + * + * Returns 0 if the 'dl_type' of 'flow' is not an MPLS ethernet type. + * Otherwise traverses 'flow''s MPLS label stack stopping at the + * first entry that has the BoS bit set. If no such entry exists then + * the maximum number of LSEs that can be stored in 'flow' is returned. + */ +int +flow_count_mpls_labels(const struct flow *flow, struct flow_wildcards *wc) +{ + if (wc) { + wc->masks.dl_type = OVS_BE16_MAX; + } + if (eth_type_mpls(flow->dl_type)) { + int i; + int len = FLOW_MAX_MPLS_LABELS; + + for (i = 0; i < len; i++) { + if (wc) { + wc->masks.mpls_lse[i] |= htonl(MPLS_BOS_MASK); + } + if (flow->mpls_lse[i] & htonl(MPLS_BOS_MASK)) { + return i + 1; + } + } + + return len; + } else { + return 0; + } +} + +/* Returns the number consecutive of MPLS LSEs, starting at the + * innermost LSE, that are common in 'a' and 'b'. + * + * 'an' must be flow_count_mpls_labels(a). + * 'bn' must be flow_count_mpls_labels(b). + */ +int +flow_count_common_mpls_labels(const struct flow *a, int an, + const struct flow *b, int bn, + struct flow_wildcards *wc) +{ + int min_n = MIN(an, bn); + if (min_n == 0) { + return 0; + } else { + int common_n = 0; + int a_last = an - 1; + int b_last = bn - 1; + int i; + + for (i = 0; i < min_n; i++) { + if (wc) { + wc->masks.mpls_lse[a_last - i] = OVS_BE32_MAX; + wc->masks.mpls_lse[b_last - i] = OVS_BE32_MAX; + } + if (a->mpls_lse[a_last - i] != b->mpls_lse[b_last - i]) { + break; + } else { + common_n++; + } + } + + return common_n; + } +} + +/* Adds a new outermost MPLS label to 'flow' and changes 'flow''s Ethernet type + * to 'mpls_eth_type', which must be an MPLS Ethertype. + * + * If the new label is the first MPLS label in 'flow', it is generated as; + * + * - label: 2, if 'flow' is IPv6, otherwise 0. + * + * - TTL: IPv4 or IPv6 TTL, if present and nonzero, otherwise 64. + * + * - TC: IPv4 or IPv6 TOS, if present, otherwise 0. + * + * - BoS: 1. + * + * If the new label is the second or label MPLS label in 'flow', it is + * generated as; + * + * - label: Copied from outer label. + * + * - TTL: Copied from outer label. + * + * - TC: Copied from outer label. + * + * - BoS: 0. + * + * 'n' must be flow_count_mpls_labels(flow). 'n' must be less than + * FLOW_MAX_MPLS_LABELS (because otherwise flow->mpls_lse[] would overflow). + */ +void +flow_push_mpls(struct flow *flow, int n, ovs_be16 mpls_eth_type, + struct flow_wildcards *wc) +{ + ovs_assert(eth_type_mpls(mpls_eth_type)); + ovs_assert(n < FLOW_MAX_MPLS_LABELS); + + memset(wc->masks.mpls_lse, 0xff, sizeof wc->masks.mpls_lse); + if (n) { + int i; + + for (i = n; i >= 1; i--) { + flow->mpls_lse[i] = flow->mpls_lse[i - 1]; + } + flow->mpls_lse[0] = (flow->mpls_lse[1] + & htonl(~MPLS_BOS_MASK)); + } else { + int label = 0; /* IPv4 Explicit Null. */ + int tc = 0; + int ttl = 64; + + if (flow->dl_type == htons(ETH_TYPE_IPV6)) { + label = 2; + } + + if (is_ip_any(flow)) { + tc = (flow->nw_tos & IP_DSCP_MASK) >> 2; + wc->masks.nw_tos |= IP_DSCP_MASK; + + if (flow->nw_ttl) { + ttl = flow->nw_ttl; + } + wc->masks.nw_ttl = 0xff; + } + + flow->mpls_lse[0] = set_mpls_lse_values(ttl, tc, 1, htonl(label)); + + /* Clear all L3 and L4 fields. */ + BUILD_ASSERT(FLOW_WC_SEQ == 24); + memset((char *) flow + FLOW_SEGMENT_2_ENDS_AT, 0, + sizeof(struct flow) - FLOW_SEGMENT_2_ENDS_AT); + } + flow->dl_type = mpls_eth_type; +} + +/* Tries to remove the outermost MPLS label from 'flow'. Returns true if + * successful, false otherwise. On success, sets 'flow''s Ethernet type to + * 'eth_type'. + * + * 'n' must be flow_count_mpls_labels(flow). */ +bool +flow_pop_mpls(struct flow *flow, int n, ovs_be16 eth_type, + struct flow_wildcards *wc) +{ + int i; + + if (n == 0) { + /* Nothing to pop. */ + return false; + } else if (n == FLOW_MAX_MPLS_LABELS + && !(flow->mpls_lse[n - 1] & htonl(MPLS_BOS_MASK))) { + /* Can't pop because we don't know what to fill in mpls_lse[n - 1]. */ + return false; + } + + memset(wc->masks.mpls_lse, 0xff, sizeof wc->masks.mpls_lse); + for (i = 1; i < n; i++) { + flow->mpls_lse[i - 1] = flow->mpls_lse[i]; + } + flow->mpls_lse[n - 1] = 0; + flow->dl_type = eth_type; + return true; +} + /* Sets the MPLS Label that 'flow' matches to 'label', which is interpreted * as an OpenFlow 1.1 "mpls_label" value. */ void -flow_set_mpls_label(struct flow *flow, ovs_be32 label) +flow_set_mpls_label(struct flow *flow, int idx, ovs_be32 label) { - set_mpls_lse_label(&flow->mpls_lse, label); + set_mpls_lse_label(&flow->mpls_lse[idx], label); } /* Sets the MPLS TTL that 'flow' matches to 'ttl', which should be in the * range 0...255. */ void -flow_set_mpls_ttl(struct flow *flow, uint8_t ttl) +flow_set_mpls_ttl(struct flow *flow, int idx, uint8_t ttl) { - set_mpls_lse_ttl(&flow->mpls_lse, ttl); + set_mpls_lse_ttl(&flow->mpls_lse[idx], ttl); } /* Sets the MPLS TC that 'flow' matches to 'tc', which should be in the * range 0...7. */ void -flow_set_mpls_tc(struct flow *flow, uint8_t tc) +flow_set_mpls_tc(struct flow *flow, int idx, uint8_t tc) { - set_mpls_lse_tc(&flow->mpls_lse, tc); + set_mpls_lse_tc(&flow->mpls_lse[idx], tc); } /* Sets the MPLS BOS bit that 'flow' matches to which should be 0 or 1. */ void -flow_set_mpls_bos(struct flow *flow, uint8_t bos) +flow_set_mpls_bos(struct flow *flow, int idx, uint8_t bos) { - set_mpls_lse_bos(&flow->mpls_lse, bos); + set_mpls_lse_bos(&flow->mpls_lse[idx], bos); } +/* Sets the entire MPLS LSE. */ +void +flow_set_mpls_lse(struct flow *flow, int idx, ovs_be32 lse) +{ + flow->mpls_lse[idx] = lse; +} static void flow_compose_l4(struct ofpbuf *b, const struct flow *flow) @@ -1157,7 +1340,7 @@ flow_compose(struct ofpbuf *b, const struct flow *flow) } if (flow->vlan_tci & htons(VLAN_CFI)) { - eth_push_vlan(b, flow->vlan_tci); + eth_push_vlan(b, htons(ETH_TYPE_VLAN), flow->vlan_tci); } if (flow->dl_type == htons(ETH_TYPE_IP)) { @@ -1224,8 +1407,17 @@ flow_compose(struct ofpbuf *b, const struct flow *flow) } if (eth_type_mpls(flow->dl_type)) { + int n; + b->l2_5 = b->l3; - push_mpls(b, flow->dl_type, flow->mpls_lse); + for (n = 1; n < FLOW_MAX_MPLS_LABELS; n++) { + if (flow->mpls_lse[n - 1] & htonl(MPLS_BOS_MASK)) { + break; + } + } + while (n > 0) { + push_mpls(b, flow->dl_type, flow->mpls_lse[--n]); + } } } @@ -1505,11 +1697,7 @@ miniflow_hash_in_minimask(const struct miniflow *flow, hash = basis; for (map = mask->masks.map; map; map = zero_rightmost_1bit(map)) { - if (*p) { - int ofs = raw_ctz(map); - hash = mhash_add(hash, miniflow_get(flow, ofs) & *p); - } - p++; + hash = mhash_add(hash, miniflow_get(flow, raw_ctz(map)) & *p++); } return mhash_finish(hash, (p - mask->masks.values) * 4); @@ -1531,10 +1719,7 @@ flow_hash_in_minimask(const struct flow *flow, const struct minimask *mask, hash = basis; for (map = mask->masks.map; map; map = zero_rightmost_1bit(map)) { - if (*p) { - hash = mhash_add(hash, flow_u32[raw_ctz(map)] & *p); - } - p++; + hash = mhash_add(hash, flow_u32[raw_ctz(map)] & *p++); } return mhash_finish(hash, (p - mask->masks.values) * 4); @@ -1551,15 +1736,14 @@ flow_hash_in_minimask_range(const struct flow *flow, uint8_t start, uint8_t end, uint32_t *basis) { const uint32_t *flow_u32 = (const uint32_t *)flow; - const uint32_t *p; - uint64_t map = miniflow_get_map_in_range(&mask->masks, start, end, &p); + unsigned int offset; + uint64_t map = miniflow_get_map_in_range(&mask->masks, start, end, + &offset); + const uint32_t *p = mask->masks.values + offset; uint32_t hash = *basis; for (; map; map = zero_rightmost_1bit(map)) { - if (*p) { - hash = mhash_add(hash, flow_u32[raw_ctz(map)] & *p); - } - p++; + hash = mhash_add(hash, flow_u32[raw_ctz(map)] & *p++); } *basis = hash; /* Allow continuation from the unfinished value. */