Merge branch 'mainstream'
authorGiuseppe Lettieri <g.lettieri@iet.unipi.it>
Sat, 24 Aug 2013 10:23:11 +0000 (12:23 +0200)
committerGiuseppe Lettieri <g.lettieri@iet.unipi.it>
Sat, 24 Aug 2013 10:23:11 +0000 (12:23 +0200)
92 files changed:
AUTHORS
NEWS
OPENFLOW-1.1+
datapath/actions.c
datapath/datapath.c
datapath/flow.c
datapath/flow.h
datapath/linux/Modules.mk
datapath/linux/compat/include/linux/sctp.h [new file with mode: 0644]
datapath/linux/compat/include/net/ipv6.h
datapath/linux/compat/include/net/sctp/checksum.h [new file with mode: 0644]
datapath/vport-lisp.c
datapath/vport.c
debian/copyright.in
include/linux/openvswitch.h
include/sparse/netinet/in.h
include/sparse/pthread.h
lib/automake.mk
lib/bfd.c
lib/bfd.h
lib/bond.c
lib/byte-order.h
lib/cfm.c
lib/compiler.h
lib/coverage.c
lib/coverage.h
lib/crc32c.c [new file with mode: 0644]
lib/crc32c.h [new file with mode: 0644]
lib/dpif-linux.c
lib/dpif-netdev.c
lib/dpif-provider.h
lib/dpif.c
lib/dpif.h
lib/fatal-signal.c
lib/flow.c
lib/flow.h
lib/lacp.c
lib/match.c
lib/meta-flow.c
lib/meta-flow.h
lib/netdev-bsd.c
lib/netdev-dummy.c
lib/netdev-linux.c
lib/netdev-vport.c
lib/netdev.c
lib/netlink-socket.c
lib/nx-match.c
lib/odp-execute.c
lib/odp-util.c
lib/odp-util.h
lib/ofp-errors.h
lib/ofp-parse.c
lib/ofp-print.c
lib/ofp-util.c
lib/ovs-atomic-gcc4+.c
lib/ovs-atomic.h
lib/ovs-thread.c
lib/ovs-thread.h
lib/packets.c
lib/packets.h
lib/seq.c
lib/stp.c
lib/uuid.c
lib/vlog.c
lib/vlog.h
ofproto/ofproto-dpif-ipfix.c
ofproto/ofproto-dpif-ipfix.h
ofproto/ofproto-dpif-sflow.c
ofproto/ofproto-dpif-upcall.c
ofproto/ofproto-dpif-upcall.h
ofproto/ofproto-dpif-xlate.c
ofproto/ofproto-dpif.c
ofproto/ofproto-dpif.h
ofproto/ofproto-provider.h
ofproto/ofproto.c
ofproto/ofproto.h
ofproto/tunnel.c
tests/bfd.at
tests/learn.at
tests/library.at
tests/ofp-print.at
tests/ofproto-dpif.at
tests/ovs-ofctl.at
tests/test-atomic.c
tests/test-csum.c
tests/test-netflow.c
utilities/ovs-ofctl.8.in
utilities/ovs-vsctl.8.in
vswitchd/bridge.c
vswitchd/system-stats.c
vswitchd/vswitch.ovsschema
vswitchd/vswitch.xml

diff --git a/AUTHORS b/AUTHORS
index fc665b3..890ed3a 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -36,6 +36,7 @@ FUJITA Tomonori         fujita.tomonori@lab.ntt.co.jp
 Gaetano Catalli         gaetano.catalli@gmail.com
 Giuseppe Lettieri       g.lettieri@iet.unipi.it
 Glen Gibb               grg@stanford.edu
+Guolin Yang             gyang@nicira.com
 Gurucharan Shetty       gshetty@nicira.com
 Henry Mai               hmai@nicira.com
 Hao Zheng               hzheng@nicira.com
@@ -70,6 +71,7 @@ Paul Fazzone            pfazzone@nicira.com
 Paul Ingram             paul@nicira.com
 Pavithra Ramesh         paramesh@vmware.com
 Philippe Jung           phil.jung@free.fr
+pritesh                 pritesh.kothari@cisco.com
 Pravin B Shelar         pshelar@nicira.com
 Raju Subramanian        rsubramanian@nicira.com
 Ravi Kerur              Ravi.Kerur@telekom.com
diff --git a/NEWS b/NEWS
index 1246383..ef97b04 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,7 @@
 post-v1.12.0
 ---------------------
+    - OpenFlow:
+      * Support matching, rewriting SCTP ports
 
 
 v1.12.0 - xx xxx xxxx
@@ -25,6 +27,7 @@ v1.12.0 - xx xxx xxxx
     - Support for Linux kernels up to 3.10
     - ovs-ofctl:
       * New "ofp-parse" for printing OpenFlow messages read from a file.
+    - Added configurable flow caching support to IPFIX exporter.
 
 
 v1.11.0 - xx xxx xxxx
index 329c5a2..190dd29 100644 (file)
@@ -85,11 +85,6 @@ probably incomplete.
       feature.  This is partially merged.
       [optional for OF1.1+]
 
-    * SCTP.  Joe Stringer maintains a patch series that adds this
-      feature.  It has received review comments that need to be
-      addressed before it is merged.
-      [optional for OF1.1+]
-
     * Match and set double-tagged VLANs (QinQ).  This requires kernel
       work for reasonable performance.
       [optional for OF1.1+]
index 2c09d57..fa4b904 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/in.h>
 #include <linux/ip.h>
 #include <linux/openvswitch.h>
+#include <linux/sctp.h>
 #include <linux/tcp.h>
 #include <linux/udp.h>
 #include <linux/in6.h>
@@ -31,6 +32,7 @@
 #include <net/ipv6.h>
 #include <net/checksum.h>
 #include <net/dsfield.h>
+#include <net/sctp/checksum.h>
 
 #include "checksum.h"
 #include "datapath.h"
@@ -360,6 +362,39 @@ static int set_tcp(struct sk_buff *skb, const struct ovs_key_tcp *tcp_port_key)
        return 0;
 }
 
+static int set_sctp(struct sk_buff *skb,
+                    const struct ovs_key_sctp *sctp_port_key)
+{
+       struct sctphdr *sh;
+       int err;
+       unsigned int sctphoff = skb_transport_offset(skb);
+
+       err = make_writable(skb, sctphoff + sizeof(struct sctphdr));
+       if (unlikely(err))
+               return err;
+
+       sh = sctp_hdr(skb);
+       if (sctp_port_key->sctp_src != sh->source ||
+           sctp_port_key->sctp_dst != sh->dest) {
+               __le32 old_correct_csum, new_csum, old_csum;
+
+               old_csum = sh->checksum;
+               old_correct_csum = sctp_compute_cksum(skb, sctphoff);
+
+               sh->source = sctp_port_key->sctp_src;
+               sh->dest = sctp_port_key->sctp_dst;
+
+               new_csum = sctp_compute_cksum(skb, sctphoff);
+
+               /* Carry any checksum errors through. */
+               sh->checksum = old_csum ^ old_correct_csum ^ new_csum;
+
+               skb_clear_rxhash(skb);
+       }
+
+       return 0;
+}
+
 static int do_output(struct datapath *dp, struct sk_buff *skb, int out_port)
 {
        struct vport *vport;
@@ -469,6 +504,10 @@ static int execute_set_action(struct sk_buff *skb,
        case OVS_KEY_ATTR_UDP:
                err = set_udp(skb, nla_data(nested_attr));
                break;
+
+       case OVS_KEY_ATTR_SCTP:
+               err = set_sctp(skb, nla_data(nested_attr));
+               break;
        }
 
        return err;
index 48f17c0..27deec8 100644 (file)
@@ -726,6 +726,12 @@ static int validate_set(const struct nlattr *a,
 
                return validate_tp_port(flow_key);
 
+       case OVS_KEY_ATTR_SCTP:
+               if (flow_key->ip.proto != IPPROTO_SCTP)
+                       return -EINVAL;
+
+               return validate_tp_port(flow_key);
+
        default:
                return -EINVAL;
        }
index 4075350..3dc1b44 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/if_arp.h>
 #include <linux/ip.h>
 #include <linux/ipv6.h>
+#include <linux/sctp.h>
 #include <linux/tcp.h>
 #include <linux/udp.h>
 #include <linux/icmp.h>
@@ -130,6 +131,7 @@ static bool ovs_match_validate(const struct sw_flow_match *match,
                        | (1ULL << OVS_KEY_ATTR_IPV6)
                        | (1ULL << OVS_KEY_ATTR_TCP)
                        | (1ULL << OVS_KEY_ATTR_UDP)
+                       | (1ULL << OVS_KEY_ATTR_SCTP)
                        | (1ULL << OVS_KEY_ATTR_ICMP)
                        | (1ULL << OVS_KEY_ATTR_ICMPV6)
                        | (1ULL << OVS_KEY_ATTR_ARP)
@@ -160,6 +162,12 @@ static bool ovs_match_validate(const struct sw_flow_match *match,
                                        mask_allowed |= 1ULL << OVS_KEY_ATTR_UDP;
                        }
 
+                       if (match->key->ip.proto == IPPROTO_SCTP) {
+                               key_expected |= 1ULL << OVS_KEY_ATTR_SCTP;
+                               if (match->mask && (match->mask->key.ip.proto == 0xff))
+                                       mask_allowed |= 1ULL << OVS_KEY_ATTR_SCTP;
+                       }
+
                        if (match->key->ip.proto == IPPROTO_TCP) {
                                key_expected |= 1ULL << OVS_KEY_ATTR_TCP;
                                if (match->mask && (match->mask->key.ip.proto == 0xff))
@@ -186,6 +194,12 @@ static bool ovs_match_validate(const struct sw_flow_match *match,
                                        mask_allowed |= 1ULL << OVS_KEY_ATTR_UDP;
                        }
 
+                       if (match->key->ip.proto == IPPROTO_SCTP) {
+                               key_expected |= 1ULL << OVS_KEY_ATTR_SCTP;
+                               if (match->mask && (match->mask->key.ip.proto == 0xff))
+                                       mask_allowed |= 1ULL << OVS_KEY_ATTR_SCTP;
+                       }
+
                        if (match->key->ip.proto == IPPROTO_TCP) {
                                key_expected |= 1ULL << OVS_KEY_ATTR_TCP;
                                if (match->mask && (match->mask->key.ip.proto == 0xff))
@@ -281,6 +295,12 @@ static bool udphdr_ok(struct sk_buff *skb)
                                  sizeof(struct udphdr));
 }
 
+static bool sctphdr_ok(struct sk_buff *skb)
+{
+       return pskb_may_pull(skb, skb_transport_offset(skb) +
+                                 sizeof(struct sctphdr));
+}
+
 static bool icmphdr_ok(struct sk_buff *skb)
 {
        return pskb_may_pull(skb, skb_transport_offset(skb) +
@@ -799,7 +819,6 @@ invalid:
  * Ethernet header
  * @in_port: port number on which @skb was received.
  * @key: output flow key
- * @key_lenp: length of output flow key
  *
  * The caller must ensure that skb->len >= ETH_HLEN.
  *
@@ -900,6 +919,12 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key)
                                key->ipv4.tp.src = udp->source;
                                key->ipv4.tp.dst = udp->dest;
                        }
+               } else if (key->ip.proto == IPPROTO_SCTP) {
+                       if (sctphdr_ok(skb)) {
+                               struct sctphdr *sctp = sctp_hdr(skb);
+                               key->ipv4.tp.src = sctp->source;
+                               key->ipv4.tp.dst = sctp->dest;
+                       }
                } else if (key->ip.proto == IPPROTO_ICMP) {
                        if (icmphdr_ok(skb)) {
                                struct icmphdr *icmp = icmp_hdr(skb);
@@ -962,6 +987,12 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key)
                                key->ipv6.tp.src = udp->source;
                                key->ipv6.tp.dst = udp->dest;
                        }
+               } else if (key->ip.proto == NEXTHDR_SCTP) {
+                       if (sctphdr_ok(skb)) {
+                               struct sctphdr *sctp = sctp_hdr(skb);
+                               key->ipv6.tp.src = sctp->source;
+                               key->ipv6.tp.dst = sctp->dest;
+                       }
                } else if (key->ip.proto == NEXTHDR_ICMP) {
                        if (icmp6hdr_ok(skb)) {
                                error = parse_icmpv6(skb, key, nh_len);
@@ -974,10 +1005,11 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key)
        return 0;
 }
 
-static u32 ovs_flow_hash(const struct sw_flow_key *key, int key_start, int key_len)
+static u32 ovs_flow_hash(const struct sw_flow_key *key, int key_start,
+                        int key_end)
 {
        return jhash2((u32 *)((u8 *)key + key_start),
-                     DIV_ROUND_UP(key_len - key_start, sizeof(u32)), 0);
+                     DIV_ROUND_UP(key_end - key_start, sizeof(u32)), 0);
 }
 
 static int flow_key_start(const struct sw_flow_key *key)
@@ -989,31 +1021,31 @@ static int flow_key_start(const struct sw_flow_key *key)
 }
 
 static bool __cmp_key(const struct sw_flow_key *key1,
-               const struct sw_flow_key *key2,  int key_start, int key_len)
+               const struct sw_flow_key *key2,  int key_start, int key_end)
 {
        return !memcmp((u8 *)key1 + key_start,
-                       (u8 *)key2 + key_start, (key_len - key_start));
+                       (u8 *)key2 + key_start, (key_end - key_start));
 }
 
 static bool __flow_cmp_key(const struct sw_flow *flow,
-               const struct sw_flow_key *key, int key_start, int key_len)
+               const struct sw_flow_key *key, int key_start, int key_end)
 {
-       return __cmp_key(&flow->key, key, key_start, key_len);
+       return __cmp_key(&flow->key, key, key_start, key_end);
 }
 
 static bool __flow_cmp_unmasked_key(const struct sw_flow *flow,
-                 const struct sw_flow_key *key, int key_start, int key_len)
+                 const struct sw_flow_key *key, int key_start, int key_end)
 {
-       return __cmp_key(&flow->unmasked_key, key, key_start, key_len);
+       return __cmp_key(&flow->unmasked_key, key, key_start, key_end);
 }
 
 bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow,
-               const struct sw_flow_key *key, int key_len)
+               const struct sw_flow_key *key, int key_end)
 {
        int key_start;
        key_start = flow_key_start(key);
 
-       return __flow_cmp_unmasked_key(flow, key, key_start, key_len);
+       return __flow_cmp_unmasked_key(flow, key, key_start, key_end);
 
 }
 
@@ -1021,11 +1053,11 @@ struct sw_flow *ovs_flow_lookup_unmasked_key(struct flow_table *table,
                                       struct sw_flow_match *match)
 {
        struct sw_flow_key *unmasked = match->key;
-       int key_len = match->range.end;
+       int key_end = match->range.end;
        struct sw_flow *flow;
 
        flow = ovs_flow_lookup(table, unmasked);
-       if (flow && (!ovs_flow_cmp_unmasked_key(flow, unmasked, key_len)))
+       if (flow && (!ovs_flow_cmp_unmasked_key(flow, unmasked, key_end)))
                flow = NULL;
 
        return flow;
@@ -1038,16 +1070,16 @@ static struct sw_flow *ovs_masked_flow_lookup(struct flow_table *table,
        struct sw_flow *flow;
        struct hlist_head *head;
        int key_start = mask->range.start;
-       int key_len = mask->range.end;
+       int key_end = mask->range.end;
        u32 hash;
        struct sw_flow_key masked_key;
 
        ovs_flow_key_mask(&masked_key, flow_key, mask);
-       hash = ovs_flow_hash(&masked_key, key_start, key_len);
+       hash = ovs_flow_hash(&masked_key, key_start, key_end);
        head = find_bucket(table, hash);
        hlist_for_each_entry_rcu(flow, head, hash_node[table->node_ver]) {
                if (flow->mask == mask &&
-                   __flow_cmp_key(flow, &masked_key, key_start, key_len))
+                   __flow_cmp_key(flow, &masked_key, key_start, key_end))
                        return flow;
        }
        return NULL;
@@ -1096,6 +1128,7 @@ const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = {
        [OVS_KEY_ATTR_IPV6] = sizeof(struct ovs_key_ipv6),
        [OVS_KEY_ATTR_TCP] = sizeof(struct ovs_key_tcp),
        [OVS_KEY_ATTR_UDP] = sizeof(struct ovs_key_udp),
+       [OVS_KEY_ATTR_SCTP] = sizeof(struct ovs_key_sctp),
        [OVS_KEY_ATTR_ICMP] = sizeof(struct ovs_key_icmp),
        [OVS_KEY_ATTR_ICMPV6] = sizeof(struct ovs_key_icmpv6),
        [OVS_KEY_ATTR_ARP] = sizeof(struct ovs_key_arp),
@@ -1202,7 +1235,7 @@ int ovs_ipv4_tun_from_nlattr(const struct nlattr *attr,
 
                if (ovs_tunnel_key_lens[type] != nla_len(a)) {
                        OVS_NLERR("IPv4 tunnel attribute type has unexpected "
-                                 " legnth (type=%d, length=%d, expected=%d).\n",
+                                 " length (type=%d, length=%d, expected=%d).\n",
                                  type, nla_len(a), ovs_tunnel_key_lens[type]);
                        return -EINVAL;
                }
@@ -1389,7 +1422,7 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match,  u64 attrs,
                        /* Always exact match EtherType. */
                        eth_type = htons(0xffff);
                } else if (ntohs(eth_type) < ETH_P_802_3_MIN) {
-                       OVS_NLERR("EtherType is less than mimimum (type=%x, min=%x).\n",
+                       OVS_NLERR("EtherType is less than minimum (type=%x, min=%x).\n",
                                        ntohs(eth_type), ETH_P_802_3_MIN);
                        return -EINVAL;
                }
@@ -1515,6 +1548,24 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match,  u64 attrs,
                attrs &= ~(1ULL << OVS_KEY_ATTR_UDP);
        }
 
+       if (attrs & (1ULL << OVS_KEY_ATTR_SCTP)) {
+               const struct ovs_key_sctp *sctp_key;
+
+               sctp_key = nla_data(a[OVS_KEY_ATTR_SCTP]);
+               if (orig_attrs & (1ULL << OVS_KEY_ATTR_IPV4)) {
+                       SW_FLOW_KEY_PUT(match, ipv4.tp.src,
+                                       sctp_key->sctp_src, is_mask);
+                       SW_FLOW_KEY_PUT(match, ipv4.tp.dst,
+                                       sctp_key->sctp_dst, is_mask);
+               } else {
+                       SW_FLOW_KEY_PUT(match, ipv6.tp.src,
+                                       sctp_key->sctp_src, is_mask);
+                       SW_FLOW_KEY_PUT(match, ipv6.tp.dst,
+                                       sctp_key->sctp_dst, is_mask);
+               }
+               attrs &= ~(1ULL << OVS_KEY_ATTR_SCTP);
+       }
+
        if (attrs & (1ULL << OVS_KEY_ATTR_ICMP)) {
                const struct ovs_key_icmp *icmp_key;
 
@@ -1585,26 +1636,36 @@ int ovs_match_from_nlattrs(struct sw_flow_match *match,
        if (err)
                return err;
 
-       if (key_attrs & 1ULL << OVS_KEY_ATTR_ENCAP) {
-               encap = a[OVS_KEY_ATTR_ENCAP];
-               key_attrs &= ~(1ULL << OVS_KEY_ATTR_ENCAP);
-               if (nla_len(encap)) {
-                       __be16 eth_type = 0; /* ETH_P_8021Q */
+       if ((key_attrs & (1ULL << OVS_KEY_ATTR_ETHERNET)) &&
+           (key_attrs & (1ULL << OVS_KEY_ATTR_ETHERTYPE)) &&
+           (nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]) == htons(ETH_P_8021Q))) {
+               __be16 tci;
 
-                       if (a[OVS_KEY_ATTR_ETHERTYPE])
-                               eth_type = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]);
+               if (!((key_attrs & (1ULL << OVS_KEY_ATTR_VLAN)) &&
+                     (key_attrs & (1ULL << OVS_KEY_ATTR_ENCAP)))) {
+                       OVS_NLERR("Invalid Vlan frame.\n");
+                       return -EINVAL;
+               }
 
-                       if  ((eth_type == htons(ETH_P_8021Q)) && (a[OVS_KEY_ATTR_VLAN])) {
-                               encap_valid = true;
-                               key_attrs &= ~(1ULL << OVS_KEY_ATTR_ETHERTYPE);
-                               err = parse_flow_nlattrs(encap, a, &key_attrs);
-                       } else {
-                               OVS_NLERR("Encap attribute is set for a non-VLAN frame.\n");
-                               err = -EINVAL;
-                       }
+               key_attrs &= ~(1ULL << OVS_KEY_ATTR_ETHERTYPE);
+               tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]);
+               encap = a[OVS_KEY_ATTR_ENCAP];
+               key_attrs &= ~(1ULL << OVS_KEY_ATTR_ENCAP);
+               encap_valid = true;
 
+               if (tci & htons(VLAN_TAG_PRESENT)) {
+                       err = parse_flow_nlattrs(encap, a, &key_attrs);
                        if (err)
                                return err;
+               } else if (!tci) {
+                       /* Corner case for truncated 802.1Q header. */
+                       if (nla_len(encap)) {
+                               OVS_NLERR("Truncated 802.1Q header has non-zero encap attribute.\n");
+                               return -EINVAL;
+                       }
+               } else {
+                       OVS_NLERR("Encap attribute is set for a non-VLAN frame.\n");
+                       return  -EINVAL;
                }
        }
 
@@ -1617,25 +1678,36 @@ int ovs_match_from_nlattrs(struct sw_flow_match *match,
                if (err)
                        return err;
 
-               if ((mask_attrs & 1ULL << OVS_KEY_ATTR_ENCAP) && encap_valid) {
+               if (mask_attrs & 1ULL << OVS_KEY_ATTR_ENCAP)  {
                        __be16 eth_type = 0;
+                       __be16 tci = 0;
+
+                       if (!encap_valid) {
+                               OVS_NLERR("Encap mask attribute is set for non-VLAN frame.\n");
+                               return  -EINVAL;
+                       }
 
                        mask_attrs &= ~(1ULL << OVS_KEY_ATTR_ENCAP);
                        if (a[OVS_KEY_ATTR_ETHERTYPE])
                                eth_type = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]);
+
                        if (eth_type == htons(0xffff)) {
                                mask_attrs &= ~(1ULL << OVS_KEY_ATTR_ETHERTYPE);
                                encap = a[OVS_KEY_ATTR_ENCAP];
                                err = parse_flow_mask_nlattrs(encap, a, &mask_attrs);
                        } else {
-                               OVS_NLERR("VLAN frames must have an exact match"
-                                        " on the TPID (mask=%x).\n",
-                                        ntohs(eth_type));
-                               err = -EINVAL;
+                               OVS_NLERR("VLAN frames must have an exact match on the TPID (mask=%x).\n",
+                                               ntohs(eth_type));
+                               return -EINVAL;
                        }
 
-                       if (err)
-                               return err;
+                       if (a[OVS_KEY_ATTR_VLAN])
+                               tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]);
+
+                       if (!(tci & htons(VLAN_TAG_PRESENT))) {
+                               OVS_NLERR("VLAN tag present bit must have an exact match (tci_mask=%x).\n", ntohs(tci));
+                               return -EINVAL;
+                       }
                }
 
                err = ovs_key_from_nlattrs(match, mask_attrs, a, true);
@@ -1837,6 +1909,20 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey,
                                udp_key->udp_src = output->ipv6.tp.src;
                                udp_key->udp_dst = output->ipv6.tp.dst;
                        }
