Merge remote-tracking branch 'ovs-dev/master'
authorGiuseppe Lettieri <g.lettieri@iet.unipi.it>
Wed, 20 Nov 2013 13:44:38 +0000 (14:44 +0100)
committerGiuseppe Lettieri <g.lettieri@iet.unipi.it>
Wed, 20 Nov 2013 13:44:38 +0000 (14:44 +0100)
71 files changed:
AUTHORS
FAQ
Makefile.am
NEWS
OPENFLOW-1.1+
configure.ac
datapath/datapath.c
datapath/flow.h
datapath/flow_table.c
datapath/flow_table.h
debian/openvswitch-switch.logrotate
include/openflow/openflow-common.h
lib/bitmap.h
lib/bond.c
lib/bond.h
lib/cfm.c
lib/classifier.c
lib/classifier.h
lib/compiler.h
lib/dpif-linux.c
lib/dpif-netdev.c
lib/dpif.c
lib/dpif.h
lib/flow.c
lib/flow.h
lib/lacp.c
lib/lacp.h
lib/match.c
lib/match.h
lib/meta-flow.c
lib/netdev-dummy.c
lib/netdev-linux.c
lib/netdev-vport.c
lib/netdev-vport.h
lib/netlink.c
lib/netlink.h
lib/nx-match.c
lib/odp-util.c
lib/ofp-actions.c
lib/ofp-parse.c
lib/ofp-util.c
lib/ofpbuf.c
lib/packets.c
lib/packets.h
lib/util.c
lib/util.h
lib/vlandev.c
m4/openvswitch.m4
ofproto/ofproto-dpif-xlate.c
ofproto/ofproto-dpif.c
ofproto/ofproto-unixctl.man
ofproto/ofproto.c
ovsdb/ovsdb.c
tests/classifier.at
tests/library.at
tests/ofp-actions.at
tests/ofp-print.at
tests/ofproto-dpif.at
tests/test-classifier.c
tests/test-util.c
utilities/automake.mk
utilities/bugtool/ovs-bugtool.in
utilities/ovs-benchmark.c
utilities/ovs-dev.py
utilities/ovs-dpctl.c
utilities/ovs-ofctl.8.in
utilities/ovs-ofctl.c
vswitchd/INTERNALS
vswitchd/bridge.c
vswitchd/system-stats.c
vswitchd/vswitch.xml

diff --git a/AUTHORS b/AUTHORS
index ac7886b..1b508af 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -6,6 +6,7 @@ Alexandru Copot         alex.mihai.c@gmail.com
 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
@@ -87,6 +88,7 @@ Rob Hoes                rob.hoes@citrix.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
@@ -154,6 +156,7 @@ Giuseppe de Candia      giuseppe.decandia@gmail.com
 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
@@ -217,6 +220,7 @@ Sébastien RICCIO        sr@swisscenter.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
diff --git a/FAQ b/FAQ
index cd3241a..4a8a5be 100644 (file)
--- a/FAQ
+++ b/FAQ
@@ -468,6 +468,27 @@ A: Open vSwitch uses different kinds of flows for different purposes:
         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
 ----------------------
 
index d8cc733..0af7b46 100644 (file)
@@ -22,15 +22,20 @@ AM_CPPFLAGS += -DNDEBUG
 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)
 
diff --git a/NEWS b/NEWS
index 43a2079..38e3d9d 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,11 +1,19 @@
 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
index f978cb5..e194ba7 100644 (file)
@@ -119,11 +119,9 @@ didn't compare the specs carefully yet.)
       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
@@ -140,15 +138,16 @@ didn't compare the specs carefully yet.)
       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
@@ -245,6 +244,12 @@ OpenFlow 1.4
       [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
 -----------------
 
index 5407833..13d2d31 100644 (file)
@@ -48,6 +48,7 @@ AC_SEARCH_LIBS([pthread_sigmask], [pthread])
 AC_FUNC_STRERROR_R
 
 OVS_CHECK_ESX
+OVS_CHECK_WIN32
 OVS_CHECK_COVERAGE
 OVS_CHECK_NDEBUG
 OVS_CHECK_NETLINK
@@ -116,12 +117,12 @@ AC_ARG_VAR(KARCH, [Kernel Architecture String])
 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])
index e4aa672..d0a8595 100644 (file)
@@ -236,7 +236,7 @@ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb)
        }
 
        /* 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;
 
@@ -745,14 +745,6 @@ static struct sk_buff *ovs_flow_cmd_build_info(struct sw_flow *flow,
        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;
@@ -803,7 +795,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
                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;
@@ -911,7 +903,7 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
                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;
@@ -959,7 +951,7 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
        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;
index fdc309f..6b68cf1 100644 (file)
@@ -125,8 +125,8 @@ struct sw_flow_key {
 } __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 {
index ddb14da..29cfcbe 100644 (file)
@@ -434,7 +434,7 @@ static struct sw_flow *masked_flow_lookup(struct table_instance *ti,
        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)
 {
@@ -452,6 +452,14 @@ struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *tbl,
        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;
@@ -519,11 +527,7 @@ static struct sw_flow_mask *flow_mask_find(const struct flow_table *tbl,
        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)
 {
index fbe45d5..f54aa82 100644 (file)
@@ -69,9 +69,11 @@ void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow);
 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);
index 8b04240..a7a71bd 100644 (file)
@@ -7,8 +7,10 @@
     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
 }
index a38f1e3..ef9409f 100644 (file)
@@ -465,4 +465,22 @@ struct ofp_vendor_header {
 };
 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 */
