Merge branch 'mainstream'
[sliver-openvswitch.git] / lib / flow.c
index 6476029..ab49b80 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -81,6 +81,12 @@ pull_udp(struct ofpbuf *packet)
     return ofpbuf_try_pull(packet, UDP_HEADER_LEN);
 }
 
+static struct sctp_header *
+pull_sctp(struct ofpbuf *packet)
+{
+    return ofpbuf_try_pull(packet, SCTP_HEADER_LEN);
+}
+
 static struct icmp_header *
 pull_icmp(struct ofpbuf *packet)
 {
@@ -265,6 +271,17 @@ parse_udp(struct ofpbuf *packet, struct ofpbuf *b, struct flow *flow)
     }
 }
 
+static void
+parse_sctp(struct ofpbuf *packet, struct ofpbuf *b, struct flow *flow)
+{
+    const struct sctp_header *sctp = pull_sctp(b);
+    if (sctp) {
+        flow->tp_src = sctp->sctp_src;
+        flow->tp_dst = sctp->sctp_dst;
+        packet->l7 = b->data;
+    }
+}
+
 static bool
 parse_icmpv6(struct ofpbuf *b, struct flow *flow)
 {
@@ -337,7 +354,7 @@ invalid:
 }
 
 /* Initializes 'flow' members from 'packet', 'skb_priority', 'tnl', and
- * 'ofp_in_port'.
+ * 'in_port'.
  *
  * Initializes 'packet' header pointers as follows:
  *
@@ -352,12 +369,12 @@ invalid:
  *    - packet->l4 to just past the IPv4 header, if one is present and has a
  *      correct length, and otherwise NULL.
  *
- *    - packet->l7 to just past the TCP or UDP or ICMP header, if one is
+ *    - packet->l7 to just past the TCP/UDP/SCTP/ICMP header, if one is
  *      present and has a correct length, and otherwise NULL.
  */
 void