+               } else if (swkey->ip.proto == IPPROTO_SCTP) {
+                       struct ovs_key_sctp *sctp_key;
+
+                       nla = nla_reserve(skb, OVS_KEY_ATTR_SCTP, sizeof(*sctp_key));
+                       if (!nla)
+                               goto nla_put_failure;
+                       sctp_key = nla_data(nla);
+                       if (swkey->eth.type == htons(ETH_P_IP)) {
+                               sctp_key->sctp_src = swkey->ipv4.tp.src;
+                               sctp_key->sctp_dst = swkey->ipv4.tp.dst;
+                       } else if (swkey->eth.type == htons(ETH_P_IPV6)) {
+                               sctp_key->sctp_src = swkey->ipv6.tp.src;
+                               sctp_key->sctp_dst = swkey->ipv6.tp.dst;
+                       }
                } else if (swkey->eth.type == htons(ETH_P_IP) &&
                           swkey->ip.proto == IPPROTO_ICMP) {
                        struct ovs_key_icmp *icmp_key;
index d8277b5..e1d92e4 100644 (file)
@@ -101,8 +101,8 @@ struct sw_flow_key {
                        } addr;
                        union {
                                struct {
-                                       __be16 src;             /* TCP/UDP source port. */
-                                       __be16 dst;             /* TCP/UDP destination port. */
+                                       __be16 src;             /* TCP/UDP/SCTP source port. */
+                                       __be16 dst;             /* TCP/UDP/SCTP destination port. */
                                } tp;
                                struct {
                                        u8 sha[ETH_ALEN];       /* ARP source hardware address. */
@@ -117,8 +117,8 @@ struct sw_flow_key {
                        } addr;
                        __be32 label;                   /* IPv6 flow label. */
                        struct {
-                               __be16 src;             /* TCP/UDP source port. */
-                               __be16 dst;             /* TCP/UDP destination port. */
+                               __be16 src;             /* TCP/UDP/SCTP source port. */
+                               __be16 dst;             /* TCP/UDP/SCTP destination port. */
                        } tp;
                        struct {
                                struct in6_addr target; /* ND target address. */
@@ -245,7 +245,7 @@ int ovs_ipv4_tun_to_nlattr(struct sk_buff *skb,
                           const struct ovs_key_ipv4_tunnel *output);
 
 bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow,
-               const struct sw_flow_key *key, int key_len);
+               const struct sw_flow_key *key, int key_end);
 
 struct sw_flow_mask {
        int ref_count;
index 5f9c792..178cd5e 100644 (file)
@@ -57,6 +57,7 @@ openvswitch_headers += \
        linux/compat/include/linux/rcupdate.h \
        linux/compat/include/linux/reciprocal_div.h \
        linux/compat/include/linux/rtnetlink.h \
+       linux/compat/include/linux/sctp.h \
        linux/compat/include/linux/skbuff.h \
        linux/compat/include/linux/slab.h \
        linux/compat/include/linux/stddef.h \
@@ -81,4 +82,5 @@ openvswitch_headers += \
        linux/compat/include/net/route.h \
        linux/compat/include/net/sock.h \
        linux/compat/include/net/netns/generic.h \
-       linux/compat/include/net/vxlan.h
+       linux/compat/include/net/vxlan.h \
+       linux/compat/include/net/sctp/checksum.h
diff --git a/datapath/linux/compat/include/linux/sctp.h b/datapath/linux/compat/include/linux/sctp.h
new file mode 100644 (file)
index 0000000..b91c9c6
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef __LINUX_SCTP_WRAPPER_H
+#define __LINUX_SCTP_WRAPPER_H 1
+
+#include_next <linux/sctp.h>
+
+#ifndef HAVE_SKBUFF_HEADER_HELPERS
+static inline struct sctphdr *sctp_hdr(const struct sk_buff *skb)
+{
+       return (struct sctphdr *)skb_transport_header(skb);
+}
+#endif /* HAVE_SKBUFF_HEADER_HELPERS */
+
+#endif
index 7ab234a..71f4708 100644 (file)
@@ -5,6 +5,10 @@
 
 #include_next <net/ipv6.h>
 
+#ifndef NEXTHDR_SCTP
+#define NEXTHDR_SCTP    132 /* Stream Control Transport Protocol */
+#endif
+
 #if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0)
 #define ipv6_skip_exthdr rpl_ipv6_skip_exthdr
 extern int ipv6_skip_exthdr(const struct sk_buff *skb, int start,
diff --git a/datapath/linux/compat/include/net/sctp/checksum.h b/datapath/linux/compat/include/net/sctp/checksum.h
new file mode 100644 (file)
index 0000000..11fb0b6
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef __SCTP_CHECKSUM_WRAPPER_H
+#define __SCTP_CHECKSUM_WRAPPER_H 1
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+#include <net/sctp/sctp.h>
+#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25) */
+#include_next <net/sctp/checksum.h>
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,12,0)
+static inline __le32 sctp_compute_cksum(const struct sk_buff *skb,
+                                       unsigned int offset)
+{
+       const struct sk_buff *iter;
+
+       __u32 crc32 = sctp_start_cksum(skb->data + offset,
+                                      skb_headlen(skb) - offset);
+       skb_walk_frags(skb, iter)
+               crc32 = sctp_update_cksum((__u8 *) iter->data,
+                                         skb_headlen(iter), crc32);
+
+       /* Open-code sctp_end_cksum() to avoid a sparse warning due to a bug in
+        * sparse annotations in Linux fixed in 3.10 in commit eee1d5a14 (sctp:
+        * Correct type and usage of sctp_end_cksum()). */
+       return cpu_to_le32(~crc32);
+}
+#endif
+
+#endif
index 2f62d11..80e980a 100644 (file)
@@ -211,14 +211,13 @@ static void lisp_build_header(const struct vport *vport,
  *
  * @vport: port this packet was received on
  * @skb: received packet
- * @tos: ToS from encapsulating IP packet, used to copy ECN bits
+ * @tun_key: tunnel that carried packet
  *
  * Must be called with rcu_read_lock.
  *
  * Packets received by this function are in the following state:
  * - skb->data points to the inner Ethernet header.
  * - The inner Ethernet header is in the linear data area.
- * - skb->csum does not include the inner Ethernet header.
  * - The layer pointers are undefined.
  */
 static void ovs_tnl_rcv(struct vport *vport, struct sk_buff *skb,
index 03b775d..f26beaf 100644 (file)
@@ -206,7 +206,7 @@ out:
  *     ovs_vport_set_options - modify existing vport device (for kernel callers)
  *
  * @vport: vport to modify.
- * @port: New configuration.
+ * @options: New configuration.
  *
  * Modifies an existing device with the specified configuration (which is
  * dependent on device type).  ovs_mutex must be held.
@@ -352,6 +352,7 @@ int ovs_vport_get_options(const struct vport *vport, struct sk_buff *skb)
  *
  * @vport: vport that received the packet
  * @skb: skb that was received
+ * @tun_key: tunnel (if any) that carried packet
  *
  * Must be called with rcu_read_lock.  The packet cannot be shared and
  * skb->data should point to the Ethernet header.  The caller must have already
index a1a2c3c..3bcf332 100644 (file)
@@ -101,6 +101,10 @@ License:
   The full text of each license is also appended to the end of this
   file.
 
+* The following components are licensed for use as desired without restriction:
+
+       lib/crc32c.c
+
 * The following components are licensed under the
   Python Software Foundation License Version 2.
 
index a119b14..09c26b5 100644 (file)
@@ -282,6 +282,7 @@ enum ovs_key_attr {
        OVS_KEY_ATTR_ND,        /* struct ovs_key_nd */
        OVS_KEY_ATTR_SKB_MARK,  /* u32 skb mark */
        OVS_KEY_ATTR_TUNNEL,    /* Nested set of ovs_tunnel attributes */
+       OVS_KEY_ATTR_SCTP,      /* struct ovs_key_sctp */
 
 #ifdef __KERNEL__
        OVS_KEY_ATTR_IPV4_TUNNEL,  /* struct ovs_key_ipv4_tunnel */
@@ -364,6 +365,11 @@ struct ovs_key_udp {
        __be16 udp_dst;
 };
 
+struct ovs_key_sctp {
+       __be16 sctp_src;
+       __be16 sctp_dst;
+};
+
 struct ovs_key_icmp {
        __u8 icmp_type;
        __u8 icmp_code;
index 87d48d6..781358b 100644 (file)
@@ -59,6 +59,7 @@ extern const struct in6_addr in6addr_any;
 #define IPPROTO_ICMPV6 58
 #define IPPROTO_NONE 59
 #define IPPROTO_DSTOPTS 60
+#define IPPROTO_SCTP 132
 
 /* All the IP options documented in Linux ip(7). */
 #define IP_ADD_MEMBERSHIP 0
index 40c5ca3..e5b2a08 100644 (file)
@@ -30,8 +30,5 @@
 #undef PTHREAD_RWLOCK_INITIALIZER
 #define PTHREAD_RWLOCK_INITIALIZER {}
 
-#undef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
-#define PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP {}
-
 #undef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
 #define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP {}
index fa7f173..6843a6d 100644 (file)
@@ -33,6 +33,8 @@ lib_libopenvswitch_a_SOURCES = \
        lib/compiler.h \
        lib/coverage.c \
        lib/coverage.h \
+       lib/crc32c.c \
+       lib/crc32c.h \
        lib/csum.c \
        lib/csum.h \
        lib/daemon.c \
index 6f86f26..7da6fd9 100644 (file)
--- a/lib/bfd.c
+++ b/lib/bfd.c
@@ -28,6 +28,7 @@
 #include "hash.h"
 #include "hmap.h"
 #include "list.h"
+#include "netdev.h"
 #include "netlink.h"
 #include "odp-util.h"
 #include "ofpbuf.h"
@@ -151,6 +152,9 @@ struct bfd {
     bool cpath_down;              /* Concatenated Path Down. */
     uint8_t mult;                 /* bfd.DetectMult. */
 
+    struct netdev *netdev;
+    uint64_t rx_packets;          /* Packets received by 'netdev'. */
+
     enum state state;             /* bfd.SessionState. */
     enum state rmt_state;         /* bfd.RemoteSessionState. */
 
@@ -186,6 +190,21 @@ struct bfd {
 
     atomic_bool check_tnl_key;    /* Verify tunnel key of inbound packets? */
     atomic_int ref_cnt;
+
+    /* When forward_if_rx is true, bfd_forwarding() will return
+     * true as long as there are incoming packets received.
+     * Note, forwarding_override still has higher priority. */
+    bool forwarding_if_rx;
+    long long int forwarding_if_rx_detect_time;
+
+    /* BFD decay related variables. */
+    bool in_decay;                /* True when bfd is in decay. */
+    int decay_min_rx;             /* min_rx is set to decay_min_rx when */
+                                  /* in decay. */
+    int decay_rx_ctl;             /* Count bfd packets received within decay */
+                                  /* detect interval. */
+    uint64_t decay_rx_packets;    /* Packets received by 'netdev'. */
+    long long int decay_detect_time; /* Decay detection time. */
 };
 
 static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
@@ -208,6 +227,11 @@ static void bfd_set_state(struct bfd *, enum state, enum diag)
 static uint32_t generate_discriminator(void) OVS_REQUIRES(mutex);
 static void bfd_put_details(struct ds *, const struct bfd *)
     OVS_REQUIRES(mutex);
+static uint64_t bfd_rx_packets(const struct bfd *) OVS_REQUIRES(mutex);
+static void bfd_try_decay(struct bfd *) OVS_REQUIRES(mutex);
+static void bfd_decay_update(struct bfd *) OVS_REQUIRES(mutex);
+static void bfd_check_rx(struct bfd *) OVS_REQUIRES(mutex);
+static void bfd_forwarding_if_rx_update(struct bfd *) OVS_REQUIRES(mutex);
 static void bfd_unixctl_show(struct unixctl_conn *, int argc,
                              const char *argv[], void *aux OVS_UNUSED);
 static void bfd_unixctl_set_forwarding_override(struct unixctl_conn *,
@@ -255,15 +279,17 @@ bfd_get_status(const struct bfd *bfd, struct smap *smap)
  * handle for the session, or NULL if BFD is not enabled according to 'cfg'.
  * Also returns NULL if cfg is NULL. */
 struct bfd *
-bfd_configure(struct bfd *bfd, const char *name, const struct smap *cfg)
-    OVS_EXCLUDED(mutex)
+bfd_configure(struct bfd *bfd, const char *name, const struct smap *cfg,
+              struct netdev *netdev) OVS_EXCLUDED(mutex)
 {
     static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
     static atomic_uint16_t udp_src = ATOMIC_VAR_INIT(0);
 
+    int decay_min_rx;
     long long int min_tx, min_rx;
     bool need_poll = false;
-    bool cpath_down;
+    bool cfg_min_rx_changed = false;
+    bool cpath_down, forwarding_if_rx;
     const char *hwaddr;
     uint8_t ea[ETH_ADDR_LEN];
 
@@ -293,6 +319,9 @@ bfd_configure(struct bfd *bfd, const char *name, const struct smap *cfg)
         bfd->min_tx = 1000;
         bfd->mult = 3;
         atomic_init(&bfd->ref_cnt, 1);
+        bfd->netdev = netdev_ref(netdev);
+        bfd->rx_packets = bfd_rx_packets(bfd);
+        bfd->in_decay = false;
 
         /* RFC 5881 section 4
          * The source port MUST be in the range 49152 through 65535.  The same
@@ -328,6 +357,22 @@ bfd_configure(struct bfd *bfd, const char *name, const struct smap *cfg)
             || (!bfd_in_poll(bfd) && bfd->cfg_min_rx > bfd->min_rx)) {
             bfd->min_rx = bfd->cfg_min_rx;
         }
+        cfg_min_rx_changed = true;
+        need_poll = true;
+    }
+
+    decay_min_rx = smap_get_int(cfg, "decay_min_rx", 0);
+    if (bfd->decay_min_rx != decay_min_rx || cfg_min_rx_changed) {
+        if (decay_min_rx > 0 && decay_min_rx < bfd->cfg_min_rx) {
+            VLOG_WARN("%s: decay_min_rx cannot be less than %lld ms",
+                      bfd->name, bfd->cfg_min_rx);
+            bfd->decay_min_rx = 0;
+        } else {
+            bfd->decay_min_rx = decay_min_rx;
+        }
+        /* Resets decay. */
+        bfd->in_decay = false;
+        bfd_decay_update(bfd);
         need_poll = true;
     }
 
@@ -349,6 +394,16 @@ bfd_configure(struct bfd *bfd, const char *name, const struct smap *cfg)
         bfd->eth_dst_set = false;
     }
 
+    forwarding_if_rx = smap_get_bool(cfg, "forwarding_if_rx", false);
+    if (bfd->forwarding_if_rx != forwarding_if_rx) {
+        bfd->forwarding_if_rx = forwarding_if_rx;
+        if (bfd->state == STATE_UP && bfd->forwarding_if_rx) {
+            bfd_forwarding_if_rx_update(bfd);
+        } else {
+            bfd->forwarding_if_rx_detect_time = 0;
+        }
+    }
+
     if (need_poll) {
         bfd_poll(bfd);
     }
@@ -379,6 +434,7 @@ bfd_unref(struct bfd *bfd) OVS_EXCLUDED(mutex)
         if (orig == 1) {
             ovs_mutex_lock(&mutex);
             hmap_remove(all_bfds, &bfd->node);
+            netdev_close(bfd->netdev);
             free(bfd->name);
             free(bfd);
             ovs_mutex_unlock(&mutex);
@@ -404,12 +460,30 @@ bfd_wait(const struct bfd *bfd) OVS_EXCLUDED(mutex)
 void
 bfd_run(struct bfd *bfd) OVS_EXCLUDED(mutex)
 {
+    long long int now;
+    bool old_in_decay;
+
     ovs_mutex_lock(&mutex);
-    if (bfd->state > STATE_DOWN && time_msec() >= bfd->detect_time) {
+    now = time_msec();
+    old_in_decay = bfd->in_decay;
+
+    if (bfd->state > STATE_DOWN && now >= bfd->detect_time) {
         bfd_set_state(bfd, STATE_DOWN, DIAG_EXPIRED);
     }
 
-    if (bfd->min_tx != bfd->cfg_min_tx || bfd->min_rx != bfd->cfg_min_rx) {
+    /* Decay may only happen when state is STATE_UP, bfd->decay_min_rx is
+     * configured, and decay_detect_time is reached. */
+    if (bfd->state == STATE_UP && bfd->decay_min_rx > 0
+        && now >= bfd->decay_detect_time) {
+        bfd_try_decay(bfd);
+    }
+
+    /* Always checks the reception of any packet. */
+    bfd_check_rx(bfd);
+
+    if (bfd->min_tx != bfd->cfg_min_tx
+        || (bfd->min_rx != bfd->cfg_min_rx && bfd->min_rx != bfd->decay_min_rx)
+        || bfd->in_decay != old_in_decay) {
         bfd_poll(bfd);
     }
     ovs_mutex_unlock(&mutex);
@@ -502,10 +576,12 @@ bfd_put_packet(struct bfd *bfd, struct ofpbuf *p,
 }
 
 bool
-bfd_should_process_flow(const struct bfd *bfd, const struct flow *flow,
+bfd_should_process_flow(const struct bfd *bfd_, const struct flow *flow,
                         struct flow_wildcards *wc)
 {
+    struct bfd *bfd = CONST_CAST(struct bfd *, bfd_);
     bool check_tnl_key;
+
     memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst);
     if (bfd->eth_dst_set && memcmp(bfd->eth_dst, flow->dl_dst, ETH_ADDR_LEN)) {
         return false;
@@ -537,6 +613,9 @@ bfd_process_packet(struct bfd *bfd, const struct flow *flow,
     /* This function is designed to follow section RFC 5880 6.8.6 closely. */
 
     ovs_mutex_lock(&mutex);
+    /* Increments the decay rx counter. */
+    bfd->decay_rx_ctl++;
+
     if (flow->nw_ttl != 255) {
         /* XXX Should drop in the kernel to prevent DOS. */
         goto out;
@@ -686,18 +765,43 @@ bfd_process_packet(struct bfd *bfd, const struct flow *flow,
 out:
     ovs_mutex_unlock(&mutex);
 }
+
+/* Must be called when the netdev owned by 'bfd' should change. */
+void
+bfd_set_netdev(struct bfd *bfd, const struct netdev *netdev)
+    OVS_EXCLUDED(mutex)
+{
+    ovs_mutex_lock(&mutex);
+    if (bfd->netdev != netdev) {
+        netdev_close(bfd->netdev);
+        bfd->netdev = netdev_ref(netdev);
+        if (bfd->decay_min_rx && bfd->state == STATE_UP) {
+            bfd_decay_update(bfd);
+        }
+        if (bfd->forwarding_if_rx && bfd->state == STATE_UP) {
+            bfd_forwarding_if_rx_update(bfd);
+        }
+        bfd->rx_packets = bfd_rx_packets(bfd);
+    }
+    ovs_mutex_unlock(&mutex);
+}
+
 \f
 static bool
 bfd_forwarding__(const struct bfd *bfd) OVS_REQUIRES(mutex)
 {
+    long long int time;
+
     if (bfd->forwarding_override != -1) {
         return bfd->forwarding_override == 1;
     }
 
-    return bfd->state == STATE_UP
-        && bfd->rmt_diag != DIAG_PATH_DOWN
-        && bfd->rmt_diag != DIAG_CPATH_DOWN
-        && bfd->rmt_diag != DIAG_RCPATH_DOWN;
+    time = bfd->forwarding_if_rx_detect_time;
+    return (bfd->state == STATE_UP
+            || (bfd->forwarding_if_rx && time > time_msec()))
+           && bfd->rmt_diag != DIAG_PATH_DOWN
+           && bfd->rmt_diag != DIAG_CPATH_DOWN
+           && bfd->rmt_diag != DIAG_RCPATH_DOWN;
 }
 
 /* Helpers. */
@@ -713,7 +817,7 @@ bfd_poll(struct bfd *bfd) OVS_REQUIRES(mutex)
     if (bfd->state > STATE_DOWN && !bfd_in_poll(bfd)
         && !(bfd->flags & FLAG_FINAL)) {
         bfd->poll_min_tx = bfd->cfg_min_tx;
-        bfd->poll_min_rx = bfd->cfg_min_rx;
+        bfd->poll_min_rx = bfd->in_decay ? bfd->decay_min_rx : bfd->cfg_min_rx;
         bfd->flags |= FLAG_POLL;
         bfd->next_tx = 0;
         VLOG_INFO_RL(&rl, "%s: Initiating poll sequence", bfd->name);
@@ -886,10 +990,90 @@ bfd_set_state(struct bfd *bfd, enum state state, enum diag diag)
             bfd->rmt_flags = 0;
             bfd->rmt_disc = 0;
             bfd->rmt_min_tx = 0;
+            /* Resets the min_rx if in_decay. */
+            if (bfd->in_decay) {
+                bfd->min_rx = bfd->cfg_min_rx;
+                bfd->in_decay = false;
+            }
+        }
+        /* Resets the decay when state changes to STATE_UP
+         * and decay_min_rx is configured. */
+        if (bfd->state == STATE_UP && bfd->decay_min_rx) {
+            bfd_decay_update(bfd);
         }
     }
 }
 
+static uint64_t
+bfd_rx_packets(const struct bfd *bfd) OVS_REQUIRES(mutex)
+{
+    struct netdev_stats stats;
+
+    if (!netdev_get_stats(bfd->netdev, &stats)) {
+        return stats.rx_packets;
+    } else {
+        return 0;
+    }
+}
+
+/* Decays the bfd->min_rx to bfd->decay_min_rx when 'diff' is less than
+ * the 'expect' value. */
+static void
+bfd_try_decay(struct bfd *bfd) OVS_REQUIRES(mutex)
+{
+    int64_t diff, expect;
+
+    /* The 'diff' is the difference between current interface rx_packets
+     * stats and last-time check.  The 'expect' is the recorded number of
+     * bfd control packets received within an approximately decay_min_rx
+     * (2000 ms if decay_min_rx is less than 2000 ms) interval.
+     *
+     * Since the update of rx_packets stats at interface happens
+     * asynchronously to the bfd_rx_packets() function, the 'diff' value
+     * can be jittered.  Thusly, we double the decay_rx_ctl to provide
+     * more wiggle room. */
+    diff = bfd_rx_packets(bfd) - bfd->decay_rx_packets;
+    expect = 2 * MAX(bfd->decay_rx_ctl, 1);
+    bfd->in_decay = diff <= expect ? true : false;
+    bfd_decay_update(bfd);
+}
+
+/* Updates the rx_packets, decay_rx_ctl and decay_detect_time. */
+static void
+bfd_decay_update(struct bfd * bfd) OVS_REQUIRES(mutex)
+{
+    bfd->decay_rx_packets = bfd_rx_packets(bfd);
+    bfd->decay_rx_ctl = 0;
+    bfd->decay_detect_time = MAX(bfd->decay_min_rx, 2000) + time_msec();
+}
+
+/* Checks if there are packets received during the time since last call.
+ * If forwarding_if_rx is enabled and packets are received, updates the
+ * forwarding_if_rx_detect_time. */
+static void
+bfd_check_rx(struct bfd *bfd) OVS_REQUIRES(mutex)
+{
+    uint64_t rx_packets = bfd_rx_packets(bfd);
+    int64_t diff;
+
+    diff = rx_packets - bfd->rx_packets;
+    bfd->rx_packets = rx_packets;
+    if (diff < 0) {
+        VLOG_INFO_RL(&rl, "rx_packets count is smaller than last time.");
+    }
+    if (bfd->forwarding_if_rx && diff > 0) {
+        bfd_forwarding_if_rx_update(bfd);
+    }
+}
+
+/* Updates the forwarding_if_rx_detect_time. */
+static void
+bfd_forwarding_if_rx_update(struct bfd *bfd) OVS_REQUIRES(mutex)
+{
+    int64_t incr = bfd_rx_interval(bfd) * bfd->mult;
+    bfd->forwarding_if_rx_detect_time = MAX(incr, 2000) + time_msec();
+}
+
 static uint32_t
 generate_discriminator(void)
 {
index 67d012e..0e1e33d 100644 (file)
--- a/lib/bfd.h
+++ b/lib/bfd.h
@@ -24,6 +24,7 @@
 struct bfd;
 struct flow;
 struct flow_wildcards;
+struct netdev;
 struct ofpbuf;
 struct smap;
 
@@ -40,11 +41,13 @@ void bfd_process_packet(struct bfd *, const struct flow *,
                         const struct ofpbuf *);
 
 struct bfd *bfd_configure(struct bfd *, const char *name,
-                          const struct smap *smap);
+                          const struct smap *smap,
+                          struct netdev *netdev);
 struct bfd *bfd_ref(const struct bfd *);
 void bfd_unref(struct bfd *);
 
 bool bfd_forwarding(const struct bfd *);
 void bfd_get_status(const struct bfd *, struct smap *);
+void bfd_set_netdev(struct bfd *, const struct netdev *);
 
 #endif /* bfd.h */
index 3834774..5be1bae 100644 (file)
@@ -755,6 +755,7 @@ bond_shift_load(struct bond_entry *hash, struct bond_slave *to)
     to->tx_bytes += delta;
 
     /* Arrange for flows to be revalidated. */
+    hash->slave = to;
     bond->bond_revalidate = true;
 }
 
index 1eba6fe..f9be78b 100644 (file)
@@ -40,6 +40,16 @@ ovs_be64 htonll(uint64_t);
 uint64_t ntohll(ovs_be64);
 #endif
 
+#if defined(WORDS_BIGENDIAN)
+static inline uint32_t
+uint32_byteswap(uint32_t crc) {
+    return (((crc & 0x000000ff) << 24) |
+            ((crc & 0x0000ff00) <<  8) |
+            ((crc & 0x00ff0000) >>  8) |
+            ((crc & 0xff000000) >> 24));
+}
+#endif
+
 /* These macros may substitute for htons(), htonl(), and htonll() in contexts
  * where function calls are not allowed, such as case labels.  They should not
  * be used elsewhere because all of them evaluate their argument many times. */
index a238c67..297894e 100644 (file)
--- a/lib/cfm.c
+++ b/lib/cfm.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 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.
@@ -168,7 +168,7 @@ cfm_rx_packets(const struct cfm *cfm) OVS_REQUIRES(mutex)
 }
 
 static const uint8_t *
-cfm_ccm_addr(const struct cfm *cfm)
+cfm_ccm_addr(struct cfm *cfm)
 {
     bool extended;
     atomic_read(&cfm->extended, &extended);
@@ -648,9 +648,10 @@ cfm_set_netdev(struct cfm *cfm, const struct netdev *netdev)
 /* Returns true if 'cfm' should process packets from 'flow'.  Sets
  * fields in 'wc' that were used to make the determination. */
 bool
-cfm_should_process_flow(const struct cfm *cfm, const struct flow *flow,
+cfm_should_process_flow(const struct cfm *cfm_, const struct flow *flow,
                         struct flow_wildcards *wc)
 {
+    struct cfm *cfm = CONST_CAST(struct cfm *, cfm_);
     bool check_tnl_key;
 
     atomic_read(&cfm->check_tnl_key, &check_tnl_key);
@@ -839,8 +840,9 @@ cfm_get_health(const struct cfm *cfm) OVS_EXCLUDED(mutex)
  * 'cfm' is operationally down, or -1 if 'cfm' has no operational state
  * (because it isn't in extended mode). */
 int
-cfm_get_opup(const struct cfm *cfm) OVS_EXCLUDED(mutex)
+cfm_get_opup(const struct cfm *cfm_) OVS_EXCLUDED(mutex)
 {
+    struct cfm *cfm = CONST_CAST(struct cfm *, cfm_);
     bool extended;
     int opup;
 
@@ -879,7 +881,7 @@ cfm_find(const char *name) OVS_REQUIRES(mutex)
 }
 
 static void
-cfm_print_details(struct ds *ds, const struct cfm *cfm) OVS_REQUIRES(mutex)
+cfm_print_details(struct ds *ds, struct cfm *cfm) OVS_REQUIRES(mutex)
 {
     struct remote_mp *rmp;
     bool extended;
@@ -926,7 +928,7 @@ cfm_unixctl_show(struct unixctl_conn *conn, int argc, const char *argv[],
                  void *aux OVS_UNUSED) OVS_EXCLUDED(mutex)
 {
     struct ds ds = DS_EMPTY_INITIALIZER;
-    const struct cfm *cfm;
+    struct cfm *cfm;
 
     ovs_mutex_lock(&mutex);
     if (argc > 1) {
index 519b832..fb4d46c 100644 (file)
 #define OVS_EXCLUDED(...) __attribute__((locks_excluded(__VA_ARGS__)))
 #define OVS_ACQ_BEFORE(...) __attribute__((acquired_before(__VA_ARGS__)))
 #define OVS_ACQ_AFTER(...) __attribute__((acquired_after(__VA_ARGS__)))
+#define OVS_NO_THREAD_SAFETY_ANALYSIS \
+    __attribute__((no_thread_safety_analysis))
 #else  /* not Clang */
 #define OVS_LOCKABLE
 #define OVS_REQ_RDLOCK(...)
 #define OVS_RELEASES(...)
 #define OVS_ACQ_BEFORE(...)
 #define OVS_ACQ_AFTER(...)
+#define OVS_NO_THREAD_SAFETY_ANALYSIS
 #endif
 
 /* ISO C says that a C implementation may choose any integer type for an enum
index f152474..23e2997 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 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.
@@ -35,10 +35,24 @@ extern struct coverage_counter *__stop_coverage[];
 #define coverage_counters __start_coverage
 #define n_coverage_counters  (__stop_coverage - __start_coverage)
 #else  /* !USE_LINKER_SECTIONS */
-#define COVERAGE_COUNTER(NAME) COVERAGE_DEFINE__(NAME);
+#define COVERAGE_COUNTER(COUNTER)                                       \
+        DECLARE_EXTERN_PER_THREAD_DATA(unsigned int,                    \
+                                       counter_##COUNTER);              \
+        DEFINE_EXTERN_PER_THREAD_DATA(counter_##COUNTER, 0);            \
+        static unsigned int COUNTER##_count(void)                       \
+        {                                                               \
+            unsigned int *countp = counter_##COUNTER##_get();           \
+            unsigned int count = *countp;                               \
+            *countp = 0;                                                \
+            return count;                                               \
+        }                                                               \
+        extern struct coverage_counter counter_##COUNTER;               \
+        struct coverage_counter counter_##COUNTER                       \
+            = { #COUNTER, COUNTER##_count, 0 };
 #include "coverage.def"
 #undef COVERAGE_COUNTER
 
+extern struct coverage_counter *coverage_counters[];
 struct coverage_counter *coverage_counters[] = {
 #define COVERAGE_COUNTER(NAME) &counter_##NAME,
 #include "coverage.def"
@@ -47,7 +61,7 @@ struct coverage_counter *coverage_counters[] = {
 #define n_coverage_counters ARRAY_SIZE(coverage_counters)
 #endif  /* !USE_LINKER_SECTIONS */
 
-static unsigned int epoch;
+static struct ovs_mutex coverage_mutex = OVS_MUTEX_INITIALIZER;
 
 static void coverage_read(struct svec *);
 
@@ -73,8 +87,8 @@ coverage_init(void)
                              coverage_unixctl_show, NULL);
 }
 
-/* Sorts coverage counters in descending order by count, within equal counts
- * alphabetically by name. */
+/* Sorts coverage counters in descending order by total, within equal
+ * totals alphabetically by name. */
 static int
 compare_coverage_counters(const void *a_, const void *b_)
 {
@@ -82,8 +96,8 @@ compare_coverage_counters(const void *a_, const void *b_)
     const struct coverage_counter *const *bp = b_;
     const struct coverage_counter *a = *ap;
     const struct coverage_counter *b = *bp;
-    if (a->count != b->count) {
-        return a->count < b->count ? 1 : -1;
+    if (a->total != b->total) {
+        return a->total < b->total ? 1 : -1;
     } else {
         return strcmp(a->name, b->name);
     }
@@ -96,11 +110,13 @@ coverage_hash(void)
     uint32_t hash = 0;
     int n_groups, i;
 
-    /* Sort coverage counters into groups with equal counts. */
+    /* Sort coverage counters into groups with equal totals. */
     c = xmalloc(n_coverage_counters * sizeof *c);
+    ovs_mutex_lock(&coverage_mutex);
     for (i = 0; i < n_coverage_counters; i++) {
         c[i] = coverage_counters[i];
     }
+    ovs_mutex_unlock(&coverage_mutex);
     qsort(c, n_coverage_counters, sizeof *c, compare_coverage_counters);
 
     /* Hash the names in each group along with the rank. */
@@ -108,13 +124,13 @@ coverage_hash(void)
     for (i = 0; i < n_coverage_counters; ) {
         int j;
 
-        if (!c[i]->count) {
+        if (!c[i]->total) {
             break;
         }
         n_groups++;
         hash = hash_int(i, hash);
         for (j = i; j < n_coverage_counters; j++) {
-            if (c[j]->count != c[i]->count) {
+            if (c[j]->total != c[i]->total) {
                 break;
             }
             hash = hash_string(c[j]->name, hash);
@@ -170,7 +186,7 @@ coverage_log(void)
         uint32_t hash = coverage_hash();
         if (coverage_hit(hash)) {
             VLOG_INFO("Skipping details of duplicate event coverage for "
-                      "hash=%08"PRIx32" in epoch %u", hash, epoch);
+                      "hash=%08"PRIx32, hash);
         } else {
             struct svec lines;
             const char *line;
@@ -186,17 +202,11 @@ coverage_log(void)
     }
 }
 
-static void
-coverage_read_counter(struct svec *lines, const struct coverage_counter *c)
-{
-    svec_add_nocopy(lines, xasprintf("%-24s %5u / %9llu",
-                                     c->name, c->count, c->count + c->total));
-}
-
 /* Adds coverage counter information to 'lines'. */
 static void
 coverage_read(struct svec *lines)
 {
+    unsigned long long int *totals;
     size_t n_never_hit;
     uint32_t hash;
     size_t i;
@@ -204,37 +214,38 @@ coverage_read(struct svec *lines)
     hash = coverage_hash();
 
     n_never_hit = 0;
-    svec_add_nocopy(lines, xasprintf("Event coverage (epoch %u/entire run), "
-                                     "hash=%08"PRIx32":", epoch, hash));
+    svec_add_nocopy(lines,
+                    xasprintf("Event coverage, hash=%08"PRIx32":", hash));
+
+    totals = xmalloc(n_coverage_counters * sizeof *totals);
+    ovs_mutex_lock(&coverage_mutex);
     for (i = 0; i < n_coverage_counters; i++) {
-        struct coverage_counter *c = coverage_counters[i];
-        if (c->count) {
-            coverage_read_counter(lines, c);
-        }
+        totals[i] = coverage_counters[i]->total;
     }
+    ovs_mutex_unlock(&coverage_mutex);
+
     for (i = 0; i < n_coverage_counters; i++) {
-        struct coverage_counter *c = coverage_counters[i];
-        if (!c->count) {
-            if (c->total) {
-                coverage_read_counter(lines, c);
-            } else {
-                n_never_hit++;
-            }
+        if (totals[i]) {
+            svec_add_nocopy(lines, xasprintf("%-24s %9llu",
+                                             coverage_counters[i]->name,
+                                             totals[i]));
+        } else {
+            n_never_hit++;
         }
     }
     svec_add_nocopy(lines, xasprintf("%zu events never hit", n_never_hit));
+    free(totals);
 }
 
-/* Advances to the next epoch of coverage, resetting all the counters to 0. */
 void
 coverage_clear(void)
 {
     size_t i;
 
-    epoch++;
+    ovs_mutex_lock(&coverage_mutex);
     for (i = 0; i < n_coverage_counters; i++) {
         struct coverage_counter *c = coverage_counters[i];
-        c->total += c->count;
-        c->count = 0;
+        c->total += c->count();
     }
+    ovs_mutex_unlock(&coverage_mutex);
 }
index 968c489..3d1a115 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 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.
  * for traditional coverage instrumentation with e.g. "gcov", but it is still
  * a useful debugging tool. */
 
+#include "ovs-thread.h"
 #include "vlog.h"
 
 /* A coverage counter. */
 struct coverage_counter {
-    const char *name;           /* Textual name. */
-    unsigned int count;         /* Count within the current epoch. */
-    unsigned long long int total; /* Total count over all epochs. */
+    const char *const name;            /* Textual name. */
+    unsigned int (*const count)(void); /* Gets, zeros this thread's count. */
+    unsigned long long int total;      /* Total count. */
 };
 
 /* Defines COUNTER.  There must be exactly one such definition at file scope
  * within a program. */
 #if USE_LINKER_SECTIONS
 #define COVERAGE_DEFINE(COUNTER)                                        \
-        COVERAGE_DEFINE__(COUNTER);                                     \
+        DEFINE_STATIC_PER_THREAD_DATA(unsigned int,                     \
+                                      counter_##COUNTER, 0);            \
+        static unsigned int COUNTER##_count(void)                       \
+        {                                                               \
+            unsigned int *countp = counter_##COUNTER##_get();           \
+            unsigned int count = *countp;                               \
+            *countp = 0;                                                \
+            return count;                                               \
+        }                                                               \
+        static inline void COUNTER##_add(unsigned int n)                \
+        {                                                               \
+            *counter_##COUNTER##_get() += n;                            \
+        }                                                               \
+        extern struct coverage_counter counter_##COUNTER;               \
+        struct coverage_counter counter_##COUNTER                       \
+            = { #COUNTER, COUNTER##_count, 0 };                         \
         extern struct coverage_counter *counter_ptr_##COUNTER;          \
         struct coverage_counter *counter_ptr_##COUNTER                  \
             __attribute__((section("coverage"))) = &counter_##COUNTER
 #else
-#define COVERAGE_DEFINE(MODULE) \
-        extern struct coverage_counter counter_##MODULE
+#define COVERAGE_DEFINE(COUNTER)                                        \
+        DECLARE_EXTERN_PER_THREAD_DATA(unsigned int,                    \
+                                       counter_##COUNTER);              \
+        static inline void COUNTER##_add(unsigned int n)                \
+        {                                                               \
+            *counter_##COUNTER##_get() += n;                            \
+        }                                                               \
+        extern struct coverage_counter counter_##COUNTER
 #endif
 
 /* Adds 1 to COUNTER. */
-#define COVERAGE_INC(COUNTER) counter_##COUNTER.count++;
+#define COVERAGE_INC(COUNTER) COVERAGE_ADD(COUNTER, 1)
 
 /* Adds AMOUNT to COUNTER. */
-#define COVERAGE_ADD(COUNTER, AMOUNT) counter_##COUNTER.count += (AMOUNT);
+#define COVERAGE_ADD(COUNTER, AMOUNT) COUNTER##_add(AMOUNT)
 
 void coverage_init(void);
 void coverage_log(void);
@@ -61,7 +83,5 @@ void coverage_clear(void);
 
 /* Implementation detail. */
 #define COVERAGE_DEFINE__(COUNTER)                              \
-        extern struct coverage_counter counter_##COUNTER;       \
-        struct coverage_counter counter_##COUNTER = { #COUNTER, 0, 0 }
 
 #endif /* coverage.h */
diff --git a/lib/crc32c.c b/lib/crc32c.c
new file mode 100644 (file)
index 0000000..e8dd6ee
--- /dev/null
@@ -0,0 +1,159 @@
+/*-
+ *  COPYRIGHT (C) 1986 Gary S. Brown.  You may use this program, or
+ *  code or tables extracted from it, as desired without restriction.
+ */
+
+/*
+ *  First, the polynomial itself and its table of feedback terms.  The
+ *  polynomial is
+ *  X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
+ *
+ *  Note that we take it "backwards" and put the highest-order term in
+ *  the lowest-order bit.  The X^32 term is "implied"; the LSB is the
+ *  X^31 term, etc.  The X^0 term (usually shown as "+1") results in
+ *  the MSB being 1
+ *
+ *  Note that the usual hardware shift register implementation, which
+ *  is what we're using (we're merely optimizing it by doing eight-bit
+ *  chunks at a time) shifts bits into the lowest-order term.  In our
+ *  implementation, that means shifting towards the right.  Why do we
+ *  do it this way?  Because the calculated CRC must be transmitted in
+ *  order from highest-order term to lowest-order term.  UARTs transmit
+ *  characters in order from LSB to MSB.  By storing the CRC this way
+ *  we hand it to the UART in the order low-byte to high-byte; the UART
+ *  sends each low-bit to hight-bit; and the result is transmission bit
+ *  by bit from highest- to lowest-order term without requiring any bit
+ *  shuffling on our part.  Reception works similarly
+ *
+ *  The feedback terms table consists of 256, 32-bit entries.  Notes
+ *
+ *      The table can be generated at runtime if desired; code to do so
+ *      can be found in FreeBSD.  It might not be obvious, but the feedback
+ *      terms simply represent the results of eight shift/xor opera
+ *      tions for all combinations of data and CRC register values
+ *
+ *      The values must be right-shifted by eight bits by the "updcrc
+ *      logic; the shift must be unsigned (bring in zeroes).  On some
+ *      hardware you could probably optimize the shift in assembler by
+ *      using byte-swap instructions
+ *      polynomial $edb88320
+ *
+ *
+ * CRC32 code derived from work by Gary S. Brown.
+ */
+
+#include <config.h>
+#include "crc32c.h"
+#include "byte-order.h"
+
+/*****************************************************************/
+/*                                                               */
+/* CRC LOOKUP TABLE                                              */
+/* ================                                              */
+/* The following CRC lookup table was generated automagically    */
+/* by the Rocksoft^tm Model CRC Algorithm Table Generation       */
+/* Program V1.0 using the following model parameters:            */
+/*                                                               */
+/*    Width   : 4 bytes.                                         */
+/*    Poly    : 0x1EDC6F41L                                      */
+/*    Reverse : TRUE.                                            */
+/*                                                               */
+/* For more information on the Rocksoft^tm Model CRC Algorithm,  */
+/* see the document titled "A Painless Guide to CRC Error        */
+/* Detection Algorithms" by Ross Williams                        */
+/* (ross@guest.adelaide.edu.au.). This document is likely to be  */
+/* in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft".        */
+/*                                                               */
+/*****************************************************************/
+static const uint32_t crc32Table[256] = {
+    0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L,
+    0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL,
+    0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL,
+    0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L,
+    0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL,
+    0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L,
+    0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L,
+    0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL,
+    0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL,
+    0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L,
+    0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L,
+    0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL,
+    0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L,
+    0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL,
+    0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL,
+    0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L,
+    0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L,
+    0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L,
+    0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L,
+    0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L,
+    0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L,
+    0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L,
+    0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L,
+    0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L,
+    0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L,
+    0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L,
+    0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L,
+    0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L,
+    0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L,
+    0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L,
+    0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L,
+    0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L,
+    0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL,
+    0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L,
+    0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L,
+    0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL,
+    0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L,
+    0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL,
+    0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL,
+    0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L,
+    0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L,
+    0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL,
+    0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL,
+    0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L,
+    0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL,
+    0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L,
+    0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L,
+    0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL,
+    0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L,
+    0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL,
+    0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL,
+    0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L,
+    0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL,
+    0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L,
+    0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L,
+    0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL,
+    0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL,
+    0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L,
+    0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L,
+    0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL,
+    0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L,
+    0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL,
+    0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL,
+    0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L
+};
+
+/*
+ * Compute a CRC32c checksum as per the SCTP requirements in RFC4960. This
+ * includes beginning with a checksum of all ones, and returning the negated
+ * CRC. Unlike the RFC, we return the checksum in network byte-order.
+ */
+ovs_be32
+crc32c(const uint8_t *data, size_t size)
+{
+    uint32_t crc = 0xffffffffL;
+
+    while (size--) {
+        crc = crc32Table[(crc ^ *data++) & 0xff] ^ (crc >> 8);
+    }
+
+    /* The result of this CRC calculation provides us a value in the reverse
+     * byte-order as compared with our architecture. On big-endian systems,
+     * this is opposite to our return type. So, to return a big-endian
+     * value, we must swap the byte-order. */
+#if defined(WORDS_BIGENDIAN)
+    crc = uint32_byteswap(crc);
+#endif
+
+    /* Our value is in network byte-order. OVS_FORCE keeps sparse happy. */
+    return (OVS_FORCE ovs_be32) ~crc;
+}
diff --git a/lib/crc32c.h b/lib/crc32c.h
new file mode 100644 (file)
index 0000000..92c7d7f
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2012 The University of Waikato.
+ * Author: Joe Stringer
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CRC32C_H
+#define CRC32C_H 1
+
+#include "openvswitch/types.h"
+
+ovs_be32 crc32c(const uint8_t *data, size_t);
+
+#endif /* crc32c.h */
index 1b97410..ca5f496 100644 (file)
@@ -248,7 +248,7 @@ open_dpif(const struct dpif_linux_dp *dp, struct dpif **dpifp)
 
     dpif = xzalloc(sizeof *dpif);
     dpif->port_notifier = NULL;
-    ovs_mutex_init(&dpif->upcall_lock, PTHREAD_MUTEX_DEFAULT);
+    ovs_mutex_init(&dpif->upcall_lock);
     dpif->epoll_fd = -1;
 
     dpif_init(&dpif->dpif, &dpif_linux_class, dp->name,
@@ -655,10 +655,10 @@ dpif_linux_port_query_by_name(const struct dpif *dpif, const char *devname,
     return dpif_linux_port_query__(dpif, 0, devname, dpif_port);
 }
 
-static odp_port_t
+static uint32_t
 dpif_linux_get_max_ports(const struct dpif *dpif OVS_UNUSED)
 {
-    return u32_to_odp(MAX_PORTS);
+    return MAX_PORTS;
 }
 
 static uint32_t
index 07c1467..ee60dcd 100644 (file)
@@ -616,10 +616,10 @@ dpif_netdev_port_query_by_name(const struct dpif *dpif, const char *devname,
     return error;
 }
 
-static odp_port_t
+static uint32_t
 dpif_netdev_get_max_ports(const struct dpif *dpif OVS_UNUSED)
 {
-    return u32_to_odp(MAX_PORTS);
+    return MAX_PORTS;
 }
 
 static void
index 1609c12..f85a658 100644 (file)
@@ -146,7 +146,7 @@ struct dpif_class {
 
     /* Returns one greater than the largest port number accepted in flow
      * actions. */
-    odp_port_t (*get_max_ports)(const struct dpif *dpif);
+    uint32_t (*get_max_ports)(const struct dpif *dpif);
 
     /* Returns the Netlink PID value to supply in OVS_ACTION_ATTR_USERSPACE
      * actions as the OVS_USERSPACE_ATTR_PID attribute's value, for use in
index 1c1a524..bb95502 100644 (file)
@@ -635,7 +635,7 @@ dpif_port_query_by_name(const struct dpif *dpif, const char *devname,
 
 /* Returns one greater than the maximum port number accepted in flow
  * actions. */
-odp_port_t
+uint32_t
 dpif_get_max_ports(const struct dpif *dpif)
 {
     return dpif->dpif_class->get_max_ports(dpif);
index d028085..7a258c7 100644 (file)
@@ -415,7 +415,7 @@ int dpif_port_query_by_name(const struct dpif *, const char *devname,
                             struct dpif_port *);
 int dpif_port_get_name(struct dpif *, odp_port_t port_no,
                        char *name, size_t name_size);
-odp_port_t dpif_get_max_ports(const struct dpif *);
+uint32_t dpif_get_max_ports(const struct dpif *);
 uint32_t dpif_port_get_pid(const struct dpif *, odp_port_t port_no);
 
 struct dpif_port_dump {
index b4c5ee6..e980f4b 100644 (file)
@@ -77,7 +77,7 @@ fatal_signal_init(void)
         assert_single_threaded();
         inited = true;
 
-        ovs_mutex_init(&mutex, PTHREAD_MUTEX_RECURSIVE);
+        ovs_mutex_init_recursive(&mutex);
         xpipe_nonblocking(signal_fds);
 
         for (i = 0; i < ARRAY_SIZE(fatal_signals); i++) {
index 3e29aa1..ab49b80 100644 (file)
@@ -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)
 {
@@ -352,7 +369,7 @@ 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
@@ -430,6 +447,8 @@ flow_extract(struct ofpbuf *packet, uint32_t skb_priority, uint32_t pkt_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) {
@@ -450,6 +469,8 @@ flow_extract(struct ofpbuf *packet, uint32_t skb_priority, uint32_t pkt_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;
@@ -761,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)) {
@@ -773,7 +794,7 @@ 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;
         }
     }
@@ -999,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;
 
index 8164d9c..ecd850a 100644 (file)
@@ -101,8 +101,8 @@ struct flow {
     uint16_t mpls_depth;        /* Depth of MPLS stack. */
     ovs_be16 vlan_tci;          /* If 802.1Q, TCI | VLAN_CFI; otherwise 0. */
     ovs_be16 dl_type;           /* Ethernet frame type. */
-    ovs_be16 tp_src;            /* TCP/UDP source port. */
-    ovs_be16 tp_dst;            /* TCP/UDP destination port. */
+    ovs_be16 tp_src;            /* TCP/UDP/SCTP source port. */
+    ovs_be16 tp_dst;            /* TCP/UDP/SCTP destination port. */
     uint8_t dl_src[6];          /* Ethernet source address. */
     uint8_t dl_dst[6];          /* Ethernet destination address. */
     uint8_t nw_proto;           /* IP protocol or low 8 bits of ARP opcode. */
index 8269874..5421e2a 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, 2012 Nicira, Inc.
+/* Copyright (c) 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.
@@ -207,7 +207,7 @@ lacp_create(void) OVS_EXCLUDED(mutex)
     struct lacp *lacp;
 
     if (ovsthread_once_start(&once)) {
-        ovs_mutex_init(&mutex, PTHREAD_MUTEX_RECURSIVE);
+        ovs_mutex_init_recursive(&mutex);
         ovsthread_once_done(&once);
     }
 
index e97b0b1..c038d48 100644 (file)
@@ -868,6 +868,8 @@ match_format(const struct match *match, struct ds *s, unsigned int priority)
                     ds_put_cstr(s, "tcp,");
                 } else if (f->nw_proto == IPPROTO_UDP) {
                     ds_put_cstr(s, "udp,");
+                } else if (f->nw_proto == IPPROTO_SCTP) {
+                    ds_put_cstr(s, "sctp,");
                 } else {
                     ds_put_cstr(s, "ip,");
                     skip_proto = false;
@@ -884,6 +886,8 @@ match_format(const struct match *match, struct ds *s, unsigned int priority)
                     ds_put_cstr(s, "tcp6,");
                 } else if (f->nw_proto == IPPROTO_UDP) {
                     ds_put_cstr(s, "udp6,");
+                } else if (f->nw_proto == IPPROTO_SCTP) {
+                    ds_put_cstr(s, "sctp6,");
                 } else {
                     ds_put_cstr(s, "ipv6,");
                     skip_proto = false;
index ce061a3..902a21d 100644 (file)
@@ -499,6 +499,26 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_UDP_DST, "OXM_OF_UDP_DST",
     },
 
+    {
+        MFF_SCTP_SRC, "sctp_src", NULL,
+        MF_FIELD_SIZES(be16),
+        MFM_FULLY,
+        MFS_DECIMAL,
+        MFP_SCTP,
+        true,
+        OXM_OF_SCTP_SRC, "OXM_OF_SCTP_SRC",
+        OXM_OF_SCTP_SRC, "OXM_OF_SCTP_SRC",
+    }, {
+        MFF_SCTP_DST, "sctp_dst", NULL,
+        MF_FIELD_SIZES(be16),
+        MFM_FULLY,
+        MFS_DECIMAL,
+        MFP_SCTP,
+        true,
+        OXM_OF_SCTP_DST, "OXM_OF_SCTP_DST",
+        OXM_OF_SCTP_DST, "OXM_OF_SCTP_DST",
+    },
+
     {
         MFF_ICMPV4_TYPE, "icmp_type", NULL,
         MF_FIELD_SIZES(u8),
@@ -781,11 +801,13 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
 
     case MFF_TCP_SRC:
     case MFF_UDP_SRC:
+    case MFF_SCTP_SRC:
     case MFF_ICMPV4_TYPE:
     case MFF_ICMPV6_TYPE:
         return !wc->masks.tp_src;
     case MFF_TCP_DST:
     case MFF_UDP_DST:
+    case MFF_SCTP_DST:
     case MFF_ICMPV4_CODE:
     case MFF_ICMPV6_CODE:
         return !wc->masks.tp_dst;
@@ -866,6 +888,8 @@ mf_are_prereqs_ok(const struct mf_field *mf, const struct flow *flow)
         return is_ip_any(flow) && flow->nw_proto == IPPROTO_TCP;
     case MFP_UDP:
         return is_ip_any(flow) && flow->nw_proto == IPPROTO_UDP;
+    case MFP_SCTP:
+        return is_ip_any(flow) && flow->nw_proto == IPPROTO_SCTP;
     case MFP_ICMPV4:
         return is_icmpv4(flow);
     case MFP_ICMPV6:
@@ -932,6 +956,8 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value)
     case MFF_TCP_DST:
     case MFF_UDP_SRC:
     case MFF_UDP_DST:
+    case MFF_SCTP_SRC:
+    case MFF_SCTP_DST:
     case MFF_ICMPV4_TYPE:
     case MFF_ICMPV4_CODE:
     case MFF_ICMPV6_TYPE:
@@ -1142,11 +1168,13 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow,
 
     case MFF_TCP_SRC:
     case MFF_UDP_SRC:
+    case MFF_SCTP_SRC:
         value->be16 = flow->tp_src;
         break;
 
     case MFF_TCP_DST:
     case MFF_UDP_DST:
+    case MFF_SCTP_DST:
         value->be16 = flow->tp_dst;
         break;
 
@@ -1332,11 +1360,13 @@ mf_set_value(const struct mf_field *mf,
 
     case MFF_TCP_SRC:
     case MFF_UDP_SRC:
+    case MFF_SCTP_SRC:
         match_set_tp_src(match, value->be16);
         break;
 
     case MFF_TCP_DST:
     case MFF_UDP_DST:
+    case MFF_SCTP_DST:
         match_set_tp_dst(match, value->be16);
         break;
 
@@ -1524,11 +1554,13 @@ mf_set_flow_value(const struct mf_field *mf,
 
     case MFF_TCP_SRC:
     case MFF_UDP_SRC:
+    case MFF_SCTP_SRC:
         flow->tp_src = value->be16;
         break;
 
     case MFF_TCP_DST:
     case MFF_UDP_DST:
+    case MFF_SCTP_DST:
         flow->tp_dst = value->be16;
         break;
 
@@ -1727,6 +1759,7 @@ mf_set_wild(const struct mf_field *mf, struct match *match)
 
     case MFF_TCP_SRC:
     case MFF_UDP_SRC:
+    case MFF_SCTP_SRC:
     case MFF_ICMPV4_TYPE:
     case MFF_ICMPV6_TYPE:
         match->wc.masks.tp_src = htons(0);
@@ -1735,6 +1768,7 @@ mf_set_wild(const struct mf_field *mf, struct match *match)
 
     case MFF_TCP_DST:
     case MFF_UDP_DST:
+    case MFF_SCTP_DST:
     case MFF_ICMPV4_CODE:
     case MFF_ICMPV6_CODE:
         match->wc.masks.tp_dst = htons(0);
@@ -1901,11 +1935,13 @@ mf_set(const struct mf_field *mf,
 
     case MFF_TCP_SRC:
     case MFF_UDP_SRC:
+    case MFF_SCTP_SRC:
         match_set_tp_src_masked(match, value->be16, mask->be16);
         break;
 
     case MFF_TCP_DST:
     case MFF_UDP_DST:
+    case MFF_SCTP_DST:
         match_set_tp_dst_masked(match, value->be16, mask->be16);
         break;
 
@@ -1921,23 +1957,26 @@ mf_check__(const struct mf_subfield *sf, const struct flow *flow,
 {
     if (!sf->field) {
         VLOG_WARN_RL(&rl, "unknown %s field", type);
+        return OFPERR_OFPBAC_BAD_SET_TYPE;
     } else if (!sf->n_bits) {
         VLOG_WARN_RL(&rl, "zero bit %s field %s", type, sf->field->name);
+        return OFPERR_OFPBAC_BAD_SET_LEN;
     } else if (sf->ofs >= sf->field->n_bits) {
         VLOG_WARN_RL(&rl, "bit offset %d exceeds %d-bit width of %s field %s",
                      sf->ofs, sf->field->n_bits, type, sf->field->name);
+        return OFPERR_OFPBAC_BAD_SET_LEN;
     } else if (sf->ofs + sf->n_bits > sf->field->n_bits) {
         VLOG_WARN_RL(&rl, "bit offset %d and width %d exceeds %d-bit width "
                      "of %s field %s", sf->ofs, sf->n_bits,
                      sf->field->n_bits, type, sf->field->name);
+        return OFPERR_OFPBAC_BAD_SET_LEN;
     } else if (flow && !mf_are_prereqs_ok(sf->field, flow)) {
         VLOG_WARN_RL(&rl, "%s field %s lacks correct prerequisites",
                      type, sf->field->name);
+        return OFPERR_OFPBAC_MATCH_INCONSISTENT;
     } else {
         return 0;
     }
-
-    return OFPERR_OFPBAC_BAD_ARGUMENT;
 }
 
 /* Checks whether 'sf' is valid for reading a subfield out of 'flow'.  Returns
@@ -1959,7 +1998,7 @@ mf_check_dst(const struct mf_subfield *sf, const struct flow *flow)
     if (!error && !sf->field->writable) {
         VLOG_WARN_RL(&rl, "destination field %s is not writable",
                      sf->field->name);
-        return OFPERR_OFPBAC_BAD_ARGUMENT;
+        return OFPERR_OFPBAC_BAD_SET_ARGUMENT;
     }
     return error;
 }
@@ -2010,6 +2049,8 @@ mf_random_value(const struct mf_field *mf, union mf_value *value)
     case MFF_TCP_DST:
     case MFF_UDP_SRC:
     case MFF_UDP_DST:
+    case MFF_SCTP_SRC:
+    case MFF_SCTP_DST:
     case MFF_ICMPV4_TYPE:
     case MFF_ICMPV4_CODE:
     case MFF_ICMPV6_TYPE:
index 93b894d..969ca9e 100644 (file)
@@ -121,6 +121,9 @@ enum mf_field_id {
     MFF_UDP_SRC,                /* be16 (used for IPv4 or IPv6) */
     MFF_UDP_DST,                /* be16 (used for IPv4 or IPv6) */
 
+    MFF_SCTP_SRC,               /* be16 (used for IPv4 or IPv6) */
+    MFF_SCTP_DST,               /* be16 (used for IPv4 or IPv6) */
+
     MFF_ICMPV4_TYPE,            /* u8 */
     MFF_ICMPV4_CODE,            /* u8 */
 
@@ -190,6 +193,7 @@ enum mf_prereqs {
     /* L2+L3 requirements. */
     MFP_TCP,                    /* On IPv4 or IPv6. */
     MFP_UDP,                    /* On IPv4 or IPv6. */
+    MFP_SCTP,                   /* On IPv4 or IPv6. */
     MFP_ICMPV4,
     MFP_ICMPV6,
 
index 180ce7f..7c19483 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Gaetano Catalli.
+ * Copyright (c) 2011, 2013 Gaetano Catalli.
  * Copyright (c) 2013 YAMAMOTO Takashi.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -293,7 +293,7 @@ netdev_bsd_construct_system(struct netdev *netdev_)
         return error;
     }
 
-    ovs_mutex_init(&netdev->mutex, PTHREAD_MUTEX_NORMAL);
+    ovs_mutex_init(&netdev->mutex);
     netdev->change_seq = 1;
     netdev->tap_fd = -1;
     netdev->kernel_name = xstrdup(netdev_->name);
@@ -327,7 +327,7 @@ netdev_bsd_construct_tap(struct netdev *netdev_)
 
     /* Create a tap device by opening /dev/tap.  The TAPGIFNAME ioctl is used
      * to retrieve the name of the tap device. */
-    ovs_mutex_init(&netdev->mutex, PTHREAD_MUTEX_NORMAL);
+    ovs_mutex_init(&netdev->mutex);
     netdev->tap_fd = open("/dev/tap", O_RDWR);
     netdev->change_seq = 1;
     if (netdev->tap_fd < 0) {
index e17ef9d..ac26564 100644 (file)
@@ -271,7 +271,7 @@ netdev_dummy_construct(struct netdev *netdev_)
 
     atomic_add(&next_n, 1, &n);
 
-    ovs_mutex_init(&netdev->mutex, PTHREAD_MUTEX_NORMAL);
+    ovs_mutex_init(&netdev->mutex);
     ovs_mutex_lock(&netdev->mutex);
     netdev->hwaddr[0] = 0xaa;
     netdev->hwaddr[1] = 0x55;
index 2db56ac..80fa39b 100644 (file)
@@ -619,7 +619,7 @@ netdev_linux_alloc(void)
 static void
 netdev_linux_common_construct(struct netdev_linux *netdev)
 {
-    ovs_mutex_init(&netdev->mutex, PTHREAD_MUTEX_NORMAL);
+    ovs_mutex_init(&netdev->mutex);
     netdev->change_seq = 1;
 }
 
index 76aa148..0f5dd7a 100644 (file)
@@ -171,7 +171,7 @@ netdev_vport_construct(struct netdev *netdev_)
 {
     struct netdev_vport *netdev = netdev_vport_cast(netdev_);
 
-    ovs_mutex_init(&netdev->mutex, PTHREAD_MUTEX_NORMAL);
+    ovs_mutex_init(&netdev->mutex);
     netdev->change_seq = 1;
     eth_addr_random(netdev->etheraddr);
 
index c70105b..b1dfc08 100644 (file)
@@ -128,7 +128,9 @@ netdev_run(void)
 
     ovs_rwlock_rdlock(&netdev_class_rwlock);
     HMAP_FOR_EACH (rc, hmap_node, &netdev_classes) {
-        rc->class->run();
+        if (rc->class->run) {
+            rc->class->run();
+        }
     }
     ovs_rwlock_unlock(&netdev_class_rwlock);
 }
@@ -145,7 +147,9 @@ netdev_wait(void)
 
     ovs_rwlock_rdlock(&netdev_class_rwlock);
     HMAP_FOR_EACH (rc, hmap_node, &netdev_classes) {
-        rc->class->wait();
+        if (rc->class->wait) {
+            rc->class->wait();
+        }
     }
     ovs_rwlock_unlock(&netdev_class_rwlock);
 }
index 99bd4cc..9562d38 100644 (file)
@@ -1012,7 +1012,7 @@ struct nl_pool {
     int n;
 };
 
-static struct ovs_mutex pool_mutex = OVS_ADAPTIVE_MUTEX_INITIALIZER;
+static struct ovs_mutex pool_mutex = OVS_MUTEX_INITIALIZER;
 static struct nl_pool pools[MAX_LINKS] OVS_GUARDED_BY(pool_mutex);
 
 static int
index 09f7f54..3bb71e2 100644 (file)
@@ -535,6 +535,11 @@ nxm_put_ip(struct ofpbuf *b, const struct match *match,
                         flow->tp_src, match->wc.masks.tp_src);
             nxm_put_16m(b, oxm ? OXM_OF_UDP_DST : NXM_OF_UDP_DST,
                         flow->tp_dst, match->wc.masks.tp_dst);
+        } else if (flow->nw_proto == IPPROTO_SCTP) {
+            nxm_put_16m(b, OXM_OF_SCTP_SRC, flow->tp_src,
+                        match->wc.masks.tp_src);
+            nxm_put_16m(b, OXM_OF_SCTP_DST, flow->tp_dst,
+                        match->wc.masks.tp_dst);
         } else if (flow->nw_proto == icmp_proto) {
             if (match->wc.masks.tp_src) {
                 nxm_put_8(b, icmp_type, ntohs(flow->tp_src));
@@ -1168,19 +1173,19 @@ nxm_reg_load_from_openflow12_set_field(
 
     /* ofp12_action_set_field is padded to 64 bits by zero */
     if (oasf_len != ROUND_UP(sizeof(*oasf) + oxm_length, 8)) {
-        return OFPERR_OFPBAC_BAD_ARGUMENT;
+        return OFPERR_OFPBAC_BAD_SET_LEN;
     }
     if (!is_all_zeros((const uint8_t *)(oasf) + sizeof *oasf + oxm_length,
                       oasf_len - oxm_length - sizeof *oasf)) {
-        return OFPERR_OFPBAC_BAD_ARGUMENT;
+        return OFPERR_OFPBAC_BAD_SET_ARGUMENT;
     }
 
     if (NXM_HASMASK(oxm_header)) {
-        return OFPERR_OFPBAC_BAD_ARGUMENT;
+        return OFPERR_OFPBAC_BAD_SET_TYPE;
     }
     mf = mf_from_nxm_header(oxm_header);
     if (!mf) {
-        return OFPERR_OFPBAC_BAD_ARGUMENT;
+        return OFPERR_OFPBAC_BAD_SET_TYPE;
     }
     load = ofpact_put_REG_LOAD(ofpacts);
     ofpact_set_field_init(load, mf, oasf + 1);
index d505c60..0f03855 100644 (file)
@@ -54,6 +54,7 @@ odp_execute_set_action(struct ofpbuf *packet, const struct nlattr *a,
     const struct ovs_key_ipv6 *ipv6_key;
     const struct ovs_key_tcp *tcp_key;
     const struct ovs_key_udp *udp_key;
+    const struct ovs_key_sctp *sctp_key;
 
     switch (type) {
     case OVS_KEY_ATTR_PRIORITY:
@@ -96,6 +97,11 @@ odp_execute_set_action(struct ofpbuf *packet, const struct nlattr *a,
         packet_set_udp_port(packet, udp_key->udp_src, udp_key->udp_dst);
         break;
 
+    case OVS_KEY_ATTR_SCTP:
+        sctp_key = nl_attr_get_unspec(a, sizeof(struct ovs_key_sctp));
+        packet_set_sctp_port(packet, sctp_key->sctp_src, sctp_key->sctp_dst);
+        break;
+
     case OVS_KEY_ATTR_MPLS:
          set_mpls_lse(packet, nl_attr_get_be32(a));
          break;
index a09042e..15ad2be 100644 (file)
@@ -109,6 +109,7 @@ ovs_key_attr_to_string(enum ovs_key_attr attr, char *namebuf, size_t bufsize)
     case OVS_KEY_ATTR_IPV6: return "ipv6";
     case OVS_KEY_ATTR_TCP: return "tcp";
     case OVS_KEY_ATTR_UDP: return "udp";
+    case OVS_KEY_ATTR_SCTP: return "sctp";
     case OVS_KEY_ATTR_ICMP: return "icmp";
     case OVS_KEY_ATTR_ICMPV6: return "icmpv6";
     case OVS_KEY_ATTR_ARP: return "arp";
@@ -746,6 +747,7 @@ odp_flow_key_attr_len(uint16_t type)
     case OVS_KEY_ATTR_IPV6: return sizeof(struct ovs_key_ipv6);
     case OVS_KEY_ATTR_TCP: return sizeof(struct ovs_key_tcp);
     case OVS_KEY_ATTR_UDP: return sizeof(struct ovs_key_udp);
+    case OVS_KEY_ATTR_SCTP: return sizeof(struct ovs_key_sctp);
     case OVS_KEY_ATTR_ICMP: return sizeof(struct ovs_key_icmp);
     case OVS_KEY_ATTR_ICMPV6: return sizeof(struct ovs_key_icmpv6);
     case OVS_KEY_ATTR_ARP: return sizeof(struct ovs_key_arp);
@@ -1206,6 +1208,23 @@ format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
         }
         break;
 
+    case OVS_KEY_ATTR_SCTP:
+        if (ma) {
+            const struct ovs_key_sctp *sctp_mask = nl_attr_get(ma);
+            const struct ovs_key_sctp *sctp_key = nl_attr_get(a);
+
+            ds_put_format(ds, "src=%"PRIu16"/%#"PRIx16
+                          ",dst=%"PRIu16"/%#"PRIx16,
+                          ntohs(sctp_key->sctp_src), ntohs(sctp_mask->sctp_src),
+                          ntohs(sctp_key->sctp_dst), ntohs(sctp_mask->sctp_dst));
+        } else {
+            const struct ovs_key_sctp *sctp_key = nl_attr_get(a);
+
+            ds_put_format(ds, "(src=%"PRIu16",dst=%"PRIu16")",
+                          ntohs(sctp_key->sctp_src), ntohs(sctp_key->sctp_dst));
+        }
+        break;
+
     case OVS_KEY_ATTR_ICMP:
         if (!is_exact) {
             const struct ovs_key_icmp *icmp_mask = nl_attr_get(ma);
@@ -2028,6 +2047,45 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
         }
     }
 
+    {
+        int sctp_src;
+        int sctp_dst;
+        int sctp_src_mask;
+        int sctp_dst_mask;
+        int n = -1;
+
+        if (mask && sscanf(s, "sctp(src=%i/%i,dst=%i/%i)%n",
+                   &sctp_src, &sctp_src_mask,
+                   &sctp_dst, &sctp_dst_mask, &n) > 0 && n > 0) {
+            struct ovs_key_sctp sctp_key;
+            struct ovs_key_sctp sctp_mask;
+
+            sctp_key.sctp_src = htons(sctp_src);
+            sctp_key.sctp_dst = htons(sctp_dst);
+            nl_msg_put_unspec(key, OVS_KEY_ATTR_SCTP, &sctp_key, sizeof sctp_key);
+
+            sctp_mask.sctp_src = htons(sctp_src_mask);
+            sctp_mask.sctp_dst = htons(sctp_dst_mask);
+            nl_msg_put_unspec(mask, OVS_KEY_ATTR_SCTP,
+                              &sctp_mask, sizeof sctp_mask);
+            return n;
+        }
+        if (sscanf(s, "sctp(src=%i,dst=%i)%n", &sctp_src, &sctp_dst, &n) > 0
+            && n > 0) {
+            struct ovs_key_sctp sctp_key;
+
+            sctp_key.sctp_src = htons(sctp_src);
+            sctp_key.sctp_dst = htons(sctp_dst);
+            nl_msg_put_unspec(key, OVS_KEY_ATTR_SCTP, &sctp_key, sizeof sctp_key);
+
+            if (mask) {
+                memset(&sctp_key, 0xff, sizeof sctp_key);
+                nl_msg_put_unspec(mask, OVS_KEY_ATTR_SCTP, &sctp_key, sizeof sctp_key);
+            }
+            return n;
+        }
+    }
+
     {
         int icmp_type;
         int icmp_code;
@@ -2471,6 +2529,13 @@ odp_flow_key_from_flow__(struct ofpbuf *buf, const struct flow *data,
                                                sizeof *udp_key);
             udp_key->udp_src = data->tp_src;
             udp_key->udp_dst = data->tp_dst;
+        } else if (flow->nw_proto == IPPROTO_SCTP) {
+            struct ovs_key_sctp *sctp_key;
+
+            sctp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_SCTP,
+                                               sizeof *sctp_key);
+            sctp_key->sctp_src = data->tp_src;
+            sctp_key->sctp_dst = data->tp_dst;
         } else if (flow->dl_type == htons(ETH_TYPE_IP)
                 && flow->nw_proto == IPPROTO_ICMP) {
             struct ovs_key_icmp *icmp_key;
@@ -2680,20 +2745,31 @@ check_expectations(uint64_t present_attrs, int out_of_range_attr,
 static bool
 parse_ethertype(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
                 uint64_t present_attrs, uint64_t *expected_attrs,
-                struct flow *flow)
+                struct flow *flow, const struct flow *src_flow)
 {
     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+    bool is_mask = flow != src_flow;
 
     if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ETHERTYPE)) {
         flow->dl_type = nl_attr_get_be16(attrs[OVS_KEY_ATTR_ETHERTYPE]);
-        if (ntohs(flow->dl_type) < 1536) {
+        if (!is_mask && ntohs(flow->dl_type) < ETH_TYPE_MIN) {
             VLOG_ERR_RL(&rl, "invalid Ethertype %"PRIu16" in flow key",
                         ntohs(flow->dl_type));
             return false;
         }
+        if (is_mask && ntohs(src_flow->dl_type) < ETH_TYPE_MIN &&
+            flow->dl_type != htons(0xffff)) {
+            return false;
+        }
         *expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERTYPE;
     } else {
-        flow->dl_type = htons(FLOW_DL_TYPE_NONE);
+        if (!is_mask) {
+            flow->dl_type = htons(FLOW_DL_TYPE_NONE);
+        } else if (ntohs(src_flow->dl_type) < ETH_TYPE_MIN) {
+            /* See comments in odp_flow_key_from_flow__(). */
+            VLOG_ERR_RL(&rl, "mask expected for non-Ethernet II frame");
+            return false;
+        }
     }
     return true;
 }
@@ -2702,20 +2778,41 @@ static enum odp_key_fitness
 parse_l2_5_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
                   uint64_t present_attrs, int out_of_range_attr,
                   uint64_t expected_attrs, struct flow *flow,
-                  const struct nlattr *key, size_t key_len)
+                  const struct nlattr *key, size_t key_len,
+                 const struct flow *src_flow)
 {
     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-
-    if (eth_type_mpls(flow->dl_type)) {
-        expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_MPLS);
-
-        if (!(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_MPLS))) {
-            return ODP_FIT_TOO_LITTLE;
+    bool is_mask = src_flow != flow;
+    const void *check_start = NULL;
+    size_t check_len = 0;
+    enum ovs_key_attr expected_bit = 0xff;
+
+    if (eth_type_mpls(src_flow->dl_type)) {
+       if (!is_mask) {
+           expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_MPLS);
+
+           if (!(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_MPLS))) {
+               return ODP_FIT_TOO_LITTLE;
+           }
+           flow->mpls_lse = nl_attr_get_be32(attrs[OVS_KEY_ATTR_MPLS]);
+           flow->mpls_depth++;
+        } else if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_MPLS)) {
+            flow->mpls_lse = nl_attr_get_be32(attrs[OVS_KEY_ATTR_MPLS]);
+
+            if (flow->mpls_lse != 0 && flow->dl_type != htons(0xffff)) {
+                return ODP_FIT_ERROR;
+            }
+            expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_MPLS);
+            if (flow->mpls_lse) {
+                /* XXX Is this needed? */
+                flow->mpls_depth = 0xffff;
+            }
+        }
+        goto done;
+    } else if (src_flow->dl_type == htons(ETH_TYPE_IP)) {
+        if (!is_mask) {
+            expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IPV4;
         }
-        flow->mpls_lse = nl_attr_get_be32(attrs[OVS_KEY_ATTR_MPLS]);
-        flow->mpls_depth++;
-    } else if (flow->dl_type == htons(ETH_TYPE_IP)) {
-        expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IPV4;
         if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV4)) {
             const struct ovs_key_ipv4 *ipv4_key;
 
@@ -2725,12 +2822,19 @@ parse_l2_5_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
             flow->nw_proto = ipv4_key->ipv4_proto;
             flow->nw_tos = ipv4_key->ipv4_tos;
             flow->nw_ttl = ipv4_key->ipv4_ttl;
-            if (!odp_to_ovs_frag(ipv4_key->ipv4_frag, flow)) {
+            if (is_mask) {
+               flow->nw_frag = ipv4_key->ipv4_frag;
+                check_start = ipv4_key;
+                check_len = sizeof *ipv4_key;
+                expected_bit = OVS_KEY_ATTR_IPV4;
+            } else if (!odp_to_ovs_frag(ipv4_key->ipv4_frag, flow)) {
                 return ODP_FIT_ERROR;
             }
         }
-    } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
-        expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IPV6;
+    } else if (src_flow->dl_type == htons(ETH_TYPE_IPV6)) {
+        if (!is_mask) {
+            expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IPV6;
+        }
         if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV6)) {
             const struct ovs_key_ipv6 *ipv6_key;
 
@@ -2741,20 +2845,27 @@ parse_l2_5_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
             flow->nw_proto = ipv6_key->ipv6_proto;
             flow->nw_tos = ipv6_key->ipv6_tclass;
             flow->nw_ttl = ipv6_key->ipv6_hlimit;
-            if (!odp_to_ovs_frag(ipv6_key->ipv6_frag, flow)) {
+            if (is_mask) {
+               flow->nw_frag = ipv6_key->ipv6_frag;
+                check_start = ipv6_key;
+                check_len = sizeof *ipv6_key;
+                expected_bit = OVS_KEY_ATTR_IPV6;
+            } else if (!odp_to_ovs_frag(ipv6_key->ipv6_frag, flow)) {
                 return ODP_FIT_ERROR;
             }
         }
-    } else if (flow->dl_type == htons(ETH_TYPE_ARP) ||
-               flow->dl_type == htons(ETH_TYPE_RARP)) {
-        expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ARP;
+    } else if (src_flow->dl_type == htons(ETH_TYPE_ARP) ||
+               src_flow->dl_type == htons(ETH_TYPE_RARP)) {
+        if (!is_mask) {
+            expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ARP;
+        }
         if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ARP)) {
             const struct ovs_key_arp *arp_key;
 
             arp_key = nl_attr_get(attrs[OVS_KEY_ATTR_ARP]);
             flow->nw_src = arp_key->arp_sip;
             flow->nw_dst = arp_key->arp_tip;
-            if (arp_key->arp_op & htons(0xff00)) {
+            if (!is_mask && (arp_key->arp_op & htons(0xff00))) {
                 VLOG_ERR_RL(&rl, "unsupported ARP opcode %"PRIu16" in flow "
                             "key", ntohs(arp_key->arp_op));
                 return ODP_FIT_ERROR;
@@ -2762,58 +2873,103 @@ parse_l2_5_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
             flow->nw_proto = ntohs(arp_key->arp_op);
             memcpy(flow->arp_sha, arp_key->arp_sha, ETH_ADDR_LEN);
             memcpy(flow->arp_tha, arp_key->arp_tha, ETH_ADDR_LEN);
+
+            if (is_mask) {
+                check_start = arp_key;
+                check_len = sizeof *arp_key;
+                expected_bit = OVS_KEY_ATTR_ARP;
+            }
+        }
+    } else {
+        goto done;
+    }
+    if (is_mask) {
+        if (!is_all_zeros(check_start, check_len) &&
+            flow->dl_type != htons(0xffff)) {
+            return ODP_FIT_ERROR;
+        } else {
+            expected_attrs |= UINT64_C(1) << expected_bit;
         }
     }
 
-    if (flow->nw_proto == IPPROTO_TCP
-        && (flow->dl_type == htons(ETH_TYPE_IP) ||
-            flow->dl_type == htons(ETH_TYPE_IPV6))
-        && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
-        expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TCP;
+    expected_bit = OVS_KEY_ATTR_UNSPEC;
+    if (src_flow->nw_proto == IPPROTO_TCP
+        && (src_flow->dl_type == htons(ETH_TYPE_IP) ||
+            src_flow->dl_type == htons(ETH_TYPE_IPV6))
+        && !(src_flow->nw_frag & FLOW_NW_FRAG_LATER)) {
+        if (!is_mask) {
+            expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TCP;
+        }
         if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TCP)) {
             const struct ovs_key_tcp *tcp_key;
 
             tcp_key = nl_attr_get(attrs[OVS_KEY_ATTR_TCP]);
             flow->tp_src = tcp_key->tcp_src;
             flow->tp_dst = tcp_key->tcp_dst;
+            expected_bit = OVS_KEY_ATTR_TCP;
+        }
+    } else if (src_flow->nw_proto == IPPROTO_UDP
+               && (src_flow->dl_type == htons(ETH_TYPE_IP) ||
+                   src_flow->dl_type == htons(ETH_TYPE_IPV6))
+               && !(src_flow->nw_frag & FLOW_NW_FRAG_LATER)) {
+        if (!is_mask) {
+            expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_UDP;
         }
-    } else if (flow->nw_proto == IPPROTO_UDP
-               && (flow->dl_type == htons(ETH_TYPE_IP) ||
-                   flow->dl_type == htons(ETH_TYPE_IPV6))
-               && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
-        expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_UDP;
         if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_UDP)) {
             const struct ovs_key_udp *udp_key;
 
             udp_key = nl_attr_get(attrs[OVS_KEY_ATTR_UDP]);
             flow->tp_src = udp_key->udp_src;
             flow->tp_dst = udp_key->udp_dst;
+            expected_bit = OVS_KEY_ATTR_UDP;
         }
-    } else if (flow->nw_proto == IPPROTO_ICMP
-               && flow->dl_type == htons(ETH_TYPE_IP)
+    } else if (flow->nw_proto == IPPROTO_SCTP
+               && (flow->dl_type == htons(ETH_TYPE_IP) ||
+                   flow->dl_type == htons(ETH_TYPE_IPV6))
                && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
-        expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ICMP;
+        if (!is_mask) {
+            expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_SCTP;
+        }
+        if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_SCTP)) {
+            const struct ovs_key_sctp *sctp_key;
+
+            sctp_key = nl_attr_get(attrs[OVS_KEY_ATTR_SCTP]);
+            flow->tp_src = sctp_key->sctp_src;
+            flow->tp_dst = sctp_key->sctp_dst;
+            expected_bit = OVS_KEY_ATTR_SCTP;
+        }
+    } else if (src_flow->nw_proto == IPPROTO_ICMP
+               && src_flow->dl_type == htons(ETH_TYPE_IP)
+               && !(src_flow->nw_frag & FLOW_NW_FRAG_LATER)) {
+        if (!is_mask) {
+            expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ICMP;
+        }
         if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ICMP)) {
             const struct ovs_key_icmp *icmp_key;
 
             icmp_key = nl_attr_get(attrs[OVS_KEY_ATTR_ICMP]);
             flow->tp_src = htons(icmp_key->icmp_type);
             flow->tp_dst = htons(icmp_key->icmp_code);
+            expected_bit = OVS_KEY_ATTR_ICMP;
+        }
+    } else if (src_flow->nw_proto == IPPROTO_ICMPV6
+               && src_flow->dl_type == htons(ETH_TYPE_IPV6)
+               && !(src_flow->nw_frag & FLOW_NW_FRAG_LATER)) {
+        if (!is_mask) {
+            expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ICMPV6;
         }
-    } else if (flow->nw_proto == IPPROTO_ICMPV6
-               && flow->dl_type == htons(ETH_TYPE_IPV6)
-               && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
-        expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ICMPV6;
         if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ICMPV6)) {
             const struct ovs_key_icmpv6 *icmpv6_key;
 
             icmpv6_key = nl_attr_get(attrs[OVS_KEY_ATTR_ICMPV6]);
             flow->tp_src = htons(icmpv6_key->icmpv6_type);
             flow->tp_dst = htons(icmpv6_key->icmpv6_code);
-
-            if (flow->tp_src == htons(ND_NEIGHBOR_SOLICIT) ||
-                flow->tp_src == htons(ND_NEIGHBOR_ADVERT)) {
-                expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ND;
+            expected_bit = OVS_KEY_ATTR_ICMPV6;
+            if (src_flow->tp_src == htons(ND_NEIGHBOR_SOLICIT) ||
+                src_flow->tp_src == htons(ND_NEIGHBOR_ADVERT)) {
+                if (!is_mask) {
+                    expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ND;
+                }
                 if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ND)) {
                     const struct ovs_key_nd *nd_key;
 
@@ -2822,11 +2978,28 @@ parse_l2_5_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
                            sizeof flow->nd_target);
                     memcpy(flow->arp_sha, nd_key->nd_sll, ETH_ADDR_LEN);
                     memcpy(flow->arp_tha, nd_key->nd_tll, ETH_ADDR_LEN);
+                    if (is_mask) {
+                        if (!is_all_zeros((const uint8_t *) nd_key,
+                                          sizeof *nd_key) &&
+                            flow->tp_src != htons(0xffff)) {
+                            return ODP_FIT_ERROR;
+                        } else {
+                            expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ND;
+                        }
+                    }
                 }
             }
         }
     }
+    if (is_mask && expected_bit != OVS_KEY_ATTR_UNSPEC) {
+        if ((flow->tp_src || flow->tp_dst) && flow->nw_proto != 0xff) {
+            return ODP_FIT_ERROR;
+        } else {
+            expected_attrs |= UINT64_C(1) << expected_bit;
+        }
+    }
 
+done:
     return check_expectations(present_attrs, out_of_range_attr, expected_attrs,
                               key, key_len);
 }
@@ -2836,9 +3009,11 @@ static enum odp_key_fitness
 parse_8021q_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
                    uint64_t present_attrs, int out_of_range_attr,
                    uint64_t expected_attrs, struct flow *flow,
-                   const struct nlattr *key, size_t key_len)
+                   const struct nlattr *key, size_t key_len,
+                   const struct flow *src_flow)
 {
     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+    bool is_mask = src_flow != flow;
 
     const struct nlattr *encap
         = (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ENCAP)
@@ -2848,33 +3023,47 @@ parse_8021q_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
     ovs_be16 tci;
 
     /* Calculate fitness of outer attributes. */
-    expected_attrs |= ((UINT64_C(1) << OVS_KEY_ATTR_VLAN) |
-                       (UINT64_C(1) << OVS_KEY_ATTR_ENCAP));
+    if (!is_mask) {
+        expected_attrs |= ((UINT64_C(1) << OVS_KEY_ATTR_VLAN) |
+                          (UINT64_C(1) << OVS_KEY_ATTR_ENCAP));
+    } else {
+        if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN)) {
+            expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_VLAN);
+        }
+        if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ENCAP)) {
+            expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_ENCAP);
+        }
+    }
     fitness = check_expectations(present_attrs, out_of_range_attr,
                                  expected_attrs, key, key_len);
 
     /* Get the VLAN TCI value. */
-    if (!(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN))) {
+    if (!is_mask && !(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN))) {
         return ODP_FIT_TOO_LITTLE;
-    }
-    tci = nl_attr_get_be16(attrs[OVS_KEY_ATTR_VLAN]);
-    if (tci == htons(0)) {
-        /* Corner case for a truncated 802.1Q header. */
-        if (fitness == ODP_FIT_PERFECT && nl_attr_get_size(encap)) {
-            return ODP_FIT_TOO_MUCH;
-        }
+    } else {
+        tci = nl_attr_get_be16(attrs[OVS_KEY_ATTR_VLAN]);
+        if (!is_mask) {
+           if (tci == htons(0)) {
+                /* Corner case for a truncated 802.1Q header. */
+               if (fitness == ODP_FIT_PERFECT && nl_attr_get_size(encap)) {
+                   return ODP_FIT_TOO_MUCH;
+               }
+               return fitness;
+           } else if (!(tci & htons(VLAN_CFI))) {
+               VLOG_ERR_RL(&rl, "OVS_KEY_ATTR_VLAN 0x%04"PRIx16" is nonzero "
+                           "but CFI bit is not set", ntohs(tci));
+               return ODP_FIT_ERROR;
+           }
+        }
+        /* Set vlan_tci.
+         * Remove the TPID from dl_type since it's not the real Ethertype.  */
+        flow->dl_type = htons(0);
+        flow->vlan_tci = tci;
+    }
+
+    if (is_mask && !(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ENCAP))) {
         return fitness;
-    } else if (!(tci & htons(VLAN_CFI))) {
-        VLOG_ERR_RL(&rl, "OVS_KEY_ATTR_VLAN 0x%04"PRIx16" is nonzero "
-                    "but CFI bit is not set", ntohs(tci));
-        return ODP_FIT_ERROR;
     }
-
-    /* Set vlan_tci.
-     * Remove the TPID from dl_type since it's not the real Ethertype.  */
-    flow->vlan_tci = tci;
-    flow->dl_type = htons(0);
-
     /* Now parse the encapsulated attributes. */
     if (!parse_flow_nlattrs(nl_attr_get(encap), nl_attr_get_size(encap),
                             attrs, &present_attrs, &out_of_range_attr)) {
@@ -2882,39 +3071,26 @@ parse_8021q_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
     }
     expected_attrs = 0;
 
-    if (!parse_ethertype(attrs, present_attrs, &expected_attrs, flow)) {
+    if (!parse_ethertype(attrs, present_attrs, &expected_attrs, flow, src_flow)) {
         return ODP_FIT_ERROR;
     }
     encap_fitness = parse_l2_5_onward(attrs, present_attrs, out_of_range_attr,
-                                      expected_attrs, flow, key, key_len);
+                                      expected_attrs, flow, key, key_len,
+                                      src_flow);
 
     /* The overall fitness is the worse of the outer and inner attributes. */
     return MAX(fitness, encap_fitness);
 }
 
-/* Converts the 'key_len' bytes of OVS_KEY_ATTR_* attributes in 'key' to a flow
- * structure in 'flow'.  Returns an ODP_FIT_* value that indicates how well
- * 'key' fits our expectations for what a flow key should contain.
- *
- * The 'in_port' will be the datapath's understanding of the port.  The
- * caller will need to translate with odp_port_to_ofp_port() if the
- * OpenFlow port is needed.
- *
- * This function doesn't take the packet itself as an argument because none of
- * the currently understood OVS_KEY_ATTR_* attributes require it.  Currently,
- * it is always possible to infer which additional attribute(s) should appear
- * by looking at the attributes for lower-level protocols, e.g. if the network
- * protocol in OVS_KEY_ATTR_IPV4 or OVS_KEY_ATTR_IPV6 is IPPROTO_TCP then we
- * know that a OVS_KEY_ATTR_TCP attribute must appear and that otherwise it
- * must be absent. */
-enum odp_key_fitness
-odp_flow_key_to_flow(const struct nlattr *key, size_t key_len,
-                     struct flow *flow)
+static enum odp_key_fitness
+odp_flow_key_to_flow__(const struct nlattr *key, size_t key_len,
+                       struct flow *flow, const struct flow *src_flow)
 {
     const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1];
     uint64_t expected_attrs;
     uint64_t present_attrs;
     int out_of_range_attr;
+    bool is_mask = src_flow != flow;
 
     memset(flow, 0, sizeof *flow);
 
@@ -2951,7 +3127,7 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len,
         flow->in_port.odp_port
             = nl_attr_get_odp_port(attrs[OVS_KEY_ATTR_IN_PORT]);
         expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IN_PORT;
-    } else {
+    } else if (!is_mask) {
         flow->in_port.odp_port = ODPP_NONE;
     }
 
@@ -2962,20 +3138,67 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len,
         eth_key = nl_attr_get(attrs[OVS_KEY_ATTR_ETHERNET]);
         memcpy(flow->dl_src, eth_key->eth_src, ETH_ADDR_LEN);
         memcpy(flow->dl_dst, eth_key->eth_dst, ETH_ADDR_LEN);
+        if (is_mask) {
+            expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERNET;
+        }
+    }
+    if (!is_mask) {
+        expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERNET;
     }
-    expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERNET;
 
     /* Get Ethertype or 802.1Q TPID or FLOW_DL_TYPE_NONE. */
-    if (!parse_ethertype(attrs, present_attrs, &expected_attrs, flow)) {
+    if (!parse_ethertype(attrs, present_attrs, &expected_attrs, flow,
+        src_flow)) {
         return ODP_FIT_ERROR;
     }
 
-    if (flow->dl_type == htons(ETH_TYPE_VLAN)) {
+    if ((is_mask && (src_flow->vlan_tci & htons(VLAN_CFI))) ||
+        (!is_mask && src_flow->dl_type == htons(ETH_TYPE_VLAN))) {
         return parse_8021q_onward(attrs, present_attrs, out_of_range_attr,
-                                  expected_attrs, flow, key, key_len);
+                                  expected_attrs, flow, key, key_len, src_flow);
+    }
+    if (is_mask) {
+        flow->vlan_tci = htons(0xffff);
+        if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN)) {
+            flow->vlan_tci = nl_attr_get_be16(attrs[OVS_KEY_ATTR_VLAN]);
+            expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_VLAN);
+        }
     }
     return parse_l2_5_onward(attrs, present_attrs, out_of_range_attr,
-                             expected_attrs, flow, key, key_len);
+                             expected_attrs, flow, key, key_len, src_flow);
+}
+
+/* Converts the 'key_len' bytes of OVS_KEY_ATTR_* attributes in 'key' to a flow
+ * structure in 'flow'.  Returns an ODP_FIT_* value that indicates how well
+ * 'key' fits our expectations for what a flow key should contain.
+ *
+ * The 'in_port' will be the datapath's understanding of the port.  The
+ * caller will need to translate with odp_port_to_ofp_port() if the
+ * OpenFlow port is needed.
+ *
+ * This function doesn't take the packet itself as an argument because none of
+ * the currently understood OVS_KEY_ATTR_* attributes require it.  Currently,
+ * it is always possible to infer which additional attribute(s) should appear
+ * by looking at the attributes for lower-level protocols, e.g. if the network
+ * protocol in OVS_KEY_ATTR_IPV4 or OVS_KEY_ATTR_IPV6 is IPPROTO_TCP then we
+ * know that a OVS_KEY_ATTR_TCP attribute must appear and that otherwise it
+ * must be absent. */
+enum odp_key_fitness
+odp_flow_key_to_flow(const struct nlattr *key, size_t key_len,
+                     struct flow *flow)
+{
+   return odp_flow_key_to_flow__(key, key_len, flow, flow);
+}
+
+/* Converts the 'key_len' bytes of OVS_KEY_ATTR_* attributes in 'key' to a mask
+ * structure in 'mask'.  'flow' must be a previously translated flow
+ * corresponding to 'mask'.  Returns an ODP_FIT_* value that indicates how well
+ * 'key' fits our expectations for what a flow key should contain. */
+enum odp_key_fitness
+odp_flow_key_to_mask(const struct nlattr *key, size_t key_len,
+                     struct flow *mask, const struct flow *flow)
+{
+   return odp_flow_key_to_flow__(key, key_len, mask, flow);
 }
 
 /* Returns 'fitness' as a string, for use in debug messages. */