index 8980496..645f15f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -35,10 +35,12 @@ bitmap_bit__(size_t offset)
     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
index 5be1bae..90ac243 100644 (file)
@@ -99,6 +99,7 @@ struct bond {
 
     /* 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;
 };
@@ -258,6 +259,11 @@ bond_reconfigure(struct bond *bond, const struct bond_settings *s)
     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;
@@ -491,8 +497,9 @@ bond_wait(struct bond *bond)
 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;
 }
 
@@ -585,13 +592,15 @@ bond_check_admissibility(struct bond *bond, const void *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;
     }
@@ -604,6 +613,15 @@ bond_check_admissibility(struct bond *bond, const void *slave_,
     }
 
     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. */
@@ -618,12 +636,6 @@ bond_check_admissibility(struct bond *bond, const void *slave_,
         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
@@ -1249,7 +1261,7 @@ bond_unixctl_hash(struct unixctl_conn *conn, int argc, const char *argv[],
     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;
         }
@@ -1258,7 +1270,7 @@ bond_unixctl_hash(struct unixctl_conn *conn, int argc, const char *argv[],
     }
 
     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;
         }
@@ -1266,8 +1278,7 @@ bond_unixctl_hash(struct unixctl_conn *conn, int argc, const char *argv[],
         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);
@@ -1416,14 +1427,20 @@ choose_output_slave(const struct bond *bond, const struct flow *flow,
                     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;
 
index f80fead..5b3814e 100644 (file)
@@ -53,6 +53,7 @@ struct bond_settings {
 
     /* 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. */
index d256a5f..9c65b34 100644 (file)
--- a/lib/cfm.c
+++ b/lib/cfm.c
@@ -480,16 +480,18 @@ cfm_run(struct cfm *cfm) OVS_EXCLUDED(mutex)
             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) {
index 5587141..33ade96 100644 (file)
@@ -41,8 +41,9 @@ static void update_subtables_after_removal(struct classifier *,
                                            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 *,
@@ -149,13 +150,20 @@ cls_rule_is_catchall(const struct cls_rule *rule)
 /* 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
@@ -298,8 +306,15 @@ classifier_remove(struct classifier *cls, struct cls_rule *rule)
     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);
@@ -380,10 +395,7 @@ classifier_lookup(const struct classifier *cls, const struct flow *flow,
             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,
@@ -397,10 +409,7 @@ classifier_lookup(const struct classifier *cls, const struct flow *flow,
                     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;
                 }
@@ -657,11 +666,43 @@ insert_subtable(struct classifier *cls, const struct minimask *mask)
 {
     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)
@@ -673,6 +714,11 @@ insert_subtable(struct classifier *cls, const struct minimask *mask)
 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);
@@ -774,10 +820,10 @@ update_subtables_after_removal(struct classifier *cls,
     }
 }
 
-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) {
@@ -789,6 +835,71 @@ find_match(const struct cls_subtable *subtable, const struct flow *flow)
     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)
@@ -809,19 +920,30 @@ insert_rule(struct classifier *cls, struct cls_subtable *subtable,
 {
     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) {
@@ -848,6 +970,11 @@ insert_rule(struct classifier *cls, struct cls_subtable *subtable,
  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;
 }
index 6f8c186..3c3e7d1 100644 (file)
  * 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"
@@ -118,9 +129,15 @@ extern "C" {
 /* 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.
                                      */
@@ -140,6 +157,9 @@ struct cls_subtable {
     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
@@ -157,6 +177,8 @@ struct cls_rule {
     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
@@ -187,7 +209,7 @@ bool cls_rule_is_catchall(const struct cls_rule *);
 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);
index ccc949b..0dbacbf 100644 (file)
@@ -28,6 +28,7 @@
 #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))
@@ -39,6 +40,7 @@
 #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
index 42958d3..0442f77 100644 (file)
@@ -72,7 +72,7 @@ struct dpif_linux_dp {
 
     /* 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.*/
index 5195d18..11f010c 100644 (file)
@@ -31,6 +31,7 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
+#include "classifier.h"
 #include "csum.h"
 #include "dpif.h"
 #include "dpif-provider.h"
@@ -59,6 +60,9 @@
 
 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. */
@@ -92,6 +96,7 @@ struct dp_netdev {
     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. */
 
@@ -118,8 +123,12 @@ struct dp_netdev_port {
 
 /* 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. */
@@ -159,8 +168,8 @@ static int dpif_netdev_open(const struct dpif_class *, const char *name,
 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,
@@ -291,6 +300,7 @@ create_dp_netdev(const char *name, const struct dpif_class *class,
         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();
@@ -357,6 +367,7 @@ dp_netdev_free(struct dp_netdev *dp)
     }
     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);
@@ -634,6 +645,11 @@ dpif_netdev_get_max_ports(const struct dpif *dpif OVS_UNUSED)
 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);
@@ -742,13 +758,27 @@ dpif_netdev_port_poll_wait(const struct dpif *dpif_)
 }
 
 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;
         }
     }
@@ -766,23 +796,29 @@ get_dpif_flow_stats(struct dp_netdev_flow *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);
         }
@@ -790,6 +826,11 @@ dpif_netdev_flow_from_nlattrs(const struct nlattr *key, uint32_t key_len,
         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;
@@ -798,6 +839,14 @@ dpif_netdev_flow_from_nlattrs(const struct nlattr *key, uint32_t key_len,
     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,
@@ -814,7 +863,7 @@ dpif_netdev_flow_get(const struct dpif *dpif,
     }
 
     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);
@@ -842,23 +891,36 @@ set_flow_actions(struct dp_netdev_flow *netdev_flow,
 }
 
 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;
 }
 
@@ -876,23 +938,25 @@ dpif_netdev_flow_put(struct dpif *dpif, const struct dpif_flow_put *put)
 {
     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;
@@ -901,7 +965,8 @@ dpif_netdev_flow_put(struct dpif *dpif, const struct dpif_flow_put *put)
             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) {
@@ -912,8 +977,11 @@ dpif_netdev_flow_put(struct dpif *dpif, const struct dpif_flow_put *put)
                     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);
@@ -935,7 +1003,7 @@ dpif_netdev_flow_del(struct dpif *dpif, const struct dpif_flow_del *del)
     }
 
     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);
@@ -954,6 +1022,7 @@ struct dp_netdev_flow_state {
     uint32_t offset;
     struct nlattr *actions;
     struct odputil_keybuf keybuf;
+    struct odputil_keybuf maskbuf;
     struct dpif_flow_stats stats;
 };
 
@@ -994,16 +1063,24 @@ dpif_netdev_flow_dump_next(const struct dpif *dpif, void *state_,
         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) {
@@ -1038,8 +1115,7 @@ static int
 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 ||
@@ -1047,22 +1123,24 @@ dpif_netdev_execute(struct dpif *dpif, const struct dpif_execute *execute)
         return EINVAL;
     }
 
-    /* Make a deep copy of 'packet', because we might modify its data. */
-    ofpbuf_init(&copy, DP_NETDEV_HEADROOM + execute->packet->size);
-    ofpbuf_reserve(&copy, DP_NETDEV_HEADROOM);
-    ofpbuf_put(&copy, execute->packet->data, execute->packet->size);
-
-    flow_extract(&copy, 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, &copy, &key,
+        dp_netdev_execute_actions(dp, &key, copy,
                                   execute->actions, execute->actions_len);
         ovs_mutex_unlock(&dp_netdev_mutex);
+        ofpbuf_delete(copy);
     }
-
-    ofpbuf_uninit(&copy);
     return error;
 }
 
