Alexei Starovoitov ast@plumgrid.com
Alexey I. Froloff raorn@altlinux.org
Alex Wang alexw@nicira.com
+Alfredo Finelli alf@computationes.de
Andrew Evans aevans@nicira.com
Andrew Lambeth wal@nicira.com
Andy Hill hillad@gmail.com
Romain Lenglet romain.lenglet@berabera.info
Sajjad Lateef slateef@nicira.com
Sanjay Sane ssane@nicira.com
+Saurabh Shah ssaurabh@nicira.com
Shan Wei davidshan@tencent.com
Shih-Hao Li shli@nicira.com
Simon Horman horms@verge.net.au
Gordon Good ggood@nicira.com
Greg Dahlman gdahlman@hotmail.com
Gregor Schaffrath grsch@net.t-labs.tu-berlin.de
+Guolin Yang gyang@vmware.com
Hassan Khan hassan.khan@seecs.edu.pk
Hector Oron hector.oron@gmail.com
Henrik Amren henrik@nicira.com
Spiro Kourtessis spiro@vmware.com
Srini Seetharaman seethara@stanford.edu
Stephen Hemminger shemminger@vyatta.com
+Stephen Finucane stephen.finucane@intel.com
Takayuki HAMA t-hama@cb.jp.nec.com
Teemu Koponen koponen@nicira.com
Timothy Chen tchen@nicira.com
regardless of the type.
+Performance
+-----------
+
+Q: I just upgraded and I see a performance drop. Why?
+
+A: The OVS kernel datapath may have been updated to a newer version than
+ the OVS userspace components. Sometimes new versions of OVS kernel
+ module add functionality that is backwards compatible with older
+ userspace components but may cause a drop in performance with them.
+ Especially, if a kernel module from OVS 2.1 or newer is paired with
+ OVS userspace 1.10 or older, there will be a performance drop for
+ TCP traffic.
+
+ Updating the OVS userspace components to the latest released
+ version should fix the performance degradation.
+
+ To get the best possible performance and functionality, it is
+ recommended to pair the same versions of the kernel module and OVS
+ userspace.
+
+
Configuration Problems
----------------------
AM_CFLAGS += -fomit-frame-pointer
endif
+if WIN32
+psep=";"
+else
+psep=":"
+endif
# PYTHONDONTWRITEBYTECODE=yes keeps Python from creating .pyc and .pyo
# files. Creating .py[co] works OK for any given version of Open
# vSwitch, but it causes trouble if you switch from a version with
# foo/__init__.py into an (older) version with plain foo.py, since
# foo/__init__.pyc will cause Python to ignore foo.py.
if INCLUDE_PYTHON_COMPAT
-run_python = PYTHONPATH=$(top_srcdir)/python:$(top_srcdir)/python/compat:$$PYTHONPATH
+run_python = PYTHONPATH=$(top_srcdir)/python$(psep)$(top_srcdir)/python/compat$(psep)$$PYTHONPATH
else
-run_python = PYTHONPATH=$(top_srcdir)/python:$$PYTHONPATH
+run_python = PYTHONPATH=$(top_srcdir)/python$(psep)$$PYTHONPATH
endif
run_python += PYTHONDONTWRITEBYTECODE=yes $(PYTHON)
Post-v2.0.0
---------------------
+ - TCP flags matching: OVS now supports matching of TCP flags. This
+ has an adverse performance impact when using OVS userspace 1.10
+ or older (no megaflows support) together with the new OVS kernel
+ module. It is recommended that the kernel and userspace modules
+ both are upgraded at the same time.
- The default OpenFlow and OVSDB ports will change to
IANA-assigned numbers in a future release. Consider updating
your installations to specify port numbers instead of using the
defaults.
- OpenFlow:
* The OpenFlow 1.1+ "Write-Actions" instruction is now supported.
+ * OVS limits the OpenFlow port numbers it assigns to port 32767 and
+ below, leaving port numbers above that range free for assignment
+ by the controller.
- ovs-vswitchd.conf.db.5 man page will contain graphviz/dot
diagram only if graphviz package was installed at the build time.
- Support for Linux kernels up to 3.11
that is all done, is probably 2 or 3 days work.
[optional for OF1.3+]
- * Per-flow meters. Similar to IPv6 extension headers in kernel
- and design requirements. Might be politically difficult to add
- directly to the kernel module, since its functionality overlaps
- with tc. Ideally, therefore, we could implement these somehow
- with tc, but I haven't investigated whether that makes sense.
+ * Per-flow meters. OpenFlow protocol support is now implemented.
+ Support for the special OFPM_SLOWPATH and OFPM_CONTROLLER meters
+ is missing. Support for the software switch is under review.
[optional for OF1.3+]
* Per-connection event filtering. OF1.3 adopted Open vSwitch's
some kind of "hardware" support, if we judged it useful enough.)
[optional for OF1.3+]
- * MPLS BoS matching. (Included in Simon's MPLS series?)
+ * MPLS BoS matching.
+ Part of MPLS patchset by Simon Horman.
[optional for OF1.3+]
* Provider Backbone Bridge tagging. I don't plan to implement
this (but we'd accept an implementation).
[optional for OF1.3+]
- * Rework tag order. I'm not sure whether we need to do anything
- for this. Part of MPLS patchset by Simon Horman.
+ * Rework tag order.
+ Part of MPLS patchset by Simon Horman.
[required for v1.3+]
* On-demand flow counters. I think this might be a real
[EXT-235]
[optional for OF1.4+]
+General
+-----
+
+ * ovs-ofctl(8) often lists as Nicira extensions features that
+ later OpenFlow versions support in standard ways.
+
How to contribute
-----------------
AC_FUNC_STRERROR_R
OVS_CHECK_ESX
+OVS_CHECK_WIN32
OVS_CHECK_COVERAGE
OVS_CHECK_NDEBUG
OVS_CHECK_NETLINK
AC_SUBST(KARCH)
OVS_CHECK_LINUX
-AC_CONFIG_FILES([Makefile
-datapath/Makefile
-datapath/linux/Kbuild
-datapath/linux/Makefile
-datapath/linux/Makefile.main
-tests/atlocal])
+AC_CONFIG_FILES(Makefile)
+AC_CONFIG_FILES(datapath/Makefile)
+AC_CONFIG_FILES(datapath/linux/Kbuild)
+AC_CONFIG_FILES(datapath/linux/Makefile)
+AC_CONFIG_FILES(datapath/linux/Makefile.main)
+AC_CONFIG_FILES(tests/atlocal)
dnl This makes sure that include/openflow gets created in the build directory.
AC_CONFIG_COMMANDS([include/openflow/openflow.h.stamp])
}
/* Look up flow. */
- flow = ovs_flow_tbl_lookup(&dp->table, &key, &n_mask_hit);
+ flow = ovs_flow_tbl_lookup_stats(&dp->table, &key, &n_mask_hit);
if (unlikely(!flow)) {
struct dp_upcall_info upcall;
return skb;
}
-static struct sw_flow *__ovs_flow_tbl_lookup(struct flow_table *tbl,
- const struct sw_flow_key *key)
-{
- u32 __always_unused n_mask_hit;
-
- return ovs_flow_tbl_lookup(tbl, key, &n_mask_hit);
-}
-
static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
{
struct nlattr **a = info->attrs;
goto err_unlock_ovs;
/* Check if this is a duplicate flow */
- flow = __ovs_flow_tbl_lookup(&dp->table, &key);
+ flow = ovs_flow_tbl_lookup(&dp->table, &key);
if (!flow) {
/* Bail out if we're not allowed to create a new flow. */
error = -ENOENT;
goto unlock;
}
- flow = __ovs_flow_tbl_lookup(&dp->table, &key);
+ flow = ovs_flow_tbl_lookup(&dp->table, &key);
if (!flow || !ovs_flow_cmp_unmasked_key(flow, &match)) {
err = -ENOENT;
goto unlock;
if (err)
goto unlock;
- flow = __ovs_flow_tbl_lookup(&dp->table, &key);
+ flow = ovs_flow_tbl_lookup(&dp->table, &key);
if (!flow || !ovs_flow_cmp_unmasked_key(flow, &match)) {
err = -ENOENT;
goto unlock;
} __aligned(BITS_PER_LONG/8); /* Ensure that we can do comparisons as longs. */
struct sw_flow_key_range {
- size_t start;
- size_t end;
+ unsigned short int start;
+ unsigned short int end;
};
struct sw_flow_mask {
return NULL;
}
-struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *tbl,
+struct sw_flow *ovs_flow_tbl_lookup_stats(struct flow_table *tbl,
const struct sw_flow_key *key,
u32 *n_mask_hit)
{
return NULL;
}
+struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *tbl,
+ const struct sw_flow_key *key)
+{
+ u32 __always_unused n_mask_hit;
+
+ return ovs_flow_tbl_lookup_stats(tbl, key, &n_mask_hit);
+}
+
int ovs_flow_tbl_num_masks(const struct flow_table *table)
{
struct sw_flow_mask *mask;
return NULL;
}
-/**
- * add a new mask into the mask list.
- * The caller needs to make sure that 'mask' is not the same
- * as any masks that are already on the list.
- */
+/* Add 'mask' into the mask list, if it is not already there. */
static int flow_mask_insert(struct flow_table *tbl, struct sw_flow *flow,
struct sw_flow_mask *new)
{
int ovs_flow_tbl_num_masks(const struct flow_table *table);
struct sw_flow *ovs_flow_tbl_dump_next(struct table_instance *table,
u32 *bucket, u32 *idx);
-struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *,
+struct sw_flow *ovs_flow_tbl_lookup_stats(struct flow_table *,
const struct sw_flow_key *,
u32 *n_mask_hit);
+struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *,
+ const struct sw_flow_key *);
bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow,
struct sw_flow_match *match);
rotate 30
postrotate
# Tell Open vSwitch daemons to reopen their log files
- for pidfile in `cd /var/run/openvswitch && echo *.pid`; do
- ovs-appctl -t "${pidfile%%.pid}" vlog/reopen
- done
+ if [ -d /var/run/openvswitch ]; then
+ for pidfile in `cd /var/run/openvswitch && echo *.pid`; do
+ ovs-appctl -t "${pidfile%%.pid}" vlog/reopen
+ done
+ fi
endscript
}
};
OFP_ASSERT(sizeof(struct ofp_vendor_header) == 12);
+/* Table numbering. Tables can use any number up to OFPT_MAX. */
+enum ofp_table {
+ /* Last usable table number. */
+ OFPTT_MAX = 0xfe,
+
+ /* Fake tables. */
+ OFPTT_ALL = 0xff /* Wildcard table used for table config,
+ flow stats and flow deletes. */
+};
+
+enum ofp_table_config {
+ OFPTC_TABLE_MISS_CONTROLLER = 0 << 0, /* Send to controller. */
+ OFPTC_TABLE_MISS_CONTINUE = 1 << 0, /* Continue to the next table in the
+ pipeline (OpenFlow 1.0 behavior). */
+ OFPTC_TABLE_MISS_DROP = 2 << 0, /* Drop the packet. */
+ OFPTC_TABLE_MISS_MASK = 3 << 0
+};
+
#endif /* openflow/openflow-common.h */
/*
- * 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.
return 1UL << (offset % BITMAP_ULONG_BITS);
}
+#define BITMAP_N_LONGS(N_BITS) DIV_ROUND_UP(N_BITS, BITMAP_ULONG_BITS)
+
static inline size_t
bitmap_n_longs(size_t n_bits)
{
- return DIV_ROUND_UP(n_bits, BITMAP_ULONG_BITS);
+ return BITMAP_N_LONGS(n_bits);
}
static inline size_t
/* Legacy compatibility. */
long long int next_fake_iface_update; /* LLONG_MAX if disabled. */
+ bool lacp_fallback_ab; /* Fallback to active-backup on LACP failure. */
atomic_int ref_cnt;
};
bond->updelay = s->up_delay;
bond->downdelay = s->down_delay;
+ if (bond->lacp_fallback_ab != s->lacp_fallback_ab_cfg) {
+ bond->lacp_fallback_ab = s->lacp_fallback_ab_cfg;
+ revalidate = true;
+ }
+
if (bond->rebalance_interval != s->rebalance_interval) {
bond->rebalance_interval = s->rebalance_interval;
revalidate = true;
static bool
may_send_learning_packets(const struct bond *bond)
{
- return bond->lacp_status == LACP_DISABLED
- && (bond->balance == BM_SLB || bond->balance == BM_AB)
+ return ((bond->lacp_status == LACP_DISABLED
+ && (bond->balance == BM_SLB || bond->balance == BM_AB))
+ || (bond->lacp_fallback_ab && bond->lacp_status == LACP_CONFIGURED))
&& bond->active_slave;
}
* packets to them.
*
* If LACP is configured, but LACP negotiations have been unsuccessful, we
- * drop all incoming traffic. */
+ * drop all incoming traffic except if lacp_fallback_ab is enabled. */
switch (bond->lacp_status) {
case LACP_NEGOTIATED:
verdict = slave->enabled ? BV_ACCEPT : BV_DROP;
goto out;
case LACP_CONFIGURED:
- goto out;
+ if (!bond->lacp_fallback_ab) {
+ goto out;
+ }
case LACP_DISABLED:
break;
}
}
switch (bond->balance) {
+ case BM_TCP:
+ /* TCP balanced bonds require successful LACP negotiations. Based on the
+ * above check, LACP is off or lacp_fallback_ab is true on this bond.
+ * If lacp_fallback_ab is true fall through to BM_AB case else, we
+ * drop all incoming traffic. */
+ if (!bond->lacp_fallback_ab) {
+ goto out;
+ }
+
case BM_AB:
/* Drop all packets which arrive on backup slaves. This is similar to
* how Linux bonding handles active-backup bonds. */
verdict = BV_ACCEPT;
goto out;
- case BM_TCP:
- /* TCP balanced bonds require successful LACP negotiated. Based on the
- * above check, LACP is off on this bond. Therfore, we drop all
- * incoming traffic. */
- goto out;
-
case BM_SLB:
/* Drop all packets for which we have learned a different input port,
* because we probably sent the packet on one slave and got it back on
uint32_t basis;
if (vlan_s) {
- if (sscanf(vlan_s, "%u", &vlan) != 1) {
+ if (!ovs_scan(vlan_s, "%u", &vlan)) {
unixctl_command_reply_error(conn, "invalid vlan");
return;
}
}
if (basis_s) {
- if (sscanf(basis_s, "%"PRIu32, &basis) != 1) {
+ if (!ovs_scan(basis_s, "%"SCNu32, &basis)) {
unixctl_command_reply_error(conn, "invalid basis");
return;
}
basis = 0;
}
- if (sscanf(mac_s, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))
- == ETH_ADDR_SCAN_COUNT) {
+ if (ovs_scan(mac_s, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))) {
hash = bond_hash_src(mac, vlan, basis) & BOND_MASK;
hash_cstr = xasprintf("%u", hash);
struct flow_wildcards *wc, uint16_t vlan)
{
struct bond_entry *e;
+ int balance;
+ balance = bond->balance;
if (bond->lacp_status == LACP_CONFIGURED) {
/* LACP has been configured on this bond but negotiations were
- * unsuccussful. Drop all traffic. */
- return NULL;
+ * unsuccussful. If lacp_fallback_ab is enabled use active-
+ * backup mode else drop all traffic. */
+ if (!bond->lacp_fallback_ab) {
+ return NULL;
+ }
+ balance = BM_AB;
}
- switch (bond->balance) {
+ switch (balance) {
case BM_AB:
return bond->active_slave;
/* Legacy compatibility. */
bool fake_iface; /* Update fake stats for netdev 'name'? */
+ bool lacp_fallback_ab_cfg; /* Fallback to active-backup on LACP failure. */
};
/* Program startup. */
cfm->fault |= CFM_FAULT_RECV;
}
- if (old_cfm_fault != cfm->fault && !VLOG_DROP_INFO(&rl)) {
- struct ds ds = DS_EMPTY_INITIALIZER;
-
- ds_put_cstr(&ds, "from [");
- ds_put_cfm_fault(&ds, old_cfm_fault);
- ds_put_cstr(&ds, "] to [");
- ds_put_cfm_fault(&ds, cfm->fault);
- ds_put_char(&ds, ']');
- VLOG_INFO("%s: CFM faults changed %s.", cfm->name, ds_cstr(&ds));
- ds_destroy(&ds);
+ if (old_cfm_fault != cfm->fault) {
+ if (!VLOG_DROP_INFO(&rl)) {
+ struct ds ds = DS_EMPTY_INITIALIZER;
+
+ ds_put_cstr(&ds, "from [");
+ ds_put_cfm_fault(&ds, old_cfm_fault);
+ ds_put_cstr(&ds, "] to [");
+ ds_put_cfm_fault(&ds, cfm->fault);
+ ds_put_char(&ds, ']');
+ VLOG_INFO("%s: CFM faults changed %s.", cfm->name, ds_cstr(&ds));
+ ds_destroy(&ds);
+ }
/* If there is a flap, increments the counter. */
if (old_cfm_fault == false || cfm->fault == false) {
struct cls_subtable *,
unsigned int del_priority);
-static struct cls_rule *find_match(const struct cls_subtable *,
- const struct flow *);
+static struct cls_rule *find_match_wc(const struct cls_subtable *,
+ const struct flow *,
+ struct flow_wildcards *);
static struct cls_rule *find_equal(struct cls_subtable *,
const struct miniflow *, uint32_t hash);
static struct cls_rule *insert_rule(struct classifier *,
/* Initializes 'cls' as a classifier that initially contains no classification
* rules. */
void
-classifier_init(struct classifier *cls)
+classifier_init(struct classifier *cls, const uint8_t *flow_segments)
{
cls->n_rules = 0;
hmap_init(&cls->subtables);
list_init(&cls->subtables_priority);
hmap_init(&cls->partitions);
ovs_rwlock_init(&cls->rwlock);
+ cls->n_flow_segments = 0;
+ if (flow_segments) {
+ while (cls->n_flow_segments < CLS_MAX_INDICES
+ && *flow_segments < FLOW_U32S) {
+ cls->flow_segments[cls->n_flow_segments++] = *flow_segments++;
+ }
+ }
}
/* Destroys 'cls'. Rules within 'cls', if any, are not freed; this is the
struct cls_partition *partition;
struct cls_rule *head;
struct cls_subtable *subtable;
+ int i;
subtable = find_subtable(cls, &rule->match.mask);
+
+ /* Remove rule node from indices. */
+ for (i = 0; i < subtable->n_indices; i++) {
+ hindex_remove(&subtable->indices[i], &rule->index_nodes[i]);
+ }
+
head = find_equal(subtable, &rule->match.flow, rule->hmap_node.hash);
if (head != rule) {
list_remove(&rule->list);
continue;
}
- rule = find_match(subtable, flow);
- if (wc) {
- flow_wildcards_fold_minimask(wc, &subtable->mask);
- }
+ rule = find_match_wc(subtable, flow, wc);
if (rule) {
best = rule;
LIST_FOR_EACH_CONTINUE (subtable, list_node,
continue;
}
- rule = find_match(subtable, flow);
- if (wc) {
- flow_wildcards_fold_minimask(wc, &subtable->mask);
- }
+ rule = find_match_wc(subtable, flow, wc);
if (rule && rule->priority > best->priority) {
best = rule;
}
{
uint32_t hash = minimask_hash(mask, 0);
struct cls_subtable *subtable;
+ int i, index = 0;
+ struct flow_wildcards old, new;
+ uint8_t prev;
subtable = xzalloc(sizeof *subtable);
hmap_init(&subtable->rules);
minimask_clone(&subtable->mask, mask);
- hmap_insert(&cls->subtables, &subtable->hmap_node, minimask_hash(mask, 0));
+
+ /* Init indices for segmented lookup, if any. */
+ flow_wildcards_init_catchall(&new);
+ old = new;
+ prev = 0;
+ for (i = 0; i < cls->n_flow_segments; i++) {
+ flow_wildcards_fold_minimask_range(&new, mask, prev,
+ cls->flow_segments[i]);
+ /* Add an index if it adds mask bits. */
+ if (!flow_wildcards_equal(&new, &old)) {
+ hindex_init(&subtable->indices[index]);
+ subtable->index_ofs[index] = cls->flow_segments[i];
+ index++;
+ old = new;
+ }
+ prev = cls->flow_segments[i];
+ }
+ /* Check if the rest of the subtable's mask adds any bits,
+ * and remove the last index if it doesn't. */
+ if (index > 0) {
+ flow_wildcards_fold_minimask_range(&new, mask, prev, FLOW_U32S);
+ if (flow_wildcards_equal(&new, &old)) {
+ --index;
+ subtable->index_ofs[index] = 0;
+ hindex_destroy(&subtable->indices[index]);
+ }
+ }
+ subtable->n_indices = index;
+
+ hmap_insert(&cls->subtables, &subtable->hmap_node, hash);
list_push_back(&cls->subtables_priority, &subtable->list_node);
subtable->tag = (minimask_get_metadata_mask(mask) == OVS_BE64_MAX
? tag_create_deterministic(hash)
static void
destroy_subtable(struct classifier *cls, struct cls_subtable *subtable)
{
+ int i;
+
+ for (i = 0; i < subtable->n_indices; i++) {
+ hindex_destroy(&subtable->indices[i]);
+ }
minimask_destroy(&subtable->mask);
hmap_remove(&cls->subtables, &subtable->hmap_node);
hmap_destroy(&subtable->rules);
}
}
-static struct cls_rule *
-find_match(const struct cls_subtable *subtable, const struct flow *flow)
+static inline struct cls_rule *
+find_match(const struct cls_subtable *subtable, const struct flow *flow,
+ uint32_t hash)
{
- uint32_t hash = flow_hash_in_minimask(flow, &subtable->mask, 0);
struct cls_rule *rule;
HMAP_FOR_EACH_WITH_HASH (rule, hmap_node, hash, &subtable->rules) {
return NULL;
}
+static struct cls_rule *
+find_match_wc(const struct cls_subtable *subtable, const struct flow *flow,
+ struct flow_wildcards * wc)
+{
+ uint32_t basis = 0, hash;
+ struct cls_rule *rule = NULL;
+ uint8_t prev_u32ofs = 0;
+ int i;
+
+ if (!wc) {
+ return find_match(subtable, flow,
+ flow_hash_in_minimask(flow, &subtable->mask, 0));
+ }
+
+ /* Try to finish early by checking fields in segments. */
+ for (i = 0; i < subtable->n_indices; i++) {
+ struct hindex_node *inode;
+
+ hash = flow_hash_in_minimask_range(flow, &subtable->mask, prev_u32ofs,
+ subtable->index_ofs[i], &basis);
+ prev_u32ofs = subtable->index_ofs[i];
+ inode = hindex_node_with_hash(&subtable->indices[i], hash);
+ if (!inode) {
+ /* No match, can stop immediately, but must fold in the mask
+ * covered so far. */
+ flow_wildcards_fold_minimask_range(wc, &subtable->mask, 0,
+ prev_u32ofs);
+ return NULL;
+ }
+
+ /* If we have narrowed down to a single rule already, check whether
+ * that rule matches. If it does match, then we're done. If it does
+ * not match, then we know that we will never get a match, but we do
+ * not yet know how many wildcards we need to fold into 'wc' so we
+ * continue iterating through indices to find that out. (We won't
+ * waste time calling minimatch_matches_flow() again because we've set
+ * 'rule' nonnull.)
+ *
+ * This check shows a measurable benefit with non-trivial flow tables.
+ *
+ * (Rare) hash collisions may cause us to miss the opportunity for this
+ * optimization. */
+ if (!inode->s && !rule) {
+ ASSIGN_CONTAINER(rule, inode - i, index_nodes);
+ if (minimatch_matches_flow(&rule->match, flow)) {
+ goto out;
+ }
+ }
+ }
+
+ if (!rule) {
+ /* Multiple potential matches exist, look for one. */
+ hash = flow_hash_in_minimask_range(flow, &subtable->mask, prev_u32ofs,
+ FLOW_U32S, &basis);
+ rule = find_match(subtable, flow, hash);
+ } else {
+ /* We already narrowed the matching candidates down to just 'rule',
+ * but it didn't match. */
+ rule = NULL;
+ }
+ out:
+ flow_wildcards_fold_minimask(wc, &subtable->mask);
+ return rule;
+}
+
static struct cls_rule *
find_equal(struct cls_subtable *subtable, const struct miniflow *flow,
uint32_t hash)
{
struct cls_rule *head;
struct cls_rule *old = NULL;
-
- new->hmap_node.hash = miniflow_hash_in_minimask(&new->match.flow,
- &new->match.mask, 0);
-
- head = find_equal(subtable, &new->match.flow, new->hmap_node.hash);
+ int i;
+ uint32_t basis = 0, hash;
+ uint8_t prev_u32ofs = 0;
+
+ /* Add new node to segment indices. */
+ for (i = 0; i < subtable->n_indices; i++) {
+ hash = minimatch_hash_range(&new->match, prev_u32ofs,
+ subtable->index_ofs[i], &basis);
+ hindex_insert(&subtable->indices[i], &new->index_nodes[i], hash);
+ prev_u32ofs = subtable->index_ofs[i];
+ }
+ hash = minimatch_hash_range(&new->match, prev_u32ofs, FLOW_U32S, &basis);
+ head = find_equal(subtable, &new->match.flow, hash);
if (!head) {
- hmap_insert(&subtable->rules, &new->hmap_node, new->hmap_node.hash);
+ hmap_insert(&subtable->rules, &new->hmap_node, hash);
list_init(&new->list);
goto out;
} else {
/* Scan the list for the insertion point that will keep the list in
* order of decreasing priority. */
struct cls_rule *rule;
+
+ new->hmap_node.hash = hash; /* Otherwise done by hmap_insert. */
+
FOR_EACH_RULE_IN_LIST (rule, head) {
if (new->priority >= rule->priority) {
if (rule == head) {
out:
if (!old) {
update_subtables_after_insertion(cls, subtable, new->priority);
+ } else {
+ /* Remove old node from indices. */
+ for (i = 0; i < subtable->n_indices; i++) {
+ hindex_remove(&subtable->indices[i], &old->index_nodes[i]);
+ }
}
return old;
}
* list inside that highest-priority rule.
*
*
+ * Staged Lookup
+ * =============
+ *
+ * Subtable lookup is performed in ranges defined for struct flow, starting
+ * from metadata (registers, in_port, etc.), then L2 header, L3, and finally
+ * L4 ports. Whenever it is found that there are no matches in the current
+ * subtable, the rest of the subtable can be skipped. The rationale of this
+ * logic is that as many fields as possible can remain wildcarded.
+ *
+ *
* Partitioning
* ============
*
* by a single writer. */
#include "flow.h"
+#include "hindex.h"
#include "hmap.h"
#include "list.h"
#include "match.h"
/* Needed only for the lock annotation in struct classifier. */
extern struct ovs_mutex ofproto_mutex;
+/* Maximum number of staged lookup indices for each subtable. */
+enum { CLS_MAX_INDICES = 3 };
+
/* A flow classifier. */
struct classifier {
int n_rules; /* Total number of rules. */
+ uint8_t n_flow_segments;
+ uint8_t flow_segments[CLS_MAX_INDICES]; /* Flow segment boundaries to use
+ * for staged lookup. */
struct hmap subtables; /* Contains "struct cls_subtable"s. */
struct list subtables_priority; /* Subtables in descending priority order.
*/
unsigned int max_priority; /* Max priority of any rule in the subtable. */
unsigned int max_count; /* Count of max_priority rules. */
tag_type tag; /* Tag generated from mask for partitioning. */
+ uint8_t n_indices; /* How many indices to use. */
+ uint8_t index_ofs[CLS_MAX_INDICES]; /* u32 flow segment boundaries. */
+ struct hindex indices[CLS_MAX_INDICES]; /* Staged lookup indices. */
};
/* Returns true if 'table' is a "catch-all" subtable that will match every
struct minimatch match; /* Matching rule. */
unsigned int priority; /* Larger numbers are higher priorities. */
struct cls_partition *partition;
+ struct hindex_node index_nodes[CLS_MAX_INDICES]; /* Within subtable's
+ * 'indices'. */
};
/* Associates a metadata value (that is, a value of the OpenFlow 1.1+ metadata
bool cls_rule_is_loose_match(const struct cls_rule *rule,
const struct minimatch *criteria);
-void classifier_init(struct classifier *cls);
+void classifier_init(struct classifier *cls, const uint8_t *flow_segments);
void classifier_destroy(struct classifier *);
bool classifier_is_empty(const struct classifier *cls)
OVS_REQ_RDLOCK(cls->rwlock);
#define NO_RETURN __attribute__((__noreturn__))
#define OVS_UNUSED __attribute__((__unused__))
#define PRINTF_FORMAT(FMT, ARG1) __attribute__((__format__(printf, FMT, ARG1)))
+#define SCANF_FORMAT(FMT, ARG1) __attribute__((__format__(scanf, FMT, ARG1)))
#define STRFTIME_FORMAT(FMT) __attribute__((__format__(__strftime__, FMT, 0)))
#define MALLOC_LIKE __attribute__((__malloc__))
#define ALWAYS_INLINE __attribute__((always_inline))
#define NO_RETURN
#define OVS_UNUSED
#define PRINTF_FORMAT(FMT, ARG1)
+#define SCANF_FORMAT(FMT, ARG1)
#define STRFTIME_FORMAT(FMT)
#define MALLOC_LIKE
#define ALWAYS_INLINE
/* Attributes. */
const char *name; /* OVS_DP_ATTR_NAME. */
- const uint32_t *upcall_pid; /* OVS_DP_UPCALL_PID. */
+ const uint32_t *upcall_pid; /* OVS_DP_ATTR_UPCALL_PID. */
struct ovs_dp_stats stats; /* OVS_DP_ATTR_STATS. */
struct ovs_dp_megaflow_stats megaflow_stats;
/* OVS_DP_ATTR_MEGAFLOW_STATS.*/
#include <sys/stat.h>
#include <unistd.h>
+#include "classifier.h"
#include "csum.h"
#include "dpif.h"
#include "dpif-provider.h"
VLOG_DEFINE_THIS_MODULE(dpif_netdev);
+/* By default, choose a priority in the middle. */
+#define NETDEV_RULE_PRIORITY 0x8000
+
/* Configuration parameters. */
enum { MAX_PORTS = 256 }; /* Maximum number of ports. */
enum { MAX_FLOWS = 65536 }; /* Maximum number of flows in flow table. */
int max_mtu; /* Maximum MTU of any port added so far. */
struct dp_netdev_queue queues[N_QUEUES];
+ struct classifier cls; /* Classifier. */
struct hmap flow_table; /* Flow table. */
struct seq *queue_seq; /* Incremented whenever a packet is queued. */
/* A flow in dp_netdev's 'flow_table'. */
struct dp_netdev_flow {
- struct hmap_node node; /* Element in dp_netdev's 'flow_table'. */
- struct flow key;
+ /* Packet classification. */
+ struct cls_rule cr; /* In owning dp_netdev's 'cls'. */
+
+ /* Hash table index by unmasked flow.*/
+ struct hmap_node node; /* In owning dp_netdev's 'flow_table'. */
+ struct flow flow; /* The flow that created this entry. */
/* Statistics. */
long long int used; /* Last used time, in monotonic msecs. */
static int dp_netdev_output_userspace(struct dp_netdev *, const struct ofpbuf *,
int queue_no, const struct flow *,
const struct nlattr *userdata);
-static void dp_netdev_execute_actions(struct dp_netdev *,
- struct ofpbuf *, struct flow *,
+static void dp_netdev_execute_actions(struct dp_netdev *, const struct flow *,
+ struct ofpbuf *,
const struct nlattr *actions,
size_t actions_len);
static void dp_netdev_port_input(struct dp_netdev *dp,
dp->queues[i].head = dp->queues[i].tail = 0;
}
dp->queue_seq = seq_create();
+ classifier_init(&dp->cls, NULL);
hmap_init(&dp->flow_table);
list_init(&dp->port_list);
dp->port_seq = seq_create();
}
dp_netdev_purge_queues(dp);
seq_destroy(dp->queue_seq);
+ classifier_destroy(&dp->cls);
hmap_destroy(&dp->flow_table);
seq_destroy(dp->port_seq);
free(dp->name);
static void
dp_netdev_free_flow(struct dp_netdev *dp, struct dp_netdev_flow *netdev_flow)
{
+ ovs_rwlock_wrlock(&dp->cls.rwlock);
+ classifier_remove(&dp->cls, &netdev_flow->cr);
+ ovs_rwlock_unlock(&dp->cls.rwlock);
+ cls_rule_destroy(&netdev_flow->cr);
+
hmap_remove(&dp->flow_table, &netdev_flow->node);
free(netdev_flow->actions);
free(netdev_flow);
}
static struct dp_netdev_flow *
-dp_netdev_lookup_flow(const struct dp_netdev *dp, const struct flow *key)
+dp_netdev_lookup_flow(const struct dp_netdev *dp, const struct flow *flow)
+{
+ struct cls_rule *cr;
+
+ ovs_rwlock_wrlock(&dp->cls.rwlock);
+ cr = classifier_lookup(&dp->cls, flow, NULL);
+ ovs_rwlock_unlock(&dp->cls.rwlock);
+
+ return (cr
+ ? CONTAINER_OF(cr, struct dp_netdev_flow, cr)
+ : NULL);
+}
+
+static struct dp_netdev_flow *
+dp_netdev_find_flow(const struct dp_netdev *dp, const struct flow *flow)
{
struct dp_netdev_flow *netdev_flow;
- HMAP_FOR_EACH_WITH_HASH (netdev_flow, node, flow_hash(key, 0),
+ HMAP_FOR_EACH_WITH_HASH (netdev_flow, node, flow_hash(flow, 0),
&dp->flow_table) {
- if (flow_equal(&netdev_flow->key, key)) {
+ if (flow_equal(&netdev_flow->flow, flow)) {
return netdev_flow;
}
}
}
static int
-dpif_netdev_flow_from_nlattrs(const struct nlattr *key, uint32_t key_len,
- struct flow *flow)
+dpif_netdev_flow_mask_from_nlattrs(const struct nlattr *key, uint32_t key_len,
+ const struct nlattr *mask_key,
+ uint32_t mask_key_len, struct flow *flow,
+ struct flow *mask)
{
odp_port_t in_port;
- if (odp_flow_key_to_flow(key, key_len, flow) != ODP_FIT_PERFECT) {
+ if (odp_flow_key_to_flow(key, key_len, flow)
+ || (mask_key
+ && odp_flow_key_to_mask(mask_key, mask_key_len, mask, flow))) {
/* This should not happen: it indicates that odp_flow_key_from_flow()
- * and odp_flow_key_to_flow() disagree on the acceptable form of a
- * flow. Log the problem as an error, with enough details to enable
- * debugging. */
+ * and odp_flow_key_to_flow() disagree on the acceptable form of a flow
+ * or odp_flow_key_from_mask() and odp_flow_key_to_mask() disagree on
+ * the acceptable form of a mask. Log the problem as an error, with
+ * enough details to enable debugging. */
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
if (!VLOG_DROP_ERR(&rl)) {
struct ds s;
ds_init(&s);
- odp_flow_key_format(key, key_len, &s);
+ odp_flow_format(key, key_len, mask_key, mask_key_len, NULL, &s,
+ true);
VLOG_ERR("internal error parsing flow key %s", ds_cstr(&s));
ds_destroy(&s);
}
return EINVAL;
}
+ if (mask_key) {
+ /* Force unwildcard the in_port. */
+ mask->in_port.odp_port = u32_to_odp(UINT32_MAX);
+ }
+
in_port = flow->in_port.odp_port;
if (!is_valid_port_number(in_port) && in_port != ODPP_NONE) {
return EINVAL;
return 0;
}
+static int
+dpif_netdev_flow_from_nlattrs(const struct nlattr *key, uint32_t key_len,
+ struct flow *flow)
+{
+ return dpif_netdev_flow_mask_from_nlattrs(key, key_len, NULL, 0, flow,
+ NULL);
+}
+
static int
dpif_netdev_flow_get(const struct dpif *dpif,
const struct nlattr *nl_key, size_t nl_key_len,
}
ovs_mutex_lock(&dp_netdev_mutex);
- netdev_flow = dp_netdev_lookup_flow(dp, &key);
+ netdev_flow = dp_netdev_find_flow(dp, &key);
if (netdev_flow) {
if (stats) {
get_dpif_flow_stats(netdev_flow, stats);
}
static int
-dp_netdev_flow_add(struct dp_netdev *dp, const struct flow *key,
- const struct nlattr *actions, size_t actions_len)
+dp_netdev_flow_add(struct dp_netdev *dp, const struct flow *flow,
+ const struct flow_wildcards *wc,
+ const struct nlattr *actions,
+ size_t actions_len)
{
struct dp_netdev_flow *netdev_flow;
+ struct match match;
int error;
netdev_flow = xzalloc(sizeof *netdev_flow);
- netdev_flow->key = *key;
+ netdev_flow->flow = *flow;
+
+ match_init(&match, flow, wc);
+ cls_rule_init(&netdev_flow->cr, &match, NETDEV_RULE_PRIORITY);
+ ovs_rwlock_wrlock(&dp->cls.rwlock);
+ classifier_insert(&dp->cls, &netdev_flow->cr);
+ ovs_rwlock_unlock(&dp->cls.rwlock);
error = set_flow_actions(netdev_flow, actions, actions_len);
if (error) {
+ ovs_rwlock_wrlock(&dp->cls.rwlock);
+ classifier_remove(&dp->cls, &netdev_flow->cr);
+ ovs_rwlock_unlock(&dp->cls.rwlock);
+ cls_rule_destroy(&netdev_flow->cr);
+
free(netdev_flow);
return error;
}
- hmap_insert(&dp->flow_table, &netdev_flow->node,
- flow_hash(&netdev_flow->key, 0));
+ hmap_insert(&dp->flow_table, &netdev_flow->node, flow_hash(flow, 0));
return 0;
}
{
struct dp_netdev *dp = get_dp_netdev(dpif);
struct dp_netdev_flow *netdev_flow;
- struct flow key;
+ struct flow flow;
+ struct flow_wildcards wc;
int error;
- error = dpif_netdev_flow_from_nlattrs(put->key, put->key_len, &key);
+ error = dpif_netdev_flow_mask_from_nlattrs(put->key, put->key_len,
+ put->mask, put->mask_len, &flow, &wc.masks);
if (error) {
return error;
}
ovs_mutex_lock(&dp_netdev_mutex);
- netdev_flow = dp_netdev_lookup_flow(dp, &key);
+ netdev_flow = dp_netdev_lookup_flow(dp, &flow);
if (!netdev_flow) {
if (put->flags & DPIF_FP_CREATE) {
if (hmap_count(&dp->flow_table) < MAX_FLOWS) {
if (put->stats) {
memset(put->stats, 0, sizeof *put->stats);
}
- error = dp_netdev_flow_add(dp, &key, put->actions,
+ error = dp_netdev_flow_add(dp, &flow, &wc, put->actions,
put->actions_len);
} else {
error = EFBIG;
error = ENOENT;
}
} else {
- if (put->flags & DPIF_FP_MODIFY) {
+ if (put->flags & DPIF_FP_MODIFY
+ && flow_equal(&flow, &netdev_flow->flow)) {
error = set_flow_actions(netdev_flow, put->actions,
put->actions_len);
if (!error) {
clear_stats(netdev_flow);
}
}
- } else {
+ } else if (put->flags & DPIF_FP_CREATE) {
error = EEXIST;
+ } else {
+ /* Overlapping flow. */
+ error = EINVAL;
}
}
ovs_mutex_unlock(&dp_netdev_mutex);
}
ovs_mutex_lock(&dp_netdev_mutex);
- netdev_flow = dp_netdev_lookup_flow(dp, &key);
+ netdev_flow = dp_netdev_find_flow(dp, &key);
if (netdev_flow) {
if (del->stats) {
get_dpif_flow_stats(netdev_flow, del->stats);
uint32_t offset;
struct nlattr *actions;
struct odputil_keybuf keybuf;
+ struct odputil_keybuf maskbuf;
struct dpif_flow_stats stats;
};
struct ofpbuf buf;
ofpbuf_use_stack(&buf, &state->keybuf, sizeof state->keybuf);
- odp_flow_key_from_flow(&buf, &netdev_flow->key,
- netdev_flow->key.in_port.odp_port);
+ odp_flow_key_from_flow(&buf, &netdev_flow->flow,
+ netdev_flow->flow.in_port.odp_port);
*key = buf.data;
*key_len = buf.size;
}
- if (mask) {
- *mask = NULL;
- *mask_len = 0;
+ if (key && mask) {
+ struct ofpbuf buf;
+ struct flow_wildcards wc;
+
+ ofpbuf_use_stack(&buf, &state->maskbuf, sizeof state->maskbuf);
+ minimask_expand(&netdev_flow->cr.match.mask, &wc);
+ odp_flow_key_from_mask(&buf, &wc.masks, &netdev_flow->flow,
+ odp_to_u32(wc.masks.in_port.odp_port));
+
+ *mask = buf.data;
+ *mask_len = buf.size;
}
if (actions) {
dpif_netdev_execute(struct dpif *dpif, const struct dpif_execute *execute)
{
struct dp_netdev *dp = get_dp_netdev(dpif);
- struct ofpbuf copy;
- struct flow key;
+ struct flow md;
int error;
if (execute->packet->size < ETH_HEADER_LEN ||
return EINVAL;
}
- /* Make a deep copy of 'packet', because we might modify its data. */
- ofpbuf_init(©, DP_NETDEV_HEADROOM + execute->packet->size);
- ofpbuf_reserve(©, DP_NETDEV_HEADROOM);
- ofpbuf_put(©, execute->packet->data, execute->packet->size);
-
- flow_extract(©, 0, 0, NULL, NULL, &key);
- error = dpif_netdev_flow_from_nlattrs(execute->key, execute->key_len,
- &key);
+ /* Get packet metadata. */
+ error = dpif_netdev_flow_from_nlattrs(execute->key, execute->key_len, &md);
if (!error) {
+ struct ofpbuf *copy;
+ struct flow key;
+
+ /* Make a deep copy of 'packet', because we might modify its data. */
+ copy = ofpbuf_clone_with_headroom(execute->packet, DP_NETDEV_HEADROOM);
+
+ /* Extract flow key. */
+ flow_extract(copy, md.skb_priority, md.pkt_mark, &md.tunnel,
+ &md.in_port, &key);
ovs_mutex_lock(&dp_netdev_mutex);
- dp_netdev_execute_actions(dp, ©, &key,
+ dp_netdev_execute_actions(dp, &key, copy,
execute->actions, execute->actions_len);
ovs_mutex_unlock(&dp_netdev_mutex);
+ ofpbuf_delete(copy);
}
-
- ofpbuf_uninit(©);
return error;
}
netdev_flow->used = time_msec();
netdev_flow->packet_count++;
netdev_flow->byte_count += packet->size;
- netdev_flow->tcp_flags |= packet_get_tcp_flags(packet, &netdev_flow->key);
+ netdev_flow->tcp_flags |= packet_get_tcp_flags(packet, &netdev_flow->flow);
}
static void
netdev_flow = dp_netdev_lookup_flow(dp, &key);
if (netdev_flow) {
dp_netdev_flow_used(netdev_flow, packet);
- dp_netdev_execute_actions(dp, packet, &key,
+ dp_netdev_execute_actions(dp, &key, packet,
netdev_flow->actions,
netdev_flow->actions_len);
dp->n_hit++;
ovs_mutex_unlock(&dp_netdev_mutex);
}
-static void
-dp_netdev_output_port(void *dp_, struct ofpbuf *packet,
- const struct flow *flow OVS_UNUSED, odp_port_t out_port)
-{
- struct dp_netdev *dp = dp_;
- struct dp_netdev_port *p = dp->ports[odp_to_u32(out_port)];
- if (p) {
- netdev_send(p->netdev, packet);
- }
-}
-
static int
dp_netdev_output_userspace(struct dp_netdev *dp, const struct ofpbuf *packet,
int queue_no, const struct flow *flow,
}
}
+struct dp_netdev_execute_aux {
+ struct dp_netdev *dp;
+ const struct flow *key;
+};
+
static void
-dp_netdev_action_userspace(void *dp, struct ofpbuf *packet,
- const struct flow *key,
+dp_netdev_action_output(void *aux_, struct ofpbuf *packet,
+ const struct flow *flow OVS_UNUSED,
+ odp_port_t out_port)
+{
+ struct dp_netdev_execute_aux *aux = aux_;
+ struct dp_netdev_port *p = aux->dp->ports[odp_to_u32(out_port)];
+ if (p) {
+ netdev_send(p->netdev, packet);
+ }
+}
+
+static void
+dp_netdev_action_userspace(void *aux_, struct ofpbuf *packet,
+ const struct flow *flow OVS_UNUSED,
const struct nlattr *a)
{
+ struct dp_netdev_execute_aux *aux = aux_;
const struct nlattr *userdata;
userdata = nl_attr_find_nested(a, OVS_USERSPACE_ATTR_USERDATA);
- dp_netdev_output_userspace(dp, packet, DPIF_UC_ACTION, key, userdata);
+ dp_netdev_output_userspace(aux->dp, packet, DPIF_UC_ACTION, aux->key,
+ userdata);
}
static void
-dp_netdev_execute_actions(struct dp_netdev *dp,
- struct ofpbuf *packet, struct flow *key,
- const struct nlattr *actions,
- size_t actions_len)
+dp_netdev_execute_actions(struct dp_netdev *dp, const struct flow *key,
+ struct ofpbuf *packet,
+ const struct nlattr *actions, size_t actions_len)
{
- odp_execute_actions(dp, packet, key, actions, actions_len,
- dp_netdev_output_port, dp_netdev_action_userspace);
+ struct dp_netdev_execute_aux aux = {dp, key};
+ struct flow md = *key; /* Packet metadata, may be modified by actions. */
+
+ odp_execute_actions(&aux, packet, &md, actions, actions_len,
+ dp_netdev_action_output, dp_netdev_action_userspace);
}
#define DPIF_NETDEV_CLASS_FUNCTIONS \
return error;
}
-/* Returns one greater than the maximum port number accepted in flow
- * actions. */
-uint32_t
-dpif_get_max_ports(const struct dpif *dpif)
-{
- return dpif->dpif_class->get_max_ports(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 flows whose
* packets arrived on port 'port_no'.
/* Adds or modifies a flow in 'dpif'. The flow is specified by the Netlink
* attribute OVS_FLOW_ATTR_KEY with types OVS_KEY_ATTR_* in the 'key_len' bytes
- * starting at 'key', and OVS_FLOW_ATTR_MASK with types of OVS_KEY_ATTR_* in the
- * 'mask_len' bytes starting at 'mask'. The associated actions are specified by
- * the Netlink attributes with types OVS_ACTION_ATTR_* in the 'actions_len'
- * bytes starting at 'actions'.
+ * starting at 'key', and OVS_FLOW_ATTR_MASK with types of OVS_KEY_ATTR_* in
+ * the 'mask_len' bytes starting at 'mask'. The associated actions are
+ * specified by the Netlink attributes with types OVS_ACTION_ATTR_* in the
+ * 'actions_len' bytes starting at 'actions'.
*
* - If the flow's key does not exist in 'dpif', then the flow will be added if
* 'flags' includes DPIF_FP_CREATE. Otherwise the operation will fail with
* ENOENT.
*
+ * The datapath may reject attempts to insert overlapping flows with EINVAL
+ * or EEXIST, but clients should not rely on this: avoiding overlapping flows
+ * is primarily the client's responsibility.
+ *
* If the operation succeeds, then 'stats', if nonnull, will be zeroed.
*
* - If the flow's key does exist in 'dpif', then the flow's actions will be
* Flow Table
* ==========
*
- * The flow table is a hash table of "flow entries". Each flow entry contains:
+ * The flow table is a collection of "flow entries". Each flow entry contains:
*
* - A "flow", that is, a summary of the headers in an Ethernet packet. The
- * flow is the hash key and thus must be unique within the flow table.
- * Flows are fine-grained entities that include L2, L3, and L4 headers. A
- * single TCP connection consists of two flows, one in each direction.
+ * flow must be unique within the flow table. Flows are fine-grained
+ * entities that include L2, L3, and L4 headers. A single TCP connection
+ * consists of two flows, one in each direction.
*
* In Open vSwitch userspace, "struct flow" is the typical way to describe
* a flow, but the datapath interface uses a different data format to
* "struct ovs_key_*" in include/linux/openvswitch.h for details.
* lib/odp-util.h defines several functions for working with these flows.
*
- * (In case you are familiar with OpenFlow, datapath flows are analogous
- * to OpenFlow flow matches. The most important difference is that
- * OpenFlow allows fields to be wildcarded and prioritized, whereas a
- * datapath's flow table is a hash table so every flow must be
- * exact-match, thus without priorities.)
+ * - A "mask" that, for each bit in the flow, specifies whether the datapath
+ * should consider the corresponding flow bit when deciding whether a
+ * given packet matches the flow entry. The original datapath design did
+ * not support matching: every flow entry was exact match. With the
+ * addition of a mask, the interface supports datapaths with a spectrum of
+ * wildcard matching capabilities, from those that only support exact
+ * matches to those that support bitwise wildcarding on the entire flow
+ * key, as well as datapaths with capabilities somewhere in between.
+ *
+ * Datapaths do not provide a way to query their wildcarding capabilities,
+ * nor is it expected that the client should attempt to probe for the
+ * details of their support. Instead, a client installs flows with masks
+ * that wildcard as many bits as acceptable. The datapath then actually
+ * wildcards as many of those bits as it can and changes the wildcard bits
+ * that it does not support into exact match bits. A datapath that can
+ * wildcard any bit, for example, would install the supplied mask, an
+ * exact-match only datapath would install an exact-match mask regardless
+ * of what mask the client supplied, and a datapath in the middle of the
+ * spectrum would selectively change some wildcard bits into exact match
+ * bits.
+ *
+ * Regardless of the requested or installed mask, the datapath retains the
+ * original flow supplied by the client. (It does not, for example, "zero
+ * out" the wildcarded bits.) This allows the client to unambiguously
+ * identify the flow entry in later flow table operations.
+ *
+ * The flow table does not have priorities; that is, all flow entries have
+ * equal priority. Detecting overlapping flow entries is expensive in
+ * general, so the datapath is not required to do it. It is primarily the
+ * client's responsibility not to install flow entries whose flow and mask
+ * combinations overlap.
*
* - A list of "actions" that tell the datapath what to do with packets
* within a flow. Some examples of actions are OVS_ACTION_ATTR_OUTPUT,
struct dpif_port *);
int dpif_port_get_name(struct dpif *, odp_port_t port_no,
char *name, size_t name_size);
-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 {
COVERAGE_DEFINE(flow_extract);
COVERAGE_DEFINE(miniflow_malloc);
+/* U32 indices for segmented flow classification. */
+const uint8_t flow_segment_u32s[4] = {
+ FLOW_SEGMENT_1_ENDS_AT / 4,
+ FLOW_SEGMENT_2_ENDS_AT / 4,
+ FLOW_SEGMENT_3_ENDS_AT / 4,
+ FLOW_U32S
+};
+
static struct arp_eth_header *
pull_arp(struct ofpbuf *packet)
{
void
flow_get_metadata(const struct flow *flow, struct flow_metadata *fmd)
{
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 22);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 23);
fmd->tun_id = flow->tunnel.tun_id;
fmd->tun_src = flow->tunnel.ip_src;
flow_union_with_miniflow(struct flow *dst, const struct miniflow *src)
{
uint32_t *dst_u32 = (uint32_t *) dst;
- int ofs;
- int i;
+ const uint32_t *p = src->values;
+ uint64_t map;
- ofs = 0;
- for (i = 0; i < MINI_N_MAPS; i++) {
- uint32_t map;
-
- for (map = src->map[i]; map; map = zero_rightmost_1bit(map)) {
- dst_u32[raw_ctz(map) + i * 32] |= src->values[ofs++];
- }
+ for (map = src->map; map; map = zero_rightmost_1bit(map)) {
+ dst_u32[raw_ctz(map)] |= *p++;
}
}
flow_union_with_miniflow(&wc->masks, &mask->masks);
}
+inline uint64_t
+miniflow_get_map_in_range(const struct miniflow *miniflow,
+ uint8_t start, uint8_t end, const uint32_t **data)
+{
+ uint64_t map = miniflow->map;
+ uint32_t *p = miniflow->values;
+
+ if (start > 0) {
+ uint64_t msk = (UINT64_C(1) << start) - 1; /* 'start' LSBs set */
+ p += count_1bits(map & msk); /* Skip to start. */
+ map &= ~msk;
+ }
+ if (end < FLOW_U32S) {
+ uint64_t msk = (UINT64_C(1) << end) - 1; /* 'end' LSBs set */
+ map &= msk;
+ }
+
+ *data = p;
+ return map;
+}
+
+/* Fold minimask 'mask''s wildcard mask into 'wc's wildcard mask
+ * in range [start, end). */
+void
+flow_wildcards_fold_minimask_range(struct flow_wildcards *wc,
+ const struct minimask *mask,
+ uint8_t start, uint8_t end)
+{
+ uint32_t *dst_u32 = (uint32_t *)&wc->masks;
+ const uint32_t *p;
+ uint64_t map = miniflow_get_map_in_range(&mask->masks, start, end, &p);
+
+ for (; map; map = zero_rightmost_1bit(map)) {
+ dst_u32[raw_ctz(map)] |= *p++;
+ }
+}
+
/* Returns a hash of the wildcards in 'wc'. */
uint32_t
flow_wildcards_hash(const struct flow_wildcards *wc, uint32_t basis)
static int
miniflow_n_values(const struct miniflow *flow)
{
- int n, i;
-
- n = 0;
- for (i = 0; i < MINI_N_MAPS; i++) {
- n += popcount(flow->map[i]);
- }
- return n;
+ return count_1bits(flow->map);
}
static uint32_t *
{
const uint32_t *src_u32 = (const uint32_t *) src;
unsigned int ofs;
- int i;
+ uint64_t map;
dst->values = miniflow_alloc_values(dst, n);
ofs = 0;
- for (i = 0; i < MINI_N_MAPS; i++) {
- uint32_t map;
-
- for (map = dst->map[i]; map; map = zero_rightmost_1bit(map)) {
- dst->values[ofs++] = src_u32[raw_ctz(map) + i * 32];
- }
+ for (map = dst->map; map; map = zero_rightmost_1bit(map)) {
+ dst->values[ofs++] = src_u32[raw_ctz(map)];
}
}
/* Initialize dst->map, counting the number of nonzero elements. */
n = 0;
- memset(dst->map, 0, sizeof dst->map);
+ dst->map = 0;
+
for (i = 0; i < FLOW_U32S; i++) {
if (src_u32[i]) {
- dst->map[i / 32] |= 1u << (i % 32);
+ dst->map |= UINT64_C(1) << i;
n++;
}
}
miniflow_init_with_minimask(struct miniflow *dst, const struct flow *src,
const struct minimask *mask)
{
- memcpy(dst->map, mask->masks.map, sizeof dst->map);
+ dst->map = mask->masks.map;
miniflow_init__(dst, src, miniflow_n_values(dst));
}
miniflow_clone(struct miniflow *dst, const struct miniflow *src)
{
int n = miniflow_n_values(src);
- memcpy(dst->map, src->map, sizeof dst->map);
+ dst->map = src->map;
dst->values = miniflow_alloc_values(dst, n);
memcpy(dst->values, src->values, n * sizeof *dst->values);
}
} else {
dst->values = src->values;
}
- memcpy(dst->map, src->map, sizeof dst->map);
+ dst->map = src->map;
}
/* Frees any memory owned by 'flow'. Does not free the storage in which 'flow'
static const uint32_t *
miniflow_get__(const struct miniflow *flow, unsigned int u32_ofs)
{
- if (!(flow->map[u32_ofs / 32] & (1u << (u32_ofs % 32)))) {
+ if (!(flow->map & (UINT64_C(1) << u32_ofs))) {
static const uint32_t zero = 0;
return &zero;
- } else {
- const uint32_t *p = flow->values;
-
- BUILD_ASSERT(MINI_N_MAPS == 2);
- if (u32_ofs < 32) {
- p += popcount(flow->map[0] & ((1u << u32_ofs) - 1));
- } else {
- p += popcount(flow->map[0]);
- p += popcount(flow->map[1] & ((1u << (u32_ofs - 32)) - 1));
- }
- return p;
}
+ return flow->values +
+ count_1bits(flow->map & ((UINT64_C(1) << u32_ofs) - 1));
}
/* Returns the uint32_t that would be at byte offset '4 * u32_ofs' if 'flow'
{
const uint32_t *ap = a->values;
const uint32_t *bp = b->values;
- int i;
-
- for (i = 0; i < MINI_N_MAPS; i++) {
- const uint32_t a_map = a->map[i];
- const uint32_t b_map = b->map[i];
- uint32_t map;
+ const uint64_t a_map = a->map;
+ const uint64_t b_map = b->map;
+ uint64_t map;
- if (a_map == b_map) {
- for (map = a_map; map; map = zero_rightmost_1bit(map)) {
- if (*ap++ != *bp++) {
- return false;
- }
+ if (a_map == b_map) {
+ for (map = a_map; map; map = zero_rightmost_1bit(map)) {
+ if (*ap++ != *bp++) {
+ return false;
}
- } else {
- for (map = a_map | b_map; map; map = zero_rightmost_1bit(map)) {
- uint32_t bit = rightmost_1bit(map);
- uint32_t a_value = a_map & bit ? *ap++ : 0;
- uint32_t b_value = b_map & bit ? *bp++ : 0;
+ }
+ } else {
+ for (map = a_map | b_map; map; map = zero_rightmost_1bit(map)) {
+ uint64_t bit = rightmost_1bit(map);
+ uint64_t a_value = a_map & bit ? *ap++ : 0;
+ uint64_t b_value = b_map & bit ? *bp++ : 0;
- if (a_value != b_value) {
- return false;
- }
+ if (a_value != b_value) {
+ return false;
}
}
}
const struct minimask *mask)
{
const uint32_t *p;
- int i;
+ uint64_t map;
p = mask->masks.values;
- for (i = 0; i < MINI_N_MAPS; i++) {
- uint32_t map;
- for (map = mask->masks.map[i]; map; map = zero_rightmost_1bit(map)) {
- int ofs = raw_ctz(map) + i * 32;
+ for (map = mask->masks.map; map; map = zero_rightmost_1bit(map)) {
+ int ofs = raw_ctz(map);
- if ((miniflow_get(a, ofs) ^ miniflow_get(b, ofs)) & *p) {
- return false;
- }
- p++;
+ if ((miniflow_get(a, ofs) ^ miniflow_get(b, ofs)) & *p) {
+ return false;
}
+ p++;
}
return true;
{
const uint32_t *b_u32 = (const uint32_t *) b;
const uint32_t *p;
- int i;
+ uint64_t map;
p = mask->masks.values;
- for (i = 0; i < MINI_N_MAPS; i++) {
- uint32_t map;
- for (map = mask->masks.map[i]; map; map = zero_rightmost_1bit(map)) {
- int ofs = raw_ctz(map) + i * 32;
+ for (map = mask->masks.map; map; map = zero_rightmost_1bit(map)) {
+ int ofs = raw_ctz(map);
- if ((miniflow_get(a, ofs) ^ b_u32[ofs]) & *p) {
- return false;
- }
- p++;
+ if ((miniflow_get(a, ofs) ^ b_u32[ofs]) & *p) {
+ return false;
}
+ p++;
}
return true;
{
const uint32_t *p = flow->values;
uint32_t hash = basis;
- int i;
+ uint64_t hash_map = 0;
+ uint64_t map;
- for (i = 0; i < MINI_N_MAPS; i++) {
- uint32_t hash_map = 0;
- uint32_t map;
-
- for (map = flow->map[i]; map; map = zero_rightmost_1bit(map)) {
- if (*p) {
- hash = mhash_add(hash, *p);
- hash_map |= rightmost_1bit(map);
- }
- p++;
+ for (map = flow->map; map; map = zero_rightmost_1bit(map)) {
+ if (*p) {
+ hash = mhash_add(hash, *p);
+ hash_map |= rightmost_1bit(map);
}
- hash = mhash_add(hash, hash_map);
+ p++;
}
+ hash = mhash_add(hash, hash_map);
+ hash = mhash_add(hash, hash_map >> 32);
+
return mhash_finish(hash, p - flow->values);
}
{
const uint32_t *p = mask->masks.values;
uint32_t hash;
- int i;
+ uint64_t map;
hash = basis;
- for (i = 0; i < MINI_N_MAPS; i++) {
- uint32_t map;
- for (map = mask->masks.map[i]; map; map = zero_rightmost_1bit(map)) {
- if (*p) {
- int ofs = raw_ctz(map) + i * 32;
- hash = mhash_add(hash, miniflow_get(flow, ofs) & *p);
- }
- p++;
+ for (map = mask->masks.map; map; map = zero_rightmost_1bit(map)) {
+ if (*p) {
+ int ofs = raw_ctz(map);
+ hash = mhash_add(hash, miniflow_get(flow, ofs) & *p);
}
+ p++;
}
return mhash_finish(hash, (p - mask->masks.values) * 4);
flow_hash_in_minimask(const struct flow *flow, const struct minimask *mask,
uint32_t basis)
{
- const uint32_t *flow_u32;
+ const uint32_t *flow_u32 = (const uint32_t *)flow;
const uint32_t *p = mask->masks.values;
uint32_t hash;
- int i;
+ uint64_t map;
hash = basis;
- flow_u32 = (const uint32_t *) flow;
- for (i = 0; i < MINI_N_MAPS; i++) {
- uint32_t map;
+ for (map = mask->masks.map; map; map = zero_rightmost_1bit(map)) {
+ if (*p) {
+ hash = mhash_add(hash, flow_u32[raw_ctz(map)] & *p);
+ }
+ p++;
+ }
- for (map = mask->masks.map[i]; map; map = zero_rightmost_1bit(map)) {
- if (*p) {
- hash = mhash_add(hash, flow_u32[raw_ctz(map)] & *p);
- }
- p++;
+ return mhash_finish(hash, (p - mask->masks.values) * 4);
+}
+
+/* Returns a hash value for the bits of range [start, end) in 'flow',
+ * where there are 1-bits in 'mask', given 'hash'.
+ *
+ * The hash values returned by this function are the same as those returned by
+ * minimatch_hash_range(), only the form of the arguments differ. */
+uint32_t
+flow_hash_in_minimask_range(const struct flow *flow,
+ const struct minimask *mask,
+ uint8_t start, uint8_t end, uint32_t *basis)
+{
+ const uint32_t *flow_u32 = (const uint32_t *)flow;
+ const uint32_t *p;
+ uint64_t map = miniflow_get_map_in_range(&mask->masks, start, end, &p);
+ uint32_t hash = *basis;
+
+ for (; map; map = zero_rightmost_1bit(map)) {
+ if (*p) {
+ hash = mhash_add(hash, flow_u32[raw_ctz(map)] & *p);
}
- flow_u32 += 32;
+ p++;
}
+ *basis = hash; /* Allow continuation from the unfinished value. */
return mhash_finish(hash, (p - mask->masks.values) * 4);
}
+
\f
/* Initializes 'dst' as a copy of 'src'. The caller must eventually free 'dst'
* with minimask_destroy(). */
struct miniflow *dst = &dst_->masks;
const struct miniflow *a = &a_->masks;
const struct miniflow *b = &b_->masks;
- int i, n;
+ uint64_t map;
+ int n = 0;
- n = 0;
dst->values = storage;
- for (i = 0; i < MINI_N_MAPS; i++) {
- uint32_t map;
-
- dst->map[i] = 0;
- for (map = a->map[i] & b->map[i]; map;
- map = zero_rightmost_1bit(map)) {
- int ofs = raw_ctz(map) + i * 32;
- uint32_t mask = miniflow_get(a, ofs) & miniflow_get(b, ofs);
-
- if (mask) {
- dst->map[i] |= rightmost_1bit(map);
- dst->values[n++] = mask;
- }
+
+ dst->map = 0;
+ for (map = a->map & b->map; map; map = zero_rightmost_1bit(map)) {
+ int ofs = raw_ctz(map);
+ uint32_t mask = miniflow_get(a, ofs) & miniflow_get(b, ofs);
+
+ if (mask) {
+ dst->map |= rightmost_1bit(map);
+ dst->values[n++] = mask;
}
}
}
{
const struct miniflow *a = &a_->masks;
const struct miniflow *b = &b_->masks;
- int i;
-
- for (i = 0; i < MINI_N_MAPS; i++) {
- uint32_t map;
+ uint64_t map;
- for (map = a->map[i] | b->map[i]; map;
- map = zero_rightmost_1bit(map)) {
- int ofs = raw_ctz(map) + i * 32;
- uint32_t a_u32 = miniflow_get(a, ofs);
- uint32_t b_u32 = miniflow_get(b, ofs);
+ for (map = a->map | b->map; map; map = zero_rightmost_1bit(map)) {
+ int ofs = raw_ctz(map);
+ uint32_t a_u32 = miniflow_get(a, ofs);
+ uint32_t b_u32 = miniflow_get(b, ofs);
- if ((a_u32 & b_u32) != b_u32) {
- return true;
- }
+ if ((a_u32 & b_u32) != b_u32) {
+ return true;
}
}
{
const struct miniflow *mask = &mask_->masks;
const uint32_t *p = mask->values;
- int i;
-
- for (i = 0; i < MINI_N_MAPS; i++) {
- uint32_t map;
+ uint64_t map;
- for (map = mask->map[i]; map; map = zero_rightmost_1bit(map)) {
- if (*p++) {
- return false;
- }
+ for (map = mask->map; map; map = zero_rightmost_1bit(map)) {
+ if (*p++) {
+ return false;
}
}
return true;
struct dpif_flow_stats;
struct ds;
struct flow_wildcards;
-struct miniflow;
struct minimask;
struct ofpbuf;
/* This sequence number should be incremented whenever anything involving flows
* or the wildcarding of flows changes. This will cause build assertion
* failures in places which likely need to be updated. */
-#define FLOW_WC_SEQ 22
+#define FLOW_WC_SEQ 23
#define FLOW_N_REGS 8
BUILD_ASSERT_DECL(FLOW_N_REGS <= NXM_NX_MAX_REGS);
* 16-bit OpenFlow 1.0 port number. In the software datapath interface (dpif)
* layer and its implementations (e.g. dpif-linux, dpif-netdev), it is instead
* a 32-bit datapath port number.
+ *
+ * The fields are organized in four segments to facilitate staged lookup, where
+ * lower layer fields are first used to determine if the later fields need to
+ * be looked at. This enables better wildcarding for datapath flows.
*/
struct flow {
+ /* L1 */
struct flow_tnl tunnel; /* Encapsulating tunnel parameters. */
ovs_be64 metadata; /* OpenFlow Metadata. */
+ uint32_t regs[FLOW_N_REGS]; /* Registers. */
+ uint32_t skb_priority; /* Packet priority for QoS. */
+ uint32_t pkt_mark; /* Packet mark. */
+ union flow_in_port in_port; /* Input port.*/
+
+ /* L2 */
+ uint8_t dl_src[6]; /* Ethernet source address. */
+ uint8_t dl_dst[6]; /* Ethernet destination address. */
+ ovs_be16 dl_type; /* Ethernet frame type. */
+ ovs_be16 vlan_tci; /* If 802.1Q, TCI | VLAN_CFI; otherwise 0. */
+
+ /* L3 */
+ ovs_be32 mpls_lse; /* MPLS label stack entry. */
struct in6_addr ipv6_src; /* IPv6 source address. */
struct in6_addr ipv6_dst; /* IPv6 destination address. */
struct in6_addr nd_target; /* IPv6 neighbor discovery (ND) target. */
- uint32_t skb_priority; /* Packet priority for QoS. */
- uint32_t regs[FLOW_N_REGS]; /* Registers. */
+ ovs_be32 ipv6_label; /* IPv6 flow label. */
ovs_be32 nw_src; /* IPv4 source address. */
ovs_be32 nw_dst; /* IPv4 destination address. */
- ovs_be32 ipv6_label; /* IPv6 flow label. */
- union flow_in_port in_port; /* Input port.*/
- uint32_t pkt_mark; /* Packet mark. */
- ovs_be32 mpls_lse; /* MPLS label stack entry. */
- 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/SCTP source port. */
- ovs_be16 tp_dst; /* TCP/UDP/SCTP destination port. */
- ovs_be16 tcp_flags; /* TCP flags. */
- 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. */
+ uint8_t nw_frag; /* FLOW_FRAG_* flags. */
uint8_t nw_tos; /* IP ToS (including DSCP and ECN). */
+ uint8_t nw_ttl; /* IP TTL/Hop Limit. */
+ uint8_t nw_proto; /* IP protocol or low 8 bits of ARP opcode. */
uint8_t arp_sha[6]; /* ARP/ND source hardware address. */
uint8_t arp_tha[6]; /* ARP/ND target hardware address. */
- uint8_t nw_ttl; /* IP TTL/Hop Limit. */
- uint8_t nw_frag; /* FLOW_FRAG_* flags. Keep last for the
- BUILD_ASSERT_DECL below */
+ ovs_be16 tcp_flags; /* TCP flags. With L3 to avoid matching L4. */
+ ovs_be16 pad; /* Padding. */
+ /* L4 */
+ ovs_be16 tp_src; /* TCP/UDP/SCTP source port. */
+ ovs_be16 tp_dst; /* TCP/UDP/SCTP destination port.
+ * Keep last for the BUILD_ASSERT_DECL below */
};
BUILD_ASSERT_DECL(sizeof(struct flow) % 4 == 0);
#define FLOW_U32S (sizeof(struct flow) / 4)
/* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */
-BUILD_ASSERT_DECL(offsetof(struct flow, nw_frag) + 1
- == sizeof(struct flow_tnl) + 154
- && FLOW_WC_SEQ == 22);
+BUILD_ASSERT_DECL(offsetof(struct flow, tp_dst) + 2
+ == sizeof(struct flow_tnl) + 156
+ && FLOW_WC_SEQ == 23);
+
+/* Incremental points at which flow classification may be performed in
+ * segments.
+ * This is located here since this is dependent on the structure of the
+ * struct flow defined above:
+ * Each offset must be on a distint, successive U32 boundary srtictly
+ * within the struct flow. */
+enum {
+ FLOW_SEGMENT_1_ENDS_AT = offsetof(struct flow, dl_src),
+ FLOW_SEGMENT_2_ENDS_AT = offsetof(struct flow, mpls_lse),
+ FLOW_SEGMENT_3_ENDS_AT = offsetof(struct flow, tp_src),
+};
+BUILD_ASSERT_DECL(FLOW_SEGMENT_1_ENDS_AT % 4 == 0);
+BUILD_ASSERT_DECL(FLOW_SEGMENT_2_ENDS_AT % 4 == 0);
+BUILD_ASSERT_DECL(FLOW_SEGMENT_3_ENDS_AT % 4 == 0);
+BUILD_ASSERT_DECL( 0 < FLOW_SEGMENT_1_ENDS_AT);
+BUILD_ASSERT_DECL(FLOW_SEGMENT_1_ENDS_AT < FLOW_SEGMENT_2_ENDS_AT);
+BUILD_ASSERT_DECL(FLOW_SEGMENT_2_ENDS_AT < FLOW_SEGMENT_3_ENDS_AT);
+BUILD_ASSERT_DECL(FLOW_SEGMENT_3_ENDS_AT < sizeof(struct flow));
+
+extern const uint8_t flow_segment_u32s[];
/* Represents the metadata fields of struct flow. */
struct flow_metadata {
uint32_t flow_hash_in_minimask(const struct flow *, const struct minimask *,
uint32_t basis);
+uint32_t flow_hash_in_minimask_range(const struct flow *,
+ const struct minimask *,
+ uint8_t start, uint8_t end,
+ uint32_t *basis);
\f
/* Wildcards for a flow.
*
void flow_wildcards_fold_minimask(struct flow_wildcards *,
const struct minimask *);
+void flow_wildcards_fold_minimask_range(struct flow_wildcards *,
+ const struct minimask *,
+ uint8_t start, uint8_t end);
uint32_t flow_wildcards_hash(const struct flow_wildcards *, uint32_t basis);
bool flow_wildcards_equal(const struct flow_wildcards *,
/* Compressed flow. */
#define MINI_N_INLINE (sizeof(void *) == 4 ? 7 : 8)
-#define MINI_N_MAPS DIV_ROUND_UP(FLOW_U32S, 32)
+BUILD_ASSERT_DECL(FLOW_U32S <= 64);
/* A sparse representation of a "struct flow".
*
* same 'map' allows optimization .
*/
struct miniflow {
+ uint64_t map;
uint32_t *values;
uint32_t inline_values[MINI_N_INLINE];
- uint32_t map[MINI_N_MAPS];
};
void miniflow_init(struct miniflow *, const struct flow *);
uint32_t miniflow_hash(const struct miniflow *, uint32_t basis);
uint32_t miniflow_hash_in_minimask(const struct miniflow *,
const struct minimask *, uint32_t basis);
+uint64_t miniflow_get_map_in_range(const struct miniflow *, uint8_t start,
+ uint8_t end, const uint32_t **data);
\f
/* Compressed flow wildcards. */
bool fast; /* True if using fast probe interval. */
bool negotiated; /* True if LACP negotiations were successful. */
bool update; /* True if lacp_update() needs to be called. */
+ bool fallback_ab; /* True if fallback to active-backup on LACP failure. */
atomic_int ref_cnt;
};
lacp->active = s->active;
lacp->fast = s->fast;
+
+ if (lacp->fallback_ab != s->fallback_ab_cfg) {
+ lacp->fallback_ab = s->fallback_ab_cfg;
+ lacp->update = true;
+ }
+
ovs_mutex_unlock(&mutex);
}
{
/* The slave may be enabled if it's attached to an aggregator and its
* partner is synchronized.*/
- return slave->attached && (slave->partner.state & LACP_STATE_SYNC);
+ return slave->attached && (slave->partner.state & LACP_STATE_SYNC
+ || (slave->lacp && slave->lacp->fallback_ab
+ && slave->status == LACP_DEFAULTED));
}
/* This function should be called before enabling 'slave_' to send or receive
}
if (slave->status == LACP_DEFAULTED) {
+ if (lacp->fallback_ab) {
+ slave->attached = true;
+ }
continue;
}
if (lead) {
HMAP_FOR_EACH (slave, node, &lacp->slaves) {
- if (lead->partner.key != slave->partner.key
+ if ((lacp->fallback_ab && slave->status == LACP_DEFAULTED)
+ || lead->partner.key != slave->partner.key
|| !eth_addr_equals(lead->partner.sys_id,
slave->partner.sys_id)) {
slave->attached = false;
uint16_t priority; /* System priority. */
bool active; /* Active or passive mode? */
bool fast; /* Fast or slow probe interval. */
+ bool fallback_ab_cfg; /* Fallback to BM_SLB on LACP failure. */
};
void lacp_init(void);
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 22);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 23);
if (priority != OFP_DEFAULT_PRIORITY) {
ds_put_format(s, "priority=%u,", priority);
const uint32_t *target_u32 = (const uint32_t *) target;
const uint32_t *flowp = match->flow.values;
const uint32_t *maskp = match->mask.masks.values;
- int i;
-
- for (i = 0; i < MINI_N_MAPS; i++) {
- uint32_t map;
+ uint64_t map;
- for (map = match->flow.map[i]; map; map = zero_rightmost_1bit(map)) {
- if ((*flowp++ ^ target_u32[raw_ctz(map)]) & *maskp++) {
- return false;
- }
+ for (map = match->flow.map; map; map = zero_rightmost_1bit(map)) {
+ if ((*flowp++ ^ target_u32[raw_ctz(map)]) & *maskp++) {
+ return false;
}
- target_u32 += 32;
}
return true;
}
+/* Returns a hash value for the bits of range [start, end) in 'minimatch',
+ * given 'basis'.
+ *
+ * The hash values returned by this function are the same as those returned by
+ * flow_hash_in_minimask_range(), only the form of the arguments differ. */
+uint32_t
+minimatch_hash_range(const struct minimatch *match, uint8_t start, uint8_t end,
+ uint32_t *basis)
+{
+ const uint32_t *p;
+ uint64_t map = miniflow_get_map_in_range(&match->mask.masks, start, end,
+ &p);
+ const ptrdiff_t df = match->mask.masks.values - match->flow.values;
+ uint32_t hash = *basis;
+
+ for (; map; map = zero_rightmost_1bit(map)) {
+ if (*p) {
+ hash = mhash_add(hash, *(p - df) & *p);
+ }
+ p++;
+ }
+ *basis = hash; /* Allow continuation from the unfinished value. */
+ return mhash_finish(hash, (p - match->mask.masks.values) * 4);
+}
+
/* Appends a string representation of 'match' to 's'. If 'priority' is
* different from OFP_DEFAULT_PRIORITY, includes it in 's'. */
void
bool minimatch_matches_flow(const struct minimatch *, const struct flow *);
+uint32_t minimatch_hash_range(const struct minimatch *,
+ uint8_t start, uint8_t end, uint32_t *basis);
+
void minimatch_format(const struct minimatch *, struct ds *,
unsigned int priority);
char *minimatch_to_string(const struct minimatch *, unsigned int priority);
ovs_assert(mf->n_bytes == ETH_ADDR_LEN);
n = -1;
- if (sscanf(s, ETH_ADDR_SCAN_FMT"%n", ETH_ADDR_SCAN_ARGS(mac), &n) > 0
+ if (ovs_scan(s, ETH_ADDR_SCAN_FMT"%n", ETH_ADDR_SCAN_ARGS(mac), &n)
&& n == strlen(s)) {
memset(mask, 0xff, ETH_ADDR_LEN);
return NULL;
}
n = -1;
- if (sscanf(s, ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT"%n",
- ETH_ADDR_SCAN_ARGS(mac), ETH_ADDR_SCAN_ARGS(mask), &n) > 0
+ if (ovs_scan(s, ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT"%n",
+ ETH_ADDR_SCAN_ARGS(mac), ETH_ADDR_SCAN_ARGS(mask), &n)
&& n == strlen(s)) {
return NULL;
}
ovs_assert(mf->n_bytes == sizeof *ip);
- if (sscanf(s, IP_SCAN_FMT"/"IP_SCAN_FMT,
- IP_SCAN_ARGS(ip), IP_SCAN_ARGS(mask)) == IP_SCAN_COUNT * 2) {
+ if (ovs_scan(s, IP_SCAN_FMT"/"IP_SCAN_FMT,
+ IP_SCAN_ARGS(ip), IP_SCAN_ARGS(mask))) {
/* OK. */
- } else if (sscanf(s, IP_SCAN_FMT"/%d",
- IP_SCAN_ARGS(ip), &prefix) == IP_SCAN_COUNT + 1) {
+ } else if (ovs_scan(s, IP_SCAN_FMT"/%d", IP_SCAN_ARGS(ip), &prefix)) {
if (prefix <= 0 || prefix > 32) {
return xasprintf("%s: network prefix bits not between 1 and "
"32", s);
} else {
*mask = htonl(((1u << prefix) - 1) << (32 - prefix));
}
- } else if (sscanf(s, IP_SCAN_FMT, IP_SCAN_ARGS(ip)) == IP_SCAN_COUNT) {
+ } else if (ovs_scan(s, IP_SCAN_FMT, IP_SCAN_ARGS(ip))) {
*mask = OVS_BE32_MAX;
} else {
return xasprintf("%s: invalid IP address", s);
int name_len;
unsigned long long int flags;
uint32_t bit;
- int n0;
- if (sscanf(name, "%lli%n", &flags, &n0) > 0 && n0 > 0) {
+ if (ovs_scan(name, "%lli", &flags)) {
result |= flags;
continue;
}
}
return xasprintf("%s: unknown tunnel flags (valid flags are \"df\", "
- "\"csum\", \"key\"", s);
+ "\"csum\", \"key\")", s);
}
/* Parses 's', a string value for field 'mf', into 'value' and 'mask'. Returns
}
s += name_len;
- if (sscanf(s, "[%d..%d]", &start, &end) == 2) {
+ if (ovs_scan(s, "[%d..%d]", &start, &end)) {
/* Nothing to do. */
- } else if (sscanf(s, "[%d]", &start) == 1) {
+ } else if (ovs_scan(s, "[%d]", &start)) {
end = start;
} else if (!strncmp(s, "[]", 2)) {
start = 0;
static unixctl_cb_func netdev_dummy_set_admin_state;
static int netdev_dummy_construct(struct netdev *);
-static void netdev_dummy_poll_notify(struct netdev_dummy *netdev)
+static void netdev_dummy_changed(struct netdev_dummy *netdev)
OVS_REQUIRES(netdev->mutex);
static void netdev_dummy_queue_packet(struct netdev_dummy *, struct ofpbuf *);
ovs_mutex_lock(&dev->mutex);
if (!eth_addr_equals(dev->hwaddr, mac)) {
memcpy(dev->hwaddr, mac, ETH_ADDR_LEN);
- netdev_dummy_poll_notify(dev);
+ netdev_dummy_changed(dev);
}
ovs_mutex_unlock(&dev->mutex);
netdev->flags |= on;
netdev->flags &= ~off;
if (*old_flagsp != netdev->flags) {
- netdev_dummy_poll_notify(netdev);
+ netdev_dummy_changed(netdev);
}
return 0;
/* Helper functions. */
static void
-netdev_dummy_poll_notify(struct netdev_dummy *dev)
+netdev_dummy_changed(struct netdev_dummy *dev)
{
dev->change_seq++;
if (!dev->change_seq) {
{
uint8_t *s6 = in6->s6_addr;
#define X8 "%2"SCNx8
- return sscanf(line,
- " "X8 X8 X8 X8 X8 X8 X8 X8 X8 X8 X8 X8 X8 X8 X8 X8
- "%*x %*x %*x %*x %16s\n",
- &s6[0], &s6[1], &s6[2], &s6[3],
- &s6[4], &s6[5], &s6[6], &s6[7],
- &s6[8], &s6[9], &s6[10], &s6[11],
- &s6[12], &s6[13], &s6[14], &s6[15],
- ifname) == 17;
+ return ovs_scan(line,
+ " "X8 X8 X8 X8 X8 X8 X8 X8 X8 X8 X8 X8 X8 X8 X8 X8
+ "%*x %*x %*x %*x %16s\n",
+ &s6[0], &s6[1], &s6[2], &s6[3],
+ &s6[4], &s6[5], &s6[6], &s6[7],
+ &s6[8], &s6[9], &s6[10], &s6[11],
+ &s6[12], &s6[13], &s6[14], &s6[15],
+ ifname);
}
/* If 'netdev' has an assigned IPv6 address, sets '*in6' to that address (if
int refcnt, metric, mtu;
unsigned int flags, use, window, irtt;
- if (sscanf(line,
- "%16s %"SCNx32" %"SCNx32" %04X %d %u %d %"SCNx32
- " %d %u %u\n",
- iface, &dest, &gateway, &flags, &refcnt,
- &use, &metric, &mask, &mtu, &window, &irtt) != 11) {
-
+ if (!ovs_scan(line,
+ "%16s %"SCNx32" %"SCNx32" %04X %d %u %d %"SCNx32
+ " %d %u %u\n",
+ iface, &dest, &gateway, &flags, &refcnt,
+ &use, &metric, &mask, &mtu, &window, &irtt)) {
VLOG_WARN_RL(&rl, "%s: could not parse line %d: %s",
fn, ln, line);
continue;
if (++ln >= 3) {
char devname[16];
#define X64 "%"SCNu64
- if (sscanf(line,
- " %15[^:]:"
- X64 X64 X64 X64 X64 X64 X64 "%*u"
- X64 X64 X64 X64 X64 X64 X64 "%*u",
- devname,
- &stats->rx_bytes,
- &stats->rx_packets,
- &stats->rx_errors,
- &stats->rx_dropped,
- &stats->rx_fifo_errors,
- &stats->rx_frame_errors,
- &stats->multicast,
- &stats->tx_bytes,
- &stats->tx_packets,
- &stats->tx_errors,
- &stats->tx_dropped,
- &stats->tx_fifo_errors,
- &stats->collisions,
- &stats->tx_carrier_errors) != 15) {
+ if (!ovs_scan(line,
+ " %15[^:]:"
+ X64 X64 X64 X64 X64 X64 X64 "%*u"
+ X64 X64 X64 X64 X64 X64 X64 "%*u",
+ devname,
+ &stats->rx_bytes,
+ &stats->rx_packets,
+ &stats->rx_errors,
+ &stats->rx_dropped,
+ &stats->rx_fifo_errors,
+ &stats->rx_frame_errors,
+ &stats->multicast,
+ &stats->tx_bytes,
+ &stats->tx_packets,
+ &stats->tx_errors,
+ &stats->tx_dropped,
+ &stats->tx_fifo_errors,
+ &stats->collisions,
+ &stats->tx_carrier_errors)) {
VLOG_WARN_RL(&rl, "%s:%d: parse error", fn, ln);
} else if (!strcmp(devname, netdev_name)) {
stats->rx_length_errors = UINT64_MAX;
static int netdev_vport_construct(struct netdev *);
static int get_patch_config(const struct netdev *netdev, struct smap *args);
static int get_tunnel_config(const struct netdev *, struct smap *args);
-static void netdev_vport_poll_notify(struct netdev_vport *netdev)
+static void netdev_vport_changed(struct netdev_vport *netdev)
OVS_REQUIRES(netdev->mutex);
static bool
return class->get_config == get_patch_config;
}
+bool
+netdev_vport_is_layer3(const struct netdev *dev)
+{
+ const char *type = netdev_get_type(dev);
+
+ return (!strcmp("lisp", type));
+}
+
static bool
netdev_vport_needs_dst_port(const struct netdev *dev)
{
ovs_mutex_lock(&netdev->mutex);
memcpy(netdev->etheraddr, mac, ETH_ADDR_LEN);
- netdev_vport_poll_notify(netdev);
+ netdev_vport_changed(netdev);
ovs_mutex_unlock(&netdev->mutex);
return 0;
/* Helper functions. */
static void
-netdev_vport_poll_notify(struct netdev_vport *ndv)
+netdev_vport_changed(struct netdev_vport *ndv)
{
ndv->change_seq++;
if (!ndv->change_seq) {
ovs_mutex_lock(&dev->mutex);
dev->tnl_cfg = tnl_cfg;
- netdev_vport_poll_notify(dev);
+ netdev_vport_changed(dev);
ovs_mutex_unlock(&dev->mutex);
return 0;
ovs_mutex_lock(&dev->mutex);
free(dev->peer);
dev->peer = xstrdup(peer);
- netdev_vport_poll_notify(dev);
+ netdev_vport_changed(dev);
ovs_mutex_unlock(&dev->mutex);
return 0;
void netdev_vport_patch_register(void);
bool netdev_vport_is_patch(const struct netdev *);
+bool netdev_vport_is_layer3(const struct netdev *);
char *netdev_vport_patch_peer(const struct netdev *netdev);
return nla + 1;
}
+/* Appends a Netlink attribute of the given 'type' and room for 'size' bytes of
+ * data as its payload, plus Netlink padding if needed, to the tail end of
+ * 'msg', reallocating and copying its data if necessary. Returns a pointer to
+ * the first byte of data in the attribute, which is zeroed. */
+void *
+nl_msg_put_unspec_zero(struct ofpbuf *msg, uint16_t type, size_t size)
+{
+ void *data = nl_msg_put_unspec_uninit(msg, type, size);
+ memset(data, 0, size);
+ return data;
+}
+
/* Appends a Netlink attribute of the given 'type' and the 'size' bytes of
* 'data' as its payload, to the tail end of 'msg', reallocating and copying
* its data if necessary. Returns a pointer to the first byte of data in the
/* Appending attributes. */
void *nl_msg_put_unspec_uninit(struct ofpbuf *, uint16_t type, size_t);
+void *nl_msg_put_unspec_zero(struct ofpbuf *, uint16_t type, size_t);
void nl_msg_put_unspec(struct ofpbuf *, uint16_t type, const void *, size_t);
void nl_msg_put_flag(struct ofpbuf *, uint16_t type);
void nl_msg_put_u8(struct ofpbuf *, uint16_t type, uint8_t value);
{
return (maxlen >= sizeof *nla
&& nla->nla_len >= sizeof *nla
- && NLA_ALIGN(nla->nla_len) <= maxlen);
+ && nla->nla_len <= maxlen);
+}
+
+static inline size_t
+nl_attr_len_pad(const struct nlattr *nla, size_t maxlen)
+{
+ size_t len = NLA_ALIGN(nla->nla_len);
+
+ return len <= maxlen ? len : nla->nla_len;
}
/* This macro is careful to check for attributes with bad lengths. */
#define NL_ATTR_FOR_EACH(ITER, LEFT, ATTRS, ATTRS_LEN) \
for ((ITER) = (ATTRS), (LEFT) = (ATTRS_LEN); \
nl_attr_is_valid(ITER, LEFT); \
- (LEFT) -= NLA_ALIGN((ITER)->nla_len), (ITER) = nl_attr_next(ITER))
+ (LEFT) -= nl_attr_len_pad(ITER, LEFT), (ITER) = nl_attr_next(ITER))
/* This macro does not check for attributes with bad lengths. It should only
#define NL_ATTR_FOR_EACH_UNSAFE(ITER, LEFT, ATTRS, ATTRS_LEN) \
for ((ITER) = (ATTRS), (LEFT) = (ATTRS_LEN); \
(LEFT) > 0; \
- (LEFT) -= NLA_ALIGN((ITER)->nla_len), (ITER) = nl_attr_next(ITER))
+ (LEFT) -= nl_attr_len_pad(ITER, LEFT), (ITER) = nl_attr_next(ITER))
/* These variants are convenient for iterating nested attributes. */
#define NL_NESTED_FOR_EACH(ITER, LEFT, A) \
int match_len;
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 22);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 23);
/* Metadata. */
if (match->wc.masks.in_port.ofp_port) {
uint32_t bit;
int n0;
- if (sscanf(&s[n], "%lli%n", &flags, &n0) > 0 && n0 > 0) {
+ if (ovs_scan(&s[n], "%lli%n", &flags, &n0)) {
n += n0 + (s[n + n0] == ',');
result |= flags;
continue;
cookie.flow_sample.collector_set_id,
cookie.flow_sample.obs_domain_id,
cookie.flow_sample.obs_point_id);
- } else if (userdata_len == sizeof cookie.ipfix
+ } else if (userdata_len >= sizeof cookie.ipfix
&& cookie.type == USER_ACTION_COOKIE_IPFIX) {
ds_put_format(ds, ",ipfix");
} else {
parse_odp_action(const char *s, const struct simap *port_names,
struct ofpbuf *actions)
{
- /* Many of the sscanf calls in this function use oversized destination
- * fields because some sscanf() implementations truncate the range of %i
- * directives, so that e.g. "%"SCNi16 interprets input of "0xfedc" as a
- * value of 0x7fff. The other alternatives are to allow only a single
- * radix (e.g. decimal or hexadecimal) or to write more sophisticated
- * parsers.
- *
- * The tun_id parser has to use an alternative approach because there is no
- * type larger than 64 bits. */
-
{
- unsigned long long int port;
- int n = -1;
+ uint32_t port;
+ int n;
- if (sscanf(s, "%lli%n", &port, &n) > 0 && n > 0) {
+ if (ovs_scan(s, "%"SCNi32"%n", &port, &n)) {
nl_msg_put_u32(actions, OVS_ACTION_ATTR_OUTPUT, port);
return n;
}
}
{
- unsigned long long int pid;
- unsigned long long int output;
- unsigned long long int probability;
- unsigned long long int collector_set_id;
- unsigned long long int obs_domain_id;
- unsigned long long int obs_point_id;
+ uint32_t pid;
+ uint32_t output;
+ uint32_t probability;
+ uint32_t collector_set_id;
+ uint32_t obs_domain_id;
+ uint32_t obs_point_id;
int vid, pcp;
int n = -1;
- if (sscanf(s, "userspace(pid=%lli)%n", &pid, &n) > 0 && n > 0) {
+ if (ovs_scan(s, "userspace(pid=%"SCNi32")%n", &pid, &n)) {
odp_put_userspace_action(pid, NULL, 0, actions);
return n;
- } else if (sscanf(s, "userspace(pid=%lli,sFlow(vid=%i,"
- "pcp=%i,output=%lli))%n",
- &pid, &vid, &pcp, &output, &n) > 0 && n > 0) {
+ } else if (ovs_scan(s, "userspace(pid=%"SCNi32",sFlow(vid=%i,"
+ "pcp=%i,output=%"SCNi32"))%n",
+ &pid, &vid, &pcp, &output, &n)) {
union user_action_cookie cookie;
uint16_t tci;
odp_put_userspace_action(pid, &cookie, sizeof cookie.sflow,
actions);
return n;
- } else if (sscanf(s, "userspace(pid=%lli,slow_path%n", &pid, &n) > 0
- && n > 0) {
+ } else if (ovs_scan(s, "userspace(pid=%"SCNi32",slow_path%n",
+ &pid, &n)) {
union user_action_cookie cookie;
int res;
odp_put_userspace_action(pid, &cookie, sizeof cookie.slow_path,
actions);
return n;
- } else if (sscanf(s, "userspace(pid=%lli,flow_sample(probability=%lli,"
- "collector_set_id=%lli,obs_domain_id=%lli,"
- "obs_point_id=%lli))%n",
- &pid, &probability, &collector_set_id,
- &obs_domain_id, &obs_point_id, &n) > 0 && n > 0) {
+ } else if (ovs_scan(s, "userspace(pid=%"SCNi32","
+ "flow_sample(probability=%"SCNi32","
+ "collector_set_id=%"SCNi32","
+ "obs_domain_id=%"SCNi32","
+ "obs_point_id=%"SCNi32"))%n",
+ &pid, &probability, &collector_set_id,
+ &obs_domain_id, &obs_point_id, &n)) {
union user_action_cookie cookie;
cookie.type = USER_ACTION_COOKIE_FLOW_SAMPLE;
odp_put_userspace_action(pid, &cookie, sizeof cookie.flow_sample,
actions);
return n;
- } else if (sscanf(s, "userspace(pid=%lli,ipfix)%n", &pid, &n) > 0
- && n > 0) {
+ } else if (ovs_scan(s, "userspace(pid=%"SCNi32",ipfix)%n", &pid, &n)) {
union user_action_cookie cookie;
cookie.type = USER_ACTION_COOKIE_IPFIX;
odp_put_userspace_action(pid, &cookie, sizeof cookie.ipfix,
actions);
return n;
- } else if (sscanf(s, "userspace(pid=%lli,userdata(%n", &pid, &n) > 0
- && n > 0) {
+ } else if (ovs_scan(s, "userspace(pid=%"SCNi32",userdata(%n",
+ &pid, &n)) {
struct ofpbuf buf;
char *end;
int cfi = 1;
int n = -1;
- if ((sscanf(s, "push_vlan(vid=%i,pcp=%i)%n", &vid, &pcp, &n) > 0
- && n > 0)
- || (sscanf(s, "push_vlan(vid=%i,pcp=%i,cfi=%i)%n",
- &vid, &pcp, &cfi, &n) > 0 && n > 0)
- || (sscanf(s, "push_vlan(tpid=%i,vid=%i,pcp=%i)%n",
- &tpid, &vid, &pcp, &n) > 0 && n > 0)
- || (sscanf(s, "push_vlan(tpid=%i,vid=%i,pcp=%i,cfi=%i)%n",
- &tpid, &vid, &pcp, &cfi, &n) > 0 && n > 0)) {
+ if (ovs_scan(s, "push_vlan(vid=%i,pcp=%i)%n", &vid, &pcp, &n)
+ || ovs_scan(s, "push_vlan(vid=%i,pcp=%i,cfi=%i)%n",
+ &vid, &pcp, &cfi, &n)
+ || ovs_scan(s, "push_vlan(tpid=%i,vid=%i,pcp=%i)%n",
+ &tpid, &vid, &pcp, &n)
+ || ovs_scan(s, "push_vlan(tpid=%i,vid=%i,pcp=%i,cfi=%i)%n",
+ &tpid, &vid, &pcp, &cfi, &n)) {
push.vlan_tpid = htons(tpid);
push.vlan_tci = htons((vid << VLAN_VID_SHIFT)
| (pcp << VLAN_PCP_SHIFT)
double percentage;
int n = -1;
- if (sscanf(s, "sample(sample=%lf%%,actions(%n", &percentage, &n) > 0
- && percentage >= 0. && percentage <= 100.0
- && n > 0) {
+ if (ovs_scan(s, "sample(sample=%lf%%,actions(%n", &percentage, &n)
+ && percentage >= 0. && percentage <= 100.0) {
size_t sample_ofs, actions_ofs;
double probability;
int size = nl_attr_get_size(key);
if (odp_flow_key_attr_len(type) >=0) {
- memset(nl_msg_put_unspec_uninit(ofp, type, size), 0, size);
+ nl_msg_put_unspec_zero(ofp, type, size);
} else {
size_t nested_mask;
parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
struct ofpbuf *key, struct ofpbuf *mask)
{
- /* Many of the sscanf calls in this function use oversized destination
- * fields because some sscanf() implementations truncate the range of %i
- * directives, so that e.g. "%"SCNi16 interprets input of "0xfedc" as a
- * value of 0x7fff. The other alternatives are to allow only a single
- * radix (e.g. decimal or hexadecimal) or to write more sophisticated
- * parsers.
- *
- * The tun_id parser has to use an alternative approach because there is no
- * type larger than 64 bits. */
-
{
- unsigned long long int priority;
- unsigned long long int priority_mask;
+ uint32_t priority;
+ uint32_t priority_mask;
int n = -1;
- if (mask && sscanf(s, "skb_priority(%lli/%lli)%n", &priority,
- &priority_mask, &n) > 0 && n > 0) {
+ if (mask && ovs_scan(s, "skb_priority(%"SCNi32"/%"SCNi32")%n",
+ &priority, &priority_mask, &n)) {
nl_msg_put_u32(key, OVS_KEY_ATTR_PRIORITY, priority);
nl_msg_put_u32(mask, OVS_KEY_ATTR_PRIORITY, priority_mask);
return n;
- } else if (sscanf(s, "skb_priority(%lli)%n",
- &priority, &n) > 0 && n > 0) {
+ } else if (ovs_scan(s, "skb_priority(%"SCNi32")%n", &priority, &n)) {
nl_msg_put_u32(key, OVS_KEY_ATTR_PRIORITY, priority);
if (mask) {
nl_msg_put_u32(mask, OVS_KEY_ATTR_PRIORITY, UINT32_MAX);
}
{
- unsigned long long int mark;
- unsigned long long int mark_mask;
+ uint32_t mark;
+ uint32_t mark_mask;
int n = -1;
- if (mask && sscanf(s, "skb_mark(%lli/%lli)%n", &mark,
- &mark_mask, &n) > 0 && n > 0) {
+ if (mask && ovs_scan(s, "skb_mark(%"SCNi32"/%"SCNi32")%n", &mark,
+ &mark_mask, &n)) {
nl_msg_put_u32(key, OVS_KEY_ATTR_SKB_MARK, mark);
nl_msg_put_u32(mask, OVS_KEY_ATTR_SKB_MARK, mark_mask);
return n;
- } else if (sscanf(s, "skb_mark(%lli)%n", &mark, &n) > 0 && n > 0) {
+ } else if (ovs_scan(s, "skb_mark(%"SCNi32")%n", &mark, &n)) {
nl_msg_put_u32(key, OVS_KEY_ATTR_SKB_MARK, mark);
if (mask) {
nl_msg_put_u32(mask, OVS_KEY_ATTR_SKB_MARK, UINT32_MAX);
}
{
- char tun_id_s[32];
- int tos, tos_mask, ttl, ttl_mask;
+ uint64_t tun_id, tun_id_mask;
struct flow_tnl tun_key, tun_key_mask;
- unsigned long long tun_id_mask;
int n = -1;
- if (mask && sscanf(s, "tunnel(tun_id=%31[x0123456789abcdefABCDEF]/%llx,"
- "src="IP_SCAN_FMT"/"IP_SCAN_FMT",dst="IP_SCAN_FMT
- "/"IP_SCAN_FMT",tos=%i/%i,ttl=%i/%i,flags%n",
- tun_id_s, &tun_id_mask,
- IP_SCAN_ARGS(&tun_key.ip_src),
- IP_SCAN_ARGS(&tun_key_mask.ip_src),
- IP_SCAN_ARGS(&tun_key.ip_dst),
- IP_SCAN_ARGS(&tun_key_mask.ip_dst),
- &tos, &tos_mask, &ttl, &ttl_mask,
- &n) > 0 && n > 0) {
+ if (mask && ovs_scan(s, "tunnel(tun_id=%"SCNi64"/%"SCNi64","
+ "src="IP_SCAN_FMT"/"IP_SCAN_FMT",dst="IP_SCAN_FMT
+ "/"IP_SCAN_FMT",tos=%"SCNi8"/%"SCNi8","
+ "ttl=%"SCNi8"/%"SCNi8",flags%n",
+ &tun_id, &tun_id_mask,
+ IP_SCAN_ARGS(&tun_key.ip_src),
+ IP_SCAN_ARGS(&tun_key_mask.ip_src),
+ IP_SCAN_ARGS(&tun_key.ip_dst),
+ IP_SCAN_ARGS(&tun_key_mask.ip_dst),
+ &tun_key.ip_tos, &tun_key_mask.ip_tos,
+ &tun_key.ip_ttl, &tun_key_mask.ip_ttl, &n)) {
int res;
uint32_t flags;
- tun_key.tun_id = htonll(strtoull(tun_id_s, NULL, 0));
+ tun_key.tun_id = htonll(tun_id);
tun_key_mask.tun_id = htonll(tun_id_mask);
- tun_key.ip_tos = tos;
- tun_key_mask.ip_tos = tos_mask;
- tun_key.ip_ttl = ttl;
- tun_key_mask.ip_ttl = ttl_mask;
res = parse_flags(&s[n], flow_tun_flag_to_string, &flags);
tun_key.flags = flags;
tun_key_mask.flags = UINT16_MAX;
tun_key_to_attr(mask, &tun_key_mask);
}
return n;
- } else if (sscanf(s, "tunnel(tun_id=%31[x0123456789abcdefABCDEF],"
- "src="IP_SCAN_FMT",dst="IP_SCAN_FMT
- ",tos=%i,ttl=%i,flags%n", tun_id_s,
- IP_SCAN_ARGS(&tun_key.ip_src),
- IP_SCAN_ARGS(&tun_key.ip_dst), &tos, &ttl,
- &n) > 0 && n > 0) {
+ } else if (ovs_scan(s, "tunnel(tun_id=%"SCNi64","
+ "src="IP_SCAN_FMT",dst="IP_SCAN_FMT
+ ",tos=%"SCNi8",ttl=%"SCNi8",flags%n", &tun_id,
+ IP_SCAN_ARGS(&tun_key.ip_src),
+ IP_SCAN_ARGS(&tun_key.ip_dst),
+ &tun_key.ip_tos, &tun_key.ip_ttl, &n)) {
int res;
uint32_t flags;
- tun_key.tun_id = htonll(strtoull(tun_id_s, NULL, 0));
- tun_key.ip_tos = tos;
- tun_key.ip_ttl = ttl;
+ tun_key.tun_id = htonll(tun_id);
res = parse_flags(&s[n], flow_tun_flag_to_string, &flags);
tun_key.flags = flags;
}
{
- unsigned long long int in_port;
- unsigned long long int in_port_mask;
+ uint32_t in_port;
+ uint32_t in_port_mask;
int n = -1;
- if (mask && sscanf(s, "in_port(%lli/%lli)%n", &in_port,
- &in_port_mask, &n) > 0 && n > 0) {
+ if (mask && ovs_scan(s, "in_port(%"SCNi32"/%"SCNi32")%n",
+ &in_port, &in_port_mask, &n)) {
nl_msg_put_u32(key, OVS_KEY_ATTR_IN_PORT, in_port);
nl_msg_put_u32(mask, OVS_KEY_ATTR_IN_PORT, in_port_mask);
return n;
- } else if (sscanf(s, "in_port(%lli)%n", &in_port, &n) > 0 && n > 0) {
+ } else if (ovs_scan(s, "in_port(%"SCNi32")%n", &in_port, &n)) {
nl_msg_put_u32(key, OVS_KEY_ATTR_IN_PORT, in_port);
if (mask) {
nl_msg_put_u32(mask, OVS_KEY_ATTR_IN_PORT, UINT32_MAX);
struct ovs_key_ethernet eth_key_mask;
int n = -1;
- if (mask && sscanf(s,
- "eth(src="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT","
- "dst="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT")%n",
- ETH_ADDR_SCAN_ARGS(eth_key.eth_src),
- ETH_ADDR_SCAN_ARGS(eth_key_mask.eth_src),
- ETH_ADDR_SCAN_ARGS(eth_key.eth_dst),
- ETH_ADDR_SCAN_ARGS(eth_key_mask.eth_dst), &n) > 0 && n > 0) {
-
+ if (mask && ovs_scan(s,
+ "eth(src="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT","
+ "dst="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT")%n",
+ ETH_ADDR_SCAN_ARGS(eth_key.eth_src),
+ ETH_ADDR_SCAN_ARGS(eth_key_mask.eth_src),
+ ETH_ADDR_SCAN_ARGS(eth_key.eth_dst),
+ ETH_ADDR_SCAN_ARGS(eth_key_mask.eth_dst), &n)) {
nl_msg_put_unspec(key, OVS_KEY_ATTR_ETHERNET,
ð_key, sizeof eth_key);
nl_msg_put_unspec(mask, OVS_KEY_ATTR_ETHERNET,
ð_key_mask, sizeof eth_key_mask);
return n;
- } else if (sscanf(s,
- "eth(src="ETH_ADDR_SCAN_FMT",dst="ETH_ADDR_SCAN_FMT")%n",
- ETH_ADDR_SCAN_ARGS(eth_key.eth_src),
- ETH_ADDR_SCAN_ARGS(eth_key.eth_dst), &n) > 0 && n > 0) {
+ } else if (ovs_scan(s, "eth(src="ETH_ADDR_SCAN_FMT","
+ "dst="ETH_ADDR_SCAN_FMT")%n",
+ ETH_ADDR_SCAN_ARGS(eth_key.eth_src),
+ ETH_ADDR_SCAN_ARGS(eth_key.eth_dst), &n)) {
nl_msg_put_unspec(key, OVS_KEY_ATTR_ETHERNET,
ð_key, sizeof eth_key);
}
{
- uint16_t vid, vid_mask;
+ int vid, vid_mask;
int pcp, pcp_mask;
int cfi, cfi_mask;
int n = -1;
- if (mask && (sscanf(s, "vlan(vid=%"SCNi16"/%"SCNi16",pcp=%i/%i)%n",
- &vid, &vid_mask, &pcp, &pcp_mask, &n) > 0 && n > 0)) {
+ if (mask && ovs_scan(s, "vlan(vid=%i/%i,pcp=%i/%i)%n",
+ &vid, &vid_mask, &pcp, &pcp_mask, &n)) {
nl_msg_put_be16(key, OVS_KEY_ATTR_VLAN,
htons((vid << VLAN_VID_SHIFT) |
(pcp << VLAN_PCP_SHIFT) |
(pcp_mask << VLAN_PCP_SHIFT) |
(1 << VLAN_CFI_SHIFT)));
return n;
- } else if ((sscanf(s, "vlan(vid=%"SCNi16",pcp=%i)%n",
- &vid, &pcp, &n) > 0 && n > 0)) {
+ } else if (ovs_scan(s, "vlan(vid=%i,pcp=%i)%n", &vid, &pcp, &n)) {
nl_msg_put_be16(key, OVS_KEY_ATTR_VLAN,
htons((vid << VLAN_VID_SHIFT) |
(pcp << VLAN_PCP_SHIFT) |
nl_msg_put_be16(mask, OVS_KEY_ATTR_VLAN, OVS_BE16_MAX);
}
return n;
- } else if (mask && (sscanf(s, "vlan(vid=%"SCNi16"/%"SCNi16",pcp=%i/%i,cfi=%i/%i)%n",
- &vid, &vid_mask, &pcp, &pcp_mask, &cfi, &cfi_mask, &n) > 0 && n > 0)) {
+ } else if (mask
+ && ovs_scan(s, "vlan(vid=%i/%i,pcp=%i/%i,cfi=%i/%i)%n",
+ &vid, &vid_mask, &pcp, &pcp_mask,
+ &cfi, &cfi_mask, &n)) {
nl_msg_put_be16(key, OVS_KEY_ATTR_VLAN,
htons((vid << VLAN_VID_SHIFT) |
(pcp << VLAN_PCP_SHIFT) |
(pcp_mask << VLAN_PCP_SHIFT) |
(cfi_mask << VLAN_CFI_SHIFT)));
return n;
- } else if ((sscanf(s, "vlan(vid=%"SCNi16",pcp=%i,cfi=%i)%n",
- &vid, &pcp, &cfi, &n) > 0 && n > 0)) {
+ } else if (ovs_scan(s, "vlan(vid=%i,pcp=%i,cfi=%i)%n",
+ &vid, &pcp, &cfi, &n)) {
nl_msg_put_be16(key, OVS_KEY_ATTR_VLAN,
htons((vid << VLAN_VID_SHIFT) |
(pcp << VLAN_PCP_SHIFT) |
int eth_type_mask;
int n = -1;
- if (mask && sscanf(s, "eth_type(%i/%i)%n",
- ð_type, ð_type_mask, &n) > 0 && n > 0) {
+ if (mask && ovs_scan(s, "eth_type(%i/%i)%n",
+ ð_type, ð_type_mask, &n)) {
if (eth_type != 0) {
nl_msg_put_be16(key, OVS_KEY_ATTR_ETHERTYPE, htons(eth_type));
}
nl_msg_put_be16(mask, OVS_KEY_ATTR_ETHERTYPE, htons(eth_type_mask));
return n;
- } else if (sscanf(s, "eth_type(%i)%n", ð_type, &n) > 0 && n > 0) {
+ } else if (ovs_scan(s, "eth_type(%i)%n", ð_type, &n)) {
nl_msg_put_be16(key, OVS_KEY_ATTR_ETHERTYPE, htons(eth_type));
if (mask) {
nl_msg_put_be16(mask, OVS_KEY_ATTR_ETHERTYPE, OVS_BE16_MAX);
int label_mask, tc_mask, ttl_mask, bos_mask;
int n = -1;
- if (mask && sscanf(s, "mpls(label=%"SCNi32"/%"SCNi32",tc=%i/%i,ttl=%i/%i,bos=%i/%i)%n",
- &label, &label_mask, &tc, &tc_mask, &ttl, &ttl_mask, &bos, &bos_mask, &n) > 0 && n > 0) {
+ if (mask && ovs_scan(s, "mpls(label=%i/%i,tc=%i/%i,"
+ "ttl=%i/%i,bos=%i/%i)%n",
+ &label, &label_mask, &tc, &tc_mask,
+ &ttl, &ttl_mask, &bos, &bos_mask, &n)) {
struct ovs_key_mpls *mpls, *mpls_mask;
mpls = nl_msg_put_unspec_uninit(key, OVS_KEY_ATTR_MPLS,
mpls_mask->mpls_lse = mpls_lse_from_components(
label_mask, tc_mask, ttl_mask, bos_mask);
return n;
- } else if (sscanf(s, "mpls(label=%"SCNi32",tc=%i,ttl=%i,bos=%i)%n",
- &label, &tc, &ttl, &bos, &n) > 0 &&
- n > 0) {
+ } else if (ovs_scan(s, "mpls(label=%i,tc=%i,ttl=%i,bos=%i)%n",
+ &label, &tc, &ttl, &bos, &n)) {
struct ovs_key_mpls *mpls;
mpls = nl_msg_put_unspec_uninit(key, OVS_KEY_ATTR_MPLS,
{
- ovs_be32 ipv4_src, ipv4_src_mask;
- ovs_be32 ipv4_dst, ipv4_dst_mask;
- int ipv4_proto, ipv4_proto_mask;
- int ipv4_tos, ipv4_tos_mask;
- int ipv4_ttl, ipv4_ttl_mask;
+ struct ovs_key_ipv4 ipv4_key;
+ struct ovs_key_ipv4 ipv4_mask;
+
char frag[8];
- int ipv4_frag_mask;
enum ovs_frag_type ipv4_frag;
int n = -1;
- if (mask && sscanf(s, "ipv4(src="IP_SCAN_FMT"/"IP_SCAN_FMT","
- "dst="IP_SCAN_FMT"/"IP_SCAN_FMT","
- "proto=%i/%i,tos=%i/%i,ttl=%i/%i,"
- "frag=%7[a-z]/%i)%n",
- IP_SCAN_ARGS(&ipv4_src), IP_SCAN_ARGS(&ipv4_src_mask),
- IP_SCAN_ARGS(&ipv4_dst), IP_SCAN_ARGS(&ipv4_dst_mask),
- &ipv4_proto, &ipv4_proto_mask,
- &ipv4_tos, &ipv4_tos_mask, &ipv4_ttl, &ipv4_ttl_mask,
- frag, &ipv4_frag_mask, &n) > 0
- && n > 0
+ if (mask
+ && ovs_scan(s, "ipv4(src="IP_SCAN_FMT"/"IP_SCAN_FMT","
+ "dst="IP_SCAN_FMT"/"IP_SCAN_FMT","
+ "proto=%"SCNi8"/%"SCNi8","
+ "tos=%"SCNi8"/%"SCNi8","
+ "ttl=%"SCNi8"/%"SCNi8","
+ "frag=%7[a-z]/%"SCNi8")%n",
+ IP_SCAN_ARGS(&ipv4_key.ipv4_src),
+ IP_SCAN_ARGS(&ipv4_mask.ipv4_src),
+ IP_SCAN_ARGS(&ipv4_key.ipv4_dst),
+ IP_SCAN_ARGS(&ipv4_mask.ipv4_dst),
+ &ipv4_key.ipv4_proto, &ipv4_mask.ipv4_proto,
+ &ipv4_key.ipv4_tos, &ipv4_mask.ipv4_tos,
+ &ipv4_key.ipv4_ttl, &ipv4_mask.ipv4_ttl,
+ frag, &ipv4_mask.ipv4_frag, &n)
&& ovs_frag_type_from_string(frag, &ipv4_frag)) {
- struct ovs_key_ipv4 ipv4_key;
- struct ovs_key_ipv4 ipv4_mask;
-
- ipv4_key.ipv4_src = ipv4_src;
- ipv4_key.ipv4_dst = ipv4_dst;
- ipv4_key.ipv4_proto = ipv4_proto;
- ipv4_key.ipv4_tos = ipv4_tos;
- ipv4_key.ipv4_ttl = ipv4_ttl;
ipv4_key.ipv4_frag = ipv4_frag;
nl_msg_put_unspec(key, OVS_KEY_ATTR_IPV4,
&ipv4_key, sizeof ipv4_key);
- ipv4_mask.ipv4_src = ipv4_src_mask;
- ipv4_mask.ipv4_dst = ipv4_dst_mask;
- ipv4_mask.ipv4_proto = ipv4_proto_mask;
- ipv4_mask.ipv4_tos = ipv4_tos_mask;
- ipv4_mask.ipv4_ttl = ipv4_ttl_mask;
- ipv4_mask.ipv4_frag = ipv4_frag_mask;
nl_msg_put_unspec(mask, OVS_KEY_ATTR_IPV4,
&ipv4_mask, sizeof ipv4_mask);
return n;
- } else if (sscanf(s, "ipv4(src="IP_SCAN_FMT",dst="IP_SCAN_FMT","
- "proto=%i,tos=%i,ttl=%i,frag=%7[a-z])%n",
- IP_SCAN_ARGS(&ipv4_src), IP_SCAN_ARGS(&ipv4_dst),
- &ipv4_proto, &ipv4_tos, &ipv4_ttl, frag, &n) > 0
- && n > 0
- && ovs_frag_type_from_string(frag, &ipv4_frag)) {
- struct ovs_key_ipv4 ipv4_key;
-
- ipv4_key.ipv4_src = ipv4_src;
- ipv4_key.ipv4_dst = ipv4_dst;
- ipv4_key.ipv4_proto = ipv4_proto;
- ipv4_key.ipv4_tos = ipv4_tos;
- ipv4_key.ipv4_ttl = ipv4_ttl;
+ } else if (ovs_scan(s, "ipv4(src="IP_SCAN_FMT",dst="IP_SCAN_FMT","
+ "proto=%"SCNi8",tos=%"SCNi8",ttl=%"SCNi8","
+ "frag=%7[a-z])%n",
+ IP_SCAN_ARGS(&ipv4_key.ipv4_src),
+ IP_SCAN_ARGS(&ipv4_key.ipv4_dst),
+ &ipv4_key.ipv4_proto,
+ &ipv4_key.ipv4_tos,
+ &ipv4_key.ipv4_ttl,
+ frag, &n) > 0
+ && ovs_frag_type_from_string(frag, &ipv4_frag)) {
ipv4_key.ipv4_frag = ipv4_frag;
nl_msg_put_unspec(key, OVS_KEY_ATTR_IPV4,
&ipv4_key, sizeof ipv4_key);
int ipv6_frag_mask;
int n = -1;
- if (mask && sscanf(s, "ipv6(src="IPV6_SCAN_FMT"/"IPV6_SCAN_FMT",dst="
- IPV6_SCAN_FMT"/"IPV6_SCAN_FMT","
- "label=%i/%i,proto=%i/%i,tclass=%i/%i,"
- "hlimit=%i/%i,frag=%7[a-z]/%i)%n",
- ipv6_src_s, ipv6_src_mask_s, ipv6_dst_s, ipv6_dst_mask_s,
- &ipv6_label, &ipv6_label_mask, &ipv6_proto,
- &ipv6_proto_mask, &ipv6_tclass, &ipv6_tclass_mask,
- &ipv6_hlimit, &ipv6_hlimit_mask, frag,
- &ipv6_frag_mask, &n) > 0
- && n > 0
+ if (mask && ovs_scan(s, "ipv6(src="IPV6_SCAN_FMT"/"IPV6_SCAN_FMT",dst="
+ IPV6_SCAN_FMT"/"IPV6_SCAN_FMT","
+ "label=%i/%i,proto=%i/%i,tclass=%i/%i,"
+ "hlimit=%i/%i,frag=%7[a-z]/%i)%n",
+ ipv6_src_s, ipv6_src_mask_s,
+ ipv6_dst_s, ipv6_dst_mask_s,
+ &ipv6_label, &ipv6_label_mask, &ipv6_proto,
+ &ipv6_proto_mask, &ipv6_tclass, &ipv6_tclass_mask,
+ &ipv6_hlimit, &ipv6_hlimit_mask, frag,
+ &ipv6_frag_mask, &n)
&& ovs_frag_type_from_string(frag, &ipv6_frag)) {
struct ovs_key_ipv6 ipv6_key;
struct ovs_key_ipv6 ipv6_mask;
nl_msg_put_unspec(mask, OVS_KEY_ATTR_IPV6,
&ipv6_mask, sizeof ipv6_mask);
return n;
- } else if (sscanf(s, "ipv6(src="IPV6_SCAN_FMT",dst="IPV6_SCAN_FMT","
- "label=%i,proto=%i,tclass=%i,hlimit=%i,frag=%7[a-z])%n",
- ipv6_src_s, ipv6_dst_s, &ipv6_label,
- &ipv6_proto, &ipv6_tclass, &ipv6_hlimit, frag, &n) > 0
- && n > 0
- && ovs_frag_type_from_string(frag, &ipv6_frag)) {
+ } else if (ovs_scan(s, "ipv6(src="IPV6_SCAN_FMT",dst="IPV6_SCAN_FMT","
+ "label=%i,proto=%i,tclass=%i,hlimit=%i,"
+ "frag=%7[a-z])%n",
+ ipv6_src_s, ipv6_dst_s, &ipv6_label,
+ &ipv6_proto, &ipv6_tclass, &ipv6_hlimit, frag, &n)
+ && ovs_frag_type_from_string(frag, &ipv6_frag)) {
struct ovs_key_ipv6 ipv6_key;
if (inet_pton(AF_INET6, ipv6_src_s, &ipv6_key.ipv6_src) != 1 ||
int tcp_dst_mask;
int n = -1;
- if (mask && sscanf(s, "tcp(src=%i/%i,dst=%i/%i)%n",
- &tcp_src, &tcp_src_mask, &tcp_dst, &tcp_dst_mask, &n) > 0
- && n > 0) {
+ if (mask && ovs_scan(s, "tcp(src=%i/%i,dst=%i/%i)%n",
+ &tcp_src, &tcp_src_mask, &tcp_dst,
+ &tcp_dst_mask, &n)) {
struct ovs_key_tcp tcp_key;
struct ovs_key_tcp tcp_mask;
nl_msg_put_unspec(mask, OVS_KEY_ATTR_TCP,
&tcp_mask, sizeof tcp_mask);
return n;
- } else if (sscanf(s, "tcp(src=%i,dst=%i)%n",&tcp_src, &tcp_dst, &n) > 0
- && n > 0) {
+ } else if (ovs_scan(s, "tcp(src=%i,dst=%i)%n",
+ &tcp_src, &tcp_dst, &n)) {
struct ovs_key_tcp tcp_key;
tcp_key.tcp_src = htons(tcp_src);
uint16_t tcp_flags, tcp_flags_mask;
int n = -1;
- if (mask && sscanf(s, "tcp_flags(%"SCNi16"/%"SCNi16")%n",
- &tcp_flags, &tcp_flags_mask, &n) > 0 && n > 0) {
+ if (mask && ovs_scan(s, "tcp_flags(%"SCNi16"/%"SCNi16")%n",
+ &tcp_flags, &tcp_flags_mask, &n) > 0 && n > 0) {
nl_msg_put_be16(key, OVS_KEY_ATTR_TCP_FLAGS, htons(tcp_flags));
nl_msg_put_be16(mask, OVS_KEY_ATTR_TCP_FLAGS, htons(tcp_flags_mask));
return n;
- } else if (sscanf(s, "tcp_flags(%"SCNi16")%n", &tcp_flags, &n) > 0 && n > 0) {
+ } else if (ovs_scan(s, "tcp_flags(%"SCNi16")%n", &tcp_flags, &n)) {
nl_msg_put_be16(key, OVS_KEY_ATTR_TCP_FLAGS, htons(tcp_flags));
if (mask) {
nl_msg_put_be16(mask, OVS_KEY_ATTR_TCP_FLAGS,
int udp_dst_mask;
int n = -1;
- if (mask && sscanf(s, "udp(src=%i/%i,dst=%i/%i)%n",
- &udp_src, &udp_src_mask,
- &udp_dst, &udp_dst_mask, &n) > 0 && n > 0) {
+ if (mask && ovs_scan(s, "udp(src=%i/%i,dst=%i/%i)%n",
+ &udp_src, &udp_src_mask,
+ &udp_dst, &udp_dst_mask, &n)) {
struct ovs_key_udp udp_key;
struct ovs_key_udp udp_mask;
&udp_mask, sizeof udp_mask);
return n;
}
- if (sscanf(s, "udp(src=%i,dst=%i)%n", &udp_src, &udp_dst, &n) > 0
- && n > 0) {
+ if (ovs_scan(s, "udp(src=%i,dst=%i)%n", &udp_src, &udp_dst, &n)) {
struct ovs_key_udp udp_key;
udp_key.udp_src = htons(udp_src);
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) {
+ if (mask && ovs_scan(s, "sctp(src=%i/%i,dst=%i/%i)%n",
+ &sctp_src, &sctp_src_mask,
+ &sctp_dst, &sctp_dst_mask, &n)) {
struct ovs_key_sctp sctp_key;
struct ovs_key_sctp sctp_mask;
&sctp_mask, sizeof sctp_mask);
return n;
}
- if (sscanf(s, "sctp(src=%i,dst=%i)%n", &sctp_src, &sctp_dst, &n) > 0
- && n > 0) {
+ if (ovs_scan(s, "sctp(src=%i,dst=%i)%n", &sctp_src, &sctp_dst, &n)) {
struct ovs_key_sctp sctp_key;
sctp_key.sctp_src = htons(sctp_src);
}
{
- int icmp_type;
- int icmp_code;
- int icmp_type_mask;
- int icmp_code_mask;
+ struct ovs_key_icmp icmp_key;
+ struct ovs_key_icmp icmp_mask;
int n = -1;
- if (mask && sscanf(s, "icmp(type=%i/%i,code=%i/%i)%n",
- &icmp_type, &icmp_type_mask,
- &icmp_code, &icmp_code_mask, &n) > 0 && n > 0) {
- struct ovs_key_icmp icmp_key;
- struct ovs_key_icmp icmp_mask;
-
- icmp_key.icmp_type = icmp_type;
- icmp_key.icmp_code = icmp_code;
+ if (mask && ovs_scan(s, "icmp(type=%"SCNi8"/%"SCNi8","
+ "code=%"SCNi8"/%"SCNi8")%n",
+ &icmp_key.icmp_type, &icmp_mask.icmp_type,
+ &icmp_key.icmp_code, &icmp_mask.icmp_code, &n)) {
nl_msg_put_unspec(key, OVS_KEY_ATTR_ICMP,
&icmp_key, sizeof icmp_key);
-
- icmp_mask.icmp_type = icmp_type_mask;
- icmp_mask.icmp_code = icmp_code_mask;
nl_msg_put_unspec(mask, OVS_KEY_ATTR_ICMP,
&icmp_mask, sizeof icmp_mask);
return n;
- } else if (sscanf(s, "icmp(type=%i,code=%i)%n",
- &icmp_type, &icmp_code, &n) > 0
- && n > 0) {
- struct ovs_key_icmp icmp_key;
-
- icmp_key.icmp_type = icmp_type;
- icmp_key.icmp_code = icmp_code;
+ } else if (ovs_scan(s, "icmp(type=%"SCNi8",code=%"SCNi8")%n",
+ &icmp_key.icmp_type, &icmp_key.icmp_code, &n)) {
nl_msg_put_unspec(key, OVS_KEY_ATTR_ICMP,
&icmp_key, sizeof icmp_key);
if (mask) {
{
struct ovs_key_icmpv6 icmpv6_key;
struct ovs_key_icmpv6 icmpv6_mask;
- int icmpv6_type_mask;
- int icmpv6_code_mask;
int n = -1;
- if (mask && sscanf(s, "icmpv6(type=%"SCNi8"/%i,code=%"SCNi8"/%i)%n",
- &icmpv6_key.icmpv6_type, &icmpv6_type_mask,
- &icmpv6_key.icmpv6_code, &icmpv6_code_mask, &n) > 0
- && n > 0) {
+ if (mask && ovs_scan(s, "icmpv6(type=%"SCNi8"/%"SCNi8","
+ "code=%"SCNi8"/%"SCNi8")%n",
+ &icmpv6_key.icmpv6_type, &icmpv6_mask.icmpv6_type,
+ &icmpv6_key.icmpv6_code, &icmpv6_mask.icmpv6_code,
+ &n)) {
nl_msg_put_unspec(key, OVS_KEY_ATTR_ICMPV6,
&icmpv6_key, sizeof icmpv6_key);
-
- icmpv6_mask.icmpv6_type = icmpv6_type_mask;
- icmpv6_mask.icmpv6_code = icmpv6_code_mask;
nl_msg_put_unspec(mask, OVS_KEY_ATTR_ICMPV6, &icmpv6_mask,
sizeof icmpv6_mask);
return n;
- } else if (sscanf(s, "icmpv6(type=%"SCNi8",code=%"SCNi8")%n",
- &icmpv6_key.icmpv6_type, &icmpv6_key.icmpv6_code,&n) > 0
- && n > 0) {
+ } else if (ovs_scan(s, "icmpv6(type=%"SCNi8",code=%"SCNi8")%n",
+ &icmpv6_key.icmpv6_type, &icmpv6_key.icmpv6_code,
+ &n)) {
nl_msg_put_unspec(key, OVS_KEY_ATTR_ICMPV6,
&icmpv6_key, sizeof icmpv6_key);
}
{
- ovs_be32 arp_sip, arp_sip_mask;
- ovs_be32 arp_tip, arp_tip_mask;
- int arp_op, arp_op_mask;
- uint8_t arp_sha[ETH_ADDR_LEN];
- uint8_t arp_sha_mask[ETH_ADDR_LEN];
- uint8_t arp_tha[ETH_ADDR_LEN];
- uint8_t arp_tha_mask[ETH_ADDR_LEN];
+ struct ovs_key_arp arp_key;
+ struct ovs_key_arp arp_mask;
+ uint16_t arp_op, arp_op_mask;
int n = -1;
- if (mask && sscanf(s, "arp(sip="IP_SCAN_FMT"/"IP_SCAN_FMT","
- "tip="IP_SCAN_FMT"/"IP_SCAN_FMT","
- "op=%i/%i,sha="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT","
- "tha="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT")%n",
- IP_SCAN_ARGS(&arp_sip), IP_SCAN_ARGS(&arp_sip_mask),
- IP_SCAN_ARGS(&arp_tip), IP_SCAN_ARGS(&arp_tip_mask),
- &arp_op, &arp_op_mask,
- ETH_ADDR_SCAN_ARGS(arp_sha),
- ETH_ADDR_SCAN_ARGS(arp_sha_mask),
- ETH_ADDR_SCAN_ARGS(arp_tha),
- ETH_ADDR_SCAN_ARGS(arp_tha_mask), &n) > 0 && n > 0) {
- struct ovs_key_arp arp_key;
- struct ovs_key_arp arp_mask;
-
- memset(&arp_key, 0, sizeof arp_key);
- arp_key.arp_sip = arp_sip;
- arp_key.arp_tip = arp_tip;
+ if (mask && ovs_scan(s, "arp(sip="IP_SCAN_FMT"/"IP_SCAN_FMT","
+ "tip="IP_SCAN_FMT"/"IP_SCAN_FMT","
+ "op=%"SCNi16"/%"SCNi16","
+ "sha="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT","
+ "tha="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT")%n",
+ IP_SCAN_ARGS(&arp_key.arp_sip),
+ IP_SCAN_ARGS(&arp_mask.arp_sip),
+ IP_SCAN_ARGS(&arp_key.arp_tip),
+ IP_SCAN_ARGS(&arp_mask.arp_tip),
+ &arp_op, &arp_op_mask,
+ ETH_ADDR_SCAN_ARGS(arp_key.arp_sha),
+ ETH_ADDR_SCAN_ARGS(arp_mask.arp_sha),
+ ETH_ADDR_SCAN_ARGS(arp_key.arp_tha),
+ ETH_ADDR_SCAN_ARGS(arp_mask.arp_tha), &n)) {
arp_key.arp_op = htons(arp_op);
- memcpy(arp_key.arp_sha, arp_sha, ETH_ADDR_LEN);
- memcpy(arp_key.arp_tha, arp_tha, ETH_ADDR_LEN);
nl_msg_put_unspec(key, OVS_KEY_ATTR_ARP, &arp_key, sizeof arp_key);
-
- arp_mask.arp_sip = arp_sip_mask;
- arp_mask.arp_tip = arp_tip_mask;
arp_mask.arp_op = htons(arp_op_mask);
- memcpy(arp_mask.arp_sha, arp_sha_mask, ETH_ADDR_LEN);
- memcpy(arp_mask.arp_tha, arp_tha_mask, ETH_ADDR_LEN);
nl_msg_put_unspec(mask, OVS_KEY_ATTR_ARP,
&arp_mask, sizeof arp_mask);
return n;
- } else if (sscanf(s, "arp(sip="IP_SCAN_FMT",tip="IP_SCAN_FMT","
- "op=%i,sha="ETH_ADDR_SCAN_FMT",tha="ETH_ADDR_SCAN_FMT")%n",
- IP_SCAN_ARGS(&arp_sip),
- IP_SCAN_ARGS(&arp_tip),
- &arp_op,
- ETH_ADDR_SCAN_ARGS(arp_sha),
- ETH_ADDR_SCAN_ARGS(arp_tha), &n) > 0 && n > 0) {
- struct ovs_key_arp arp_key;
-
- memset(&arp_key, 0, sizeof arp_key);
- arp_key.arp_sip = arp_sip;
- arp_key.arp_tip = arp_tip;
+ } else if (ovs_scan(s, "arp(sip="IP_SCAN_FMT",tip="IP_SCAN_FMT","
+ "op=%"SCNi16",sha="ETH_ADDR_SCAN_FMT","
+ "tha="ETH_ADDR_SCAN_FMT")%n",
+ IP_SCAN_ARGS(&arp_key.arp_sip),
+ IP_SCAN_ARGS(&arp_key.arp_tip),
+ &arp_op,
+ ETH_ADDR_SCAN_ARGS(arp_key.arp_sha),
+ ETH_ADDR_SCAN_ARGS(arp_key.arp_tha), &n)) {
arp_key.arp_op = htons(arp_op);
- memcpy(arp_key.arp_sha, arp_sha, ETH_ADDR_LEN);
- memcpy(arp_key.arp_tha, arp_tha, ETH_ADDR_LEN);
nl_msg_put_unspec(key, OVS_KEY_ATTR_ARP, &arp_key, sizeof arp_key);
if (mask) {
memset(nd_sll_mask, 0xff, sizeof nd_sll_mask);
memset(nd_tll_mask, 0xff, sizeof nd_tll_mask);
- if (mask && sscanf(s, "nd(target="IPV6_SCAN_FMT"/"IPV6_SCAN_FMT")%n",
- nd_target_s, nd_target_mask_s, &n) > 0 && n > 0) {
+ if (mask && ovs_scan(s, "nd(target="IPV6_SCAN_FMT"/"IPV6_SCAN_FMT")%n",
+ nd_target_s, nd_target_mask_s, &n)) {
put_nd_key(n, nd_target_s, NULL, NULL, key);
put_nd_mask(n, nd_target_mask_s, NULL, NULL, mask);
- } else if (sscanf(s, "nd(target="IPV6_SCAN_FMT")%n",
- nd_target_s, &n) > 0 && n > 0) {
+ } else if (ovs_scan(s, "nd(target="IPV6_SCAN_FMT")%n",
+ nd_target_s, &n)) {
put_nd_key(n, nd_target_s, NULL, NULL, key);
if (mask) {
put_nd_mask(n, nd_target_mask_s, NULL, NULL, mask);
}
- } else if (mask && sscanf(s, "nd(target="IPV6_SCAN_FMT"/"IPV6_SCAN_FMT
- ",sll="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT")%n",
- nd_target_s, nd_target_mask_s,
- ETH_ADDR_SCAN_ARGS(nd_sll),
- ETH_ADDR_SCAN_ARGS(nd_sll_mask), &n) > 0 && n > 0) {
+ } else if (mask &&
+ ovs_scan(s, "nd(target="IPV6_SCAN_FMT"/"IPV6_SCAN_FMT
+ ",sll="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT")%n",
+ nd_target_s, nd_target_mask_s,
+ ETH_ADDR_SCAN_ARGS(nd_sll),
+ ETH_ADDR_SCAN_ARGS(nd_sll_mask), &n)) {
put_nd_key(n, nd_target_s, nd_sll, NULL, key);
put_nd_mask(n, nd_target_mask_s, nd_sll_mask, NULL, mask);
- } else if (sscanf(s, "nd(target="IPV6_SCAN_FMT",sll="ETH_ADDR_SCAN_FMT")%n",
- nd_target_s, ETH_ADDR_SCAN_ARGS(nd_sll), &n) > 0
- && n > 0) {
+ } else if (ovs_scan(s, "nd(target="IPV6_SCAN_FMT","
+ "sll="ETH_ADDR_SCAN_FMT")%n",
+ nd_target_s, ETH_ADDR_SCAN_ARGS(nd_sll), &n)) {
put_nd_key(n, nd_target_s, nd_sll, NULL, key);
if (mask) {
put_nd_mask(n, nd_target_mask_s, nd_sll_mask, NULL, mask);
}
- } else if (mask && sscanf(s, "nd(target="IPV6_SCAN_FMT"/"IPV6_SCAN_FMT
- ",tll="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT")%n",
- nd_target_s, nd_target_mask_s,
- ETH_ADDR_SCAN_ARGS(nd_tll),
- ETH_ADDR_SCAN_ARGS(nd_tll_mask), &n) > 0 && n > 0) {
+ } else if (mask &&
+ ovs_scan(s, "nd(target="IPV6_SCAN_FMT"/"IPV6_SCAN_FMT
+ ",tll="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT")%n",
+ nd_target_s, nd_target_mask_s,
+ ETH_ADDR_SCAN_ARGS(nd_tll),
+ ETH_ADDR_SCAN_ARGS(nd_tll_mask), &n)) {
put_nd_key(n, nd_target_s, NULL, nd_tll, key);
put_nd_mask(n, nd_target_mask_s, NULL, nd_tll_mask, mask);
- } else if (sscanf(s, "nd(target="IPV6_SCAN_FMT",tll="ETH_ADDR_SCAN_FMT")%n",
- nd_target_s, ETH_ADDR_SCAN_ARGS(nd_tll), &n) > 0
- && n > 0) {
+ } else if (ovs_scan(s, "nd(target="IPV6_SCAN_FMT","
+ "tll="ETH_ADDR_SCAN_FMT")%n",
+ nd_target_s, ETH_ADDR_SCAN_ARGS(nd_tll), &n)) {
put_nd_key(n, nd_target_s, NULL, nd_tll, key);
if (mask) {
put_nd_mask(n, nd_target_mask_s, NULL, nd_tll_mask, mask);
}
- } else if (mask && sscanf(s, "nd(target="IPV6_SCAN_FMT"/"IPV6_SCAN_FMT
- ",sll="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT","
- "tll="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT")%n",
- nd_target_s, nd_target_mask_s,
- ETH_ADDR_SCAN_ARGS(nd_sll), ETH_ADDR_SCAN_ARGS(nd_sll_mask),
- ETH_ADDR_SCAN_ARGS(nd_tll), ETH_ADDR_SCAN_ARGS(nd_tll_mask),
- &n) > 0
- && n > 0) {
+ } else if (mask &&
+ ovs_scan(s, "nd(target="IPV6_SCAN_FMT"/"IPV6_SCAN_FMT
+ ",sll="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT","
+ "tll="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT")%n",
+ nd_target_s, nd_target_mask_s,
+ ETH_ADDR_SCAN_ARGS(nd_sll),
+ ETH_ADDR_SCAN_ARGS(nd_sll_mask),
+ ETH_ADDR_SCAN_ARGS(nd_tll),
+ ETH_ADDR_SCAN_ARGS(nd_tll_mask),
+ &n)) {
put_nd_key(n, nd_target_s, nd_sll, nd_tll, key);
put_nd_mask(n, nd_target_mask_s, nd_sll_mask, nd_tll_mask, mask);
- } else if (sscanf(s, "nd(target="IPV6_SCAN_FMT",sll="ETH_ADDR_SCAN_FMT","
- "tll="ETH_ADDR_SCAN_FMT")%n",
- nd_target_s, ETH_ADDR_SCAN_ARGS(nd_sll),
- ETH_ADDR_SCAN_ARGS(nd_tll), &n) > 0
- && n > 0) {
+ } else if (ovs_scan(s, "nd(target="IPV6_SCAN_FMT","
+ "sll="ETH_ADDR_SCAN_FMT","
+ "tll="ETH_ADDR_SCAN_FMT")%n",
+ nd_target_s, ETH_ADDR_SCAN_ARGS(nd_sll),
+ ETH_ADDR_SCAN_ARGS(nd_tll), &n)) {
put_nd_key(n, nd_target_s, nd_sll, nd_tll, key);
if (mask) {
put_nd_mask(n, nd_target_mask_s,
flow->dl_type == htons(ETH_TYPE_RARP)) {
struct ovs_key_arp *arp_key;
- arp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ARP,
- sizeof *arp_key);
- memset(arp_key, 0, sizeof *arp_key);
+ arp_key = nl_msg_put_unspec_zero(buf, OVS_KEY_ATTR_ARP,
+ sizeof *arp_key);
arp_key->arp_sip = data->nw_src;
arp_key->arp_tip = data->nw_dst;
arp_key->arp_op = htons(data->nw_proto);
nl_msg_put_u32(odp_actions, OVS_USERSPACE_ATTR_PID, pid);
if (userdata) {
userdata_ofs = odp_actions->size + NLA_HDRLEN;
- nl_msg_put_unspec(odp_actions, OVS_USERSPACE_ATTR_USERDATA,
- userdata, userdata_size);
+
+ /* The OVS kernel module before OVS 1.11 and the upstream Linux kernel
+ * module before Linux 3.10 required the userdata to be exactly 8 bytes
+ * long:
+ *
+ * - The kernel rejected shorter userdata with -ERANGE.
+ *
+ * - The kernel silently dropped userdata beyond the first 8 bytes.
+ *
+ * Thus, for maximum compatibility, always put at least 8 bytes. (We
+ * separately disable features that required more than 8 bytes.) */
+ memcpy(nl_msg_put_unspec_zero(odp_actions, OVS_USERSPACE_ATTR_USERDATA,
+ MAX(8, userdata_size)),
+ userdata, userdata_size);
} else {
userdata_ofs = 0;
}
case 1: {
struct ovs_action_push_mpls *mpls;
- mpls = nl_msg_put_unspec_uninit(odp_actions, OVS_ACTION_ATTR_PUSH_MPLS,
- sizeof *mpls);
- memset(mpls, 0, sizeof *mpls);
+ mpls = nl_msg_put_unspec_zero(odp_actions, OVS_ACTION_ATTR_PUSH_MPLS,
+ sizeof *mpls);
mpls->mpls_ethertype = flow->dl_type;
mpls->mpls_lse = flow->mpls_lse;
break;
unsigned int actions_len,
enum ofp_version version,
struct ofpbuf *ofpacts) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
const union ofp_action *actions;
enum ofperr error;
enum ofp_version version,
struct ofpbuf *ofpacts)
{
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
const struct ofp11_instruction *instructions;
const struct ofp11_instruction *insts[N_OVS_INSTRUCTIONS];
enum ofperr error;
}
}
-/* May modify flow->dl_type and flow->vlan_tci, caller must restore them.
+/* May modify flow->dl_type, flow->nw_proto and flow->vlan_tci,
+ * caller must restore them.
*
* Modifies some actions, filling in fields that could not be properly set
* without context. */
case OFPACT_PUSH_MPLS:
flow->dl_type = ofpact_get_PUSH_MPLS(a)->ethertype;
+ /* The packet is now MPLS and the MPLS payload is opaque.
+ * Thus nothing can be assumed about the network protocol.
+ * Temporarily mark that we have no nw_proto. */
+ flow->nw_proto = 0;
return 0;
case OFPACT_POP_MPLS:
struct ofpact *a;
ovs_be16 dl_type = flow->dl_type;
ovs_be16 vlan_tci = flow->vlan_tci;
+ uint8_t nw_proto = flow->nw_proto;
enum ofperr error = 0;
OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
/* Restore fields that may have been modified. */
flow->dl_type = dl_type;
flow->vlan_tci = vlan_tci;
+ flow->nw_proto = nw_proto;
return error;
}
const struct ofpact *a;
enum ovs_instruction_type inst;
- inst = OVSINST_OFPIT11_APPLY_ACTIONS;
+ inst = OVSINST_OFPIT13_METER;
OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
enum ovs_instruction_type next;
next = ovs_instruction_type_from_ofpact_type(a->type);
- if (inst == OVSINST_OFPIT11_APPLY_ACTIONS
- ? next < inst
- : next <= inst) {
+ if (a > ofpacts
+ && (inst == OVSINST_OFPIT11_APPLY_ACTIONS
+ ? next < inst
+ : next <= inst)) {
const char *name = ovs_instruction_name_from_type(inst);
const char *next_name = ovs_instruction_name_from_type(next);
enqueue = ofpact_get_ENQUEUE(a);
ds_put_format(s, "enqueue:");
ofputil_format_port(enqueue->port, s);
- ds_put_format(s, "q%"PRIu32, enqueue->queue);
+ ds_put_format(s, ":%"PRIu32, enqueue->queue);
break;
case OFPACT_OUTPUT_REG:
static char * WARN_UNUSED_RESULT
str_to_mac(const char *str, uint8_t mac[6])
{
- if (sscanf(str, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))
- != ETH_ADDR_SCAN_COUNT) {
+ if (!ovs_scan(str, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))) {
return xasprintf("invalid mac address %s", str);
}
return NULL;
parse_enqueue(char *arg, struct ofpbuf *ofpacts)
{
char *sp = NULL;
- char *port = strtok_r(arg, ":q", &sp);
+ char *port = strtok_r(arg, ":q,", &sp);
char *queue = strtok_r(NULL, "", &sp);
struct ofpact_enqueue *enqueue;
if (port == NULL || queue == NULL) {
- return xstrdup("\"enqueue\" syntax is \"enqueue:PORT:QUEUE\"");
+ return xstrdup("\"enqueue\" syntax is \"enqueue:PORT:QUEUE\" or "
+ "\"enqueue(PORT,QUEUE)\"");
}
enqueue = ofpact_put_ENQUEUE(ofpacts);
*usable_protocols = OFPUTIL_P_OF11_UP;
if (!strcasecmp(table_id, "all")) {
- tm->table_id = 255;
+ tm->table_id = OFPTT_ALL;
} else {
char *error = str_to_u8(table_id, "table_id", &tm->table_id);
if (error) {
void
ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
{
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 22);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 23);
/* Initialize most of wc. */
flow_wildcards_init_catchall(wc);
/* Get table ID.
*
- * OF1.1 entirely forbids table_id == 255.
- * OF1.2+ allows table_id == 255 only for deletes. */
+ * OF1.1 entirely forbids table_id == OFPTT_ALL.
+ * OF1.2+ allows table_id == OFPTT_ALL only for deletes. */
fm->table_id = ofm->table_id;
- if (fm->table_id == 255
+ if (fm->table_id == OFPTT_ALL
&& (oh->version == OFP11_VERSION
|| (ofm->command != OFPFC_DELETE &&
ofm->command != OFPFC_DELETE_STRICT))) {
}
mb = ofpbuf_put_uninit(bands, sizeof *mb);
mb->type = ntohs(ombh->type);
+ if (mb->type != OFPMBT13_DROP && mb->type != OFPMBT13_DSCP_REMARK) {
+ return OFPERR_OFPMMFC_BAD_BAND;
+ }
mb->rate = ntohl(ombh->rate);
mb->burst_size = ntohl(ombh->burst_size);
mb->prec_level = (mb->type == OFPMBT13_DSCP_REMARK) ?
/* Translate the message. */
mm->command = ntohs(omm->command);
+ if (mm->command != OFPMC13_ADD &&
+ mm->command != OFPMC13_MODIFY &&
+ mm->command != OFPMC13_DELETE) {
+ return OFPERR_OFPMMFC_BAD_COMMAND;
+ }
mm->meter.meter_id = ntohl(omm->meter_id);
if (mm->command == OFPMC13_DELETE) {
enum ofperr error;
mm->meter.flags = ntohs(omm->flags);
+ if (mm->meter.flags & OFPMF13_KBPS &&
+ mm->meter.flags & OFPMF13_PKTPS) {
+ return OFPERR_OFPMMFC_BAD_FLAGS;
+ }
mm->meter.bands = bands->data;
error = ofputil_pull_bands(&b, b.size, &mm->meter.n_bands, bands);
ofm->cookie = fm->cookie;
}
ofm->cookie_mask = fm->cookie_mask;
- if (fm->table_id != 255
+ if (fm->table_id != OFPTT_ALL
|| (protocol != OFPUTIL_P_OF11_STD
&& (fm->command == OFPFC_DELETE ||
fm->command == OFPFC_DELETE_STRICT))) {
uint8_t byte;
bool ok;
- s += strspn(s, " ");
+ s += strspn(s, " \t\r\n");
byte = hexits_value(s, 2, &ok);
if (!ok) {
if (n) {
bool
eth_addr_from_string(const char *s, uint8_t ea[ETH_ADDR_LEN])
{
- if (sscanf(s, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(ea))
- == ETH_ADDR_SCAN_COUNT) {
+ if (ovs_scan(s, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(ea))) {
return true;
} else {
memset(ea, 0, ETH_ADDR_LEN);
* uint8_t mac[ETH_ADDR_LEN];
* int a, b;
*
- * if (sscanf(string, "%d"ETH_ADDR_SCAN_FMT"%d",
- * &a, ETH_ADDR_SCAN_ARGS(mac), &b) == 1 + ETH_ADDR_SCAN_COUNT + 1) {
+ * if (ovs_scan(string, "%d"ETH_ADDR_SCAN_FMT"%d",
+ * &a, ETH_ADDR_SCAN_ARGS(mac), &b)) {
* ...
* }
*/
#define ETH_ADDR_SCAN_FMT "%"SCNx8":%"SCNx8":%"SCNx8":%"SCNx8":%"SCNx8":%"SCNx8
#define ETH_ADDR_SCAN_ARGS(ea) \
&(ea)[0], &(ea)[1], &(ea)[2], &(ea)[3], &(ea)[4], &(ea)[5]
-#define ETH_ADDR_SCAN_COUNT 6
#define ETH_TYPE_IP 0x0800
#define ETH_TYPE_ARP 0x0806
* ovs_be32 ip;
* int a, b;
*
- * if (sscanf(string, "%d"IP_SCAN_FMT"%d",
- * &a, IP_SCAN_ARGS(&ip), &b) == 1 + IP_SCAN_COUNT + 1) {
+ * if (ovs_scan(string, "%d"IP_SCAN_FMT"%d", &a, IP_SCAN_ARGS(&ip), &b)) {
* ...
* }
*/
&((uint8_t *) ip)[1], \
&((uint8_t *) ip)[2], \
&((uint8_t *) ip)[3]
-#define IP_SCAN_COUNT 4
/* Returns true if 'netmask' is a CIDR netmask, that is, if it consists of N
* high-order 1-bits and 32-N low-order 0-bits. */
* char ipv6_s[IPV6_SCAN_LEN + 1];
* struct in6_addr ipv6;
*
- * if (sscanf(string, "%d"IPV6_SCAN_FMT"%d", &a, ipv6_s, &b) == 3
+ * if (ovs_scan(string, "%d"IPV6_SCAN_FMT"%d", &a, ipv6_s, &b)
* && inet_pton(AF_INET6, ipv6_s, &ipv6) == 1) {
* ...
* }
#include <config.h>
#include "util.h"
+#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <pthread.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
+#include "bitmap.h"
#include "byte-order.h"
#include "coverage.h"
#include "ovs-thread.h"
}
/* Returns the number of trailing 0-bits in 'n'. Undefined if 'n' == 0. */
-#if !defined(UINT_MAX) || !defined(UINT32_MAX)
-#error "Someone screwed up the #includes."
-#elif __GNUC__ >= 4 && UINT_MAX == UINT32_MAX
+#if __GNUC__ >= 4
/* Defined inline in util.h. */
#else
-static int
-raw_ctz(uint32_t n)
+int
+raw_ctz(uint64_t n)
{
- unsigned int k;
- int count = 31;
+ uint64_t k;
+ int count = 63;
#define CTZ_STEP(X) \
k = n << (X); \
count -= X; \
n = k; \
}
+ CTZ_STEP(32);
CTZ_STEP(16);
CTZ_STEP(8);
CTZ_STEP(4);
#endif
/* Returns the number of 1-bits in 'x', between 0 and 32 inclusive. */
-unsigned int
-popcount(uint32_t x)
+static unsigned int
+count_1bits_32(uint32_t x)
{
/* In my testing, this implementation is over twice as fast as any other
* portable implementation that I tried, including GCC 4.4
#define INIT32(X) INIT16(X), INIT16((X) + 16)
#define INIT64(X) INIT32(X), INIT32((X) + 32)
- static const uint8_t popcount8[256] = {
+ static const uint8_t count_1bits_8[256] = {
INIT64(0), INIT64(64), INIT64(128), INIT64(192)
};
- return (popcount8[x & 0xff] +
- popcount8[(x >> 8) & 0xff] +
- popcount8[(x >> 16) & 0xff] +
- popcount8[x >> 24]);
+ return (count_1bits_8[x & 0xff] +
+ count_1bits_8[(x >> 8) & 0xff] +
+ count_1bits_8[(x >> 16) & 0xff] +
+ count_1bits_8[x >> 24]);
+}
+
+/* Returns the number of 1-bits in 'x', between 0 and 64 inclusive. */
+unsigned int
+count_1bits(uint64_t x)
+{
+ return count_1bits_32(x) + count_1bits_32(x >> 32);
}
/* Returns true if the 'n' bytes starting at 'p' are zeros. */
n_bits);
return ntohll(value);
}
+\f
+/* ovs_scan */
+
+struct scan_spec {
+ unsigned int width;
+ enum {
+ SCAN_DISCARD,
+ SCAN_CHAR,
+ SCAN_SHORT,
+ SCAN_INT,
+ SCAN_LONG,
+ SCAN_LLONG,
+ SCAN_INTMAX_T,
+ SCAN_PTRDIFF_T,
+ SCAN_SIZE_T
+ } type;
+};
+
+static const char *
+skip_spaces(const char *s)
+{
+ while (isspace((unsigned char) *s)) {
+ s++;
+ }
+ return s;
+}
+
+static const char *
+scan_int(const char *s, const struct scan_spec *spec, int base, va_list *args)
+{
+ const char *start = s;
+ uintmax_t value;
+ bool negative;
+ int n_digits;
+
+ negative = *s == '-';
+ s += *s == '-' || *s == '+';
+
+ if ((!base || base == 16) && *s == '0' && (s[1] == 'x' || s[1] == 'X')) {
+ base = 16;
+ s += 2;
+ } else if (!base) {
+ base = *s == '0' ? 8 : 10;
+ }
+
+ if (s - start >= spec->width) {
+ return NULL;
+ }
+
+ value = 0;
+ n_digits = 0;
+ while (s - start < spec->width) {
+ int digit = hexit_value(*s);
+
+ if (digit < 0 || digit >= base) {
+ break;
+ }
+ value = value * base + digit;
+ n_digits++;
+ s++;
+ }
+ if (!n_digits) {
+ return NULL;
+ }
+
+ if (negative) {
+ value = -value;
+ }
+
+ switch (spec->type) {
+ case SCAN_DISCARD:
+ break;
+ case SCAN_CHAR:
+ *va_arg(*args, char *) = value;
+ break;
+ case SCAN_SHORT:
+ *va_arg(*args, short int *) = value;
+ break;
+ case SCAN_INT:
+ *va_arg(*args, int *) = value;
+ break;
+ case SCAN_LONG:
+ *va_arg(*args, long int *) = value;
+ break;
+ case SCAN_LLONG:
+ *va_arg(*args, long long int *) = value;
+ break;
+ case SCAN_INTMAX_T:
+ *va_arg(*args, intmax_t *) = value;
+ break;
+ case SCAN_PTRDIFF_T:
+ *va_arg(*args, ptrdiff_t *) = value;
+ break;
+ case SCAN_SIZE_T:
+ *va_arg(*args, size_t *) = value;
+ break;
+ }
+ return s;
+}
+
+static const char *
+skip_digits(const char *s)
+{
+ while (*s >= '0' && *s <= '9') {
+ s++;
+ }
+ return s;
+}
+
+static const char *
+scan_float(const char *s, const struct scan_spec *spec, va_list *args)
+{
+ const char *start = s;
+ long double value;
+ char *tail;
+ char *copy;
+ bool ok;
+
+ s += *s == '+' || *s == '-';
+ s = skip_digits(s);
+ if (*s == '.') {
+ s = skip_digits(s + 1);
+ }
+ if (*s == 'e' || *s == 'E') {
+ s++;
+ s += *s == '+' || *s == '-';
+ s = skip_digits(s);
+ }
+
+ if (s - start > spec->width) {
+ s = start + spec->width;
+ }
+
+ copy = xmemdup0(start, s - start);
+ value = strtold(copy, &tail);
+ ok = *tail == '\0';
+ free(copy);
+ if (!ok) {
+ return NULL;
+ }
+
+ switch (spec->type) {
+ case SCAN_DISCARD:
+ break;
+ case SCAN_INT:
+ *va_arg(*args, float *) = value;
+ break;
+ case SCAN_LONG:
+ *va_arg(*args, double *) = value;
+ break;
+ case SCAN_LLONG:
+ *va_arg(*args, long double *) = value;
+ break;
+
+ case SCAN_CHAR:
+ case SCAN_SHORT:
+ case SCAN_INTMAX_T:
+ case SCAN_PTRDIFF_T:
+ case SCAN_SIZE_T:
+ NOT_REACHED();
+ }
+ return s;
+}
+
+static void
+scan_output_string(const struct scan_spec *spec,
+ const char *s, size_t n,
+ va_list *args)
+{
+ if (spec->type != SCAN_DISCARD) {
+ char *out = va_arg(*args, char *);
+ memcpy(out, s, n);
+ out[n] = '\0';
+ }
+}
+
+static const char *
+scan_string(const char *s, const struct scan_spec *spec, va_list *args)
+{
+ size_t n;
+
+ for (n = 0; n < spec->width; n++) {
+ if (!s[n] || isspace((unsigned char) s[n])) {
+ break;
+ }
+ }
+ if (!n) {
+ return NULL;
+ }
+
+ scan_output_string(spec, s, n, args);
+ return s + n;
+}
+
+static const char *
+parse_scanset(const char *p_, unsigned long *set, bool *complemented)
+{
+ const uint8_t *p = (const uint8_t *) p_;
+
+ *complemented = *p == '^';
+ p += *complemented;
+
+ if (*p == ']') {
+ bitmap_set1(set, ']');
+ p++;
+ }
+
+ while (*p && *p != ']') {
+ if (p[1] == '-' && p[2] != ']' && p[2] > *p) {
+ bitmap_set_multiple(set, *p, p[2] - *p + 1, true);
+ p += 3;
+ } else {
+ bitmap_set1(set, *p++);
+ }
+ }
+ if (*p == ']') {
+ p++;
+ }
+ return (const char *) p;
+}
+
+static const char *
+scan_set(const char *s, const struct scan_spec *spec, const char **pp,
+ va_list *args)
+{
+ unsigned long set[BITMAP_N_LONGS(UCHAR_MAX + 1)];
+ bool complemented;
+ unsigned int n;
+
+ /* Parse the scan set. */
+ memset(set, 0, sizeof set);
+ *pp = parse_scanset(*pp, set, &complemented);
+
+ /* Parse the data. */
+ n = 0;
+ while (s[n]
+ && bitmap_is_set(set, (unsigned char) s[n]) == !complemented
+ && n < spec->width) {
+ n++;
+ }
+ if (!n) {
+ return NULL;
+ }
+ scan_output_string(spec, s, n, args);
+ return s + n;
+}
+
+static const char *
+scan_chars(const char *s, const struct scan_spec *spec, va_list *args)
+{
+ unsigned int n = spec->width == UINT_MAX ? 1 : spec->width;
+
+ if (strlen(s) < n) {
+ return NULL;
+ }
+ if (spec->type != SCAN_DISCARD) {
+ memcpy(va_arg(*args, char *), s, n);
+ }
+ return s + n;
+}
+
+/* This is an implementation of the standard sscanf() function, with the
+ * following exceptions:
+ *
+ * - It returns true if the entire template was successfully scanned and
+ * converted, false if any conversion failed.
+ *
+ * - The standard doesn't define sscanf() behavior when an out-of-range value
+ * is scanned, e.g. if a "%"PRIi8 conversion scans "-1" or "0x1ff". Some
+ * implementations consider this an error and stop scanning. This
+ * implementation never considers an out-of-range value an error; instead,
+ * it stores the least-significant bits of the converted value in the
+ * destination, e.g. the value 255 for both examples earlier.
+ *
+ * - Only single-byte characters are supported, that is, the 'l' modifier
+ * on %s, %[, and %c is not supported. The GNU extension 'a' modifier is
+ * also not supported.
+ *
+ * - %p is not supported.
+ */
+bool
+ovs_scan(const char *s, const char *template, ...)
+{
+ const char *const start = s;
+ bool ok = false;
+ const char *p;
+ va_list args;
+
+ va_start(args, template);
+ p = template;
+ while (*p != '\0') {
+ struct scan_spec spec;
+ unsigned char c = *p++;
+ bool discard;
+
+ if (isspace(c)) {
+ s = skip_spaces(s);
+ continue;
+ } else if (c != '%') {
+ if (*s != c) {
+ goto exit;
+ }
+ s++;
+ continue;
+ } else if (*p == '%') {
+ if (*s++ != '%') {
+ goto exit;
+ }
+ p++;
+ continue;
+ }
+
+ /* Parse '*' flag. */
+ discard = *p == '*';
+ p += discard;
+
+ /* Parse field width. */
+ spec.width = 0;
+ while (*p >= '0' && *p <= '9') {
+ spec.width = spec.width * 10 + (*p++ - '0');
+ }
+ if (spec.width == 0) {
+ spec.width = UINT_MAX;
+ }
+
+ /* Parse type modifier. */
+ switch (*p) {
+ case 'h':
+ if (p[1] == 'h') {
+ spec.type = SCAN_CHAR;
+ p += 2;
+ } else {
+ spec.type = SCAN_SHORT;
+ p++;
+ }
+ break;
+
+ case 'j':
+ spec.type = SCAN_INTMAX_T;
+ p++;
+ break;
+
+ case 'l':
+ if (p[1] == 'l') {
+ spec.type = SCAN_LLONG;
+ p += 2;
+ } else {
+ spec.type = SCAN_LONG;
+ p++;
+ }
+ break;
+
+ case 'L':
+ case 'q':
+ spec.type = SCAN_LLONG;
+ p++;
+ break;
+
+ case 't':
+ spec.type = SCAN_PTRDIFF_T;
+ p++;
+ break;
+
+ case 'z':
+ spec.type = SCAN_SIZE_T;
+ p++;
+ break;
+
+ default:
+ spec.type = SCAN_INT;
+ break;
+ }
+
+ if (discard) {
+ spec.type = SCAN_DISCARD;
+ }
+
+ c = *p++;
+ if (c != 'c' && c != 'n' && c != '[') {
+ s = skip_spaces(s);
+ }
+ switch (c) {
+ case 'd':
+ s = scan_int(s, &spec, 10, &args);
+ break;
+
+ case 'i':
+ s = scan_int(s, &spec, 0, &args);
+ break;
+
+ case 'o':
+ s = scan_int(s, &spec, 8, &args);
+ break;
+
+ case 'u':
+ s = scan_int(s, &spec, 10, &args);
+ break;
+
+ case 'x':
+ case 'X':
+ s = scan_int(s, &spec, 16, &args);
+ break;
+
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'E':
+ case 'G':
+ s = scan_float(s, &spec, &args);
+ break;
+
+ case 's':
+ s = scan_string(s, &spec, &args);
+ break;
+
+ case '[':
+ s = scan_set(s, &spec, &p, &args);
+ break;
+
+ case 'c':
+ s = scan_chars(s, &spec, &args);
+ break;
+
+ case 'n':
+ if (spec.type != SCAN_DISCARD) {
+ *va_arg(args, int *) = s - start;
+ }
+ break;
+ }
+
+ if (!s) {
+ goto exit;
+ }
+ }
+ ok = true;
+
+exit:
+ va_end(args);
+ return ok;
+}
+
bool str_to_ulong(const char *, int base, unsigned long *);
bool str_to_ullong(const char *, int base, unsigned long long *);
+bool ovs_scan(const char *s, const char *template, ...) SCANF_FORMAT(2, 3);
+
bool str_to_double(const char *, double *);
int hexit_value(int c);
\f
/* Bitwise tests. */
-/* Returns the number of trailing 0-bits in 'n'. Undefined if 'n' == 0.
- *
- * This compiles to a single machine instruction ("bsf") with GCC on x86. */
-#if !defined(UINT_MAX) || !defined(UINT32_MAX)
-#error "Someone screwed up the #includes."
-#elif __GNUC__ >= 4 && UINT_MAX == UINT32_MAX
+int log_2_floor(uint32_t);
+int log_2_ceil(uint32_t);
+unsigned int count_1bits(uint64_t);
+
+/* Returns the number of trailing 0-bits in 'n'. Undefined if 'n' == 0. */
+#if __GNUC__ >= 4
static inline int
-raw_ctz(uint32_t n)
+raw_ctz(uint64_t n)
{
- return __builtin_ctz(n);
+ /* With GCC 4.7 on 32-bit x86, if a 32-bit integer is passed as 'n', using
+ * a plain __builtin_ctzll() here always generates an out-of-line function
+ * call. The test below helps it to emit a single 'bsf' instruction. */
+ return (__builtin_constant_p(n <= UINT32_MAX) && n <= UINT32_MAX
+ ? __builtin_ctz(n)
+ : __builtin_ctzll(n));
}
#else
/* Defined in util.c. */
-int raw_ctz(uint32_t n);
+int raw_ctz(uint64_t n);
#endif
/* Returns the number of trailing 0-bits in 'n', or 32 if 'n' is 0. */
return n ? raw_ctz(n) : 32;
}
-int log_2_floor(uint32_t);
-int log_2_ceil(uint32_t);
-unsigned int popcount(uint32_t);
+/* Returns the number of trailing 0-bits in 'n', or 64 if 'n' is 0. */
+static inline int
+ctz64(uint64_t n)
+{
+ return n ? raw_ctz(n) : 64;
+}
/* Returns the rightmost 1-bit in 'x' (e.g. 01011000 => 00001000), or 0 if 'x'
* is 0. */
char vlan_dev[16], real_dev[16];
int vid;
- if (sscanf(line, "%15[^ |] | %d | %15s",
- vlan_dev, &vid, real_dev) == 3) {
+ if (ovs_scan(line, "%15[^ |] | %d | %15s", vlan_dev, &vid, real_dev)) {
vlandev_add__(vlan_dev, real_dev, vid);
}
}
AC_DEFINE([ESX], [1], [Define to 1 if building on ESX.])
fi])
+dnl Checks for WINDOWS.
+AC_DEFUN([OVS_CHECK_WIN32],
+ [AC_CHECK_HEADER([windows.h],
+ [WIN32=yes],
+ [WIN32=no])
+ AM_CONDITIONAL([WIN32], [test "$WIN32" = yes])
+ if test "$WIN32" = yes; then
+ AC_DEFINE([WIN32], [1], [Define to 1 if building on WIN32.])
+ fi])
+
dnl Checks for Netlink support.
AC_DEFUN([OVS_CHECK_NETLINK],
[AC_CHECK_HEADER([linux/netlink.h],
for dir in $PATH; do
IFS=$ovs_save_IFS
test -z "$dir" && dir=.
- if test -x $dir/$binary && $dir/$binary -c 'import sys
+ if test -x "$dir"/"$binary" && "$dir"/"$binary" -c 'import sys
if sys.hexversion >= 0x02040000 and sys.hexversion < 0x03000000:
sys.exit(0)
else:
/* If 'struct flow' gets additional metadata, we'll need to zero it out
* before traversing a patch port. */
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 22);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 23);
if (!xport) {
xlate_report(ctx, "Nonexistent output port");
struct bfd *bfd; /* BFD, if any. */
bool may_enable; /* May be enabled in bonds. */
bool is_tunnel; /* This port is a tunnel. */
+ bool is_layer3; /* This is a layer 3 port. */
long long int carrier_seq; /* Carrier status changes. */
struct ofport_dpif *peer; /* Peer if patch port. */
static struct ofport_dpif *get_ofp_port(const struct ofproto_dpif *ofproto,
ofp_port_t ofp_port);
static void ofproto_trace(struct ofproto_dpif *, const struct flow *,
- const struct ofpbuf *packet, struct ds *);
+ const struct ofpbuf *packet,
+ const struct ofpact[], size_t ofpacts_len,
+ struct ds *);
/* Upcalls. */
static void handle_upcalls(struct dpif_backer *);
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
struct shash_node *node, *next;
- uint32_t max_ports;
int error;
error = open_dpif_backer(ofproto->up.type, &ofproto->backer);
return error;
}
- max_ports = dpif_get_max_ports(ofproto->backer->dpif);
- ofproto_init_max_ports(ofproto_, MIN(max_ports, ofp_to_u16(OFPP_MAX)));
-
ofproto->netflow = NULL;
ofproto->sflow = NULL;
ofproto->ipfix = NULL;
ovs_mutex_init(&ofproto->stats_mutex);
ovs_mutex_init(&ofproto->vsp_mutex);
- classifier_init(&ofproto->facets);
+ classifier_init(&ofproto->facets, NULL);
ofproto->consistency_rl = LLONG_MIN;
guarded_list_init(&ofproto->pins);
port->realdev_ofp_port = 0;
port->vlandev_vid = 0;
port->carrier_seq = netdev_get_carrier_resets(netdev);
+ port->is_layer3 = netdev_vport_is_layer3(netdev);
if (netdev_vport_is_patch(netdev)) {
/* By bailing out here, we don't submit the port to the sFlow module
bundle->floodable = true;
LIST_FOR_EACH (port, bundle_node, &bundle->ports) {
if (port->up.pp.config & OFPUTIL_PC_NO_FLOOD
+ || port->is_layer3
|| !stp_forward_in_state(port->stp_state)) {
bundle->floodable = false;
break;
port->bundle = bundle;
list_push_back(&bundle->ports, &port->bundle_node);
if (port->up.pp.config & OFPUTIL_PC_NO_FLOOD
+ || port->is_layer3
|| !stp_forward_in_state(port->stp_state)) {
bundle->floodable = false;
}
ds_put_char(result, '\n');
}
-static void
-ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[],
- void *aux OVS_UNUSED)
+/* Parses the 'argc' elements of 'argv', ignoring argv[0]. The following
+ * forms are supported:
+ *
+ * - [dpname] odp_flow [-generate | packet]
+ * - bridge br_flow [-generate | packet]
+ *
+ * On success, initializes '*ofprotop' and 'flow' and returns NULL. On failure
+ * returns a nonnull error message. */
+static const char *
+parse_flow_and_packet(int argc, const char *argv[],
+ struct ofproto_dpif **ofprotop, struct flow *flow,
+ struct ofpbuf **packetp)
{
const struct dpif_backer *backer = NULL;
- struct ofproto_dpif *ofproto;
- struct ofpbuf odp_key, odp_mask;
+ const char *error = NULL;
+ struct simap port_names = SIMAP_INITIALIZER(&port_names);
struct ofpbuf *packet;
- struct ds result;
- struct flow flow;
- struct simap port_names;
- char *s;
+ struct ofpbuf odp_key;
+ struct ofpbuf odp_mask;
- packet = NULL;
- backer = NULL;
- ds_init(&result);
ofpbuf_init(&odp_key, 0);
ofpbuf_init(&odp_mask, 0);
- simap_init(&port_names);
/* Handle "-generate" or a hex string as the last argument. */
if (!strcmp(argv[argc - 1], "-generate")) {
packet = ofpbuf_new(0);
argc--;
} else {
- const char *error = eth_from_hex(argv[argc - 1], &packet);
+ error = eth_from_hex(argv[argc - 1], &packet);
if (!error) {
argc--;
} else if (argc == 4) {
/* The 3-argument form must end in "-generate' or a hex string. */
- unixctl_command_reply_error(conn, error);
goto exit;
}
}
dp_type = argv[1];
}
backer = shash_find_data(&all_dpif_backers, dp_type);
- } else {
+ } else if (argc == 2) {
struct shash_node *node;
if (shash_count(&all_dpif_backers) == 1) {
node = shash_first(&all_dpif_backers);
backer = node->data;
}
+ } else {
+ error = "Syntax error";
+ goto exit;
}
if (backer && backer->dpif) {
struct dpif_port dpif_port;
* bridge is specified. If function odp_flow_key_from_string()
* returns 0, the flow is a odp_flow. If function
* parse_ofp_exact_flow() returns 0, the flow is a br_flow. */
- if (!odp_flow_from_string(argv[argc - 1], &port_names, &odp_key, &odp_mask)) {
+ if (!odp_flow_from_string(argv[argc - 1], &port_names,
+ &odp_key, &odp_mask)) {
if (!backer) {
- unixctl_command_reply_error(conn, "Cannot find the datapath");
+ error = "Cannot find the datapath";
goto exit;
}
- if (xlate_receive(backer, NULL, odp_key.data, odp_key.size, &flow,
- NULL, &ofproto, NULL)) {
- unixctl_command_reply_error(conn, "Invalid datapath flow");
+ if (xlate_receive(backer, NULL, odp_key.data, odp_key.size, flow,
+ NULL, ofprotop, NULL)) {
+ error = "Invalid datapath flow";
goto exit;
}
- ds_put_format(&result, "Bridge: %s\n", ofproto->up.name);
- } else if (!parse_ofp_exact_flow(&flow, NULL, argv[argc - 1], NULL)) {
+ } else if (!parse_ofp_exact_flow(flow, NULL, argv[argc - 1], NULL)) {
if (argc != 3) {
- unixctl_command_reply_error(conn, "Must specify bridge name");
+ error = "Must specify bridge name";
goto exit;
}
- ofproto = ofproto_dpif_lookup(argv[1]);
- if (!ofproto) {
- unixctl_command_reply_error(conn, "Unknown bridge name");
+ *ofprotop = ofproto_dpif_lookup(argv[1]);
+ if (!*ofprotop) {
+ error = "Unknown bridge name";
goto exit;
}
} else {
- unixctl_command_reply_error(conn, "Bad flow syntax");
+ error = "Bad flow syntax";
goto exit;
}
/* Generate a packet, if requested. */
if (packet) {
if (!packet->size) {
- flow_compose(packet, &flow);
+ flow_compose(packet, flow);
} else {
- union flow_in_port in_port_;
-
- in_port_ = flow.in_port;
- ds_put_cstr(&result, "Packet: ");
- s = ofp_packet_to_string(packet->data, packet->size);
- ds_put_cstr(&result, s);
- free(s);
+ union flow_in_port in_port = flow->in_port;
/* Use the metadata from the flow and the packet argument
* to reconstruct the flow. */
- flow_extract(packet, flow.skb_priority, flow.pkt_mark, NULL,
- &in_port_, &flow);
+ flow_extract(packet, flow->skb_priority, flow->pkt_mark, NULL,
+ &in_port, flow);
}
}
- ofproto_trace(ofproto, &flow, packet, &result);
- unixctl_command_reply(conn, ds_cstr(&result));
+ error = NULL;
exit:
- ds_destroy(&result);
- ofpbuf_delete(packet);
+ if (error) {
+ ofpbuf_delete(packet);
+ packet = NULL;
+ }
+ *packetp = packet;
ofpbuf_uninit(&odp_key);
ofpbuf_uninit(&odp_mask);
simap_destroy(&port_names);
+ return error;
+}
+
+static void
+ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[],
+ void *aux OVS_UNUSED)
+{
+ struct ofproto_dpif *ofproto;
+ struct ofpbuf *packet;
+ const char *error;
+ struct flow flow;
+
+ error = parse_flow_and_packet(argc, argv, &ofproto, &flow, &packet);
+ if (!error) {
+ struct ds result;
+
+ ds_init(&result);
+ ofproto_trace(ofproto, &flow, packet, NULL, 0, &result);
+ unixctl_command_reply(conn, ds_cstr(&result));
+ ds_destroy(&result);
+ ofpbuf_delete(packet);
+ } else {
+ unixctl_command_reply_error(conn, error);
+ }
}
+static void
+ofproto_unixctl_trace_actions(struct unixctl_conn *conn, int argc,
+ const char *argv[], void *aux OVS_UNUSED)
+{
+ enum ofputil_protocol usable_protocols;
+ struct ofproto_dpif *ofproto;
+ bool enforce_consistency;
+ struct ofpbuf ofpacts;
+ struct ofpbuf *packet;
+ struct ds result;
+ struct flow flow;
+ uint16_t in_port;
+
+ /* Three kinds of error return values! */
+ enum ofperr retval;
+ const char *error;
+ char *rw_error;
+
+ packet = NULL;
+ ds_init(&result);
+ ofpbuf_init(&ofpacts, 0);
+
+ /* Parse actions. */
+ rw_error = parse_ofpacts(argv[--argc], &ofpacts, &usable_protocols);
+ if (rw_error) {
+ unixctl_command_reply_error(conn, rw_error);
+ free(rw_error);
+ goto exit;
+ }
+
+ /* OpenFlow 1.1 and later suggest that the switch enforces certain forms of
+ * consistency between the flow and the actions, so enforce these by
+ * default if the actions can only work in OF1.1 or later. */
+ enforce_consistency = !(usable_protocols & OFPUTIL_P_OF10_ANY);
+ if (!strcmp(argv[1], "-consistent")) {
+ enforce_consistency = true;
+ argv++;
+ argc--;
+ }
+
+ error = parse_flow_and_packet(argc, argv, &ofproto, &flow, &packet);
+ if (error) {
+ unixctl_command_reply_error(conn, error);
+ goto exit;
+ }
+
+ /* Do the same checks as handle_packet_out() in ofproto.c.
+ *
+ * We pass a 'table_id' of 0 to ofproto_check_ofpacts(), which isn't
+ * strictly correct because these actions aren't in any table, but it's OK
+ * because it 'table_id' is used only to check goto_table instructions, but
+ * packet-outs take a list of actions and therefore it can't include
+ * instructions.
+ *
+ * We skip the "meter" check here because meter is an instruction, not an
+ * action, and thus cannot appear in ofpacts. */
+ in_port = ofp_to_u16(flow.in_port.ofp_port);
+ if (in_port >= ofproto->up.max_ports && in_port < ofp_to_u16(OFPP_MAX)) {
+ unixctl_command_reply_error(conn, "invalid in_port");
+ goto exit;
+ }
+ retval = ofpacts_check(ofpacts.data, ofpacts.size, &flow,
+ enforce_consistency,
+ u16_to_ofp(ofproto->up.max_ports), 0, 0);
+ if (retval) {
+ ds_clear(&result);
+ ds_put_format(&result, "Bad actions: %s", ofperr_to_string(retval));
+ unixctl_command_reply_error(conn, ds_cstr(&result));
+ goto exit;
+ }
+
+ ofproto_trace(ofproto, &flow, packet, ofpacts.data, ofpacts.size, &result);
+ unixctl_command_reply(conn, ds_cstr(&result));
+
+exit:
+ ds_destroy(&result);
+ ofpbuf_delete(packet);
+ ofpbuf_uninit(&ofpacts);
+}
+
+/* Implements a "trace" through 'ofproto''s flow table, appending a textual
+ * description of the results to 'ds'.
+ *
+ * The trace follows a packet with the specified 'flow' through the flow
+ * table. 'packet' may be nonnull to trace an actual packet, with consequent
+ * side effects (if it is nonnull then its flow must be 'flow').
+ *
+ * If 'ofpacts' is nonnull then its 'ofpacts_len' bytes specify the actions to
+ * trace, otherwise the actions are determined by a flow table lookup. */
static void
ofproto_trace(struct ofproto_dpif *ofproto, const struct flow *flow,
- const struct ofpbuf *packet, struct ds *ds)
+ const struct ofpbuf *packet,
+ const struct ofpact ofpacts[], size_t ofpacts_len,
+ struct ds *ds)
{
struct rule_dpif *rule;
struct flow_wildcards wc;
+ ds_put_format(ds, "Bridge: %s\n", ofproto->up.name);
ds_put_cstr(ds, "Flow: ");
flow_format(ds, flow);
ds_put_char(ds, '\n');
flow_wildcards_init_catchall(&wc);
- rule_dpif_lookup(ofproto, flow, &wc, &rule);
+ if (ofpacts) {
+ rule = NULL;
+ } else {
+ rule_dpif_lookup(ofproto, flow, &wc, &rule);
- trace_format_rule(ds, 0, rule);
- if (rule == ofproto->miss_rule) {
- ds_put_cstr(ds, "\nNo match, flow generates \"packet in\"s.\n");
- } else if (rule == ofproto->no_packet_in_rule) {
- ds_put_cstr(ds, "\nNo match, packets dropped because "
- "OFPPC_NO_PACKET_IN is set on in_port.\n");
- } else if (rule == ofproto->drop_frags_rule) {
- ds_put_cstr(ds, "\nPackets dropped because they are IP fragments "
- "and the fragment handling mode is \"drop\".\n");
+ trace_format_rule(ds, 0, rule);
+ if (rule == ofproto->miss_rule) {
+ ds_put_cstr(ds, "\nNo match, flow generates \"packet in\"s.\n");
+ } else if (rule == ofproto->no_packet_in_rule) {
+ ds_put_cstr(ds, "\nNo match, packets dropped because "
+ "OFPPC_NO_PACKET_IN is set on in_port.\n");
+ } else if (rule == ofproto->drop_frags_rule) {
+ ds_put_cstr(ds, "\nPackets dropped because they are IP fragments "
+ "and the fragment handling mode is \"drop\".\n");
+ }
}
- if (rule) {
+ if (rule || ofpacts) {
uint64_t odp_actions_stub[1024 / 8];
struct ofpbuf odp_actions;
struct trace_ctx trace;
ofpbuf_use_stub(&odp_actions,
odp_actions_stub, sizeof odp_actions_stub);
xlate_in_init(&trace.xin, ofproto, flow, rule, tcp_flags, packet);
+ if (ofpacts) {
+ trace.xin.ofpacts = ofpacts;
+ trace.xin.ofpacts_len = ofpacts_len;
+ }
trace.xin.resubmit_hook = trace_resubmit;
trace.xin.report_hook = trace_report;
unixctl_command_register(
"ofproto/trace",
- "[dp_name]|bridge odp_flow|br_flow [-generate|packet]",
+ "{[dp_name] odp_flow | bridge br_flow} [-generate|packet]",
1, 3, ofproto_unixctl_trace, NULL);
+ unixctl_command_register(
+ "ofproto/trace-packet-out",
+ "[-consistent] {[dp_name] odp_flow | bridge br_flow} [-generate|packet] actions",
+ 2, 6, ofproto_unixctl_trace_actions, NULL);
unixctl_command_register("fdb/flush", "[bridge]", 0, 1,
ofproto_unixctl_fdb_flush, NULL);
unixctl_command_register("fdb/show", "bridge", 1, 1,
Lists the names of the running ofproto instances. These are the names
that may be used on \fBofproto/trace\fR.
.
-.IP "\fBofproto/trace\fR [\fIdpname\fR] \fIodp_flow\fR [\fB\-generate \fR| \
-\fIpacket\fR]"
-.IQ "\fBofproto/trace\fR \fIbridge\fR \fIbr_flow\fR \
-[\fB\-generate \fR| \fIpacket\fR]"
+.IP "\fBofproto/trace\fR [\fIdpname\fR] \fIodp_flow\fR [\fB\-generate \fR| \fIpacket\fR]"
+.IQ "\fBofproto/trace\fR \fIbridge\fR \fIbr_flow\fR [\fB\-generate \fR| \fIpacket\fR]"
+.IQ "\fBofproto/trace\-packet\-out\fR [\fB\-consistent\fR] [\fIdpname\fR] \fIodp_flow\fR [\fB\-generate \fR| \fIpacket\fR] \fIactions\fR"
+.IQ "\fBofproto/trace\-packet\-out\fR [\fB\-consistent\fR] \fIbridge\fR \fIbr_flow\fR [\fB\-generate \fR| \fIpacket\fR] \fIactions\fR"
Traces the path of an imaginary packet through \fIswitch\fR and
-reports the path that it took. The packet's headers (e.g. source and
-destination) and metadata (e.g. input port), together called its
-``flow,'' are usually all that matter for this purpose. You can
-specify the flow in the following ways:
+reports the path that it took. The initial treatment of the packet
+varies based on the command:
+.
+.RS
+.IP \(bu
+\fBofproto/trace\fR looks the packet up in the OpenFlow flow table, as
+if the packet had arrived on an OpenFlow port.
+.
+.IP \(bu
+\fBofproto/trace\-packet\-out\fR applies the specified OpenFlow
+\fIactions\fR, as if the packet, flow, and actions had been specified
+in an OpenFlow ``packet-out'' request.
+.RE
+.
+.IP
+The packet's headers (e.g. source and destination) and metadata
+(e.g. input port), together called its ``flow,'' are usually all that
+matter for the purpose of tracing a packet. You can specify the flow
+in the following ways:
.
.RS
.IP "\fIdpname\fR \fIodp_flow\fR"
.IP "Side effects."
Some actions have side effects. For example, the \fBnormal\fR action
can update the MAC learning table, and the \fBlearn\fR action can
-change OpenFlow tables. \fBofproto/trace\fR only performs side
+change OpenFlow tables. The trace commands only perform side
effects when a packet is specified. If you want side effects to take
place, then you must supply a packet.
.
.IP
(Output actions are obviously side effects too, but
-\fBofproto/trace\fR never executes them, even when one specifies a
+the trace commands never execute them, even when one specifies a
packet.)
.
.IP "Incomplete information."
path of a packet using just the flow, but in some special
circumstances it needs to look at parts of the packet that are not
included in the flow. When this is the case, and you do not supply a
-packet, then \fBofproto/trace\fR will tell you it needs a packet.
+packet, then a trace command will tell you it needs a packet.
.RE
.
.IP
-If you wish to include a packet as part of the \fBofproto/trace\fR
-operation, there are two ways to do it:
+If you wish to include a packet as part of a trace operation, there
+are two ways to do it:
.
.RS
.IP \fB\-generate\fR
.IP \fIin_port\fR
The port on which the packet arrived.
.RE
+.RE
.
+.IP
The in_port value is kernel datapath port number for the first format
and OpenFlow port number for the second format. The numbering of these
two types of port usually differs and there is no relationship.
-.RE
+.
+.IP
+\fBofproto\-trace\-packet\-out\fR accepts an additional
+\fB\-consistent\fR option. With this option specified, the command
+rejects \fIactions\fR that are inconsistent with the specified packet.
+(An example of an inconsistency is attempting to strip the VLAN tag
+from a packet that does not have a VLAN tag.) Open vSwitch ignores
+most forms of inconsistency in OpenFlow 1.0 and rejects
+inconsistencies in later versions of OpenFlow. The option is
+necessary because the command does not ordinarily imply a particular
+OpenFlow version. One exception is that, when \fIactions\fR includes
+an action that only OpenFlow 1.1 and later supports (such as
+\fBpush_vlan\fR), \fB\-consistent\fR is automatically enabled.
.IP "\fBofproto/self\-check\fR [\fIswitch\fR]"
Runs an internal consistency check on \fIswitch\fR, if specified,
otherwise on all ofproto instances, and responds with a brief summary
update_port(ofproto, netdev_name);
}
if (ofp_portp) {
- struct ofproto_port ofproto_port;
-
- ofproto_port_query_by_name(ofproto, netdev_get_name(netdev),
- &ofproto_port);
- *ofp_portp = error ? OFPP_NONE : ofproto_port.ofp_port;
- ofproto_port_destroy(&ofproto_port);
+ *ofp_portp = OFPP_NONE;
+ if (!error) {
+ struct ofproto_port ofproto_port;
+
+ error = ofproto_port_query_by_name(ofproto,
+ netdev_get_name(netdev),
+ &ofproto_port);
+ if (!error) {
+ *ofp_portp = ofproto_port.ofp_port;
+ ofproto_port_destroy(&ofproto_port);
+ }
+ }
}
return error;
}
/* Search for a free OpenFlow port number. We try not to
* immediately reuse them to prevent problems due to old
- * flows. */
+ * flows.
+ *
+ * We limit the automatically assigned port numbers to the lower half
+ * of the port range, to reserve the upper half for assignment by
+ * controllers. */
for (;;) {
- if (++ofproto->alloc_port_no >= ofproto->max_ports) {
+ if (++ofproto->alloc_port_no >= MIN(ofproto->max_ports, 32768)) {
ofproto->alloc_port_no = 1;
}
last_used_at = ofport_get_usage(ofproto,
oftable_init(struct oftable *table)
{
memset(table, 0, sizeof *table);
- classifier_init(&table->cls);
+ classifier_init(&table->cls, flow_segment_u32s);
table->max_flows = UINT_MAX;
}
-/* 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.
is_valid_version(const char *s)
{
int n = -1;
- ignore(sscanf(s, "%*[0-9].%*[0-9].%*[0-9]%n", &n));
+ ignore(ovs_scan(s, "%*[0-9].%*[0-9].%*[0-9]%n", &n));
return n != -1 && s[n] == '\0';
}
[AT_SETUP([miniflow - m4_bpatsubst(testname, [-], [ ])])
AT_CHECK([test-classifier testname], [0], [], [])
AT_CLEANUP])])
+
+AT_BANNER([flow classifier lookup segmentation])
+AT_SETUP([flow classifier - lookup segmentation])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [2], [3])
+AT_DATA([flows.txt], [dnl
+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.15,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=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
+table=0 in_port=3 priority=16,tcp,nw_src=10.1.0.0/255.255.0.0,action=output(1)
+table=0 in_port=3 priority=0,ip,action=drop
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=2,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=192.168.0.2,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=80'], [0], [stdout])
+AT_CHECK([tail -2 stdout], [0],
+ [Relevant fields: skb_priority=0,tcp,in_port=2,nw_dst=192.168.0.0/16,nw_frag=no
+Datapath actions: 1
+])
+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=192.168.0.2,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=80'], [0], [stdout])
+AT_CHECK([tail -2 stdout], [0],
+ [Relevant fields: skb_priority=0,tcp,in_port=1,nw_dst=192.168.0.2,nw_frag=no
+Datapath actions: 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=80'], [0], [stdout])
+AT_CHECK([tail -2 stdout], [0],
+ [Relevant fields: skb_priority=0,tcp,in_port=1,nw_dst=10.1.2.15,nw_frag=no,tp_dst=80
+Datapath actions: 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],
+ [Relevant fields: skb_priority=0,tcp,in_port=1,nw_dst=10.1.2.15,nw_frag=no,tp_dst=79
+Datapath actions: 2
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
[[ctz],
[round_up_pow2],
[round_down_pow2],
- [popcount],
+ [count_1bits],
[log_2_floor],
[bitwise_copy],
[bitwise_zero],
[bitwise_one],
- [bitwise_is_all_zeros]],
+ [bitwise_is_all_zeros],
+ [ovs_scan]],
[AT_SETUP([testname[()] function])
AT_KEYWORDS([testname])
AT_CHECK([test-util testname], [0], [], [])
# actions=mod_tp_dst:443
000a 0008 01bb 0000
-# actions=enqueue:10q55
+# actions=enqueue:10:55
000b 0010 000a 000000000000 00000037
# actions=resubmit:5
[ovs-ofctl '-vPATTERN:console:%c|%p|%m' parse-ofp11-instructions < input.txt],
[0], [expout], [experr])
AT_CLEANUP
+
+AT_SETUP([ofp-actions - inconsistent MPLS actions])
+OVS_VSWITCHD_START
+dnl OK: Use fin_timeout action on TCP flow
+AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn add-flow br0 'tcp actions=fin_timeout(idle_timeout=1)'])
+dnl Bad: Use fin_timeout action on TCP flow that has been converted to MPLS
+AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn add-flow br0 'tcp actions=push_mpls:0x8847,fin_timeout(idle_timeout=1)'],
+ [1], [], [dnl
+ovs-ofctl: actions are invalid with specified match (OFPBAC_MATCH_INCONSISTENT)
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
])
AT_CLEANUP
+dnl This triggered a buggy "instructions out of order" message earlier.
+AT_SETUP([OFPT_FLOW_MOD - OF1.3 - meter])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+04 0e 00 40 cf fe 6b 86 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 e8 \
+ff ff ff ff ff ff ff ff ff ff ff ff 00 00 00 00 \
+00 01 00 04 00 00 00 00 00 06 00 08 00 00 00 01"], [0], [dnl
+OFPT_FLOW_MOD (OF1.3) (xid=0xcffe6b86): ADD priority=1000 actions=meter:1
+])
+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_METER_MOD request - bad band - OF1.3])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+04 1d 00 20 85 01 d7 38 00 00 00 00 00 00 00 01
+00 05 00 10 00 00 00 02 00 00 00 02 00 00 00 00
+"], [0], [dnl
+OFPT_METER_MOD (OF1.3) (xid=0x8501d738): ***decode error: OFPMMFC_BAD_BAND***
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_METER_MOD request - bad command - OF1.3])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+04 1d 00 10 28 a6 26 52 00 08 00 00 00 00 00 01
+"], [0], [dnl
+OFPT_METER_MOD (OF1.3) (xid=0x28a62652): ***decode error: OFPMMFC_BAD_COMMAND***
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_METER_MOD request - bad flags - OF1.3])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+04 1d 00 20 82 b3 a1 a4 00 00 00 03 00 00 00 01 \
+00 01 00 10 00 00 00 02 00 00 00 02 00 00 00 00 \
+"], [0], [dnl
+OFPT_METER_MOD (OF1.3) (xid=0x82b3a1a4): ***decode error: OFPMMFC_BAD_FLAGS***
+])
+AT_CLEANUP
+
AT_SETUP([OFPST_METER request - OF1.3])
AT_KEYWORDS([ofp-print OFPT_STATS_REQUEST])
AT_CHECK([ovs-ofctl ofp-print "041200180000000200090000000000000000000100000000"], [0], [dnl
cookie=0xb dl_src=50:55:55:55:55:55 dl_type=0x8847 actions=load:1000->OXM_OF_MPLS_LABEL[[]],controller
cookie=0xd dl_src=60:66:66:66:66:66 actions=pop_mpls:0x0800,controller
cookie=0xc dl_src=70:77:77:77:77:77 actions=push_mpls:0x8848,load:1000->OXM_OF_MPLS_LABEL[[]],load:7->OXM_OF_MPLS_TC[[]],controller
-cookie=0xd dl_src=80:88:88:88:88:88 arp actions=load:2->OXM_OF_ARP_OP[[]],controller,load:0xc0a88001->OXM_OF_ARP_SPA[[]],controller,load:0x404444444441->OXM_OF_ARP_THA[[]],controller
+cookie=0xd dl_src=80:88:88:88:88:88 arp actions=load:2->OXM_OF_ARP_OP[[]],controller,load:0xc0a88001->OXM_OF_ARP_SPA[[]],controller,load:0x404444444441->OXM_OF_ARP_THA[[]],load:0x01010101->OXM_OF_ARP_SPA[[]],load:0x02020202->OXM_OF_ARP_TPA[[]],controller
])
AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
arp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=192.168.128.1,arp_tpa=192.168.0.2,arp_op=2,arp_sha=50:54:00:00:00:05,arp_tha=00:00:00:00:00:00
NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
-arp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=192.168.128.1,arp_tpa=192.168.0.2,arp_op=2,arp_sha=50:54:00:00:00:05,arp_tha=40:44:44:44:44:41
+arp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=1.1.1.1,arp_tpa=2.2.2.2,arp_op=2,arp_sha=50:54:00:00:00:05,arp_tha=40:44:44:44:44:41
NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
arp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=192.168.0.1,arp_tpa=192.168.0.2,arp_op=2,arp_sha=50:54:00:00:00:05,arp_tha=00:00:00:00:00:00
NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
arp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=192.168.128.1,arp_tpa=192.168.0.2,arp_op=2,arp_sha=50:54:00:00:00:05,arp_tha=00:00:00:00:00:00
NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
-arp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=192.168.128.1,arp_tpa=192.168.0.2,arp_op=2,arp_sha=50:54:00:00:00:05,arp_tha=40:44:44:44:44:41
+arp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=1.1.1.1,arp_tpa=2.2.2.2,arp_op=2,arp_sha=50:54:00:00:00:05,arp_tha=40:44:44:44:44:41
NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
arp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=192.168.0.1,arp_tpa=192.168.0.2,arp_op=2,arp_sha=50:54:00:00:00:05,arp_tha=00:00:00:00:00:00
NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
arp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=192.168.128.1,arp_tpa=192.168.0.2,arp_op=2,arp_sha=50:54:00:00:00:05,arp_tha=00:00:00:00:00:00
NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
-arp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=192.168.128.1,arp_tpa=192.168.0.2,arp_op=2,arp_sha=50:54:00:00:00:05,arp_tha=40:44:44:44:44:41
+arp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=1.1.1.1,arp_tpa=2.2.2.2,arp_op=2,arp_sha=50:54:00:00:00:05,arp_tha=40:44:44:44:44:41
])
AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore])
cookie=0xa, n_packets=3, n_bytes=180, dl_src=41:44:44:44:44:42 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],pop_mpls:0x0800,CONTROLLER:65535
cookie=0xb, n_packets=3, n_bytes=180, mpls,dl_src=50:55:55:55:55:55 actions=load:0x3e8->OXM_OF_MPLS_LABEL[[]],CONTROLLER:65535
cookie=0xc, n_packets=3, n_bytes=180, dl_src=70:77:77:77:77:77 actions=push_mpls:0x8848,load:0x3e8->OXM_OF_MPLS_LABEL[[]],load:0x7->OXM_OF_MPLS_TC[[]],CONTROLLER:65535
- cookie=0xd, n_packets=3, n_bytes=180, arp,dl_src=80:88:88:88:88:88 actions=load:0x2->NXM_OF_ARP_OP[[]],CONTROLLER:65535,load:0xc0a88001->NXM_OF_ARP_SPA[[]],CONTROLLER:65535,load:0x404444444441->NXM_NX_ARP_THA[[]],CONTROLLER:65535
+ cookie=0xd, n_packets=3, n_bytes=180, arp,dl_src=80:88:88:88:88:88 actions=load:0x2->NXM_OF_ARP_OP[[]],CONTROLLER:65535,load:0xc0a88001->NXM_OF_ARP_SPA[[]],CONTROLLER:65535,load:0x404444444441->NXM_NX_ARP_THA[[]],load:0x1010101->NXM_OF_ARP_SPA[[]],load:0x2020202->NXM_OF_ARP_TPA[[]],CONTROLLER:65535
cookie=0xd, n_packets=3, n_bytes=186, dl_src=60:66:66:66:66:66 actions=pop_mpls:0x0800,CONTROLLER:65535
n_packets=3, n_bytes=180, dl_src=10:11:11:11:11:11 actions=CONTROLLER:65535
NXST_FLOW reply:
AT_CHECK([tail -1 stdout], [0], [dnl
Datapath actions: 2
])
-AT_CHECK([head -n 3 stdout], [0], [dnl
+AT_CHECK([head -n 2 stdout], [0], [dnl
Bridge: br0
-Packet: arp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:01,dl_dst=50:54:00:00:00:02,arp_spa=0.0.0.0,arp_tpa=0.0.0.0,arp_sha=00:00:00:00:00:00,arp_tha=00:00:00:00:00:00
Flow: pkt_mark=0x2,skb_priority=0x1,arp,metadata=0,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:01,dl_dst=50:54:00:00:00:02,arp_spa=0.0.0.0,arp_tpa=0.0.0.0,arp_sha=00:00:00:00:00:00,arp_tha=00:00:00:00:00:00
])
AT_CHECK([tail -1 stdout], [0], [dnl
Datapath actions: 2
])
-AT_CHECK([head -n 3 stdout], [0], [dnl
+AT_CHECK([head -n 2 stdout], [0], [dnl
Bridge: br0
-Packet: arp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:01,dl_dst=50:54:00:00:00:02,arp_spa=0.0.0.0,arp_tpa=0.0.0.0,arp_sha=00:00:00:00:00:00,arp_tha=00:00:00:00:00:00
Flow: pkt_mark=0x2,skb_priority=0x1,arp,metadata=0,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:01,dl_dst=50:54:00:00:00:02,arp_spa=0.0.0.0,arp_tpa=0.0.0.0,arp_sha=00:00:00:00:00:00,arp_tha=00:00:00:00:00:00
])
Datapath actions: 1
])
AT_CHECK([head -n 2 stdout], [0], [dnl
-Packet: arp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:02,dl_dst=50:54:00:00:00:01,arp_spa=0.0.0.0,arp_tpa=0.0.0.0,arp_sha=00:00:00:00:00:00,arp_tha=00:00:00:00:00:00
+Bridge: br0
Flow: pkt_mark=0x1,skb_priority=0x2,arp,metadata=0,in_port=2,vlan_tci=0x0000,dl_src=50:54:00:00:00:02,dl_dst=50:54:00:00:00:01,arp_spa=0.0.0.0,arp_tpa=0.0.0.0,arp_sha=00:00:00:00:00:00,arp_tha=00:00:00:00:00:00
])
OVS_VSWITCHD_STOP
AT_CLEANUP
+AT_SETUP([ofproto-dpif - ofproto/trace-packet-out])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], 1, 2, 3)
+
+AT_DATA([flows.txt], [dnl
+in_port=1 actions=output:2
+in_port=2 actions=output:1
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+AT_CHECK([ovs-appctl ofproto/trace-packet-out br0 in_port=1 'mod_vlan_vid:123,resubmit(,0)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0], [dnl
+Datapath actions: push_vlan(vid=123,pcp=0),2
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+
m4_define([OFPROTO_TRACE],
[flow="$2"
AT_CHECK([ovs-appctl ofproto/trace $1 "$flow" $3], [0], [stdout])
' | sort]])
m4_define([STRIP_XOUT], [[sed '
s/used:[0-9]*\.[0-9]*/used:0.0/
- s/Datapath actions:.*/Datapath actions: <del>/
+ s/actions:.*/actions: <del>/
+ s/packets:[0-9]*/packets:0/
+ s/bytes:[0-9]*/bytes:0/
' | sort]])
AT_SETUP([ofproto-dpif megaflow - port classification])
AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
-AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
-skb_priority=0,ip,in_port=1,nw_frag=no, n_subfacets:2, used:0.0s, Datapath actions: <del>
+AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
+skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
])
OVS_VSWITCHD_STOP
AT_CLEANUP
AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
-AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
-skb_priority=0,ip,in_port=1,dl_src=50:54:00:00:00:09,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
-skb_priority=0,ip,in_port=1,dl_src=50:54:00:00:00:0b,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:0c/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
])
OVS_VSWITCHD_STOP
AT_CLEANUP
AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
-AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
-skb_priority=0,icmp,in_port=1,nw_src=10.0.0.2,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
-skb_priority=0,icmp,in_port=1,nw_src=10.0.0.4,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
+skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.2/255.255.255.255,dst=10.0.0.1/0.0.0.0,proto=1/0xff,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
+skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.4/255.255.255.255,dst=10.0.0.3/0.0.0.0,proto=1/0xff,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
])
OVS_VSWITCHD_STOP
AT_CLEANUP
AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
-AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
-skb_priority=0,icmp,in_port=1,nw_frag=no,icmp_type=8, n_subfacets:2, used:0.0s, Datapath actions: <del>
+AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
+skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0xff,tos=0/0,ttl=64/0,frag=no/0xff),icmp(type=8/0xff,code=0/0), packets:0, bytes:0, used:0.0s, actions: <del>
])
OVS_VSWITCHD_STOP
AT_CLEANUP
AT_CHECK([ovs-ofctl add-flow br0 action=normal])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
-AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
-skb_priority=0,ip,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
-skb_priority=0,ip,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:0b,dl_dst=50:54:00:00:00:0c,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
])
OVS_VSWITCHD_STOP
AT_CLEANUP
AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x8847),mpls(label=11,tc=3,ttl=64,bos=1)'])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0a),eth_type(0x8847),mpls(label=11,tc=3,ttl=64,bos=1)'])
-AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
-skb_priority=0,mpls,in_port=1,dl_src=50:54:00:00:00:09,mpls_label=11,mpls_tc=3,mpls_ttl=64,mpls_bos=1,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
-skb_priority=0,mpls,in_port=1,dl_src=50:54:00:00:00:0b,mpls_label=11,mpls_tc=3,mpls_ttl=64,mpls_bos=1,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x8847),mpls(label=11,tc=3,ttl=64,bos=1), packets:0, bytes:0, used:0.0s, actions: <del>
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x8847),mpls(label=11,tc=3,ttl=64,bos=1), packets:0, bytes:0, used:0.0s, actions: <del>
])
OVS_VSWITCHD_STOP
AT_CLEANUP
AT_CHECK([ovs-ofctl add-flow br0 action=normal])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
-AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
-skb_priority=0,icmp,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.0.0.2,nw_dst=10.0.0.1,nw_tos=0,nw_frag=no,icmp_type=8,icmp_code=0, n_subfacets:1, used:0.0s, Datapath actions: <del>
-skb_priority=0,icmp,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:0b,dl_dst=50:54:00:00:00:0c,nw_src=10.0.0.4,nw_dst=10.0.0.3,nw_tos=0,nw_frag=no,icmp_type=8,icmp_code=0, n_subfacets:1, used:0.0s, Datapath actions: <del>
+AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2/255.255.255.255,dst=10.0.0.1/255.255.255.255,proto=1/0xff,tos=0/0xfc,ttl=64/0,frag=no/0xff),icmp(type=8,code=0), packets:0, bytes:0, used:0.0s, actions: <del>
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4/255.255.255.255,dst=10.0.0.3/255.255.255.255,proto=1/0xff,tos=0/0xfc,ttl=64/0,frag=no/0xff),icmp(type=8,code=0), packets:0, bytes:0, used:0.0s, actions: <del>
])
OVS_VSWITCHD_STOP
AT_CLEANUP
AT_CHECK([ovs-ofctl add-flow br0 action=normal])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
-AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
-skb_priority=0,ip,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
-skb_priority=0,ip,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:0b,dl_dst=50:54:00:00:00:0c,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
])
OVS_VSWITCHD_STOP
AT_CLEANUP
AT_CHECK([ovs-ofctl add-flow br0 action=normal])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
-AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
-skb_priority=0,ip,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
-skb_priority=0,ip,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:0b,dl_dst=50:54:00:00:00:0c,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
])
OVS_VSWITCHD_STOP
AT_CLEANUP
AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
-AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
-skb_priority=0,icmp,in_port=7,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.0.0.2,nw_dst=10.0.0.1,nw_frag=no,icmp_type=8,icmp_code=0, n_subfacets:1, used:0.0s, Datapath actions: <del>
-skb_priority=0,icmp,in_port=7,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:0b,dl_dst=50:54:00:00:00:0c,nw_src=10.0.0.4,nw_dst=10.0.0.3,nw_frag=no,icmp_type=8,icmp_code=0, n_subfacets:1, used:0.0s, Datapath actions: <del>
+AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
+skb_priority(0),in_port(7),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2/255.255.255.255,dst=10.0.0.1/255.255.255.255,proto=1/0xff,tos=0/0,ttl=64/0,frag=no/0xff),icmp(type=8,code=0), packets:0, bytes:0, used:0.0s, actions: <del>
+skb_priority(0),in_port(7),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4/255.255.255.255,dst=10.0.0.3/255.255.255.255,proto=1/0xff,tos=0/0,ttl=64/0,frag=no/0xff),icmp(type=8,code=0), packets:0, bytes:0, used:0.0s, actions: <del>
])
OVS_VSWITCHD_STOP
AT_CLEANUP
AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
-AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
-skb_priority=0,ip,in_port=1,dl_src=50:54:00:00:00:09,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
-skb_priority=0,ip,in_port=1,dl_src=50:54:00:00:00:0b,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:0c/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
])
OVS_VSWITCHD_STOP
AT_CLEANUP
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=
1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
-AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
-skb_priority=0,ip,in_port=1,dl_src=50:54:00:00:00:09,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
-skb_priority=0,ip,in_port=1,dl_src=50:54:00:00:00:0b,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:0c/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
])
OVS_VSWITCHD_STOP
AT_CLEANUP
AT_CHECK([ovs-ofctl -O OpenFlow12 add-flows br0 flows.txt])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
-AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
-skb_priority=0,ip,in_port=1,dl_src=50:54:00:00:00:09,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
-skb_priority=0,ip,in_port=1,dl_src=50:54:00:00:00:0b,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:0c/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
])
OVS_VSWITCHD_STOP
AT_CLEANUP
AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
-AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
-skb_priority=0,ip,in_port=1,nw_frag=no, n_subfacets:2, used:0.0s, Datapath actions: <del>
+AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
+skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
])
OVS_VSWITCHD_STOP
AT_CLEANUP
AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x8100),vlan(vid=11,pcp=7),encap(eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0))'])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
-AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
-skb_priority=0,ip,in_port=1,dl_vlan=11,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
-skb_priority=0,ip,in_port=1,vlan_tci=0x0000/0x1fff,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
+skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
+skb_priority(0),in_port(1),eth_type(0x8100),vlan(vid=11/0xfff,pcp=7/0x0,cfi=1/1),encap(eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff)), packets:0, bytes:0, used:0.0s, actions: <del>
])
OVS_VSWITCHD_STOP
AT_CLEANUP
AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
-AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
-skb_priority=0,ip,in_port=1,nw_src=10.0.0.2,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
-skb_priority=0,ip,in_port=1,nw_src=10.0.0.4,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
+skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.2/255.255.255.255,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
+skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.4/255.255.255.255,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
])
OVS_VSWITCHD_STOP
AT_CLEANUP
AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
-AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
-skb_priority=0,ip,in_port=1,nw_src=10.0.0.2,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
-skb_priority=0,ip,in_port=1,nw_src=10.0.0.4,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
+skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.2/255.255.255.255,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
+skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.4/255.255.255.255,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
])
OVS_VSWITCHD_STOP
AT_CLEANUP
ovs-appctl time/warp 100
done
dnl The original flow is missing due to a revalidation.
-AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
-skb_priority=0,ip,in_port=1,vlan_tci=0x0000/0x0fff,dl_src=50:54:00:00:00:09,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
-skb_priority=0,ip,in_port=1,vlan_tci=0x0000/0x0fff,dl_src=50:54:00:00:00:0b,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b/ff:ff:ff:ff:ff:ff,dst=50:54:00:00:00:0c/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
])
OVS_VSWITCHD_STOP
AT_CLEANUP
AT_SETUP([ofproto-dpif megaflow - tunnels])
OVS_VSWITCHD_START(
- [add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 \
- ofport_request=1 -- \
- add-port br0 p2 -- set Interface p2 type=gre options:remote_ip=1.1.1.1 \
- ofport_request=2 options:key=flow -- \
- add-port br0 p3 -- set Interface p3 type=dummy ofport_request=3 \
- ofport_request=3 -- \
- add-port br0 p4 -- set Interface p4 type=gre options:remote_ip=1.1.1.2 \
- options:tos=inherit options:ttl=inherit ofport_request=4 options:key=flow])
+ [add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1])
+AT_CHECK([ovs-vsctl add-port br0 p2 -- set Interface p2 type=gre \
+ options:remote_ip=1.1.1.1 ofport_request=2 options:key=flow])
+AT_CHECK([ovs-vsctl add-port br0 p3 -- set Interface p3 type=dummy \
+ ofport_request=3])
+AT_CHECK([ovs-vsctl add-port br0 p4 -- set Interface p4 type=gre \
+ options:remote_ip=1.1.1.2 options:tos=inherit options:ttl=inherit \
+ ofport_request=4 options:key=flow])
AT_DATA([flows.txt], [dnl
in_port=1,actions=output(2)
in_port=3,actions=output(4)
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0x1,ttl=64,frag=no),icmp(type=8,code=0)'])
AT_CHECK([ovs-appctl netdev-dummy/receive p3 'in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0xfd,ttl=128,frag=no),icmp(type=8,code=0)'])
AT_CHECK([ovs-appctl netdev-dummy/receive p3 'in_port(3),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0x1,ttl=64,frag=no),icmp(type=8,code=0)'])
-AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
-skb_priority=0,ip,in_port=1,nw_ecn=1,nw_frag=no, n_subfacets:2, used:0.0s, Datapath actions: <del>
-skb_priority=0,ip,in_port=3,nw_tos=0,nw_ecn=1,nw_ttl=64,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
-skb_priority=0,ip,in_port=3,nw_tos=252,nw_ecn=1,nw_ttl=128,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
+AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
+skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0xfd/0x3,ttl=128/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
+skb_priority(0),in_port(3),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0xfd/0xff,ttl=128/0xff,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
+skb_priority(0),in_port(3),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0x1/0xff,ttl=64/0xff,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
])
OVS_VSWITCHD_STOP
AT_CLEANUP
AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
-AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl
-skb_priority=0,icmp,in_port=1,nw_src=10.0.0.2,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: <del>
-skb_priority=0,icmp,in_port=1,nw_src=10.0.0.4,nw_dst=10.0.0.3,nw_tos=0,nw_ecn=0,nw_ttl=64, n_subfacets:1, used:0.0s, Datapath actions: <del>
+AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_XOUT], [0], [dnl
+skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.2/255.255.255.255,dst=10.0.0.1/0.0.0.0,proto=1/0xff,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions: <del>
+skb_priority(0),in_port(1),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no), packets:0, bytes:0, used:0.0s, actions: <del>
])
OVS_VSWITCHD_STOP
AT_CLEANUP
dnl MAC, so both the source and destination MAC addresses are
dnl un-wildcarded, since the ODP commit functions update both the source
dnl and destination MAC addresses.
-AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_USED], [0], [dnl
-skb_priority=0,ip,in_port=1,dl_dst=50:54:00:00:00:0a,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: 2
-skb_priority=0,ip,in_port=1,dl_src=50:54:00:00:00:0b,dl_dst=50:54:00:00:00:0c,nw_frag=no, n_subfacets:1, used:0.0s, Datapath actions: set(eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0a)),2
+AT_CHECK([ovs-appctl dpif/dump-flows br0 | STRIP_USED], [0], [dnl
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/ff:ff:ff:ff:ff:ff),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions:2
+skb_priority(0),in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4/0.0.0.0,dst=10.0.0.3/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff), packets:0, bytes:0, used:0.0s, actions:set(eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0a)),2
])
OVS_VSWITCHD_STOP
AT_CLEANUP
assert(classifier_count(cls) == tcls->n_rules);
for (i = 0; i < confidence; i++) {
- struct cls_rule *cr0, *cr1;
+ struct cls_rule *cr0, *cr1, *cr2;
struct flow flow;
+ struct flow_wildcards wc;
unsigned int x;
+ flow_wildcards_init_catchall(&wc);
x = random_range(N_FLOW_VALUES);
memset(&flow, 0, sizeof flow);
flow.nw_src = nw_src_values[get_value(&x, N_NW_SRC_VALUES)];
flow.nw_proto = nw_proto_values[get_value(&x, N_NW_PROTO_VALUES)];
flow.nw_tos = nw_dscp_values[get_value(&x, N_NW_DSCP_VALUES)];
- cr0 = classifier_lookup(cls, &flow, NULL);
+ cr0 = classifier_lookup(cls, &flow, &wc);
cr1 = tcls_lookup(tcls, &flow);
assert((cr0 == NULL) == (cr1 == NULL));
if (cr0 != NULL) {
assert(cls_rule_equal(cr0, cr1));
assert(tr0->aux == tr1->aux);
}
+ cr2 = classifier_lookup(cls, &flow, NULL);
+ assert(cr2 == cr0);
}
}
struct classifier cls;
struct tcls tcls;
- classifier_init(&cls);
+ classifier_init(&cls, flow_segment_u32s);
ovs_rwlock_rdlock(&cls.rwlock);
tcls_init(&tcls);
assert(classifier_is_empty(&cls));
rule = make_rule(wc_fields,
hash_bytes(&wc_fields, sizeof wc_fields, 0), 0);
- classifier_init(&cls);
+ classifier_init(&cls, flow_segment_u32s);
ovs_rwlock_wrlock(&cls.rwlock);
tcls_init(&tcls);
rule2->aux += 5;
rule2->aux += 5;
- classifier_init(&cls);
+ classifier_init(&cls, flow_segment_u32s);
ovs_rwlock_wrlock(&cls.rwlock);
tcls_init(&tcls);
tcls_insert(&tcls, rule1);
pri_rules[i] = -1;
}
- classifier_init(&cls);
+ classifier_init(&cls, flow_segment_u32s);
ovs_rwlock_wrlock(&cls.rwlock);
tcls_init(&tcls);
value_mask = ~wcf & ((1u << CLS_N_FIELDS) - 1);
} while ((1 << count_ones(value_mask)) < N_RULES);
- classifier_init(&cls);
+ classifier_init(&cls, flow_segment_u32s);
ovs_rwlock_wrlock(&cls.rwlock);
tcls_init(&tcls);
}
shuffle(priorities, ARRAY_SIZE(priorities));
- classifier_init(&cls);
+ classifier_init(&cls, flow_segment_u32s);
ovs_rwlock_wrlock(&cls.rwlock);
tcls_init(&tcls);
}
}
+static void
+check_ctz64(uint64_t x, int n)
+{
+ if (ctz64(x) != n) {
+ fprintf(stderr, "ctz64(%"PRIu64") is %d but should be %d\n",
+ x, ctz64(x), n);
+ abort();
+ }
+}
+
static void
test_ctz(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
{
check_ctz((random_uint32() | 1) << n, n);
}
+
+ for (n = 0; n < 64; n++) {
+ /* Check minimum x such that f(x) == n. */
+ check_ctz64(UINT64_C(1) << n, n);
+
+ /* Check maximum x such that f(x) == n. */
+ check_ctz64(UINT64_MAX << n, n);
+
+ /* Check a random value in the middle. */
+ check_ctz64((random_uint64() | UINT64_C(1)) << n, n);
+ }
+
/* Check ctz(0). */
check_ctz(0, 32);
+ check_ctz64(0, 64);
}
/* Returns a random number in the range 'min'...'max' inclusive. */
}
static void
-shuffle(unsigned int *p, size_t n)
+shuffle(uint64_t *p, size_t n)
{
for (; n > 1; n--, p++) {
- unsigned int *q = &p[random_range(n)];
- unsigned int tmp = *p;
+ uint64_t *q = &p[random_range(n)];
+ uint64_t tmp = *p;
*p = *q;
*q = tmp;
}
}
static void
-check_popcount(uint32_t x, int n)
+check_count_1bits(uint64_t x, int n)
{
- if (popcount(x) != n) {
- fprintf(stderr, "popcount(%#"PRIx32") is %d but should be %d\n",
- x, popcount(x), n);
+ if (count_1bits(x) != n) {
+ fprintf(stderr, "count_1bits(%#"PRIx64") is %d but should be %d\n",
+ x, count_1bits(x), n);
abort();
}
}
static void
-test_popcount(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+test_count_1bits(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
{
- unsigned int bits[32];
+ uint64_t bits[64];
int i;
for (i = 0; i < ARRAY_SIZE(bits); i++) {
- bits[i] = 1u << i;
+ bits[i] = UINT64_C(1) << i;
}
- check_popcount(0, 0);
+ check_count_1bits(0, 0);
for (i = 0; i < 1000; i++) {
- uint32_t x = 0;
+ uint64_t x = 0;
int j;
shuffle(bits, ARRAY_SIZE(bits));
- for (j = 0; j < 32; j++) {
+ for (j = 0; j < 64; j++) {
x |= bits[j];
- check_popcount(x, j + 1);
+ check_count_1bits(x, j + 1);
}
- assert(x == UINT32_MAX);
+ assert(x == UINT64_MAX);
shuffle(bits, ARRAY_SIZE(bits));
- for (j = 31; j >= 0; j--) {
+ for (j = 63; j >= 0; j--) {
x &= ~bits[j];
- check_popcount(x, j);
+ check_count_1bits(x, j);
}
assert(x == 0);
}
{
ovs_assert(false);
}
+
+static void
+test_ovs_scan(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+ char str[16], str2[16], str3[16];
+ long double ld, ld2;
+ long long ll, ll2;
+ signed char c, c2;
+ ptrdiff_t pd, pd2;
+ intmax_t im, im2;
+ size_t sz, sz2;
+ int n, n2, n3;
+ double d, d2;
+ short s, s2;
+ float f, f2;
+ long l, l2;
+ int i, i2;
+
+ ovs_assert(ovs_scan("", ""));
+ ovs_assert(ovs_scan("", " "));
+ ovs_assert(ovs_scan(" ", " "));
+ ovs_assert(ovs_scan(" ", " "));
+ ovs_assert(ovs_scan(" \t ", " "));
+
+ ovs_assert(ovs_scan("xyzzy", "xyzzy"));
+ ovs_assert(ovs_scan("xy%zzy", "xy%%zzy"));
+ ovs_assert(!ovs_scan(" xy%zzy", "xy%%zzy"));
+ ovs_assert(ovs_scan(" xy%\tzzy", " xy%% zzy"));
+
+ ovs_assert(ovs_scan("123", "%d", &i));
+ ovs_assert(i == 123);
+ ovs_assert(ovs_scan("0", "%d", &i));
+ ovs_assert(i == 0);
+ ovs_assert(!ovs_scan("123", "%d%d", &i, &i2));
+ ovs_assert(ovs_scan("+123", "%d", &i));
+ ovs_assert(i == 123);
+ ovs_assert(ovs_scan("-123", "%d", &i));
+ ovs_assert(i == -123);
+ ovs_assert(ovs_scan("0123", "%d", &i));
+ ovs_assert(i == 123);
+ ovs_assert(ovs_scan(" 123", "%d", &i));
+ ovs_assert(i == 123);
+ ovs_assert(ovs_scan("0x123", "%d", &i));
+ ovs_assert(i == 0);
+ ovs_assert(ovs_scan("123", "%2d %d", &i, &i2));
+ ovs_assert(i == 12);
+ ovs_assert(i2 == 3);
+ ovs_assert(ovs_scan("+123", "%2d %d", &i, &i2));
+ ovs_assert(i == 1);
+ ovs_assert(i2 == 23);
+ ovs_assert(ovs_scan("-123", "%2d %d", &i, &i2));
+ ovs_assert(i == -1);
+ ovs_assert(i2 == 23);
+ ovs_assert(ovs_scan("0123", "%2d %d", &i, &i2));
+ ovs_assert(i == 1);
+ ovs_assert(i2 == 23);
+ ovs_assert(ovs_scan("123", "%*2d %d", &i));
+ ovs_assert(i == 3);
+ ovs_assert(ovs_scan("+123", "%2d %*d", &i));
+ ovs_assert(i == 1);
+ ovs_assert(i2 == 23);
+ ovs_assert(ovs_scan("-123", "%*2d %*d"));
+
+ ovs_assert(ovs_scan("123", "%u", &i));
+ ovs_assert(i == 123);
+ ovs_assert(ovs_scan("0", "%u", &i));
+ ovs_assert(i == 0);
+ ovs_assert(!ovs_scan("123", "%u%u", &i, &i2));
+ ovs_assert(ovs_scan("+123", "%u", &i));
+ ovs_assert(i == 123);
+ ovs_assert(ovs_scan("-123", "%u", &i));
+ ovs_assert(i == -123);
+ ovs_assert(ovs_scan("0123", "%u", &i));
+ ovs_assert(i == 123);
+ ovs_assert(ovs_scan(" 123", "%u", &i));
+ ovs_assert(i == 123);
+ ovs_assert(ovs_scan("0x123", "%u", &i));
+ ovs_assert(i == 0);
+ ovs_assert(ovs_scan("123", "%2u %u", &i, &i2));
+ ovs_assert(i == 12);
+ ovs_assert(i2 == 3);
+ ovs_assert(ovs_scan("+123", "%2u %u", &i, &i2));
+ ovs_assert(i == 1);
+ ovs_assert(i2 == 23);
+ ovs_assert(ovs_scan("-123", "%2u %u", &i, &i2));
+ ovs_assert(i == -1);
+ ovs_assert(i2 == 23);
+ ovs_assert(ovs_scan("0123", "%2u %u", &i, &i2));
+ ovs_assert(i == 1);
+ ovs_assert(i2 == 23);
+ ovs_assert(ovs_scan("123", "%*2u %u", &i));
+ ovs_assert(i == 3);
+ ovs_assert(ovs_scan("+123", "%2u %*u", &i));
+ ovs_assert(i == 1);
+ ovs_assert(i2 == 23);
+ ovs_assert(ovs_scan("-123", "%*2u %*u"));
+
+ ovs_assert(ovs_scan("123", "%i", &i));
+ ovs_assert(i == 123);
+ ovs_assert(ovs_scan("0", "%i", &i));
+ ovs_assert(i == 0);
+ ovs_assert(!ovs_scan("123", "%i%i", &i, &i2));
+ ovs_assert(ovs_scan("+123", "%i", &i));
+ ovs_assert(i == 123);
+ ovs_assert(ovs_scan("-123", "%i", &i));
+ ovs_assert(i == -123);
+ ovs_assert(ovs_scan("0123", "%i", &i));
+ ovs_assert(i == 0123);
+ ovs_assert(ovs_scan(" 123", "%i", &i));
+ ovs_assert(i == 123);
+ ovs_assert(ovs_scan("0x123", "%i", &i));
+ ovs_assert(i == 0x123);
+ ovs_assert(ovs_scan("123", "%2i %i", &i, &i2));
+ ovs_assert(i == 12);
+ ovs_assert(i2 == 3);
+ ovs_assert(ovs_scan("+123", "%2i %i", &i, &i2));
+ ovs_assert(i == 1);
+ ovs_assert(i2 == 23);
+ ovs_assert(ovs_scan("-123", "%2i %i", &i, &i2));
+ ovs_assert(i == -1);
+ ovs_assert(i2 == 23);
+ ovs_assert(ovs_scan("0123", "%2i %i", &i, &i2));
+ ovs_assert(i == 1);
+ ovs_assert(i2 == 23);
+ ovs_assert(ovs_scan("123", "%*2i %i", &i));
+ ovs_assert(i == 3);
+ ovs_assert(ovs_scan("+123", "%2i %*i", &i));
+ ovs_assert(i == 1);
+ ovs_assert(i2 == 23);
+ ovs_assert(ovs_scan("-123", "%*2i %*i"));
+
+ ovs_assert(ovs_scan("123", "%o", &i));
+ ovs_assert(i == 0123);
+ ovs_assert(ovs_scan("0", "%o", &i));
+ ovs_assert(i == 0);
+ ovs_assert(!ovs_scan("123", "%o%o", &i, &i2));
+ ovs_assert(ovs_scan("+123", "%o", &i));
+ ovs_assert(i == 0123);
+ ovs_assert(ovs_scan("-123", "%o", &i));
+ ovs_assert(i == -0123);
+ ovs_assert(ovs_scan("0123", "%o", &i));
+ ovs_assert(i == 0123);
+ ovs_assert(ovs_scan(" 123", "%o", &i));
+ ovs_assert(i == 0123);
+ ovs_assert(ovs_scan("0x123", "%o", &i));
+ ovs_assert(i == 0);
+ ovs_assert(ovs_scan("123", "%2o %o", &i, &i2));
+ ovs_assert(i == 012);
+ ovs_assert(i2 == 3);
+ ovs_assert(ovs_scan("+123", "%2o %o", &i, &i2));
+ ovs_assert(i == 1);
+ ovs_assert(i2 == 023);
+ ovs_assert(ovs_scan("-123", "%2o %o", &i, &i2));
+ ovs_assert(i == -1);
+ ovs_assert(i2 == 023);
+ ovs_assert(ovs_scan("0123", "%2o %o", &i, &i2));
+ ovs_assert(i == 1);
+ ovs_assert(i2 == 023);
+ ovs_assert(ovs_scan("123", "%*2o %o", &i));
+ ovs_assert(i == 3);
+ ovs_assert(ovs_scan("+123", "%2o %*o", &i));
+ ovs_assert(i == 1);
+ ovs_assert(i2 == 023);
+ ovs_assert(ovs_scan("-123", "%*2o %*o"));
+
+ ovs_assert(ovs_scan("123", "%x", &i));
+ ovs_assert(i == 0x123);
+ ovs_assert(ovs_scan("0", "%x", &i));
+ ovs_assert(i == 0);
+ ovs_assert(!ovs_scan("123", "%x%x", &i, &i2));
+ ovs_assert(ovs_scan("+123", "%x", &i));
+ ovs_assert(i == 0x123);
+ ovs_assert(ovs_scan("-123", "%x", &i));
+ ovs_assert(i == -0x123);
+ ovs_assert(ovs_scan("0123", "%x", &i));
+ ovs_assert(i == 0x123);
+ ovs_assert(ovs_scan(" 123", "%x", &i));
+ ovs_assert(i == 0x123);
+ ovs_assert(ovs_scan("0x123", "%x", &i));
+ ovs_assert(i == 0x123);
+ ovs_assert(ovs_scan("123", "%2x %x", &i, &i2));
+ ovs_assert(i == 0x12);
+ ovs_assert(i2 == 3);
+ ovs_assert(ovs_scan("+123", "%2x %x", &i, &i2));
+ ovs_assert(i == 1);
+ ovs_assert(i2 == 0x23);
+ ovs_assert(ovs_scan("-123", "%2x %x", &i, &i2));
+ ovs_assert(i == -1);
+ ovs_assert(i2 == 0x23);
+ ovs_assert(ovs_scan("0123", "%2x %x", &i, &i2));
+ ovs_assert(i == 1);
+ ovs_assert(i2 == 0x23);
+ ovs_assert(ovs_scan("123", "%*2x %x", &i));
+ ovs_assert(i == 3);
+ ovs_assert(ovs_scan("+123", "%2x %*x", &i));
+ ovs_assert(i == 1);
+ ovs_assert(i2 == 0x23);
+ ovs_assert(ovs_scan("-123", "%*2x %*x"));
+
+ ovs_assert(ovs_scan("123", "%hd", &s));
+ ovs_assert(s == 123);
+ ovs_assert(!ovs_scan("123", "%hd%hd", &s, &s2));
+ ovs_assert(ovs_scan("+123", "%hd", &s));
+ ovs_assert(s == 123);
+ ovs_assert(ovs_scan("-123", "%hd", &s));
+ ovs_assert(s == -123);
+ ovs_assert(ovs_scan("0123", "%hd", &s));
+ ovs_assert(s == 123);
+ ovs_assert(ovs_scan(" 123", "%hd", &s));
+ ovs_assert(s == 123);
+ ovs_assert(ovs_scan("0x123", "%hd", &s));
+ ovs_assert(s == 0);
+ ovs_assert(ovs_scan("123", "%2hd %hd", &s, &s2));
+ ovs_assert(s == 12);
+ ovs_assert(s2 == 3);
+ ovs_assert(ovs_scan("+123", "%2hd %hd", &s, &s2));
+ ovs_assert(s == 1);
+ ovs_assert(s2 == 23);
+ ovs_assert(ovs_scan("-123", "%2hd %hd", &s, &s2));
+ ovs_assert(s == -1);
+ ovs_assert(s2 == 23);
+ ovs_assert(ovs_scan("0123", "%2hd %hd", &s, &s2));
+ ovs_assert(s == 1);
+ ovs_assert(s2 == 23);
+
+ ovs_assert(ovs_scan("123", "%hhd", &c));
+ ovs_assert(c == 123);
+ ovs_assert(ovs_scan("0", "%hhd", &c));
+ ovs_assert(c == 0);
+ ovs_assert(!ovs_scan("123", "%hhd%hhd", &c, &c2));
+ ovs_assert(ovs_scan("+123", "%hhd", &c));
+ ovs_assert(c == 123);
+ ovs_assert(ovs_scan("-123", "%hhd", &c));
+ ovs_assert(c == -123);
+ ovs_assert(ovs_scan("0123", "%hhd", &c));
+ ovs_assert(c == 123);
+ ovs_assert(ovs_scan(" 123", "%hhd", &c));
+ ovs_assert(c == 123);
+ ovs_assert(ovs_scan("0x123", "%hhd", &c));
+ ovs_assert(c == 0);
+ ovs_assert(ovs_scan("123", "%2hhd %hhd", &c, &c2));
+ ovs_assert(c == 12);
+ ovs_assert(c2 == 3);
+ ovs_assert(ovs_scan("+123", "%2hhd %hhd", &c, &c2));
+ ovs_assert(c == 1);
+ ovs_assert(c2 == 23);
+ ovs_assert(ovs_scan("-123", "%2hhd %hhd", &c, &c2));
+ ovs_assert(c == -1);
+ ovs_assert(c2 == 23);
+ ovs_assert(ovs_scan("0123", "%2hhd %hhd", &c, &c2));
+ ovs_assert(c == 1);
+ ovs_assert(c2 == 23);
+
+ ovs_assert(ovs_scan("123", "%ld", &l));
+ ovs_assert(l == 123);
+ ovs_assert(ovs_scan("0", "%ld", &l));
+ ovs_assert(l == 0);
+ ovs_assert(!ovs_scan("123", "%ld%ld", &l, &l2));
+ ovs_assert(ovs_scan("+123", "%ld", &l));
+ ovs_assert(l == 123);
+ ovs_assert(ovs_scan("-123", "%ld", &l));
+ ovs_assert(l == -123);
+ ovs_assert(ovs_scan("0123", "%ld", &l));
+ ovs_assert(l == 123);
+ ovs_assert(ovs_scan(" 123", "%ld", &l));
+ ovs_assert(l == 123);
+ ovs_assert(ovs_scan("0x123", "%ld", &l));
+ ovs_assert(l == 0);
+ ovs_assert(ovs_scan("123", "%2ld %ld", &l, &l2));
+ ovs_assert(l == 12);
+ ovs_assert(l2 == 3);
+ ovs_assert(ovs_scan("+123", "%2ld %ld", &l, &l2));
+ ovs_assert(l == 1);
+ ovs_assert(l2 == 23);
+ ovs_assert(ovs_scan("-123", "%2ld %ld", &l, &l2));
+ ovs_assert(l == -1);
+ ovs_assert(l2 == 23);
+ ovs_assert(ovs_scan("0123", "%2ld %ld", &l, &l2));
+ ovs_assert(l == 1);
+ ovs_assert(l2 == 23);
+
+ ovs_assert(ovs_scan("123", "%lld", &ll));
+ ovs_assert(ll == 123);
+ ovs_assert(ovs_scan("0", "%lld", &ll));
+ ovs_assert(ll == 0);
+ ovs_assert(!ovs_scan("123", "%lld%lld", &ll, &ll2));
+ ovs_assert(ovs_scan("+123", "%lld", &ll));
+ ovs_assert(ll == 123);
+ ovs_assert(ovs_scan("-123", "%lld", &ll));
+ ovs_assert(ll == -123);
+ ovs_assert(ovs_scan("0123", "%lld", &ll));
+ ovs_assert(ll == 123);
+ ovs_assert(ovs_scan(" 123", "%lld", &ll));
+ ovs_assert(ll == 123);
+ ovs_assert(ovs_scan("0x123", "%lld", &ll));
+ ovs_assert(ll == 0);
+ ovs_assert(ovs_scan("123", "%2lld %lld", &ll, &ll2));
+ ovs_assert(ll == 12);
+ ovs_assert(ll2 == 3);
+ ovs_assert(ovs_scan("+123", "%2lld %lld", &ll, &ll2));
+ ovs_assert(ll == 1);
+ ovs_assert(ll2 == 23);
+ ovs_assert(ovs_scan("-123", "%2lld %lld", &ll, &ll2));
+ ovs_assert(ll == -1);
+ ovs_assert(ll2 == 23);
+ ovs_assert(ovs_scan("0123", "%2lld %lld", &ll, &ll2));
+ ovs_assert(ll == 1);
+ ovs_assert(ll2 == 23);
+
+ ovs_assert(ovs_scan("123", "%jd", &im));
+ ovs_assert(im == 123);
+ ovs_assert(ovs_scan("0", "%jd", &im));
+ ovs_assert(im == 0);
+ ovs_assert(!ovs_scan("123", "%jd%jd", &im, &im2));
+ ovs_assert(ovs_scan("+123", "%jd", &im));
+ ovs_assert(im == 123);
+ ovs_assert(ovs_scan("-123", "%jd", &im));
+ ovs_assert(im == -123);
+ ovs_assert(ovs_scan("0123", "%jd", &im));
+ ovs_assert(im == 123);
+ ovs_assert(ovs_scan(" 123", "%jd", &im));
+ ovs_assert(im == 123);
+ ovs_assert(ovs_scan("0x123", "%jd", &im));
+ ovs_assert(im == 0);
+ ovs_assert(ovs_scan("123", "%2jd %jd", &im, &im2));
+ ovs_assert(im == 12);
+ ovs_assert(im2 == 3);
+ ovs_assert(ovs_scan("+123", "%2jd %jd", &im, &im2));
+ ovs_assert(im == 1);
+ ovs_assert(im2 == 23);
+ ovs_assert(ovs_scan("-123", "%2jd %jd", &im, &im2));
+ ovs_assert(im == -1);
+ ovs_assert(im2 == 23);
+ ovs_assert(ovs_scan("0123", "%2jd %jd", &im, &im2));
+ ovs_assert(im == 1);
+ ovs_assert(im2 == 23);
+
+ ovs_assert(ovs_scan("123", "%td", &pd));
+ ovs_assert(pd == 123);
+ ovs_assert(ovs_scan("0", "%td", &pd));
+ ovs_assert(pd == 0);
+ ovs_assert(!ovs_scan("123", "%td%td", &pd, &pd2));
+ ovs_assert(ovs_scan("+123", "%td", &pd));
+ ovs_assert(pd == 123);
+ ovs_assert(ovs_scan("-123", "%td", &pd));
+ ovs_assert(pd == -123);
+ ovs_assert(ovs_scan("0123", "%td", &pd));
+ ovs_assert(pd == 123);
+ ovs_assert(ovs_scan(" 123", "%td", &pd));
+ ovs_assert(pd == 123);
+ ovs_assert(ovs_scan("0x123", "%td", &pd));
+ ovs_assert(pd == 0);
+ ovs_assert(ovs_scan("123", "%2td %td", &pd, &pd2));
+ ovs_assert(pd == 12);
+ ovs_assert(pd2 == 3);
+ ovs_assert(ovs_scan("+123", "%2td %td", &pd, &pd2));
+ ovs_assert(pd == 1);
+ ovs_assert(pd2 == 23);
+ ovs_assert(ovs_scan("-123", "%2td %td", &pd, &pd2));
+ ovs_assert(pd == -1);
+ ovs_assert(pd2 == 23);
+ ovs_assert(ovs_scan("0123", "%2td %td", &pd, &pd2));
+ ovs_assert(pd == 1);
+ ovs_assert(pd2 == 23);
+
+ ovs_assert(ovs_scan("123", "%zd", &sz));
+ ovs_assert(sz == 123);
+ ovs_assert(ovs_scan("0", "%zd", &sz));
+ ovs_assert(sz == 0);
+ ovs_assert(!ovs_scan("123", "%zd%zd", &sz, &sz2));
+ ovs_assert(ovs_scan("+123", "%zd", &sz));
+ ovs_assert(sz == 123);
+ ovs_assert(ovs_scan("-123", "%zd", &sz));
+ ovs_assert(sz == -123);
+ ovs_assert(ovs_scan("0123", "%zd", &sz));
+ ovs_assert(sz == 123);
+ ovs_assert(ovs_scan(" 123", "%zd", &sz));
+ ovs_assert(sz == 123);
+ ovs_assert(ovs_scan("0x123", "%zd", &sz));
+ ovs_assert(sz == 0);
+ ovs_assert(ovs_scan("123", "%2zd %zd", &sz, &sz2));
+ ovs_assert(sz == 12);
+ ovs_assert(sz2 == 3);
+ ovs_assert(ovs_scan("+123", "%2zd %zd", &sz, &sz2));
+ ovs_assert(sz == 1);
+ ovs_assert(sz2 == 23);
+ ovs_assert(ovs_scan("-123", "%2zd %zd", &sz, &sz2));
+ ovs_assert(sz == -1);
+ ovs_assert(sz2 == 23);
+ ovs_assert(ovs_scan("0123", "%2zd %zd", &sz, &sz2));
+ ovs_assert(sz == 1);
+ ovs_assert(sz2 == 23);
+
+ ovs_assert(ovs_scan("0.25", "%f", &f));
+ ovs_assert(f == 0.25);
+ ovs_assert(ovs_scan("1.0", "%f", &f));
+ ovs_assert(f == 1.0);
+ ovs_assert(ovs_scan("-5", "%f", &f));
+ ovs_assert(f == -5.0);
+ ovs_assert(ovs_scan("+6", "%f", &f));
+ ovs_assert(f == 6.0);
+ ovs_assert(ovs_scan("-1e5", "%f", &f));
+ ovs_assert(f == -1e5);
+ ovs_assert(ovs_scan("-.25", "%f", &f));
+ ovs_assert(f == -.25);
+ ovs_assert(ovs_scan("+123.e1", "%f", &f));
+ ovs_assert(f == 1230.0);
+ ovs_assert(ovs_scan("25e-2", "%f", &f));
+ ovs_assert(f == 0.25);
+ ovs_assert(ovs_scan("0.25", "%1f %f", &f, &f2));
+ ovs_assert(f == 0);
+ ovs_assert(f2 == 0.25);
+ ovs_assert(ovs_scan("1.0", "%2f %f", &f, &f2));
+ ovs_assert(f == 1.0);
+ ovs_assert(f2 == 0.0);
+ ovs_assert(!ovs_scan("-5", "%1f", &f));
+ ovs_assert(!ovs_scan("+6", "%1f", &f));
+ ovs_assert(!ovs_scan("-1e5", "%2f %*f", &f));
+ ovs_assert(f == -1);
+ ovs_assert(!ovs_scan("-.25", "%2f", &f));
+ ovs_assert(!ovs_scan("+123.e1", "%6f", &f));
+ ovs_assert(!ovs_scan("25e-2", "%4f", &f));
+
+ ovs_assert(ovs_scan("0.25", "%lf", &d));
+ ovs_assert(d == 0.25);
+ ovs_assert(ovs_scan("1.0", "%lf", &d));
+ ovs_assert(d == 1.0);
+ ovs_assert(ovs_scan("-5", "%lf", &d));
+ ovs_assert(d == -5.0);
+ ovs_assert(ovs_scan("+6", "%lf", &d));
+ ovs_assert(d == 6.0);
+ ovs_assert(ovs_scan("-1e5", "%lf", &d));
+ ovs_assert(d == -1e5);
+ ovs_assert(ovs_scan("-.25", "%lf", &d));
+ ovs_assert(d == -.25);
+ ovs_assert(ovs_scan("+123.e1", "%lf", &d));
+ ovs_assert(d == 1230.0);
+ ovs_assert(ovs_scan("25e-2", "%lf", &d));
+ ovs_assert(d == 0.25);
+ ovs_assert(ovs_scan("0.25", "%1lf %lf", &d, &d2));
+ ovs_assert(d == 0);
+ ovs_assert(d2 == 0.25);
+ ovs_assert(ovs_scan("1.0", "%2lf %lf", &d, &d2));
+ ovs_assert(d == 1.0);
+ ovs_assert(d2 == 0.0);
+ ovs_assert(!ovs_scan("-5", "%1lf", &d));
+ ovs_assert(!ovs_scan("+6", "%1lf", &d));
+ ovs_assert(!ovs_scan("-1e5", "%2lf %*f", &d));
+ ovs_assert(d == -1);
+ ovs_assert(!ovs_scan("-.25", "%2lf", &d));
+ ovs_assert(!ovs_scan("+123.e1", "%6lf", &d));
+ ovs_assert(!ovs_scan("25e-2", "%4lf", &d));
+
+ ovs_assert(ovs_scan("0.25", "%Lf", &ld));
+ ovs_assert(ld == 0.25);
+ ovs_assert(ovs_scan("1.0", "%Lf", &ld));
+ ovs_assert(ld == 1.0);
+ ovs_assert(ovs_scan("-5", "%Lf", &ld));
+ ovs_assert(ld == -5.0);
+ ovs_assert(ovs_scan("+6", "%Lf", &ld));
+ ovs_assert(ld == 6.0);
+ ovs_assert(ovs_scan("-1e5", "%Lf", &ld));
+ ovs_assert(ld == -1e5);
+ ovs_assert(ovs_scan("-.25", "%Lf", &ld));
+ ovs_assert(ld == -.25);
+ ovs_assert(ovs_scan("+123.e1", "%Lf", &ld));
+ ovs_assert(ld == 1230.0);
+ ovs_assert(ovs_scan("25e-2", "%Lf", &ld));
+ ovs_assert(ld == 0.25);
+ ovs_assert(ovs_scan("0.25", "%1Lf %Lf", &ld, &ld2));
+ ovs_assert(ld == 0);
+ ovs_assert(ld2 == 0.25);
+ ovs_assert(ovs_scan("1.0", "%2Lf %Lf", &ld, &ld2));
+ ovs_assert(ld == 1.0);
+ ovs_assert(ld2 == 0.0);
+ ovs_assert(!ovs_scan("-5", "%1Lf", &ld));
+ ovs_assert(!ovs_scan("+6", "%1Lf", &ld));
+ ovs_assert(!ovs_scan("-1e5", "%2Lf %*f", &ld));
+ ovs_assert(ld == -1);
+ ovs_assert(!ovs_scan("-.25", "%2Lf", &ld));
+ ovs_assert(!ovs_scan("+123.e1", "%6Lf", &ld));
+ ovs_assert(!ovs_scan("25e-2", "%4Lf", &ld));
+
+ ovs_assert(ovs_scan(" Hello,\tworld ", "%*s%n%*s%n", &n, &n2));
+ ovs_assert(n == 7);
+ ovs_assert(n2 == 13);
+ ovs_assert(!ovs_scan(" Hello,\tworld ", "%*s%*s%*s"));
+ ovs_assert(ovs_scan(" Hello,\tworld ", "%6s%n%5s%n", str, &n, str2, &n2));
+ ovs_assert(!strcmp(str, "Hello,"));
+ ovs_assert(n == 7);
+ ovs_assert(!strcmp(str2, "world"));
+ ovs_assert(n2 == 13);
+ ovs_assert(ovs_scan(" Hello,\tworld ", "%5s%5s%5s", str, str2, str3));
+ ovs_assert(!strcmp(str, "Hello"));
+ ovs_assert(!strcmp(str2, ","));
+ ovs_assert(!strcmp(str3, "world"));
+ ovs_assert(!ovs_scan(" ", "%*s"));
+
+ ovs_assert(ovs_scan(" Hello,\tworld ", "%*c%n%*c%n%c%n",
+ &n, &n2, &c, &n3));
+ ovs_assert(n == 1);
+ ovs_assert(n2 == 2);
+ ovs_assert(c == 'e');
+ ovs_assert(n3 == 3);
+ ovs_assert(ovs_scan(" Hello,\tworld ", "%*5c%5c", str));
+ ovs_assert(!memcmp(str, "o,\two", 5));
+ ovs_assert(!ovs_scan(" Hello,\tworld ", "%*15c"));
+
+ ovs_assert(ovs_scan("0x1234xyzzy", "%9[x0-9a-fA-F]%n", str, &n));
+ ovs_assert(!strcmp(str, "0x1234x"));
+ ovs_assert(n == 7);
+ ovs_assert(ovs_scan("foo:bar=baz", "%5[^:=]%n:%5[^:=]%n=%5[^:=]%n",
+ str, &n, str2, &n2, str3, &n3));
+ ovs_assert(!strcmp(str, "foo"));
+ ovs_assert(n == 3);
+ ovs_assert(!strcmp(str2, "bar"));
+ ovs_assert(n2 == 7);
+ ovs_assert(!strcmp(str3, "baz"));
+ ovs_assert(n3 == 11);
+ ovs_assert(!ovs_scan(" ", "%*[0-9]"));
+ ovs_assert(ovs_scan("0x123a]4xyzzy-", "%[]x0-9a-fA-F]", str));
+ ovs_assert(!strcmp(str, "0x123a]4x"));
+ ovs_assert(ovs_scan("abc]xyz","%[^]xyz]", str));
+ ovs_assert(!strcmp(str, "abc"));
+ ovs_assert(!ovs_scan("0x123a]4xyzzy-", "%[x0-9]a-fA-F]", str));
+ ovs_assert(ovs_scan("0x12-3]xyz", "%[x0-9a-f-]", str));
+ ovs_assert(!strcmp(str, "0x12-3"));
+ ovs_assert(ovs_scan("0x12-3]xyz", "%[^a-f-]", str));
+ ovs_assert(!strcmp(str, "0x12"));
+ ovs_assert(sscanf("0x12-3]xyz", "%[^-a-f]", str));
+ ovs_assert(!strcmp(str, "0x12"));
+}
\f
static const struct command commands[] = {
{"ctz", 0, 0, test_ctz},
{"round_up_pow2", 0, 0, test_round_up_pow2},
{"round_down_pow2", 0, 0, test_round_down_pow2},
- {"popcount", 0, 0, test_popcount},
+ {"count_1bits", 0, 0, test_count_1bits},
{"log_2_floor", 0, 0, test_log_2_floor},
{"bitwise_copy", 0, 0, test_bitwise_copy},
{"bitwise_zero", 0, 0, test_bitwise_zero},
{"bitwise_is_all_zeros", 0, 0, test_bitwise_is_all_zeros},
{"follow-symlinks", 1, INT_MAX, test_follow_symlinks},
{"assert", 0, 0, test_assert},
+ {"ovs_scan", 0, 0, test_ovs_scan},
{NULL, 0, 0, NULL},
};
utilities/ovs-save
scripts_DATA += utilities/ovs-lib
+utilities/ovs-lib: $(top_builddir)/config.status
+
EXTRA_DIST += \
utilities/ovs-check-dead-ifs.in \
utilities/ovs-ctl.in \
file_output(CAP_NETWORK_CONFIG, [SYSCONFIG_NETWORK, RESOLV_CONF, NSSWITCH_CONF, HOSTS])
file_output(CAP_NETWORK_CONFIG, [NTP_CONF, IPTABLES_CONFIG, HOSTS_ALLOW, HOSTS_DENY])
file_output(CAP_NETWORK_CONFIG, [OPENVSWITCH_DEFAULT_SWITCH,
- OPENVSWITCH_SYSCONFIG_SWITCH, OPENVSWITCH_DEFAULT_CONTROLLER])
+ OPENVSWITCH_SYSCONFIG_SWITCH])
cmd_output(CAP_NETWORK_INFO, [IFCONFIG, '-a'])
cmd_output(CAP_NETWORK_INFO, [ROUTE, '-n'])
*min = *max = 0;
if (colon && colon[1] != '\0') {
const char *ports = colon + 1;
- if (sscanf(ports, "%hu-%hu", min, max) == 2) {
+ if (ovs_scan(ports, "%hu-%hu", min, max)) {
if (*min > *max) {
ovs_fatal(0, "%s: minimum is greater than maximum", s_);
}
- } else if (sscanf(ports, "%hu", min) == 1) {
+ } else if (ovs_scan(ports, "%hu", min)) {
*max = *min;
} else {
ovs_fatal(0, "%s: number or range expected", s_);
# First install the basic requirements needed to build Open vSwitch.
sudo apt-get install git build-essential libtool autoconf pkg-config \\
- libssl-dev pkg-config gdb linux-headers-`uname -r`
+ libssl-dev gdb linux-headers-`uname -r`
# Next clone the Open vSwitch source.
git clone git://git.openvswitch.org/openvswitch %(ovs)s
for (i = 3; i < argc; i++) {
char name[16];
int number;
- int n = -1;
- if (sscanf(argv[i], "%15[^=]=%d%n", name, &number, &n) > 0 && n > 0) {
+ if (ovs_scan(argv[i], "%15[^=]=%d", name, &number)) {
uintptr_t n = number;
simap_put(&port_names, name, n);
} else {
When a switch has more than one controller configured, only the
traffic to and from a single controller is output. If none of the
controllers is configured as a master or a slave (using a Nicira
-extension to OpenFlow), then a controller is chosen arbitrarily among
+extension to OpenFlow 1.0 or 1.1, or a standard request in OpenFlow
+1.2 or later), then a controller is chosen arbitrarily among
them. If there is a master controller, it is chosen; otherwise, if
there are any controllers that are not masters or slaves, one is
chosen arbitrarily; otherwise, a slave controller is chosen
\fBnx\-match\fR mode. See the description of the \fBset\-frags\fR
command, above, for more details.
.
+.IP \fBarp_spa=\fIip\fR[\fB/\fInetmask\fR]
+.IQ \fBarp_tpa=\fIip\fR[\fB/\fInetmask\fR]
+When \fBdl_type\fR specifies either ARP or RARP, \fBarp_spa\fR and
+\fBarp_tha\fR match the source and target IPv4 address, respectively.
+An address may be specified as an IP address or host name
+(e.g. \fB192.168.1.1\fR or \fBwww.example.com\fR). The optional
+\fInetmask\fR allows restricting a match to an IPv4 address prefix.
+The netmask may be specified as a dotted quad
+(e.g. \fB192.168.1.0/255.255.255.0\fR) or as a CIDR block
+(e.g. \fB192.168.1.0/24\fR).
+.
.IP \fBarp_sha=\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fR
.IQ \fBarp_tha=\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fR
When \fBdl_type\fR specifies either ARP or RARP, \fBarp_sha\fR and
\fBarp_tha\fR match the source and target hardware address, respectively. An
-address is specified as 6 pairs of hexadecimal digits delimited by colons.
+address is specified as 6 pairs of hexadecimal digits delimited by colons
+(e.g. \fB00:0A:E4:25:6B:B0\fR).
+.
+.IP \fBarp_sha=\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB/\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fR
+.IQ \fBarp_tha=\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB/\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fB:\fIxx\fR
+When \fBdl_type\fR specifies either ARP or RARP, \fBarp_sha\fR and
+\fBarp_tha\fR match the source and target hardware address, respectively. An
+address is specified as 6 pairs of hexadecimal digits delimited by colons
+(e.g. \fB00:0A:E4:25:6B:B0\fR), with a wildcard mask following the slash.
.
+
.IP \fBipv6_src=\fIipv6\fR[\fB/\fInetmask\fR]
.IQ \fBipv6_dst=\fIipv6\fR[\fB/\fInetmask\fR]
When \fBdl_type\fR is 0x86dd (possibly via shorthand, e.g., \fBipv6\fR
address option. An address is specified as 6 pairs of hexadecimal
digits delimited by colons.
.
+.IP \fBmpls_bos=\fIbos\fR
+When \fBdl_type\fR is 0x8847 or 0x8848 (possibly via shorthand e.g.,
+\fBmpls\fR or \fBmplsm\fR), matches the bottom-of-stack bit of the
+outer-most MPLS label stack entry. Valid values are 0 and 1.
+.IP
+If 1 then for a packet with a well-formed MPLS label stack the
+bottom-of-stack bit indicates that the outer label stack entry is also
+the inner-most label stack entry and thus that is that there is only one
+label stack entry present. Conversely, if 0 then for a packet with a
+well-formed MPLS label stack the bottom-of-stack bit indicates that the
+outer label stack entry is not the inner-most label stack entry and
+thus there is more than one label stack entry present.
+.
+.IP \fBmpls_label=\fIlabel\fR
+When \fBdl_type\fR is 0x8847 or 0x8848 (possibly via shorthand e.g.,
+\fBmpls\fR or \fBmplsm\fR), matches the label of the outer
+MPLS label stack entry. The label is a 20-bit value that is decimal by default;
+use a \fB0x\fR prefix to specify them in hexadecimal.
+.
+.IP \fBmpls_tc=\fItc\fR
+When \fBdl_type\fR is 0x8847 or 0x8848 (possibly via shorthand e.g.,
+\fBmpls\fR or \fBmplsm\fR), matches the traffic-class of the outer
+MPLS label stack entry. Valid values are between 0 (lowest) and 7 (highest).
+.
.IP \fBtun_id=\fItunnel-id\fR[\fB/\fImask\fR]
.IQ \fBtunnel_id=\fItunnel-id\fR[\fB/\fImask\fR]
Matches tunnel identifier \fItunnel-id\fR. Only packets that arrive
The \fBadd\-flow\fR, \fBadd\-flows\fR, and \fBmod\-flows\fR commands
require an additional field, which must be the final field specified:
.
-.IP \fBactions=\fR[\fItarget\fR][\fB,\fItarget\fR...]\fR
+.IP \fBactions=\fR[\fIaction\fR][\fB,\fIaction\fR...]\fR
Specifies a comma-separated list of actions to take on a packet when the
-flow entry matches. If no \fItarget\fR is specified, then packets
-matching the flow are dropped. The \fItarget\fR may be an OpenFlow port
-number designating the physical port on which to output the packet, or one
-of the following keywords:
+flow entry matches. If no \fIaction\fR is specified, then packets
+matching the flow are dropped. The following forms of \fIaction\fR
+are supported:
.
.RS
-.IP \fBoutput:\fIport\fR
-Outputs the packet to \fIport\fR, which must be an OpenFlow port
-number or keyword (e.g. \fBLOCAL\fR).
+.IP \fIport\fR
+.IQ \fBoutput:\fIport\fR
+Outputs the packet to OpenFlow port number \fIport\fR. If \fIport\fR
+is the packet's input port, the packet is not output.
.
.IP \fBoutput:\fIsrc\fB[\fIstart\fB..\fIend\fB]
Outputs the packet to the OpenFlow port number read from \fIsrc\fR,
which must be an NXM field as described above. For example,
\fBoutput:NXM_NX_REG0[16..31]\fR outputs to the OpenFlow port number
-written in the upper half of register 0. This form of \fBoutput\fR
-uses an OpenFlow extension that is not supported by standard OpenFlow
-switches.
-.
-.IP \fBenqueue:\fIport\fB:\fIqueue\fR
-Enqueues the packet on the specified \fIqueue\fR within port
-\fIport\fR, which must be an OpenFlow port number or keyword
-(e.g. \fBLOCAL\fR).. The number of supported queues depends on the
-switch; some OpenFlow implementations do not support queuing at all.
+written in the upper half of register 0. If the port number is the
+packet's input port, the packet is not output.
+.IP
+This form of \fBoutput\fR was added in Open vSwitch 1.3.0. This form
+of \fBoutput\fR uses an OpenFlow extension that is not supported by
+standard OpenFlow switches.
.
.IP \fBnormal\fR
Subjects the packet to the device's normal L2/L3 processing. (This
Outputs the packet on all switch physical ports other than the port on
which it was received.
.
+.IP \fBlocal\fR
+Outputs the packet on the ``local port,'' which corresponds to the
+network device that has the same name as the bridge.
+.
+.IP \fBin_port\fR
+Outputs the packet on the port from which it was received.
+.
.IP \fBcontroller(\fIkey\fB=\fIvalue\fR...\fB)
Sends the packet to the OpenFlow controller as a ``packet in''
message. The supported key-value pairs are:
controller uses the \fBNXT_SET_CONTROLLER_ID\fR Nicira extension to
OpenFlow.
.RE
+.IP
Any \fIreason\fR other than \fBaction\fR and any nonzero
\fIcontroller-id\fR uses a Nicira vendor extension that, as of this
writing, is only known to be implemented by Open vSwitch (version 1.6
Shorthand for \fBcontroller()\fR or
\fBcontroller(max_len=\fInbytes\fB)\fR, respectively.
.
-.IP \fBlocal\fR
-Outputs the packet on the ``local port,'' which corresponds to the
-network device that has the same name as the bridge.
-.
-.IP \fBin_port\fR
-Outputs the packet on the port from which it was received.
+.IP \fBenqueue(\fIport\fB,\fIqueue\fB)\fR
+Enqueues the packet on the specified \fIqueue\fR within port
+\fIport\fR, which must be an OpenFlow port number or keyword
+(e.g. \fBLOCAL\fR). The number of supported queues depends on the
+switch; some OpenFlow implementations do not support queuing at all.
.
.IP \fBdrop\fR
Discards the packet, so no further processing or forwarding takes place.
``packet_in'' message will be sent only to the controllers having
controller id zero which have registered for the invalid ttl packets.
.
+.IP \fBset_mpls_label\fR:\fIlabel\fR
+Set the label of the outer MPLS label stack entry of a packet.
+\fIlabel\fR should be a 20-bit value that is decimal by default;
+use a \fB0x\fR prefix to specify them in hexadecimal.
+.
+.IP \fBset_mpls_tc\fR:\fItc\fR
+Set the traffic-class of the outer MPLS label stack entry of a packet.
+\fItc\fR should be a in the range 0 to 7 inclusive.
+.
.IP \fBset_mpls_ttl\fR:\fIttl\fR
Set the TTL of the outer MPLS label stack entry of a packet.
\fIttl\fR should be in the range 0 to 255 inclusive.
Writes the literal \fIvalue\fR into the field \fIdst\fR, which should
be specified as a name used for matching. (This is similar to
\fBload\fR but more closely matches the set-field action defined in
-Open Flow 1.2 and above.)
+OpenFlow 1.2 and above.)
.
.IP
Example: \fBset_field:00:11:22:33:44:55->eth_src\fR.
struct vconn *vconn;
struct fte *fte;
- classifier_init(&cls);
+ classifier_init(&cls, NULL);
usable_protocols = read_flows_from_file(argv[2], &cls, FILE_IDX);
protocol = open_vconn(argv[1], &vconn);
struct ds a_s, b_s;
struct fte *fte;
- classifier_init(&cls);
+ classifier_init(&cls, NULL);
read_flows_from_source(argv[1], &cls, 0);
read_flows_from_source(argv[2], &cls, 1);
otherwise very simple in that, after LACP negotiation is complete,
there is no need for special handling of received packets.
+Several of the physical switches that support LACP block all traffic
+for ports that are configured to use LACP, until LACP is negotiated with
+the host. When configuring a LACP bond on a OVS host (eg: XenServer),
+this means that there will be an interruption of the network connectivity
+between the time the ports on the physical switch and the bond on the OVS
+host are configured. The interruption may be relatively long, if different
+people are responsible for managing the switches and the OVS host.
+
+Such network connectivity failure can be avoided if LACP can be configured
+on the OVS host before configuring the physical switch, and having
+the OVS host fall back to a bond mode (active-backup) till the physical
+switch LACP configuration is complete. An option "lacp-fallback-ab" exists to
+provide such behavior on openvswitch.
+
Active Backup Bonding
---------------------
system_id = smap_get(&port->cfg->other_config, "lacp-system-id");
if (system_id) {
- if (sscanf(system_id, ETH_ADDR_SCAN_FMT,
- ETH_ADDR_SCAN_ARGS(s->id)) != ETH_ADDR_SCAN_COUNT) {
+ if (!ovs_scan(system_id, ETH_ADDR_SCAN_FMT,
+ ETH_ADDR_SCAN_ARGS(s->id))) {
VLOG_WARN("port %s: LACP system ID (%s) must be an Ethernet"
" address.", port->name, system_id);
return NULL;
lacp_time = smap_get(&port->cfg->other_config, "lacp-time");
s->fast = lacp_time && !strcasecmp(lacp_time, "fast");
+
+ s->fallback_ab_cfg = smap_get_bool(&port->cfg->other_config,
+ "lacp-fallback-ab", false);
+
return s;
}
s->fake_iface = port->cfg->bond_fake_iface;
+ s->lacp_fallback_ab_cfg = smap_get_bool(&port->cfg->other_config,
+ "lacp-fallback-ab", false);
+
LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
netdev_set_miimon_interval(iface->netdev, miimon_interval);
}
char key[16];
int value;
- if (sscanf(line, "%15[^:]: %u", key, &value) == 2) {
+ if (ovs_scan(line, "%15[^:]: %u", key, &value)) {
int *valuep = shash_find_data(&dict, key);
if (valuep) {
*valuep = value;
while (fgets(line, sizeof line, stream)) {
long long int btime;
- if (sscanf(line, "btime %lld", &btime) == 1) {
+ if (ovs_scan(line, "btime %lld", &btime)) {
boot_time = btime * 1000;
goto done;
}
paren = strchr(line, '(');
if (paren) {
int x;
- if (sscanf(paren + 1, "%d", &x) == 1) {
+ if (ovs_scan(paren + 1, "%d", &x)) {
crashes = x;
}
}
<p>
The following modes require the upstream switch to support 802.3ad with
- successful LACP negotiation:
+ successful LACP negotiation. If LACP negotiation fails and
+ other-config:lacp-fallback-ab is true, then <code>active-backup</code>
+ mode is used:
</p>
<dl>
in LACP negotiations initiated by a remote switch, but not allowed to
initiate such negotiations themselves. If LACP is enabled on a port
whose partner switch does not support LACP, the bond will be
- disabled. Defaults to <code>off</code> if unset.
+ disabled, unless other-config:lacp-fallback-ab is set to true.
+ Defaults to <code>off</code> if unset.
</column>
<column name="other_config" key="lacp-system-id">
rate of once every 30 seconds.
</p>
</column>
+
+ <column name="other_config" key="lacp-fallback-ab"
+ type='{"type": "boolean"}'>
+ <p>
+ Determines the behavior of openvswitch bond in LACP mode. If
+ the partner switch does not support LACP, setting this option
+ to <code>true</code> allows openvswitch to fallback to
+ active-backup. If the option is set to <code>false</code>, the
+ bond will be disabled. In both the cases, once the partner switch
+ is configured to LACP mode, the bond will use LACP.
+ </p>
+ </column>
</group>
<group title="Rebalancing Configuration">
address.</p>
</column>
- <column name="ofport">
- <p>OpenFlow port number for this interface. Unlike most columns, this
- column's value should be set only by Open vSwitch itself. Other
- clients should set this column to an empty set (the default) when
- creating an <ref table="Interface"/>.</p>
- <p>Open vSwitch populates this column when the port number becomes
- known. If the interface is successfully added,
- <ref column="ofport"/> will be set to a number between 1 and 65535
- (generally either in the range 1 to 65279, inclusive, or 65534, the
- port number for the OpenFlow ``local port''). If the interface
- cannot be added then Open vSwitch sets this column
- to -1.</p>
- <p>When <ref column="ofport_request"/> is not set, Open vSwitch picks
- an appropriate value for this column and then tries to keep the value
- constant across restarts.</p>
- </column>
-
- <column name="ofport_request">
- <p>Requested OpenFlow port number for this interface. The port
- number must be between 1 and 65279, inclusive. Some datapaths
- cannot satisfy all requests for particular port numbers. When
- this column is empty or the request cannot be fulfilled, the
- system will choose a free port. The <ref column="ofport"/>
- column reports the assigned OpenFlow port number.</p>
- <p>The port number must be requested in the same transaction
- that creates the port.</p>
- </column>
+ <group title="OpenFlow Port Number">
+ <p>
+ When a client adds a new interface, Open vSwitch chooses an OpenFlow
+ port number for the new port. If the client that adds the port fills
+ in <ref column="ofport_request"/>, then Open vSwitch tries to use its
+ value as the OpenFlow port number. Otherwise, or if the requested
+ port number is already in use or cannot be used for another reason,
+ Open vSwitch automatically assigns a free port number. Regardless of
+ how the port number was obtained, Open vSwitch then reports in <ref
+ column="ofport"/> the port number actually assigned.
+ </p>
+
+ <p>
+ Open vSwitch limits the port numbers that it automatically assigns to
+ the range 1 through 32,767, inclusive. Controllers therefore have
+ free use of ports 32,768 and up.
+ </p>
+
+ <column name="ofport">
+ <p>
+ OpenFlow port number for this interface. Open vSwitch sets this
+ column's value, so other clients should treat it as read-only.
+ </p>
+ <p>
+ The OpenFlow ``local'' port (<code>OFPP_LOCAL</code>) is 65,534.
+ The other valid port numbers are in the range 1 to 65,279,
+ inclusive. Value -1 indicates an error adding the interface.
+ </p>
+ </column>
+
+ <column name="ofport_request"
+ type='{"type": "integer", "minInteger": 1, "maxInteger": 65279}'>
+ <p>
+ Requested OpenFlow port number for this interface.
+ </p>
+
+ <p>
+ Open vSwitch currently assigns the OpenFlow port number for an
+ interface once, when the client first adds the interface. It does
+ not change the port number later if the client sets or changes or
+ clears <ref column="ofport_request"/>. Therefore, to ensure that
+ <ref column="ofport_request"/> takes effect, the client should set
+ it in the same database transaction that creates the interface.
+ (Future versions of Open vSwitch might honor changes to <ref
+ column="ofport_request"/>.)
+ </p>
+ </column>
+ </group>
</group>
<group title="System-Specific Details">
<dt><code>lisp</code></dt>
<dd>
- A layer 3 tunnel over the experimental, UDP-based Locator/ID
- Separation Protocol (RFC 6830).
+ <p>
+ A layer 3 tunnel over the experimental, UDP-based Locator/ID
+ Separation Protocol (RFC 6830).
+ </p>
+ <p>
+ Only IPv4 and IPv6 packets are supported by the protocol, and
+ they are sent and received without an Ethernet header. Traffic
+ to/from LISP ports is expected to be configured explicitly, and
+ the ports are not intended to participate in learning based
+ switching. As such, they are always excluded from packet
+ flooding.
+ </p>
</dd>
<dt><code>patch</code></dt>