@@ -3286,6 +3509,14 @@ commit_set_port_action(const struct flow *flow, struct flow *base,
 
         commit_set_action(odp_actions, OVS_KEY_ATTR_UDP,
                           &port_key, sizeof(port_key));
+    } else if (flow->nw_proto == IPPROTO_SCTP) {
+        struct ovs_key_sctp port_key;
+
+        port_key.sctp_src = base->tp_src = flow->tp_src;
+        port_key.sctp_dst = base->tp_dst = flow->tp_dst;
+
+        commit_set_action(odp_actions, OVS_KEY_ATTR_SCTP,
+                          &port_key, sizeof(port_key));
     }
 }
 
index 0c40f38..192cfa0 100644 (file)
@@ -122,6 +122,9 @@ enum odp_key_fitness {
 };
 enum odp_key_fitness odp_flow_key_to_flow(const struct nlattr *, size_t,
                                           struct flow *);
+enum odp_key_fitness odp_flow_key_to_mask(const struct nlattr *key, size_t len,
+                                          struct flow *mask,
+                                          const struct flow *flow);
 const char *odp_key_fitness_to_string(enum odp_key_fitness);
 
 void commit_odp_tunnel_action(const struct flow *, struct flow *base,
index 5bf5826..79acd30 100644 (file)
@@ -214,7 +214,8 @@ enum ofperr {
     /* OF1.1+(2,9).  Invalid group id in forward action. */
     OFPERR_OFPBAC_BAD_OUT_GROUP,
 
-    /* OF1.1+(2,10).  Action can't apply for this match. */
+    /* NX1.0(1,522), OF1.1+(2,10).  Action can't apply for this match or a
+     * prerequisite for use of this field is unmet. */
     OFPERR_OFPBAC_MATCH_INCONSISTENT,
 
     /* OF1.1+(2,11).  Action order is unsupported for the action list in an
@@ -224,14 +225,17 @@ enum ofperr {
     /* OF1.1+(2,12).  Actions uses an unsupported tag/encap. */
     OFPERR_OFPBAC_BAD_TAG,
 
-    /* OF1.2+(2,13).  Unsupported type in SET_FIELD action. */
-    OFPERR_OFPBAC_SET_TYPE,
+    /* NX1.0-1.1(1,523), OF1.2+(2,13).  Action uses unknown or unsupported OXM
+     * or NXM field. */
+    OFPERR_OFPBAC_BAD_SET_TYPE,
 
-    /* OF1.2+(2,14).  Length problem in SET_FIELD action. */
-    OFPERR_OFPBAC_SET_LEN,
+    /* NX1.0-1.1(1,524), OF1.2+(2,14).  Action references past the end of an
+     * OXM or NXM field, or uses a length of zero. */
+    OFPERR_OFPBAC_BAD_SET_LEN,
 
-    /* OF1.2+(2,15).  Bad argument in SET_FIELD action. */
-    OFPERR_OFPBAC_ARGUMENT,
+    /* NX1.0-1.1(1,525), OF1.2+(2,15).  Action sets a field to an invalid or
+     * unsupported value, or modifies a read-only field. */
+    OFPERR_OFPBAC_BAD_SET_ARGUMENT,
 
     /* NX1.0-1.1(2,256), NX1.2+(11).  Must-be-zero action argument had nonzero
      * value. */
index 5cb39f5..dd0738c 100644 (file)
@@ -132,7 +132,7 @@ str_to_u64(const char *str, uint64_t *valuep)
 static char * WARN_UNUSED_RESULT
 str_to_be64(const char *str, ovs_be64 *valuep)
 {
-    uint64_t value;
+    uint64_t value = 0;
     char *error;
 
     error = str_to_u64(str, &value);
@@ -246,7 +246,7 @@ parse_resubmit(char *arg, struct ofpbuf *ofpacts)
 
     table_s = strsep(&arg, ",");
     if (table_s && table_s[0]) {
-        uint32_t table_id;
+        uint32_t table_id = 0;
         char *error;
 
         error = str_to_u32(table_s, &table_id);
@@ -598,10 +598,10 @@ parse_named_action(enum ofputil_action_code code,
     size_t orig_size = ofpacts->size;
     struct ofpact_tunnel *tunnel;
     char *error = NULL;
-    uint16_t ethertype;
-    uint16_t vid;
-    uint8_t pcp;
-    uint8_t tos;
+    uint16_t ethertype = 0;
+    uint16_t vid = 0;
+    uint8_t tos = 0;
+    uint8_t pcp = 0;
 
     switch (code) {
     case OFPUTIL_ACTION_INVALID:
@@ -1034,11 +1034,13 @@ parse_protocol(const char *name, const struct protocol **p_out)
         { "icmp", ETH_TYPE_IP, IPPROTO_ICMP },
         { "tcp", ETH_TYPE_IP, IPPROTO_TCP },
         { "udp", ETH_TYPE_IP, IPPROTO_UDP },
+        { "sctp", ETH_TYPE_IP, IPPROTO_SCTP },
         { "ipv6", ETH_TYPE_IPV6, 0 },
         { "ip6", ETH_TYPE_IPV6, 0 },
         { "icmp6", ETH_TYPE_IPV6, IPPROTO_ICMPV6 },
         { "tcp6", ETH_TYPE_IPV6, IPPROTO_TCP },
         { "udp6", ETH_TYPE_IPV6, IPPROTO_UDP },
+        { "sctp6", ETH_TYPE_IPV6, IPPROTO_SCTP },
         { "rarp", ETH_TYPE_RARP, 0},
         { "mpls", ETH_TYPE_MPLS, 0 },
         { "mplsm", ETH_TYPE_MPLS_MCAST, 0 },
@@ -1184,7 +1186,7 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string)
                                       value);
                 }
             } else if (fields & F_PRIORITY && !strcmp(name, "priority")) {
-                uint16_t priority;
+                uint16_t priority = 0;
 
                 error = str_to_u16(value, name, &priority);
                 fm->priority = priority;
index 21989a9..23ba9d4 100644 (file)
@@ -74,6 +74,10 @@ ofp_packet_to_string(const void *data, size_t len)
             struct udp_header *uh = buf.l4;
             ds_put_format(&ds, " udp_csum:%"PRIx16,
                           ntohs(uh->udp_csum));
+        } else if (flow.nw_proto == IPPROTO_SCTP) {
+            struct sctp_header *sh = buf.l4;
+            ds_put_format(&ds, " sctp_csum:%"PRIx32,
+                          ntohl(sh->sctp_csum));
         }
     }
 
@@ -649,6 +653,8 @@ ofp10_match_to_string(const struct ofp10_match *om, int verbosity)
                     ds_put_cstr(&f, "tcp,");
                 } else if (om->nw_proto == IPPROTO_UDP) {
                     ds_put_cstr(&f, "udp,");
+                } else if (om->nw_proto == IPPROTO_SCTP) {
+                    ds_put_cstr(&f, "sctp,");
                 } else {
                     ds_put_cstr(&f, "ip,");
                     skip_proto = false;
@@ -1106,13 +1112,9 @@ ofputil_meter_capabilities_to_name(uint32_t bit)
 static const char *
 ofputil_meter_band_types_to_name(uint32_t bit)
 {
-    /*
-     * Note: Meter band types start from 1.  We assume that the lowest bit
-     * in the band_types corresponds to DROP band type (1).
-     */
     switch (bit) {
-    case 1 << (OFPMBT13_DROP - 1):          return "drop";
-    case 1 << (OFPMBT13_DSCP_REMARK - 1):   return "dscp_remark";
+    case 1 << OFPMBT13_DROP:          return "drop";
+    case 1 << OFPMBT13_DSCP_REMARK:   return "dscp_remark";
     }
 
     return NULL;
index 45ff0a1..62cfd2e 100644 (file)
@@ -420,6 +420,7 @@ ofputil_match_from_ofp11_match(const struct ofp11_match *ofmatch,
 
         case IPPROTO_TCP:
         case IPPROTO_UDP:
+        case IPPROTO_SCTP:
             if (!(wc & (OFPFW11_TP_SRC))) {
                 match_set_tp_src(match, ofmatch->tp_src);
             }
@@ -428,11 +429,6 @@ ofputil_match_from_ofp11_match(const struct ofp11_match *ofmatch,
             }
             break;
 
-        case IPPROTO_SCTP:
-            /* We don't support SCTP and it seems that we should tell the
-             * controller, since OF1.1 implementations are supposed to. */
-            return OFPERR_OFPBMC_BAD_FIELD;
-
         default:
             /* OF1.1 says explicitly to ignore this. */
             break;
@@ -4835,13 +4831,15 @@ ofputil_normalize_match__(struct match *match, bool may_log)
         may_match = MAY_NW_PROTO | MAY_IPVx | MAY_NW_ADDR;
         if (match->flow.nw_proto == IPPROTO_TCP ||
             match->flow.nw_proto == IPPROTO_UDP ||
+            match->flow.nw_proto == IPPROTO_SCTP ||
             match->flow.nw_proto == IPPROTO_ICMP) {
             may_match |= MAY_TP_ADDR;
         }
     } else if (match->flow.dl_type == htons(ETH_TYPE_IPV6)) {
         may_match = MAY_NW_PROTO | MAY_IPVx | MAY_IPV6;
         if (match->flow.nw_proto == IPPROTO_TCP ||
-            match->flow.nw_proto == IPPROTO_UDP) {
+            match->flow.nw_proto == IPPROTO_UDP ||
+            match->flow.nw_proto == IPPROTO_SCTP) {
             may_match |= MAY_TP_ADDR;
         } else if (match->flow.nw_proto == IPPROTO_ICMPV6) {
             may_match |= MAY_TP_ADDR;
index 1693848..d6a68ae 100644 (file)
@@ -20,7 +20,7 @@
 #include "ovs-thread.h"
 
 #if OVS_ATOMIC_GCC4P_IMPL
-static struct ovs_mutex mutex = OVS_ADAPTIVE_MUTEX_INITIALIZER;
+static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
 
 #define DEFINE_LOCKED_OP(TYPE, NAME, OPERATOR)                          \
     TYPE##_t                                                            \
index 3fc9dcb..3467f11 100644 (file)
@@ -21,7 +21,7 @@
  *
  * This library implements atomic operations with an API based on the one
  * defined in C11.  It includes multiple implementations for compilers and
- * libraries with varying degrees of built-in support for C11, including an
+ * libraries with varying degrees of built-in support for C11, including a
  * fallback implementation for systems that have pthreads but no other support
  * for atomics.
  *
index c156070..b3a87bb 100644 (file)
@@ -47,6 +47,7 @@ static bool multithreaded;
     void \
     ovs_##TYPE##_##FUN##_at(const struct ovs_##TYPE *l_, \
                             const char *where) \
+        OVS_NO_THREAD_SAFETY_ANALYSIS \
     { \
         struct ovs_##TYPE *l = CONST_CAST(struct ovs_##TYPE *, l_); \
         int error = pthread_##TYPE##_##FUN(&l->lock); \
@@ -63,6 +64,7 @@ LOCK_FUNCTION(rwlock, wrlock);
     int \
     ovs_##TYPE##_##FUN##_at(const struct ovs_##TYPE *l_, \
                             const char *where) \
+        OVS_NO_THREAD_SAFETY_ANALYSIS \
     { \
         struct ovs_##TYPE *l = CONST_CAST(struct ovs_##TYPE *, l_); \
         int error = pthread_##TYPE##_##FUN(&l->lock); \
@@ -81,6 +83,7 @@ TRY_LOCK_FUNCTION(rwlock, trywrlock);
 #define UNLOCK_FUNCTION(TYPE, FUN) \
     void \
     ovs_##TYPE##_##FUN(const struct ovs_##TYPE *l_) \
+        OVS_NO_THREAD_SAFETY_ANALYSIS \
     { \
         struct ovs_##TYPE *l = CONST_CAST(struct ovs_##TYPE *, l_); \
         int error; \
@@ -132,8 +135,8 @@ typedef void destructor_func(void *);
 XPTHREAD_FUNC2(pthread_key_create, pthread_key_t *, destructor_func *);
 XPTHREAD_FUNC2(pthread_setspecific, pthread_key_t, const void *);
 
-void
-ovs_mutex_init(const struct ovs_mutex *l_, int type)
+static void
+ovs_mutex_init__(const struct ovs_mutex *l_, int type)
 {
     struct ovs_mutex *l = CONST_CAST(struct ovs_mutex *, l_);
     pthread_mutexattr_t attr;
@@ -149,6 +152,20 @@ ovs_mutex_init(const struct ovs_mutex *l_, int type)
     xpthread_mutexattr_destroy(&attr);
 }
 
+/* Initializes 'mutex' as a normal (non-recursive) mutex. */
+void
+ovs_mutex_init(const struct ovs_mutex *mutex)
+{
+    ovs_mutex_init__(mutex, PTHREAD_MUTEX_ERRORCHECK);
+}
+
+/* Initializes 'mutex' as a recursive mutex. */
+void
+ovs_mutex_init_recursive(const struct ovs_mutex *mutex)
+{
+    ovs_mutex_init__(mutex, PTHREAD_MUTEX_RECURSIVE);
+}
+
 void
 ovs_rwlock_init(const struct ovs_rwlock *l_)
 {
index b7bc5d1..7f3195d 100644 (file)
@@ -30,38 +30,11 @@ struct OVS_LOCKABLE ovs_mutex {
     const char *where;
 };
 
-/* "struct ovs_mutex" initializers:
- *
- *    - OVS_MUTEX_INITIALIZER: common case.
- *
- *    - OVS_ADAPTIVE_MUTEX_INITIALIZER for a mutex that spins briefly then goes
- *      to sleeps after some number of iterations.
- *
- *    - OVS_ERRORCHECK_MUTEX_INITIALIZER for a mutex that is used for
- *      error-checking. */
-#define OVS_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, NULL }
-#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
-#define OVS_ADAPTIVE_MUTEX_INITIALIZER \
-    { PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP, NULL }
-#else
-#define OVS_ADAPTIVE_MUTEX_INITIALIZER OVS_MUTEX_INITIALIZER
-#endif
+/* "struct ovs_mutex" initializer. */
 #ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
-#define OVS_ERRORCHECK_MUTEX_INITIALIZER \
-    { PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP, NULL }
+#define OVS_MUTEX_INITIALIZER { PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP, NULL }
 #else
-#define OVS_ERRORCHECK_MUTEX_INITIALIZER OVS_MUTEX_INITIALIZER
-#endif
-\f
-/* Mutex types, suitable for use with pthread_mutexattr_settype().
- * There is only one nonstandard type:
- *
- *    - PTHREAD_MUTEX_ADAPTIVE_NP, the type used for
- *      OVS_ADAPTIVE_MUTEX_INITIALIZER. */
-#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
-#define OVS_MUTEX_ADAPTIVE PTHREAD_MUTEX_ADAPTIVE_NP
-#else
-#define OVS_MUTEX_ADAPTIVE PTHREAD_MUTEX_NORMAL
+#define OVS_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, NULL }
 #endif
 
 /* ovs_mutex functions analogous to pthread_mutex_*() functions.
@@ -69,7 +42,8 @@ struct OVS_LOCKABLE ovs_mutex {
  * Most of these functions abort the process with an error message on any
  * error.  ovs_mutex_trylock() is an exception: it passes through a 0 or EBUSY
  * return value to the caller and aborts on any other error. */
-void ovs_mutex_init(const struct ovs_mutex *, int type);
+void ovs_mutex_init(const struct ovs_mutex *);
+void ovs_mutex_init_recursive(const struct ovs_mutex *);
 void ovs_mutex_destroy(const struct ovs_mutex *);
 void ovs_mutex_unlock(const struct ovs_mutex *mutex) OVS_RELEASES(mutex);
 void ovs_mutex_lock_at(const struct ovs_mutex *mutex, const char *where)
@@ -463,7 +437,7 @@ struct ovsthread_once {
 #define OVSTHREAD_ONCE_INITIALIZER              \
     {                                           \
         ATOMIC_VAR_INIT(false),                 \
-        OVS_ADAPTIVE_MUTEX_INITIALIZER,         \
+        OVS_MUTEX_INITIALIZER,                  \
     }
 
 static inline bool ovsthread_once_start(struct ovsthread_once *once)
@@ -475,7 +449,7 @@ bool ovsthread_once_start__(struct ovsthread_once *once)
     OVS_TRY_LOCK(false, once->mutex);
 
 static inline bool
-ovsthread_once_is_done__(const struct ovsthread_once *once)
+ovsthread_once_is_done__(struct ovsthread_once *once)
 {
     bool done;
 
index b95e1e0..253184f 100644 (file)
@@ -23,6 +23,7 @@
 #include <stdlib.h>
 #include "byte-order.h"
 #include "csum.h"
+#include "crc32c.h"
 #include "flow.h"
 #include "hmap.h"
 #include "dynamic-string.h"
@@ -885,6 +886,27 @@ packet_set_udp_port(struct ofpbuf *packet, ovs_be16 src, ovs_be16 dst)
     }
 }
 
+/* Sets the SCTP source and destination port ('src' and 'dst' respectively) of
+ * the SCTP header contained in 'packet'.  'packet' must be a valid SCTP packet
+ * with its l4 marker properly populated. */
+void
+packet_set_sctp_port(struct ofpbuf *packet, ovs_be16 src, ovs_be16 dst)
+{
+    struct sctp_header *sh = packet->l4;
+    ovs_be32 old_csum, old_correct_csum, new_csum;
+    uint16_t tp_len = packet->size - ((uint8_t*)sh - (uint8_t*)packet->data);
+
+    old_csum = sh->sctp_csum;
+    sh->sctp_csum = 0;
+    old_correct_csum = crc32c(packet->l4, tp_len);
+
+    sh->sctp_src = src;
+    sh->sctp_dst = dst;
+
+    new_csum = crc32c(packet->l4, tp_len);
+    sh->sctp_csum = old_csum ^ old_correct_csum ^ new_csum;
+}
+
 /* If 'packet' is a TCP packet, returns the TCP flags.  Otherwise, returns 0.
  *
  * 'flow' must be the flow corresponding to 'packet' and 'packet''s header
index 33be891..2108bef 100644 (file)
@@ -471,6 +471,15 @@ struct icmp_header {
 };
 BUILD_ASSERT_DECL(ICMP_HEADER_LEN == sizeof(struct icmp_header));
 
+#define SCTP_HEADER_LEN 12
+struct sctp_header {
+    ovs_be16 sctp_src;
+    ovs_be16 sctp_dst;
+    ovs_be32 sctp_vtag;
+    ovs_be32 sctp_csum;
+};
+BUILD_ASSERT_DECL(SCTP_HEADER_LEN == sizeof(struct sctp_header));
+
 #define UDP_HEADER_LEN 8
 struct udp_header {
     ovs_be16 udp_src;
@@ -601,6 +610,7 @@ void packet_set_ipv6(struct ofpbuf *, uint8_t proto, const ovs_be32 src[4],
                      ovs_be32 fl, uint8_t hlmit);
 void packet_set_tcp_port(struct ofpbuf *, ovs_be16 src, ovs_be16 dst);
 void packet_set_udp_port(struct ofpbuf *, ovs_be16 src, ovs_be16 dst);
+void packet_set_sctp_port(struct ofpbuf *, ovs_be16 src, ovs_be16 dst);
 
 uint8_t packet_get_tcp_flags(const struct ofpbuf *, const struct flow *);
 void packet_format_tcp_flags(struct ds *, uint8_t);
index ed205a1..7a34244 100644 (file)
--- a/lib/seq.c
+++ b/lib/seq.c
@@ -52,7 +52,7 @@ struct seq_thread {
     bool waiting OVS_GUARDED;        /* True if latch_wait() already called. */
 };
 
-static struct ovs_mutex seq_mutex = OVS_ADAPTIVE_MUTEX_INITIALIZER;
+static struct ovs_mutex seq_mutex = OVS_MUTEX_INITIALIZER;
 
 static uint64_t seq_next OVS_GUARDED_BY(seq_mutex) = 1;
 
index d570be9..6e1efd0 100644 (file)
--- a/lib/stp.c
+++ b/lib/stp.c
@@ -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.
@@ -264,7 +264,7 @@ stp_create(const char *name, stp_identifier bridge_id,
          * into the stp module through a patch port.  This happens
          * intentionally as part of the unit tests.  Ideally we'd ditch
          * the call back function, but for now this is what we have. */
-        ovs_mutex_init(&mutex,  PTHREAD_MUTEX_RECURSIVE);
+        ovs_mutex_init_recursive(&mutex);
         ovsthread_once_done(&once);
     }
 
index 18748ea..315c851 100644 (file)
@@ -81,7 +81,7 @@ uuid_init(void)
 void
 uuid_generate(struct uuid *uuid)
 {
-    static struct ovs_mutex mutex = OVS_ADAPTIVE_MUTEX_INITIALIZER;
+    static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
     uint64_t copy[2];
 
     uuid_init();
index ac229b4..a267112 100644 (file)
@@ -74,6 +74,7 @@ extern struct vlog_module *__stop_vlog_modules[];
 #include "vlog-modules.def"
 #undef VLOG_MODULE
 
+extern struct vlog_module *vlog_modules[];
 struct vlog_module *vlog_modules[] = {
 #define VLOG_MODULE(NAME) &VLM_##NAME,
 #include "vlog-modules.def"
@@ -106,7 +107,7 @@ DEFINE_STATIC_PER_THREAD_DATA(unsigned int, msg_num, 0);
  *
  * All of the following is protected by 'log_file_mutex', which nests inside
  * pattern_rwlock. */
-static struct ovs_mutex log_file_mutex = OVS_ADAPTIVE_MUTEX_INITIALIZER;
+static struct ovs_mutex log_file_mutex = OVS_MUTEX_INITIALIZER;
 static char *log_file_name OVS_GUARDED_BY(log_file_mutex);
 static int log_fd OVS_GUARDED_BY(log_file_mutex) = -1;
 static struct async_append *log_writer OVS_GUARDED_BY(log_file_mutex);
index 87a9654..d2c6679 100644 (file)
@@ -119,7 +119,7 @@ struct vlog_rate_limit {
             0,                              /* first_dropped */         \
             0,                              /* last_dropped */          \
             0,                              /* n_dropped */             \
-            OVS_ADAPTIVE_MUTEX_INITIALIZER  /* mutex */                 \
+            OVS_MUTEX_INITIALIZER           /* mutex */                 \
         }
 
 /* Configuring how each module logs messages. */
index 8e8e7a2..a9cc73e 100644 (file)
 
 #include <config.h>
 #include "ofproto-dpif-ipfix.h"
+#include <sys/time.h>
 #include "byte-order.h"
 #include "collectors.h"
 #include "flow.h"
 #include "hash.h"
 #include "hmap.h"
+#include "list.h"
 #include "ofpbuf.h"
 #include "ofproto.h"
 #include "packets.h"
+#include "poll-loop.h"
 #include "sset.h"
 #include "util.h"
 #include "timeval.h"
@@ -42,6 +45,10 @@ struct dpif_ipfix_exporter {
     struct collectors *collectors;
     uint32_t seq_number;
     time_t last_template_set_time;
+    struct hmap cache_flow_key_map;  /* ipfix_flow_cache_entry. */
+    struct list cache_flow_start_timestamp_list;  /* ipfix_flow_cache_entry. */
+    uint32_t cache_active_timeout;  /* In seconds. */
+    uint32_t cache_max_flows;
 };
 
 struct dpif_ipfix_bridge_exporter {
@@ -62,7 +69,7 @@ struct dpif_ipfix_flow_exporter_map_node {
 
 struct dpif_ipfix {
     struct dpif_ipfix_bridge_exporter bridge_exporter;
-    struct hmap flow_exporter_map;  /* dpif_ipfix_flow_exporter_map_nodes. */
+    struct hmap flow_exporter_map;  /* dpif_ipfix_flow_exporter_map_node. */
     atomic_int ref_cnt;
 };
 
@@ -143,32 +150,30 @@ struct ipfix_template_field_specifier {
 });
 BUILD_ASSERT_DECL(sizeof(struct ipfix_template_field_specifier) == 4);
 
-/* Part of data record for common metadata and Ethernet entities. */
+/* Part of data record flow key for common metadata and Ethernet entities. */
 OVS_PACKED(
-struct ipfix_data_record_common {
+struct ipfix_data_record_flow_key_common {
     ovs_be32 observation_point_id;  /* OBSERVATION_POINT_ID */
-    ovs_be64 packet_delta_count;  /* PACKET_DELTA_COUNT */
-    ovs_be64 layer2_octet_delta_count;  /* LAYER2_OCTET_DELTA_COUNT */
     uint8_t source_mac_address[6];  /* SOURCE_MAC_ADDRESS */
     uint8_t destination_mac_address[6];  /* DESTINATION_MAC_ADDRESS */
     ovs_be16 ethernet_type;  /* ETHERNET_TYPE */
-    ovs_be16 ethernet_total_length;  /* ETHERNET_TOTAL_LENGTH */
     uint8_t ethernet_header_length;  /* ETHERNET_HEADER_LENGTH */
 });
-BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_common) == 37);
+BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_flow_key_common) == 19);
 
-/* Part of data record for VLAN entities. */
+/* Part of data record flow key for VLAN entities. */
 OVS_PACKED(
-struct ipfix_data_record_vlan {
+struct ipfix_data_record_flow_key_vlan {
     ovs_be16 vlan_id;  /* VLAN_ID */
     ovs_be16 dot1q_vlan_id;  /* DOT1Q_VLAN_ID */
     uint8_t dot1q_priority;  /* DOT1Q_PRIORITY */
 });
-BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_vlan) == 5);
+BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_flow_key_vlan) == 5);
 
-/* Part of data record for IP entities. */
+/* Part of data record flow key for IP entities. */
+/* XXX: Replace IP_TTL with MINIMUM_TTL and MAXIMUM_TTL? */
 OVS_PACKED(
-struct ipfix_data_record_ip {
+struct ipfix_data_record_flow_key_ip {
     uint8_t ip_version;  /* IP_VERSION */
     uint8_t ip_ttl;  /* IP_TTL */
     uint8_t protocol_identifier;  /* PROTOCOL_IDENTIFIER */
@@ -176,32 +181,116 @@ struct ipfix_data_record_ip {
     uint8_t ip_precedence;  /* IP_PRECEDENCE */
     uint8_t ip_class_of_service;  /* IP_CLASS_OF_SERVICE */
 });
-BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_ip) == 6);
+BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_flow_key_ip) == 6);
 