@@ -1154,7 +1232,7 @@ dp_netdev_flow_used(struct dp_netdev_flow *netdev_flow,
     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
@@ -1174,7 +1252,7 @@ dp_netdev_port_input(struct dp_netdev *dp, struct dp_netdev_port *port,
     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++;
@@ -1244,17 +1322,6 @@ dpif_netdev_wait(struct dpif *dpif)
     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,
@@ -1306,25 +1373,46 @@ dp_netdev_output_userspace(struct dp_netdev *dp, const struct ofpbuf *packet,
     }
 }
 
+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                    \
index b338b33..7151ef9 100644 (file)
@@ -633,14 +633,6 @@ dpif_port_query_by_name(const struct dpif *dpif, const char *devname,
     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'.
@@ -892,15 +884,19 @@ dpif_flow_put__(struct dpif *dpif, const struct dpif_flow_put *put)
 
 /* 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
index de7450a..499ee79 100644 (file)
  * 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,
@@ -418,7 +444,6 @@ int dpif_port_query_by_name(const struct dpif *, const char *devname,
                             struct dpif_port *);
 int dpif_port_get_name(struct dpif *, odp_port_t port_no,
                        char *name, size_t name_size);
-uint32_t dpif_get_max_ports(const struct dpif *);
 uint32_t dpif_port_get_pid(const struct dpif *, odp_port_t port_no);
 
 struct dpif_port_dump {
index 8c336f6..c6683a5 100644 (file)
 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)
 {
@@ -515,7 +523,7 @@ flow_zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards)
 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;
@@ -662,16 +670,11 @@ static void
 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++;
     }
 }
 
@@ -683,6 +686,43 @@ flow_wildcards_fold_minimask(struct flow_wildcards *wc,
     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)
@@ -1106,13 +1146,7 @@ flow_compose(struct ofpbuf *b, const struct flow *flow)
 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 *
@@ -1139,16 +1173,12 @@ miniflow_init__(struct miniflow *dst, const struct flow *src, int n)
 {
     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)];
     }
 }
 
@@ -1163,10 +1193,11 @@ miniflow_init(struct miniflow *dst, const struct flow *src)
 
     /* 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++;
         }
     }
@@ -1180,7 +1211,7 @@ void
 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));
 }
 
@@ -1190,7 +1221,7 @@ void
 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);
 }
@@ -1207,7 +1238,7 @@ miniflow_move(struct miniflow *dst, struct miniflow *src)
     } 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'
@@ -1231,21 +1262,12 @@ miniflow_expand(const struct miniflow *src, struct flow *dst)
 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'
@@ -1281,28 +1303,24 @@ miniflow_equal(const struct miniflow *a, const struct miniflow *b)
 {
     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;
             }
         }
     }
@@ -1317,20 +1335,17 @@ miniflow_equal_in_minimask(const struct miniflow *a, const struct miniflow *b,
                            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;
@@ -1344,20 +1359,17 @@ miniflow_equal_flow_in_minimask(const struct miniflow *a, const struct flow *b,
 {
     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;
@@ -1369,21 +1381,19 @@ miniflow_hash(const struct miniflow *flow, uint32_t basis)
 {
     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);
 }
 
@@ -1398,19 +1408,16 @@ miniflow_hash_in_minimask(const struct miniflow *flow,
 {
     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);
@@ -1425,27 +1432,48 @@ uint32_t
 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(). */
@@ -1483,23 +1511,19 @@ minimask_combine(struct minimask *dst_,
     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;
         }
     }
 }
