/* miniflow_push_* macros allow filling in a miniflow data values in order.
* Assertions are needed only when the layout of the struct flow is modified.
* 'ofs' is a compile-time constant, which allows most of the code be optimized
- * away. Some GCC versions gave warnigns on ALWAYS_INLINE, so these are
+ * away. Some GCC versions gave warnings on ALWAYS_INLINE, so these are
* defined as macros. */
-#if (FLOW_WC_SEQ != 26)
+#if (FLOW_WC_SEQ != 27)
#define MINIFLOW_ASSERT(X) ovs_assert(X)
#else
#define MINIFLOW_ASSERT(X)
(icmp->icmp6_type == ND_NEIGHBOR_SOLICIT ||
icmp->icmp6_type == ND_NEIGHBOR_ADVERT)) {
- *nd_target = data_try_pull(datap, sizep, sizeof *nd_target);
+ *nd_target = data_try_pull(datap, sizep, sizeof **nd_target);
if (OVS_UNLIKELY(!*nd_target)) {
return false;
}
flow_extract(struct ofpbuf *packet, const struct pkt_metadata *md,
struct flow *flow)
{
- uint32_t buf[FLOW_U32S];
- struct miniflow mf;
+ struct {
+ struct miniflow mf;
+ uint32_t buf[FLOW_U32S];
+ } m;
COVERAGE_INC(flow_extract);
- miniflow_initialize(&mf, buf);
- miniflow_extract(packet, md, &mf);
- miniflow_expand(&mf, flow);
+ miniflow_initialize(&m.mf, m.buf);
+ miniflow_extract(packet, md, &m.mf);
+ miniflow_expand(&m.mf, flow);
}
-/* Caller is responsible for initializing 'dst->values' with enough storage
- * for FLOW_U32S * 4 bytes. */
+/* Caller is responsible for initializing 'dst' with enough storage for
+ * FLOW_U32S * 4 bytes. */
void
miniflow_extract(struct ofpbuf *packet, const struct pkt_metadata *md,
struct miniflow *dst)
{
void *data = ofpbuf_data(packet);
size_t size = ofpbuf_size(packet);
+ uint32_t *values = miniflow_values(dst);
+ struct mf_ctx mf = { 0, values, values + FLOW_U32S };
char *l2;
- struct mf_ctx mf = { 0, dst->values, dst->values + FLOW_U32S };
ovs_be16 dl_type;
uint8_t nw_frag, nw_tos, nw_ttl, nw_proto;
miniflow_push_be16(mf, tp_src, htons(icmp->icmp_type));
miniflow_push_be16(mf, tp_dst, htons(icmp->icmp_code));
}
+ } else if (OVS_LIKELY(nw_proto == IPPROTO_IGMP)) {
+ if (OVS_LIKELY(size >= IGMP_HEADER_LEN)) {
+ const struct igmp_header *igmp = data;
+
+ miniflow_push_be16(mf, tp_src, htons(igmp->igmp_type));
+ miniflow_push_be16(mf, tp_dst, htons(igmp->igmp_code));
+ miniflow_push_be32(mf, igmp_group_ip4,
+ get_16aligned_be32(&igmp->group));
+ }
} else if (OVS_LIKELY(nw_proto == IPPROTO_ICMPV6)) {
if (OVS_LIKELY(size >= sizeof(struct icmp6_hdr))) {
const struct in6_addr *nd_target = NULL;
memset(arp_buf, 0, sizeof arp_buf);
if (OVS_LIKELY(parse_icmpv6(&data, &size, icmp, &nd_target,
arp_buf))) {
+ miniflow_push_words(mf, arp_sha, arp_buf,
+ ETH_ADDR_LEN * 2 / 4);
if (nd_target) {
miniflow_push_words(mf, nd_target, nd_target,
sizeof *nd_target / 4);
}
- miniflow_push_words(mf, arp_sha, arp_buf,
- ETH_ADDR_LEN * 2 / 4);
miniflow_push_be16(mf, tp_src, htons(icmp->icmp6_type));
miniflow_push_be16(mf, tp_dst, htons(icmp->icmp6_code));
}
void
flow_get_metadata(const struct flow *flow, struct flow_metadata *fmd)
{
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 26);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 27);
fmd->dp_hash = flow->dp_hash;
fmd->recirc_id = flow->recirc_id;
return "csum";
case FLOW_TNL_F_KEY:
return "key";
+ case FLOW_TNL_F_OAM:
+ return "oam";
default:
return NULL;
}
wc->masks.regs[idx] = mask;
}
+/* Sets the wildcard mask for register 'idx' in 'wc' to 'mask'.
+ * (A 0-bit indicates a wildcard bit.) */
+void
+flow_wildcards_set_xreg_mask(struct flow_wildcards *wc, int idx, uint64_t mask)
+{
+ flow_set_xreg(&wc->masks, idx, mask);
+}
+
/* Calculates the 5-tuple hash from the given miniflow.
* This returns the same value as flow_hash_5tuple for the corresponding
* flow. */
if (flow) {
ovs_be16 dl_type = MINIFLOW_GET_BE16(flow, dl_type);
- hash = mhash_add(hash, MINIFLOW_GET_U8(flow, nw_proto));
+ hash = hash_add(hash, MINIFLOW_GET_U8(flow, nw_proto));
/* Separate loops for better optimization. */
if (dl_type == htons(ETH_TYPE_IPV6)) {
uint32_t value;
MINIFLOW_FOR_EACH_IN_MAP(value, flow, map) {
- hash = mhash_add(hash, value);
+ hash = hash_add(hash, value);
}
} else {
uint64_t map = MINIFLOW_MAP(nw_src) | MINIFLOW_MAP(nw_dst)
uint32_t value;
MINIFLOW_FOR_EACH_IN_MAP(value, flow, map) {
- hash = mhash_add(hash, value);
+ hash = hash_add(hash, value);
}
}
- hash = mhash_finish(hash, 42); /* Arbitrary number. */
+ hash = hash_finish(hash, 42); /* Arbitrary number. */
}
return hash;
}
if (flow) {
const uint32_t *flow_u32 = (const uint32_t *)flow;
- hash = mhash_add(hash, flow->nw_proto);
+ hash = hash_add(hash, flow->nw_proto);
if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
int ofs = offsetof(struct flow, ipv6_src) / 4;
int end = ofs + 2 * sizeof flow->ipv6_src / 4;
while (ofs < end) {
- hash = mhash_add(hash, flow_u32[ofs++]);
+ hash = hash_add(hash, flow_u32[ofs++]);
}
} else {
- hash = mhash_add(hash, (OVS_FORCE uint32_t) flow->nw_src);
- hash = mhash_add(hash, (OVS_FORCE uint32_t) flow->nw_dst);
+ hash = hash_add(hash, (OVS_FORCE uint32_t) flow->nw_src);
+ hash = hash_add(hash, (OVS_FORCE uint32_t) flow->nw_dst);
}
- hash = mhash_add(hash, flow_u32[offsetof(struct flow, tp_src) / 4]);
+ hash = hash_add(hash, flow_u32[offsetof(struct flow, tp_src) / 4]);
- hash = mhash_finish(hash, 42); /* Arbitrary number. */
+ hash = hash_finish(hash, 42); /* Arbitrary number. */
}
return hash;
}
hash = basis;
for (i = 0; i < FLOW_U32S; i++) {
- hash = mhash_add(hash, flow_u32[i] & wc_u32[i]);
+ hash = hash_add(hash, flow_u32[i] & wc_u32[i]);
}
- return mhash_finish(hash, 4 * FLOW_U32S);
+ return hash_finish(hash, 4 * FLOW_U32S);
}
/* Sets the VLAN VID that 'flow' matches to 'vid', which is interpreted as an
flow->mpls_lse[0] = set_mpls_lse_values(ttl, tc, 1, htonl(label));
/* Clear all L3 and L4 fields. */
- BUILD_ASSERT(FLOW_WC_SEQ == 26);
+ BUILD_ASSERT(FLOW_WC_SEQ == 27);
memset((char *) flow + FLOW_SEGMENT_2_ENDS_AT, 0,
sizeof(struct flow) - FLOW_SEGMENT_2_ENDS_AT);
}
icmp->icmp_type = ntohs(flow->tp_src);
icmp->icmp_code = ntohs(flow->tp_dst);
icmp->icmp_csum = csum(icmp, ICMP_HEADER_LEN);
+ } else if (flow->nw_proto == IPPROTO_IGMP) {
+ struct igmp_header *igmp;
+
+ l4_len = sizeof *igmp;
+ igmp = ofpbuf_put_zeros(b, l4_len);
+ igmp->igmp_type = ntohs(flow->tp_src);
+ igmp->igmp_code = ntohs(flow->tp_dst);
+ put_16aligned_be32(&igmp->group, flow->igmp_group_ip4);
+ igmp->igmp_csum = csum(igmp, IGMP_HEADER_LEN);
} else if (flow->nw_proto == IPPROTO_ICMPV6) {
struct icmp6_hdr *icmp;
l4_len = flow_compose_l4(b, flow);
+ ip = ofpbuf_l3(b);
ip->ip_tot_len = htons(b->l4_ofs - b->l3_ofs + l4_len);
ip->ip_csum = csum(ip, sizeof *ip);
} else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
l4_len = flow_compose_l4(b, flow);
+ nh = ofpbuf_l3(b);
nh->ip6_plen = htons(l4_len);
} else if (flow->dl_type == htons(ETH_TYPE_ARP) ||
flow->dl_type == htons(ETH_TYPE_RARP)) {
static uint32_t *
miniflow_alloc_values(struct miniflow *flow, int n)
{
- if (n <= MINI_N_INLINE) {
+ int size = MINIFLOW_VALUES_SIZE(n);
+
+ if (size <= sizeof flow->inline_values) {
+ flow->values_inline = true;
return flow->inline_values;
} else {
COVERAGE_INC(miniflow_malloc);
- return xmalloc(n * sizeof *flow->values);
+ flow->values_inline = false;
+ flow->offline_values = xmalloc(size);
+ return flow->offline_values;
}
}
* when a miniflow is initialized from a (mini)mask, the values can be zeroes,
* so that the flow and mask always have the same maps.
*
- * This function initializes 'dst->values' (either inline if possible or with
+ * This function initializes values (either inline if possible or with
* malloc() otherwise) and copies the uint32_t elements of 'src' indicated by
* 'dst->map' into it. */
static void
miniflow_init__(struct miniflow *dst, const struct flow *src, int n)
{
const uint32_t *src_u32 = (const uint32_t *) src;
- unsigned int ofs;
+ uint32_t *dst_u32 = miniflow_alloc_values(dst, n);
uint64_t map;
- dst->values = miniflow_alloc_values(dst, n);
- ofs = 0;
for (map = dst->map; map; map = zero_rightmost_1bit(map)) {
- dst->values[ofs++] = src_u32[raw_ctz(map)];
+ *dst_u32++ = src_u32[raw_ctz(map)];
}
}
/* Initializes 'dst' as a copy of 'src'. The caller must eventually free 'dst'
- * with miniflow_destroy(). */
+ * with miniflow_destroy().
+ * Always allocates offline storage. */
void
miniflow_init(struct miniflow *dst, const struct flow *src)
{
void
miniflow_clone(struct miniflow *dst, const struct miniflow *src)
{
- int n = miniflow_n_values(src);
+ int size = MINIFLOW_VALUES_SIZE(miniflow_n_values(src));
+ uint32_t *values;
+
dst->map = src->map;
- dst->values = miniflow_alloc_values(dst, n);
- memcpy(dst->values, src->values, n * sizeof *dst->values);
+ if (size <= sizeof dst->inline_values) {
+ dst->values_inline = true;
+ values = dst->inline_values;
+ } else {
+ dst->values_inline = false;
+ COVERAGE_INC(miniflow_malloc);
+ dst->offline_values = xmalloc(size);
+ values = dst->offline_values;
+ }
+ memcpy(values, miniflow_get_values(src), size);
+}
+
+/* Initializes 'dst' as a copy of 'src'. The caller must have allocated
+ * 'dst' to have inline space all data in 'src'. */
+void
+miniflow_clone_inline(struct miniflow *dst, const struct miniflow *src,
+ size_t n_values)
+{
+ dst->values_inline = true;
+ dst->map = src->map;
+ memcpy(dst->inline_values, miniflow_get_values(src),
+ MINIFLOW_VALUES_SIZE(n_values));
}
/* Initializes 'dst' with the data in 'src', destroying 'src'.
- * The caller must eventually free 'dst' with miniflow_destroy(). */
+ * The caller must eventually free 'dst' with miniflow_destroy().
+ * 'dst' must be regularly sized miniflow, but 'src' can have
+ * larger than default inline values. */
void
miniflow_move(struct miniflow *dst, struct miniflow *src)
{
- if (src->values == src->inline_values) {
- dst->values = dst->inline_values;
- memcpy(dst->values, src->values,
- miniflow_n_values(src) * sizeof *dst->values);
+ int size = MINIFLOW_VALUES_SIZE(miniflow_n_values(src));
+
+ dst->map = src->map;
+ if (size <= sizeof dst->inline_values) {
+ dst->values_inline = true;
+ memcpy(dst->inline_values, miniflow_get_values(src), size);
+ miniflow_destroy(src);
+ } else if (src->values_inline) {
+ dst->values_inline = false;
+ COVERAGE_INC(miniflow_malloc);
+ dst->offline_values = xmalloc(size);
+ memcpy(dst->offline_values, src->inline_values, size);
} else {
- dst->values = src->values;
+ dst->values_inline = false;
+ dst->offline_values = src->offline_values;
}
- dst->map = src->map;
}
/* Frees any memory owned by 'flow'. Does not free the storage in which 'flow'
void
miniflow_destroy(struct miniflow *flow)
{
- if (flow->values != flow->inline_values) {
- free(flow->values);
+ if (!flow->values_inline) {
+ free(flow->offline_values);
}
}
miniflow_get(const struct miniflow *flow, unsigned int u32_ofs)
{
return (flow->map & UINT64_C(1) << u32_ofs)
- ? *(flow->values +
+ ? *(miniflow_get_u32_values(flow) +
count_1bits(flow->map & ((UINT64_C(1) << u32_ofs) - 1)))
: 0;
}
bool
miniflow_equal(const struct miniflow *a, const struct miniflow *b)
{
- const uint32_t *ap = a->values;
- const uint32_t *bp = b->values;
+ const uint32_t *ap = miniflow_get_u32_values(a);
+ const uint32_t *bp = miniflow_get_u32_values(b);
const uint64_t a_map = a->map;
const uint64_t b_map = b->map;
- uint64_t map;
- if (a_map == b_map) {
- for (map = a_map; map; map = zero_rightmost_1bit(map)) {
- if (*ap++ != *bp++) {
- return false;
- }
- }
+ if (OVS_LIKELY(a_map == b_map)) {
+ int count = miniflow_n_values(a);
+
+ return !memcmp(ap, bp, count * sizeof *ap);
} else {
+ uint64_t map;
+
for (map = a_map | b_map; map; map = zero_rightmost_1bit(map)) {
uint64_t bit = rightmost_1bit(map);
uint64_t a_value = a_map & bit ? *ap++ : 0;
miniflow_equal_in_minimask(const struct miniflow *a, const struct miniflow *b,
const struct minimask *mask)
{
- const uint32_t *p;
+ const uint32_t *p = miniflow_get_u32_values(&mask->masks);
uint64_t map;
- p = mask->masks.values;
-
for (map = mask->masks.map; map; map = zero_rightmost_1bit(map)) {
int ofs = raw_ctz(map);
- if ((miniflow_get(a, ofs) ^ miniflow_get(b, ofs)) & *p) {
+ if ((miniflow_get(a, ofs) ^ miniflow_get(b, ofs)) & *p++) {
return false;
}
- p++;
}
return true;
const struct minimask *mask)
{
const uint32_t *b_u32 = (const uint32_t *) b;
- const uint32_t *p;
+ const uint32_t *p = miniflow_get_u32_values(&mask->masks);
uint64_t map;
- p = mask->masks.values;
-
for (map = mask->masks.map; map; map = zero_rightmost_1bit(map)) {
int ofs = raw_ctz(map);
- if ((miniflow_get(a, ofs) ^ b_u32[ofs]) & *p) {
+ if ((miniflow_get(a, ofs) ^ b_u32[ofs]) & *p++) {
return false;
}
- p++;
}
return true;
uint32_t storage[FLOW_U32S])
{
struct miniflow *dst = &dst_->masks;
+ uint32_t *dst_values = storage;
const struct miniflow *a = &a_->masks;
const struct miniflow *b = &b_->masks;
uint64_t map;
int n = 0;
- dst->values = storage;
+ dst->values_inline = false;
+ dst->offline_values = storage;
dst->map = 0;
for (map = a->map & b->map; map; map = zero_rightmost_1bit(map)) {
if (mask) {
dst->map |= rightmost_1bit(map);
- dst->values[n++] = mask;
+ dst_values[n++] = mask;
}
}
}
bool
minimask_has_extra(const struct minimask *a, const struct minimask *b)
{
- const uint32_t *p = b->masks.values;
+ const uint32_t *p = miniflow_get_u32_values(&b->masks);
uint64_t map;
for (map = b->masks.map; map; map = zero_rightmost_1bit(map)) {