-/* Part of data record for IPv4 entities. */
+/* Part of data record flow key for IPv4 entities. */
 OVS_PACKED(
-struct ipfix_data_record_ipv4 {
+struct ipfix_data_record_flow_key_ipv4 {
     ovs_be32 source_ipv4_address;  /* SOURCE_IPV4_ADDRESS */
     ovs_be32 destination_ipv4_address;  /* DESTINATION_IPV4_ADDRESS */
 });
-BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_ipv4) == 8);
+BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_flow_key_ipv4) == 8);
 
-/* Part of data record for IPv4 entities. */
+/* Part of data record flow key for IPv6 entities. */
 OVS_PACKED(
-struct ipfix_data_record_ipv6 {
+struct ipfix_data_record_flow_key_ipv6 {
     uint8_t source_ipv6_address[16];  /* SOURCE_IPV6_ADDRESS */
     uint8_t destination_ipv6_address[16];  /* DESTINATION_IPV6_ADDRESS */
     ovs_be32 flow_label_ipv6;  /* FLOW_LABEL_IPV6 */
 });
-BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_ipv6) == 36);
+BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_flow_key_ipv6) == 36);
 
-/* Part of data record for TCP/UDP entities. */
+/* Part of data record flow key for TCP/UDP entities. */
 OVS_PACKED(
-struct ipfix_data_record_tcpudp {
+struct ipfix_data_record_flow_key_tcpudp {
     ovs_be16 source_transport_port;  /* SOURCE_TRANSPORT_PORT */
     ovs_be16 destination_transport_port;  /* DESTINATION_TRANSPORT_PORT */
 });
-BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_tcpudp) == 4);
+BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_flow_key_tcpudp) == 4);
+
+/* Cf. IETF RFC 5102 Section 5.11.3. */
+enum ipfix_flow_end_reason {
+    IDLE_TIMEOUT = 0x01,
+    ACTIVE_TIMEOUT = 0x02,
+    END_OF_FLOW_DETECTED = 0x03,
+    FORCED_END = 0x04,
+    LACK_OF_RESOURCES = 0x05
+};
+
+/* Part of data record for common aggregated elements. */
+OVS_PACKED(
+struct ipfix_data_record_aggregated_common {
+    ovs_be32 flow_start_delta_microseconds; /* FLOW_START_DELTA_MICROSECONDS */
+    ovs_be32 flow_end_delta_microseconds; /* FLOW_END_DELTA_MICROSECONDS */
+    ovs_be64 packet_delta_count;  /* PACKET_DELTA_COUNT */
+    ovs_be64 layer2_octet_delta_count;  /* LAYER2_OCTET_DELTA_COUNT */
+    uint8_t flow_end_reason;  /* FLOW_END_REASON */
+});
+BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_aggregated_common) == 25);
+
+/* Part of data record for IP aggregated elements. */
+OVS_PACKED(
+struct ipfix_data_record_aggregated_ip {
+    ovs_be64 octet_delta_sum_of_squares;  /* OCTET_DELTA_SUM_OF_SQUARES */
+    ovs_be64 minimum_ip_total_length;  /* MINIMUM_IP_TOTAL_LENGTH */
+    ovs_be64 maximum_ip_total_length;  /* MAXIMUM_IP_TOTAL_LENGTH */
+});
+BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_aggregated_ip) == 24);
+
+#define MAX_FLOW_KEY_LEN                                 \
+    (sizeof(struct ipfix_data_record_flow_key_common)    \
+     + sizeof(struct ipfix_data_record_flow_key_vlan)    \
+     + sizeof(struct ipfix_data_record_flow_key_ip)      \
+     + sizeof(struct ipfix_data_record_flow_key_ipv6)    \
+     + sizeof(struct ipfix_data_record_flow_key_tcpudp))
+
+#define MAX_DATA_RECORD_LEN                                 \
+    (MAX_FLOW_KEY_LEN                                       \
+     + sizeof(struct ipfix_data_record_aggregated_common)   \
+     + sizeof(struct ipfix_data_record_aggregated_ip))
+
+/* Max length of a data set.  To simplify the implementation, each
+ * data record is sent in a separate data set, so each data set
+ * contains at most one data record. */
+#define MAX_DATA_SET_LEN             \
+    (sizeof(struct ipfix_set_header) \
+     + MAX_DATA_RECORD_LEN)
+
+/* Max length of an IPFIX message. Arbitrarily set to accomodate low
+ * MTU. */
+#define MAX_MESSAGE_LEN 1024
+
+/* Cache structures. */
+
+/* Flow key. */
+struct ipfix_flow_key {
+    uint32_t obs_domain_id;
+    uint16_t template_id;
+    size_t flow_key_msg_part_size;
+    uint64_t flow_key_msg_part[DIV_ROUND_UP(MAX_FLOW_KEY_LEN, 8)];
+};
+
+/* Flow cache entry. */
+struct ipfix_flow_cache_entry {
+    struct hmap_node flow_key_map_node;
+    struct list cache_flow_start_timestamp_list_node;
+    struct ipfix_flow_key flow_key;
+    /* Common aggregated elements. */
+    uint64_t flow_start_timestamp_usec;
+    uint64_t flow_end_timestamp_usec;
+    uint64_t packet_delta_count;
+    uint64_t layer2_octet_delta_count;
+    uint64_t octet_delta_sum_of_squares;  /* 0 if not IP. */
+    uint16_t minimum_ip_total_length;  /* 0 if not IP. */
+    uint16_t maximum_ip_total_length;  /* 0 if not IP. */
+};
+
+static void dpif_ipfix_cache_expire(struct dpif_ipfix_exporter *, bool,
+                                    const uint64_t, const uint32_t);
+
+static void get_export_time_now(uint64_t *, uint32_t *);
+
+static void dpif_ipfix_cache_expire_now(struct dpif_ipfix_exporter *, bool);
 
 static bool
 ofproto_ipfix_bridge_exporter_options_equal(
@@ -211,6 +300,8 @@ ofproto_ipfix_bridge_exporter_options_equal(
     return (a->obs_domain_id == b->obs_domain_id
             && a->obs_point_id == b->obs_point_id
             && a->sampling_rate == b->sampling_rate
+            && a->cache_active_timeout == b->cache_active_timeout
+            && a->cache_max_flows == b->cache_max_flows
             && sset_equals(&a->targets, &b->targets));
 }
 
@@ -240,6 +331,8 @@ ofproto_ipfix_flow_exporter_options_equal(
     const struct ofproto_ipfix_flow_exporter_options *b)
 {
     return (a->collector_set_id == b->collector_set_id
+            && a->cache_active_timeout == b->cache_active_timeout
+            && a->cache_max_flows == b->cache_max_flows
             && sset_equals(&a->targets, &b->targets));
 }
 
@@ -263,18 +356,44 @@ ofproto_ipfix_flow_exporter_options_destroy(
     }
 }
 