-flow_extract(struct ofpbuf *packet, uint32_t skb_priority, uint32_t skb_mark,
-             const struct flow_tnl *tnl, uint16_t ofp_in_port,
+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,
              struct flow *flow)
 {
     struct ofpbuf b = *packet;
@@ -371,9 +388,11 @@ flow_extract(struct ofpbuf *packet, uint32_t skb_priority, uint32_t skb_mark,
         ovs_assert(tnl != &flow->tunnel);
         flow->tunnel = *tnl;
     }
-    flow->in_port = ofp_in_port;
+    if (in_port) {
+        flow->in_port = *in_port;
+    }
     flow->skb_priority = skb_priority;
-    flow->skb_mark = skb_mark;
+    flow->pkt_mark = pkt_mark;
 
     packet->l2   = b.data;
     packet->l2_5 = NULL;
@@ -428,6 +447,8 @@ flow_extract(struct ofpbuf *packet, uint32_t skb_priority, uint32_t skb_mark,
                     parse_tcp(packet, &b, flow);
                 } else if (flow->nw_proto == IPPROTO_UDP) {
                     parse_udp(packet, &b, flow);
+                } else if (flow->nw_proto == IPPROTO_SCTP) {
+                    parse_sctp(packet, &b, flow);
                 } else if (flow->nw_proto == IPPROTO_ICMP) {
                     const struct icmp_header *icmp = pull_icmp(&b);
                     if (icmp) {
@@ -448,6 +469,8 @@ flow_extract(struct ofpbuf *packet, uint32_t skb_priority, uint32_t skb_mark,
             parse_tcp(packet, &b, flow);
         } else if (flow->nw_proto == IPPROTO_UDP) {
             parse_udp(packet, &b, flow);
+        } else if (flow->nw_proto == IPPROTO_SCTP) {
+            parse_sctp(packet, &b, flow);
         } else if (flow->nw_proto == IPPROTO_ICMPV6) {
             if (parse_icmpv6(&b, flow)) {
                 packet->l7 = b.data;
@@ -498,7 +521,8 @@ flow_get_metadata(const struct flow *flow, struct flow_metadata *fmd)
     fmd->tun_dst = flow->tunnel.ip_dst;
     fmd->metadata = flow->metadata;
     memcpy(fmd->regs, flow->regs, sizeof fmd->regs);
-    fmd->in_port = flow->in_port;
+    fmd->pkt_mark = flow->pkt_mark;
+    fmd->in_port = flow->in_port.ofp_port;
 }
 
 char *
@@ -604,13 +628,13 @@ flow_wildcards_is_catchall(const struct flow_wildcards *wc)
     return true;
 }
 
-/* Initializes 'dst' as the combination of wildcards in 'src1' and 'src2'.
- * That is, a bit or a field is wildcarded in 'dst' if it is wildcarded in
- * 'src1' or 'src2' or both.  */
+/* Sets 'dst' as the bitwise AND of wildcards in 'src1' and 'src2'.
+ * That is, a bit or a field is wildcarded in 'dst' if it is wildcarded
+ * in 'src1' or 'src2' or both.  */
 void
-flow_wildcards_combine(struct flow_wildcards *dst,
-                       const struct flow_wildcards *src1,
-                       const struct flow_wildcards *src2)
+flow_wildcards_and(struct flow_wildcards *dst,
+                   const struct flow_wildcards *src1,
+                   const struct flow_wildcards *src2)
 {
     uint32_t *dst_u32 = (uint32_t *) &dst->masks;
     const uint32_t *src1_u32 = (const uint32_t *) &src1->masks;
@@ -622,6 +646,24 @@ flow_wildcards_combine(struct flow_wildcards *dst,
     }
 }
 
+/* Sets 'dst' as the bitwise OR of wildcards in 'src1' and 'src2'.  That
+ * is, a bit or a field is wildcarded in 'dst' if it is neither
+ * wildcarded in 'src1' nor 'src2'. */
+void
+flow_wildcards_or(struct flow_wildcards *dst,
+                  const struct flow_wildcards *src1,
+                  const struct flow_wildcards *src2)
+{
+    uint32_t *dst_u32 = (uint32_t *) &dst->masks;
+    const uint32_t *src1_u32 = (const uint32_t *) &src1->masks;
+    const uint32_t *src2_u32 = (const uint32_t *) &src2->masks;
+    size_t i;
+
+    for (i = 0; i < FLOW_U32S; i++) {
+        dst_u32[i] = src1_u32[i] | src2_u32[i];
+    }
+}
+
 /* Perform a bitwise OR of miniflow 'src' flow data with the equivalent
  * fields in 'dst', storing the result in 'dst'. */
 static void
@@ -740,7 +782,7 @@ flow_hash_symmetric_l4(const struct flow *flow, uint32_t basis)
     if (fields.eth_type == htons(ETH_TYPE_IP)) {
         fields.ipv4_addr = flow->nw_src ^ flow->nw_dst;
         fields.ip_proto = flow->nw_proto;
-        if (fields.ip_proto == IPPROTO_TCP) {
+        if (fields.ip_proto == IPPROTO_TCP || fields.ip_proto == IPPROTO_SCTP) {
             fields.tp_port = flow->tp_src ^ flow->tp_dst;
         }
     } else if (fields.eth_type == htons(ETH_TYPE_IPV6)) {
@@ -752,13 +794,46 @@ flow_hash_symmetric_l4(const struct flow *flow, uint32_t basis)
             ipv6_addr[i] = a[i] ^ b[i];
         }
         fields.ip_proto = flow->nw_proto;
-        if (fields.ip_proto == IPPROTO_TCP) {
+        if (fields.ip_proto == IPPROTO_TCP || fields.ip_proto == IPPROTO_SCTP) {
             fields.tp_port = flow->tp_src ^ flow->tp_dst;
         }
     }
     return jhash_bytes(&fields, sizeof fields, basis);
 }
 
+/* Masks the fields in 'wc' that are used by the flow hash 'fields'. */
+void
+flow_mask_hash_fields(const struct flow *flow, struct flow_wildcards *wc,
+                      enum nx_hash_fields fields)
+{
+    switch (fields) {
+    case NX_HASH_FIELDS_ETH_SRC:
+        memset(&wc->masks.dl_src, 0xff, sizeof wc->masks.dl_src);
+        break;
+
+    case NX_HASH_FIELDS_SYMMETRIC_L4:
+        memset(&wc->masks.dl_src, 0xff, sizeof wc->masks.dl_src);
+        memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst);
+        if (flow->dl_type == htons(ETH_TYPE_IP)) {
+            memset(&wc->masks.nw_src, 0xff, sizeof wc->masks.nw_src);
+            memset(&wc->masks.nw_dst, 0xff, sizeof wc->masks.nw_dst);
+        } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
+            memset(&wc->masks.ipv6_src, 0xff, sizeof wc->masks.ipv6_src);
+            memset(&wc->masks.ipv6_dst, 0xff, sizeof wc->masks.ipv6_dst);
+        }
+        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);
+        }
+        wc->masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI);
+        break;
+
+    default:
+        NOT_REACHED();
+    }
+}
+
 /* Hashes the portions of 'flow' designated by 'fields'. */
 uint32_t
 flow_hash_fields(const struct flow *flow, enum nx_hash_fields fields,
@@ -795,6 +870,24 @@ flow_hash_fields_valid(enum nx_hash_fields fields)
         || fields == NX_HASH_FIELDS_SYMMETRIC_L4;
 }
 
+/* Returns a hash value for the bits of 'flow' that are active based on
+ * 'wc', given 'basis'. */
+uint32_t
+flow_hash_in_wildcards(const struct flow *flow,
+                       const struct flow_wildcards *wc, uint32_t basis)
+{
+    const uint32_t *wc_u32 = (const uint32_t *) &wc->masks;
+    const uint32_t *flow_u32 = (const uint32_t *) flow;
+    uint32_t hash;
+    size_t i;
+
+    hash = basis;
+    for (i = 0; i < FLOW_U32S; i++) {
+        hash = mhash_add(hash, flow_u32[i] & wc_u32[i]);
+    }
+    return mhash_finish(hash, 4 * FLOW_U32S);
+}
+
 /* Sets the VLAN VID that 'flow' matches to 'vid', which is interpreted as an
  * OpenFlow 1.0 "dl_vlan" value:
  *
@@ -927,6 +1020,12 @@ flow_compose(struct ofpbuf *b, const struct flow *flow)
                 b->l4 = udp = ofpbuf_put_zeros(b, sizeof *udp);
                 udp->udp_src = flow->tp_src;
                 udp->udp_dst = flow->tp_dst;
+            } else if (flow->nw_proto == IPPROTO_SCTP) {
+                struct sctp_header *sctp;
+
+                b->l4 = sctp = ofpbuf_put_zeros(b, sizeof *sctp);
+                sctp->sctp_src = flow->tp_src;
+                sctp->sctp_dst = flow->tp_dst;
             } else if (flow->nw_proto == IPPROTO_ICMP) {
                 struct icmp_header *icmp;