@@ -1556,20 +1580,15 @@ minimask_has_extra(const struct minimask *a_, const struct minimask *b_)
 {
     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;
         }
     }
 
@@ -1583,15 +1602,11 @@ minimask_is_catchall(const struct minimask *mask_)
 {
     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;
index 093d509..5e78073 100644 (file)
 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);
@@ -88,44 +87,76 @@ union flow_in_port {
  * 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 {
@@ -234,6 +265,10 @@ hash_odp_port(odp_port_t odp_port)
 
 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.
  *
@@ -262,6 +297,9 @@ bool flow_wildcards_has_extra(const struct flow_wildcards *,
 
 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 *,
@@ -287,7 +325,7 @@ bool flow_equal_except(const struct flow *a, const struct flow *b,
 /* 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".
  *
@@ -321,9 +359,9 @@ bool flow_equal_except(const struct flow *a, const struct flow *b,
  * 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 *);
@@ -349,6 +387,8 @@ bool miniflow_equal_flow_in_minimask(const struct miniflow *a,
 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. */
 
index 5421e2a..fce65b3 100644 (file)
@@ -102,6 +102,7 @@ struct lacp {
     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;
 };
@@ -283,6 +284,12 @@ lacp_configure(struct lacp *lacp, const struct lacp_settings *s)
 
     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);
 }
 
@@ -450,7 +457,9 @@ slave_may_enable__(struct slave *slave) OVS_REQUIRES(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
@@ -587,6 +596,9 @@ lacp_update_attached(struct lacp *lacp) OVS_REQUIRES(mutex)
         }
 
         if (slave->status == LACP_DEFAULTED) {
+            if (lacp->fallback_ab) {
+                slave->attached = true;
+            }
             continue;
         }
 
@@ -603,7 +615,8 @@ lacp_update_attached(struct lacp *lacp) OVS_REQUIRES(mutex)
 
     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;
index 89b0e0a..593b80d 100644 (file)
@@ -35,6 +35,7 @@ struct lacp_settings {
     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);
index 0f674b0..71d86be 100644 (file)
@@ -842,7 +842,7 @@ match_format(const struct match *match, struct ds *s, unsigned int priority)
 
     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);
@@ -1173,22 +1173,42 @@ minimatch_matches_flow(const struct minimatch *match,
     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
index 1e938a1..ee01acd 100644 (file)
@@ -159,6 +159,9 @@ uint32_t minimatch_hash(const struct minimatch *, uint32_t basis);
 
 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);
index 9f39c18..b37f781 100644 (file)
@@ -2373,15 +2373,15 @@ mf_from_ethernet_string(const struct mf_field *mf, const char *s,
     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;
     }
@@ -2397,11 +2397,10 @@ mf_from_ipv4_string(const struct mf_field *mf, const char *s,
 
     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);
@@ -2410,7 +2409,7 @@ mf_from_ipv4_string(const struct mf_field *mf, const char *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);
@@ -2548,9 +2547,8 @@ parse_flow_tun_flags(const char *s_, const char *(*bit_to_string)(uint32_t),
         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;
         }
@@ -2594,7 +2592,7 @@ mf_from_tun_flags_string(const char *s, ovs_be16 *valuep, ovs_be16 *maskp)
     }
 
     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
@@ -2941,9 +2939,9 @@ mf_parse_subfield__(struct mf_subfield *sf, const char **sp)
     }
 
     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;
index fe54576..fd30454 100644 (file)
@@ -91,7 +91,7 @@ struct netdev_rx_dummy {
 
 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 *);
 
@@ -571,7 +571,7 @@ netdev_dummy_set_etheraddr(struct netdev *netdev,
     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);
 