+static void
+dpif_ipfix_exporter_init(struct dpif_ipfix_exporter *exporter)
+{
+    exporter->collectors = NULL;
+    exporter->seq_number = 1;
+    exporter->last_template_set_time = TIME_MIN;
+    hmap_init(&exporter->cache_flow_key_map);
+    list_init(&exporter->cache_flow_start_timestamp_list);
+    exporter->cache_active_timeout = 0;
+    exporter->cache_max_flows = 0;
+}
+
 static void
 dpif_ipfix_exporter_clear(struct dpif_ipfix_exporter *exporter)
 {
+    /* Flush the cache with flow end reason "forced end." */
+    dpif_ipfix_cache_expire_now(exporter, true);
+
     collectors_destroy(exporter->collectors);
     exporter->collectors = NULL;
     exporter->seq_number = 1;
     exporter->last_template_set_time = TIME_MIN;
+    exporter->cache_active_timeout = 0;
+    exporter->cache_max_flows = 0;
+}
+
+static void
+dpif_ipfix_exporter_destroy(struct dpif_ipfix_exporter *exporter)
+{
+    dpif_ipfix_exporter_clear(exporter);
+    hmap_destroy(&exporter->cache_flow_key_map);
 }
 
 static bool
 dpif_ipfix_exporter_set_options(struct dpif_ipfix_exporter *exporter,
-                                const struct sset *targets)
+                                const struct sset *targets,
+                                const uint32_t cache_active_timeout,
+                                const uint32_t cache_max_flows)
 {
     collectors_destroy(exporter->collectors);
     collectors_create(targets, IPFIX_DEFAULT_COLLECTOR_PORT,
@@ -285,9 +404,19 @@ dpif_ipfix_exporter_set_options(struct dpif_ipfix_exporter *exporter,
         dpif_ipfix_exporter_clear(exporter);
         return false;
     }
+    exporter->cache_active_timeout = cache_active_timeout;
+    exporter->cache_max_flows = cache_max_flows;
     return true;
 }
 
+static void
+dpif_ipfix_bridge_exporter_init(struct dpif_ipfix_bridge_exporter *exporter)
+{
+    dpif_ipfix_exporter_init(&exporter->exporter);
+    exporter->options = NULL;
+    exporter->probability = 0;
+}
+
 static void
 dpif_ipfix_bridge_exporter_clear(struct dpif_ipfix_bridge_exporter *exporter)
 {
@@ -297,6 +426,13 @@ dpif_ipfix_bridge_exporter_clear(struct dpif_ipfix_bridge_exporter *exporter)
     exporter->probability = 0;
 }
 
+static void
+dpif_ipfix_bridge_exporter_destroy(struct dpif_ipfix_bridge_exporter *exporter)
+{
+    dpif_ipfix_bridge_exporter_clear(exporter);
+    dpif_ipfix_exporter_destroy(&exporter->exporter);
+}
+
 static void
 dpif_ipfix_bridge_exporter_set_options(
     struct dpif_ipfix_bridge_exporter *exporter,
@@ -322,8 +458,9 @@ dpif_ipfix_bridge_exporter_set_options(
     if (options_changed
         || collectors_count(exporter->exporter.collectors)
             < sset_count(&options->targets)) {
-        if (!dpif_ipfix_exporter_set_options(&exporter->exporter,
-                                             &options->targets)) {
+        if (!dpif_ipfix_exporter_set_options(
+                &exporter->exporter, &options->targets,
+                options->cache_active_timeout, options->cache_max_flows)) {
             return;
         }
     }
@@ -337,11 +474,16 @@ dpif_ipfix_bridge_exporter_set_options(
     exporter->options = ofproto_ipfix_bridge_exporter_options_clone(options);
     exporter->probability =
         MAX(1, UINT32_MAX / exporter->options->sampling_rate);
+
+    /* Run over the cache as some entries might have expired after
+     * changing the timeouts. */
+    dpif_ipfix_cache_expire_now(&exporter->exporter, false);
 }
 
 static struct dpif_ipfix_flow_exporter_map_node*
 dpif_ipfix_find_flow_exporter_map_node(
     const struct dpif_ipfix *di, const uint32_t collector_set_id)
+    OVS_REQUIRES(mutex)
 {
     struct dpif_ipfix_flow_exporter_map_node *exporter_node;
 
@@ -357,6 +499,13 @@ dpif_ipfix_find_flow_exporter_map_node(
     return NULL;
 }
 
+static void
+dpif_ipfix_flow_exporter_init(struct dpif_ipfix_flow_exporter *exporter)
+{
+    dpif_ipfix_exporter_init(&exporter->exporter);
+    exporter->options = NULL;
+}
+
 static void
 dpif_ipfix_flow_exporter_clear(struct dpif_ipfix_flow_exporter *exporter)
 {
@@ -365,6 +514,13 @@ dpif_ipfix_flow_exporter_clear(struct dpif_ipfix_flow_exporter *exporter)
     exporter->options = NULL;
 }
 
+static void
+dpif_ipfix_flow_exporter_destroy(struct dpif_ipfix_flow_exporter *exporter)
+{
+    dpif_ipfix_flow_exporter_clear(exporter);
+    dpif_ipfix_exporter_destroy(&exporter->exporter);
+}
+
 static bool
 dpif_ipfix_flow_exporter_set_options(
     struct dpif_ipfix_flow_exporter *exporter,
@@ -390,8 +546,9 @@ dpif_ipfix_flow_exporter_set_options(
     if (options_changed
         || collectors_count(exporter->exporter.collectors)
             < sset_count(&options->targets)) {
-        if (!dpif_ipfix_exporter_set_options(&exporter->exporter,
-                                             &options->targets)) {
+        if (!dpif_ipfix_exporter_set_options(
+                &exporter->exporter, &options->targets,
+                options->cache_active_timeout, options->cache_max_flows)) {
             return false;
         }
     }
@@ -404,6 +561,10 @@ dpif_ipfix_flow_exporter_set_options(
     ofproto_ipfix_flow_exporter_options_destroy(exporter->options);
     exporter->options = ofproto_ipfix_flow_exporter_options_clone(options);
 
+    /* Run over the cache as some entries might have expired after
+     * changing the timeouts. */
+    dpif_ipfix_cache_expire_now(&exporter->exporter, false);
+
     return true;
 }
 
@@ -431,7 +592,7 @@ dpif_ipfix_set_options(
             di, options->collector_set_id);
         if (!node) {
             node = xzalloc(sizeof *node);
-            dpif_ipfix_exporter_clear(&node->exporter.exporter);
+            dpif_ipfix_flow_exporter_init(&node->exporter);
             hmap_insert(&di->flow_exporter_map, &node->node,
                         hash_int(options->collector_set_id, 0));
         }
@@ -460,7 +621,7 @@ dpif_ipfix_set_options(
             }
             if (i == n_flow_exporters_options) {  // Not found.
                 hmap_remove(&di->flow_exporter_map, &node->node);
-                dpif_ipfix_flow_exporter_clear(&node->exporter);
+                dpif_ipfix_flow_exporter_destroy(&node->exporter);
                 free(node);
             }
         }
@@ -476,7 +637,7 @@ dpif_ipfix_create(void)
 {
     struct dpif_ipfix *di;
     di = xzalloc(sizeof *di);
-    dpif_ipfix_exporter_clear(&di->bridge_exporter.exporter);
+    dpif_ipfix_bridge_exporter_init(&di->bridge_exporter);
     hmap_init(&di->flow_exporter_map);
     atomic_init(&di->ref_cnt, 1);
     return di;
@@ -508,14 +669,14 @@ dpif_ipfix_get_bridge_exporter_probability(const struct dpif_ipfix *di)
 static void
 dpif_ipfix_clear(struct dpif_ipfix *di) OVS_REQUIRES(mutex)
 {
-    struct dpif_ipfix_flow_exporter_map_node *node, *next;
+    struct dpif_ipfix_flow_exporter_map_node *exp_node, *exp_next;
 
     dpif_ipfix_bridge_exporter_clear(&di->bridge_exporter);
 
-    HMAP_FOR_EACH_SAFE (node, next, node, &di->flow_exporter_map) {
-        hmap_remove(&di->flow_exporter_map, &node->node);
-        dpif_ipfix_flow_exporter_clear(&node->exporter);
-        free(node);
+    HMAP_FOR_EACH_SAFE (exp_node, exp_next, node, &di->flow_exporter_map) {
+        hmap_remove(&di->flow_exporter_map, &exp_node->node);
+        dpif_ipfix_flow_exporter_destroy(&exp_node->exporter);
+        free(exp_node);
     }
 }
 
@@ -533,6 +694,7 @@ dpif_ipfix_unref(struct dpif_ipfix *di) OVS_EXCLUDED(mutex)
     if (orig == 1) {
         ovs_mutex_lock(&mutex);
         dpif_ipfix_clear(di);
+        dpif_ipfix_bridge_exporter_destroy(&di->bridge_exporter);
         hmap_destroy(&di->flow_exporter_map);
         free(di);
         ovs_mutex_unlock(&mutex);
@@ -540,15 +702,15 @@ dpif_ipfix_unref(struct dpif_ipfix *di) OVS_EXCLUDED(mutex)
 }
 
 static void
-ipfix_init_header(uint32_t seq_number, uint32_t obs_domain_id,
-                  struct ofpbuf *msg)
+ipfix_init_header(uint32_t export_time_sec, uint32_t seq_number,
+                  uint32_t obs_domain_id, struct ofpbuf *msg)
 {
     struct ipfix_header *hdr;
 
     hdr = ofpbuf_put_zeros(msg, sizeof *hdr);
     hdr->version = htons(IPFIX_VERSION);
     hdr->length = htons(sizeof *hdr);  /* Updated in ipfix_send_msg. */
-    hdr->export_time = htonl(time_wall());
+    hdr->export_time = htonl(export_time_sec);
     hdr->seq_number = htonl(seq_number);
     hdr->obs_domain_id = htonl(obs_domain_id);
 }
@@ -601,15 +763,14 @@ ipfix_define_template_fields(enum ipfix_proto_l2 l2, enum ipfix_proto_l3 l3,
         count++; \
     }
 
+    /* 1. Flow key. */
+
     DEF(OBSERVATION_POINT_ID);
-    DEF(PACKET_DELTA_COUNT);
-    DEF(LAYER2_OCTET_DELTA_COUNT);
 
     /* Common Ethernet entities. */
     DEF(SOURCE_MAC_ADDRESS);
     DEF(DESTINATION_MAC_ADDRESS);
     DEF(ETHERNET_TYPE);
-    DEF(ETHERNET_TOTAL_LENGTH);
     DEF(ETHERNET_HEADER_LENGTH);
 
     if (l2 == IPFIX_PROTO_L2_VLAN) {
@@ -641,6 +802,20 @@ ipfix_define_template_fields(enum ipfix_proto_l2 l2, enum ipfix_proto_l3 l3,
         DEF(DESTINATION_TRANSPORT_PORT);
     }
 
+    /* 2. Flow aggregated data. */
+
+    DEF(FLOW_START_DELTA_MICROSECONDS);
+    DEF(FLOW_END_DELTA_MICROSECONDS);
+    DEF(PACKET_DELTA_COUNT);
+    DEF(LAYER2_OCTET_DELTA_COUNT);
+    DEF(FLOW_END_REASON);
+
+    if (l3 != IPFIX_PROTO_L3_UNKNOWN) {
+        DEF(OCTET_DELTA_SUM_OF_SQUARES);
+        DEF(MINIMUM_IP_TOTAL_LENGTH);
+        DEF(MAXIMUM_IP_TOTAL_LENGTH);
+    }
+
 #undef DEF
 
     return count;
@@ -648,9 +823,9 @@ ipfix_define_template_fields(enum ipfix_proto_l2 l2, enum ipfix_proto_l3 l3,
 
 static void
 ipfix_send_template_msg(struct dpif_ipfix_exporter *exporter,
-                        uint32_t obs_domain_id)
+                        uint32_t export_time_sec, uint32_t obs_domain_id)
 {
-    uint64_t msg_stub[DIV_ROUND_UP(1500, 8)];
+    uint64_t msg_stub[DIV_ROUND_UP(MAX_MESSAGE_LEN, 8)];
     struct ofpbuf msg;
     size_t set_hdr_offset, tmpl_hdr_offset;
     struct ipfix_set_header *set_hdr;
@@ -662,7 +837,8 @@ ipfix_send_template_msg(struct dpif_ipfix_exporter *exporter,
 
     ofpbuf_use_stub(&msg, msg_stub, sizeof msg_stub);
 
-    ipfix_init_header(exporter->seq_number, obs_domain_id, &msg);
+    ipfix_init_header(export_time_sec, exporter->seq_number, obs_domain_id,
+                      &msg);
     set_hdr_offset = msg.size;
 
     /* Add a Template Set. */
@@ -701,24 +877,149 @@ ipfix_send_template_msg(struct dpif_ipfix_exporter *exporter,
     ofpbuf_uninit(&msg);
 }
 
+static inline uint32_t
+ipfix_hash_flow_key(const struct ipfix_flow_key *flow_key, uint32_t basis)
+{
+    uint32_t hash;
+    hash = hash_int(flow_key->obs_domain_id, basis);
+    hash = hash_int(flow_key->template_id, hash);
+    hash = hash_bytes(flow_key->flow_key_msg_part,
+                      flow_key->flow_key_msg_part_size, hash);
+    return hash;
+}
+
+static bool
+ipfix_flow_key_equal(const struct ipfix_flow_key *a,
+                     const struct ipfix_flow_key *b)
+{
+    /* The template ID determines the flow key size, so not need to
+     * compare it. */
+    return (a->obs_domain_id == b->obs_domain_id
+            && a->template_id == b->template_id
+            && memcmp(a->flow_key_msg_part, b->flow_key_msg_part,
+                      a->flow_key_msg_part_size) == 0);
+}
+
+static struct ipfix_flow_cache_entry*
+ipfix_cache_find_entry(const struct dpif_ipfix_exporter *exporter,
+                       const struct ipfix_flow_key *flow_key)
+{
+    struct ipfix_flow_cache_entry *entry;
+
+    HMAP_FOR_EACH_WITH_HASH (entry, flow_key_map_node,
+                             ipfix_hash_flow_key(flow_key, 0),
+                             &exporter->cache_flow_key_map) {
+        if (ipfix_flow_key_equal(&entry->flow_key, flow_key)) {
+            return entry;
+        }
+    }
+
+    return NULL;
+}
+
+static bool
+ipfix_cache_next_timeout_msec(const struct dpif_ipfix_exporter *exporter,
+                              long long int *next_timeout_msec)
+{
+    struct ipfix_flow_cache_entry *entry;
+
+    LIST_FOR_EACH (entry, cache_flow_start_timestamp_list_node,
+                   &exporter->cache_flow_start_timestamp_list) {
+        *next_timeout_msec = entry->flow_start_timestamp_usec / 1000LL
+            + 1000LL * exporter->cache_active_timeout;
+        return true;
+    }
+
+    return false;
+}
+
+static void
+ipfix_cache_aggregate_entries(struct ipfix_flow_cache_entry *from_entry,
+                              struct ipfix_flow_cache_entry *to_entry)
+{
+    uint64_t *to_start, *to_end, *from_start, *from_end;
+    uint16_t *to_min_len, *to_max_len, *from_min_len, *from_max_len;
+
+    to_start = &to_entry->flow_start_timestamp_usec;
+    to_end = &to_entry->flow_end_timestamp_usec;
+    from_start = &from_entry->flow_start_timestamp_usec;
+    from_end = &from_entry->flow_end_timestamp_usec;
+
+    if (*to_start > *from_start) {
+        *to_start = *from_start;
+    }
+    if (*to_end < *from_end) {
+        *to_end = *from_end;
+    }
+
+    to_entry->packet_delta_count += from_entry->packet_delta_count;
+    to_entry->layer2_octet_delta_count += from_entry->layer2_octet_delta_count;
+
+    to_entry->octet_delta_sum_of_squares +=
+        from_entry->octet_delta_sum_of_squares;
+
+    to_min_len = &to_entry->minimum_ip_total_length;
+    to_max_len = &to_entry->maximum_ip_total_length;
+    from_min_len = &from_entry->minimum_ip_total_length;
+    from_max_len = &from_entry->maximum_ip_total_length;
+
+    if (!*to_min_len || (*from_min_len && *to_min_len > *from_min_len)) {
+        *to_min_len = *from_min_len;
+    }
+    if (*to_max_len < *from_max_len) {
+        *to_max_len = *from_max_len;
+    }
+}
+
+/* Add an entry into a flow cache.  The entry is either aggregated into
+ * an existing entry with the same flow key and free()d, or it is
+ * inserted into the cache. */
+static void
+ipfix_cache_update(struct dpif_ipfix_exporter *exporter,
+                   struct ipfix_flow_cache_entry *entry)
+{
+    struct ipfix_flow_cache_entry *old_entry;
+
+    old_entry = ipfix_cache_find_entry(exporter, &entry->flow_key);
+
+    if (old_entry == NULL) {
+        hmap_insert(&exporter->cache_flow_key_map, &entry->flow_key_map_node,
+                    ipfix_hash_flow_key(&entry->flow_key, 0));
+
+        /* As the latest entry added into the cache, it should
+         * logically have the highest flow_start_timestamp_usec, so
+         * append it at the tail. */
+        list_push_back(&exporter->cache_flow_start_timestamp_list,
+                       &entry->cache_flow_start_timestamp_list_node);
+
+        /* Enforce exporter->cache_max_flows limit. */
+        if (hmap_count(&exporter->cache_flow_key_map)
+            > exporter->cache_max_flows) {
+            dpif_ipfix_cache_expire_now(exporter, false);
+        }
+    } else {
+        ipfix_cache_aggregate_entries(entry, old_entry);
+        free(entry);
+    }
+}
+
 static void
-ipfix_send_data_msg(struct dpif_ipfix_exporter *exporter, struct ofpbuf *packet,
-                    const struct flow *flow, uint64_t packet_delta_count,
-                    uint32_t obs_domain_id, uint32_t obs_point_id)
+ipfix_cache_entry_init(struct ipfix_flow_cache_entry *entry,
+                       struct ofpbuf *packet, const struct flow *flow,
+                       uint64_t packet_delta_count, uint32_t obs_domain_id,
+                       uint32_t obs_point_id)
 {
-    uint64_t msg_stub[DIV_ROUND_UP(1500, 8)];
+    struct ipfix_flow_key *flow_key;
     struct ofpbuf msg;
-    size_t set_hdr_offset;
-    struct ipfix_set_header *set_hdr;
     enum ipfix_proto_l2 l2;
     enum ipfix_proto_l3 l3;
     enum ipfix_proto_l4 l4;
+    uint8_t ethernet_header_length;
+    uint16_t ethernet_total_length;
 
-    ofpbuf_use_stub(&msg, msg_stub, sizeof msg_stub);
-
-    ipfix_init_header(exporter->seq_number, obs_domain_id, &msg);
-    exporter->seq_number++;
-    set_hdr_offset = msg.size;
+    flow_key = &entry->flow_key;
+    ofpbuf_use_stack(&msg, flow_key->flow_key_msg_part,
+                     sizeof flow_key->flow_key_msg_part);
 
     /* Choose the right template ID matching the protocols in the
      * sampled packet. */
@@ -745,46 +1046,33 @@ ipfix_send_data_msg(struct dpif_ipfix_exporter *exporter, struct ofpbuf *packet,
         }
     }
 
-    /* Add a Data Set. */
-    set_hdr = ofpbuf_put_zeros(&msg, sizeof *set_hdr);
-    set_hdr->set_id = htons(ipfix_get_template_id(l2, l3, l4));
+    flow_key->obs_domain_id = obs_domain_id;
+    flow_key->template_id = ipfix_get_template_id(l2, l3, l4);
 
     /* The fields defined in the ipfix_data_record_* structs and sent
      * below must match exactly the templates defined in
      * ipfix_define_template_fields. */
 
+    ethernet_header_length = (l2 == IPFIX_PROTO_L2_VLAN)
+        ? VLAN_ETH_HEADER_LEN : ETH_HEADER_LEN;
+    ethernet_total_length = packet->size;
+
     /* Common Ethernet entities. */
     {
-        struct ipfix_data_record_common *data_common;
-        uint16_t ethernet_total_length;
-        uint8_t ethernet_header_length;
-        uint64_t layer2_octet_delta_count;
-
-        ethernet_total_length = packet->size;
-        ethernet_header_length = (l2 == IPFIX_PROTO_L2_VLAN)
-            ? VLAN_ETH_HEADER_LEN : ETH_HEADER_LEN;
-
-        /* Calculate the total matched octet count by considering as
-         * an approximation that all matched packets have the same
-         * length. */
-        layer2_octet_delta_count = packet_delta_count * ethernet_total_length;
+        struct ipfix_data_record_flow_key_common *data_common;
 
         data_common = ofpbuf_put_zeros(&msg, sizeof *data_common);
         data_common->observation_point_id = htonl(obs_point_id);
-        data_common->packet_delta_count = htonll(packet_delta_count);
-        data_common->layer2_octet_delta_count =
-            htonll(layer2_octet_delta_count);
         memcpy(data_common->source_mac_address, flow->dl_src,
                sizeof flow->dl_src);
         memcpy(data_common->destination_mac_address, flow->dl_dst,
                sizeof flow->dl_dst);
         data_common->ethernet_type = flow->dl_type;
-        data_common->ethernet_total_length = htons(ethernet_total_length);
         data_common->ethernet_header_length = ethernet_header_length;
     }
 
     if (l2 == IPFIX_PROTO_L2_VLAN) {
-        struct ipfix_data_record_vlan *data_vlan;
+        struct ipfix_data_record_flow_key_vlan *data_vlan;
         uint16_t vlan_id = vlan_tci_to_vid(flow->vlan_tci);
         uint8_t priority = vlan_tci_to_pcp(flow->vlan_tci);
 
@@ -795,7 +1083,7 @@ ipfix_send_data_msg(struct dpif_ipfix_exporter *exporter, struct ofpbuf *packet,
     }
 
     if (l3 != IPFIX_PROTO_L3_UNKNOWN) {
-        struct ipfix_data_record_ip *data_ip;
+        struct ipfix_data_record_flow_key_ip *data_ip;
 
         data_ip = ofpbuf_put_zeros(&msg, sizeof *data_ip);
         data_ip->ip_version = (l3 == IPFIX_PROTO_L3_IPV4) ? 4 : 6;
@@ -806,12 +1094,12 @@ ipfix_send_data_msg(struct dpif_ipfix_exporter *exporter, struct ofpbuf *packet,
         data_ip->ip_class_of_service = flow->nw_tos;
 
         if (l3 == IPFIX_PROTO_L3_IPV4) {
-            struct ipfix_data_record_ipv4 *data_ipv4;
+            struct ipfix_data_record_flow_key_ipv4 *data_ipv4;
             data_ipv4 = ofpbuf_put_zeros(&msg, sizeof *data_ipv4);
             data_ipv4->source_ipv4_address = flow->nw_src;
             data_ipv4->destination_ipv4_address = flow->nw_dst;
         } else {  /* l3 == IPFIX_PROTO_L3_IPV6 */
-            struct ipfix_data_record_ipv6 *data_ipv6;
+            struct ipfix_data_record_flow_key_ipv6 *data_ipv6;
 
             data_ipv6 = ofpbuf_put_zeros(&msg, sizeof *data_ipv6);
             memcpy(data_ipv6->source_ipv6_address, &flow->ipv6_src,
@@ -823,16 +1111,128 @@ ipfix_send_data_msg(struct dpif_ipfix_exporter *exporter, struct ofpbuf *packet,
     }
 
     if (l4 != IPFIX_PROTO_L4_UNKNOWN) {
-        struct ipfix_data_record_tcpudp *data_tcpudp;
+        struct ipfix_data_record_flow_key_tcpudp *data_tcpudp;
 
         data_tcpudp = ofpbuf_put_zeros(&msg, sizeof *data_tcpudp);
         data_tcpudp->source_transport_port = flow->tp_src;
         data_tcpudp->destination_transport_port = flow->tp_dst;
     }
 
-    set_hdr = (struct ipfix_set_header*)((uint8_t*)msg.data + set_hdr_offset);
-    set_hdr->length = htons(msg.size - set_hdr_offset);
+    flow_key->flow_key_msg_part_size = msg.size;
+
+    {
+        struct timeval now;
+        uint64_t layer2_octet_delta_count;
+
+        /* Calculate the total matched octet count by considering as
+         * an approximation that all matched packets have the same
+         * length. */
+        layer2_octet_delta_count = packet_delta_count * ethernet_total_length;
+
+        xgettimeofday(&now);
+        entry->flow_end_timestamp_usec = now.tv_usec + 1000000LL * now.tv_sec;
+        entry->flow_start_timestamp_usec = entry->flow_end_timestamp_usec;
+        entry->packet_delta_count = packet_delta_count;
+        entry->layer2_octet_delta_count = layer2_octet_delta_count;
+    }
+
+    if (l3 != IPFIX_PROTO_L3_UNKNOWN) {
+        uint16_t ip_total_length =
+            ethernet_total_length - ethernet_header_length;
+
+        entry->octet_delta_sum_of_squares =
+            packet_delta_count * ip_total_length * ip_total_length;
+        entry->minimum_ip_total_length = ip_total_length;
+        entry->maximum_ip_total_length = ip_total_length;
+    } else {
+        entry->octet_delta_sum_of_squares = 0;
+        entry->minimum_ip_total_length = 0;
+        entry->maximum_ip_total_length = 0;
+    }
+}
+
+/* Send each single data record in its own data set, to simplify the
+ * implementation by avoiding having to group record by template ID
+ * before sending. */
+static void
+ipfix_put_data_set(uint32_t export_time_sec,
+                   struct ipfix_flow_cache_entry *entry,
+                   enum ipfix_flow_end_reason flow_end_reason,
+                   struct ofpbuf *msg)
+{
+    size_t set_hdr_offset;
+    struct ipfix_set_header *set_hdr;
+
+    set_hdr_offset = msg->size;
+
+    /* Put a Data Set. */
+    set_hdr = ofpbuf_put_zeros(msg, sizeof *set_hdr);
+    set_hdr->set_id = htons(entry->flow_key.template_id);
+
+    /* Copy the flow key part of the data record. */
+
+    ofpbuf_put(msg, entry->flow_key.flow_key_msg_part,
+               entry->flow_key.flow_key_msg_part_size);
+
+    /* Put the non-key part of the data record. */
+
+    {
+        struct ipfix_data_record_aggregated_common *data_aggregated_common;
+        uint64_t export_time_usec, flow_start_delta_usec, flow_end_delta_usec;
+
+        /* Calculate the negative deltas relative to the export time
+         * in seconds sent in the header, not the exact export
+         * time. */
+        export_time_usec = 1000000LL * export_time_sec;
+        flow_start_delta_usec = export_time_usec
+            - entry->flow_start_timestamp_usec;
+        flow_end_delta_usec = export_time_usec
+            - entry->flow_end_timestamp_usec;
+
+        data_aggregated_common = ofpbuf_put_zeros(
+            msg, sizeof *data_aggregated_common);
+        data_aggregated_common->flow_start_delta_microseconds = htonl(
+            flow_start_delta_usec);
+        data_aggregated_common->flow_end_delta_microseconds = htonl(
+            flow_end_delta_usec);
+        data_aggregated_common->packet_delta_count = htonll(
+            entry->packet_delta_count);
+        data_aggregated_common->layer2_octet_delta_count = htonll(
+            entry->layer2_octet_delta_count);
+        data_aggregated_common->flow_end_reason = flow_end_reason;
+    }
+
+    if (entry->octet_delta_sum_of_squares) {  /* IP packet. */
+        struct ipfix_data_record_aggregated_ip *data_aggregated_ip;
+
+        data_aggregated_ip = ofpbuf_put_zeros(
+            msg, sizeof *data_aggregated_ip);
+        data_aggregated_ip->octet_delta_sum_of_squares = htonll(
+            entry->octet_delta_sum_of_squares);
+        data_aggregated_ip->minimum_ip_total_length = htonll(
+            entry->minimum_ip_total_length);
+        data_aggregated_ip->maximum_ip_total_length = htonll(
+            entry->maximum_ip_total_length);
+    }
 
+    set_hdr = (struct ipfix_set_header*)((uint8_t*)msg->data + set_hdr_offset);
+    set_hdr->length = htons(msg->size - set_hdr_offset);
+}
+
+/* Send an IPFIX message with a single data record. */
+static void
+ipfix_send_data_msg(struct dpif_ipfix_exporter *exporter,
+                    uint32_t export_time_sec,
+                    struct ipfix_flow_cache_entry *entry,
+                    enum ipfix_flow_end_reason flow_end_reason)
+{
+    uint64_t msg_stub[DIV_ROUND_UP(MAX_MESSAGE_LEN, 8)];
+    struct ofpbuf msg;
+    ofpbuf_use_stub(&msg, msg_stub, sizeof msg_stub);
+
+    ipfix_init_header(export_time_sec, exporter->seq_number++,
+                      entry->flow_key.obs_domain_id, &msg);
+    ipfix_put_data_set(export_time_sec, entry, flow_end_reason, &msg);
     ipfix_send_msg(exporter->collectors, &msg);
 
     ofpbuf_uninit(&msg);
@@ -844,14 +1244,13 @@ dpif_ipfix_sample(struct dpif_ipfix_exporter *exporter,
                   uint64_t packet_delta_count, uint32_t obs_domain_id,
                   uint32_t obs_point_id)
 {
-    time_t now = time_wall();
-    if ((exporter->last_template_set_time + IPFIX_TEMPLATE_INTERVAL) <= now) {
-        ipfix_send_template_msg(exporter, obs_domain_id);
-        exporter->last_template_set_time = now;
-    }
+    struct ipfix_flow_cache_entry *entry;
 
-    ipfix_send_data_msg(exporter, packet, flow, packet_delta_count,
-                        obs_domain_id, obs_point_id);
+    /* Create a flow cache entry from the sample. */
+    entry = xmalloc(sizeof *entry);
+    ipfix_cache_entry_init(entry, packet, flow, packet_delta_count,
+                           obs_domain_id, obs_point_id);
+    ipfix_cache_update(exporter, entry);
 }
 
 void
@@ -890,3 +1289,130 @@ dpif_ipfix_flow_sample(struct dpif_ipfix *di, struct ofpbuf *packet,
     }
     ovs_mutex_unlock(&mutex);
 }
+
+static void
+dpif_ipfix_cache_expire(struct dpif_ipfix_exporter *exporter,
+                        bool forced_end, const uint64_t export_time_usec,
+                        const uint32_t export_time_sec)
+{
+    struct ipfix_flow_cache_entry *entry, *next_entry;
+    uint64_t max_flow_start_timestamp_usec;
+    bool template_msg_sent = false;
+    enum ipfix_flow_end_reason flow_end_reason;
+
+    if (list_is_empty(&exporter->cache_flow_start_timestamp_list)) {
+        return;
+    }
+
+    max_flow_start_timestamp_usec = export_time_usec -
+        1000000LL * exporter->cache_active_timeout;
+
+    LIST_FOR_EACH_SAFE (entry, next_entry, cache_flow_start_timestamp_list_node,
+                        &exporter->cache_flow_start_timestamp_list) {
+        if (forced_end) {
+            flow_end_reason = FORCED_END;
+        } else if (entry->flow_start_timestamp_usec
+                   <= max_flow_start_timestamp_usec) {
+            flow_end_reason = ACTIVE_TIMEOUT;
+        } else if (hmap_count(&exporter->cache_flow_key_map)
+                   > exporter->cache_max_flows) {
+            /* Enforce exporter->cache_max_flows. */
+            flow_end_reason = LACK_OF_RESOURCES;
+        } else {
+            /* Remaining flows haven't expired yet. */
+            break;
+        }
+
+        list_remove(&entry->cache_flow_start_timestamp_list_node);
+        hmap_remove(&exporter->cache_flow_key_map,
+                    &entry->flow_key_map_node);
+
+        if (!template_msg_sent
+            && (exporter->last_template_set_time + IPFIX_TEMPLATE_INTERVAL)
+                <= export_time_sec) {
+            ipfix_send_template_msg(exporter, export_time_sec,
+                                    entry->flow_key.obs_domain_id);
+            exporter->last_template_set_time = export_time_sec;
+            template_msg_sent = true;
+        }
+
+        /* XXX: Group multiple data records for the same obs domain id
+         * into the same message. */
+        ipfix_send_data_msg(exporter, export_time_sec, entry, flow_end_reason);
+        free(entry);
+    }
+}
+
+static void
+get_export_time_now(uint64_t *export_time_usec, uint32_t *export_time_sec)
+{
+    struct timeval export_time;
+    xgettimeofday(&export_time);
+
+    *export_time_usec = export_time.tv_usec + 1000000LL * export_time.tv_sec;
+
+    /* The IPFIX start and end deltas are negative deltas relative to
+     * the export time, so set the export time 1 second off to
+     * calculate those deltas. */
+    if (export_time.tv_usec == 0) {
+        *export_time_sec = export_time.tv_sec;
+    } else {
+        *export_time_sec = export_time.tv_sec + 1;
+    }
+}
+
+static void
+dpif_ipfix_cache_expire_now(struct dpif_ipfix_exporter *exporter,
+                            bool forced_end)
+{
+    uint64_t export_time_usec;
+    uint32_t export_time_sec;
+
+    get_export_time_now(&export_time_usec, &export_time_sec);
+    dpif_ipfix_cache_expire(exporter, forced_end, export_time_usec,
+                            export_time_sec);
+}
+
+void
+dpif_ipfix_run(struct dpif_ipfix *di) OVS_EXCLUDED(mutex)
+{
+    uint64_t export_time_usec;
+    uint32_t export_time_sec;
+    struct dpif_ipfix_flow_exporter_map_node *flow_exporter_node;
+
+    ovs_mutex_lock(&mutex);
+    get_export_time_now(&export_time_usec, &export_time_sec);
+    if (di->bridge_exporter.probability > 0) {  /* Bridge exporter enabled. */
+      dpif_ipfix_cache_expire(
+          &di->bridge_exporter.exporter, false, export_time_usec,
+          export_time_sec);
+    }
+    HMAP_FOR_EACH (flow_exporter_node, node, &di->flow_exporter_map) {
+        dpif_ipfix_cache_expire(
+            &flow_exporter_node->exporter.exporter, false, export_time_usec,
+            export_time_sec);
+    }
+    ovs_mutex_unlock(&mutex);
+}
+
+void
+dpif_ipfix_wait(struct dpif_ipfix *di) OVS_EXCLUDED(mutex)
+{
+    long long int next_timeout_msec = LLONG_MAX;
+    struct dpif_ipfix_flow_exporter_map_node *flow_exporter_node;
+
+    ovs_mutex_lock(&mutex);
+    if (di->bridge_exporter.probability > 0) {  /* Bridge exporter enabled. */
+        if (ipfix_cache_next_timeout_msec(
+                &di->bridge_exporter.exporter, &next_timeout_msec)) {
+            poll_timer_wait_until(next_timeout_msec);
+        }
+    }
+    HMAP_FOR_EACH (flow_exporter_node, node, &di->flow_exporter_map) {
+        if (ipfix_cache_next_timeout_msec(
+                &flow_exporter_node->exporter.exporter, &next_timeout_msec)) {
+            poll_timer_wait_until(next_timeout_msec);
+        }
+    }
+    ovs_mutex_unlock(&mutex);
+}
index c050dba..6ebf8b0 100644 (file)
@@ -41,4 +41,7 @@ void dpif_ipfix_flow_sample(struct dpif_ipfix *, struct ofpbuf *,
                             const struct flow *, uint32_t, uint16_t, uint32_t,
                             uint32_t);
 
+void dpif_ipfix_run(struct dpif_ipfix *);
+void dpif_ipfix_wait(struct dpif_ipfix *);
+
 #endif /* ofproto/ofproto-dpif-ipfix.h */
index b387b94..158887f 100644 (file)
@@ -280,6 +280,7 @@ dpif_sflow_clear__(struct dpif_sflow *ds) OVS_REQUIRES(mutex)
 {
     if (ds->sflow_agent) {
         sfl_agent_release(ds->sflow_agent);
+        free(ds->sflow_agent);
         ds->sflow_agent = NULL;
     }
     collectors_destroy(ds->collectors);
@@ -317,7 +318,7 @@ dpif_sflow_create(void)
     struct dpif_sflow *ds;
 
     if (ovsthread_once_start(&once)) {
-        ovs_mutex_init(&mutex, PTHREAD_MUTEX_RECURSIVE);
+        ovs_mutex_init_recursive(&mutex);
         ovsthread_once_done(&once);
     }
 
index 2420865..db78af3 100644 (file)
@@ -123,9 +123,9 @@ udpif_create(struct dpif_backer *backer, struct dpif *dpif)
     list_init(&udpif->upcalls);
     list_init(&udpif->fmbs);
     atomic_init(&udpif->reval_seq, 0);
-    ovs_mutex_init(&udpif->drop_key_mutex, PTHREAD_MUTEX_NORMAL);
-    ovs_mutex_init(&udpif->upcall_mutex, PTHREAD_MUTEX_NORMAL);
-    ovs_mutex_init(&udpif->fmb_mutex, PTHREAD_MUTEX_NORMAL);
+    ovs_mutex_init(&udpif->drop_key_mutex);
+    ovs_mutex_init(&udpif->upcall_mutex);
+    ovs_mutex_init(&udpif->fmb_mutex);
 
     return udpif;
 }
@@ -219,7 +219,7 @@ udpif_recv_set(struct udpif *udpif, size_t n_handlers, bool enable)
             handler->udpif = udpif;
             list_init(&handler->upcalls);
             xpthread_cond_init(&handler->wake_cond, NULL);
-            ovs_mutex_init(&handler->mutex, PTHREAD_MUTEX_NORMAL);
+            ovs_mutex_init(&handler->mutex);
             xpthread_create(&handler->thread, NULL, udpif_miss_handler, handler);
         }
         xpthread_create(&udpif->dispatcher, NULL, udpif_dispatcher, udpif);
@@ -554,30 +554,30 @@ recv_upcalls(struct udpif *udpif)
                     }
                 }
             }
-           hash =  mhash_finish(hash, n_bytes);
-
-           handler = &udpif->handlers[hash % udpif->n_handlers];
-
-           ovs_mutex_lock(&handler->mutex);
-           if (handler->n_upcalls < MAX_QUEUE_LENGTH) {
-               list_push_back(&handler->upcalls, &upcall->list_node);
-               handler->n_upcalls++;
-               xpthread_cond_signal(&handler->wake_cond);
-               ovs_mutex_unlock(&handler->mutex);
-               if (!VLOG_DROP_DBG(&rl)) {
-                   struct ds ds = DS_EMPTY_INITIALIZER;
-
-                   odp_flow_key_format(upcall->dpif_upcall.key,
-                                       upcall->dpif_upcall.key_len,
-                                       &ds);
-                   VLOG_DBG("dispatcher: miss enqueue (%s)", ds_cstr(&ds));
-                   ds_destroy(&ds);
-               }
-           } else {
-               ovs_mutex_unlock(&handler->mutex);
-               COVERAGE_INC(miss_queue_overflow);
-               upcall_destroy(upcall);
-           }
+            hash =  mhash_finish(hash, n_bytes);
+
+            handler = &udpif->handlers[hash % udpif->n_handlers];
+
+            ovs_mutex_lock(&handler->mutex);
+            if (handler->n_upcalls < MAX_QUEUE_LENGTH) {
+                list_push_back(&handler->upcalls, &upcall->list_node);
+                handler->n_upcalls++;
+                xpthread_cond_signal(&handler->wake_cond);
+                ovs_mutex_unlock(&handler->mutex);
+                if (!VLOG_DROP_DBG(&rl)) {
+                    struct ds ds = DS_EMPTY_INITIALIZER;
+
+                    odp_flow_key_format(upcall->dpif_upcall.key,
+                                        upcall->dpif_upcall.key_len,
+                                        &ds);
+                    VLOG_DBG("dispatcher: miss enqueue (%s)", ds_cstr(&ds));
+                    ds_destroy(&ds);
+                }
+            } else {
+                ovs_mutex_unlock(&handler->mutex);
+                COVERAGE_INC(miss_queue_overflow);
+                upcall_destroy(upcall);
+            }
         } else {
             ovs_mutex_lock(&udpif->upcall_mutex);
             if (udpif->n_upcalls < MAX_QUEUE_LENGTH) {
index f742060..f597672 100644 (file)
@@ -66,7 +66,7 @@ struct upcall {
     /* Raw upcall plus data for keeping track of the memory backing it. */
     struct dpif_upcall dpif_upcall; /* As returned by dpif_recv() */
     struct ofpbuf upcall_buf;       /* Owns some data in 'dpif_upcall'. */
-    uint64_t upcall_stub[256 / 8];  /* Buffer to reduce need for malloc(). */
+    uint64_t upcall_stub[512 / 8];  /* Buffer to reduce need for malloc(). */
 };
 
 struct upcall *upcall_next(struct udpif *);
index e80ec84..54fabfe 100644 (file)
@@ -1663,6 +1663,25 @@ compose_output_action(struct xlate_ctx *ctx, ofp_port_t ofp_port)
     compose_output_action__(ctx, ofp_port, true);
 }
 
+static void
+xlate_recursively(struct xlate_ctx *ctx, struct rule_dpif *rule)
+    OVS_RELEASES(rule->up.evict)
+{
+    struct rule_dpif *old_rule = ctx->rule;
+
+    if (ctx->xin->resubmit_stats) {
+        rule_credit_stats(rule, ctx->xin->resubmit_stats);
+    }
+
+    ctx->recurse++;
+    ctx->rule = rule;
+    do_xlate_actions(rule->up.ofpacts, rule->up.ofpacts_len, ctx);
+    ctx->rule = old_rule;
+    ctx->recurse--;
+
+    rule_release(rule);
+}
+
 static void
 xlate_table_action(struct xlate_ctx *ctx,
                    ofp_port_t in_port, uint8_t table_id, bool may_packet_in)
@@ -1671,28 +1690,28 @@ xlate_table_action(struct xlate_ctx *ctx,
         struct rule_dpif *rule;
         ofp_port_t old_in_port = ctx->xin->flow.in_port.ofp_port;
         uint8_t old_table_id = ctx->table_id;
+        bool got_rule;
 
         ctx->table_id = table_id;
 
-        /* Look up a flow with 'in_port' as the input port. */
+        /* Look up a flow with 'in_port' as the input port.  Then restore the
+         * original input port (otherwise OFPP_NORMAL and OFPP_IN_PORT will
+         * have surprising behavior). */
         ctx->xin->flow.in_port.ofp_port = in_port;
-        rule_dpif_lookup_in_table(ctx->xbridge->ofproto, &ctx->xin->flow,
-                                  &ctx->xout->wc, table_id, &rule);
-
-        /* Restore the original input port.  Otherwise OFPP_NORMAL and
-         * OFPP_IN_PORT will have surprising behavior. */
+        got_rule = rule_dpif_lookup_in_table(ctx->xbridge->ofproto,
+                                             &ctx->xin->flow, &ctx->xout->wc,
+                                             table_id, &rule);
         ctx->xin->flow.in_port.ofp_port = old_in_port;
 
         if (ctx->xin->resubmit_hook) {
             ctx->xin->resubmit_hook(ctx->xin, rule, ctx->recurse);
         }
 
-        if (rule == NULL && may_packet_in) {
+        if (got_rule) {
+            xlate_recursively(ctx, rule);
+        } else if (may_packet_in) {
             struct xport *xport;
 
-            /* Makes clang's thread safety analysis happy. */
-            rule_release(rule);
-
             /* XXX
              * check if table configuration flags
              * OFPTC_TABLE_MISS_CONTROLLER, default.
@@ -1704,23 +1723,9 @@ xlate_table_action(struct xlate_ctx *ctx,
                                     ctx->xbridge->miss_rule,
                                     ctx->xbridge->no_packet_in_rule);
             ovs_rwlock_rdlock(&rule->up.evict);
+            xlate_recursively(ctx, rule);
         }
 
-        if (rule && ctx->xin->resubmit_stats) {
-            rule_credit_stats(rule, ctx->xin->resubmit_stats);
-        }
-
-        if (rule) {
-            struct rule_dpif *old_rule = ctx->rule;
-
-            ctx->recurse++;
-            ctx->rule = rule;
-            do_xlate_actions(rule->up.ofpacts, rule->up.ofpacts_len, ctx);
-            ctx->rule = old_rule;
-            ctx->recurse--;
-        }
-        rule_release(rule);
-
         ctx->table_id = old_table_id;
     } else {
         static struct vlog_rate_limit recurse_rl = VLOG_RATE_LIMIT_INIT(1, 1);
@@ -2524,7 +2529,10 @@ actions_output_to_local_port(const struct xlate_ctx *ctx)
 }
 
 /* Translates the 'ofpacts_len' bytes of "struct ofpacts" starting at 'ofpacts'
- * into datapath actions in 'odp_actions', using 'ctx'. */
+ * into datapath actions in 'odp_actions', using 'ctx'.
+ *
+ * The caller must take responsibility for eventually freeing 'xout', with
+ * xlate_out_uninit(). */
 void
 xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
 {
index 4690215..80c7c4c 100644 (file)
@@ -1248,7 +1248,7 @@ construct(struct ofproto *ofproto_)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
     struct shash_node *node, *next;
-    odp_port_t max_ports;
+    uint32_t max_ports;
     int error;
 
     error = open_dpif_backer(ofproto->up.type, &ofproto->backer);
@@ -1257,8 +1257,7 @@ construct(struct ofproto *ofproto_)
     }
 
     max_ports = dpif_get_max_ports(ofproto->backer->dpif);
-    ofproto_init_max_ports(ofproto_, u16_to_ofp(MIN(odp_to_u32(max_ports),
-                                                    ofp_to_u16(OFPP_MAX))));
+    ofproto_init_max_ports(ofproto_, MIN(max_ports, ofp_to_u16(OFPP_MAX)));
 
     ofproto->netflow = NULL;
     ofproto->sflow = NULL;
@@ -1268,20 +1267,20 @@ construct(struct ofproto *ofproto_)
     ofproto->ml = mac_learning_create(MAC_ENTRY_DEFAULT_IDLE_TIME);
     ofproto->mbridge = mbridge_create();
     ofproto->has_bonded_bundles = false;
-    ovs_mutex_init(&ofproto->vsp_mutex, PTHREAD_MUTEX_NORMAL);
+    ovs_mutex_init(&ofproto->vsp_mutex);
 
     classifier_init(&ofproto->facets);
     ofproto->consistency_rl = LLONG_MIN;
 
     list_init(&ofproto->completions);
 
-    ovs_mutex_init(&ofproto->flow_mod_mutex, PTHREAD_MUTEX_NORMAL);
+    ovs_mutex_init(&ofproto->flow_mod_mutex);
     ovs_mutex_lock(&ofproto->flow_mod_mutex);
     list_init(&ofproto->flow_mods);
     ofproto->n_flow_mods = 0;
     ovs_mutex_unlock(&ofproto->flow_mod_mutex);
 
-    ovs_mutex_init(&ofproto->pin_mutex, PTHREAD_MUTEX_NORMAL);
+    ovs_mutex_init(&ofproto->pin_mutex);
     ovs_mutex_lock(&ofproto->pin_mutex);
     list_init(&ofproto->pins);
     ofproto->n_pins = 0;
@@ -1585,6 +1584,9 @@ run(struct ofproto *ofproto_)
     if (ofproto->sflow) {
         dpif_sflow_run(ofproto->sflow);
     }
+    if (ofproto->ipfix) {
+        dpif_ipfix_run(ofproto->ipfix);
+    }
 
     HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
         port_run(ofport);
@@ -1644,6 +1646,9 @@ wait(struct ofproto *ofproto_)
     if (ofproto->sflow) {
         dpif_sflow_wait(ofproto->sflow);
     }
+    if (ofproto->ipfix) {
+        dpif_ipfix_wait(ofproto->ipfix);
+    }
     HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
         port_wait(ofport);
     }
@@ -1905,6 +1910,10 @@ port_modified(struct ofport *port_)
         cfm_set_netdev(port->cfm, port->up.netdev);
     }
 
+    if (port->bfd) {
+        bfd_set_netdev(port->bfd, port->up.netdev);
+    }
+
     if (port->is_tunnel && tnl_port_reconfigure(port, port->up.netdev,
                                                 port->odp_port)) {
         ofproto_dpif_cast(port->up.ofproto)->backer->need_revalidate =
@@ -1969,20 +1978,25 @@ set_ipfix(
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
     struct dpif_ipfix *di = ofproto->ipfix;
+    bool has_options = bridge_exporter_options || flow_exporters_options;
 
-    if (bridge_exporter_options || flow_exporters_options) {
-        if (!di) {
-            di = ofproto->ipfix = dpif_ipfix_create();
-        }
+    if (has_options && !di) {
+        di = ofproto->ipfix = dpif_ipfix_create();
+    }
+
+    if (di) {
+        /* Call set_options in any case to cleanly flush the flow
+         * caches in the last exporters that are to be destroyed. */
         dpif_ipfix_set_options(
             di, bridge_exporter_options, flow_exporters_options,
             n_flow_exporters_options);
-    } else {
-        if (di) {
+
+        if (!has_options) {
             dpif_ipfix_unref(di);
             ofproto->ipfix = NULL;
         }
     }
+
     return 0;
 }
 
@@ -2039,7 +2053,8 @@ set_bfd(struct ofport *ofport_, const struct smap *cfg)
     struct bfd *old;
 
     old = ofport->bfd;
-    ofport->bfd = bfd_configure(old, netdev_get_name(ofport->up.netdev), cfg);
+    ofport->bfd = bfd_configure(old, netdev_get_name(ofport->up.netdev),
+                                cfg, ofport->up.netdev);
     if (ofport->bfd != old) {
         ofproto->backer->need_revalidate = REV_RECONFIGURE;
     }
@@ -2952,6 +2967,8 @@ port_run(struct ofport_dpif *ofport)
     long long int carrier_seq = netdev_get_carrier_resets(ofport->up.netdev);
     bool carrier_changed = carrier_seq != ofport->carrier_seq;
     bool enable = netdev_get_carrier(ofport->up.netdev);
+    bool cfm_enable = false;
+    bool bfd_enable = false;
 
     ofport->carrier_seq = carrier_seq;
 
@@ -2961,16 +2978,20 @@ port_run(struct ofport_dpif *ofport)
         int cfm_opup = cfm_get_opup(ofport->cfm);
 
         cfm_run(ofport->cfm);
-        enable = enable && !cfm_get_fault(ofport->cfm);
+        cfm_enable = !cfm_get_fault(ofport->cfm);
 
         if (cfm_opup >= 0) {
-            enable = enable && cfm_opup;
+            cfm_enable = cfm_enable && cfm_opup;
         }
     }
 
     if (ofport->bfd) {
         bfd_run(ofport->bfd);
-        enable = enable && bfd_forwarding(ofport->bfd);
+        bfd_enable = bfd_forwarding(ofport->bfd);
+    }
+
+    if (ofport->bfd || ofport->cfm) {
+        enable = enable && (cfm_enable || bfd_enable);
     }
 
     if (ofport->bundle) {
@@ -3318,7 +3339,6 @@ handle_flow_miss_with_facet(struct flow_miss *miss, struct facet *facet,
     facet->byte_count += miss->stats.n_bytes;
     facet->prev_byte_count += miss->stats.n_bytes;
 
-    subfacet = subfacet_create(facet, miss);
     want_path = facet->xout.slow ? SF_SLOW_PATH : SF_FAST_PATH;
 
     /* Don't install the flow if it's the result of the "userspace"
@@ -4770,7 +4790,7 @@ bool
 rule_dpif_lookup_in_table(struct ofproto_dpif *ofproto,
                           const struct flow *flow, struct flow_wildcards *wc,
                           uint8_t table_id, struct rule_dpif **rule)
-    OVS_ACQ_RDLOCK((*rule)->up.evict)
+    OVS_TRY_RDLOCK(true, (*rule)->up.evict)
 {
     struct cls_rule *cls_rule;
     struct classifier *cls;
@@ -4827,6 +4847,7 @@ choose_miss_rule(enum ofputil_port_config config, struct rule_dpif *miss_rule,
 
 void
 rule_release(struct rule_dpif *rule)
+    OVS_NO_THREAD_SAFETY_ANALYSIS
 {
     if (rule) {
         ovs_rwlock_unlock(&rule->up.evict);
@@ -4866,7 +4887,7 @@ static enum ofperr
 rule_construct(struct rule *rule_)
 {
     struct rule_dpif *rule = rule_dpif_cast(rule_);
-    ovs_mutex_init(&rule->stats_mutex, PTHREAD_MUTEX_NORMAL);
+    ovs_mutex_init(&rule->stats_mutex);
     ovs_mutex_lock(&rule->stats_mutex);
     rule->packet_count = 0;
     rule->byte_count = 0;
index 6a4ae07..15e58e9 100644 (file)
@@ -91,7 +91,7 @@ void rule_dpif_lookup(struct ofproto_dpif *, const struct flow *,
 bool rule_dpif_lookup_in_table(struct ofproto_dpif *, const struct flow *,
                                struct flow_wildcards *, uint8_t table_id,
                                struct rule_dpif **rule)
-    OVS_ACQ_RDLOCK((*rule)->up.evict);
+    OVS_TRY_RDLOCK(true, (*rule)->up.evict);
 
 void rule_release(struct rule_dpif *rule) OVS_RELEASES(rule->up.evict);
 
index ef4d588..d8b6a79 100644 (file)
@@ -65,8 +65,8 @@ struct ofproto {
     struct shash port_by_name;
     unsigned long *ofp_port_ids;/* Bitmap of used OpenFlow port numbers. */
     struct simap ofp_requests;  /* OpenFlow port number requests. */
-    ofp_port_t alloc_port_no;   /* Last allocated OpenFlow port number. */
-    ofp_port_t max_ports;       /* Max possible OpenFlow port num, plus one. */
+    uint16_t alloc_port_no;     /* Last allocated OpenFlow port number. */
+    uint16_t max_ports;         /* Max possible OpenFlow port num, plus one. */
 
     /* Flow tables. */
     struct oftable *tables;
@@ -114,7 +114,7 @@ struct ofproto {
 };
 
 void ofproto_init_tables(struct ofproto *, int n_tables);
-void ofproto_init_max_ports(struct ofproto *, ofp_port_t max_ports);
+void ofproto_init_max_ports(struct ofproto *, uint16_t max_ports);
 
 struct ofproto *ofproto_lookup(const char *name);
 struct ofport *ofproto_get_port(const struct ofproto *, ofp_port_t ofp_port);
index c8edb2d..709fd07 100644 (file)
@@ -432,12 +432,12 @@ ofproto_create(const char *datapath_name, const char *datapath_type,
     hmap_init(&ofproto->ports);
     shash_init(&ofproto->port_by_name);
     simap_init(&ofproto->ofp_requests);
-    ofproto->max_ports = OFPP_MAX;
+    ofproto->max_ports = ofp_to_u16(OFPP_MAX);
     ofproto->tables = NULL;
     ofproto->n_tables = 0;
     hindex_init(&ofproto->cookies);
     list_init(&ofproto->expirable);
-    ovs_mutex_init(&ofproto->expirable_mutex, PTHREAD_MUTEX_RECURSIVE);
+    ovs_mutex_init_recursive(&ofproto->expirable_mutex);
     ofproto->connmgr = connmgr_create(ofproto, datapath_name, datapath_name);
     ofproto->state = S_OPENFLOW;
     list_init(&ofproto->pending);
@@ -461,7 +461,7 @@ ofproto_create(const char *datapath_name, const char *datapath_type,
 
     /* The "max_ports" member should have been set by ->construct(ofproto).
      * Port 0 is not a valid OpenFlow port, so mark that as unavailable. */
-    ofproto->ofp_port_ids = bitmap_allocate(ofp_to_u16(ofproto->max_ports));
+    ofproto->ofp_port_ids = bitmap_allocate(ofproto->max_ports);
     bitmap_set1(ofproto->ofp_port_ids, 0);
 
     /* Check that hidden tables, if any, are at the end. */
@@ -520,9 +520,9 @@ ofproto_init_tables(struct ofproto *ofproto, int n_tables)
  * Reserved ports numbered OFPP_MAX and higher are special and not subject to
  * the 'max_ports' restriction. */
 void
-ofproto_init_max_ports(struct ofproto *ofproto, ofp_port_t max_ports)
+ofproto_init_max_ports(struct ofproto *ofproto, uint16_t max_ports)
 {
-    ovs_assert(ofp_to_u16(max_ports) <= ofp_to_u16(OFPP_MAX));
+    ovs_assert(max_ports <= ofp_to_u16(OFPP_MAX));
     ofproto->max_ports = max_ports;
 }
 
@@ -1742,32 +1742,28 @@ reinit_ports(struct ofproto *p)
 static ofp_port_t
 alloc_ofp_port(struct ofproto *ofproto, const char *netdev_name)
 {
-    uint16_t max_ports = ofp_to_u16(ofproto->max_ports);
     uint16_t port_idx;
 
     port_idx = simap_get(&ofproto->ofp_requests, netdev_name);
-    if (!port_idx) {
-        port_idx = UINT16_MAX;
-    }
+    port_idx = port_idx ? port_idx : UINT16_MAX;
 
-    if (port_idx >= max_ports
+    if (port_idx >= ofproto->max_ports
         || bitmap_is_set(ofproto->ofp_port_ids, port_idx)) {
-        uint16_t end_port_no = ofp_to_u16(ofproto->alloc_port_no);
-        uint16_t alloc_port_no = end_port_no;
+        uint16_t end_port_no = ofproto->alloc_port_no;
 
         /* Search for a free OpenFlow port number.  We try not to
          * immediately reuse them to prevent problems due to old
          * flows. */
         for (;;) {
-            if (++alloc_port_no >= max_ports) {
-                alloc_port_no = 0;
+            if (++ofproto->alloc_port_no >= ofproto->max_ports) {
+                ofproto->alloc_port_no = 0;
             }
-            if (!bitmap_is_set(ofproto->ofp_port_ids, alloc_port_no)) {
-                port_idx = alloc_port_no;
-                ofproto->alloc_port_no = u16_to_ofp(alloc_port_no);
+            if (!bitmap_is_set(ofproto->ofp_port_ids,
+                               ofproto->alloc_port_no)) {
+                port_idx = ofproto->alloc_port_no;
                 break;
             }
-            if (alloc_port_no == end_port_no) {
+            if (ofproto->alloc_port_no == end_port_no) {
                 return OFPP_NONE;
             }
         }
@@ -1779,7 +1775,7 @@ alloc_ofp_port(struct ofproto *ofproto, const char *netdev_name)
 static void
 dealloc_ofp_port(const struct ofproto *ofproto, ofp_port_t ofp_port)
 {
-    if (ofp_to_u16(ofp_port) < ofp_to_u16(ofproto->max_ports)) {
+    if (ofp_to_u16(ofp_port) < ofproto->max_ports) {
         bitmap_set0(ofproto->ofp_port_ids, ofp_to_u16(ofp_port));
     }
 }
@@ -2103,6 +2099,10 @@ init_ports(struct ofproto *p)
             netdev = ofport_open(p, &ofproto_port, &pp);
             if (netdev) {
                 ofport_install(p, netdev, &pp);
+                if (ofp_to_u16(ofproto_port.ofp_port) < p->max_ports) {
+                    p->alloc_port_no = MAX(p->alloc_port_no,
+                                           ofp_to_u16(ofproto_port.ofp_port));
+                }
             }
         }
     }
@@ -2462,8 +2462,8 @@ ofproto_check_ofpacts(struct ofproto *ofproto,
     enum ofperr error;
     uint32_t mid;
 
-    error = ofpacts_check(ofpacts, ofpacts_len, flow, ofproto->max_ports,
-                          table_id);
+    error = ofpacts_check(ofpacts, ofpacts_len, flow,
+                          u16_to_ofp(ofproto->max_ports), table_id);
     if (error) {
         return error;
     }
@@ -2500,7 +2500,7 @@ handle_packet_out(struct ofconn *ofconn, const struct ofp_header *oh)
     if (error) {
         goto exit_free_ofpacts;
     }
-    if (ofp_to_u16(po.in_port) >= ofp_to_u16(p->max_ports)
+    if (ofp_to_u16(po.in_port) >= p->max_ports
         && ofp_to_u16(po.in_port) < ofp_to_u16(OFPP_MAX)) {
         error = OFPERR_OFPBRC_BAD_PORT;
         goto exit_free_ofpacts;
@@ -3436,7 +3436,7 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
     rule->flow_cookie = fm->new_cookie;
     rule->created = rule->modified = rule->used = time_msec();
 
-    ovs_mutex_init(&rule->timeout_mutex, OVS_MUTEX_ADAPTIVE);
+    ovs_mutex_init(&rule->timeout_mutex);
     ovs_mutex_lock(&rule->timeout_mutex);
     rule->idle_timeout = fm->idle_timeout;
     rule->hard_timeout = fm->hard_timeout;
@@ -3554,7 +3554,7 @@ modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
 
         /* Verify actions. */
         error = ofpacts_check(fm->ofpacts, fm->ofpacts_len, &fm->match.flow,
-                              ofproto->max_ports, rule->table_id);
+                              u16_to_ofp(ofproto->max_ports), rule->table_id);
         if (error) {
             return error;
         }
@@ -5124,7 +5124,7 @@ choose_rule_to_evict(struct oftable *table, struct rule **rulep)
      *     group has no evictable rules.
      *
      *   - The outer loop can exit only if table's 'max_flows' is all filled up
-     *     by unevictable rules'. */
+     *     by unevictable rules. */
     HEAP_FOR_EACH (evg, size_node, &table->eviction_groups_by_size) {
         struct rule *rule;
 
index 516bbad..9adda2c 100644 (file)
@@ -76,11 +76,15 @@ struct ofproto_ipfix_bridge_exporter_options {
     uint32_t sampling_rate;
     uint32_t obs_domain_id;  /* Bridge-wide Observation Domain ID. */
     uint32_t obs_point_id;  /* Bridge-wide Observation Point ID. */
+    uint32_t cache_active_timeout;
+    uint32_t cache_max_flows;
 };
 
 struct ofproto_ipfix_flow_exporter_options {
     uint32_t collector_set_id;
     struct sset targets;
+    uint32_t cache_active_timeout;
+    uint32_t cache_max_flows;
 };
 
 struct ofproto_stp_settings {
index 0ba0066..202358b 100644 (file)
@@ -457,7 +457,6 @@ tnl_find(struct tnl_match *match_) OVS_REQ_RDLOCK(rwlock)
     }
 
     /* Flow-based everything */
-    match.ip_src = 0;
     match.ip_src_flow = true;
     tnl_port = tnl_find_exact(&match);
     if (tnl_port) {
index fb8b1d3..0b2b7cc 100644 (file)
@@ -20,9 +20,9 @@ AT_CHECK([ovs-appctl bfd/show $1 | sed -e '/Time:/d' | sed -e '/Discriminator/d'
 m4_define([BFD_CHECK_TX], [
 AT_CHECK([ovs-appctl bfd/show $1 | sed -n '/TX Interval/p'],[0],
 [dnl
-       TX Interval: Approx 1000ms
-       Local Minimum TX Interval: $2
-       Remote Minimum TX Interval: $3
+       TX Interval: Approx $2
+       Local Minimum TX Interval: $3
+       Remote Minimum TX Interval: $4
 ])
 ])
 
@@ -30,8 +30,8 @@ m4_define([BFD_CHECK_RX], [
 AT_CHECK([ovs-appctl bfd/show $1 | sed -n '/RX Interval/p'],[0],
 [dnl
        RX Interval: Approx $2
-       Local Minimum RX Interval: $2
-       Remote Minimum RX Interval: $3
+       Local Minimum RX Interval: $3
+       Remote Minimum RX Interval: $4
 ])
 ])
 AT_SETUP([bfd - basic config on different bridges])
@@ -202,14 +202,14 @@ BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [N
 #Edit the min Tx value.
 AT_CHECK([ovs-vsctl set interface p0 bfd:min_tx=200])
 for i in `seq 0 20`; do ovs-appctl time/warp 100; done
-BFD_CHECK_TX([p0], [200ms], [100ms])
-BFD_CHECK_TX([p1], [100ms], [200ms])
+BFD_CHECK_TX([p0], [1000ms], [200ms], [100ms])
+BFD_CHECK_TX([p1], [1000ms], [100ms], [200ms])
 
 #Edit the min Rx value.
 AT_CHECK([ovs-vsctl set interface p1 bfd:min_rx=300])
 for i in `seq 0 20`; do ovs-appctl time/warp 100; done
-BFD_CHECK_RX([p1], [300ms], [1000ms])
-BFD_CHECK_RX([p0], [1000ms], [300ms])
+BFD_CHECK_RX([p1], [300ms], [300ms], [1000ms])
+BFD_CHECK_RX([p0], [1000ms], [1000ms], [300ms])
 
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -247,3 +247,514 @@ This flow is handled by the userspace slow path because it:
 
 OVS_VSWITCHD_STOP
 AT_CLEANUP
+
+# Tests below are for bfd decay features.
+AT_SETUP([bfd - bfd decay])
+OVS_VSWITCHD_START([add-br br1 -- set bridge br1 datapath-type=dummy -- \
+                    add-port br1 p1 -- set Interface p1 type=patch \
+                    options:peer=p0 ofport_request=2 -- \
+                    add-port br0 p0 -- set Interface p0 type=patch \
+                    options:peer=p1 ofport_request=1 -- \
+                    set Interface p0 bfd:enable=true bfd:min_tx=300 bfd:min_rx=300 bfd:decay_min_rx=3000 -- \
+                    set Interface p1 bfd:enable=true bfd:min_tx=500 bfd:min_rx=500])
+
+ovs-appctl time/stop
+
+# wait for local session state to go from down to up.
+for i in `seq 0 1`; do ovs-appctl time/warp 500; done
+BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [init], [No Diagnostic])
+
+
+# Test-1 BFD decay: decay to decay_min_rx
+# bfd:decay_min_rx is set to 3000ms after the local state of p0 goes up,
+# so for the first 2500ms, there should be no change.
+for i in `seq 0 4`; do ovs-appctl time/warp 500; done
+BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
+BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
+BFD_CHECK_RX([p0], [500ms], [300ms], [500ms])
+
+# advance the clock by 500ms.
+ovs-appctl time/warp 500
+# now at 3000ms, min_rx should decay to 3000ms and there should be
+# poll sequence flags.
+BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [final], [up], [No Diagnostic])
+BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [poll], [up], [No Diagnostic])
+BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
+BFD_CHECK_RX([p0], [3000ms], [3000ms], [500ms])
+
+# since the tx_min of p0 is still 500ms, after 500ms from decay,
+# the control message will be sent from p0 to p1, and p1 'flag'
+# will go back to none.
+ovs-appctl time/warp 500
+BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
+
+# the rx_min of p0 is 3000ms now, and p1 will send next control message
+# 3000ms after decay. so, advance clock by 2500ms to make that happen.
+for i in `seq 0 4`; do ovs-appctl time/warp 500; done
+BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
+BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
+BFD_CHECK_RX([p0], [3000ms], [3000ms], [500ms])
+# End of Test-1 ###############################################################
+
+
+# Test-2 BFD decay: go back to cfg_min_rx when there is traffic
+# receive packet at 1/100ms rate for 3000ms.
+for i in `seq 0 30`
+do
+    ovs-appctl time/warp 100
+    AT_CHECK([ovs-ofctl packet-out br1 3 2  "90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202"],
+             [0], [stdout], [])
+done
+# after a decay interval (3000ms), the p0 min_rx will go back to
+# cfg_min_rx. there should be poll sequence flags.
+BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [final], [up], [No Diagnostic])
+BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [poll], [up], [No Diagnostic])
+BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
+BFD_CHECK_RX([p0], [500ms], [300ms], [500ms])
+
+# 500ms later, both direction will send control messages,
+# and their 'flag' will go back to none.
+ovs-appctl time/warp 500
+BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
+BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
+BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
+BFD_CHECK_RX([p0], [500ms], [300ms], [500ms])
+# End of Test-2 ###############################################################
+
+
+# Test-3 BFD decay: go back to cfg_min_rx when decay_min_rx is changed
+# advance the clock by 2500ms to 3000m after restore of
+# min_rx. p0 is decayed, and there should be the poll sequence flags.
+for i in `seq 0 4`; do ovs-appctl time/warp 500; done
+BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [final], [up], [No Diagnostic])
+BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [poll], [up], [No Diagnostic])
+BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
+BFD_CHECK_RX([p0], [3000ms], [3000ms], [500ms])
+
+# advance the clock, to make 'flag' go back to none.
+for i in `seq 0 5`; do ovs-appctl time/warp 500; done
+BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
+BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
+
+# change decay_min_rx to 1000ms.
+# for decay_min_rx < 2000ms, the decay detection time is set to 2000ms.
+# this should firstly reset the min_rx and start poll sequence.
+AT_CHECK([ovs-vsctl set Interface p0 bfd:decay_min_rx=1000])
+BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [final], [up], [No Diagnostic])
+BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [poll], [up], [No Diagnostic])
+BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
+BFD_CHECK_RX([p0], [500ms], [300ms], [500ms])
+
+# for the following 1500ms, there should be no decay,
+# since the decay_detect_time is set to 2000ms.
+for i in `seq 0 2`
+do
+    ovs-appctl time/warp 500
+    BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
+    BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
+    BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
+    BFD_CHECK_RX([p0], [500ms], [300ms], [500ms])
+done
+
+ovs-appctl time/warp 500
+# at 2000ms, decay should happen and there should be the poll sequence flags.
+BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [final], [up], [No Diagnostic])
+BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [poll], [up], [No Diagnostic])
+BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
+BFD_CHECK_RX([p0], [1000ms], [1000ms], [500ms])
+# advance the clock, so 'flag' go back to none.
+for i in `seq 0 4`; do ovs-appctl time/warp 500; done
+# End of Test-3 ###############################################################
+
+
+# Test-4 BFD decay: set min_rx to 800ms.
+# this should firstly reset the min_rx and then re-decay to 1000ms.
+AT_CHECK([ovs-vsctl set Interface p0 bfd:min_rx=800])
+BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [final], [up], [No Diagnostic])
+BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [poll], [up], [No Diagnostic])
+BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
+BFD_CHECK_RX([p0], [800ms], [800ms], [500ms])
+
+# for the following 1600ms, there should be no decay,
+# since the decay detection time is set to 2000ms.
+for i in `seq 0 1`
+do
+    ovs-appctl time/warp 800
+    BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
+    BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
+    BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
+    BFD_CHECK_RX([p0], [800ms], [800ms], [500ms])
+done
+
+ovs-appctl time/warp 400
+# at 2000ms, decay should happen and there should be the poll sequence flags.
+BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [final], [up], [No Diagnostic])
+BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [poll], [up], [No Diagnostic])
+BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
+BFD_CHECK_RX([p0], [1000ms], [1000ms], [500ms])
+# advance the clock, so 'flag' go back to none.
+for i in `seq 0 4`; do ovs-appctl time/warp 500; done
+# End of Test-4 ###############################################################
+
+
+# Test-5 BFD decay: set min_rx to 300ms and decay_min_rx to 5000ms together.
+AT_CHECK([ovs-vsctl set Interface p0 bfd:min_rx=300 bfd:decay_min_rx=5000])
+BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [final], [up], [No Diagnostic])
+BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [poll], [up], [No Diagnostic])
+BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
+BFD_CHECK_RX([p0], [500ms], [300ms], [500ms])
+
+# for decay_min_rx > 2000ms, the decay detection time is set to
+# decay_min_rx (5000ms).
+# for the following 4500ms, there should be no decay,
+# since the decay detection time is set to 5000ms.
+for i in `seq 0 8`
+do
+    ovs-appctl time/warp 500
+    BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
+    BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
+    BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
+    BFD_CHECK_RX([p0], [500ms], [300ms], [500ms])
+done
+
+ovs-appctl time/warp 500
+# at 5000ms, decay should happen and there should be the poll sequence flags.
+BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [final], [up], [No Diagnostic])
+BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [poll], [up], [No Diagnostic])
+BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
+BFD_CHECK_RX([p0], [5000ms], [5000ms], [500ms])
+# advance the clock, to make 'flag' go back to none.
+for i in `seq 0 9`; do ovs-appctl time/warp 500; done
+# End of Test-5 ###############################################################
+
+
+# Test-6 BFD decay: set decay_min_rx to 0 to disable bfd decay.
+AT_CHECK([ovs-vsctl set Interface p0 bfd:decay_min_rx=0])
+# min_rx is reset, and there should be the poll sequence flags.
+BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [final], [up], [No Diagnostic])
+BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [poll], [up], [No Diagnostic])
+BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
+BFD_CHECK_RX([p0], [500ms], [300ms], [500ms])
+for i in `seq 0 20`
+do
+    ovs-appctl time/warp 500
+    BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
+    BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
+    BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
+    BFD_CHECK_RX([p0], [500ms], [300ms], [500ms])
+done
+# End of Test-6 ################################################################
+
+
+# Test-7 BFD decay: rmt_min_tx is greater than decay_min_rx
+AT_CHECK([ovs-vsctl set Interface p0 bfd:decay_min_rx=3000 -- set interface p1 bfd:min_tx=5000])
+# there will be poll sequences from both sides. and it is hard to determine the
+# order. so just skip 10000ms and check the RX/TX. at that time, p0 should be in decay already.
+for i in `seq 0 19`; do echo $i; ovs-appctl bfd/show; ovs-appctl time/warp 500; done
+BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
+BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
+BFD_CHECK_TX([p0], [500ms], [300ms], [5000ms])
+BFD_CHECK_RX([p0], [5000ms], [3000ms], [500ms])
+# then, there should be no change of status,
+for i in `seq 0 9`
+do
+    ovs-appctl time/warp 500
+    BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
+    BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
+    BFD_CHECK_TX([p0], [500ms], [300ms], [5000ms])
+    BFD_CHECK_RX([p0], [5000ms], [3000ms], [500ms])
+done
+# reset the p1's min_tx to 500ms.
+AT_CHECK([ovs-vsctl set Interface p1 bfd:min_tx=500])
+# check the poll sequence. since p0 has been in decay, now the RX will show 3000ms.
+BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [final], [up], [No Diagnostic])
+BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [poll], [up], [No Diagnostic])
+BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
+BFD_CHECK_RX([p0], [3000ms], [3000ms], [500ms])
+# advance the clock by 3000ms, at that time, p1 will send the control packets.
+# then there will be no poll flags.
+for i in `seq 0 5`; do ovs-appctl time/warp 500; done
+BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
+BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
+BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
+BFD_CHECK_RX([p0], [3000ms], [3000ms], [500ms])
+# End of Test-7 ###############################################################
+
+
+# Test-8 BFD decay: state up->down->up.
+# turn bfd off on p1
+AT_CHECK([ovs-vsctl set Interface p1 bfd:enable=false])
+
+# check the state change of bfd on p0. After 9000 ms (3 min_rx intervals)
+for i in `seq 0 8`; do ovs-appctl time/warp 1000; done
+BFD_CHECK([p0], [false], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic])
+BFD_CHECK_TX([p0], [1000ms], [1000ms], [0ms])
+BFD_CHECK_RX([p0], [300ms], [300ms], [1ms])
+
+# resume the bfd on p1. the bfd should not go to decay mode direclty.
+AT_CHECK([ovs-vsctl set Interface p1 bfd:enable=true])
+for i in `seq 0 1`; do ovs-appctl time/warp 500; done
+BFD_CHECK([p0], [true], [false], [none], [up], [Control Detection Time Expired], [none], [up], [No Diagnostic])
+BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
+BFD_CHECK_RX([p0], [500ms], [300ms], [500ms])
+
+# since the decay_min_rx is still 3000ms, so after 3000ms, there should be the decay and poll sequence.
+for i in `seq 0 5`; do ovs-appctl time/warp 500; done
+BFD_CHECK([p0], [true], [false], [none], [up], [Control Detection Time Expired], [final], [up], [No Diagnostic])
+BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [poll], [up], [Control Detection Time Expired])
+BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
+BFD_CHECK_RX([p0], [3000ms], [3000ms], [500ms])
+# End of Test-8 ################################################################
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+# Tests below are for bfd forwarding_if_rx feature.
+# forwarding_if_rx Test1: bfd is enabled on one end of link.
+AT_SETUP([bfd - bfd forwarding_if_rx 1])
+OVS_VSWITCHD_START([add-br br1 -- set bridge br1 datapath-type=dummy -- \
+                    add-port br1 p1 -- set Interface p1 type=patch \
+                    options:peer=p0 ofport_request=2 -- \
+                    add-port br0 p0 -- set Interface p0 type=patch \
+                    options:peer=p1 ofport_request=1 -- \
+                    set Interface p0 bfd:enable=true bfd:min_tx=500 bfd:min_rx=500 -- \
+                    add-port br1 p2 -- set Interface p2 type=internal ofport_request=3])
+
+ovs-appctl time/stop
+# check the inital status.
+BFD_CHECK([p0], [false], [false], [none], [down], [No Diagnostic], [none], [down], [No Diagnostic])
+BFD_CHECK_TX([p0], [1000ms], [1000ms], [0ms])
+BFD_CHECK_RX([p0], [500ms], [500ms], [1ms])
+
+# enable forwarding_if_rx.
+AT_CHECK([ovs-vsctl set Interface p0 bfd:forwarding_if_rx=true], [0])
+
+# there should be no change of forwarding flag, since
+# there is no traffic.
+for i in `seq 0 3`
+do
+    ovs-appctl time/warp 500
+    BFD_CHECK([p0], [false], [false], [none], [down], [No Diagnostic], [none], [down], [No Diagnostic])
+done
+
+# receive one packet.
+AT_CHECK([ovs-ofctl packet-out br1 3 2  "90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202"],
+             [0], [stdout], [])
+for i in `seq 0 14`
+do
+    ovs-appctl time/warp 100
+    # the forwarding flag should be true, since there is data received.
+    BFD_CHECK([p0], [true], [false], [none], [down], [No Diagnostic], [none], [down], [No Diagnostic])
+    BFD_CHECK_TX([p0], [1000ms], [1000ms], [0ms])
+    BFD_CHECK_RX([p0], [500ms], [500ms], [1ms])
+done
+
+# Stop sending packets for 1000ms.
+for i in `seq 0 9`; do ovs-appctl time/warp 100; done
+BFD_CHECK([p0], [false], [false], [none], [down], [No Diagnostic], [none], [down], [No Diagnostic])
+BFD_CHECK_TX([p0], [1000ms], [1000ms], [0ms])
+BFD_CHECK_RX([p0], [500ms], [500ms], [1ms])
+
+# receive packet at 1/100ms rate for 1000ms.
+for i in `seq 0 9`
+do
+    ovs-appctl time/warp 100
+    AT_CHECK([ovs-ofctl packet-out br1 3 2  "90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202"],
+             [0], [stdout], [])
+done
+# the forwarding flag should be true, since there is data received.
+BFD_CHECK([p0], [true], [false], [none], [down], [No Diagnostic], [none], [down], [No Diagnostic])
+BFD_CHECK_TX([p0], [1000ms], [1000ms], [0ms])
+BFD_CHECK_RX([p0], [500ms], [500ms], [1ms])
+
+# reset bfd forwarding_if_rx.
+AT_CHECK([ovs-vsctl set Interface p0 bfd:forwarding_if_rx=false], [0])
+# forwarding flag should turn to false since the STATE is DOWN.
+BFD_CHECK([p0], [false], [false], [none], [down], [No Diagnostic], [none], [down], [No Diagnostic])
+BFD_CHECK_TX([p0], [1000ms], [1000ms], [0ms])
+BFD_CHECK_RX([p0], [500ms], [500ms], [1ms])
+
+AT_CHECK([ovs-vsctl del-br br1], [0], [ignore])
+AT_CLEANUP
+
+# forwarding_if_rx Test2: bfd is enabled on both ends of link.
+AT_SETUP([bfd - bfd forwarding_if_rx 2])
+OVS_VSWITCHD_START([add-br br1 -- set bridge br1 datapath-type=dummy -- \
+                    add-port br1 p1 -- set Interface p1 type=patch \
+                    options:peer=p0 ofport_request=2 -- \
+                    add-port br0 p0 -- set Interface p0 type=patch \
+                    options:peer=p1 ofport_request=1 -- \
+                    set Interface p0 bfd:enable=true bfd:min_tx=500 bfd:min_rx=500 -- \
+                    set Interface p1 bfd:enable=true bfd:min_tx=300 bfd:min_rx=300 -- \
+                    add-port br1 p2 -- set Interface p2 type=internal ofport_request=3])
+
+ovs-appctl time/stop
+# advance the clock, to stablize the states.
+for i in `seq 0 9`; do ovs-appctl time/warp 500; done
+
+# enable forwarding_if_rx.
+AT_CHECK([ovs-vsctl set Interface p0 bfd:forwarding_if_rx=true], [0])
+
+# there should be no change of the forwarding flag, since
+# the bfd on both ends is already up.
+for i in `seq 0 5`
+do
+    ovs-appctl time/warp 500
+    BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
+done
+
+# stop the bfd on one side.
+AT_CHECK([ovs-vsctl set Interface p1 bfd:enable=false], [0])
+# for within 1500ms, the detection timer is not out.
+# there is no change to status.
+for i in `seq 0 1`
+do
+    ovs-appctl time/warp 500
+    BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
+    for i in `seq 0 5`
+    do
+        AT_CHECK([ovs-ofctl packet-out br1 3 2  "90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202"],
+                 [0], [stdout], [])
+    done
+done
+
+# at 1500ms, the STATE should go DOWN, due to Control Detection Time Expired.
+# but forwarding flag should be still true.
+ovs-appctl time/warp 500
+BFD_CHECK([p0], [true], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic])
+
+# receive packet at 1/100ms rate for 1000ms.
+for i in `seq 0 9`
+do
+    AT_CHECK([ovs-ofctl packet-out br1 3 2  "90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202"],
+             [0], [stdout], [])
+    ovs-appctl time/warp 100
+    # the forwarding flag should always be true during this time.
+    BFD_CHECK([p0], [true], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic])
+done
+
+# reset bfd forwarding_if_rx.
+AT_CHECK([ovs-vsctl set Interface p0 bfd:forwarding_if_rx=false], [0])
+# forwarding flag should turn to false since the STATE is DOWN.
+BFD_CHECK([p0], [false], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic])
+BFD_CHECK_TX([p0], [1000ms], [1000ms], [0ms])
+BFD_CHECK_RX([p0], [500ms], [500ms], [1ms])
+
+# re-enable bfd on the other end. the states should be up.
+AT_CHECK([ovs-vsctl set Interface p1 bfd:enable=true bfd:min_tx=300 bfd:min_rx=300])
+# advance the clock, to stablize the states.
+for i in `seq 0 9`; do ovs-appctl time/warp 500; done
+BFD_CHECK([p0], [true], [false], [none], [up], [Control Detection Time Expired], [none], [up], [No Diagnostic])
+BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [Control Detection Time Expired])
+BFD_CHECK_TX([p0], [500ms], [500ms], [300ms])
+BFD_CHECK_RX([p0], [500ms], [500ms], [300ms])
+
+AT_CHECK([ovs-vsctl del-br br1], [0], [ignore])
+AT_CLEANUP
+
+# forwarding_if_rx Test3: bfd is enabled on both ends of link and decay is enabled.
+AT_SETUP([bfd - bfd forwarding_if_rx 3])
+OVS_VSWITCHD_START([add-br br1 -- set bridge br1 datapath-type=dummy -- \
+                    add-port br1 p1 -- set Interface p1 type=patch \
+                    options:peer=p0 ofport_request=2 -- \
+                    add-port br0 p0 -- set Interface p0 type=patch \
+                    options:peer=p1 ofport_request=1 -- \
+                    set Interface p0 bfd:enable=true bfd:min_tx=300 bfd:min_rx=300 bfd:decay_min_rx=3000 -- \
+                    set Interface p1 bfd:enable=true bfd:min_tx=500 bfd:min_rx=500])
+
+ovs-appctl time/stop
+# advance the clock, to stablize the states.
+for i in `seq 0 19`; do ovs-appctl time/warp 500; done
+BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
+BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
+BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
+BFD_CHECK_RX([p0], [3000ms], [3000ms], [500ms])
+
+# enable forwarding_if_rx.
+AT_CHECK([ovs-vsctl set Interface p0 bfd:forwarding_if_rx=true], [0])
+
+# there should be no change of the forwarding flag, since
+# the bfd on both ends is already up.
+for i in `seq 0 9`
+do
+    ovs-appctl time/warp 500
+    BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
+done
+
+# reconfigure the decay_min_rx to 1000ms. check the poll sequence.
+AT_CHECK([ovs-vsctl set interface p0 bfd:decay_min_rx=1000])
+BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [final], [up], [No Diagnostic])
+BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [poll], [up], [No Diagnostic])
+BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
+BFD_CHECK_RX([p0], [500ms], [300ms], [500ms])
+
+# wait for 2000ms to decay.
+for i in `seq 0 3`; do ovs-appctl time/warp 500; done
+BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [final], [up], [No Diagnostic])
+BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [poll], [up], [No Diagnostic])
+BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
+BFD_CHECK_RX([p0], [1000ms], [1000ms], [500ms])
+
+# wait for 1000ms, so that the flags will go back to none.
+for i in `seq 0 1`; do ovs-appctl time/warp 500; done
+BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
+BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
+BFD_CHECK_TX([p0], [500ms], [300ms], [500ms])
+BFD_CHECK_RX([p0], [1000ms], [1000ms], [500ms])
+
+# stop the bfd on one side.
+AT_CHECK([ovs-vsctl set Interface p1 bfd:enable=false], [0])
+# for within 2500ms, the detection timer is not out.
+# there is no change to status.
+for i in `seq 0 4`
+do
+    ovs-appctl time/warp 500
+    BFD_CHECK([p0], [true], [false], [none], [up], [No Diagnostic], [none], [up], [No Diagnostic])
+    AT_CHECK([ovs-ofctl packet-out br1 3 2  "90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202"],
+             [0], [stdout], [])
+done
+
+# at 3000ms, the STATE should go DOWN, due to Control Detection Time Expired.
+# but forwarding flag should be still true.
+ovs-appctl time/warp 500
+BFD_CHECK([p0], [true], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic])
+
+# receive packet at 1/100ms rate for 1000ms.
+for i in `seq 0 9`
+do
+    AT_CHECK([ovs-ofctl packet-out br1 3 2  "90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202"],
+             [0], [stdout], [])
+    ovs-appctl time/warp 100
+    # the forwarding flag should always be true during this time.
+    BFD_CHECK([p0], [true], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic])
+done
+
+# stop receiving for 2000ms.
+for i in `seq 0 19`; do ovs-appctl time/warp 100; done
+BFD_CHECK([p0], [false], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic])
+
+# reset bfd forwarding_if_rx.
+AT_CHECK([ovs-vsctl set Interface p0 bfd:forwarding_if_rx=false])
+# forwarding flag should turn to false since the STATE is DOWN.
+BFD_CHECK([p0], [false], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic])
+BFD_CHECK_TX([p0], [1000ms], [1000ms], [0ms])
+BFD_CHECK_RX([p0], [300ms], [300ms], [1ms])
+
+# re-enable bfd forwarding_if_rx.
+AT_CHECK([ovs-vsctl set Interface p0 bfd:forwarding_if_rx=true])
+# there should be no change.
+BFD_CHECK([p0], [false], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic])
+BFD_CHECK_TX([p0], [1000ms], [1000ms], [0ms])
+BFD_CHECK_RX([p0], [300ms], [300ms], [1ms])
+
+# re-enable bfd on the other end. the states should be up.
+AT_CHECK([ovs-vsctl set Interface p1 bfd:enable=true bfd:min_tx=300 bfd:min_rx=300])
+# advance the clock, to stablize the states.
+for i in `seq 0 9`; do ovs-appctl time/warp 500; done
+BFD_CHECK([p0], [true], [false], [none], [up], [Control Detection Time Expired], [none], [up], [No Diagnostic])
+BFD_CHECK([p1], [true], [false], [none], [up], [No Diagnostic], [none], [up], [Control Detection Time Expired])
+BFD_CHECK_TX([p0], [300ms], [300ms], [300ms])
+BFD_CHECK_RX([p0], [1000ms], [1000ms], [300ms])
+
+AT_CHECK([ovs-vsctl del-br br1], [0], [ignore])
+AT_CLEANUP
\ No newline at end of file
index fc8d071..63356b4 100644 (file)
@@ -75,13 +75,13 @@ AT_CHECK([[ovs-ofctl parse-flow 'actions=learn(load:5->NXM_OF_IP_DST[])']],
   [1], [], [stderr])
 AT_CHECK([sed -e 's/.*|meta_flow|WARN|//' < stderr], [0],
   [[destination field ip_dst lacks correct prerequisites
-ovs-ofctl: actions are invalid with specified match (OFPBAC_BAD_ARGUMENT)
+ovs-ofctl: actions are invalid with specified match (OFPBAC_MATCH_INCONSISTENT)
 ]], [[]])
 AT_CHECK([[ovs-ofctl parse-flow 'actions=learn(load:NXM_OF_IP_DST[]->NXM_NX_REG1[])']],
   [1], [], [stderr])
 AT_CHECK([sed -e 's/.*|meta_flow|WARN|//' < stderr], [0],
   [[source field ip_dst lacks correct prerequisites
-ovs-ofctl: actions are invalid with specified match (OFPBAC_BAD_ARGUMENT)
+ovs-ofctl: actions are invalid with specified match (OFPBAC_MATCH_INCONSISTENT)
 ]])
 AT_CLEANUP
 
