lib/flow: add dp_hash and recirc_id to struct flow
[sliver-openvswitch.git] / lib / flow.c
index 3633dff..f6ec135 100644 (file)
@@ -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,12 @@ 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 (md) {
+        flow->tunnel = md->tunnel;
+        flow->in_port = md->in_port;
+        flow->skb_priority = md->skb_priority;
+        flow->pkt_mark = md->pkt_mark;
     }
-    if (in_port) {
-        flow->in_port = *in_port;
-    }
-    flow->skb_priority = skb_priority;
-    flow->pkt_mark = pkt_mark;
 
     packet->l2   = b.data;
     packet->l2_5 = NULL;
@@ -519,12 +514,26 @@ 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 == 25);
 
+    fmd->dp_hash = flow->dp_hash;
+    fmd->recirc_id = flow->recirc_id;
     fmd->tun_id = flow->tunnel.tun_id;
     fmd->tun_src = flow->tunnel.ip_src;
     fmd->tun_dst = flow->tunnel.ip_dst;
@@ -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++;
@@ -811,6 +820,25 @@ flow_wildcards_set_reg_mask(struct flow_wildcards *wc, int idx, uint32_t mask)
     wc->masks.regs[idx] = mask;
 }
 
+/* Calculates the 5-tuple hash from the given flow. */
+uint32_t
+flow_hash_5tuple(const struct flow *flow, uint32_t basis)
+{
+    uint32_t hash = 0;
+
+    if (!flow) {
+        return 0;
+    }
+
+    hash = mhash_add(basis, (OVS_FORCE uint32_t) flow->nw_src);
+    hash = mhash_add(hash, (OVS_FORCE uint32_t) flow->nw_dst);
+    hash = mhash_add(hash, ((OVS_FORCE uint32_t) flow->tp_src << 16)
+                           | (OVS_FORCE uint32_t) flow->tp_dst);
+    hash = mhash_add(hash, flow->nw_proto);
+
+    return mhash_finish(hash, 13);
+}
+
 /* Hashes 'flow' based on its L2 through L4 protocol information. */
 uint32_t
 flow_hash_symmetric_l4(const struct flow *flow, uint32_t basis)
@@ -922,14 +950,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 +974,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 +1063,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 == 25);
+        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 +1359,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 +1426,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]);
+        }
     }
 }
 \f
@@ -1505,11 +1716,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 +1738,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 +1755,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. */