@@ -666,7 +666,7 @@ netdev_dummy_update_flags__(struct netdev_dummy *netdev,
     netdev->flags |= on;
     netdev->flags &= ~off;
     if (*old_flagsp != netdev->flags) {
-        netdev_dummy_poll_notify(netdev);
+        netdev_dummy_changed(netdev);
     }
 
     return 0;
@@ -703,7 +703,7 @@ netdev_dummy_change_seq(const struct netdev *netdev_)
 /* 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) {
index 7e75144..ae0e5a0 100644 (file)
@@ -2309,14 +2309,14 @@ parse_if_inet6_line(const char *line,
 {
     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
@@ -2424,12 +2424,11 @@ netdev_linux_get_next_hop(const struct in_addr *host, struct in_addr *next_hop,
             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;
@@ -4560,25 +4559,25 @@ get_stats_via_proc(const char *netdev_name, struct netdev_stats *stats)
         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;
index 0374ae3..07b2381 100644 (file)
@@ -71,7 +71,7 @@ struct vport_class {
 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
@@ -108,6 +108,14 @@ netdev_vport_is_patch(const struct netdev *netdev)
     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)
 {
@@ -205,7 +213,7 @@ netdev_vport_set_etheraddr(struct netdev *netdev_,
 
     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;
@@ -285,7 +293,7 @@ netdev_vport_wait(void)
 /* 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) {
@@ -495,7 +503,7 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args)
 
     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;
@@ -669,7 +677,7 @@ set_patch_config(struct netdev *dev_, const struct smap *args)
     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;
index dc49097..eaeb386 100644 (file)
@@ -30,6 +30,7 @@ void netdev_vport_tunnel_register(void);
 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);
 
index 40477ea..680c676 100644 (file)
@@ -220,6 +220,18 @@ nl_msg_put_unspec_uninit(struct ofpbuf *msg, uint16_t type, size_t size)
     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
index 21d49d3..f9234da 100644 (file)
@@ -59,6 +59,7 @@ void *nl_msg_push_uninit(struct ofpbuf *, size_t);
 
 /* 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);
@@ -136,14 +137,22 @@ nl_attr_is_valid(const struct nlattr *nla, size_t maxlen)
 {
     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
@@ -152,7 +161,7 @@ nl_attr_is_valid(const struct nlattr *nla, size_t maxlen)
 #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)                               \
index 8282cc2..72fc00f 100644 (file)
@@ -572,7 +572,7 @@ nx_put_raw(struct ofpbuf *b, bool oxm, const struct match *match,
     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) {
index 6ac3853..62cc638 100644 (file)
@@ -217,7 +217,7 @@ parse_flags(const char *s, const char *(*bit_to_string)(uint32_t),
         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;
@@ -308,7 +308,7 @@ format_odp_userspace_action(struct ds *ds, const struct nlattr *attr)
                               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 {
@@ -466,21 +466,11 @@ static int
 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;
         }
@@ -498,21 +488,21 @@ parse_odp_action(const char *s, const struct simap *port_names,
     }
 
     {
-        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;
 
@@ -527,8 +517,8 @@ parse_odp_action(const char *s, const struct simap *port_names,
             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;
 
@@ -550,11 +540,13 @@ parse_odp_action(const char *s, const struct simap *port_names,
             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;
@@ -565,16 +557,15 @@ parse_odp_action(const char *s, const struct simap *port_names,
             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;
 
@@ -611,14 +602,13 @@ parse_odp_action(const char *s, const struct simap *port_names,
         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)
@@ -639,9 +629,8 @@ parse_odp_action(const char *s, const struct simap *port_names,
         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;
 
@@ -1392,7 +1381,7 @@ generate_all_wildcard_mask(struct ofpbuf *ofp, const struct nlattr *key)
     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;
 
@@ -1563,28 +1552,17 @@ static int
 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);
@@ -1594,16 +1572,16 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
     }
 
     {
-        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);
@@ -1613,31 +1591,26 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
     }
 
     {
-        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;
@@ -1655,18 +1628,16 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
                 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;
 
@@ -1689,16 +1660,16 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
     }
 
     {
-        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);
@@ -1731,23 +1702,22 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
         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,
                               &eth_key, sizeof eth_key);
             nl_msg_put_unspec(mask, OVS_KEY_ATTR_ETHERNET,
                               &eth_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,
                               &eth_key, sizeof eth_key);
 
@@ -1761,13 +1731,13 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
     }
 
     {
-        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) |
@@ -1777,8 +1747,7 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
                                   (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) |
@@ -1787,8 +1756,10 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
                 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) |
@@ -1798,8 +1769,8 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
                                   (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) |
@@ -1816,14 +1787,14 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
         int eth_type_mask;
         int n = -1;
 
-        if (mask && sscanf(s, "eth_type(%i/%i)%n",
-                   &eth_type, &eth_type_mask, &n) > 0 && n > 0) {
+        if (mask && ovs_scan(s, "eth_type(%i/%i)%n",
+                             &eth_type, &eth_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", &eth_type, &n) > 0 && n > 0) {
+        } else if (ovs_scan(s, "eth_type(%i)%n", &eth_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);
@@ -1837,8 +1808,10 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
         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,
@@ -1850,9 +1823,8 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
             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,
@@ -1869,61 +1841,46 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
 
 
     {
-        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);
@@ -1951,16 +1908,16 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
         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;
@@ -1988,12 +1945,12 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
             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 ||
@@ -2024,9 +1981,9 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
         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;
 
@@ -2039,8 +1996,8 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
             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);
@@ -2060,12 +2017,12 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
         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,
@@ -2082,9 +2039,9 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
         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;
 
@@ -2098,8 +2055,7 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
                               &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);
@@ -2121,9 +2077,9 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
         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;
 
@@ -2137,8 +2093,7 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
                               &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);
@@ -2154,35 +2109,21 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
     }
 
     {
-        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) {
@@ -2197,25 +2138,21 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
     {
         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);
 
@@ -2229,60 +2166,40 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
     }
 
     {
-        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) {
@@ -2307,59 +2224,63 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
         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,
@@ -2563,9 +2484,8 @@ odp_flow_key_from_flow__(struct ofpbuf *buf, const struct flow *data,
                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);
@@ -3310,8 +3230,20 @@ odp_put_userspace_action(uint32_t pid,
     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;
     }
@@ -3436,9 +3368,8 @@ commit_mpls_action(const struct flow *flow, struct flow *base,
     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;
index bae479e..4558669 100644 (file)
@@ -697,7 +697,6 @@ ofpacts_pull_openflow_actions(struct ofpbuf *openflow,
                               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;
 
@@ -1738,7 +1737,6 @@ ofpacts_pull_openflow_instructions(struct ofpbuf *openflow,
                                    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;
@@ -1869,7 +1867,8 @@ ofpact_check_output_port(ofp_port_t port, ofp_port_t max_ports)
     }
 }
 
-/* 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. */
@@ -2056,6 +2055,10 @@ ofpact_check__(struct ofpact *a, struct flow *flow,
 
     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:
@@ -2127,6 +2130,7 @@ ofpacts_check(struct ofpact ofpacts[], size_t ofpacts_len,
     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) {
@@ -2139,6 +2143,7 @@ ofpacts_check(struct ofpact ofpacts[], size_t 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;
 }
 
@@ -2150,14 +2155,15 @@ ofpacts_verify(const struct ofpact ofpacts[], size_t ofpacts_len)
     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);
 
@@ -3280,7 +3286,7 @@ ofpact_format(const struct ofpact *a, struct ds *s)
         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:
index 915dc90..8698030 100644 (file)
@@ -147,8 +147,7 @@ str_to_be64(const char *str, ovs_be64 *valuep)
 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;
@@ -179,12 +178,13 @@ static char * WARN_UNUSED_RESULT
 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);
@@ -1844,7 +1844,7 @@ parse_ofp_table_mod(struct ofputil_table_mod *tm, const char *table_id,
     *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) {
index 9645e04..7903de8 100644 (file)
@@ -84,7 +84,7 @@ ofputil_netmask_to_wcbits(ovs_be32 netmask)
 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);
@@ -1542,10 +1542,10 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
 
         /* 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))) {
@@ -1698,6 +1698,9 @@ ofputil_pull_bands(struct ofpbuf *msg, size_t len, uint16_t *n_bands,
         }
         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) ?
@@ -1728,6 +1731,11 @@ ofputil_decode_meter_mod(const struct ofp_header *oh,
 
     /* 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) {
@@ -1738,6 +1746,10 @@ ofputil_decode_meter_mod(const struct ofp_header *oh,
         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);
@@ -2081,7 +2093,7 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
             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))) {
index f7145db..d578ab5 100644 (file)
@@ -407,7 +407,7 @@ ofpbuf_put_hex(struct ofpbuf *b, const char *s, size_t *n)
         uint8_t byte;
         bool ok;
 
-        s += strspn(s, " ");
+        s += strspn(s, " \t\r\n");
         byte = hexits_value(s, 2, &ok);
         if (!ok) {
             if (n) {
index dc1970a..4bec4d1 100644 (file)
@@ -130,8 +130,7 @@ eth_addr_is_reserved(const uint8_t ea[ETH_ADDR_LEN])
 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);
index f4f00ce..ef8c00c 100644 (file)
@@ -181,15 +181,14 @@ ovs_be32 set_mpls_lse_values(uint8_t ttl, uint8_t tc, uint8_t bos,
  * 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
@@ -381,8 +380,7 @@ mpls_lse_to_bos(ovs_be32 mpls_lse)
  * 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)) {
  *     ...
  * }
  */
@@ -392,7 +390,6 @@ mpls_lse_to_bos(ovs_be32 mpls_lse)
         &((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. */
@@ -578,7 +575,7 @@ struct ovs_16aligned_ip6_frag {
  * 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) {
  *     ...
  * }
index a32c80a..c2ffab2 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <config.h>
 #include "util.h"
+#include <ctype.h>
 #include <errno.h>
 #include <limits.h>
 #include <pthread.h>
@@ -26,6 +27,7 @@
 #include <string.h>
 #include <sys/stat.h>
 #include <unistd.h>
+#include "bitmap.h"
 #include "byte-order.h"
 #include "coverage.h"
 #include "ovs-thread.h"
@@ -887,16 +889,14 @@ log_2_ceil(uint32_t n)
 }
 
 /* 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);                               \
@@ -904,6 +904,7 @@ raw_ctz(uint32_t n)
         count -= X;                             \
         n = k;                                  \
     }
+    CTZ_STEP(32);
     CTZ_STEP(16);
     CTZ_STEP(8);
     CTZ_STEP(4);
@@ -916,8 +917,8 @@ raw_ctz(uint32_t n)
 #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
@@ -939,14 +940,21 @@ popcount(uint32_t x)
 #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. */
@@ -1245,3 +1253,444 @@ bitwise_get(const void *src, unsigned int src_len,
                  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;
+}
+
index 32a7c8c..acb2d8f 100644 (file)
@@ -264,6 +264,8 @@ bool str_to_uint(const char *, int base, unsigned int *);
 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);
@@ -283,20 +285,25 @@ void ignore(bool x OVS_UNUSED);
 \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. */
@@ -306,9 +313,12 @@ ctz(uint32_t n)
     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. */
index 6531bf0..b793f77 100644 (file)
@@ -221,8 +221,7 @@ vlandev_linux_refresh(void)
         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);
         }
     }