index ded0a22..a978c13 100644 (file)
@@ -7,7 +7,7 @@ AT_CHECK([test-flows <flows 3<pcap], [0], [checked 247 packets, 0 errors
 AT_CLEANUP
 
 AT_SETUP([test TCP/IP checksumming])
-AT_CHECK([test-csum], [0], [....#....#....##................................#................................#
+AT_CHECK([test-csum], [0], [....#....#....###................................#................................#
 ])
 AT_CLEANUP
 
index 986b931..f554aba 100644 (file)
@@ -825,6 +825,36 @@ OFPT_FLOW_MOD (OF1.2) (xid=0x52334507): ADD priority=255,ip actions=set_field:19
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPT_FLOW_MOD - OF1.2 - set-field sctp_src])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\
+03 0e 00 58 52 33 45 07 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff \
+ff ff ff ff ff ff ff ff ff ff ff ff 00 00 00 00 \
+00 01 00 0f 80 00 0a 02 08 00 80 00 14 01 84 00 \
+00 04 00 18 00 00 00 00 00 19 00 10 80 00 22 02 \
+0d 06 00 00 00 00 00 00                         \
+" 2], [0], [dnl
+OFPT_FLOW_MOD (OF1.2) (xid=0x52334507): ADD priority=255,sctp actions=set_field:3334->sctp_src
+], [dnl
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_FLOW_MOD - OF1.2 - set-field sctp_dst])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\
+03 0e 00 58 52 33 45 07 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff \
+ff ff ff ff ff ff ff ff ff ff ff ff 00 00 00 00 \
+00 01 00 0f 80 00 0a 02 08 00 80 00 14 01 84 00 \
+00 04 00 18 00 00 00 00 00 19 00 10 80 00 24 02 \
+11 5d 00 00 00 00 00 00                         \
+" 2], [0], [dnl
+OFPT_FLOW_MOD (OF1.2) (xid=0x52334507): ADD priority=255,sctp actions=set_field:4445->sctp_dst
+], [dnl
+])
+AT_CLEANUP
+
 AT_SETUP([OFPT_FLOW reply - OF1.2 - set-field ip_src])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\
