2.0 yes [*] [*] [*] ---
2.1 yes [*] [*] [*] ---
2.2 yes [*] [*] [*] [%]
+ 2.3 yes yes yes yes [%]
[*] Supported, with one or more missing features.
[%] Support is unsafe: ovs-vswitchd will abort when certain
unimplemented features are tested.
- Because of missing features, OpenFlow 1.1, 1.2, and 1.3 must be
- enabled manually. The following command enables OpenFlow 1.0, 1.1,
- 1.2, and 1.3 on bridge br0:
+ Open vSwitch 2.3 enables OpenFlow 1.0, 1.1, 1.2, and 1.3 by default
+ in ovs-vswitchd. In Open vSwitch 1.10 through 2.2, OpenFlow 1.1,
+ 1.2, and 1.3 must be enabled manually in ovs-vswitchd. Either way,
+ the user may override the default:
- ovs-vsctl set bridge br0 protocols=OpenFlow10,OpenFlow11,OpenFlow12,OpenFlow13
+ - To enable OpenFlow 1.0, 1.1, 1.2, and 1.3 on bridge br0:
- Use the -O option to enable support for later versions of OpenFlow
- in ovs-ofctl. For example:
+ ovs-vsctl set bridge br0 protocols=OpenFlow10,OpenFlow11,OpenFlow12,OpenFlow13
+
+ - To enable only OpenFlow 1.0 on bridge br0:
+
+ ovs-vsctl set bridge br0 protocols=OpenFlow10
+
+ All current versions of ovs-ofctl enable only OpenFlow 1.0 by
+ default. Use the -O option to enable support for later versions of
+ OpenFlow in ovs-ofctl. For example:
ovs-ofctl -O OpenFlow13 dump-flows br0
invoked with a special --enable-of14 command line option.
OPENFLOW-1.1+ in the Open vSwitch source tree tracks support for
- OpenFlow 1.1 and later features. When support for a given OpenFlow
- version is solidly implemented, Open vSwitch will enable that
- version by default.
+ OpenFlow 1.1 and later features. When support for OpenFlow 1.4 is
+ solidly implemented, Open vSwitch will enable that version by
+ default.
Q: Does Open vSwitch support MPLS?
Post-v2.2.0
---------------------
-
+ - OpenFlow 1.1, 1.2, and 1.3 are now enabled by default in
+ ovs-vswitchd.
+ - Linux kernel datapath now has an exact match cache optimizing the
+ flow matching process.
+ - Datapath flows now have partially wildcarded tranport port field
+ matches. This reduces userspace upcalls, but increases the
+ number of different masks in the datapath. The kernel datapath
+ exact match cache removes the overhead of matching the incoming
+ packets with the larger number of masks, but when paired with an
+ older kernel module, some workloads may perform worse with the
+ new userspace.
v2.2.0 - xx xxx xxx
---------------------
if (ce->skb_hash == skb_hash) {
struct sw_flow_mask *mask;
- struct sw_flow *flow;
mask = rcu_dereference_ovsl(ma->masks[ce->mask_index]);
if (mask) {
bool forwarding_if_rx;
long long int forwarding_if_rx_detect_time;
+ /* When 'bfd->forwarding_if_rx' is set, at least one bfd control packet
+ * is required to be received every 100 * bfd->cfg_min_rx. If bfd
+ * control packet is not received within this interval, even if data
+ * packets are received, the bfd->forwarding will still be false. */
+ long long int demand_rx_bfd_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 */
}
/* XXX: RFC 5880 Section 6.8.6 Demand mode related calculations here. */
+ if (bfd->forwarding_if_rx) {
+ bfd->demand_rx_bfd_time = time_msec() + 100 * bfd->cfg_min_rx;
+ }
+
out:
bfd_forwarding__(bfd);
ovs_mutex_unlock(&mutex);
static bool
bfd_forwarding__(struct bfd *bfd) OVS_REQUIRES(mutex)
{
- long long int time;
+ long long int now = time_msec();
+ bool forwarding_if_rx;
bool last_forwarding = bfd->last_forwarding;
if (bfd->forwarding_override != -1) {
return bfd->forwarding_override == 1;
}
- time = bfd->forwarding_if_rx_detect_time;
- bfd->last_forwarding = (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;
+ forwarding_if_rx = bfd->forwarding_if_rx
+ && bfd->forwarding_if_rx_detect_time > now
+ && bfd->demand_rx_bfd_time > now;
+
+ bfd->last_forwarding = (bfd->state == STATE_UP || forwarding_if_rx)
+ && bfd->rmt_diag != DIAG_PATH_DOWN
+ && bfd->rmt_diag != DIAG_CPATH_DOWN
+ && bfd->rmt_diag != DIAG_RCPATH_DOWN;
if (bfd->last_forwarding != last_forwarding) {
bfd->flap_count++;
bfd_status_changed(bfd);
/* True when the variables returned by cfm_get_*() are changed
* since last check. */
bool status_changed;
+
+ /* When 'cfm->demand' is set, at least one ccm is required to be received
+ * every 100 * cfm_interval. If ccm is not received within this interval,
+ * even if data packets are received, the cfm fault will be set. */
+ struct timer demand_rx_ccm_t;
};
/* Remote MPs represent foreign network entities that are configured to have
if (cfm->demand) {
uint64_t rx_packets = cfm_rx_packets(cfm);
demand_override = hmap_count(&cfm->remote_mps) == 1
- && rx_packets > cfm->rx_packets;
+ && rx_packets > cfm->rx_packets
+ && !timer_expired(&cfm->demand_rx_ccm_t);
cfm->rx_packets = rx_packets;
}
rmp->mpid = ccm_mpid;
if (!cfm_fault) {
rmp->num_health_ccm++;
+ if (cfm->demand) {
+ timer_set_duration(&cfm->demand_rx_ccm_t,
+ 100 * cfm->ccm_interval_ms);
+ }
}
rmp->recv = true;
cfm->recv_fault |= cfm_fault;
VLOG_DEFINE_THIS_MODULE(classifier);
struct trie_node;
+struct trie_ctx;
+
+/* Ports trie depends on both ports sharing the same ovs_be32. */
+#define TP_PORTS_OFS32 (offsetof(struct flow, tp_src) / 4)
+BUILD_ASSERT_DECL(TP_PORTS_OFS32 == offsetof(struct flow, tp_dst) / 4);
/* Prefix trie for a 'field' */
struct cls_trie {
uint8_t index_ofs[CLS_MAX_INDICES]; /* u32 flow segment boundaries. */
struct hindex indices[CLS_MAX_INDICES]; /* Staged lookup indices. */
unsigned int trie_plen[CLS_MAX_TRIES]; /* Trie prefix length in 'mask'. */
+ int ports_mask_len;
+ struct trie_node *ports_trie; /* NULL if none. */
struct minimask mask; /* Wildcards for fields. */
/* 'mask' must be the last field. */
};
return cls_match;
}
-struct trie_ctx;
static struct cls_subtable *find_subtable(const struct cls_classifier *,
const struct minimask *);
static struct cls_subtable *insert_subtable(struct cls_classifier *,
const struct mf_field *);
static unsigned int trie_lookup(const struct cls_trie *, const struct flow *,
unsigned int *checkbits);
-
+static unsigned int trie_lookup_value(const struct trie_node *,
+ const ovs_be32 value[],
+ unsigned int *checkbits);
static void trie_destroy(struct trie_node *);
static void trie_insert(struct cls_trie *, const struct cls_rule *, int mlen);
+static void trie_insert_prefix(struct trie_node **, const ovs_be32 *prefix,
+ int mlen);
static void trie_remove(struct cls_trie *, const struct cls_rule *, int mlen);
+static void trie_remove_prefix(struct trie_node **, const ovs_be32 *prefix,
+ int mlen);
static void mask_set_prefix_bits(struct flow_wildcards *, uint8_t be32ofs,
unsigned int nbits);
static bool mask_prefix_bits_set(const struct flow_wildcards *,
return partition;
}
+static inline ovs_be32 minimatch_get_ports(const struct minimatch *match)
+{
+ /* Could optimize to use the same map if needed for fast path. */
+ return MINIFLOW_GET_BE32(&match->flow, tp_src)
+ & MINIFLOW_GET_BE32(&match->mask.masks, tp_src);
+}
+
/* Inserts 'rule' into 'cls'. Until 'rule' is removed from 'cls', the caller
* must not modify or free it.
*
trie_insert(&cls->tries[i], rule, subtable->trie_plen[i]);
}
}
+
+ /* Ports trie. */
+ if (subtable->ports_mask_len) {
+ /* We mask the value to be inserted to always have the wildcarded
+ * bits in known (zero) state, so we can include them in comparison
+ * and they will always match (== their original value does not
+ * matter). */
+ ovs_be32 masked_ports = minimatch_get_ports(&rule->match);
+
+ trie_insert_prefix(&subtable->ports_trie, &masked_ports,
+ subtable->ports_mask_len);
+ }
+
return NULL;
} else {
struct cls_rule *old_cls_rule = old_rule->cls_rule;
ovs_assert(cls_match);
subtable = find_subtable(cls, &rule->match.mask);
-
ovs_assert(subtable);
+ if (subtable->ports_mask_len) {
+ ovs_be32 masked_ports = minimatch_get_ports(&rule->match);
+
+ trie_remove_prefix(&subtable->ports_trie,
+ &masked_ports, subtable->ports_mask_len);
+ }
for (i = 0; i < cls->n_tries; i++) {
if (subtable->trie_plen[i]) {
trie_remove(&cls->tries[i], rule, subtable->trie_plen[i]);
cls->tries[i].field);
}
+ /* Ports trie. */
+ subtable->ports_trie = NULL;
+ subtable->ports_mask_len
+ = 32 - ctz32(ntohl(MINIFLOW_GET_BE32(&mask->masks, tp_src)));
+
hmap_insert(&cls->subtables, &subtable->hmap_node, hash);
elem.subtable = subtable;
elem.tag = subtable->tag;
}
}
+ trie_destroy(subtable->ports_trie);
+
for (i = 0; i < subtable->n_indices; i++) {
hindex_destroy(&subtable->indices[i]);
}
* but it didn't match. */
rule = NULL;
}
+ if (!rule && subtable->ports_mask_len) {
+ /* Ports are always part of the final range, if any.
+ * No match was found for the ports. Use the ports trie to figure out
+ * which ports bits to unwildcard. */
+ unsigned int mbits;
+ ovs_be32 value, mask;
+
+ mask = MINIFLOW_GET_BE32(&subtable->mask.masks, tp_src);
+ value = ((OVS_FORCE ovs_be32 *)flow)[TP_PORTS_OFS32] & mask;
+ trie_lookup_value(subtable->ports_trie, &value, &mbits);
+
+ ((OVS_FORCE ovs_be32 *)&wc->masks)[TP_PORTS_OFS32] |=
+ mask & htonl(~0 << (32 - mbits));
+
+ ofs.start = TP_PORTS_OFS32;
+ goto range_out;
+ }
out:
/* Must unwildcard all the fields, as they were looked at. */
flow_wildcards_fold_minimask(wc, &subtable->mask);
static void
trie_insert(struct cls_trie *trie, const struct cls_rule *rule, int mlen)
{
- const ovs_be32 *prefix = minimatch_get_prefix(&rule->match, trie->field);
+ trie_insert_prefix(&trie->root,
+ minimatch_get_prefix(&rule->match, trie->field), mlen);
+}
+
+static void
+trie_insert_prefix(struct trie_node **edge, const ovs_be32 *prefix, int mlen)
+{
struct trie_node *node;
- struct trie_node **edge;
int ofs = 0;
/* Walk the tree. */
- for (edge = &trie->root;
- (node = *edge) != NULL;
+ for (; (node = *edge) != NULL;
edge = trie_next_edge(node, prefix, ofs)) {
unsigned int eqbits = trie_prefix_equal_bits(node, prefix, ofs, mlen);
ofs += eqbits;
static void
trie_remove(struct cls_trie *trie, const struct cls_rule *rule, int mlen)
{
- const ovs_be32 *prefix = minimatch_get_prefix(&rule->match, trie->field);
+ trie_remove_prefix(&trie->root,
+ minimatch_get_prefix(&rule->match, trie->field), mlen);
+}
+
+/* 'mlen' must be the (non-zero) CIDR prefix length of the 'trie->field' mask
+ * in 'rule'. */
+static void
+trie_remove_prefix(struct trie_node **root, const ovs_be32 *prefix, int mlen)
+{
struct trie_node *node;
struct trie_node **edges[sizeof(union mf_value) * 8];
int depth = 0, ofs = 0;
/* Walk the tree. */
- for (edges[depth] = &trie->root;
+ for (edges[0] = root;
(node = *edges[depth]) != NULL;
edges[++depth] = trie_next_edge(node, prefix, ofs)) {
unsigned int eqbits = trie_prefix_equal_bits(node, prefix, ofs, mlen);
+
if (eqbits < node->nbits) {
/* Mismatch, nothing to be removed. This should never happen, as
* only rules in the classifier are ever removed. */
case OFP13_VERSION:
ds_put_cstr(string, " (OF1.3)");
break;
+ case OFP14_VERSION:
+ ds_put_cstr(string, " (OF1.4)");
+ break;
default:
ds_put_format(string, " (OF 0x%02"PRIx8")", oh->version);
break;
uint32_t ofputil_protocols_to_version_bitmap(enum ofputil_protocol);
enum ofputil_protocol ofputil_protocols_from_version_bitmap(uint32_t bitmap);
-/* Bitmap of OpenFlow versions that Open vSwitch supports. */
-#define OFPUTIL_SUPPORTED_VERSIONS \
- ((1u << OFP10_VERSION) | (1u << OFP12_VERSION) | (1u << OFP13_VERSION))
-
-/* Bitmap of OpenFlow versions to enable by default (a subset of
- * OFPUTIL_SUPPORTED_VERSIONS). */
-#define OFPUTIL_DEFAULT_VERSIONS (1u << OFP10_VERSION)
+/* Bitmaps of OpenFlow versions that Open vSwitch supports, and that it enables
+ * by default. When Open vSwitch has experimental or incomplete support for
+ * newer versions of OpenFlow, those versions should not be supported by
+ * default and thus should be omitted from the latter bitmap. */
+#define OFPUTIL_SUPPORTED_VERSIONS ((1u << OFP10_VERSION) | \
+ (1u << OFP11_VERSION) | \
+ (1u << OFP12_VERSION) | \
+ (1u << OFP13_VERSION))
+#define OFPUTIL_DEFAULT_VERSIONS OFPUTIL_SUPPORTED_VERSIONS
enum ofputil_protocol ofputil_protocols_from_string(const char *s);
uint16_t user_cookie_offset;/* Used for user_action_cookie fixup. */
bool exit; /* No further actions should be processed. */
+ bool use_recirc; /* Should generate recirc? */
+ struct xlate_recirc recirc; /* Information used for generating
+ * recirculation actions */
+
/* OpenFlow 1.1+ action set.
*
* 'action_set' accumulates "struct ofpact"s added by OFPACT_WRITE_ACTIONS.
return xport->xbundle;
}
- /* Special-case OFPP_NONE, which a controller may use as the ingress
- * port for traffic that it is sourcing. */
- if (in_port == OFPP_NONE) {
+ /* Special-case OFPP_NONE (OF1.0) and OFPP_CONTROLLER (OF1.1+),
+ * which a controller may use as the ingress port for traffic that
+ * it is sourcing. */
+ if (in_port == OFPP_CONTROLLER || in_port == OFPP_NONE) {
return &ofpp_none_bundle;
}
/* Partially configured bundle with no slaves. Drop the packet. */
return;
} else if (!out_xbundle->bond) {
- ctx->xout->use_recirc = false;
+ ctx->use_recirc = false;
xport = CONTAINER_OF(list_front(&out_xbundle->xports), struct xport,
bundle_node);
} else {
struct ofport_dpif *ofport;
- struct xlate_recirc *xr = &ctx->xout->recirc;
+ struct xlate_recirc *xr = &ctx->recirc;
struct flow_wildcards *wc = &ctx->xout->wc;
if (ctx->xbridge->enable_recirc) {
- ctx->xout->use_recirc = bond_may_recirc(
+ ctx->use_recirc = bond_may_recirc(
out_xbundle->bond, &xr->recirc_id, &xr->hash_basis);
- if (ctx->xout->use_recirc) {
+ if (ctx->use_recirc) {
/* Only TCP mode uses recirculation. */
xr->hash_alg = OVS_HASH_ALG_L4;
bond_update_post_recirc_rules(out_xbundle->bond, false);
/* If ctx->xout->use_recirc is set, the main thread will handle stats
* accounting for this bond. */
- if (!ctx->xout->use_recirc) {
+ if (!ctx->use_recirc) {
if (ctx->xin->resubmit_stats) {
bond_account(out_xbundle->bond, &ctx->xin->flow, vid,
ctx->xin->resubmit_stats->n_bytes);
&ctx->xout->odp_actions,
&ctx->xout->wc);
- if (ctx->xout->use_recirc) {
+ if (ctx->use_recirc) {
struct ovs_action_hash *act_hash;
- struct xlate_recirc *xr = &ctx->xout->recirc;
+ struct xlate_recirc *xr = &ctx->recirc;
/* Hash action. */
act_hash = nl_msg_put_unspec_uninit(&ctx->xout->odp_actions,
ctx.orig_skb_priority = flow->skb_priority;
ctx.table_id = 0;
ctx.exit = false;
+ ctx.use_recirc = false;
if (!xin->ofpacts && !ctx.rule) {
ctx.table_id = rule_dpif_lookup(ctx.xbridge->ofproto, flow,
ctx.rule = rule;
}
xout->fail_open = ctx.rule && rule_dpif_is_fail_open(ctx.rule);
- xout->use_recirc = false;
if (xin->ofpacts) {
ofpacts = xin->ofpacts;
ofp_port_t nf_output_iface; /* Output interface index for NetFlow. */
mirror_mask_t mirrors; /* Bitmap of associated mirrors. */
- bool use_recirc; /* Should generate recirc? */
- struct xlate_recirc recirc; /* Information used for generating
- * recirculation actions */
uint64_t odp_actions_stub[256 / 8];
struct ofpbuf odp_actions;
};
return error;
}
-/* Tests whether 'backer''s datapath supports recirculation Only newer datapath
- * supports OVS_KEY_ATTR in OVS_ACTION_ATTR_USERSPACE actions. We need to
- * disable some features on older datapaths that don't support this feature.
+/* Tests whether 'backer''s datapath supports recirculation. Only newer
+ * datapaths support OVS_KEY_ATTR_RECIRC_ID in keys. We need to disable some
+ * features on older datapaths that don't support this feature.
*
* Returns false if 'backer' definitely does not support recirculation, true if
* it seems to support recirculation or if at least the error we get is
ovs_rwlock_unlock(&ofproto->ml->rwlock);
}
+ /* Always updates the ofproto->pins_seqno to avoid frequent wakeup during
+ * flow restore. Even though nothing is processed during flow restore,
+ * all queued 'pins' will be handled immediately when flow restore
+ * completes. */
+ ofproto->pins_seqno = seq_read(ofproto->pins_seq);
+
/* Do not perform any periodic activity required by 'ofproto' while
* waiting for flow restore to complete. */
if (!ofproto_get_flow_restore_wait()) {
}
}
- /* Always updates the ofproto->pins_seqno to avoid frequent wakeup during
- * flow restore. Even though nothing is processed during flow restore,
- * all queued 'pins' will be handled immediately when flow restore
- * completes. */
- ofproto->pins_seqno = seq_read(ofproto->pins_seq);
-
if (ofproto->netflow) {
netflow_run(ofproto->netflow);
}
# forwarding_if_rx Test1
# Test1 tests the case when bfd is only enabled on one end of the link.
-# Under this situation, the bfd state should be DOWN and the forwarding
-# flag should be FALSE by default. However, if forwarding_if_rx is
-# enabled, as long as there is packet received, the bfd forwarding flag
-# should be TRUE.
+# Under this situation, the forwarding flag should always be false, even
+# though there is data packet received, since there is no bfd control
+# packet received during the demand_rx_bfd interval.
AT_SETUP([bfd - bfd forwarding_if_rx - bfd on one side])
OVS_VSWITCHD_START([add-br br1 -- set bridge br1 datapath-type=dummy -- \
add-port br1 p1 -- set Interface p1 type=patch \
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])
-
-# 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.
+# the forwarding flag should be false, due to the demand_rx_bfd.
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
AT_CHECK([ovs-vsctl del-br br1], [0], [ignore])
AT_CLEANUP
+# forwarding_if_rx Test4
+# Test4 is for testing the demand_rx_bfd feature.
+# bfd is enabled on both ends of the link.
+AT_SETUP([bfd - bfd forwarding_if_rx - demand_rx_bfd])
+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:forwarding_if_rx=true -- \
+ 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])
+
+# disable the bfd on p1.
+AT_CHECK([ovs-vsctl set Interface p1 bfd:enable=false], [0])
+
+# advance clock by 4000ms, while receiving packets.
+# the STATE should go DOWN, due to Control Detection Time Expired.
+# but forwarding flag should be still true.
+for i in `seq 0 7`
+do
+ ovs-appctl time/warp 500
+ AT_CHECK([ovs-ofctl packet-out br1 3 2 "90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202"],
+ [0], [stdout], [])
+done
+BFD_CHECK([p0], [true], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic])
+
+# advance clock long enough to trigger the demand_bfd_rx interval
+# (100 * bfd->cfm_min_rx), forwarding flag should go down since there
+# is no bfd control packet received during the demand_rx_bfd.
+for i in `seq 0 120`
+do
+ ovs-appctl time/warp 300
+ AT_CHECK([ovs-ofctl packet-out br1 3 2 "90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202"],
+ [0], [stdout], [])
+done
+BFD_CHECK([p0], [false], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic])
+
+# now enable the bfd on p1 again.
+AT_CHECK([ovs-vsctl set Interface p1 bfd:enable=true], [0])
+# advance clock by 5000ms. and p1 and p0 should be all up.
+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], [300ms], [500ms])
+
+# disable the bfd on p1 again.
+AT_CHECK([ovs-vsctl set Interface p1 bfd:enable=false], [0])
+# advance clock long enough to trigger the demand_rx_bfd,
+# forwarding flag should go down since there is no bfd control packet
+# received during the demand_rx_bfd.
+for i in `seq 0 120`
+do
+ ovs-appctl time/warp 300
+ AT_CHECK([ovs-ofctl packet-out br1 3 2 "90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202"],
+ [0], [stdout], [])
+done
+BFD_CHECK([p0], [false], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic])
+
+AT_CHECK([ovs-vsctl del-br br1], [0], [ignore])
+AT_CLEANUP
+
# test bfd:flap_count.
# This test contains three part:
# part 1. tests the flap_count on normal bfd monitored link.
# Part-3 now turn on forwarding_if_rx.
AT_CHECK([ovs-vsctl set Interface p0 bfd:forwarding_if_rx=true], [0])
+for i in `seq 0 10`; do ovs-appctl time/warp 100; done
# disable the bfd on p1.
AT_CHECK([ovs-vsctl set Interface p1 bfd:enable=false], [0])
# flap_count should remain unchanged.
BFD_VSCTL_LIST_IFACE([p0], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["5"])
-# stop the traffic for 4000ms, the forwarding flag of p0 should turn false.
+# stop the traffic for more than 100 * bfd->cfm_min_rx ms, the forwarding flag of p0 should turn false.
# and there should be the increment of flap_count.
-for i in `seq 0 7`; do ovs-appctl time/warp 500; done
+for i in `seq 0 120`; do ovs-appctl time/warp 100; done
BFD_CHECK([p0], [false], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic])
BFD_VSCTL_LIST_IFACE([p0], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["6"])
AT_CHECK([ovs-ofctl packet-out br1 3 2 "90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202"],
[0], [stdout], [])
done
-BFD_CHECK([p0], [true], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic])
-# flap_count should be incremented again.
-BFD_VSCTL_LIST_IFACE([p0], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["7"])
-
-# stop the traffic for 4000ms, the forwarding flag of p0 should turn false.
-# and there should be the increment of flap_count.
-for i in `seq 0 7`; do ovs-appctl time/warp 500; done
+# forwarding should be false, since there is still no bfd control packet received.
BFD_CHECK([p0], [false], [false], [none], [down], [Control Detection Time Expired], [none], [down], [No Diagnostic])
-BFD_VSCTL_LIST_IFACE([p0], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["8"])
+BFD_VSCTL_LIST_IFACE([p0], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["6"])
# turn on the bfd on p1.
AT_CHECK([ovs-vsctl set interface p1 bfd:enable=true])
for i in `seq 0 49`; do ovs-appctl time/warp 100; done
# even though there is no data traffic, since p1 bfd is on again, should increment the flap_count.
-BFD_VSCTL_LIST_IFACE([p0], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["9"])
+BFD_VSCTL_LIST_IFACE([p0], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["7"])
BFD_VSCTL_LIST_IFACE([p1], ["s/^.*flap_count=\(.*\), forwarding.*$/\1/p"], ["1"])
OVS_VSWITCHD_STOP
])
])
+m4_define([CFM_CHECK_EXTENDED_FAULT], [
+AT_CHECK([ovs-appctl cfm/show $1 | sed -e '/next CCM tx:/d' | sed -e '/next fault check:/d' | sed -e '/recv since check:/d'],[0],
+[dnl
+---- $1 ----
+MPID $2: extended
+ fault: $3
+ average health: $4
+ opstate: $5
+ remote_opstate: $6
+ interval: $7
+])
+])
+
m4_define([CFM_VSCTL_LIST_IFACE], [
AT_CHECK([ovs-vsctl list interface $1 | sed -n '/$2/p'],[0],
[dnl
OVS_VSWITCHD_STOP
AT_CLEANUP
+# test demand_rx_ccm under demand mode.
+AT_SETUP([cfm - demand_rx_ccm])
+#Create 2 bridges connected by patch ports and enable cfm
+OVS_VSWITCHD_START([add-br br1 -- \
+ set bridge br1 datapath-type=dummy \
+ other-config:hwaddr=aa:55:aa:56:00:00 -- \
+ 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 cfm_mpid=1 other_config:cfm_interval=300 other_config:cfm_extended=true other_config:cfm_demand=true -- \
+ set Interface p1 cfm_mpid=2 other_config:cfm_interval=300 other_config:cfm_extended=true other_config:cfm_demand=true])
+
+ovs-appctl time/stop
+# wait for a while to stablize cfm. (need a longer time, since in demand mode
+# the fault interval is (MAX(ccm_interval_ms, 500) * 3.5) ms)
+for i in `seq 0 200`; do ovs-appctl time/warp 100; done
+CFM_CHECK_EXTENDED([p0], [1], [100], [up], [up], [300ms], [2], [up])
+CFM_CHECK_EXTENDED([p1], [2], [100], [up], [up], [300ms], [1], [up])
+
+# turn off the cfm on p1.
+AT_CHECK([ovs-vsctl clear Interface p1 cfm_mpid])
+# cfm should never go down while receiving data packets.
+for i in `seq 0 200`
+do
+ ovs-appctl time/warp 100
+ AT_CHECK([ovs-ofctl packet-out br1 3 2 "90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202"],
+ [0], [stdout], [])
+done
+CFM_CHECK_EXTENDED([p0], [1], [0], [up], [up], [300ms], [2], [up])
+
+# wait longer, since the demand_rx_ccm interval is 100 * 300 ms.
+# since there is no ccm received, the [recv] fault should be raised.
+for i in `seq 0 200`
+do
+ ovs-appctl time/warp 100
+ AT_CHECK([ovs-ofctl packet-out br1 3 2 "90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202"],
+ [0], [stdout], [])
+done
+CFM_CHECK_EXTENDED_FAULT([p0], [1], [recv], [0], [up], [up], [300ms])
+
+# now turn on the cfm on p1 again,
+AT_CHECK([ovs-vsctl set Interface p1 cfm_mpid=2])
+# cfm should be up for both p0 and p1
+for i in `seq 0 200`; do ovs-appctl time/warp 100; done
+CFM_CHECK_EXTENDED([p0], [1], [100], [up], [up], [300ms], [2], [up])
+CFM_CHECK_EXTENDED([p1], [2], [100], [up], [up], [300ms], [1], [up])
+
+# now turn off the cfm on p1 again
+AT_CHECK([ovs-vsctl clear Interface p1 cfm_mpid])
+# since there is no ccm received, the [recv] fault should be raised.
+for i in `seq 0 400`
+do
+ ovs-appctl time/warp 100
+ AT_CHECK([ovs-ofctl packet-out br1 3 2 "90e2ba01475000101856b2e80806000108000604000100101856b2e80202020300000000000002020202"],
+ [0], [stdout], [])
+done
+CFM_CHECK_EXTENDED_FAULT([p0], [1], [recv], [0], [up], [up], [300ms])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
# test cfm_flap_count.
AT_SETUP([cfm - flap_count])
#Create 2 bridges connected by patch ports and enable cfm
])
AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=10.1.2.15,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=79'], [0], [stdout])
AT_CHECK([tail -2 stdout], [0],
- [Megaflow: recirc_id=0,skb_priority=0,tcp,in_port=1,nw_dst=10.1.2.15,nw_frag=no,tp_dst=79
+ [Megaflow: recirc_id=0,skb_priority=0,tcp,in_port=1,nw_dst=10.1.2.15,nw_frag=no,tp_dst=0x40/0xfff0
Datapath actions: 2
])
OVS_VSWITCHD_STOP
table=0 in_port=1 priority=16,tcp,nw_dst=10.1.0.0/255.255.0.0,action=output(3)
table=0 in_port=1 priority=32,tcp,nw_dst=10.1.2.0/255.255.255.0,tp_src=79,action=output(2)
table=0 in_port=1 priority=33,tcp,nw_dst=10.1.2.15,tp_dst=80,action=drop
+table=0 in_port=1 priority=33,tcp,nw_dst=10.1.2.15,tp_dst=8080,action=output(2)
+table=0 in_port=1 priority=33,tcp,nw_dst=10.1.2.15,tp_dst=192,action=output(2)
table=0 in_port=1 priority=0,ip,action=drop
table=0 in_port=2 priority=16,tcp,nw_dst=192.168.0.0/255.255.0.0,action=output(1)
table=0 in_port=2 priority=0,ip,action=drop
])
AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=10.1.2.15,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=79'], [0], [stdout])
AT_CHECK([tail -2 stdout], [0],
- [Megaflow: recirc_id=0,skb_priority=0,tcp,in_port=1,nw_dst=10.1.2.15,nw_frag=no,tp_src=8,tp_dst=79
+ [Megaflow: recirc_id=0,skb_priority=0,tcp,in_port=1,nw_dst=10.1.2.15,nw_frag=no,tp_src=0x0/0xffc0,tp_dst=0x40/0xfff0
Datapath actions: 3
])
OVS_VSWITCHD_STOP(["/'prefixes' with incompatible field: ipv6_label/d"])
05 1e 00 18 00 00 00 0a \
00 00 00 02 02 00 00 00 ff ff ff ff ff ff ff ff \
"], [0], [dnl
-OFPT_ROLE_STATUS (OF 0x05) (xid=0xa): role=master reason=experimenter_data_changed
+OFPT_ROLE_STATUS (OF1.4) (xid=0xa): role=master reason=experimenter_data_changed
])
AT_CLEANUP
05 1e 00 18 00 00 00 0a \
00 00 00 02 01 00 00 00 ff ff ff ff ff ff ff ff \
"], [0], [dnl
-OFPT_ROLE_STATUS (OF 0x05) (xid=0xa): role=master reason=configuration_changed
+OFPT_ROLE_STATUS (OF1.4) (xid=0xa): role=master reason=configuration_changed
])
AT_CLEANUP
05 1e 00 18 00 00 00 0a \
00 00 00 02 01 00 00 00 00 00 00 00 00 00 00 10 \
"], [0], [dnl
-OFPT_ROLE_STATUS (OF 0x05) (xid=0xa): role=master generation_id=16 reason=configuration_changed
+OFPT_ROLE_STATUS (OF1.4) (xid=0xa): role=master generation_id=16 reason=configuration_changed
])
AT_CLEANUP
])
AT_CHECK([ovs-ofctl encode-hello 0x3e], [0], [dnl
00000000 05 00 00 08 00 00 00 01-
-OFPT_HELLO (OF 0x05) (xid=0x1):
+OFPT_HELLO (OF1.4) (xid=0x1):
version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05
])
/ofproto|INFO|datapath ID changed to fedcba9876543210/d']])
dnl Add bridges, ports, etc.
- AT_CHECK([ovs-vsctl -- add-br br0 -- set bridge br0 datapath-type=dummy other-config:datapath-id=fedcba9876543210 other-config:hwaddr=aa:55:aa:55:00:00 protocols=[[OpenFlow10,OpenFlow11,OpenFlow12,OpenFlow13]] fail-mode=secure -- $1 m4_if([$2], [], [], [| ${PERL} $srcdir/uuidfilt.pl])], [0], [$2])
+ AT_CHECK([ovs-vsctl -- add-br br0 -- set bridge br0 datapath-type=dummy other-config:datapath-id=fedcba9876543210 other-config:hwaddr=aa:55:aa:55:00:00 fail-mode=secure -- $1 m4_if([$2], [], [], [| ${PERL} $srcdir/uuidfilt.pl])], [0], [$2])
])
m4_divert_push([PREPARE_TESTS])
if (retval == sizeof hello) {
enum ofpraw raw;
- CHECK(hello.version, OFP10_VERSION);
+ CHECK(hello.version, OFP13_VERSION);
CHECK(ofpraw_decode_partial(&raw, &hello, sizeof hello), 0);
CHECK(raw, OFPRAW_OFPT_HELLO);
CHECK(ntohs(hello.length), sizeof hello);
if (retval == sizeof hello) {
enum ofpraw raw;
- CHECK(hello.version, OFP10_VERSION);
+ CHECK(hello.version, OFP13_VERSION);
CHECK(ofpraw_decode_partial(&raw, &hello, sizeof hello), 0);
CHECK(raw, OFPRAW_OFPT_HELLO);
CHECK(ntohs(hello.length), sizeof hello);
const char *type = argv[1];
struct ofpbuf *hello;
- hello = ofpraw_alloc_xid(OFPRAW_OFPT_HELLO, OFP10_VERSION,
+ hello = ofpraw_alloc_xid(OFPRAW_OFPT_HELLO, OFP13_VERSION,
htonl(0x12345678), 0);
test_send_hello(type, ofpbuf_data(hello), ofpbuf_size(hello), 0);
ofpbuf_delete(hello);
struct ofpbuf *hello;
enum { EXTRA_BYTES = 8 };
- hello = ofpraw_alloc_xid(OFPRAW_OFPT_HELLO, OFP10_VERSION,
+ hello = ofpraw_alloc_xid(OFPRAW_OFPT_HELLO, OFP13_VERSION,
htonl(0x12345678), EXTRA_BYTES);
ofpbuf_put_zeros(hello, EXTRA_BYTES);
ofpmsg_update_length(hello);
const char *type = argv[1];
struct ofpbuf *echo;
- echo = ofpraw_alloc_xid(OFPRAW_OFPT_ECHO_REQUEST, OFP10_VERSION,
+ echo = ofpraw_alloc_xid(OFPRAW_OFPT_ECHO_REQUEST, OFP13_VERSION,
htonl(0x12345678), 0);
test_send_hello(type, ofpbuf_data(echo), ofpbuf_size(echo), EPROTO);
ofpbuf_delete(echo);
const char *type = argv[1];
struct ofpbuf *hello;
- hello = ofpraw_alloc_xid(OFPRAW_OFPT_HELLO, OFP10_VERSION,
+ hello = ofpraw_alloc_xid(OFPRAW_OFPT_HELLO, OFP13_VERSION,
htonl(0x12345678), 0);
((struct ofp_header *) ofpbuf_data(hello))->version = 0;
test_send_hello(type, ofpbuf_data(hello), ofpbuf_size(hello), EPROTO);
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
uint32_t versions;
enum ofputil_protocol version_protocols;
+ /* For now, ovs-ofctl only enables OpenFlow 1.0 by default. This is
+ * because ovs-ofctl implements command such as "add-flow" as raw OpenFlow
+ * requests, but those requests have subtly different semantics in
+ * different OpenFlow versions. For example:
+ *
+ * - In OpenFlow 1.0, a "mod-flow" operation that does not find any
+ * existing flow to modify adds a new flow.
+ *
+ * - In OpenFlow 1.1, a "mod-flow" operation that does not find any
+ * existing flow to modify adds a new flow, but only if the mod-flow
+ * did not match on the flow cookie.
+ *
+ * - In OpenFlow 1.2 and a later, a "mod-flow" operation never adds a
+ * new flow.
+ */
+ set_allowed_ofp_versions("OpenFlow10");
+
for (;;) {
unsigned long int timeout;
int c;
bridge_configure_stp(br);
bridge_configure_tables(br);
bridge_configure_dp_desc(br);
-
- if (smap_get(&br->cfg->other_config, "flow-eviction-threshold")) {
- /* XXX: Remove this warning message eventually. */
- VLOG_WARN_ONCE("As of June 2013, flow-eviction-threshold has been"
- " moved to the Open_vSwitch table. Ignoring its"
- " setting in the bridge table.");
- }
}
free(managers);
<column name="protocols">
<p>
- List of OpenFlow protocols that may be used when negotiating a
- connection with a controller. A default value of
- <code>OpenFlow10</code> will be used if this column is empty.
+ List of OpenFlow protocols that may be used when negotiating
+ a connection with a controller. OpenFlow 1.0, 1.1, 1.2, and
+ 1.3 are enabled by default if this column is empty.
</p>
<p>
</column>
<column name="bfd" key="forwarding_if_rx" type='{"type": "boolean"}'>
- True to consider the interface capable of packet I/O as long as it
- continues to receive any packets (not just BFD packets). This
- prevents link congestion that causes consecutive BFD control packets
- to be lost from marking the interface down.
+ When <code>true</code>, traffic received on the
+ <ref table="Interface"/> is used to indicate the capability of packet
+ I/O. BFD control packets are still transmitted and received. At
+ least one BFD control packet must be received every 100 * <ref
+ column="bfd" key="min_rx"/> amount of time. Otherwise, even if
+ traffic are received, the <ref column="bfd" key="forwarding"/>
+ will be <code>false</code>.
</column>
<column name="bfd" key="cpath_down" type='{"type": "boolean"}'>
<ref column="other_config" key="cfm_extended"/> is true, the CFM
module operates in demand mode. When in demand mode, traffic
received on the <ref table="Interface"/> is used to indicate
- liveness. CCMs are still transmitted and received, but if the
- <ref table="Interface"/> is receiving traffic, their absence does not
- cause a connectivity fault.
+ liveness. CCMs are still transmitted and received. At least one
+ CCM must be received every 100 * <ref column="other_config"
+ key="cfm_interval"/> amount of time. Otherwise, even if traffic
+ are received, the CFM module will raise the connectivity fault.
</p>
<p>