index 012901f..7c37985 100644 (file)
@@ -56,6 +56,16 @@ AC_DEFUN([OVS_CHECK_ESX],
       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],
@@ -195,7 +205,7 @@ AC_DEFUN([OVS_CHECK_PYTHON],
           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:
index 367dd88..a331c0b 100644 (file)
@@ -1663,7 +1663,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
 
     /* 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");
index 9756480..6c1d5a9 100644 (file)
@@ -332,6 +332,7 @@ struct ofport_dpif {
     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. */
 
@@ -537,7 +538,9 @@ ofproto_dpif_cast(const struct ofproto *ofproto)
 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 *);
@@ -1233,7 +1236,6 @@ construct(struct ofproto *ofproto_)
 {
     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);
@@ -1241,9 +1243,6 @@ construct(struct ofproto *ofproto_)
         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;
@@ -1255,7 +1254,7 @@ construct(struct ofproto *ofproto_)
     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);
@@ -1710,6 +1709,7 @@ port_construct(struct ofport *port_)
     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
@@ -2307,6 +2307,7 @@ bundle_update(struct ofbundle *bundle)
     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;
@@ -2354,6 +2355,7 @@ bundle_add_port(struct ofbundle *bundle, ofp_port_t ofp_port,
         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;
         }
@@ -5280,37 +5282,39 @@ trace_report(struct xlate_in *xin, const char *s, int recurse)
     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;
         }
     }
@@ -5327,12 +5331,15 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[],
             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;