@@ -859,6 +889,40 @@ OFPST_FLOW reply (OF1.2) (xid=0x52334509):
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPT_FLOW reply - OF1.2 - set-field sctp_src])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\
+03 13 00 68 52 33 45 04 00 01 00 00 00 00 00 00 \
+00 58 00 00 00 00 00 00 00 00 00 00 00 ff 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 01 00 0f 80 00 0a 02 08 00 80 00 14 01 84 00 \
+00 04 00 18 00 00 00 00 00 19 00 10 80 00 22 02 \
+0d 06 00 00 00 00 00 00                         \
+" 2], [0], [dnl
+OFPST_FLOW reply (OF1.2) (xid=0x52334504):
+ cookie=0x0, duration=0s, table=0, n_packets=0, n_bytes=0, priority=255,sctp actions=set_field:3334->sctp_src
+], [dnl
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_FLOW reply - OF1.2 - set-field sctp_dst])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\
+03 13 00 68 52 33 45 09 00 01 00 00 00 00 00 00 \
+00 58 00 00 00 00 00 00 00 00 00 00 00 ff 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \
+00 01 00 0f 80 00 0a 02 08 00 80 00 14 01 84 00 \
+00 04 00 18 00 00 00 00 00 19 00 10 80 00 24 02 \
+11 5d 00 00 00 00 00 00                         \
+" 2], [0], [dnl
+OFPST_FLOW reply (OF1.2) (xid=0x52334509):
+ cookie=0x0, duration=0s, table=0, n_packets=0, n_bytes=0, priority=255,sctp actions=set_field:4445->sctp_dst
+], [dnl
+])
+AT_CLEANUP
+
 AT_SETUP([OFPT_PORT_MOD - OF1.0])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print "\
@@ -1670,7 +1734,7 @@ AT_SETUP([OFPST_METER_FEATURES reply - OF1.3])
 AT_KEYWORDS([ofp-print OFPT_STATS_REPLY])
 AT_CHECK([ovs-ofctl ofp-print "\
 04 13 00 20 00 00 00 02 00 0b 00 00 00 00 00 00 \
-00 01 00 00 00 00 00 03 00 00 00 0F 10 02 00 00 \
+00 01 00 00 00 00 00 06 00 00 00 0F 10 02 00 00 \
 "], [0], [dnl
 OFPST_METER_FEATURES reply (OF1.3) (xid=0x2):
 max_meter:65536 max_bands:16 max_color:2
index b093998..af19672 100644 (file)
@@ -646,16 +646,54 @@ udp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_ds
 ])
 
 AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore])
