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
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
post-v1.12.0
---------------------
+ - OpenFlow:
+ * Support matching, rewriting SCTP ports
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
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+]
#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>
#include <net/ipv6.h>
#include <net/checksum.h>
#include <net/dsfield.h>
+#include <net/sctp/checksum.h>
#include "checksum.h"
#include "datapath.h"
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;
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;
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;
}
#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>
| (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)
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))
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))
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) +
* 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.
*
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);
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);
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)
}
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);
}
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;
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;
[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),
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;
}
/* 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;
}
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;
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;
}
}
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);
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;
} 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. */
} 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. */
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;
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 \
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
--- /dev/null
+#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
#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,
--- /dev/null
+#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
*
* @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,
* 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.
*
* @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
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.
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 */
__be16 udp_dst;
};
+struct ovs_key_sctp {
+ __be16 sctp_src;
+ __be16 sctp_dst;
+};
+
struct ovs_key_icmp {
__u8 icmp_type;
__u8 icmp_code;
#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
#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 {}
lib/compiler.h \
lib/coverage.c \
lib/coverage.h \
+ lib/crc32c.c \
+ lib/crc32c.h \
lib/csum.c \
lib/csum.h \
lib/daemon.c \
#include "hash.h"
#include "hmap.h"
#include "list.h"
+#include "netdev.h"
#include "netlink.h"
#include "odp-util.h"
#include "ofpbuf.h"
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. */
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;
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 *,
* 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];
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
|| (!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;
}
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);
}
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);
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);
}
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;
/* 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;
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. */
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);
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)
{
struct bfd;
struct flow;
struct flow_wildcards;
+struct netdev;
struct ofpbuf;
struct smap;
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 */
to->tx_bytes += delta;
/* Arrange for flows to be revalidated. */
+ hash->slave = to;
bond->bond_revalidate = true;
}
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. */
/*
- * 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.
}
static const uint8_t *
-cfm_ccm_addr(const struct cfm *cfm)
+cfm_ccm_addr(struct cfm *cfm)
{
bool extended;
atomic_read(&cfm->extended, &extended);
/* 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);
* '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;
}
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;
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) {
#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
/*
- * 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.
#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"
#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 *);
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_)
{
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);
}
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. */
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);
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;
}
}
-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;
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);
}
/*
- * 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);
/* Implementation detail. */
#define COVERAGE_DEFINE__(COUNTER) \
- extern struct coverage_counter counter_##COUNTER; \
- struct coverage_counter counter_##COUNTER = { #COUNTER, 0, 0 }
#endif /* coverage.h */
--- /dev/null
+/*-
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
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,
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
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
/* 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
/* 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);
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 {
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++) {
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)
{
}
}
+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)
{
* - 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
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) {
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;
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)) {
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;
}
}
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;
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. */
-/* 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.
struct lacp *lacp;
if (ovsthread_once_start(&once)) {
- ovs_mutex_init(&mutex, PTHREAD_MUTEX_RECURSIVE);
+ ovs_mutex_init_recursive(&mutex);
ovsthread_once_done(&once);
}
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;
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;
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),
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;
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:
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:
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;
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;
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;
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);
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);
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;
{
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
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;
}
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:
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 */
/* 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,
/*
- * 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");
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);
/* 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) {
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;
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;
}
{
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);
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);
}
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);
}
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
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));
/* 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);
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:
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;
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";
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);
}
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);
}
}
+ {
+ 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;
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;
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;
}
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;
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;
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;
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;
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);
}
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)
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)) {
}
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);
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;
}
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. */
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));
}
}
};
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,
/* 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
/* 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. */
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);
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);
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:
{ "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 },
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;
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));
}
}
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;
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;
case IPPROTO_TCP:
case IPPROTO_UDP:
+ case IPPROTO_SCTP:
if (!(wc & (OFPFW11_TP_SRC))) {
match_set_tp_src(match, ofmatch->tp_src);
}
}
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;
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;
#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 \
*
* 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.
*
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); \
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); \
#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; \
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;
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_)
{
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.
* 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)
#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)
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;
#include <stdlib.h>
#include "byte-order.h"
#include "csum.h"
+#include "crc32c.h"
#include "flow.h"
#include "hmap.h"
#include "dynamic-string.h"
}
}
+/* 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
};
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;
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);
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;
/*
- * 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.
* 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);
}
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();
#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"
*
* 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);
0, /* first_dropped */ \
0, /* last_dropped */ \
0, /* n_dropped */ \
- OVS_ADAPTIVE_MUTEX_INITIALIZER /* mutex */ \
+ OVS_MUTEX_INITIALIZER /* mutex */ \
}
/* Configuring how each module logs messages. */
#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"
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 {
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;
};
});
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 */
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(
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));
}
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));
}
}
}
+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,
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)
{
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,
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;
}
}
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;
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)
{
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,
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;
}
}
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;
}
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));
}
}
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);
}
}
{
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;
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);
}
}
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);
}
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);
}
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) {
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;
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;
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. */
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. */
}
}
- /* 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);
}
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;
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,
}
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);
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
}
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);
+}
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 */
{
if (ds->sflow_agent) {
sfl_agent_release(ds->sflow_agent);
+ free(ds->sflow_agent);
ds->sflow_agent = NULL;
}
collectors_destroy(ds->collectors);
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);
}
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;
}
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);
}
}
}
- 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) {
/* 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 *);
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)
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.
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);
}
/* 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)
{
{
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);
}
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;
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;
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);
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);
}
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 =
{
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;
}
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;
}
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;
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) {
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"
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;
void
rule_release(struct rule_dpif *rule)
+ OVS_NO_THREAD_SAFETY_ANALYSIS
{
if (rule) {
ovs_rwlock_unlock(&rule->up.evict);
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;
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);
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;
};
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);
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);
/* 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. */
* 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;
}
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;
}
}
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));
}
}
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));
+ }
}
}
}
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;
}
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;
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;
/* 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;
}
* 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;
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 {
}
/* Flow-based everything */
- match.ip_src = 0;
match.ip_src_flow = true;
tnl_port = tnl_find_exact(&match);
if (tnl_port) {
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
])
])
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])
#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
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
[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
AT_CLEANUP
AT_SETUP([test TCP/IP checksumming])
-AT_CHECK([test-csum], [0], [....#....#....##................................#................................#
+AT_CHECK([test-csum], [0], [....#....#....###................................#................................#
])
AT_CLEANUP
])
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 "\
])
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 "\
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
])
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
])
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
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)
]])
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)
]])
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)
]])
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)
]])
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
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
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
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
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
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
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
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)
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
{
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
#include <config.h>
#include "csum.h"
+#include "crc32c.h"
#include <inttypes.h>
#include <netinet/in.h>
#include <stdio.h>
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)
{
}
test_rfc1624();
+ test_crc32c();
/* Test recalc_csum16(). */
for (i = 0; i < 32; i++) {
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,
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));
.
.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).
.
.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
.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
.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.
.
.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.
.
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
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
.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):
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) {
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++;
}
}
\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;
{"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"}}}},
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.