@@ -5347,91 +5354,207 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[],
      * 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_portflow);
         }
     }
 
-    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;
@@ -5444,6 +5567,10 @@ ofproto_trace(struct ofproto_dpif *ofproto, const struct flow *flow,
         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;
 
@@ -5877,8 +6004,12 @@ ofproto_dpif_unixctl_init(void)
 
     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,
index dd8e8d8..89013d9 100644 (file)
@@ -6,15 +6,30 @@ These commands manage the core OpenFlow switch implementation (called
 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"
@@ -41,13 +56,13 @@ instead of just a flow:
 .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."
@@ -55,12 +70,12 @@ Most of the time, Open vSwitch can figure out everything about the
 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
@@ -93,11 +108,25 @@ The tunnel ID on which the packet arrived.
 .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
index 0c66a59..5cd6b1e 100644 (file)
@@ -1772,12 +1772,18 @@ ofproto_port_add(struct ofproto *ofproto, struct netdev *netdev,
         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;
 }
@@ -2004,9 +2010,13 @@ alloc_ofp_port(struct ofproto *ofproto, const char *netdev_name)
 
         /* 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,
@@ -6614,7 +6624,7 @@ static void
 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;
 }
 
index 6b53f4a..e27d0de 100644 (file)
@@ -1,4 +1,4 @@
-/* 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.
@@ -140,7 +140,7 @@ static bool
 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';
 }
 
index cf0cc44..546c8f7 100644 (file)
@@ -22,3 +22,41 @@ m4_foreach(
   [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
index 6d1c6da..6e28573 100644 (file)
@@ -114,12 +114,13 @@ m4_foreach(
   [[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], [], [])
index 244c9ee..08ebccf 100644 (file)
@@ -39,7 +39,7 @@ AT_DATA([test-data], [dnl
 # actions=mod_tp_dst:443
 000a 0008 01bb 0000
 
-# actions=enqueue:10q55
+# actions=enqueue:10:55
 000b 0010 000a 000000000000 00000037
 
 # actions=resubmit:5
@@ -477,3 +477,15 @@ AT_CHECK(
   [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
index 8701f54..82e8c3d 100644 (file)
@@ -855,6 +855,18 @@ OFPT_FLOW_MOD (OF1.2) (xid=0x52334507): ADD priority=255,sctp actions=set_field:
 ])
 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 "\
@@ -1844,6 +1856,35 @@ type=drop rate=1024 burst_size=128
 ])
 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
index 2a2d325..9a7c8ff 100644 (file)
@@ -413,7 +413,7 @@ cookie=0xa dl_src=40:44:44:44:44:48 actions=push_mpls:0x8847,load:10->OXM_OF_MPL
 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])
 
@@ -816,19 +816,19 @@ arp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=ff:ff:f
 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])
@@ -891,7 +891,7 @@ AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
  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:
@@ -1461,9 +1461,8 @@ AT_CHECK([ovs-appctl ofproto/trace \
 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
 ])
 
@@ -1473,9 +1472,8 @@ AT_CHECK([ovs-appctl ofproto/trace ovs-dummy \
 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
 ])
 
@@ -1486,7 +1484,7 @@ AT_CHECK([tail -1 stdout], [0], [dnl
 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
 ])
 
@@ -1610,6 +1608,25 @@ ovs-appctl: ovs-vswitchd: server returned an error
 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])
@@ -2523,7 +2540,9 @@ m4_define([STRIP_USED], [[sed '
 ' | 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])
@@ -2535,8 +2554,8 @@ table=0 in_port=1 actions=output(2)
 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
@@ -2550,9 +2569,9 @@ table=0 in_port=1,dl_src=50:54:00:00:00:09 actions=output(2)
 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
@@ -2566,9 +2585,9 @@ table=0 in_port=1,icmp,nw_src=10.0.0.4 actions=output(2)
 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
@@ -2582,8 +2601,8 @@ table=0 in_port=1,icmp,icmp_type=8 actions=output(2)
 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
@@ -2594,9 +2613,9 @@ ADD_OF_PORTS([br0], [1], [2])
 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
@@ -2611,9 +2630,9 @@ table=0 dl_src=50:54:00:00:00:0b actions=pop_mpls:0x0800,2
 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
@@ -2635,9 +2654,9 @@ ovs-vsctl \
 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
@@ -2654,9 +2673,9 @@ AT_CHECK([ovs-appctl netdev-dummy/set-admin-state up], 0, [OK
 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
@@ -2673,9 +2692,9 @@ AT_CHECK([ovs-appctl netdev-dummy/set-admin-state up], 0, [OK
 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
@@ -2710,9 +2729,9 @@ ovs-appctl time/warp 5000
 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
@@ -2727,9 +2746,9 @@ table=0 in_port=90,dl_src=50:54:00:00:00:09 actions=output(2)
 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
@@ -2745,9 +2764,9 @@ 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
@@ -2762,9 +2781,9 @@ table=1 dl_src=50:54:00:00:00:09 actions=output(2)
 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
@@ -2783,8 +2802,8 @@ in_port=1 actions=output:2
 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
@@ -2803,9 +2822,9 @@ in_port=1 actions=output:2
 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
@@ -2821,9 +2840,9 @@ table=0 in_port=91 reg0=0x0a000002,actions=output(2)
 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
@@ -2837,9 +2856,9 @@ table=0 in_port=1 ip,actions=push:NXM_OF_IP_SRC[[]],output(2)
 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
@@ -2862,23 +2881,23 @@ for i in 1 2; do
     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)
@@ -2890,10 +2909,10 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:
 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
@@ -2907,9 +2926,9 @@ table=0 in_port=1,icmp,nw_src=10.0.0.4 actions=dec_ttl,output(2)
 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
@@ -2933,9 +2952,9 @@ dnl ofproto-dpif).  The second entry actually updates the destination
 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
index 3f39f8f..ee7e76c 100644 (file)
@@ -403,10 +403,12 @@ compare_classifiers(struct classifier *cls, struct tcls *tcls)
 
     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)];
@@ -426,7 +428,7 @@ compare_classifiers(struct classifier *cls, struct tcls *tcls)
         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) {
@@ -436,6 +438,8 @@ compare_classifiers(struct classifier *cls, struct tcls *tcls)
             assert(cls_rule_equal(cr0, cr1));
             assert(tr0->aux == tr1->aux);
         }
+        cr2 = classifier_lookup(cls, &flow, NULL);
+        assert(cr2 == cr0);
     }
 }
 
@@ -612,7 +616,7 @@ test_empty(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
     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));
@@ -644,7 +648,7 @@ test_single_rule(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
         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);
 
@@ -683,7 +687,7 @@ test_rule_replacement(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
         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);
@@ -795,7 +799,7 @@ test_many_rules_in_one_list (int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
                 pri_rules[i] = -1;
             }
 
-            classifier_init(&cls);
+            classifier_init(&cls, flow_segment_u32s);
             ovs_rwlock_wrlock(&cls.rwlock);
             tcls_init(&tcls);
 
@@ -897,7 +901,7 @@ test_many_rules_in_one_table(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
             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);
 
@@ -959,7 +963,7 @@ test_many_rules_in_n_tables(int n_tables)
         }
         shuffle(priorities, ARRAY_SIZE(priorities));
 
-        classifier_init(&cls);
+        classifier_init(&cls, flow_segment_u32s);
         ovs_rwlock_wrlock(&cls.rwlock);
         tcls_init(&tcls);
 
index 6ed2f77..7183f46 100644 (file)
@@ -70,6 +70,16 @@ check_ctz(uint32_t x, int n)
     }
 }
 
+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)
 {
@@ -86,8 +96,21 @@ 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. */
@@ -154,53 +177,53 @@ test_round_down_pow2(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 }
 
 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);
     }