+
+dnl Checksum SCTP.
+AT_CHECK([ovs-ofctl monitor br0 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log])
+
+for i in 1 ; do
+    ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 20 22 22 22 22 22 08 00 45 00 00 24 00 00 00 00 00 84 00 00 C0 A8 00 01 C0 A8 00 02 04 58 08 af 00 00 00 00 d9 d7 91 57 01 00 00 34 cf 28 ec 4e 00 01 40 00 00 0a ff ff b7 53 24 19 00 05 00 08 7f 00 00 01 00 05 00 08 c0 a8 02 07 00 0c 00 06 00 05 00 00 80 00 00 04 c0 00 00 04'
+done
+
+OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+AT_CHECK([cat ofctl_monitor.log], [0], [dnl
+NXT_PACKET_IN (xid=0x0): cookie=0x1 total_len=98 in_port=1 (via action) data_len=98 (unbuffered)
+sctp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=20:22:22:22:22:22,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=1112,tp_dst=2223 sctp_csum:d9d79157
+dnl
+NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x3 total_len=102 in_port=1 reg0=0x1 (via action) data_len=102 (unbuffered)
+sctp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=20:22:22:22:22:22,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=1112,tp_dst=2223 sctp_csum:d9d79157
+dnl
+NXT_PACKET_IN (xid=0x0): table_id=2 cookie=0x4 total_len=102 in_port=1 reg0=0x1 reg1=0x2 (via action) data_len=102 (unbuffered)
+sctp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=1112,tp_dst=2223 sctp_csum:d9d79157
+dnl
+NXT_PACKET_IN (xid=0x0): table_id=3 cookie=0x5 total_len=102 in_port=1 reg0=0x1 reg1=0x2 reg2=0x3 (via action) data_len=102 (unbuffered)
+sctp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=1112,tp_dst=2223 sctp_csum:d9d79157
+dnl
+NXT_PACKET_IN (xid=0x0): table_id=4 cookie=0x6 total_len=102 in_port=1 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 (via action) data_len=102 (unbuffered)
+sctp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=1112,tp_dst=2223 sctp_csum:d9d79157
+dnl
+NXT_PACKET_IN (xid=0x0): table_id=5 cookie=0x7 total_len=102 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=102 (unbuffered)
+sctp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=1112,tp_dst=2223 sctp_csum:d9d79157
+dnl
+NXT_PACKET_IN (xid=0x0): table_id=6 cookie=0x8 total_len=102 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=102 (unbuffered)
+sctp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=85,tp_dst=2223 sctp_csum:7f12662e
+dnl
+NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=102 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=102 (unbuffered)
+sctp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=85,tp_dst=86 sctp_csum:a7e86f67
+dnl
+NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=102 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=102 (unbuffered)
+sctp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=85,tp_dst=86 sctp_csum:a7e86f67
+])
+
 AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
- cookie=0x1, n_packets=2, n_bytes=120, dl_src=20:22:22:22:22:22 actions=CONTROLLER:65535,resubmit(80,1)
+ cookie=0x1, n_packets=3, n_bytes=218, dl_src=20:22:22:22:22:22 actions=CONTROLLER:65535,resubmit(80,1)
  cookie=0x2, n_packets=3, n_bytes=180, dl_src=30:33:33:33:33:33 actions=mod_vlan_vid:15,CONTROLLER:65535
- cookie=0x3, table=1, n_packets=2, n_bytes=120, in_port=80 actions=load:0x1->NXM_NX_REG0[[]],mod_vlan_vid:80,CONTROLLER:65535,resubmit(81,2)
- cookie=0x4, table=2, n_packets=2, n_bytes=120, in_port=81 actions=load:0x2->NXM_NX_REG1[[]],mod_dl_src:80:81:81:81:81:81,CONTROLLER:65535,resubmit(82,3)
- cookie=0x5, table=3, n_packets=2, n_bytes=120, in_port=82 actions=load:0x3->NXM_NX_REG2[[]],mod_dl_dst:82:82:82:82:82:82,CONTROLLER:65535,resubmit(83,4)
- cookie=0x6, table=4, n_packets=2, n_bytes=120, in_port=83 actions=load:0x4->NXM_NX_REG3[[]],mod_nw_src:83.83.83.83,CONTROLLER:65535,resubmit(84,5)
- cookie=0x7, table=5, n_packets=2, n_bytes=120, in_port=84 actions=load:0x5->NXM_NX_REG4[[]],load:0x6->NXM_NX_TUN_ID[[]],mod_nw_dst:84.84.84.84,CONTROLLER:65535,resubmit(85,6)
- cookie=0x8, table=6, n_packets=2, n_bytes=120, in_port=85 actions=mod_tp_src:85,CONTROLLER:65535,resubmit(86,7)
- cookie=0x9, table=7, n_packets=2, n_bytes=120, in_port=86 actions=mod_tp_dst:86,CONTROLLER:65535,CONTROLLER:65535
+ cookie=0x3, table=1, n_packets=3, n_bytes=218, in_port=80 actions=load:0x1->NXM_NX_REG0[[]],mod_vlan_vid:80,CONTROLLER:65535,resubmit(81,2)
+ cookie=0x4, table=2, n_packets=3, n_bytes=218, in_port=81 actions=load:0x2->NXM_NX_REG1[[]],mod_dl_src:80:81:81:81:81:81,CONTROLLER:65535,resubmit(82,3)
+ cookie=0x5, table=3, n_packets=3, n_bytes=218, in_port=82 actions=load:0x3->NXM_NX_REG2[[]],mod_dl_dst:82:82:82:82:82:82,CONTROLLER:65535,resubmit(83,4)
+ cookie=0x6, table=4, n_packets=3, n_bytes=218, in_port=83 actions=load:0x4->NXM_NX_REG3[[]],mod_nw_src:83.83.83.83,CONTROLLER:65535,resubmit(84,5)
+ cookie=0x7, table=5, n_packets=3, n_bytes=218, in_port=84 actions=load:0x5->NXM_NX_REG4[[]],load:0x6->NXM_NX_TUN_ID[[]],mod_nw_dst:84.84.84.84,CONTROLLER:65535,resubmit(85,6)
+ cookie=0x8, table=6, n_packets=3, n_bytes=218, in_port=85 actions=mod_tp_src:85,CONTROLLER:65535,resubmit(86,7)
+ cookie=0x9, table=7, n_packets=3, n_bytes=218, in_port=86 actions=mod_tp_dst:86,CONTROLLER:65535,CONTROLLER:65535
  cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:44:41 actions=mod_vlan_vid:99,mod_vlan_pcp:1,CONTROLLER:65535
  cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:44:42 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],CONTROLLER:65535
  cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:44:43 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],CONTROLLER:65535
@@ -2689,3 +2727,84 @@ AT_CHECK([tail -1 stdout], [0], [Datapath actions: 5
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
+
+# Tests the bundling with various bfd and cfm configurations.
+AT_SETUP([ofproto - bundle with variable bfd/cfm config])
+OVS_VSWITCHD_START([add-br br1 -- set bridge br1 datapath-type=dummy -- \
+                    add-bond br0 br0bond p0 p2 bond-mode=active-backup -- \
+                    add-bond br1 br1bond p1 p3 bond-mode=active-backup -- \
+                    set Interface p1 type=patch options:peer=p0 ofport_request=2 -- \
+                    set Interface p3 type=patch options:peer=p2 ofport_request=4 -- \
+                    set Interface p0 type=patch options:peer=p1 ofport_request=1 -- \
+                    set Interface p2 type=patch options:peer=p3 ofport_request=3 -- \
+                    set Interface p0 bfd:enable=true bfd:min_tx=300 bfd:min_rx=300 -- \
+                    set Interface p0 cfm_mpid=1 -- \
+                    set Interface p1 bfd:enable=true bfd:min_tx=500 bfd:min_rx=500])
+
+ovs-appctl time/stop
+# advance the clock to stablize everything.
+for i in `seq 0 49`; do ovs-appctl time/warp 100; done
+# cfm/show should show 'recv' fault.
+AT_CHECK([ovs-appctl cfm/show | sed -n '/^.*fault:.*/p'], [0], [dnl
+       fault: recv
+])
+# bfd/show should show 'up'.
+AT_CHECK([ovs-appctl bfd/show | sed -n '/^.*Session State:.*/p'], [0], [dnl
+       Local Session State: up
+       Remote Session State: up
+       Local Session State: up
+       Remote Session State: up
+])
+# bond/show should show 'may-enable: true' for all slaves.
+AT_CHECK([ovs-appctl bond/show | sed -n '/^.*may_enable:.*/p'], [0], [dnl
+       may_enable: true
+       may_enable: true
+       may_enable: true
+       may_enable: true
+])
+
+# now disable the bfd on p1.
+AT_CHECK([ovs-vsctl set Interface p1 bfd:enable=false])
+# advance the clock to stablize everything.
+for i in `seq 0 49`; do ovs-appctl time/warp 100; done
+# cfm/show should show 'recv' fault.
+AT_CHECK([ovs-appctl cfm/show | sed -n '/^.*fault:.*/p'], [0], [dnl
+       fault: recv
+])
+# bfd/show should show 'down'.
+AT_CHECK([ovs-appctl bfd/show | sed -n '/^.*Session State:.*/p'], [0], [dnl
+       Local Session State: down
+       Remote Session State: down
+])
+# bond/show should show 'may-enable: false' for p0.
+AT_CHECK([ovs-appctl bond/show | sed -n '/^.*may_enable:.*/p'], [0], [dnl
+       may_enable: false
+       may_enable: true
+       may_enable: true
+       may_enable: true
+])
+
+# now enable the bfd on p1 and disable bfd on p0.
+AT_CHECK([ovs-vsctl set Interface p1 bfd:enable=true])
+AT_CHECK([ovs-vsctl set Interface p0 bfd:enable=false])
+# advance the clock to stablize everything.
+for i in `seq 0 49`; do ovs-appctl time/warp 100; done
+# cfm/show should show 'recv' fault.
+AT_CHECK([ovs-appctl cfm/show | sed -n '/^.*fault:.*/p'], [0], [dnl
+       fault: recv
+])
+# bfd/show should show 'down'.
+AT_CHECK([ovs-appctl bfd/show | sed -n '/^.*Session State:.*/p'], [0], [dnl
+       Local Session State: down
+       Remote Session State: down
+])
+# bond/show should show 'may-enable: false' for p0 and p1.
+AT_CHECK([ovs-appctl bond/show | sed -n '/^.*may_enable:.*/p'], [0], [dnl
+       may_enable: false
+       may_enable: true
+       may_enable: false
+       may_enable: true
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
index 996ea06..a0148a6 100644 (file)
@@ -113,6 +113,8 @@ udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1
 cookie=0x123456789abcdef hard_timeout=10 priority=60000 actions=controller
 actions=note:41.42.43,note:00.01.02.03.04.05.06.07,note
 ip,actions=set_field:10.4.3.77->ip_src
+sctp actions=set_field:3334->sctp_src
+sctp actions=set_field:4445->sctp_dst
 in_port=0 actions=resubmit:0
 actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
 ]])
@@ -130,6 +132,8 @@ OFPT_FLOW_MOD: ADD udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1
 OFPT_FLOW_MOD: ADD priority=60000 cookie:0x123456789abcdef hard:10 actions=CONTROLLER:65535
 OFPT_FLOW_MOD: ADD actions=note:41.42.43.00.00.00,note:00.01.02.03.04.05.06.07.00.00.00.00.00.00,note:00.00.00.00.00.00
 OFPT_FLOW_MOD: ADD ip actions=load:0xa04034d->NXM_OF_IP_SRC[]
+OFPT_FLOW_MOD: ADD sctp actions=load:0xd06->OXM_OF_SCTP_SRC[]
+OFPT_FLOW_MOD: ADD sctp actions=load:0x115d->OXM_OF_SCTP_DST[]
 OFPT_FLOW_MOD: ADD in_port=0 actions=resubmit:0
 OFPT_FLOW_MOD: ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
 ]])
@@ -146,6 +150,8 @@ udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1
 cookie=0x123456789abcdef hard_timeout=10 priority=60000 actions=controller
 actions=note:41.42.43,note:00.01.02.03.04.05.06.07,note
 ipv6,actions=set_field:fe80:0123:4567:890a:a6ba:dbff:fefe:59fa->ipv6_src
+sctp actions=set_field:3334->sctp_src
+sctp actions=set_field:4445->sctp_dst
 in_port=0 actions=resubmit:0
 actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
 ]])
@@ -163,6 +169,8 @@ OFPT_FLOW_MOD (OF1.2): ADD table:255 udp,nw_src=192.168.0.3,tp_dst=53 actions=po
 OFPT_FLOW_MOD (OF1.2): ADD table:255 priority=60000 cookie:0x123456789abcdef hard:10 actions=CONTROLLER:65535
 OFPT_FLOW_MOD (OF1.2): ADD table:255 actions=note:41.42.43.00.00.00,note:00.01.02.03.04.05.06.07.00.00.00.00.00.00,note:00.00.00.00.00.00
 OFPT_FLOW_MOD (OF1.2): ADD table:255 ipv6 actions=set_field:fe80:123:4567:890a:a6ba:dbff:fefe:59fa->ipv6_src
+OFPT_FLOW_MOD (OF1.2): ADD table:255 sctp actions=set_field:3334->sctp_src
+OFPT_FLOW_MOD (OF1.2): ADD table:255 sctp actions=set_field:4445->sctp_dst
 OFPT_FLOW_MOD (OF1.2): ADD table:255 in_port=0 actions=resubmit:0
 OFPT_FLOW_MOD (OF1.2): ADD table:255 actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
 ]])
@@ -340,12 +348,14 @@ ipv6,ipv6_label=0x12345 actions=2
 ipv6,ipv6_src=2001:db8:3c4d:1:2:3:4:5 actions=3
 ipv6,ipv6_src=2001:db8:3c4d:1:2:3:4:5/64 actions=4
 ipv6,ipv6_dst=2001:db8:3c4d:1:2:3:4:5/127 actions=5
-tcp6,ipv6_src=2001:db8:3c4d:1::1,tp_dst=80 actions=drop 
-udp6,ipv6_src=2001:db8:3c4d:1::3,tp_dst=53 actions=drop 
+tcp6,ipv6_src=2001:db8:3c4d:1::1,tp_dst=80 actions=drop
+udp6,ipv6_src=2001:db8:3c4d:1::3,tp_dst=53 actions=drop
+sctp6,ipv6_src=2001:db8:3c4d:1::5,tp_dst=309 actions=drop
 in_port=3 icmp6,ipv6_src=2001:db8:3c4d:1::1,icmp_type=134 actions=drop
 udp dl_vlan_pcp=7 idle_timeout=5 actions=strip_vlan output:0
 tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1
 udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1
+sctp,nw_src=192.168.0.3,tp_dst=309 actions=pop_queue,output:1
 icmp6,icmp_type=135,nd_target=FEC0::1234:F045:8FFF:1111:FE4E:0571 actions=drop
 icmp6,icmp_type=135,nd_sll=00:0A:E4:25:6B:B0 actions=drop
 icmp6,icmp_type=136,nd_target=FEC0::1234:F045:8FFF:1111:FE4E:0571,nd_tll=00:0A:E4:25:6B:B1 actions=drop
@@ -372,10 +382,12 @@ NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_SRC_W(20010db83c4d000100000
 NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_DST_W(20010db83c4d00010002000300040004/fffffffffffffffffffffffffffffffe) actions=output:5
 NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_SRC(20010db83c4d00010000000000000001), NXM_OF_IP_PROTO(06), NXM_OF_TCP_DST(0050) actions=drop
 NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_SRC(20010db83c4d00010000000000000003), NXM_OF_IP_PROTO(11), NXM_OF_UDP_DST(0035) actions=drop
+NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_SRC(20010db83c4d00010000000000000005), NXM_OF_IP_PROTO(84), OXM_OF_SCTP_DST(0135) actions=drop
 NXT_FLOW_MOD: ADD NXM_OF_IN_PORT(0003), NXM_OF_ETH_TYPE(86dd), NXM_NX_IPV6_SRC(20010db83c4d00010000000000000001), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(86) actions=drop
 NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800), NXM_OF_VLAN_TCI_W(f000/f000), NXM_OF_IP_PROTO(11) idle:5 actions=strip_vlan,output:0
 NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800), NXM_OF_IP_SRC(c0a80003), NXM_OF_IP_PROTO(06), NXM_OF_TCP_DST(0050) actions=set_queue:37,output:1
 NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800), NXM_OF_IP_SRC(c0a80003), NXM_OF_IP_PROTO(11), NXM_OF_UDP_DST(0035) actions=pop_queue,output:1
+NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(0800), NXM_OF_IP_SRC(c0a80003), NXM_OF_IP_PROTO(84), OXM_OF_SCTP_DST(0135) actions=pop_queue,output:1
 NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(86dd), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(87), NXM_NX_ND_TARGET(fec000001234f0458fff1111fe4e0571) actions=drop
 NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(86dd), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(87), NXM_NX_ND_SLL(000ae4256bb0) actions=drop
 NXT_FLOW_MOD: ADD NXM_OF_ETH_TYPE(86dd), NXM_OF_IP_PROTO(3a), NXM_NX_ICMPV6_TYPE(88), NXM_NX_ND_TARGET(fec000001234f0458fff1111fe4e0571), NXM_NX_ND_TLL(000ae4256bb1) actions=drop
@@ -1145,6 +1157,14 @@ xxxxxxxx xxxxxxxx 01bb xxxx
 0038204f xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx 11 xxxx dnl
 xxxxxxxx xxxxxxxx xxxx 01bb
 
+# sctp,tp_src=443
+0038208f xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx 84 xxxx dnl
+xxxxxxxx xxxxxxxx 01bb xxxx
+
+# sctp,tp_dst=443
+0038204f xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx 84 xxxx dnl
+xxxxxxxx xxxxxxxx xxxx 01bb
+
 # icmp,icmp_type=5
 0038208f xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx 01 xxxx dnl
 xxxxxxxx xxxxxxxx 0005 xxxx
@@ -1153,7 +1173,7 @@ xxxxxxxx xxxxxxxx 0005 xxxx
 0038204f xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx 01 xxxx dnl
 xxxxxxxx xxxxxxxx xxxx 0008
 
-dnl Ignore tp_src if not TCP or UDP:
+dnl Ignore tp_src if not TCP/UDP/SCTP:
 # ip,nw_proto=21,tp_src=443
 # normal:  3: 8f -> cf
 # normal: 36: 01 -> 00
@@ -1164,7 +1184,7 @@ dnl Ignore tp_src if not TCP or UDP:
 0038208f xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxx xx xx 0800 xx 15 xxxx dnl
 xxxxxxxx xxxxxxxx 01bb xxxx
 
-dnl Ignore tp_dst if not TCP or UDP:
+dnl Ignore tp_dst if not TCP/UDP/SCTP:
 # ip,nw_proto=21,tp_dst=443
 # normal:  3: 4f -> cf
 # normal: 38: 01 -> 00
@@ -1408,28 +1428,25 @@ dnl Try invalid TOS:
 0000 00 00 0800 00 11 00000000ffffffff 00000000ffffffff 0000 01bb dnl
 00000000 00 000000 0000000000000000ffffffffffffffff
 
-dnl SCTP, no ports.
-# ip,nw_proto=132
+# sctp
 0000 0058 00000000 000003d7 dnl
 000000000000ffffffffffff 000000000000ffffffffffff dnl
 0000 00 00 0800 00 84 00000000ffffffff 00000000ffffffff 0000 0000 dnl
 00000000 00 000000 0000000000000000ffffffffffffffff
 
-dnl SCTP tp_src matching not supported:
-# bad ofp11_match: OFPBMC_BAD_FIELD
+# sctp,tp_src=443
 0000 0058 00000000 00000397 dnl
 000000000000ffffffffffff 000000000000ffffffffffff dnl
 0000 00 00 0800 00 84 00000000ffffffff 00000000ffffffff 01bb 0000 dnl
 00000000 00 000000 0000000000000000ffffffffffffffff
 
-dnl SCTP tp_dst matching not supported:
-# bad ofp11_match: OFPBMC_BAD_FIELD
+# sctp,tp_dst=443
 0000 0058 00000000 00000357 dnl
 000000000000ffffffffffff 000000000000ffffffffffff dnl
 0000 00 00 0800 00 84 00000000ffffffff 00000000ffffffff 0000 01bb dnl
 00000000 00 000000 0000000000000000ffffffffffffffff
 
-dnl Ignore tp_src if not TCP or UDP or SCTP:
+dnl Ignore tp_src if not TCP/UDP/SCTP:
 # ip,nw_proto=21
 # 11: 97 -> d7
 # 60: 01 -> 00
@@ -1439,7 +1456,7 @@ dnl Ignore tp_src if not TCP or UDP or SCTP:
 0000 00 00 0800 00 15 00000000ffffffff 00000000ffffffff 01bb 0000 dnl
 00000000 00 000000 0000000000000000ffffffffffffffff
 
-dnl Ignore tp_dst if not TCP or UDP or SCTP:
+dnl Ignore tp_dst if not TCP/UDP/SCTP:
 # ip,nw_proto=22
 # 11: 57 -> d7
 # 62: 01 -> 00
@@ -1640,6 +1657,20 @@ OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(11) OXM_OF_UDP_DST_W(5005/FFFF)
 OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(11) OXM_OF_UDP_DST_W(5005/0000)
 OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(02) OXM_OF_UDP_DST(1293)
 
+# SCTP source port
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(84) OXM_OF_SCTP_SRC(8732)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(84) OXM_OF_SCTP_SRC_W(0132/01FF)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(84) OXM_OF_SCTP_SRC_W(0132/FFFF)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(84) OXM_OF_SCTP_SRC_W(0132/0000)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(06) OXM_OF_SCTP_SRC(7823)
+
+# SCTP destination port
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(84) OXM_OF_SCTP_DST(1782)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(84) OXM_OF_SCTP_DST_W(5005/F00F)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(84) OXM_OF_SCTP_DST_W(5005/FFFF)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(84) OXM_OF_SCTP_DST_W(5005/0000)
+OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(02) OXM_OF_SCTP_DST(1293)
+
 # ICMP type
 OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(01) OXM_OF_ICMPV4_TYPE(12)
 OXM_OF_ETH_TYPE(0800) OXM_OF_IP_PROTO(00) OXM_OF_ICMPV4_TYPE(10)
@@ -1842,6 +1873,20 @@ OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(11), OXM_OF_UDP_DST(5005)
 OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(11)
 nx_pull_match() returned error OFPBMC_BAD_PREREQ
 
+# SCTP source port
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(84), OXM_OF_SCTP_SRC(8732)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(84), OXM_OF_SCTP_SRC_W(0132/01ff)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(84), OXM_OF_SCTP_SRC(0132)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(84)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# SCTP destination port
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(84), OXM_OF_SCTP_DST(1782)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(84), OXM_OF_SCTP_DST_W(5005/f00f)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(84), OXM_OF_SCTP_DST(5005)
+OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(84)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
 # ICMP type
 OXM_OF_ETH_TYPE(0800), OXM_OF_IP_PROTO(01), OXM_OF_ICMPV4_TYPE(12)
 nx_pull_match() returned error OFPBMC_BAD_PREREQ
index e9bd6bd..e6df1cd 100644 (file)
@@ -66,9 +66,9 @@ test_atomic_flag(void)
 {
     atomic_flag flag = ATOMIC_FLAG_INIT;
     ovs_assert(atomic_flag_test_and_set(&flag) == false);
-    ovs_assert(flag.b == true);
+    ovs_assert(atomic_flag_test_and_set(&flag) == true);
     atomic_flag_clear(&flag);
-    ovs_assert(flag.b == false);
+    ovs_assert(atomic_flag_test_and_set(&flag) == false);
 }
 
 int
index b08f236..ef126de 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <config.h>
 #include "csum.h"
+#include "crc32c.h"
 #include <inttypes.h>
 #include <netinet/in.h>
 #include <stdio.h>
@@ -136,6 +137,44 @@ test_rfc1624(void)
     mark('#');
 }
 
+/* CRC32C checksum tests, based on Intel IPPs, Chapter 13,
+ * ippsCRC32C_8u() example, found at the following location:
+ * http://software.intel.com/sites/products/documentation/hpc/ipp/ipps/ */
+static void
+test_crc32c(void)
+{
+    int i;
+    uint8_t data[48] = {
+        0x01, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
+        0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x18,
+        0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    };
+
+    /* iSCSI Read PDU */
+    assert(ntohl(crc32c(data, 48)) == 0x563a96d9L);
+
+    /* 32 bytes of all zeroes */
+    for (i = 0; i < 32; i++) data[i] = 0x00;
+    assert(ntohl(crc32c(data, 32)) == 0xaa36918aL);
+
+    /* 32 bytes of all ones */
+    for (i = 0; i < 32; i++) data[i] = 0xff;
+    assert(ntohl(crc32c(data, 32)) == 0x43aba862L);
+
+    /* 32 bytes of incrementing 00..1f */
+    for (i = 0; i < 32; i++) data[i] = i;
+    assert(ntohl(crc32c(data, 32)) == 0x4e79dd46L);
+
+    /* 32 bytes of decrementing 1f..00 */
+    for (i  = 0; i < 32; i++) data[i] = 31 - i;
+    assert(ntohl(crc32c(data, 32)) == 0x5cdb3f11L);
+
+    mark('#');
+}
+
 int
 main(void)
 {
@@ -198,6 +237,7 @@ main(void)
     }
 
     test_rfc1624();
+    test_crc32c();
 
     /* Test recalc_csum16(). */
     for (i = 0; i < 32; i++) {
index 413837e..e995852 100644 (file)
@@ -100,6 +100,11 @@ print_netflow(struct ofpbuf *buf)
                    ntohs(rec->src_port), ntohs(rec->dst_port));
             break;
 
+        case IPPROTO_SCTP:
+            printf(", SCTP %"PRIu16" > %"PRIu16,
+                   ntohs(rec->src_port), ntohs(rec->dst_port));
+            break;
+
         case IPPROTO_ICMP:
             printf(", ICMP %"PRIu16":%"PRIu16,
                    ntohs(rec->dst_port) >> 8,
@@ -120,6 +125,7 @@ print_netflow(struct ofpbuf *buf)
 
         if (rec->ip_proto != IPPROTO_TCP &&
             rec->ip_proto != IPPROTO_UDP &&
+            rec->ip_proto != IPPROTO_SCTP &&
             rec->ip_proto != IPPROTO_ICMP) {
             if (rec->src_port != htons(0)) {
                 printf(", src_port %"PRIu16, ntohs(rec->src_port));
index 47b591a..85b2e44 100644 (file)
@@ -537,8 +537,8 @@ above).
 .
 .IP \fBtp_src=\fIport\fR
 .IQ \fBtp_dst=\fIport\fR
-When \fBdl_type\fR and \fBnw_proto\fR specify TCP or UDP, \fBtp_src\fR
-and \fBtp_dst\fR match the UDP or TCP source or destination port
+When \fBdl_type\fR and \fBnw_proto\fR specify TCP or UDP or SCTP, \fBtp_src\fR
+and \fBtp_dst\fR match the UDP or TCP or SCTP source or destination port
 \fIport\fR, respectively, which is specified as a decimal number
 between 0 and 65535, inclusive (e.g. 80 to match packets originating
 from a HTTP server).
@@ -548,7 +548,7 @@ these settings are ignored (see \fBFlow Syntax\fR above).
 .
 .IP \fBtp_src=\fIport\fB/\fImask\fR
 .IQ \fBtp_dst=\fIport\fB/\fImask\fR
-Bitwise match on TCP (or UDP) source or destination port,
+Bitwise match on TCP (or UDP or SCTP) source or destination port,
 respectively.  The \fIport\fR and \fImask\fR are 16-bit numbers
 written in decimal or in hexadecimal prefixed by \fB0x\fR.  Each 1-bit
 in \fImask\fR requires that the corresponding bit in \fIport\fR must
@@ -606,7 +606,7 @@ ports.
 .IP
 Like the exact-match forms of \fBtp_src\fR and \fBtp_dst\fR described
 above, the bitwise match forms apply only when \fBdl_type\fR and
-\fBnw_proto\fR specify TCP or UDP.
+\fBnw_proto\fR specify TCP or UDP or SCTP.
 .
 .IP \fBicmp_type=\fItype\fR
 .IQ \fBicmp_code=\fIcode\fR
@@ -659,6 +659,9 @@ Same as \fBdl_type=0x0800,nw_proto=6\fR.
 .IP \fBudp\fR
 Same as \fBdl_type=0x0800,nw_proto=17\fR.
 .
+.IP \fBsctp\fR
+Same as \fBdl_type=0x0800,nw_proto=132\fR.
+.
 .IP \fBarp\fR
 Same as \fBdl_type=0x0806\fR.
 .
@@ -827,6 +830,9 @@ Same as \fBdl_type=0x86dd,nw_proto=6\fR.
 .IP \fBudp6\fR
 Same as \fBdl_type=0x86dd,nw_proto=17\fR.
 .
+.IP \fBsctp6\fR
+Same as \fBdl_type=0x86dd,nw_proto=132\fR.
+.
 .IP \fBicmp6\fR
 Same as \fBdl_type=0x86dd,nw_proto=58\fR.
 .
@@ -983,10 +989,10 @@ Sets the IPv4 source address to \fIip\fR.
 Sets the IPv4 destination address to \fIip\fR.
 .
 .IP \fBmod_tp_src\fB:\fIport\fR
-Sets the TCP or UDP source port to \fIport\fR.
+Sets the TCP or UDP or SCTP source port to \fIport\fR.
 .
 .IP \fBmod_tp_dst\fB:\fIport\fR
-Sets the TCP or UDP destination port to \fIport\fR.
+Sets the TCP or UDP or SCTP destination port to \fIport\fR.
 .
 .IP \fBmod_nw_tos\fB:\fItos\fR
 Sets the IPv4 ToS/DSCP field to \fItos\fR, which must be a multiple of
@@ -1545,7 +1551,7 @@ A flow that does not specify any part of a field that is used for sorting is
 sorted after all the flows that do specify the field.  For example,
 \fB\-\-sort=tcp_src\fR will sort all the flows that specify a TCP
 source port in ascending order, followed by the flows that do not
-specify a TCP source port at all.  
+specify a TCP source port at all.
 .IP \(bu
 A flow that only specifies some bits in a field is sorted as if the
 wildcarded bits were zero.  For example, \fB\-\-sort=nw_src\fR would
index fd29b06..bdeb348 100644 (file)
@@ -942,11 +942,12 @@ Deconfigure sFlow from \fBbr0\fR, which also destroys the sFlow record
 .PP
 Configure bridge \fBbr0\fR to send one IPFIX flow record per packet
 sample to UDP port 4739 on host 192.168.0.34, with Observation Domain
-ID 123 and Observation Point ID 456:
+ID 123 and Observation Point ID 456, a flow cache active timeout of 1
+minute (60 seconds), and a maximum flow cache size of 13 flows:
 .IP
 .B "ovs\-vsctl \-\- set Bridge br0 ipfix=@i \(rs"
 .IP
-.B "\-\- \-\-id=@i create IPFIX targets=\(rs\(dq192.168.0.34:4739\(rs\(dq obs_domain_id=123 obs_point_id=456"
+.B "\-\- \-\-id=@i create IPFIX targets=\(rs\(dq192.168.0.34:4739\(rs\(dq obs_domain_id=123 obs_point_id=456 cache_active_timeout=60 cache_max_flows=13"
 .PP
 Deconfigure the IPFIX settings from \fBbr0\fR, which also destroys the
 IPFIX record (since it is now unreferenced):
index 3d63125..a11e646 100644 (file)
@@ -1025,6 +1025,12 @@ bridge_configure_ipfix(struct bridge *br)
         if (be_cfg->obs_point_id) {
             be_opts.obs_point_id = *be_cfg->obs_point_id;
         }
+        if (be_cfg->cache_active_timeout) {
+            be_opts.cache_active_timeout = *be_cfg->cache_active_timeout;
+        }
+        if (be_cfg->cache_max_flows) {
+            be_opts.cache_max_flows = *be_cfg->cache_max_flows;
+        }
     }
 
     if (n_fe_opts > 0) {
@@ -1037,6 +1043,10 @@ bridge_configure_ipfix(struct bridge *br)
                 sset_init(&opts->targets);
                 sset_add_array(&opts->targets, fe_cfg->ipfix->targets,
                                fe_cfg->ipfix->n_targets);
+                opts->cache_active_timeout = fe_cfg->ipfix->cache_active_timeout
+                    ? *fe_cfg->ipfix->cache_active_timeout : 0;
+                opts->cache_max_flows = fe_cfg->ipfix->cache_max_flows
+                    ? *fe_cfg->ipfix->cache_max_flows : 0;
                 opts++;
             }
         }
index ae523f3..e8a8512 100644 (file)
@@ -506,7 +506,7 @@ get_filesys_stats(struct smap *stats OVS_UNUSED)
 \f
 #define SYSTEM_STATS_INTERVAL (5 * 1000) /* In milliseconds. */
 
-static struct ovs_mutex mutex = OVS_ADAPTIVE_MUTEX_INITIALIZER;
+static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
 static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
 static struct latch latch OVS_GUARDED_BY(mutex);
 static bool enabled;
index c1c3ef4..538dad3 100644 (file)
@@ -1,6 +1,6 @@
 {"name": "Open_vSwitch",
  "version": "7.3.0",
- "cksum": "1081379034 19765",
+ "cksum": "2483452374 20182",
  "tables": {
    "Open_vSwitch": {
      "columns": {
                           "minInteger": 0,
                           "maxInteger": 4294967295},
                   "min": 0, "max": 1}},
+       "cache_active_timeout": {
+         "type": {"key": {"type": "integer",
+                          "minInteger": 0,
+                          "maxInteger": 4200},
+                  "min": 0, "max": 1}},
+       "cache_max_flows": {
+         "type": {"key": {"type": "integer",
+                          "minInteger": 0,
+                          "maxInteger": 4294967295},
+                  "min": 0, "max": 1}},
        "external_ids": {
          "type": {"key": "string", "value": "string",
                   "min": 0, "max": "unlimited"}}}},
index 5bbe943..e625940 100644 (file)
           specified.  Defaults to <code>100</code>.
       </column>
 
+      <column name="bfd" key="decay_min_rx" type='{"type": "integer"}'>
+          <code>decay_min_rx</code> is used to set the <code>min_rx</code>,
+          when there is no obvious incoming data traffic at the interface.
+          It cannot be set less than the <code>min_rx</code>. The decay feature
+          is disabled by setting the <code>decay_min_rx</code> to 0. And the
+          feature is reset everytime itself or <code>min_rx</code> is
+          reconfigured.
+      </column>
+
+      <column name="bfd" key="forwarding_if_rx" type='{"type": "boolean"}'>
+          When <code>forwarding_if_rx</code> is true the interface will be
+          considered capable of packet I/O as long as there is packet
+          received at interface.  This is important in that when link becomes
+          temporarily conjested, consecutive BFD control packets can be lost.
+          And the <code>forwarding_if_rx</code> can prevent link failover by
+          detecting non-control packets received at interface.
+      </column>
+
       <column name="bfd" key="cpath_down" type='{"type": "boolean"}'>
           Concatenated path down may be used when the local system should not
           have traffic forwarded to it for some reason other than a connectivty
       referenced from a <ref table="Flow_Sample_Collector_Set"/>.
     </column>
 
+    <column name="cache_active_timeout">
+      The maximum period in seconds for which an IPFIX flow record is
+      cached and aggregated before being sent.  If not specified,
+      defaults to 0.  If 0, caching is disabled.
+    </column>
+
+    <column name="cache_max_flows">
+      The maximum number of IPFIX flow records that can be cached at a
+      time.  If not specified, defaults to 0.  If 0, caching is
+      disabled.
+    </column>
+
     <group title="Common Columns">
       The overall purpose of these columns is described under <code>Common
       Columns</code> at the beginning of this document.