@@ -406,12 +429,544 @@ test_assert(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 {
     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},
@@ -419,6 +974,7 @@ static const struct command commands[] = {
     {"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},
 };
 
index d26f961..b7a4d73 100644 (file)
@@ -20,6 +20,8 @@ scripts_SCRIPTS += \
        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 \
index f4bf383..15f8271 100755 (executable)
@@ -549,7 +549,7 @@ exclude those logs from the archive.
     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'])
index 6eeedc6..0bb316f 100644 (file)
@@ -101,11 +101,11 @@ parse_target(const char *s_, struct in_addr *addr,
     *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_);
index 21f0fc5..d33b2ba 100755 (executable)
@@ -226,7 +226,7 @@ Basic Configuration:
 
     # 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
index 78475e7..29f1151 100644 (file)
@@ -1116,9 +1116,8 @@ dpctl_normalize_actions(int argc, char *argv[])
     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 {
index dc86619..e5e488a 100644 (file)
@@ -382,7 +382,8 @@ between a switch and its controller.
 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
@@ -922,12 +923,32 @@ The \fBip_frag\fR match type is likely to be most useful in
 \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
@@ -966,6 +987,30 @@ Neighbor Advertisement (ICMPv6 type 136), matches the target link\-layer
 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
@@ -1035,31 +1080,28 @@ command to be used as input for other commands that parse flows.
 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
@@ -1075,6 +1117,13 @@ tree protocol).
 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:
@@ -1095,6 +1144,7 @@ controller connection will only have a nonzero connection ID if its
 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
@@ -1105,12 +1155,11 @@ or later).
 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.
@@ -1252,6 +1301,15 @@ invalid ttl packets.  If controller ids are not specified, the
 ``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.
@@ -1318,7 +1376,7 @@ value just popped.
 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.
index a0dc5c8..f3b58dd 100644 (file)
@@ -2398,7 +2398,7 @@ ofctl_replace_flows(int argc OVS_UNUSED, char *argv[])
     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);
@@ -2468,7 +2468,7 @@ ofctl_diff_flows(int argc OVS_UNUSED, char *argv[])
     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);
 
index 2b48c9b..994353d 100644 (file)
@@ -143,6 +143,20 @@ LACP bonding requires the remote switch to implement LACP, but it is
 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
 ---------------------
 
index fecae60..555f45d 100644 (file)
@@ -3302,8 +3302,8 @@ port_configure_lacp(struct port *port, struct lacp_settings *s)
 
     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;
@@ -3326,6 +3326,10 @@ port_configure_lacp(struct port *port, struct lacp_settings *s)
 
     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;
 }
 
@@ -3414,6 +3418,9 @@ port_configure_bond(struct port *port, struct bond_settings *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);
     }
index e8a8512..2960b87 100644 (file)
@@ -148,7 +148,7 @@ get_memory_stats(struct smap *stats)
             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;
@@ -192,7 +192,7 @@ get_boot_time(void)
 
         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;
             }
@@ -344,7 +344,7 @@ count_crashes(pid_t pid)
     paren = strchr(line, '(');
     if (paren) {
         int x;
-        if (sscanf(paren + 1, "%d", &x) == 1) {
+        if (ovs_scan(paren + 1, "%d", &x)) {
             crashes = x;
         }
     }
index 52924ee..35d59b9 100644 (file)
 
       <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>