Merge branch 'mainstream'
authorGiuseppe Lettieri <g.lettieri@iet.unipi.it>
Tue, 5 Nov 2013 08:56:15 +0000 (09:56 +0100)
committerGiuseppe Lettieri <g.lettieri@iet.unipi.it>
Tue, 5 Nov 2013 08:56:15 +0000 (09:56 +0100)
123 files changed:
FAQ
INSTALL.SSL
INSTALL.XenServer
NEWS
OPENFLOW-1.1+
README
build-aux/extract-ofp-msgs
configure.ac
datapath/datapath.c
datapath/flow.c
datapath/flow.h
datapath/flow_netlink.c
datapath/flow_table.c
datapath/linux/compat/dev-openvswitch.c
datapath/linux/compat/include/linux/netdevice.h
datapath/linux/compat/netdevice.c
datapath/vport-internal_dev.c
datapath/vport-netdev.c
debian/.gitignore
debian/automake.mk
debian/changelog
debian/control
debian/ifupdown.sh
debian/openvswitch-controller.README.Debian [deleted file]
debian/openvswitch-controller.default [deleted file]
debian/openvswitch-controller.dirs [deleted file]
debian/openvswitch-controller.init [deleted file]
debian/openvswitch-controller.install [deleted file]
debian/openvswitch-controller.manpages [deleted file]
debian/openvswitch-controller.postinst [deleted file]
debian/openvswitch-controller.postrm [deleted file]
debian/openvswitch-switch.README.Debian
include/linux/openvswitch.h
include/openflow/automake.mk
include/openflow/nicira-ext.h
include/openflow/openflow-1.0.h
include/openflow/openflow-1.2.h
include/openflow/openflow-1.4.h [new file with mode: 0644]
include/openflow/openflow-common.h
include/openflow/openflow.h
lib/bfd.c
lib/bundle.c
lib/cfm.c
lib/cfm.h
lib/classifier.c
lib/classifier.h
lib/daemon.c
lib/daemon.man
lib/dpif-netdev.c
lib/dpif.c
lib/dpif.h
lib/flow.c
lib/flow.h
lib/learning-switch.c
lib/match.c
lib/match.h
lib/meta-flow.c
lib/meta-flow.h
lib/netdev-linux.c
lib/nx-match.c
lib/nx-match.h
lib/odp-execute.c
lib/odp-util.c
lib/ofp-actions.c
lib/ofp-actions.h
lib/ofp-msgs.h
lib/ofp-parse.c
lib/ofp-print.c
lib/ofp-util.c
lib/ofp-util.def
lib/ofp-util.h
lib/packets.c
lib/packets.h
lib/rconn.c
lib/ssl-bootstrap.man
lib/util.c
lib/util.h
manpages.mk
ofproto/connmgr.c
ofproto/connmgr.h
ofproto/netflow.c
ofproto/netflow.h
ofproto/ofproto-dpif-upcall.c
ofproto/ofproto-dpif-xlate.c
ofproto/ofproto-dpif-xlate.h
ofproto/ofproto-dpif.c
ofproto/ofproto-dpif.h
ofproto/ofproto-provider.h
ofproto/ofproto.c
ofproto/ofproto.h
ovsdb/ovsdb-client.1.in
ovsdb/ovsdb-server.1.in
ovsdb/ovsdb-server.c
ovsdb/ovsdb-tool.c
rhel/openvswitch-fedora.spec.in
rhel/openvswitch.spec.in
tests/.gitignore
tests/automake.mk
tests/cfm.at
tests/learn.at
tests/ofp-actions.at
tests/ofp-print.at
tests/ofproto-dpif.at
tests/ofproto.at
tests/ovs-ofctl.at
tests/test-classifier.c
tests/test-controller.8.in [moved from utilities/ovs-controller.8.in with 77% similarity]
tests/test-controller.c [moved from utilities/ovs-controller.c with 100% similarity]
utilities/.gitignore
utilities/automake.mk
utilities/bugtool/ovs-bugtool.in
utilities/ovs-ofctl.8.in
utilities/ovs-ofctl.c
utilities/ovs-pki.8.in
utilities/ovs-vsctl.8.in
vswitchd/bridge.c
vswitchd/ovs-vswitchd.8.in
vswitchd/vswitch.ovsschema
vswitchd/vswitch.xml
vtep/vtep-ctl.8.in
vtep/vtep.ovsschema
vtep/vtep.xml
xenserver/openvswitch-xen.spec.in

diff --git a/FAQ b/FAQ
index 0a081ff..cd3241a 100644 (file)
--- a/FAQ
+++ b/FAQ
@@ -89,10 +89,9 @@ A: Distributed vswitch applications (e.g., VMware vNetwork distributed
    environments: OpenFlow, which exposes flow-based forwarding state,
    and the OVSDB management protocol, which exposes switch port state.
    In addition to the switch implementation itself, Open vSwitch
-   includes tools (ovs-controller, ovs-ofctl, ovs-vsctl) that developers
-   can script and extend to provide distributed vswitch capabilities
-   that are closely integrated with their virtualization management
-   platform.
+   includes tools (ovs-ofctl, ovs-vsctl) that developers can script and
+   extend to provide distributed vswitch capabilities that are closely
+   integrated with their virtualization management platform.
 
 Q: Why doesn't Open vSwitch support distribution?
 
@@ -1255,9 +1254,11 @@ A: To debug network behavior problems, trace the path of a packet,
 
 Q: How do I make a flow drop packets?
 
-A: An empty set of actions causes a packet to be dropped.  You can
-   specify an empty set of actions with "actions=" on the ovs-ofctl
-   command line.  For example:
+A: To drop a packet is to receive it without forwarding it.  OpenFlow
+   explicitly specifies forwarding actions.  Thus, a flow with an
+   empty set of actions does not forward packets anywhere, causing
+   them to be dropped.  You can specify an empty set of actions with
+   "actions=" on the ovs-ofctl command line.  For example:
 
        ovs-ofctl add-flow br0 priority=65535,actions=
 
@@ -1269,6 +1270,9 @@ A: An empty set of actions causes a packet to be dropped.  You can
 
        ovs-ofctl add-flow br0 priority=65535,actions=drop
 
+   "drop" is not an action, either in OpenFlow or Open vSwitch.
+   Rather, it is only a way to say that there are no actions.
+
 Q: I added a flow to send packets out the ingress port, like this:
 
        ovs-ofctl add-flow br0 in_port=2,actions=2
index 8eb0c49..061af97 100644 (file)
@@ -115,7 +115,7 @@ that contains the PKI structure:
       % ovs-pki req+sign ctl controller
 
 ctl-privkey.pem and ctl-cert.pem would need to be copied to the
-controller for its use at runtime.  If you were to use ovs-controller,
+controller for its use at runtime.  If you were to use test-controller,
 the simple OpenFlow controller included with Open vSwitch, then the
 --private-key and --certificate options, respectively, would point to
 these files.
index e31788a..d6f5816 100644 (file)
@@ -167,7 +167,7 @@ controller on XenServer and, as a consequence of the step above that
 deletes all of the bridges at boot time, controller configuration only
 persists until XenServer reboot.  The configuration database manager
 can, however, configure controllers for bridges.  See the BUGS section
-of ovs-controller(8) for more information on this topic.
+of test-controller(8) for more information on this topic.
 
 * The Open vSwitch startup script automatically adds a firewall rule
 to allow GRE traffic. This rule is needed for the XenServer feature
diff --git a/NEWS b/NEWS
index 50f20ad..43a2079 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -11,6 +11,10 @@ Post-v2.0.0
    - Support for Linux kernels up to 3.11
    - ovs-dpctl:
      The "show" command also displays mega flow mask stats.
+   - ovs-controller has been renamed test-controller.  It is no longer
+     packaged or installed by default, because too many users assumed
+     incorrectly that ovs-controller was a necessary or desirable part
+     of an Open vSwitch deployment.
 
 
 v2.0.0 - 15 Oct 2013
index f419803..f978cb5 100644 (file)
@@ -78,23 +78,6 @@ probably incomplete.
       reasonable performance.
       [required for OF1.1+]
 
-    * Groups.
-
-        * Type all
-          [required for OF1.1+]
-
-        * Type select
-          [optional for OF1.1+]
-
-        * Type indirect
-          [required for OF1.1+]
-
-        * Type fast failover
-          [optional for OF1.1+]
-
-        * Statistics
-          [optional for OF1.1+]
-
 OpenFlow 1.2
 ------------
 
diff --git a/README b/README
index db1e3ef..63f2933 100644 (file)
--- a/README
+++ b/README
@@ -69,8 +69,6 @@ The main components of this distribution are:
 
 Open vSwitch also provides some tools:
 
-    * ovs-controller, a simple OpenFlow controller.
-
     * ovs-ofctl, a utility for querying and controlling OpenFlow
       switches and controllers.
 
index 5eaa141..448e7db 100755 (executable)
@@ -10,6 +10,7 @@ OFP10_VERSION = 0x01
 OFP11_VERSION = 0x02
 OFP12_VERSION = 0x03
 OFP13_VERSION = 0x04
+OFP14_VERSION = 0x05
 
 NX_VENDOR_ID = 0x00002320
 
@@ -24,10 +25,12 @@ version_map = {"1.0":     (OFP10_VERSION, OFP10_VERSION),
                "1.1":     (OFP11_VERSION, OFP11_VERSION),
                "1.2":     (OFP12_VERSION, OFP12_VERSION),
                "1.3":     (OFP13_VERSION, OFP13_VERSION),
+               "1.4":     (OFP14_VERSION, OFP14_VERSION),
                "1.0+":    (OFP10_VERSION, OFP13_VERSION),
                "1.1+":    (OFP11_VERSION, OFP13_VERSION),
                "1.2+":    (OFP12_VERSION, OFP13_VERSION),
                "1.3+":    (OFP13_VERSION, OFP13_VERSION),
+               "1.4+":    (OFP14_VERSION, OFP14_VERSION),
                "1.0-1.1": (OFP10_VERSION, OFP11_VERSION),
                "1.0-1.2": (OFP10_VERSION, OFP12_VERSION),
                "1.1-1.2": (OFP11_VERSION, OFP12_VERSION),
index b906681..5407833 100644 (file)
@@ -96,6 +96,7 @@ OVS_ENABLE_OPTION([-Wno-sign-compare])
 OVS_ENABLE_OPTION([-Wpointer-arith])
 OVS_ENABLE_OPTION([-Wdeclaration-after-statement])
 OVS_ENABLE_OPTION([-Wformat-security])
+OVS_ENABLE_OPTION([-Wno-format-zero-length])
 OVS_ENABLE_OPTION([-Wswitch-enum])
 OVS_ENABLE_OPTION([-Wunused-parameter])
 OVS_ENABLE_OPTION([-Wstrict-aliasing])
index 50ee6cd..e4aa672 100644 (file)
@@ -676,8 +676,9 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
                        goto nla_put_failure;
        }
 
-       if (flow_stats.tcp_flags &&
-           nla_put_u8(skb, OVS_FLOW_ATTR_TCP_FLAGS, flow_stats.tcp_flags))
+       if ((u8)ntohs(flow_stats.tcp_flags) &&
+           nla_put_u8(skb, OVS_FLOW_ATTR_TCP_FLAGS,
+                      (u8)ntohs(flow_stats.tcp_flags)))
                goto nla_put_failure;
 
        /* If OVS_FLOW_ATTR_ACTIONS doesn't fit, skip dumping the actions if
index 2193a33..57eb6b5 100644 (file)
@@ -60,20 +60,18 @@ u64 ovs_flow_used_time(unsigned long flow_jiffies)
        return cur_ms - idle_ms;
 }
 
-#define TCP_FLAGS_OFFSET 13
-#define TCP_FLAG_MASK 0x3f
+#define TCP_FLAGS_BE16(tp) (*(__be16 *)&tcp_flag_word(tp) & htons(0x0FFF))
 
 void ovs_flow_stats_update(struct sw_flow *flow, struct sk_buff *skb)
 {
        struct sw_flow_stats *stats = &flow->stats[smp_processor_id()];
-       u8 tcp_flags = 0;
+       __be16 tcp_flags = 0;
 
        if ((flow->key.eth.type == htons(ETH_P_IP) ||
             flow->key.eth.type == htons(ETH_P_IPV6)) &&
            flow->key.ip.proto == IPPROTO_TCP &&
            likely(skb->len >= skb_transport_offset(skb) + sizeof(struct tcphdr))) {
-               u8 *tcp = (u8 *)tcp_hdr(skb);
-               tcp_flags = *(tcp + TCP_FLAGS_OFFSET) & TCP_FLAG_MASK;
+               tcp_flags = TCP_FLAGS_BE16(tcp_hdr(skb));
        }
 
        spin_lock(&stats->lock);
@@ -484,6 +482,7 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key)
                                struct tcphdr *tcp = tcp_hdr(skb);
                                key->ipv4.tp.src = tcp->source;
                                key->ipv4.tp.dst = tcp->dest;
+                               key->ipv4.tp.flags = TCP_FLAGS_BE16(tcp);
                        }
                } else if (key->ip.proto == IPPROTO_UDP) {
                        if (udphdr_ok(skb)) {
@@ -552,6 +551,7 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key)
                                struct tcphdr *tcp = tcp_hdr(skb);
                                key->ipv6.tp.src = tcp->source;
                                key->ipv6.tp.dst = tcp->dest;
+                               key->ipv6.tp.flags = TCP_FLAGS_BE16(tcp);
                        }
                } else if (key->ip.proto == NEXTHDR_UDP) {
                        if (udphdr_ok(skb)) {
index d1ac85a..fdc309f 100644 (file)
@@ -96,6 +96,7 @@ struct sw_flow_key {
                                struct {
                                        __be16 src;             /* TCP/UDP/SCTP source port. */
                                        __be16 dst;             /* TCP/UDP/SCTP destination port. */
+                                       __be16 flags;           /* TCP flags. */
                                } tp;
                                struct {
                                        u8 sha[ETH_ALEN];       /* ARP source hardware address. */
@@ -112,6 +113,7 @@ struct sw_flow_key {
                        struct {
                                __be16 src;             /* TCP/UDP/SCTP source port. */
                                __be16 dst;             /* TCP/UDP/SCTP destination port. */
+                               __be16 flags;           /* TCP flags. */
                        } tp;
                        struct {
                                struct in6_addr target; /* ND target address. */
@@ -152,7 +154,7 @@ struct sw_flow_stats {
        u64 byte_count;                 /* Number of bytes matched. */
        unsigned long used;             /* Last used time (in jiffies). */
        spinlock_t lock;                /* Lock for atomic stats update. */
-       u8 tcp_flags;                   /* Union of seen TCP flags. */
+       __be16 tcp_flags;               /* Union of seen TCP flags. */
 } ____cacheline_aligned_in_smp;
 
 struct sw_flow {
index 515a9f6..75c72b3 100644 (file)
@@ -114,6 +114,7 @@ static bool match_validate(const struct sw_flow_match *match,
        mask_allowed &= ~((1ULL << OVS_KEY_ATTR_IPV4)
                        | (1ULL << OVS_KEY_ATTR_IPV6)
                        | (1ULL << OVS_KEY_ATTR_TCP)
+                       | (1ULL << OVS_KEY_ATTR_TCP_FLAGS)
                        | (1ULL << OVS_KEY_ATTR_UDP)
                        | (1ULL << OVS_KEY_ATTR_SCTP)
                        | (1ULL << OVS_KEY_ATTR_ICMP)
@@ -154,8 +155,11 @@ static bool match_validate(const struct sw_flow_match *match,
 
                        if (match->key->ip.proto == IPPROTO_TCP) {
                                key_expected |= 1ULL << OVS_KEY_ATTR_TCP;
-                               if (match->mask && (match->mask->key.ip.proto == 0xff))
+                               key_expected |= 1ULL << OVS_KEY_ATTR_TCP_FLAGS;
+                               if (match->mask && (match->mask->key.ip.proto == 0xff)) {
                                        mask_allowed |= 1ULL << OVS_KEY_ATTR_TCP;
+                                       mask_allowed |= 1ULL << OVS_KEY_ATTR_TCP_FLAGS;
+                               }
                        }
 
                        if (match->key->ip.proto == IPPROTO_ICMP) {
@@ -186,8 +190,11 @@ static bool match_validate(const struct sw_flow_match *match,
 
                        if (match->key->ip.proto == IPPROTO_TCP) {
                                key_expected |= 1ULL << OVS_KEY_ATTR_TCP;
-                               if (match->mask && (match->mask->key.ip.proto == 0xff))
+                               key_expected |= 1ULL << OVS_KEY_ATTR_TCP_FLAGS;
+                               if (match->mask && (match->mask->key.ip.proto == 0xff)) {
                                        mask_allowed |= 1ULL << OVS_KEY_ATTR_TCP;
+                                       mask_allowed |= 1ULL << OVS_KEY_ATTR_TCP_FLAGS;
+                               }
                        }
 
                        if (match->key->ip.proto == IPPROTO_ICMPV6) {
@@ -235,6 +242,7 @@ static const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = {
        [OVS_KEY_ATTR_IPV4] = sizeof(struct ovs_key_ipv4),
        [OVS_KEY_ATTR_IPV6] = sizeof(struct ovs_key_ipv6),
        [OVS_KEY_ATTR_TCP] = sizeof(struct ovs_key_tcp),
+       [OVS_KEY_ATTR_TCP_FLAGS] = sizeof(__be16),
        [OVS_KEY_ATTR_UDP] = sizeof(struct ovs_key_udp),
        [OVS_KEY_ATTR_SCTP] = sizeof(struct ovs_key_sctp),
        [OVS_KEY_ATTR_ICMP] = sizeof(struct ovs_key_icmp),
@@ -634,6 +642,19 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match,  u64 attrs,
                attrs &= ~(1ULL << OVS_KEY_ATTR_TCP);
        }
 
+       if (attrs & (1ULL << OVS_KEY_ATTR_TCP_FLAGS)) {
+               if (orig_attrs & (1ULL << OVS_KEY_ATTR_IPV4)) {
+                       SW_FLOW_KEY_PUT(match, ipv4.tp.flags,
+                                       nla_get_be16(a[OVS_KEY_ATTR_TCP_FLAGS]),
+                                       is_mask);
+               } else {
+                       SW_FLOW_KEY_PUT(match, ipv6.tp.flags,
+                                       nla_get_be16(a[OVS_KEY_ATTR_TCP_FLAGS]),
+                                       is_mask);
+               }
+               attrs &= ~(1ULL << OVS_KEY_ATTR_TCP_FLAGS);
+       }
+
        if (attrs & (1ULL << OVS_KEY_ATTR_UDP)) {
                const struct ovs_key_udp *udp_key;
 
@@ -1004,9 +1025,15 @@ int ovs_nla_put_flow(const struct sw_flow_key *swkey,
                        if (swkey->eth.type == htons(ETH_P_IP)) {
                                tcp_key->tcp_src = output->ipv4.tp.src;
                                tcp_key->tcp_dst = output->ipv4.tp.dst;
+                               if (nla_put_be16(skb, OVS_KEY_ATTR_TCP_FLAGS,
+                                                output->ipv4.tp.flags))
+                                       goto nla_put_failure;
                        } else if (swkey->eth.type == htons(ETH_P_IPV6)) {
                                tcp_key->tcp_src = output->ipv6.tp.src;
                                tcp_key->tcp_dst = output->ipv6.tp.dst;
+                               if (nla_put_be16(skb, OVS_KEY_ATTR_TCP_FLAGS,
+                                                output->ipv6.tp.flags))
+                                       goto nla_put_failure;
                        }
                } else if (swkey->ip.proto == IPPROTO_UDP) {
                        struct ovs_key_udp *udp_key;
index c2a7aa5..ddb14da 100644 (file)
@@ -426,7 +426,7 @@ static struct sw_flow *masked_flow_lookup(struct table_instance *ti,
        hash = flow_hash(&masked_key, key_start, key_end);
        head = find_bucket(ti, hash);
        hlist_for_each_entry_rcu(flow, head, hash_node[ti->node_ver]) {
-               if (flow->mask == mask &&
+               if (flow->mask == mask && flow->hash == hash &&
                    flow_cmp_masked_key(flow, &masked_key,
                                          key_start, key_end))
                        return flow;
index 5b7444b..1035fe8 100644 (file)
@@ -1,6 +1,8 @@
-#ifndef HAVE_DEV_DISABLE_LRO
-
+#include <linux/if_bridge.h>
 #include <linux/netdevice.h>
+#include <linux/version.h>
+
+#ifndef HAVE_DEV_DISABLE_LRO
 
 #ifdef NETIF_F_LRO
 #include <linux/ethtool.h>
@@ -30,3 +32,60 @@ void dev_disable_lro(struct net_device *dev) { }
 #endif /* NETIF_F_LRO */
 
 #endif /* HAVE_DEV_DISABLE_LRO */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) || \
+    defined HAVE_RHEL_OVS_HOOK
+
+static int nr_bridges;
+
+#ifdef HAVE_RHEL_OVS_HOOK
+int netdev_rx_handler_register(struct net_device *dev,
+                              openvswitch_handle_frame_hook_t *hook,
+                              void *rx_handler_data)
+{
+       nr_bridges++;
+       rcu_assign_pointer(dev->ax25_ptr, rx_handler_data);
+
+       if (nr_bridges == 1)
+               rcu_assign_pointer(openvswitch_handle_frame_hook, hook);
+       return 0;
+}
+#else
+
+int netdev_rx_handler_register(struct net_device *dev,
+                              struct sk_buff *(*hook)(struct net_bridge_port *p,
+                                                      struct sk_buff *skb),
+                              void *rx_handler_data)
+{
+       nr_bridges++;
+       if (dev->br_port)
+               return -EBUSY;
+
+       rcu_assign_pointer(dev->br_port, rx_handler_data);
+
+       if (nr_bridges == 1)
+               br_handle_frame_hook = hook;
+       return 0;
+}
+#endif
+
+void netdev_rx_handler_unregister(struct net_device *dev)
+{
+       nr_bridges--;
+#ifdef HAVE_RHEL_OVS_HOOK
+       rcu_assign_pointer(dev->ax25_ptr, NULL);
+
+       if (nr_bridges)
+               return;
+
+       rcu_assign_pointer(openvswitch_handle_frame_hook, NULL);
+#else
+       rcu_assign_pointer(dev->br_port, NULL);
+
+       if (nr_bridges)
+               return;
+
+       br_handle_frame_hook = NULL;
+#endif
+}
+#endif
index c5c366b..b303f39 100644 (file)
@@ -2,6 +2,7 @@
 #define __LINUX_NETDEVICE_WRAPPER_H 1
 
 #include_next <linux/netdevice.h>
+#include <linux/if_bridge.h>
 
 struct net;
 
@@ -11,11 +12,6 @@ struct net;
 #define to_net_dev(class) container_of(class, struct net_device, NETDEV_DEV_MEMBER)
 #endif
 
-#ifdef HAVE_RHEL_OVS_HOOK
-extern struct sk_buff *(*openvswitch_handle_frame_hook)(struct sk_buff *skb);
-extern int nr_bridges;
-#endif
-
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
 extern void unregister_netdevice_queue(struct net_device *dev,
                                        struct list_head *head);
@@ -28,32 +24,23 @@ extern void dev_disable_lro(struct net_device *dev);
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) || \
     defined HAVE_RHEL_OVS_HOOK
-static inline int netdev_rx_handler_register(struct net_device *dev,
-                                            void *rx_handler,
-                                            void *rx_handler_data)
-{
-#ifdef HAVE_RHEL_OVS_HOOK
-       rcu_assign_pointer(dev->ax25_ptr, rx_handler_data);
-       nr_bridges++;
-       rcu_assign_pointer(openvswitch_handle_frame_hook, rx_handler);
-#else
-       if (dev->br_port)
-               return -EBUSY;
-       rcu_assign_pointer(dev->br_port, rx_handler_data);
-#endif
-       return 0;
-}
-static inline void netdev_rx_handler_unregister(struct net_device *dev)
-{
+
 #ifdef HAVE_RHEL_OVS_HOOK
-       rcu_assign_pointer(dev->ax25_ptr, NULL);
+typedef struct sk_buff *(openvswitch_handle_frame_hook_t)(struct sk_buff *skb);
+extern openvswitch_handle_frame_hook_t *openvswitch_handle_frame_hook;
 
-       if (--nr_bridges <= 0)
-               rcu_assign_pointer(openvswitch_handle_frame_hook, NULL);
+int netdev_rx_handler_register(struct net_device *dev,
+                              openvswitch_handle_frame_hook_t *hook,
+                              void *rx_handler_data);
 #else
-       rcu_assign_pointer(dev->br_port, NULL);
+
+int netdev_rx_handler_register(struct net_device *dev,
+                              struct sk_buff *(*netdev_hook)(struct net_bridge_port *p,
+                                                            struct sk_buff *skb),
+                              void *rx_handler_data);
 #endif
-}
+
+void netdev_rx_handler_unregister(struct net_device *dev);
 #endif
 
 #ifndef HAVE_DEV_GET_BY_INDEX_RCU
index 248066d..3d28a9b 100644 (file)
@@ -1,10 +1,6 @@
 #include <linux/netdevice.h>
 #include <linux/if_vlan.h>
 
-#ifdef HAVE_RHEL_OVS_HOOK
-int nr_bridges = 0;
-#endif
-
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)
 #ifndef HAVE_CAN_CHECKSUM_PROTOCOL
 static bool can_checksum_protocol(unsigned long features, __be16 protocol)
index 738710e..637d712 100644 (file)
@@ -155,7 +155,7 @@ static void do_setup(struct net_device *netdev)
        netdev->tx_queue_len = 0;
 
        netdev->features = NETIF_F_LLTX | NETIF_F_SG | NETIF_F_FRAGLIST |
-                          NETIF_F_HIGHDMA | NETIF_F_HW_CSUM | NETIF_F_TSO;
+                          NETIF_F_HIGHDMA | NETIF_F_HW_CSUM | NETIF_F_GSO_SOFTWARE;
 
        netdev->vlan_features = netdev->features;
        netdev->features |= NETIF_F_HW_VLAN_CTAG_TX;
index 7780386..c15923b 100644 (file)
@@ -84,36 +84,6 @@ static struct sk_buff *netdev_frame_hook(struct net_bridge_port *p,
 #error
 #endif
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) || \
-    defined HAVE_RHEL_OVS_HOOK
-static int netdev_init(void) { return 0; }
-static void netdev_exit(void) { }
-#else
-static int port_count;
-
-static void netdev_init(void)
-{
-       port_count++;
-       if (port_count > 1)
-               return;
-
-       /* Hook into callback used by the bridge to intercept packets.
-        * Parasites we are. */
-       br_handle_frame_hook = netdev_frame_hook;
-
-       return;
-}
-
-static void netdev_exit(void)
-{
-       port_count--;
-       if (port_count > 0)
-               return;
-
-       br_handle_frame_hook = NULL;
-}
-#endif
-
 static struct net_device *get_dpdev(struct datapath *dp)
 {
        struct vport *local;
@@ -166,7 +136,6 @@ static struct vport *netdev_create(const struct vport_parms *parms)
        netdev_vport->dev->priv_flags |= IFF_OVS_DATAPATH;
        rtnl_unlock();
 
-       netdev_init();
        return vport;
 
 error_master_upper_dev_unlink:
@@ -206,8 +175,6 @@ static void netdev_destroy(struct vport *vport)
 {
        struct netdev_vport *netdev_vport = netdev_vport_priv(vport);
 
-       netdev_exit();
-
        rtnl_lock();
        if (ovs_netdev_get_vport(netdev_vport->dev))
                ovs_netdev_detach_dev(vport);
index 1c5e09f..c1aa059 100644 (file)
@@ -8,7 +8,6 @@
 /openvswitch
 /openvswitch-common
 /openvswitch-common.copyright
-/openvswitch-controller
 /openvswitch-datapath-source
 /openvswitch-datapath-dkms
 /openvswitch-dbg
index 33d7282..59db61d 100644 (file)
@@ -11,14 +11,6 @@ EXTRA_DIST += \
        debian/openvswitch-common.docs \
        debian/openvswitch-common.install \
        debian/openvswitch-common.manpages \
-       debian/openvswitch-controller.README.Debian \
-       debian/openvswitch-controller.default \
-       debian/openvswitch-controller.dirs \
-       debian/openvswitch-controller.init \
-       debian/openvswitch-controller.install \
-       debian/openvswitch-controller.manpages \
-       debian/openvswitch-controller.postinst \
-       debian/openvswitch-controller.postrm \
        debian/openvswitch-datapath-module-_KVERS_.postinst.modules.in \
        debian/openvswitch-datapath-dkms.postinst \
        debian/openvswitch-datapath-dkms.prerm \
index aea2b7a..be1a5a5 100644 (file)
@@ -1,7 +1,10 @@
 openvswitch (2.0.90-1) unstable; urgency=low
    [ Open vSwitch team ]
+   * The openvswitch-controller package has been removed, because too many
+     users assumed incorrectly that ovs-controller was a necessary or
+     desirable part of an Open vSwitch deployment.
    * New upstream version
-    - Nothing yet!  Try NEWS...
+    - Try NEWS for more details...
 
  -- Open vSwitch team <dev@openvswitch.org>  Wed, 28 Aug 2013 16:17:38 -0700
 
index 215b9ef..4c439b6 100644 (file)
@@ -60,8 +60,7 @@ Description: Open vSwitch common components
  to support distribution across multiple physical servers similar to
  VMware's vNetwork distributed vswitch or Cisco's Nexus 1000V.
  .
- openvswitch-common provides components required by both openvswitch-switch
- and openvswitch-controller.
+ openvswitch-common provides components required by both openvswitch-switch.
 
 Package: openvswitch-switch
 Architecture: linux-any
@@ -118,30 +117,12 @@ Description: Open vSwitch public key infrastructure dependency package
  Open vSwitch switches and controllers, reducing the risk of
  man-in-the-middle attacks on the Open vSwitch network infrastructure.
 
-Package: openvswitch-controller
-Architecture: linux-any
-Depends:
- ${shlibs:Depends}, openvswitch-common (= ${binary:Version}),
- openvswitch-pki (= ${source:Version}), ${misc:Depends}
-Description: Open vSwitch controller implementation
- Open vSwitch is a production quality, multilayer, software-based,
- Ethernet virtual switch. It is designed to enable massive network
- automation through programmatic extension, while still supporting
- standard management interfaces and protocols (e.g. NetFlow, IPFIX,
- sFlow, SPAN, RSPAN, CLI, LACP, 802.1ag). In addition, it is designed
- to support distribution across multiple physical servers similar to
- VMware's vNetwork distributed vswitch or Cisco's Nexus 1000V.
- .
- The Open vSwitch controller enables OpenFlow switches that connect to it
- to act as MAC-learning Ethernet switches.
-
 Package: openvswitch-dbg
 Section: debug
 Architecture: linux-any
 Depends:
  ${shlibs:Depends}, ${misc:Depends},
  openvswitch-common (= ${binary:Version}),
- openvswitch-controller (= ${binary:Version}),
  openvswitch-switch (= ${binary:Version})
 Description: Debug symbols for Open vSwitch packages
  Open vSwitch is a production quality, multilayer, software-based,
index e456533..a621100 100755 (executable)
@@ -66,6 +66,12 @@ if [ "${MODE}" = "start" ]; then
                     ifconfig "${slave}" up
                 done
                 ;;
+        OVSTunnel)
+                ovs_vsctl -- --may-exist add-port "${IF_OVS_BRIDGE}"\
+                    "${IFACE}" ${IF_OVS_OPTIONS} -- set Interface "${IFACE}" \
+                    type=${IF_OVS_TUNNEL_TYPE} ${IF_OVS_TUNNEL_OPTIONS} \
+                    ${OVS_EXTRA+-- $OVS_EXTRA}
+                ;;
         *)
                 exit 0
                 ;;
@@ -79,7 +85,7 @@ elif [ "${MODE}" = "stop" ]; then
 
                 ovs_vsctl -- --if-exists del-br "${IFACE}"
                 ;;
-        OVSPort|OVSIntPort|OVSBond)
+        OVSPort|OVSIntPort|OVSBond|OVSTunnel)
                 ovs_vsctl -- --if-exists del-port "${IF_OVS_BRIDGE}" "${IFACE}"
                 ;;
         *)
diff --git a/debian/openvswitch-controller.README.Debian b/debian/openvswitch-controller.README.Debian
deleted file mode 100644 (file)
index 0c5fcba..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-README.Debian for openvswitch-controller
--------------------------------------
-
-* To (re)configure the controller, edit /etc/default/openvswitch-controller
-  and run "/etc/init.d/openvswitch-controller restart".
-
- -- Ben Pfaff <blp@nicira.com>, Fri,  4 Mar 2011 14:28:53 -0800
diff --git a/debian/openvswitch-controller.default b/debian/openvswitch-controller.default
deleted file mode 100644 (file)
index a274f7e..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-# This is a POSIX shell fragment                -*- sh -*-
-
-# LISTEN: What OpenFlow connection methods should the controller listen on?
-#
-# This is a space-delimited list of connection methods:
-#
-# * "pssl:[PORT]": Listen for SSL connections on the specified PORT
-#   (default: 6633).  The private key, certificate, and CA certificate
-#   must be specified below.
-#
-# * "ptcp:[PORT]": Listen for TCP connections on the specified PORT
-#   (default: 6633).  Not recommended for security reasons.
-#
-LISTEN="pssl:"
-
-# PRIVKEY: Name of file containing controller's private key.
-# Required if SSL enabled.
-PRIVKEY=/etc/openvswitch-controller/privkey.pem
-
-# CERT: Name of file containing certificate for private key.
-# Required if SSL enabled.
-CERT=/etc/openvswitch-controller/cert.pem
-
-# CACERT: Name of file containing switch CA certificate.
-# Required if SSL enabled.
-CACERT=/etc/openvswitch-controller/cacert.pem
-
-# Additional options to pass to controller, e.g. "--hub"
-DAEMON_OPTS=""
diff --git a/debian/openvswitch-controller.dirs b/debian/openvswitch-controller.dirs
deleted file mode 100644 (file)
index 4ada77c..0000000
+++ /dev/null
@@ -1 +0,0 @@
-etc/openvswitch-controller
diff --git a/debian/openvswitch-controller.init b/debian/openvswitch-controller.init
deleted file mode 100755 (executable)
index 44d2c87..0000000
+++ /dev/null
@@ -1,278 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2011 Nicira, Inc.
-# Copyright (c) 2007, 2009 Javier Fernandez-Sanguino <jfs@debian.org>
-#
-# This is free software; you may redistribute it and/or modify
-# it under the terms of the GNU General Public License as
-# published by the Free Software Foundation; either version 2,
-# or (at your option) any later version.
-#
-# This is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License with
-# the Debian operating system, in /usr/share/common-licenses/GPL;  if
-# not, write to the Free Software Foundation, Inc., 59 Temple Place,
-# Suite 330, Boston, MA 02111-1307 USA
-#
-### BEGIN INIT INFO
-# Provides:          openvswitch-controller
-# Required-Start:    $network $local_fs $remote_fs
-# Required-Stop:     $remote_fs
-# Should-Start:      $named
-# Should-Stop:       
-# Default-Start:     2 3 4 5
-# Default-Stop:      0 1 6
-# Short-Description: Open vSwitch controller
-# Description:       The Open vSwitch controller enables OpenFlow switches that connect to it
-#                    to act as MAC-learning Ethernet switches.
-### END INIT INFO
-
-PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
-
-DAEMON=/usr/bin/ovs-controller # Introduce the server's location here
-NAME=ovs-controller         # Introduce the short server's name here
-DESC=ovs-controller         # Introduce a short description here
-LOGDIR=/var/log/openvswitch # Log directory to use
-
-PIDFILE=/var/run/openvswitch/$NAME.pid 
-
-test -x $DAEMON || exit 0
-
-. /lib/lsb/init-functions
-
-# Default options, these can be overriden by the information
-# at /etc/default/openvswitch-controller
-DAEMON_OPTS=""          # Additional options given to the server 
-
-DODTIME=10              # Time to wait for the server to die, in seconds
-                        # If this value is set too low you might not
-                        # let some servers to die gracefully and
-                        # 'restart' will not work
-                        
-LOGFILE=$LOGDIR/$NAME.log  # Server logfile
-#DAEMONUSER=            # User to run the daemons as. If this value
-                        # is set start-stop-daemon will chuid the server
-
-# Include defaults if available
-default=/etc/default/openvswitch-controller
-if [ -f $default ] ; then
-    . $default
-fi
-
-# Check that the user exists (if we set a user)
-# Does the user exist?
-if [ -n "$DAEMONUSER" ] ; then
-    if getent passwd | grep -q "^$DAEMONUSER:"; then
-        # Obtain the uid and gid
-        DAEMONUID=`getent passwd |grep "^$DAEMONUSER:" | awk -F : '{print $3}'`
-        DAEMONGID=`getent passwd |grep "^$DAEMONUSER:" | awk -F : '{print $4}'`
-    else
-        log_failure_msg "The user $DAEMONUSER, required to run $NAME does not exist."
-        exit 1
-    fi
-fi
-
-
-set -e
-
-running_pid() {
-# Check if a given process pid's cmdline matches a given name
-    pid=$1
-    name=$2
-    [ -z "$pid" ] && return 1 
-    [ ! -d /proc/$pid ] &&  return 1
-    cmd=`cat /proc/$pid/cmdline | tr "\000" "\n"|head -n 1 |cut -d : -f 1`
-    # Is this the expected server
-    [ "$cmd" != "$name" ] &&  return 1
-    return 0
-}
-
-running() {
-# Check if the process is running looking at /proc
-# (works for all users)
-
-    # No pidfile, probably no daemon present
-    [ ! -f "$PIDFILE" ] && return 1
-    pid=`cat $PIDFILE`
-    running_pid $pid $DAEMON || return 1
-    return 0
-}
-
-start_server() {
-    if [ -z "$LISTEN" ]; then
-        echo "$default: No connection methods configured, controller disabled" >&2
-        exit 0
-    fi
-
-    if [ ! -d /var/run/openvswitch ]; then
-        install -d -m 755 -o root -g root /var/run/openvswitch
-    fi
-
-    SSL_OPTS=
-    case $LISTEN in
-        *ssl*)
-            : ${PRIVKEY:=/etc/openvswitch-controller/privkey.pem}
-            : ${CERT:=/etc/openvswitch-controller/cert.pem}
-            : ${CACERT:=/etc/openvswitch-controller/cacert.pem}
-            if test ! -e "$PRIVKEY" || test ! -e "$CERT" ||
-                test ! -e "$CACERT"; then
-                if test ! -e "$PRIVKEY"; then
-                    echo "$PRIVKEY: private key missing" >&2
-                fi
-                if test ! -e "$CERT"; then
-                    echo "$CERT: certificate for private key missing" >&2
-                fi
-                if test ! -e "$CACERT"; then
-                    echo "$CACERT: CA certificate missing" >&2
-                fi
-                exit 1
-            fi
-            SSL_OPTS="--private-key=$PRIVKEY --certificate=$CERT --ca-cert=$CACERT"
-            ;;
-    esac
-
-# Start the process using the wrapper
-        if [ -z "$DAEMONUSER" ] ; then
-            start-stop-daemon --start --pidfile $PIDFILE \
-                        --exec $DAEMON -- --detach --pidfile=$PIDFILE \
-                        $LISTEN $DAEMON_OPTS $SSL_OPTS
-            errcode=$?
-        else
-# if we are using a daemonuser then change the user id
-            start-stop-daemon --start --quiet --pidfile $PIDFILE \
-                        --chuid $DAEMONUSER --exec $DAEMON -- \
-                        --detach --pidfile=$PIDFILE $LISTEN $DAEMON_OPTS \
-                        $SSL_OPTS
-            errcode=$?
-        fi
-        return $errcode
-}
-
-stop_server() {
-# Stop the process using the wrapper
-        if [ -z "$DAEMONUSER" ] ; then
-            start-stop-daemon --stop --quiet --pidfile $PIDFILE \
-                        --exec $DAEMON
-            errcode=$?
-        else
-# if we are using a daemonuser then look for process that match
-            start-stop-daemon --stop --quiet --pidfile $PIDFILE \
-                        --user $DAEMONUSER --exec $DAEMON
-            errcode=$?
-        fi
-
-        return $errcode
-}
-
-reload_server() {
-    [ ! -f "$PIDFILE" ] && return 1
-    pid=`cat $PIDFILE` # This is the daemon's pid
-    # Send a SIGHUP
-    kill -1 $pid
-    return $?
-}
-
-force_stop() {
-# Force the process to die killing it manually
-    [ ! -e "$PIDFILE" ] && return
-    if running ; then
-        kill -15 $pid
-        # Is it really dead?
-        sleep "$DODTIME"
-        if running ; then
-            kill -9 $pid
-            sleep "$DODTIME"
-            if running ; then
-                echo "Cannot kill $NAME (pid=$pid)!"
-                exit 1
-            fi
-        fi
-    fi
-    rm -f $PIDFILE
-}
-
-
-case "$1" in
-  start)
-        log_daemon_msg "Starting $DESC " "$NAME"
-        # Check if it's running first
-        if running ;  then
-            log_progress_msg "apparently already running"
-            log_end_msg 0
-            exit 0
-        fi
-        if start_server && running ;  then
-            # It's ok, the server started and is running
-            log_end_msg 0
-        else
-            # Either we could not start it or it is not running
-            # after we did
-            # NOTE: Some servers might die some time after they start,
-            # this code does not try to detect this and might give
-            # a false positive (use 'status' for that)
-            log_end_msg 1
-        fi
-        ;;
-  stop)
-        log_daemon_msg "Stopping $DESC" "$NAME"
-        if running ; then
-            # Only stop the server if we see it running
-            stop_server
-            log_end_msg $?
-        else
-            # If it's not running don't do anything
-            log_progress_msg "apparently not running"
-            log_end_msg 0
-            exit 0
-        fi
-        ;;
-  force-stop)
-        # First try to stop gracefully the program
-        $0 stop
-        if running; then
-            # If it's still running try to kill it more forcefully
-            log_daemon_msg "Stopping (force) $DESC" "$NAME"
-            force_stop
-            log_end_msg $?
-        fi
-        ;;
-  restart|force-reload)
-        log_daemon_msg "Restarting $DESC" "$NAME"
-        if running; then
-            stop_server
-            # Wait some sensible amount, some server need this.
-            [ -n "$DODTIME" ] && sleep $DODTIME
-        fi
-        start_server
-        running
-        log_end_msg $?
-        ;;
-  status)
-
-        log_daemon_msg "Checking status of $DESC" "$NAME"
-        if running ;  then
-            log_progress_msg "running"
-            log_end_msg 0
-        else
-            log_progress_msg "apparently not running"
-            log_end_msg 1
-            exit 1
-        fi
-        ;;
-  # Use this if the daemon cannot reload
-  reload)
-        log_warning_msg "Reloading $NAME daemon: not implemented, as the daemon"
-        log_warning_msg "cannot re-read the config file (use restart)."
-        ;;
-  *)
-        N=/etc/init.d/openvswitch-controller
-        echo "Usage: $N {start|stop|force-stop|restart|force-reload|status}" >&2
-        exit 1
-        ;;
-esac
-
-exit 0
diff --git a/debian/openvswitch-controller.install b/debian/openvswitch-controller.install
deleted file mode 100644 (file)
index a22ec45..0000000
+++ /dev/null
@@ -1 +0,0 @@
-usr/bin/ovs-controller
diff --git a/debian/openvswitch-controller.manpages b/debian/openvswitch-controller.manpages
deleted file mode 100644 (file)
index 6a9911e..0000000
+++ /dev/null
@@ -1 +0,0 @@
-_debian/utilities/ovs-controller.8
diff --git a/debian/openvswitch-controller.postinst b/debian/openvswitch-controller.postinst
deleted file mode 100755 (executable)
index 3073dc0..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-#!/bin/sh
-# postinst script for openvswitch-controller
-#
-# see: dh_installdeb(1)
-
-set -e
-
-# summary of how this script can be called:
-#        * <postinst> `configure' <most-recently-configured-version>
-#        * <old-postinst> `abort-upgrade' <new version>
-#        * <conflictor's-postinst> `abort-remove' `in-favour' <package>
-#          <new-version>
-#        * <postinst> `abort-remove'
-#        * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
-#          <failed-install-package> <version> `removing'
-#          <conflicting-package> <version>
-# for details, see http://www.debian.org/doc/debian-policy/ or
-# the debian-policy package
-
-
-case "$1" in
-    configure)
-        cd /etc/openvswitch-controller
-
-        # If cacert.pem is a symlink to the old location for cacert.pem,
-        # remove it so that we can symlink it to the new location.
-        if test -h cacert.pem && \
-           test X"`readlink cacert.pem`" = X/usr/share/openvswitch/pki/switchca/cacert.pem; then
-            rm -f cacert.pem
-        fi
-
-        if ! test -e cacert.pem; then
-            ln -s /var/lib/openvswitch/pki/switchca/cacert.pem cacert.pem
-        fi
-        if ! test -e privkey.pem || ! test -e cert.pem; then
-            oldumask=$(umask)
-            umask 077
-            ovs-pki req+sign tmp controller >/dev/null
-            mv tmp-privkey.pem privkey.pem
-            mv tmp-cert.pem cert.pem
-            mv tmp-req.pem req.pem
-            chmod go+r cert.pem req.pem
-            umask $oldumask
-        fi
-        ;;
-
-    abort-upgrade|abort-remove|abort-deconfigure)
-        ;;
-
-    *)
-        echo "postinst called with unknown argument \`$1'" >&2
-        exit 1
-        ;;
-esac
-
-#DEBHELPER#
-
-exit 0
-
-
diff --git a/debian/openvswitch-controller.postrm b/debian/openvswitch-controller.postrm
deleted file mode 100755 (executable)
index 42cb523..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/bin/sh
-# postrm script for openvswitch-controller
-#
-# see: dh_installdeb(1)
-
-set -e
-
-# summary of how this script can be called:
-#        * <postrm> `remove'
-#        * <postrm> `purge'
-#        * <old-postrm> `upgrade' <new-version>
-#        * <new-postrm> `failed-upgrade' <old-version>
-#        * <new-postrm> `abort-install'
-#        * <new-postrm> `abort-install' <old-version>
-#        * <new-postrm> `abort-upgrade' <old-version>
-#        * <disappearer's-postrm> `disappear' <overwriter>
-#          <overwriter-version>
-# for details, see http://www.debian.org/doc/debian-policy/ or
-# the debian-policy package
-
-
-case "$1" in
-    purge)
-        if cd /etc/openvswitch-controller; then
-            rm -f cacert.pem cert.pem privkey.pem req.pem
-            rm -f tmp-privkey.pem tmp-cert.pem tmp-req.pem
-        fi
-        ;;
-
-    remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
-        ;;
-
-    *)
-        echo "postrm called with unknown argument \`$1'" >&2
-        exit 1
-        ;;
-esac
-
-# dh_installdeb will replace this with shell code automatically
-# generated by other debhelper scripts.
-
-#DEBHELPER#
-
-exit 0
index 8617e77..a316003 100644 (file)
@@ -27,9 +27,9 @@ allow-br0 eth0
 
 The following OVS specific "command" options are supported:
 
-    - ovs_type: This can either be OVSBridge, OVSPort, OVSIntPort or OVSBond
-      depending on whether you configure a bridge, port, an internal port or
-      a bond. This is a required option.
+    - ovs_type: This can either be OVSBridge, OVSPort, OVSIntPort, OVSBond or
+      OVSTunnel depending on whether you configure a bridge, port, an internal
+      port, a bond or a tunnel. This is a required option.
 
     - ovs_ports: This option specifies all the ports that belong to a bridge.
 
@@ -39,6 +39,12 @@ The following OVS specific "command" options are supported:
     - ovs_bonds: This option specifies the list of physical interfaces to be
       bonded together.
 
+    - ovs_tunnel_type: For "OVSTunnel" interfaces, the type of the tunnel.
+      For example, "gre", "vxlan", etc.
+
+    - ovs_tunnel_options: For "OVSTunnel" interfaces, this field should be
+      used to specify the tunnel options like remote_ip, key, etc.
+
     - ovs_options: This option lets you add extra arguments to a ovs-vsctl
       command. See examples.
 
@@ -121,7 +127,23 @@ iface bond0 inet manual
     ovs_bonds eth2 eth3
     ovs_options bond_mode=balance-tcp lacp=active
 
-ex 6: Create and destroy bridges.
+ex 6: Tunnel.
+
+allow-ovs br1
+iface br1 inet static
+    address 192.168.1.1
+    netmask 255.255.255.0
+    ovs_type OVSBridge
+    ovs_ports gre1
+
+allow-br1 gre1
+iface gre1 inet manual
+    ovs_bridge br1
+    ovs_type OVSTunnel
+    ovs_tunnel_type gre
+    ovs_tunnel_options options:remote_ip=182.168.1.2 options:key=1
+
+ex 7: Create and destroy bridges.
 
 ifup --allow=ovs $list_of_bridges
 ifdown --allow=ovs $list_of_bridges
index f46d9d0..b429201 100644 (file)
@@ -294,6 +294,7 @@ enum ovs_key_attr {
        OVS_KEY_ATTR_SKB_MARK,  /* u32 skb mark */
        OVS_KEY_ATTR_TUNNEL,    /* Nested set of ovs_tunnel attributes */
        OVS_KEY_ATTR_SCTP,      /* struct ovs_key_sctp */
+       OVS_KEY_ATTR_TCP_FLAGS, /* be16 TCP flags. */
 
 #ifdef __KERNEL__
        OVS_KEY_ATTR_IPV4_TUNNEL,  /* struct ovs_key_ipv4_tunnel */
index 76a8f06..2938642 100644 (file)
@@ -4,6 +4,7 @@ noinst_HEADERS += \
        include/openflow/openflow-1.1.h \
        include/openflow/openflow-1.2.h \
        include/openflow/openflow-1.3.h \
+       include/openflow/openflow-1.4.h \
        include/openflow/openflow-common.h \
        include/openflow/openflow.h
 
@@ -20,6 +21,7 @@ HSTAMP_FILES = \
        include/openflow/openflow-1.1.hstamp \
        include/openflow/openflow-1.2.hstamp \
        include/openflow/openflow-1.3.hstamp \
+       include/openflow/openflow-1.4.hstamp \
        include/openflow/openflow-common.hstamp \
        include/openflow/openflow.hstamp
 CLEANFILES += $(HSTAMP_FILES)
@@ -37,6 +39,8 @@ include/openflow/openflow-1.3.hstamp: \
        include/openflow/openflow-common.h \
        include/openflow/openflow-1.1.h \
        include/openflow/openflow-1.2.h
+include/openflow/openflow-1.4.hstamp: \
+       include/openflow/openflow-1.4.h
 include/openflow/nicira-ext.hstamp: \
        include/openflow/openflow.h \
        include/openflow/openflow-common.h \
index 3362a49..22939f4 100644 (file)
@@ -312,6 +312,8 @@ enum nx_action_subtype {
     NXAST_STACK_PUSH,           /* struct nx_action_stack */
     NXAST_STACK_POP,            /* struct nx_action_stack */
     NXAST_SAMPLE,               /* struct nx_action_sample */
+    NXAST_SET_MPLS_LABEL,       /* struct nx_action_ttl */
+    NXAST_SET_MPLS_TC,          /* struct nx_action_ttl */
 };
 
 /* Header for Nicira-defined actions. */
@@ -492,6 +494,11 @@ OFP_ASSERT(sizeof(struct nx_action_pop_queue) == 16);
  *   - NXM_OF_TCP_DST
  *   - NXM_OF_UDP_SRC
  *   - NXM_OF_UDP_DST
+ *   - NXM_NX_ARP_SHA
+ *   - NXM_NX_ARP_THA
+ *   - NXM_OF_ARP_OP
+ *   - NXM_OF_ARP_SPA
+ *   - NXM_OF_ARP_TPA
  *     Modifying any of the above fields changes the corresponding packet
  *     header.
  *
@@ -1784,6 +1791,18 @@ OFP_ASSERT(sizeof(struct nx_action_output_reg) == 24);
 #define NXM_NX_PKT_MARK   NXM_HEADER  (0x0001, 33, 4)
 #define NXM_NX_PKT_MARK_W NXM_HEADER_W(0x0001, 33, 4)
 
+/* The flags in the TCP header.
+*
+* Prereqs:
+*   NXM_OF_ETH_TYPE must be either 0x0800 or 0x86dd.
+*   NXM_OF_IP_PROTO must match 6 exactly.
+*
+* Format: 16-bit integer with 4 most-significant bits forced to 0.
+*
+* Masking: Bits 0-11 fully maskable. */
+#define NXM_NX_TCP_FLAGS   NXM_HEADER  (0x0001, 34, 2)
+#define NXM_NX_TCP_FLAGS_W NXM_HEADER_W(0x0001, 34, 2)
+
 /* ## --------------------- ## */
 /* ## Requests and replies. ## */
 /* ## --------------------- ## */
@@ -2261,6 +2280,28 @@ struct nx_action_pop_mpls {
 };
 OFP_ASSERT(sizeof(struct nx_action_pop_mpls) == 16);
 
+/* Action structure for NXAST_SET_MPLS_LABEL. */
+struct nx_action_mpls_label {
+    ovs_be16 type;                  /* OFPAT_VENDOR. */
+    ovs_be16 len;                   /* Length is 8. */
+    ovs_be32 vendor;                /* NX_VENDOR_ID. */
+    ovs_be16 subtype;               /* NXAST_SET_MPLS_LABEL. */
+    uint8_t  zeros[2];               /* Must be zero. */
+    ovs_be32 label;                 /* LABEL */
+};
+OFP_ASSERT(sizeof(struct nx_action_mpls_label) == 16);
+
+/* Action structure for NXAST_SET_MPLS_TC. */
+struct nx_action_mpls_tc {
+    ovs_be16 type;                  /* OFPAT_VENDOR. */
+    ovs_be16 len;                   /* Length is 8. */
+    ovs_be32 vendor;                /* NX_VENDOR_ID. */
+    ovs_be16 subtype;               /* NXAST_SET_MPLS_TC. */
+    uint8_t  tc;                    /* TC */
+    uint8_t  pad[5];
+};
+OFP_ASSERT(sizeof(struct nx_action_mpls_tc) == 16);
+
 /* Action structure for NXAST_SET_MPLS_TTL. */
 struct nx_action_mpls_ttl {
     ovs_be16 type;                  /* OFPAT_VENDOR. */
index 34d97c4..002c75d 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.
@@ -128,6 +128,15 @@ struct ofp10_port_mod {
 };
 OFP_ASSERT(sizeof(struct ofp10_port_mod) == 24);
 
+struct ofp10_packet_queue {
+    ovs_be32 queue_id;          /* id for the specific queue. */
+    ovs_be16 len;               /* Length in bytes of this queue desc. */
+    uint8_t pad[2];             /* 64-bit alignment. */
+    /* Followed by any number of queue properties expressed using
+     * ofp_queue_prop_header, to fill out a total of 'len' bytes. */
+};
+OFP_ASSERT(sizeof(struct ofp10_packet_queue) == 8);
+
 /* Query for port queue configuration. */
 struct ofp10_queue_get_config_request {
     ovs_be16 port;          /* Port to be queried. Should refer
index 541b143..694cd55 100644 (file)
@@ -251,22 +251,16 @@ enum ofp12_capabilities {
     OFPC12_PORT_BLOCKED   = 1 << 8   /* Switch will block looping ports. */
 };
 
-/* OpenFlow 1.2 specific types
- * (struct ofp11_stats_request/reply, member type). */
-enum ofp12_stats_types {
-    /* Group features.
-     * The request body is empty.
-     * The reply body is struct ofp12_group_features_stats. */
-    OFPST12_GROUP_FEATURES = 8
-};
-
-/* OpenFlow 1.2 specific properties
- * (struct ofp_queue_prop_header member property). */
-enum ofp12_queue_properties {
-    OFPQT12_MIN_RATE = 1,         /* Minimum datarate guaranteed. */
-    OFPQT12_MAX_RATE = 2,         /* Maximum datarate. */
-    OFPQT12_EXPERIMENTER = 0xffff /* Experimenter defined property. */
+/* Full description for a queue. */
+struct ofp12_packet_queue {
+    ovs_be32 queue_id;     /* id for the specific queue. */
+    ovs_be32 port;         /* Port this queue is attached to. */
+    ovs_be16 len;          /* Length in bytes of this queue desc. */
+    uint8_t pad[6];        /* 64-bit alignment. */
+    /* Followed by any number of queue properties expressed using
+     * ofp_queue_prop_header, to fill out a total of 'len' bytes. */
 };
+OFP_ASSERT(sizeof(struct ofp12_packet_queue) == 16);
 
 /* Body of reply to OFPST_TABLE request. */
 struct ofp12_table_stats {
diff --git a/include/openflow/openflow-1.4.h b/include/openflow/openflow-1.4.h
new file mode 100644 (file)
index 0000000..332a0d3
--- /dev/null
@@ -0,0 +1,156 @@
+/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+* Junior University
+* Copyright (c) 2011, 2012 Open Networking Foundation
+*
+* We are making the OpenFlow specification and associated documentation
+* (Software) available for public use and benefit with the expectation
+* that others will use, modify and enhance the Software and contribute
+* those enhancements back to the community. However, since we would
+* like to make the Software available for broadest use, with as few
+* restrictions as possible permission is hereby granted, free of
+* charge, to any person obtaining a copy of this Software to deal in
+* the Software under the copyrights without restriction, including
+* without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+*
+* The above copyright notice and this permission notice shall be
+* included in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+* NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+* SOFTWARE.
+*
+* The name and trademarks of copyright holder(s) may NOT be used in
+* advertising or publicity pertaining to the Software or any
+* derivatives without specific, written prior permission.
+*/
+
+/* OpenFlow: protocol between controller and datapath. */
+
+#ifndef OPENFLOW_14_H
+#define OPENFLOW_14_H 1
+
+#include "openflow/openflow-1.3.h"
+/*
+ * OpenFlow 1.4 is more extensible by using TLV structures
+ */
+
+/* Common header for all async config Properties */
+struct ofp14_async_config_prop_header {
+    ovs_be16    type;       /* One of OFPACPT_*. */
+    ovs_be16    length;     /* Length in bytes of this property. */
+};
+OFP_ASSERT(sizeof(struct ofp14_async_config_prop_header) == 4);
+
+/* Asynchronous message configuration.
+ * OFPT_GET_ASYNC_REPLY or OFPT_SET_ASYNC.
+ */
+struct ofp14_async_config {
+    struct ofp_header header;
+    /* Async config Property list - 0 or more */
+    struct ofp14_async_config_prop_header properties[0];
+};
+OFP_ASSERT(sizeof(struct ofp14_async_config) == 8);
+
+/* Async Config property types.
+* Low order bit cleared indicates a property for the slave role.
+* Low order bit set indicates a property for the master/equal role.
+*/
+enum ofp14_async_config_prop_type {
+    OFPACPT_PACKET_IN_SLAVE       = 0, /* Packet-in mask for slave. */
+    OFPACPT_PACKET_IN_MASTER      = 1, /* Packet-in mask for master. */
+    OFPACPT_PORT_STATUS_SLAVE     = 2, /* Port-status mask for slave. */
+    OFPACPT_PORT_STATUS_MASTER    = 3, /* Port-status mask for master. */
+    OFPACPT_FLOW_REMOVED_SLAVE    = 4, /* Flow removed mask for slave. */
+    OFPACPT_FLOW_REMOVED_MASTER   = 5, /* Flow removed mask for master. */
+    OFPACPT_ROLE_STATUS_SLAVE     = 6, /* Role status mask for slave. */
+    OFPACPT_ROLE_STATUS_MASTER    = 7, /* Role status mask for master. */
+    OFPACPT_TABLE_STATUS_SLAVE    = 8, /* Table status mask for slave. */
+    OFPACPT_TABLE_STATUS_MASTER   = 9, /* Table status mask for master. */
+    OFPACPT_REQUESTFORWARD_SLAVE  = 10, /* RequestForward mask for slave. */
+    OFPACPT_REQUESTFORWARD_MASTER = 11, /* RequestForward mask for master. */
+    OFPTFPT_EXPERIMENTER_SLAVE    = 0xFFFE, /* Experimenter for slave. */
+    OFPTFPT_EXPERIMENTER_MASTER   = 0xFFFF, /* Experimenter for master. */
+};
+
+/* Various reason based properties */
+struct ofp14_async_config_prop_reasons {
+    /* 'type' is one of OFPACPT_PACKET_IN_*, OFPACPT_PORT_STATUS_*,
+     * OFPACPT_FLOW_REMOVED_*, OFPACPT_ROLE_STATUS_*,
+     * OFPACPT_TABLE_STATUS_*, OFPACPT_REQUESTFORWARD_*. */
+    ovs_be16    type;
+    ovs_be16    length; /* Length in bytes of this property. */
+    ovs_be32    mask;   /* Bitmasks of reason values. */
+};
+OFP_ASSERT(sizeof(struct ofp14_async_config_prop_reasons) == 8);
+
+/* Experimenter async config property */
+struct ofp14_async_config_prop_experimenter {
+    ovs_be16        type;       /* One of OFPTFPT_EXPERIMENTER_SLAVE,
+                                   OFPTFPT_EXPERIMENTER_MASTER. */
+    ovs_be16        length;     /* Length in bytes of this property. */
+    ovs_be32        experimenter;  /* Experimenter ID which takes the same
+                                      form as in struct
+                                      ofp_experimenter_header. */
+    ovs_be32        exp_type;      /* Experimenter defined. */
+    /* Followed by:
+     *   - Exactly (length - 12) bytes containing the experimenter data, then
+     *   - Exactly (length + 7)/8*8 - (length) (between 0 and 7)
+     *     bytes of all-zero bytes */
+};
+OFP_ASSERT(sizeof(struct ofp14_async_config_prop_experimenter) == 12);
+
+/* Common header for all Role Properties */
+struct ofp14_role_prop_header {
+    ovs_be16 type;   /* One of OFPRPT_*. */
+    ovs_be16 length; /* Length in bytes of this property. */
+};
+OFP_ASSERT(sizeof(struct ofp14_role_prop_header) == 4);
+
+/* Role status event message. */
+struct ofp14_role_status {
+    ovs_be32 role;              /* One of OFPCR_ROLE_*. */
+    uint8_t  reason;            /* One of OFPCRR_*. */
+    uint8_t  pad[3];            /* Align to 64 bits. */
+    ovs_be64 generation_id;     /* Master Election Generation Id */
+
+    /* Followed by a list of struct ofp14_role_prop_header */
+};
+OFP_ASSERT(sizeof(struct ofp14_role_status) == 16);
+
+/* What changed about the controller role */
+enum ofp14_controller_role_reason {
+    OFPCRR_MASTER_REQUEST = 0,  /* Another controller asked to be master. */
+    OFPCRR_CONFIG         = 1,  /* Configuration changed on the switch. */
+    OFPCRR_EXPERIMENTER   = 2,  /* Experimenter data changed. */
+};
+
+/* Role property types.
+*/
+enum ofp14_role_prop_type {
+    OFPRPT_EXPERIMENTER         = 0xFFFF, /* Experimenter property. */
+};
+
+/* Experimenter role property */
+struct ofp14_role_prop_experimenter {
+    ovs_be16        type;       /* One of OFPRPT_EXPERIMENTER. */
+    ovs_be16        length;     /* Length in bytes of this property. */
+    ovs_be32        experimenter; /* Experimenter ID which takes the same
+                                     form as in struct
+                                     ofp_experimenter_header. */
+    ovs_be32        exp_type;     /* Experimenter defined. */
+    /* Followed by:
+     *   - Exactly (length - 12) bytes containing the experimenter data, then
+     *   - Exactly (length + 7)/8*8 - (length) (between 0 and 7)
+     *     bytes of all-zero bytes */
+};
+OFP_ASSERT(sizeof(struct ofp14_role_prop_experimenter) == 12);
+
+#endif /* openflow/openflow-1.4.h */
index 45d03ef..a38f1e3 100644 (file)
@@ -204,19 +204,10 @@ enum ofp_port_features {
     OFPPF_10GB_FD    = 1 << 6,  /* 10 Gb full-duplex rate support. */
 };
 
-struct ofp_packet_queue {
-    ovs_be32 queue_id;          /* id for the specific queue. */
-    ovs_be16 len;               /* Length in bytes of this queue desc. */
-    uint8_t pad[2];             /* 64-bit alignment. */
-    /* struct ofp_queue_prop_header properties[0]; List of properties.  */
-};
-OFP_ASSERT(sizeof(struct ofp_packet_queue) == 8);
-
 enum ofp_queue_properties {
-    OFPQT_NONE = 0,       /* No property defined for queue (default). */
-    OFPQT_MIN_RATE,       /* Minimum datarate guaranteed. */
-                          /* Other types should be added here
-                           * (i.e. max rate, precedence, etc). */
+    OFPQT_MIN_RATE = 1,          /* Minimum datarate guaranteed. */
+    OFPQT_MAX_RATE = 2,          /* Maximum guaranteed rate. */
+    OFPQT_EXPERIMENTER = 0xffff, /* Experimenter defined property. */
 };
 
 /* Common description for a queue. */
@@ -227,13 +218,14 @@ struct ofp_queue_prop_header {
 };
 OFP_ASSERT(sizeof(struct ofp_queue_prop_header) == 8);
 
-/* Min-Rate queue property description. */
-struct ofp_queue_prop_min_rate {
-    struct ofp_queue_prop_header prop_header; /* prop: OFPQT_MIN, len: 16. */
+/* Min-Rate and Max-Rate queue property description (OFPQT_MIN and
+ * OFPQT_MAX). */
+struct ofp_queue_prop_rate {
+    struct ofp_queue_prop_header prop_header;
     ovs_be16 rate;        /* In 1/10 of a percent; >1000 -> disabled. */
     uint8_t pad[6];       /* 64-bit alignment */
 };
-OFP_ASSERT(sizeof(struct ofp_queue_prop_min_rate) == 16);
+OFP_ASSERT(sizeof(struct ofp_queue_prop_rate) == 16);
 
 /* Switch features. */
 struct ofp_switch_features {
@@ -443,6 +435,14 @@ enum ofp_group {
     OFPG_ANY        = 0xffffffff   /* Wildcard, for flow stats requests. */
 };
 
+/* Group configuration flags */
+enum ofp_group_capabilities {
+    OFPGFC_SELECT_WEIGHT   = 1 << 0, /* Support weight for select groups */
+    OFPGFC_SELECT_LIVENESS = 1 << 1, /* Support liveness for select groups */
+    OFPGFC_CHAINING        = 1 << 2, /* Support chaining groups */
+    OFPGFC_CHAINING_CHECKS = 1 << 3, /* Check chaining for loops and delete */
+};
+
 enum ofp_hello_elem_type {
     OFPHET_VERSIONBITMAP          = 1, /* Bitmap of version supported. */
 };
index d5a78fe..6ec6588 100644 (file)
@@ -21,5 +21,6 @@
 #include "openflow/openflow-1.1.h"
 #include "openflow/openflow-1.2.h"
 #include "openflow/openflow-1.3.h"
+#include "openflow/openflow-1.4.h"
 
 #endif /* openflow/openflow.h */
index 115053b..740f4fc 100644 (file)
--- a/lib/bfd.c
+++ b/lib/bfd.c
@@ -639,8 +639,10 @@ bfd_process_packet(struct bfd *bfd, const struct flow *flow,
 
     msg = ofpbuf_at(p, (uint8_t *)p->l7 - (uint8_t *)p->data, BFD_PACKET_LEN);
     if (!msg) {
-        VLOG_INFO_RL(&rl, "%s: Received unparseable BFD control message.",
-                     bfd->name);
+        VLOG_INFO_RL(&rl, "%s: Received too-short BFD control message (only "
+                     "%td bytes long, at least %d required).",
+                     bfd->name, (uint8_t *) ofpbuf_tail(p) - (uint8_t *) p->l7,
+                     BFD_PACKET_LEN);
         goto out;
     }
 
index dcabaaa..7d00f87 100644 (file)
@@ -204,7 +204,7 @@ bundle_check(const struct ofpact_bundle *bundle, ofp_port_t max_ports,
         ofp_port_t ofp_port = bundle->slaves[i];
         enum ofperr error;
 
-        error = ofputil_check_output_port(ofp_port, max_ports);
+        error = ofpact_check_output_port(ofp_port, max_ports);
         if (error) {
             VLOG_WARN_RL(&rl, "invalid slave %"PRIu16, ofp_port);
             return error;
index 730a00f..d256a5f 100644 (file)
--- a/lib/cfm.c
+++ b/lib/cfm.c
@@ -129,6 +129,8 @@ struct cfm {
     atomic_bool check_tnl_key; /* Verify the tunnel key of inbound packets? */
     atomic_bool extended;      /* Extended mode. */
     atomic_int ref_cnt;
+
+    uint64_t flap_count;       /* Count the flaps since boot. */
 };
 
 /* Remote MPs represent foreign network entities that are configured to have
@@ -330,6 +332,7 @@ cfm_create(const struct netdev *netdev) OVS_EXCLUDED(mutex)
     cfm->fault_override = -1;
     cfm->health = -1;
     cfm->last_tx = 0;
+    cfm->flap_count = 0;
     atomic_init(&cfm->extended, false);
     atomic_init(&cfm->check_tnl_key, false);
     atomic_init(&cfm->ref_cnt, 1);
@@ -487,6 +490,11 @@ cfm_run(struct cfm *cfm) OVS_EXCLUDED(mutex)
             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) {
+                cfm->flap_count++;
+            }
         }
 
         cfm->booted = true;
@@ -834,6 +842,17 @@ cfm_get_fault(const struct cfm *cfm) OVS_EXCLUDED(mutex)
     return fault;
 }
 
+/* Gets the number of cfm fault flapping since start. */
+uint64_t
+cfm_get_flap_count(const struct cfm *cfm) OVS_EXCLUDED(mutex)
+{
+    uint64_t flap_count;
+    ovs_mutex_lock(&mutex);
+    flap_count = cfm->flap_count;
+    ovs_mutex_unlock(&mutex);
+    return flap_count;
+}
+
 /* Gets the health of 'cfm'.  Returns an integer between 0 and 100 indicating
  * the health of the link as a percentage of ccm frames received in
  * CFM_HEALTH_INTERVAL * 'fault_interval' if there is only 1 remote_mpid,
index 9d1ea4c..4213eb5 100644 (file)
--- a/lib/cfm.h
+++ b/lib/cfm.h
@@ -77,6 +77,7 @@ bool cfm_should_process_flow(const struct cfm *cfm, const struct flow *,
                              struct flow_wildcards *);
 void cfm_process_heartbeat(struct cfm *, const struct ofpbuf *packet);
 int cfm_get_fault(const struct cfm *);
+uint64_t cfm_get_flap_count(const struct cfm *);
 int cfm_get_health(const struct cfm *);
 int cfm_get_opup(const struct cfm *);
 void cfm_get_remote_mpids(const struct cfm *, uint64_t **rmps, size_t *n_rmps);
index 53487a4..5587141 100644 (file)
 #include "packets.h"
 #include "ovs-thread.h"
 
-static struct cls_table *find_table(const struct classifier *,
-                                    const struct minimask *);
-static struct cls_table *insert_table(struct classifier *,
-                                      const struct minimask *);
+static struct cls_subtable *find_subtable(const struct classifier *,
+                                          const struct minimask *);
+static struct cls_subtable *insert_subtable(struct classifier *,
+                                            const struct minimask *);
 
-static void destroy_table(struct classifier *, struct cls_table *);
+static void destroy_subtable(struct classifier *, struct cls_subtable *);
 
-static void update_tables_after_insertion(struct classifier *,
-                                          struct cls_table *,
-                                          unsigned int new_priority);
-static void update_tables_after_removal(struct classifier *,
-                                        struct cls_table *,
-                                        unsigned int del_priority);
+static void update_subtables_after_insertion(struct classifier *,
+                                             struct cls_subtable *,
+                                             unsigned int new_priority);
+static void update_subtables_after_removal(struct classifier *,
+                                           struct cls_subtable *,
+                                           unsigned int del_priority);
 
-static struct cls_rule *find_match(const struct cls_table *,
+static struct cls_rule *find_match(const struct cls_subtable *,
                                    const struct flow *);
-static struct cls_rule *find_equal(struct cls_table *,
+static struct cls_rule *find_equal(struct cls_subtable *,
                                    const struct miniflow *, uint32_t hash);
 static struct cls_rule *insert_rule(struct classifier *,
-                                    struct cls_table *, struct cls_rule *);
+                                    struct cls_subtable *, struct cls_rule *);
 
 /* Iterates RULE over HEAD and all of the cls_rules on HEAD->list. */
 #define FOR_EACH_RULE_IN_LIST(RULE, HEAD)                               \
@@ -152,8 +152,8 @@ void
 classifier_init(struct classifier *cls)
 {
     cls->n_rules = 0;
-    hmap_init(&cls->tables);
-    list_init(&cls->tables_priority);
+    hmap_init(&cls->subtables);
+    list_init(&cls->subtables_priority);
     hmap_init(&cls->partitions);
     ovs_rwlock_init(&cls->rwlock);
 }
@@ -164,13 +164,14 @@ void
 classifier_destroy(struct classifier *cls)
 {
     if (cls) {
-        struct cls_table *partition, *next_partition;
-        struct cls_table *table, *next_table;
+        struct cls_subtable *partition, *next_partition;
+        struct cls_subtable *subtable, *next_subtable;
 
-        HMAP_FOR_EACH_SAFE (table, next_table, hmap_node, &cls->tables) {
-            destroy_table(cls, table);
+        HMAP_FOR_EACH_SAFE (subtable, next_subtable, hmap_node,
+                            &cls->subtables) {
+            destroy_subtable(cls, subtable);
         }
-        hmap_destroy(&cls->tables);
+        hmap_destroy(&cls->subtables);
 
         HMAP_FOR_EACH_SAFE (partition, next_partition, hmap_node,
                             &cls->partitions) {
@@ -218,7 +219,7 @@ find_partition(const struct classifier *cls, ovs_be64 metadata, uint32_t hash)
 }
 
 static struct cls_partition *
-create_partition(struct classifier *cls, struct cls_table *table,
+create_partition(struct classifier *cls, struct cls_subtable *subtable,
                  ovs_be64 metadata)
 {
     uint32_t hash = hash_metadata(metadata);
@@ -230,7 +231,7 @@ create_partition(struct classifier *cls, struct cls_table *table,
         tag_tracker_init(&partition->tracker);
         hmap_insert(&cls->partitions, &partition->hmap_node, hash);
     }
-    tag_tracker_add(&partition->tracker, &partition->tags, table->tag);
+    tag_tracker_add(&partition->tracker, &partition->tags, subtable->tag);
     return partition;
 }
 
@@ -251,23 +252,23 @@ struct cls_rule *
 classifier_replace(struct classifier *cls, struct cls_rule *rule)
 {
     struct cls_rule *old_rule;
-    struct cls_table *table;
+    struct cls_subtable *subtable;
 
-    table = find_table(cls, &rule->match.mask);
-    if (!table) {
-        table = insert_table(cls, &rule->match.mask);
+    subtable = find_subtable(cls, &rule->match.mask);
+    if (!subtable) {
+        subtable = insert_subtable(cls, &rule->match.mask);
     }
 
-    old_rule = insert_rule(cls, table, rule);
+    old_rule = insert_rule(cls, subtable, rule);
     if (!old_rule) {
         if (minimask_get_metadata_mask(&rule->match.mask) == OVS_BE64_MAX) {
             ovs_be64 metadata = miniflow_get_metadata(&rule->match.flow);
-            rule->partition = create_partition(cls, table, metadata);
+            rule->partition = create_partition(cls, subtable, metadata);
         } else {
             rule->partition = NULL;
         }
 
-        table->n_table_rules++;
+        subtable->n_rules++;
         cls->n_rules++;
     } else {
         rule->partition = old_rule->partition;
@@ -296,36 +297,36 @@ classifier_remove(struct classifier *cls, struct cls_rule *rule)
 {
     struct cls_partition *partition;
     struct cls_rule *head;
-    struct cls_table *table;
+    struct cls_subtable *subtable;
 
-    table = find_table(cls, &rule->match.mask);
-    head = find_equal(table, &rule->match.flow, rule->hmap_node.hash);
+    subtable = find_subtable(cls, &rule->match.mask);
+    head = find_equal(subtable, &rule->match.flow, rule->hmap_node.hash);
     if (head != rule) {
         list_remove(&rule->list);
     } else if (list_is_empty(&rule->list)) {
-        hmap_remove(&table->rules, &rule->hmap_node);
+        hmap_remove(&subtable->rules, &rule->hmap_node);
     } else {
         struct cls_rule *next = CONTAINER_OF(rule->list.next,
                                              struct cls_rule, list);
 
         list_remove(&rule->list);
-        hmap_replace(&table->rules, &rule->hmap_node, &next->hmap_node);
+        hmap_replace(&subtable->rules, &rule->hmap_node, &next->hmap_node);
     }
 
     partition = rule->partition;
     if (partition) {
         tag_tracker_subtract(&partition->tracker, &partition->tags,
-                             table->tag);
+                             subtable->tag);
         if (!partition->tags) {
             hmap_remove(&cls->partitions, &partition->hmap_node);
             free(partition);
         }
     }
 
-    if (--table->n_table_rules == 0) {
-        destroy_table(cls, table);
+    if (--subtable->n_rules == 0) {
+        destroy_subtable(cls, subtable);
     } else {
-        update_tables_after_removal(cls, table, rule->priority);
+        update_subtables_after_removal(cls, subtable, rule->priority);
     }
     cls->n_rules--;
 }
@@ -343,27 +344,27 @@ classifier_lookup(const struct classifier *cls, const struct flow *flow,
                   struct flow_wildcards *wc)
 {
     const struct cls_partition *partition;
-    struct cls_table *table;
+    struct cls_subtable *subtable;
     struct cls_rule *best;
     tag_type tags;
 
-    /* Determine 'tags' such that, if 'table->tag' doesn't intersect them, then
-     * 'flow' cannot possibly match in 'table':
+    /* Determine 'tags' such that, if 'subtable->tag' doesn't intersect them,
+     * then 'flow' cannot possibly match in 'subtable':
      *
      *     - If flow->metadata maps to a given 'partition', then we can use
      *       'tags' for 'partition->tags'.
      *
      *     - If flow->metadata has no partition, then no rule in 'cls' has an
      *       exact-match for flow->metadata.  That means that we don't need to
-     *       search any table that includes flow->metadata in its mask.
+     *       search any subtable that includes flow->metadata in its mask.
      *
-     * In either case, we always need to search any cls_tables that do not
+     * In either case, we always need to search any cls_subtables that do not
      * include flow->metadata in its mask.  One way to do that would be to
-     * check the "cls_table"s explicitly for that, but that would require an
-     * extra branch per table.  Instead, we mark such a cls_table's 'tags' as
-     * TAG_ALL and make sure that 'tags' is never empty.  This means that
-     * 'tags' always intersects such a cls_table's 'tags', so we don't need a
-     * special case.
+     * check the "cls_subtable"s explicitly for that, but that would require an
+     * extra branch per subtable.  Instead, we mark such a cls_subtable's
+     * 'tags' as TAG_ALL and make sure that 'tags' is never empty.  This means
+     * that 'tags' always intersects such a cls_subtable's 'tags', so we don't
+     * need a special case.
      */
     partition = (hmap_is_empty(&cls->partitions)
                  ? NULL
@@ -372,32 +373,33 @@ classifier_lookup(const struct classifier *cls, const struct flow *flow,
     tags = partition ? partition->tags : TAG_ARBITRARY;
 
     best = NULL;
-    LIST_FOR_EACH (table, list_node, &cls->tables_priority) {
+    LIST_FOR_EACH (subtable, list_node, &cls->subtables_priority) {
         struct cls_rule *rule;
 
-        if (!tag_intersects(tags, table->tag)) {
+        if (!tag_intersects(tags, subtable->tag)) {
             continue;
         }
 
-        rule = find_match(table, flow);
+        rule = find_match(subtable, flow);
         if (wc) {
-            flow_wildcards_fold_minimask(wc, &table->mask);
+            flow_wildcards_fold_minimask(wc, &subtable->mask);
         }
         if (rule) {
             best = rule;
-            LIST_FOR_EACH_CONTINUE (table, list_node, &cls->tables_priority) {
-                if (table->max_priority <= best->priority) {
-                    /* Tables in descending priority order,
+            LIST_FOR_EACH_CONTINUE (subtable, list_node,
+                                    &cls->subtables_priority) {
+                if (subtable->max_priority <= best->priority) {
+                    /* Subtables are in descending priority order,
                      * can not find anything better. */
                     return best;
                 }
-                if (!tag_intersects(tags, table->tag)) {
+                if (!tag_intersects(tags, subtable->tag)) {
                     continue;
                 }
 
-                rule = find_match(table, flow);
+                rule = find_match(subtable, flow);
                 if (wc) {
-                    flow_wildcards_fold_minimask(wc, &table->mask);
+                    flow_wildcards_fold_minimask(wc, &subtable->mask);
                 }
                 if (rule && rule->priority > best->priority) {
                     best = rule;
@@ -417,19 +419,19 @@ classifier_find_rule_exactly(const struct classifier *cls,
                              const struct cls_rule *target)
 {
     struct cls_rule *head, *rule;
-    struct cls_table *table;
+    struct cls_subtable *subtable;
 
-    table = find_table(cls, &target->match.mask);
-    if (!table) {
+    subtable = find_subtable(cls, &target->match.mask);
+    if (!subtable) {
         return NULL;
     }
 
     /* Skip if there is no hope. */
-    if (target->priority > table->max_priority) {
+    if (target->priority > subtable->max_priority) {
         return NULL;
     }
 
-    head = find_equal(table, &target->match.flow,
+    head = find_equal(subtable, &target->match.flow,
                       miniflow_hash_in_minimask(&target->match.flow,
                                                 &target->match.mask, 0));
     FOR_EACH_RULE_IN_LIST (rule, head) {
@@ -465,20 +467,20 @@ bool
 classifier_rule_overlaps(const struct classifier *cls,
                          const struct cls_rule *target)
 {
-    struct cls_table *table;
+    struct cls_subtable *subtable;
 
-    /* Iterate tables in the descending max priority order. */
-    LIST_FOR_EACH (table, list_node, &cls->tables_priority) {
+    /* Iterate subtables in the descending max priority order. */
+    LIST_FOR_EACH (subtable, list_node, &cls->subtables_priority) {
         uint32_t storage[FLOW_U32S];
         struct minimask mask;
         struct cls_rule *head;
 
-        if (target->priority > table->max_priority) {
-            break; /* Can skip this and the rest of the tables. */
+        if (target->priority > subtable->max_priority) {
+            break; /* Can skip this and the rest of the subtables. */
         }
 
-        minimask_combine(&mask, &target->match.mask, &table->mask, storage);
-        HMAP_FOR_EACH (head, hmap_node, &table->rules) {
+        minimask_combine(&mask, &target->match.mask, &subtable->mask, storage);
+        HMAP_FOR_EACH (head, hmap_node, &subtable->rules) {
             struct cls_rule *rule;
 
             FOR_EACH_RULE_IN_LIST (rule, head) {
@@ -551,12 +553,13 @@ rule_matches(const struct cls_rule *rule, const struct cls_rule *target)
 }
 
 static struct cls_rule *
-search_table(const struct cls_table *table, const struct cls_rule *target)
+search_subtable(const struct cls_subtable *subtable,
+                const struct cls_rule *target)
 {
-    if (!target || !minimask_has_extra(&table->mask, &target->match.mask)) {
+    if (!target || !minimask_has_extra(&subtable->mask, &target->match.mask)) {
         struct cls_rule *rule;
 
-        HMAP_FOR_EACH (rule, hmap_node, &table->rules) {
+        HMAP_FOR_EACH (rule, hmap_node, &subtable->rules) {
             if (rule_matches(rule, target)) {
                 return rule;
             }
@@ -586,12 +589,12 @@ cls_cursor_init(struct cls_cursor *cursor, const struct classifier *cls,
 struct cls_rule *
 cls_cursor_first(struct cls_cursor *cursor)
 {
-    struct cls_table *table;
+    struct cls_subtable *subtable;
 
-    HMAP_FOR_EACH (table, hmap_node, &cursor->cls->tables) {
-        struct cls_rule *rule = search_table(table, cursor->target);
+    HMAP_FOR_EACH (subtable, hmap_node, &cursor->cls->subtables) {
+        struct cls_rule *rule = search_subtable(subtable, cursor->target);
         if (rule) {
-            cursor->table = table;
+            cursor->subtable = subtable;
             return rule;
         }
     }
@@ -605,7 +608,7 @@ struct cls_rule *
 cls_cursor_next(struct cls_cursor *cursor, const struct cls_rule *rule_)
 {
     struct cls_rule *rule = CONST_CAST(struct cls_rule *, rule_);
-    const struct cls_table *table;
+    const struct cls_subtable *subtable;
     struct cls_rule *next;
 
     next = next_rule_in_list__(rule);
@@ -614,20 +617,20 @@ cls_cursor_next(struct cls_cursor *cursor, const struct cls_rule *rule_)
     }
 
     /* 'next' is the head of the list, that is, the rule that is included in
-     * the table's hmap.  (This is important when the classifier contains rules
-     * that differ only in priority.) */
+     * the subtable's hmap.  (This is important when the classifier contains
+     * rules that differ only in priority.) */
     rule = next;
-    HMAP_FOR_EACH_CONTINUE (rule, hmap_node, &cursor->table->rules) {
+    HMAP_FOR_EACH_CONTINUE (rule, hmap_node, &cursor->subtable->rules) {
         if (rule_matches(rule, cursor->target)) {
             return rule;
         }
     }
 
-    table = cursor->table;
-    HMAP_FOR_EACH_CONTINUE (table, hmap_node, &cursor->cls->tables) {
-        rule = search_table(table, cursor->target);
+    subtable = cursor->subtable;
+    HMAP_FOR_EACH_CONTINUE (subtable, hmap_node, &cursor->cls->subtables) {
+        rule = search_subtable(subtable, cursor->target);
         if (rule) {
-            cursor->table = table;
+            cursor->subtable = subtable;
             return rule;
         }
     }
@@ -635,145 +638,149 @@ cls_cursor_next(struct cls_cursor *cursor, const struct cls_rule *rule_)
     return NULL;
 }
 \f
-static struct cls_table *
-find_table(const struct classifier *cls, const struct minimask *mask)
+static struct cls_subtable *
+find_subtable(const struct classifier *cls, const struct minimask *mask)
 {
-    struct cls_table *table;
+    struct cls_subtable *subtable;
 
-    HMAP_FOR_EACH_IN_BUCKET (table, hmap_node, minimask_hash(mask, 0),
-                             &cls->tables) {
-        if (minimask_equal(mask, &table->mask)) {
-            return table;
+    HMAP_FOR_EACH_IN_BUCKET (subtable, hmap_node, minimask_hash(mask, 0),
+                             &cls->subtables) {
+        if (minimask_equal(mask, &subtable->mask)) {
+            return subtable;
         }
     }
     return NULL;
 }
 
-static struct cls_table *
-insert_table(struct classifier *cls, const struct minimask *mask)
+static struct cls_subtable *
+insert_subtable(struct classifier *cls, const struct minimask *mask)
 {
     uint32_t hash = minimask_hash(mask, 0);
-    struct cls_table *table;
+    struct cls_subtable *subtable;
 
-    table = xzalloc(sizeof *table);
-    hmap_init(&table->rules);
-    minimask_clone(&table->mask, mask);
-    hmap_insert(&cls->tables, &table->hmap_node, minimask_hash(mask, 0));
-    list_push_back(&cls->tables_priority, &table->list_node);
-    table->tag = (minimask_get_metadata_mask(mask) == OVS_BE64_MAX
-                  ? tag_create_deterministic(hash)
-                  : TAG_ALL);
+    subtable = xzalloc(sizeof *subtable);
+    hmap_init(&subtable->rules);
+    minimask_clone(&subtable->mask, mask);
+    hmap_insert(&cls->subtables, &subtable->hmap_node, minimask_hash(mask, 0));
+    list_push_back(&cls->subtables_priority, &subtable->list_node);
+    subtable->tag = (minimask_get_metadata_mask(mask) == OVS_BE64_MAX
+                     ? tag_create_deterministic(hash)
+                     : TAG_ALL);
 
-    return table;
+    return subtable;
 }
 
 static void
-destroy_table(struct classifier *cls, struct cls_table *table)
+destroy_subtable(struct classifier *cls, struct cls_subtable *subtable)
 {
-    minimask_destroy(&table->mask);
-    hmap_remove(&cls->tables, &table->hmap_node);
-    hmap_destroy(&table->rules);
-    list_remove(&table->list_node);
-    free(table);
+    minimask_destroy(&subtable->mask);
+    hmap_remove(&cls->subtables, &subtable->hmap_node);
+    hmap_destroy(&subtable->rules);
+    list_remove(&subtable->list_node);
+    free(subtable);
 }
 
-/* This function performs the following updates for 'table' in 'cls' following
- * the addition of a new rule with priority 'new_priority' to 'table':
+/* This function performs the following updates for 'subtable' in 'cls'
+ * following the addition of a new rule with priority 'new_priority' to
+ * 'subtable':
  *
- *    - Update 'table->max_priority' and 'table->max_count' if necessary.
+ *    - Update 'subtable->max_priority' and 'subtable->max_count' if necessary.
  *
- *    - Update 'table''s position in 'cls->tables_priority' if necessary.
+ *    - Update 'subtable''s position in 'cls->subtables_priority' if necessary.
  *
  * This function should only be called after adding a new rule, not after
  * replacing a rule by an identical one or modifying a rule in-place. */
 static void
-update_tables_after_insertion(struct classifier *cls, struct cls_table *table,
-                              unsigned int new_priority)
-{
-    if (new_priority == table->max_priority) {
-        ++table->max_count;
-    } else if (new_priority > table->max_priority) {
-        struct cls_table *iter;
-
-        table->max_priority = new_priority;
-        table->max_count = 1;
-
-        /* Possibly move 'table' earlier in the priority list.  If we break out
-         * of the loop, then 'table' should be moved just after that 'iter'.
-         * If the loop terminates normally, then 'iter' will be the list head
-         * and we'll move table just after that (e.g. to the front of the
-         * list). */
-        iter = table;
+update_subtables_after_insertion(struct classifier *cls,
+                                 struct cls_subtable *subtable,
+                                 unsigned int new_priority)
+{
+    if (new_priority == subtable->max_priority) {
+        ++subtable->max_count;
+    } else if (new_priority > subtable->max_priority) {
+        struct cls_subtable *iter;
+
+        subtable->max_priority = new_priority;
+        subtable->max_count = 1;
+
+        /* Possibly move 'subtable' earlier in the priority list.  If we break
+         * out of the loop, then 'subtable' should be moved just after that
+         * 'iter'.  If the loop terminates normally, then 'iter' will be the
+         * list head and we'll move subtable just after that (e.g. to the front
+         * of the list). */
+        iter = subtable;
         LIST_FOR_EACH_REVERSE_CONTINUE (iter, list_node,
-                                        &cls->tables_priority) {
-            if (iter->max_priority >= table->max_priority) {
+                                        &cls->subtables_priority) {
+            if (iter->max_priority >= subtable->max_priority) {
                 break;
             }
         }
 
-        /* Move 'table' just after 'iter' (unless it's already there). */
-        if (iter->list_node.next != &table->list_node) {
+        /* Move 'subtable' just after 'iter' (unless it's already there). */
+        if (iter->list_node.next != &subtable->list_node) {
             list_splice(iter->list_node.next,
-                        &table->list_node, table->list_node.next);
+                        &subtable->list_node, subtable->list_node.next);
         }
     }
 }
 
-/* This function performs the following updates for 'table' in 'cls' following
- * the deletion of a rule with priority 'del_priority' from 'table':
+/* This function performs the following updates for 'subtable' in 'cls'
+ * following the deletion of a rule with priority 'del_priority' from
+ * 'subtable':
  *
- *    - Update 'table->max_priority' and 'table->max_count' if necessary.
+ *    - Update 'subtable->max_priority' and 'subtable->max_count' if necessary.
  *
- *    - Update 'table''s position in 'cls->tables_priority' if necessary.
+ *    - Update 'subtable''s position in 'cls->subtables_priority' if necessary.
  *
  * This function should only be called after removing a rule, not after
  * replacing a rule by an identical one or modifying a rule in-place. */
 static void
-update_tables_after_removal(struct classifier *cls, struct cls_table *table,
-                            unsigned int del_priority)
+update_subtables_after_removal(struct classifier *cls,
+                               struct cls_subtable *subtable,
+                               unsigned int del_priority)
 {
-    struct cls_table *iter;
+    struct cls_subtable *iter;
 
-    if (del_priority == table->max_priority && --table->max_count == 0) {
+    if (del_priority == subtable->max_priority && --subtable->max_count == 0) {
         struct cls_rule *head;
 
-        table->max_priority = 0;
-        HMAP_FOR_EACH (head, hmap_node, &table->rules) {
-            if (head->priority > table->max_priority) {
-                table->max_priority = head->priority;
-                table->max_count = 1;
-            } else if (head->priority == table->max_priority) {
-                ++table->max_count;
+        subtable->max_priority = 0;
+        HMAP_FOR_EACH (head, hmap_node, &subtable->rules) {
+            if (head->priority > subtable->max_priority) {
+                subtable->max_priority = head->priority;
+                subtable->max_count = 1;
+            } else if (head->priority == subtable->max_priority) {
+                ++subtable->max_count;
             }
         }
 
-        /* Possibly move 'table' later in the priority list.  If we break out
-         * of the loop, then 'table' should be moved just before that 'iter'.
-         * If the loop terminates normally, then 'iter' will be the list head
-         * and we'll move table just before that (e.g. to the back of the
-         * list). */
-        iter = table;
-        LIST_FOR_EACH_CONTINUE (iter, list_node, &cls->tables_priority) {
-            if (iter->max_priority <= table->max_priority) {
+        /* Possibly move 'subtable' later in the priority list.  If we break
+         * out of the loop, then 'subtable' should be moved just before that
+         * 'iter'.  If the loop terminates normally, then 'iter' will be the
+         * list head and we'll move subtable just before that (e.g. to the back
+         * of the list). */
+        iter = subtable;
+        LIST_FOR_EACH_CONTINUE (iter, list_node, &cls->subtables_priority) {
+            if (iter->max_priority <= subtable->max_priority) {
                 break;
             }
         }
 
-        /* Move 'table' just before 'iter' (unless it's already there). */
-        if (iter->list_node.prev != &table->list_node) {
+        /* Move 'subtable' just before 'iter' (unless it's already there). */
+        if (iter->list_node.prev != &subtable->list_node) {
             list_splice(&iter->list_node,
-                        &table->list_node, table->list_node.next);
+                        &subtable->list_node, subtable->list_node.next);
         }
     }
 }
 
 static struct cls_rule *
-find_match(const struct cls_table *table, const struct flow *flow)
+find_match(const struct cls_subtable *subtable, const struct flow *flow)
 {
-    uint32_t hash = flow_hash_in_minimask(flow, &table->mask, 0);
+    uint32_t hash = flow_hash_in_minimask(flow, &subtable->mask, 0);
     struct cls_rule *rule;
 
-    HMAP_FOR_EACH_WITH_HASH (rule, hmap_node, hash, &table->rules) {
+    HMAP_FOR_EACH_WITH_HASH (rule, hmap_node, hash, &subtable->rules) {
         if (minimatch_matches_flow(&rule->match, flow)) {
             return rule;
         }
@@ -783,11 +790,12 @@ find_match(const struct cls_table *table, const struct flow *flow)
 }
 
 static struct cls_rule *
-find_equal(struct cls_table *table, const struct miniflow *flow, uint32_t hash)
+find_equal(struct cls_subtable *subtable, const struct miniflow *flow,
+           uint32_t hash)
 {
     struct cls_rule *head;
 
-    HMAP_FOR_EACH_WITH_HASH (head, hmap_node, hash, &table->rules) {
+    HMAP_FOR_EACH_WITH_HASH (head, hmap_node, hash, &subtable->rules) {
         if (miniflow_equal(&head->match.flow, flow)) {
             return head;
         }
@@ -796,8 +804,8 @@ find_equal(struct cls_table *table, const struct miniflow *flow, uint32_t hash)
 }
 
 static struct cls_rule *
-insert_rule(struct classifier *cls,
-            struct cls_table *table, struct cls_rule *new)
+insert_rule(struct classifier *cls, struct cls_subtable *subtable,
+            struct cls_rule *new)
 {
     struct cls_rule *head;
     struct cls_rule *old = NULL;
@@ -805,9 +813,9 @@ insert_rule(struct classifier *cls,
     new->hmap_node.hash = miniflow_hash_in_minimask(&new->match.flow,
                                                     &new->match.mask, 0);
 
-    head = find_equal(table, &new->match.flow, new->hmap_node.hash);
+    head = find_equal(subtable, &new->match.flow, new->hmap_node.hash);
     if (!head) {
-        hmap_insert(&table->rules, &new->hmap_node, new->hmap_node.hash);
+        hmap_insert(&subtable->rules, &new->hmap_node, new->hmap_node.hash);
         list_init(&new->list);
         goto out;
     } else {
@@ -818,7 +826,7 @@ insert_rule(struct classifier *cls,
             if (new->priority >= rule->priority) {
                 if (rule == head) {
                     /* 'new' is the new highest-priority flow in the list. */
-                    hmap_replace(&table->rules,
+                    hmap_replace(&subtable->rules,
                                  &rule->hmap_node, &new->hmap_node);
                 }
 
@@ -839,7 +847,7 @@ insert_rule(struct classifier *cls,
 
  out:
     if (!old) {
-        update_tables_after_insertion(cls, table, new->priority);
+        update_subtables_after_insertion(cls, subtable, new->priority);
     }
     return old;
 }
index 8e3bf61..6f8c186 100644 (file)
  *
  * This is how the classifier works.  In a "struct classifier", each form of
  * "struct cls_rule" present (based on its ->match.mask) goes into a separate
- * "struct cls_table".  A lookup does a hash lookup in every "struct cls_table"
- * in the classifier and tracks the highest-priority match that it finds.  The
- * tables are kept in a descending priority order according to the highest
- * priority rule in each table, which allows lookup to skip over tables that
- * can't possibly have a higher-priority match than already found.
+ * "struct cls_subtable".  A lookup does a hash lookup in every "struct
+ * cls_subtable" in the classifier and tracks the highest-priority match that
+ * it finds.  The subtables are kept in a descending priority order according
+ * to the highest priority rule in each subtable, which allows lookup to skip
+ * over subtables that can't possibly have a higher-priority match than
+ * already found.
  *
  * One detail: a classifier can contain multiple rules that are identical other
  * than their priority.  When this happens, only the highest priority rule out
  * of a group of otherwise identical rules is stored directly in the "struct
- * cls_table", with the other almost-identical rules chained off a linked list
- * inside that highest-priority rule.
+ * cls_subtable", with the other almost-identical rules chained off a linked
+ * list inside that highest-priority rule.
  *
  *
  * Partitioning
  * The classifier has a special optimization to speed up matching in this
  * scenario:
  *
- *     - Each cls_table that matches on metadata gets a tag derived from the
- *       table's mask, so that it is likely that each table has a unique tag.
- *       (Duplicate tags have a performance cost but do not affect
+ *     - Each cls_subtable that matches on metadata gets a tag derived from the
+ *       subtable's mask, so that it is likely that each subtable has a unique
+ *       tag.  (Duplicate tags have a performance cost but do not affect
  *       correctness.)
  *
  *     - For each metadata value matched by any cls_rule, the classifier
  *       constructs a "struct cls_partition" indexed by the metadata value.
  *       The cls_partition has a 'tags' member whose value is the bitwise-OR of
- *       the tags of each cls_table that contains any rule that matches on the
- *       cls_partition's metadata value.  In other words, struct cls_partition
- *       associates metadata values with tables that need to be checked with
- *       flows with that specific metadata value.
+ *       the tags of each cls_subtable that contains any rule that matches on
+ *       the cls_partition's metadata value.  In other words, struct
+ *       cls_partition associates metadata values with subtables that need to
+ *       be checked with flows with that specific metadata value.
  *
  * Thus, a flow lookup can start by looking up the partition associated with
- * the flow's metadata, and then skip over any cls_table whose 'tag' does not
- * intersect the partition's 'tags'.  (The flow must also be looked up in any
- * cls_table that doesn't match on metadata.  We handle that by giving any such
- * cls_table TAG_ALL as its 'tags' so that it matches any tag.)
+ * the flow's metadata, and then skip over any cls_subtable whose 'tag' does
+ * not intersect the partition's 'tags'.  (The flow must also be looked up in
+ * any cls_subtable that doesn't match on metadata.  We handle that by giving
+ * any such cls_subtable TAG_ALL as its 'tags' so that it matches any tag.)
  *
  *
  * Thread-safety
  * =============
  *
- * When locked properly, the classifier is thread safe as long as the following
- * conditions are satisfied.
- * - Only the main thread calls functions requiring a write lock.
- * - Only the main thread is allowed to iterate over rules. */
+ * The classifier may safely be accessed by many reader threads concurrently or
+ * by a single writer. */
 
 #include "flow.h"
 #include "hmap.h"
@@ -122,35 +121,38 @@ extern struct ovs_mutex ofproto_mutex;
 /* A flow classifier. */
 struct classifier {
     int n_rules;                /* Total number of rules. */
-    struct hmap tables;         /* Contains "struct cls_table"s.  */
-    struct list tables_priority; /* Tables in descending priority order */
+    struct hmap subtables;      /* Contains "struct cls_subtable"s.  */
+    struct list subtables_priority; /* Subtables in descending priority order.
+                                     */
     struct hmap partitions;     /* Contains "struct cls_partition"s. */
     struct ovs_rwlock rwlock OVS_ACQ_AFTER(ofproto_mutex);
 };
 
 /* A set of rules that all have the same fields wildcarded. */
-struct cls_table {
-    struct hmap_node hmap_node; /* Within struct classifier 'tables' hmap. */
-    struct list list_node;      /* Within classifier 'tables_priority_list' */
+struct cls_subtable {
+    struct hmap_node hmap_node; /* Within struct classifier 'subtables' hmap.
+                                 */
+    struct list list_node;      /* Within classifier 'subtables_priority' list.
+                                 */
     struct hmap rules;          /* Contains "struct cls_rule"s. */
     struct minimask mask;       /* Wildcards for fields. */
-    int n_table_rules;          /* Number of rules, including duplicates. */
-    unsigned int max_priority;  /* Max priority of any rule in the table. */
+    int n_rules;                /* Number of rules, including duplicates. */
+    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. */
 };
 
-/* Returns true if 'table' is a "catch-all" table that will match every
+/* Returns true if 'table' is a "catch-all" subtable that will match every
  * packet (if there is no higher-priority match). */
 static inline bool
-cls_table_is_catchall(const struct cls_table *table)
+cls_subtable_is_catchall(const struct cls_subtable *subtable)
 {
-    return minimask_is_catchall(&table->mask);
+    return minimask_is_catchall(&subtable->mask);
 }
 
-/* A rule in a "struct cls_table". */
+/* A rule in a "struct cls_subtable". */
 struct cls_rule {
-    struct hmap_node hmap_node; /* Within struct cls_table 'rules'. */
+    struct hmap_node hmap_node; /* Within struct cls_subtable 'rules'. */
     struct list list;           /* List of identical, lower-priority rules. */
     struct minimatch match;     /* Matching rule. */
     unsigned int priority;      /* Larger numbers are higher priorities. */
@@ -158,12 +160,12 @@ struct cls_rule {
 };
 
 /* Associates a metadata value (that is, a value of the OpenFlow 1.1+ metadata
- * field) with tags for the "cls_table"s that contain rules that match that
+ * field) with tags for the "cls_subtable"s that contain rules that match that
  * metadata value.  */
 struct cls_partition {
     struct hmap_node hmap_node; /* In struct classifier's 'partitions' hmap. */
     ovs_be64 metadata;          /* metadata value for this partition. */
-    tag_type tags;              /* OR of each included flow's cls_table tag. */
+    tag_type tags;              /* OR of each flow's cls_subtable tag. */
     struct tag_tracker tracker; /* Tracks the bits in 'tags'. */
 };
 
@@ -219,14 +221,14 @@ struct cls_rule *classifier_find_match_exactly(const struct classifier *cls,
 
 struct cls_cursor {
     const struct classifier *cls;
-    const struct cls_table *table;
+    const struct cls_subtable *subtable;
     const struct cls_rule *target;
 };
 
 void cls_cursor_init(struct cls_cursor *cursor, const struct classifier *cls,
                      const struct cls_rule *match) OVS_REQ_RDLOCK(cls->rwlock);
 struct cls_rule *cls_cursor_first(struct cls_cursor *cursor);
-struct cls_rule *cls_cursor_next(struct cls_cursor *cursor, const struct cls_rule *);
+struct cls_rule *cls_cursor_next(struct cls_cursor *, const struct cls_rule *);
 
 #define CLS_CURSOR_FOR_EACH(RULE, MEMBER, CURSOR)                       \
     for (ASSIGN_CONTAINER(RULE, cls_cursor_first(CURSOR), MEMBER);      \
index 1c9ebe2..54641d0 100644 (file)
@@ -376,6 +376,8 @@ should_restart(int status)
 {
     if (WIFSIGNALED(status)) {
         static const int error_signals[] = {
+            /* This list of signals is documented in daemon.man.  If you
+             * change the list, update the documentation too. */
             SIGABRT, SIGALRM, SIGBUS, SIGFPE, SIGILL, SIGPIPE, SIGSEGV,
             SIGXCPU, SIGXFSZ
         };
index 7b07cb8..00de1a3 100644 (file)
@@ -26,9 +26,10 @@ run as a background process. \*(DD
 \fB\-\-monitor\fR
 Creates an additional process to monitor the \fB\*(PN\fR daemon.  If
 the daemon dies due to a signal that indicates a programming error
-(e.g. \fBSIGSEGV\fR, \fBSIGABRT\fR), then the monitor process starts a
-new copy of it.  If the daemon die or exits for another reason, the
-monitor process exits.
+(\fBSIGABRT\fR, \fBSIGALRM\fR, \fBSIGBUS\fR, \fBSIGFPE\fR,
+\fBSIGILL\fR, \fBSIGPIPE\fR, \fBSIGSEGV\fR, \fBSIGXCPU\fR, or
+\fBSIGXFSZ\fR) then the monitor process starts a new copy of it.  If
+the daemon dies or exits for another reason, the monitor process exits.
 .IP
 This option is normally used with \fB\-\-detach\fR, but it also
 functions without it.
index 36b6d4a..5195d18 100644 (file)
@@ -125,7 +125,7 @@ struct dp_netdev_flow {
     long long int used;         /* Last used time, in monotonic msecs. */
     long long int packet_count; /* Number of packets matched. */
     long long int byte_count;   /* Number of bytes matched. */
-    uint8_t tcp_flags;          /* Bitwise-OR of seen tcp_flags values. */
+    uint16_t tcp_flags;         /* Bitwise-OR of seen tcp_flags values. */
 
     /* Actions. */
     struct nlattr *actions;
@@ -632,20 +632,20 @@ 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 *flow)
+dp_netdev_free_flow(struct dp_netdev *dp, struct dp_netdev_flow *netdev_flow)
 {
-    hmap_remove(&dp->flow_table, &flow->node);
-    free(flow->actions);
-    free(flow);
+    hmap_remove(&dp->flow_table, &netdev_flow->node);
+    free(netdev_flow->actions);
+    free(netdev_flow);
 }
 
 static void
 dp_netdev_flow_flush(struct dp_netdev *dp)
 {
-    struct dp_netdev_flow *flow, *next;
+    struct dp_netdev_flow *netdev_flow, *next;
 
-    HMAP_FOR_EACH_SAFE (flow, next, node, &dp->flow_table) {
-        dp_netdev_free_flow(dp, flow);
+    HMAP_FOR_EACH_SAFE (netdev_flow, next, node, &dp->flow_table) {
+        dp_netdev_free_flow(dp, netdev_flow);
     }
 }
 
@@ -744,23 +744,25 @@ 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)
 {
-    struct dp_netdev_flow *flow;
+    struct dp_netdev_flow *netdev_flow;
 
-    HMAP_FOR_EACH_WITH_HASH (flow, node, flow_hash(key, 0), &dp->flow_table) {
-        if (flow_equal(&flow->key, key)) {
-            return flow;
+    HMAP_FOR_EACH_WITH_HASH (netdev_flow, node, flow_hash(key, 0),
+                             &dp->flow_table) {
+        if (flow_equal(&netdev_flow->key, key)) {
+            return netdev_flow;
         }
     }
     return NULL;
 }
 
 static void
-get_dpif_flow_stats(struct dp_netdev_flow *flow, struct dpif_flow_stats *stats)
+get_dpif_flow_stats(struct dp_netdev_flow *netdev_flow,
+                    struct dpif_flow_stats *stats)
 {
-    stats->n_packets = flow->packet_count;
-    stats->n_bytes = flow->byte_count;
-    stats->used = flow->used;
-    stats->tcp_flags = flow->tcp_flags;
+    stats->n_packets = netdev_flow->packet_count;
+    stats->n_bytes = netdev_flow->byte_count;
+    stats->used = netdev_flow->used;
+    stats->tcp_flags = netdev_flow->tcp_flags;
 }
 
 static int
@@ -802,7 +804,7 @@ dpif_netdev_flow_get(const struct dpif *dpif,
                      struct ofpbuf **actionsp, struct dpif_flow_stats *stats)
 {
     struct dp_netdev *dp = get_dp_netdev(dpif);
-    struct dp_netdev_flow *flow;
+    struct dp_netdev_flow *netdev_flow;
     struct flow key;
     int error;
 
@@ -812,13 +814,14 @@ dpif_netdev_flow_get(const struct dpif *dpif,
     }
 
     ovs_mutex_lock(&dp_netdev_mutex);
-    flow = dp_netdev_lookup_flow(dp, &key);
-    if (flow) {
+    netdev_flow = dp_netdev_lookup_flow(dp, &key);
+    if (netdev_flow) {
         if (stats) {
-            get_dpif_flow_stats(flow, stats);
+            get_dpif_flow_stats(netdev_flow, stats);
         }
         if (actionsp) {
-            *actionsp = ofpbuf_clone_data(flow->actions, flow->actions_len);
+            *actionsp = ofpbuf_clone_data(netdev_flow->actions,
+                                          netdev_flow->actions_len);
         }
     } else {
         error = ENOENT;
@@ -829,12 +832,12 @@ dpif_netdev_flow_get(const struct dpif *dpif,
 }
 
 static int
-set_flow_actions(struct dp_netdev_flow *flow,
+set_flow_actions(struct dp_netdev_flow *netdev_flow,
                  const struct nlattr *actions, size_t actions_len)
 {
-    flow->actions = xrealloc(flow->actions, actions_len);
-    flow->actions_len = actions_len;
-    memcpy(flow->actions, actions, actions_len);
+    netdev_flow->actions = xrealloc(netdev_flow->actions, actions_len);
+    netdev_flow->actions_len = actions_len;
+    memcpy(netdev_flow->actions, actions, actions_len);
     return 0;
 }
 
@@ -842,36 +845,37 @@ static int
 dp_netdev_flow_add(struct dp_netdev *dp, const struct flow *key,
                    const struct nlattr *actions, size_t actions_len)
 {
-    struct dp_netdev_flow *flow;
+    struct dp_netdev_flow *netdev_flow;
     int error;
 
-    flow = xzalloc(sizeof *flow);
-    flow->key = *key;
+    netdev_flow = xzalloc(sizeof *netdev_flow);
+    netdev_flow->key = *key;
 
-    error = set_flow_actions(flow, actions, actions_len);
+    error = set_flow_actions(netdev_flow, actions, actions_len);
     if (error) {
-        free(flow);
+        free(netdev_flow);
         return error;
     }
 
-    hmap_insert(&dp->flow_table, &flow->node, flow_hash(&flow->key, 0));
+    hmap_insert(&dp->flow_table, &netdev_flow->node,
+                flow_hash(&netdev_flow->key, 0));
     return 0;
 }
 
 static void
-clear_stats(struct dp_netdev_flow *flow)
+clear_stats(struct dp_netdev_flow *netdev_flow)
 {
-    flow->used = 0;
-    flow->packet_count = 0;
-    flow->byte_count = 0;
-    flow->tcp_flags = 0;
+    netdev_flow->used = 0;
+    netdev_flow->packet_count = 0;
+    netdev_flow->byte_count = 0;
+    netdev_flow->tcp_flags = 0;
 }
 
 static int
 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 *flow;
+    struct dp_netdev_flow *netdev_flow;
     struct flow key;
     int error;
 
@@ -881,8 +885,8 @@ dpif_netdev_flow_put(struct dpif *dpif, const struct dpif_flow_put *put)
     }
 
     ovs_mutex_lock(&dp_netdev_mutex);
-    flow = dp_netdev_lookup_flow(dp, &key);
-    if (!flow) {
+    netdev_flow = dp_netdev_lookup_flow(dp, &key);
+    if (!netdev_flow) {
         if (put->flags & DPIF_FP_CREATE) {
             if (hmap_count(&dp->flow_table) < MAX_FLOWS) {
                 if (put->stats) {
@@ -898,13 +902,14 @@ dpif_netdev_flow_put(struct dpif *dpif, const struct dpif_flow_put *put)
         }
     } else {
         if (put->flags & DPIF_FP_MODIFY) {
-            error = set_flow_actions(flow, put->actions, put->actions_len);
+            error = set_flow_actions(netdev_flow, put->actions,
+                                     put->actions_len);
             if (!error) {
                 if (put->stats) {
-                    get_dpif_flow_stats(flow, put->stats);
+                    get_dpif_flow_stats(netdev_flow, put->stats);
                 }
                 if (put->flags & DPIF_FP_ZERO_STATS) {
-                    clear_stats(flow);
+                    clear_stats(netdev_flow);
                 }
             }
         } else {
@@ -920,7 +925,7 @@ static int
 dpif_netdev_flow_del(struct dpif *dpif, const struct dpif_flow_del *del)
 {
     struct dp_netdev *dp = get_dp_netdev(dpif);
-    struct dp_netdev_flow *flow;
+    struct dp_netdev_flow *netdev_flow;
     struct flow key;
     int error;
 
@@ -930,12 +935,12 @@ dpif_netdev_flow_del(struct dpif *dpif, const struct dpif_flow_del *del)
     }
 
     ovs_mutex_lock(&dp_netdev_mutex);
-    flow = dp_netdev_lookup_flow(dp, &key);
-    if (flow) {
+    netdev_flow = dp_netdev_lookup_flow(dp, &key);
+    if (netdev_flow) {
         if (del->stats) {
-            get_dpif_flow_stats(flow, del->stats);
+            get_dpif_flow_stats(netdev_flow, del->stats);
         }
-        dp_netdev_free_flow(dp, flow);
+        dp_netdev_free_flow(dp, netdev_flow);
     } else {
         error = ENOENT;
     }
@@ -973,7 +978,7 @@ dpif_netdev_flow_dump_next(const struct dpif *dpif, void *state_,
 {
     struct dp_netdev_flow_state *state = state_;
     struct dp_netdev *dp = get_dp_netdev(dpif);
-    struct dp_netdev_flow *flow;
+    struct dp_netdev_flow *netdev_flow;
     struct hmap_node *node;
 
     ovs_mutex_lock(&dp_netdev_mutex);
@@ -983,13 +988,14 @@ dpif_netdev_flow_dump_next(const struct dpif *dpif, void *state_,
         return EOF;
     }
 
-    flow = CONTAINER_OF(node, struct dp_netdev_flow, node);
+    netdev_flow = CONTAINER_OF(node, struct dp_netdev_flow, node);
 
     if (key) {
         struct ofpbuf buf;
 
         ofpbuf_use_stack(&buf, &state->keybuf, sizeof state->keybuf);
-        odp_flow_key_from_flow(&buf, &flow->key, flow->key.in_port.odp_port);
+        odp_flow_key_from_flow(&buf, &netdev_flow->key,
+                               netdev_flow->key.in_port.odp_port);
 
         *key = buf.data;
         *key_len = buf.size;
@@ -1002,14 +1008,15 @@ dpif_netdev_flow_dump_next(const struct dpif *dpif, void *state_,
 
     if (actions) {
         free(state->actions);
-        state->actions = xmemdup(flow->actions, flow->actions_len);
+        state->actions = xmemdup(netdev_flow->actions,
+                         netdev_flow->actions_len);
 
         *actions = state->actions;
-        *actions_len = flow->actions_len;
+        *actions_len = netdev_flow->actions_len;
     }
 
     if (stats) {
-        get_dpif_flow_stats(flow, &state->stats);
+        get_dpif_flow_stats(netdev_flow, &state->stats);
         *stats = &state->stats;
     }
 
@@ -1141,12 +1148,13 @@ dpif_netdev_recv_purge(struct dpif *dpif)
 }
 \f
 static void
-dp_netdev_flow_used(struct dp_netdev_flow *flow, const struct ofpbuf *packet)
+dp_netdev_flow_used(struct dp_netdev_flow *netdev_flow,
+                    const struct ofpbuf *packet)
 {
-    flow->used = time_msec();
-    flow->packet_count++;
-    flow->byte_count += packet->size;
-    flow->tcp_flags |= packet_get_tcp_flags(packet, &flow->key);
+    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);
 }
 
 static void
@@ -1154,7 +1162,7 @@ dp_netdev_port_input(struct dp_netdev *dp, struct dp_netdev_port *port,
                      struct ofpbuf *packet, uint32_t skb_priority,
                      uint32_t pkt_mark, const struct flow_tnl *tnl)
 {
-    struct dp_netdev_flow *flow;
+    struct dp_netdev_flow *netdev_flow;
     struct flow key;
     union flow_in_port in_port_;
 
@@ -1163,11 +1171,12 @@ dp_netdev_port_input(struct dp_netdev *dp, struct dp_netdev_port *port,
     }
     in_port_.odp_port = port->port_no;
     flow_extract(packet, skb_priority, pkt_mark, tnl, &in_port_, &key);
-    flow = dp_netdev_lookup_flow(dp, &key);
-    if (flow) {
-        dp_netdev_flow_used(flow, packet);
+    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,
-                                  flow->actions, flow->actions_len);
+                                  netdev_flow->actions,
+                                  netdev_flow->actions_len);
         dp->n_hit++;
     } else {
         dp->n_missed++;
index 1bac8e8..b338b33 100644 (file)
@@ -1184,6 +1184,8 @@ dpif_execute__(struct dpif *dpif, const struct dpif_execute *execute)
  * OVS_ACTION_ATTR_USERSPACE actions it passes the packet through to the dpif
  * implementation.
  *
+ * This works even if 'actions_len' is too long for a Netlink attribute.
+ *
  * Returns 0 if successful, otherwise a positive errno value. */
 int
 dpif_execute(struct dpif *dpif,
@@ -1199,7 +1201,7 @@ dpif_execute(struct dpif *dpif,
     execute.actions = actions;
     execute.actions_len = actions_len;
     execute.packet = buf;
-    execute.needs_help = needs_help;
+    execute.needs_help = needs_help || nl_attr_oversized(actions_len);
     return dpif_execute__(dpif, &execute);
 }
 
index ab69c1c..de7450a 100644 (file)
@@ -452,7 +452,7 @@ struct dpif_flow_stats {
     uint64_t n_packets;
     uint64_t n_bytes;
     long long int used;
-    uint8_t tcp_flags;
+    uint16_t tcp_flags;
 };
 
 void dpif_flow_stats_extract(const struct flow *, const struct ofpbuf *packet,
index 51851cf..8c336f6 100644 (file)
@@ -256,6 +256,7 @@ parse_tcp(struct ofpbuf *packet, struct ofpbuf *b, struct flow *flow)
     if (tcp) {
         flow->tp_src = tcp->tcp_src;
         flow->tp_dst = tcp->tcp_dst;
+        flow->tcp_flags = tcp->tcp_ctl & htons(0x0fff);
         packet->l7 = b->data;
     }
 }
@@ -514,7 +515,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 == 21);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 22);
 
     fmd->tun_id = flow->tunnel.tun_id;
     fmd->tun_src = flow->tunnel.ip_src;
@@ -1044,7 +1045,7 @@ flow_compose(struct ofpbuf *b, const struct flow *flow)
                 b->l4 = tcp = ofpbuf_put_zeros(b, sizeof *tcp);
                 tcp->tcp_src = flow->tp_src;
                 tcp->tcp_dst = flow->tp_dst;
-                tcp->tcp_ctl = TCP_CTL(0, 5);
+                tcp->tcp_ctl = TCP_CTL(ntohs(flow->tcp_flags), 5);
             } else if (flow->nw_proto == IPPROTO_UDP) {
                 struct udp_header *udp;
 
@@ -1065,6 +1066,7 @@ flow_compose(struct ofpbuf *b, const struct flow *flow)
                 icmp->icmp_code = ntohs(flow->tp_dst);
                 icmp->icmp_csum = csum(icmp, ICMP_HEADER_LEN);
             }
+            b->l7 = ofpbuf_tail(b);
         }
 
         ip = b->l3;
index ad51496..093d509 100644 (file)
@@ -37,7 +37,7 @@ 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 21
+#define FLOW_WC_SEQ 22
 
 #define FLOW_N_REGS 8
 BUILD_ASSERT_DECL(FLOW_N_REGS <= NXM_NX_MAX_REGS);
@@ -107,6 +107,7 @@ struct flow {
     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. */
@@ -123,8 +124,8 @@ BUILD_ASSERT_DECL(sizeof(struct flow) % 4 == 0);
 
 /* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */
 BUILD_ASSERT_DECL(offsetof(struct flow, nw_frag) + 1
-                  == sizeof(struct flow_tnl) + 152
-                  && FLOW_WC_SEQ == 21);
+                  == sizeof(struct flow_tnl) + 154
+                  && FLOW_WC_SEQ == 22);
 
 /* Represents the metadata fields of struct flow. */
 struct flow_metadata {
index b133637..8efbce1 100644 (file)
@@ -369,6 +369,7 @@ lswitch_process_packet(struct lswitch *sw, const struct ofpbuf *msg)
     case OFPTYPE_PORT_DESC_STATS_REPLY:
     case OFPTYPE_ROLE_REQUEST:
     case OFPTYPE_ROLE_REPLY:
+    case OFPTYPE_ROLE_STATUS:
     case OFPTYPE_SET_FLOW_FORMAT:
     case OFPTYPE_FLOW_MOD_TABLE_ID:
     case OFPTYPE_SET_PACKET_IN_FORMAT:
index 73b1bf0..0f674b0 100644 (file)
@@ -122,6 +122,9 @@ match_wc_init(struct match *match, const struct flow *flow)
             memset(&wc->masks.tp_src, 0xff, sizeof wc->masks.tp_src);
             memset(&wc->masks.tp_dst, 0xff, sizeof wc->masks.tp_dst);
         }
+        if (flow->nw_proto == IPPROTO_TCP) {
+            memset(&wc->masks.tcp_flags, 0xff, sizeof wc->masks.tcp_flags);
+        }
 
         if (flow->nw_proto == IPPROTO_ICMPV6) {
             memset(&wc->masks.arp_sha, 0xff, sizeof wc->masks.arp_sha);
@@ -532,6 +535,19 @@ match_set_tp_dst_masked(struct match *match, ovs_be16 port, ovs_be16 mask)
     match->wc.masks.tp_dst = mask;
 }
 
+void
+match_set_tcp_flags(struct match *match, ovs_be16 flags)
+{
+    match_set_tcp_flags_masked(match, flags, OVS_BE16_MAX);
+}
+
+void
+match_set_tcp_flags_masked(struct match *match, ovs_be16 flags, ovs_be16 mask)
+{
+    match->flow.tcp_flags = flags & mask;
+    match->wc.masks.tcp_flags = mask;
+}
+
 void
 match_set_nw_proto(struct match *match, uint8_t nw_proto)
 {
@@ -826,7 +842,7 @@ match_format(const struct match *match, struct ds *s, unsigned int priority)
 
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 21);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 22);
 
     if (priority != OFP_DEFAULT_PRIORITY) {
         ds_put_format(s, "priority=%u,", priority);
@@ -1052,6 +1068,14 @@ match_format(const struct match *match, struct ds *s, unsigned int priority)
         format_be16_masked(s, "tp_src", f->tp_src, wc->masks.tp_src);
         format_be16_masked(s, "tp_dst", f->tp_dst, wc->masks.tp_dst);
     }
+    if (is_ip_any(f) && f->nw_proto == IPPROTO_TCP && wc->masks.tcp_flags) {
+        if (wc->masks.tcp_flags == htons(UINT16_MAX)) {
+            ds_put_format(s, "tcp_flags=0x%03"PRIx16",", ntohs(f->tcp_flags));
+        } else {
+            ds_put_format(s, "tcp_flags=0x%03"PRIx16"/0x%03"PRIx16",",
+                          ntohs(f->tcp_flags), ntohs(wc->masks.tcp_flags));
+        }
+    }
 
     if (s->length > start_len && ds_last(s) == ',') {
         s->length--;
index 6e87a09..1e938a1 100644 (file)
@@ -88,6 +88,8 @@ void match_set_tp_src(struct match *, ovs_be16);
 void match_set_tp_src_masked(struct match *, ovs_be16 port, ovs_be16 mask);
 void match_set_tp_dst(struct match *, ovs_be16);
 void match_set_tp_dst_masked(struct match *, ovs_be16 port, ovs_be16 mask);
+void match_set_tcp_flags(struct match *, ovs_be16);
+void match_set_tcp_flags_masked(struct match *, ovs_be16 flags, ovs_be16 mask);
 void match_set_nw_proto(struct match *, uint8_t);
 void match_set_nw_src(struct match *, ovs_be32);
 void match_set_nw_src_masked(struct match *, ovs_be32 ip, ovs_be32 mask);
index 12811ef..9f39c18 100644 (file)
@@ -41,13 +41,15 @@ VLOG_DEFINE_THIS_MODULE(meta_flow);
     sizeof ((union mf_value *)0)->MEMBER,       \
     8 * sizeof ((union mf_value *)0)->MEMBER
 
-static const struct mf_field mf_fields[MFF_N_IDS] = {
+extern const struct mf_field mf_fields[MFF_N_IDS]; /* Silence a warning. */
+
+const struct mf_field mf_fields[MFF_N_IDS] = {
     /* ## -------- ## */
     /* ## metadata ## */
     /* ## -------- ## */
 
     {
-        MFF_TUN_ID, "tun_id", NULL,
+        MFF_TUN_ID, "tun_id", "tunnel_id",
         MF_FIELD_SIZES(be64),
         MFM_FULLY,
         MFS_HEXADECIMAL,
@@ -409,7 +411,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
     },
 
     {
-        MFF_IP_PROTO, "nw_proto", NULL,
+        MFF_IP_PROTO, "nw_proto", "ip_proto",
         MF_FIELD_SIZES(u8),
         MFM_NONE,
         MFS_DECIMAL,
@@ -431,8 +433,8 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         OFPUTIL_P_ANY,   /* Will be shifted for OXM. */
         OFPUTIL_P_NONE,
     }, {
-        MFF_IP_DSCP_SHIFTED, "nw_tos_shifted", NULL,
-        MF_FIELD_SIZES(u8),
+        MFF_IP_DSCP_SHIFTED, "ip_dscp", NULL,
+        1, 6,
         MFM_NONE,
         MFS_DECIMAL,
         MFP_IP_ANY,
@@ -442,7 +444,7 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         OFPUTIL_P_ANY,   /* Will be shifted for non-OXM. */
         OFPUTIL_P_NONE,
     }, {
-        MFF_IP_ECN, "nw_ecn", NULL,
+        MFF_IP_ECN, "nw_ecn", "ip_ecn",
         1, 2,
         MFM_NONE,
         MFS_DECIMAL,
@@ -559,6 +561,17 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
         OXM_OF_TCP_DST, "OXM_OF_TCP_DST",
         OFPUTIL_P_ANY,
         OFPUTIL_P_NXM_OXM_ANY,
+    }, {
+        MFF_TCP_FLAGS, "tcp_flags", NULL,
+        2, 12,
+        MFM_FULLY,
+        MFS_HEXADECIMAL,
+        MFP_TCP,
+        false,
+        NXM_NX_TCP_FLAGS, "NXM_NX_TCP_FLAGS",
+        NXM_NX_TCP_FLAGS, "NXM_NX_TCP_FLAGS",
+        OFPUTIL_P_NXM_OXM_ANY,
+        OFPUTIL_P_NXM_OXM_ANY,
     },
 
     {
@@ -717,14 +730,6 @@ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 const struct mf_field *mf_from_nxm_header__(uint32_t header);
 static void nxm_init(void);
 
-/* Returns the field with the given 'id'. */
-const struct mf_field *
-mf_from_id(enum mf_field_id id)
-{
-    ovs_assert((unsigned int) id < MFF_N_IDS);
-    return &mf_fields[id];
-}
-
 /* Returns the field with the given 'name', or a null pointer if no field has
  * that name. */
 const struct mf_field *
@@ -919,6 +924,8 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
     case MFF_ICMPV4_CODE:
     case MFF_ICMPV6_CODE:
         return !wc->masks.tp_dst;
+    case MFF_TCP_FLAGS:
+        return !wc->masks.tcp_flags;
 
     case MFF_N_IDS:
     default:
@@ -1128,6 +1135,8 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value)
         return !(value->u8 & ~IP_ECN_MASK);
     case MFF_IP_FRAG:
         return !(value->u8 & ~FLOW_NW_FRAG_MASK);
+    case MFF_TCP_FLAGS:
+        return !(value->be16 & ~htons(0x0fff));
 
     case MFF_ARP_OP:
         return !(value->be16 & htons(0xff00));
@@ -1326,6 +1335,10 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow,
         value->be16 = flow->tp_dst;
         break;
 
+    case MFF_TCP_FLAGS:
+        value->be16 = flow->tcp_flags;
+        break;
+
     case MFF_ICMPV4_TYPE:
     case MFF_ICMPV6_TYPE:
         value->u8 = ntohs(flow->tp_src);
@@ -1518,6 +1531,10 @@ mf_set_value(const struct mf_field *mf,
         match_set_tp_dst(match, value->be16);
         break;
 
+    case MFF_TCP_FLAGS:
+        match_set_tcp_flags(match, value->be16);
+        break;
+
     case MFF_ICMPV4_TYPE:
     case MFF_ICMPV6_TYPE:
         match_set_icmp_type(match, value->u8);
@@ -1730,6 +1747,10 @@ mf_set_flow_value(const struct mf_field *mf,
         flow->tp_dst = value->be16;
         break;
 
+    case MFF_TCP_FLAGS:
+        flow->tcp_flags = value->be16;
+        break;
+
     case MFF_ICMPV4_TYPE:
     case MFF_ICMPV6_TYPE:
         flow->tp_src = htons(value->u8);
@@ -1941,6 +1962,11 @@ mf_set_wild(const struct mf_field *mf, struct match *match)
         match->flow.tp_dst = htons(0);
         break;
 
+    case MFF_TCP_FLAGS:
+        match->wc.masks.tcp_flags = htons(0);
+        match->flow.tcp_flags = htons(0);
+        break;
+
     case MFF_ND_TARGET:
         memset(&match->wc.masks.nd_target, 0,
                sizeof match->wc.masks.nd_target);
@@ -1998,7 +2024,7 @@ mf_set(const struct mf_field *mf,
     case MFF_ICMPV4_CODE:
     case MFF_ICMPV6_TYPE:
     case MFF_ICMPV6_CODE:
-        NOT_REACHED();
+        return OFPUTIL_P_NONE;
 
     case MFF_TUN_ID:
         match_set_tun_id_masked(match, value->be64, mask->be64);
@@ -2111,6 +2137,10 @@ mf_set(const struct mf_field *mf,
         match_set_tp_dst_masked(match, value->be16, mask->be16);
         break;
 
+    case MFF_TCP_FLAGS:
+        match_set_tcp_flags_masked(match, value->be16, mask->be16);
+        break;
+
     case MFF_N_IDS:
     default:
         NOT_REACHED();
@@ -2232,6 +2262,10 @@ mf_random_value(const struct mf_field *mf, union mf_value *value)
     case MFF_ND_TLL:
         break;
 
+    case MFF_TCP_FLAGS:
+        value->be16 &= htons(0x0fff);
+        break;
+
     case MFF_IN_PORT_OXM:
         value->be32 = ofputil_port_to_ofp11(u16_to_ofp(ntohs(value->be16)));
         break;
@@ -2569,6 +2603,8 @@ char *
 mf_parse(const struct mf_field *mf, const char *s,
          union mf_value *value, union mf_value *mask)
 {
+    char *error;
+
     if (!strcmp(s, "*")) {
         memset(value, 0, mf->n_bytes);
         memset(mask, 0, mf->n_bytes);
@@ -2578,32 +2614,47 @@ mf_parse(const struct mf_field *mf, const char *s,
     switch (mf->string) {
     case MFS_DECIMAL:
     case MFS_HEXADECIMAL:
-        return mf_from_integer_string(mf, s,
-                                      (uint8_t *) value, (uint8_t *) mask);
+        error = mf_from_integer_string(mf, s,
+                                       (uint8_t *) value, (uint8_t *) mask);
+        break;
 
     case MFS_ETHERNET:
-        return mf_from_ethernet_string(mf, s, value->mac, mask->mac);
+        error = mf_from_ethernet_string(mf, s, value->mac, mask->mac);
+        break;
 
     case MFS_IPV4:
-        return mf_from_ipv4_string(mf, s, &value->be32, &mask->be32);
+        error = mf_from_ipv4_string(mf, s, &value->be32, &mask->be32);
+        break;
 
     case MFS_IPV6:
-        return mf_from_ipv6_string(mf, s, &value->ipv6, &mask->ipv6);
+        error = mf_from_ipv6_string(mf, s, &value->ipv6, &mask->ipv6);
+        break;
 
     case MFS_OFP_PORT:
-        return mf_from_ofp_port_string(mf, s, &value->be16, &mask->be16);
+        error = mf_from_ofp_port_string(mf, s, &value->be16, &mask->be16);
+        break;
 
     case MFS_OFP_PORT_OXM:
-        return mf_from_ofp_port_string32(mf, s, &value->be32, &mask->be32);
+        error = mf_from_ofp_port_string32(mf, s, &value->be32, &mask->be32);
+        break;
 
     case MFS_FRAG:
-        return mf_from_frag_string(s, &value->u8, &mask->u8);
+        error = mf_from_frag_string(s, &value->u8, &mask->u8);
+        break;
 
     case MFS_TNL_FLAGS:
         ovs_assert(mf->n_bytes == sizeof(ovs_be16));
-        return mf_from_tun_flags_string(s, &value->be16, &mask->be16);
+        error = mf_from_tun_flags_string(s, &value->be16, &mask->be16);
+        break;
+
+    default:
+        NOT_REACHED();
     }
-    NOT_REACHED();
+
+    if (!error && !mf_is_mask_valid(mf, mask)) {
+        error = xasprintf("%s: invalid mask for field %s", s, mf->name);
+    }
+    return error;
 }
 
 /* Parses 's', a string value for field 'mf', into 'value'.  Returns NULL if
index d3185e4..c2b0bbc 100644 (file)
 #include "ofp-errors.h"
 #include "ofp-util.h"
 #include "packets.h"
+#include "util.h"
 
 struct ds;
 struct match;
 
 /* The comment on each of these indicates the member in "union mf_value" used
  * to represent its value. */
-enum mf_field_id {
+enum OVS_PACKED_ENUM mf_field_id {
     /* Metadata. */
     MFF_TUN_ID,                 /* be64 */
     MFF_TUN_SRC,                /* be32 */
@@ -118,6 +119,8 @@ enum mf_field_id {
     /* L4. */
     MFF_TCP_SRC,                /* be16 (used for IPv4 or IPv6) */
     MFF_TCP_DST,                /* be16 (used for IPv4 or IPv6) */
+    MFF_TCP_FLAGS,              /* be16, 12 bits (4 MSB zeroed,
+                                 * used for IPv4 or IPv6) */
 
     MFF_UDP_SRC,                /* be16 (used for IPv4 or IPv6) */
     MFF_UDP_DST,                /* be16 (used for IPv4 or IPv6) */
@@ -178,7 +181,7 @@ enum mf_field_id {
  * A field may only be matched if the correct lower-level protocols are also
  * matched.  For example, the TCP port may be matched only if the Ethernet type
  * matches ETH_TYPE_IP and the IP protocol matches IPPROTO_TCP. */
-enum mf_prereqs {
+enum OVS_PACKED_ENUM mf_prereqs {
     MFP_NONE,
 
     /* L2 requirements. */
@@ -207,13 +210,13 @@ enum mf_prereqs {
 /* Forms of partial-field masking allowed for a field.
  *
  * Every field may be masked as a whole. */
-enum mf_maskable {
+enum OVS_PACKED_ENUM mf_maskable {
     MFM_NONE,                   /* No sub-field masking. */
     MFM_FULLY,                  /* Every bit is individually maskable. */
 };
 
 /* How to format or parse a field's value. */
-enum mf_string {
+enum OVS_PACKED_ENUM mf_string {
     /* Integer formats.
      *
      * The particular MFS_* constant sets the output format.  On input, either
@@ -329,11 +332,18 @@ union mf_subvalue {
 BUILD_ASSERT_DECL(sizeof(union mf_value) == sizeof (union mf_subvalue));
 
 /* Finding mf_fields. */
-const struct mf_field *mf_from_id(enum mf_field_id);
 const struct mf_field *mf_from_name(const char *name);
 const struct mf_field *mf_from_nxm_header(uint32_t nxm_header);
 const struct mf_field *mf_from_nxm_name(const char *nxm_name);
 
+static inline const struct mf_field *
+mf_from_id(enum mf_field_id id)
+{
+    extern const struct mf_field mf_fields[MFF_N_IDS];
+    ovs_assert((unsigned int) id < MFF_N_IDS);
+    return &mf_fields[id];
+}
+
 /* Inspecting wildcarded bits. */
 bool mf_is_all_wild(const struct mf_field *, const struct flow_wildcards *);
 
index 2752623..7e75144 100644 (file)
@@ -61,6 +61,7 @@
 #include "netlink.h"
 #include "ofpbuf.h"
 #include "openflow/openflow.h"
+#include "ovs-atomic.h"
 #include "packets.h"
 #include "poll-loop.h"
 #include "rtnetlink-link.h"
@@ -402,6 +403,11 @@ struct netdev_rx_linux {
  * additional log messages. */
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
 
+/* Polling miimon status for all ports causes performance degradation when
+ * handling a large number of ports. If there are no devices using miimon, then
+ * we skip netdev_linux_miimon_run() and netdev_linux_miimon_wait(). */
+static atomic_int miimon_cnt = ATOMIC_VAR_INIT(0);
+
 static void netdev_linux_run(void);
 
 static int netdev_linux_do_ethtool(const char *name, struct ethtool_cmd *,
@@ -423,6 +429,7 @@ static int set_etheraddr(const char *netdev_name, const uint8_t[ETH_ADDR_LEN]);
 static int get_stats_via_netlink(int ifindex, struct netdev_stats *stats);
 static int get_stats_via_proc(const char *netdev_name, struct netdev_stats *stats);
 static int af_packet_sock(void);
+static bool netdev_linux_miimon_enabled(void);
 static void netdev_linux_miimon_run(void);
 static void netdev_linux_miimon_wait(void);
 
@@ -485,13 +492,24 @@ netdev_linux_notify_sock(void)
     return sock;
 }
 
+static bool
+netdev_linux_miimon_enabled(void)
+{
+    int miimon;
+
+    atomic_read(&miimon_cnt, &miimon);
+    return miimon > 0;
+}
+
 static void
 netdev_linux_run(void)
 {
     struct nl_sock *sock;
     int error;
 
-    netdev_linux_miimon_run();
+    if (netdev_linux_miimon_enabled()) {
+        netdev_linux_miimon_run();
+    }
 
     sock = netdev_linux_notify_sock();
     if (!sock) {
@@ -553,7 +571,9 @@ netdev_linux_wait(void)
 {
     struct nl_sock *sock;
 
-    netdev_linux_miimon_wait();
+    if (netdev_linux_miimon_enabled()) {
+        netdev_linux_miimon_wait();
+    }
     sock = netdev_linux_notify_sock();
     if (sock) {
         nl_sock_wait(sock, POLLIN);
@@ -711,6 +731,11 @@ netdev_linux_destruct(struct netdev *netdev_)
         close(netdev->tap_fd);
     }
 
+    if (netdev->miimon_interval > 0) {
+        int junk;
+        atomic_sub(&miimon_cnt, 1, &junk);
+    }
+
     ovs_mutex_destroy(&netdev->mutex);
 }
 
@@ -1222,6 +1247,14 @@ netdev_linux_set_miimon_interval(struct netdev *netdev_,
     ovs_mutex_lock(&netdev->mutex);
     interval = interval > 0 ? MAX(interval, 100) : 0;
     if (netdev->miimon_interval != interval) {
+        int junk;
+
+        if (interval && !netdev->miimon_interval) {
+            atomic_add(&miimon_cnt, 1, &junk);
+        } else if (!interval && netdev->miimon_interval) {
+            atomic_sub(&miimon_cnt, 1, &junk);
+        }
+
         netdev->miimon_interval = interval;
         timer_set_expired(&netdev->miimon_timer);
     }
index 11cbff3..8282cc2 100644 (file)
@@ -530,6 +530,8 @@ nxm_put_ip(struct ofpbuf *b, const struct match *match,
                         flow->tp_src, match->wc.masks.tp_src);
             nxm_put_16m(b, oxm ? OXM_OF_TCP_DST : NXM_OF_TCP_DST,
                         flow->tp_dst, match->wc.masks.tp_dst);
+            nxm_put_16m(b, NXM_NX_TCP_FLAGS,
+                        flow->tcp_flags, match->wc.masks.tcp_flags);
         } else if (flow->nw_proto == IPPROTO_UDP) {
             nxm_put_16m(b, oxm ? OXM_OF_UDP_SRC : NXM_OF_UDP_SRC,
                         flow->tp_src, match->wc.masks.tp_src);
@@ -570,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 == 21);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 22);
 
     /* Metadata. */
     if (match->wc.masks.in_port.ofp_port) {
@@ -1088,39 +1090,14 @@ nxm_format_reg_move(const struct ofpact_reg_move *move, struct ds *s)
     mf_format_subfield(&move->dst, s);
 }
 
-static void
-set_field_format(const struct ofpact_reg_load *load, struct ds *s)
-{
-    const struct mf_field *mf = load->dst.field;
-    union mf_value value;
-
-    ovs_assert(load->ofpact.compat == OFPUTIL_OFPAT12_SET_FIELD);
-    ds_put_format(s, "set_field:");
-    memset(&value, 0, sizeof value);
-    bitwise_copy(&load->subvalue, sizeof load->subvalue, 0,
-                 &value, mf->n_bytes, 0, load->dst.n_bits);
-    mf_format(mf, &value, NULL, s);
-    ds_put_format(s, "->%s", mf->name);
-}
-
-static void
-load_format(const struct ofpact_reg_load *load, struct ds *s)
+void
+nxm_format_reg_load(const struct ofpact_reg_load *load, struct ds *s)
 {
     ds_put_cstr(s, "load:");
     mf_format_subvalue(&load->subvalue, s);
     ds_put_cstr(s, "->");
     mf_format_subfield(&load->dst, s);
 }
-
-void
-nxm_format_reg_load(const struct ofpact_reg_load *load, struct ds *s)
-{
-    if (load->ofpact.compat == OFPUTIL_OFPAT12_SET_FIELD) {
-        set_field_format(load, s);
-    } else {
-        load_format(load, s);
-    }
-}
 \f
 enum ofperr
 nxm_reg_move_from_openflow(const struct nx_action_reg_move *narm,
@@ -1160,38 +1137,6 @@ nxm_reg_load_from_openflow(const struct nx_action_reg_load *narl,
 
     return nxm_reg_load_check(load, NULL);
 }
-
-enum ofperr
-nxm_reg_load_from_openflow12_set_field(
-    const struct ofp12_action_set_field * oasf, struct ofpbuf *ofpacts)
-{
-    uint16_t oasf_len = ntohs(oasf->len);
-    uint32_t oxm_header = ntohl(oasf->dst);
-    uint8_t oxm_length = NXM_LENGTH(oxm_header);
-    struct ofpact_reg_load *load;
-    const struct mf_field *mf;
-
-    /* ofp12_action_set_field is padded to 64 bits by zero */
-    if (oasf_len != ROUND_UP(sizeof(*oasf) + oxm_length, 8)) {
-        return OFPERR_OFPBAC_BAD_SET_LEN;
-    }
-    if (!is_all_zeros((const uint8_t *)(oasf) + sizeof *oasf + oxm_length,
-                      oasf_len - oxm_length - sizeof *oasf)) {
-        return OFPERR_OFPBAC_BAD_SET_ARGUMENT;
-    }
-
-    if (NXM_HASMASK(oxm_header)) {
-        return OFPERR_OFPBAC_BAD_SET_TYPE;
-    }
-    mf = mf_from_nxm_header(oxm_header);
-    if (!mf) {
-        return OFPERR_OFPBAC_BAD_SET_TYPE;
-    }
-    load = ofpact_put_REG_LOAD(ofpacts);
-    ofpact_set_field_init(load, mf, oasf + 1);
-
-    return nxm_reg_load_check(load, NULL);
-}
 \f
 enum ofperr
 nxm_reg_move_check(const struct ofpact_reg_move *move, const struct flow *flow)
@@ -1226,8 +1171,9 @@ nxm_reg_move_to_nxast(const struct ofpact_reg_move *move,
     narm->dst = htonl(move->dst.field->nxm_header);
 }
 
-static void
-reg_load_to_nxast(const struct ofpact_reg_load *load, struct ofpbuf *openflow)
+void
+nxm_reg_load_to_nxast(const struct ofpact_reg_load *load,
+                      struct ofpbuf *openflow)
 {
     struct nx_action_reg_load *narl;
 
@@ -1236,71 +1182,6 @@ reg_load_to_nxast(const struct ofpact_reg_load *load, struct ofpbuf *openflow)
     narl->dst = htonl(load->dst.field->nxm_header);
     narl->value = load->subvalue.be64[1];
 }
-
-static void
-set_field_to_ofast(const struct ofpact_reg_load *load,
-                      struct ofpbuf *openflow)
-{
-    const struct mf_field *mf = load->dst.field;
-    uint16_t padded_value_len = ROUND_UP(mf->n_bytes, 8);
-    struct ofp12_action_set_field *oasf;
-    char *value;
-
-    /* Set field is the only action of variable length (so far),
-     * so handling the variable length portion is open-coded here */
-    oasf = ofputil_put_OFPAT12_SET_FIELD(openflow);
-    oasf->dst = htonl(mf->oxm_header);
-    oasf->len = htons(ntohs(oasf->len) + padded_value_len);
-
-    value = ofpbuf_put_zeros(openflow, padded_value_len);
-    bitwise_copy(&load->subvalue, sizeof load->subvalue, load->dst.ofs,
-                 value, mf->n_bytes, load->dst.ofs, load->dst.n_bits);
-}
-
-void
-nxm_reg_load_to_nxast(const struct ofpact_reg_load *load,
-                      struct ofpbuf *openflow)
-{
-
-    if (load->ofpact.compat == OFPUTIL_OFPAT12_SET_FIELD) {
-        struct ofp_header *oh = (struct ofp_header *)openflow->l2;
-
-        switch(oh->version) {
-        case OFP13_VERSION:
-        case OFP12_VERSION:
-            set_field_to_ofast(load, openflow);
-            break;
-
-        case OFP11_VERSION:
-        case OFP10_VERSION:
-            if (load->dst.n_bits < 64) {
-                reg_load_to_nxast(load, openflow);
-            } else {
-                /* Split into 64bit chunks */
-                int chunk, ofs;
-                for (ofs = 0; ofs < load->dst.n_bits; ofs += chunk) {
-                    struct ofpact_reg_load subload = *load;
-
-                    chunk = MIN(load->dst.n_bits - ofs, 64);
-
-                    subload.dst.field =  load->dst.field;
-                    subload.dst.ofs = load->dst.ofs + ofs;
-                    subload.dst.n_bits = chunk;
-                    bitwise_copy(&load->subvalue, sizeof load->subvalue, ofs,
-                                 &subload.subvalue, sizeof subload.subvalue, 0,
-                                 chunk);
-                    reg_load_to_nxast(&subload, openflow);
-                }
-            }
-            break;
-
-        default:
-            NOT_REACHED();
-        }
-    } else {
-        reg_load_to_nxast(load, openflow);
-    }
-}
 \f
 /* nxm_execute_reg_move(), nxm_execute_reg_load(). */
 
index 7065225..ee3f24c 100644 (file)
@@ -70,8 +70,6 @@ enum ofperr nxm_reg_move_from_openflow(const struct nx_action_reg_move *,
                                        struct ofpbuf *ofpacts);
 enum ofperr nxm_reg_load_from_openflow(const struct nx_action_reg_load *,
                                        struct ofpbuf *ofpacts);
-enum ofperr nxm_reg_load_from_openflow12_set_field(
-    const struct ofp12_action_set_field * oasf, struct ofpbuf *ofpacts);
 
 enum ofperr nxm_reg_move_check(const struct ofpact_reg_move *,
                                const struct flow *);
index 19fcc1c..af3370d 100644 (file)
@@ -131,6 +131,7 @@ odp_execute_set_action(struct ofpbuf *packet, const struct nlattr *a,
     case OVS_KEY_ATTR_ICMP:
     case OVS_KEY_ATTR_ICMPV6:
     case OVS_KEY_ATTR_ND:
+    case OVS_KEY_ATTR_TCP_FLAGS:
     case __OVS_KEY_ATTR_MAX:
     default:
         NOT_REACHED();
index 6875e01..6ac3853 100644 (file)
@@ -109,6 +109,7 @@ ovs_key_attr_to_string(enum ovs_key_attr attr, char *namebuf, size_t bufsize)
     case OVS_KEY_ATTR_IPV4: return "ipv4";
     case OVS_KEY_ATTR_IPV6: return "ipv6";
     case OVS_KEY_ATTR_TCP: return "tcp";
+    case OVS_KEY_ATTR_TCP_FLAGS: return "tcp_flags";
     case OVS_KEY_ATTR_UDP: return "udp";
     case OVS_KEY_ATTR_SCTP: return "sctp";
     case OVS_KEY_ATTR_ICMP: return "icmp";
@@ -735,6 +736,7 @@ odp_flow_key_attr_len(uint16_t type)
     case OVS_KEY_ATTR_IPV4: return sizeof(struct ovs_key_ipv4);
     case OVS_KEY_ATTR_IPV6: return sizeof(struct ovs_key_ipv6);
     case OVS_KEY_ATTR_TCP: return sizeof(struct ovs_key_tcp);
+    case OVS_KEY_ATTR_TCP_FLAGS: return 2;
     case OVS_KEY_ATTR_UDP: return sizeof(struct ovs_key_udp);
     case OVS_KEY_ATTR_SCTP: return sizeof(struct ovs_key_sctp);
     case OVS_KEY_ATTR_ICMP: return sizeof(struct ovs_key_icmp);
@@ -1230,6 +1232,13 @@ format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
         }
         break;
 
+    case OVS_KEY_ATTR_TCP_FLAGS:
+        ds_put_format(ds, "0x%03"PRIx16, ntohs(nl_attr_get_be16(a)));
+        if (!is_exact) {
+            ds_put_format(ds, "/0x%03"PRIx16, ntohs(nl_attr_get_be16(ma)));
+        }
+        break;
+
     case OVS_KEY_ATTR_UDP:
         if (!is_exact) {
             const struct ovs_key_udp *udp_mask = nl_attr_get(ma);
@@ -2047,6 +2056,25 @@ 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) {
+            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) {
+            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,
+                                htons(UINT16_MAX));
+            }
+            return n;
+        }
+    }
+
     {
         int udp_src;
         int udp_dst;
@@ -2559,6 +2587,10 @@ odp_flow_key_from_flow__(struct ofpbuf *buf, const struct flow *data,
                                                sizeof *tcp_key);
             tcp_key->tcp_src = data->tp_src;
             tcp_key->tcp_dst = data->tp_dst;
+
+            if (data->tcp_flags) {
+                nl_msg_put_be16(buf, OVS_KEY_ATTR_TCP_FLAGS, data->tcp_flags);
+            }
         } else if (flow->nw_proto == IPPROTO_UDP) {
             struct ovs_key_udp *udp_key;
 
@@ -2944,6 +2976,10 @@ parse_l2_5_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
             flow->tp_dst = tcp_key->tcp_dst;
             expected_bit = OVS_KEY_ATTR_TCP;
         }
+        if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TCP_FLAGS)) {
+            expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TCP_FLAGS;
+            flow->tcp_flags = nl_attr_get_be16(attrs[OVS_KEY_ATTR_TCP_FLAGS]);
+        }
     } else if (src_flow->nw_proto == IPPROTO_UDP
                && (src_flow->dl_type == htons(ETH_TYPE_IP) ||
                    src_flow->dl_type == htons(ETH_TYPE_IPV6))
@@ -2959,10 +2995,10 @@ parse_l2_5_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
             flow->tp_dst = udp_key->udp_dst;
             expected_bit = OVS_KEY_ATTR_UDP;
         }
-    } else if (flow->nw_proto == IPPROTO_SCTP
-               && (flow->dl_type == htons(ETH_TYPE_IP) ||
-                   flow->dl_type == htons(ETH_TYPE_IPV6))
-               && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
+    } else if (src_flow->nw_proto == IPPROTO_SCTP
+               && (src_flow->dl_type == htons(ETH_TYPE_IP) ||
+                   src_flow->dl_type == htons(ETH_TYPE_IPV6))
+               && !(src_flow->nw_frag & FLOW_NW_FRAG_LATER)) {
         if (!is_mask) {
             expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_SCTP;
         }
index cccd6b1..bae479e 100644 (file)
@@ -53,6 +53,8 @@ union ofp_action {
     struct ofp11_action_push push;
     struct ofp11_action_pop_mpls ofp11_pop_mpls;
     struct ofp11_action_set_queue ofp11_set_queue;
+    struct ofp11_action_mpls_label ofp11_mpls_label;
+    struct ofp11_action_mpls_tc ofp11_mpls_tc;
     struct ofp11_action_mpls_ttl ofp11_mpls_ttl;
     struct ofp11_action_group group;
     struct ofp12_action_set_field set_field;
@@ -77,6 +79,8 @@ union ofp_action {
     struct nx_action_pop_mpls pop_mpls;
     struct nx_action_sample sample;
     struct nx_action_learn learn;
+    struct nx_action_mpls_label mpls_label;
+    struct nx_action_mpls_tc mpls_tc;
 };
 
 static enum ofperr
@@ -89,7 +93,7 @@ output_from_openflow10(const struct ofp10_action_output *oao,
     output->port = u16_to_ofp(ntohs(oao->port));
     output->max_len = ntohs(oao->max_len);
 
-    return ofputil_check_output_port(output->port, OFPP_MAX);
+    return ofpact_check_output_port(output->port, OFPP_MAX);
 }
 
 static enum ofperr
@@ -228,7 +232,7 @@ dec_ttl_from_openflow(struct ofpbuf *out, enum ofputil_action_code compat)
 
 static enum ofperr
 dec_ttl_cnt_ids_from_openflow(const struct nx_action_cnt_ids *nac_ids,
-                      struct ofpbuf *out)
+                              struct ofpbuf *out)
 {
     struct ofpact_cnt_ids *ids;
     size_t ids_size;
@@ -473,6 +477,14 @@ ofpact_from_nxast(const union ofp_action *a, enum ofputil_action_code code,
                                         OFPACT_MPLS_AFTER_VLAN, out);
         break;
 
+    case OFPUTIL_NXAST_SET_MPLS_LABEL:
+        ofpact_put_SET_MPLS_LABEL(out)->label = a->mpls_label.label;
+        break;
+
+    case OFPUTIL_NXAST_SET_MPLS_TC:
+        ofpact_put_SET_MPLS_TC(out)->tc = a->mpls_tc.tc;
+        break;
+
     case OFPUTIL_NXAST_SET_MPLS_TTL:
         ofpact_put_SET_MPLS_TTL(out)->ttl = a->mpls_ttl.ttl;
         break;
@@ -497,10 +509,14 @@ ofpact_from_nxast(const union ofp_action *a, enum ofputil_action_code code,
 }
 
 static enum ofperr
-ofpact_from_openflow10(const union ofp_action *a, struct ofpbuf *out)
+ofpact_from_openflow10(const union ofp_action *a,
+                       enum ofp_version version OVS_UNUSED,
+                       struct ofpbuf *out)
 {
     enum ofputil_action_code code;
     enum ofperr error;
+    struct ofpact_vlan_vid *vlan_vid;
+    struct ofpact_vlan_pcp *vlan_pcp;
 
     error = decode_openflow10_action(a, &code);
     if (error) {
@@ -520,18 +536,24 @@ ofpact_from_openflow10(const union ofp_action *a, struct ofpbuf *out)
         if (a->vlan_vid.vlan_vid & ~htons(0xfff)) {
             return OFPERR_OFPBAC_BAD_ARGUMENT;
         }
-        ofpact_put_SET_VLAN_VID(out)->vlan_vid = ntohs(a->vlan_vid.vlan_vid);
+        vlan_vid = ofpact_put_SET_VLAN_VID(out);
+        vlan_vid->vlan_vid = ntohs(a->vlan_vid.vlan_vid);
+        vlan_vid->push_vlan_if_needed = true;
+        vlan_vid->ofpact.compat = code;
         break;
 
     case OFPUTIL_OFPAT10_SET_VLAN_PCP:
         if (a->vlan_pcp.vlan_pcp & ~7) {
             return OFPERR_OFPBAC_BAD_ARGUMENT;
         }
-        ofpact_put_SET_VLAN_PCP(out)->vlan_pcp = a->vlan_pcp.vlan_pcp;
+        vlan_pcp = ofpact_put_SET_VLAN_PCP(out);
+        vlan_pcp->vlan_pcp = a->vlan_pcp.vlan_pcp;
+        vlan_pcp->push_vlan_if_needed = true;
+        vlan_pcp->ofpact.compat = code;
         break;
 
     case OFPUTIL_OFPAT10_STRIP_VLAN:
-        ofpact_put_STRIP_VLAN(out);
+        ofpact_put_STRIP_VLAN(out)->ofpact.compat = code;
         break;
 
     case OFPUTIL_OFPAT10_SET_DL_SRC:
@@ -580,6 +602,10 @@ ofpact_from_openflow10(const union ofp_action *a, struct ofpbuf *out)
     return error;
 }
 
+static enum ofperr ofpact_from_openflow11(const union ofp_action *,
+                                          enum ofp_version,
+                                          struct ofpbuf *out);
+
 static inline union ofp_action *
 action_next(const union ofp_action *a)
 {
@@ -621,15 +647,19 @@ log_bad_action(const union ofp_action *actions, size_t max_actions,
 
 static enum ofperr
 ofpacts_from_openflow(const union ofp_action *in, size_t n_in,
-                      struct ofpbuf *out,
-                      enum ofperr (*ofpact_from_openflow)(
-                          const union ofp_action *a, struct ofpbuf *out))
+                      enum ofp_version version, struct ofpbuf *out)
 {
     const union ofp_action *a;
     size_t left;
 
+    enum ofperr (*ofpact_from_openflow)(const union ofp_action *a,
+                                        enum ofp_version,
+                                        struct ofpbuf *out) =
+        (version == OFP10_VERSION) ?
+        ofpact_from_openflow10 : ofpact_from_openflow11;
+
     ACTION_FOR_EACH (a, left, in, n_in) {
-        enum ofperr error = ofpact_from_openflow(a, out);
+        enum ofperr error = ofpact_from_openflow(a, version, out);
         if (error) {
             log_bad_action(in, n_in, a, error);
             return error;
@@ -645,20 +675,28 @@ ofpacts_from_openflow(const union ofp_action *in, size_t n_in,
     return 0;
 }
 
-static enum ofperr
-ofpacts_from_openflow10(const union ofp_action *in, size_t n_in,
-                        struct ofpbuf *out)
-{
-    return ofpacts_from_openflow(in, n_in, out, ofpact_from_openflow10);
-}
-
-static enum ofperr
-ofpacts_pull_actions(struct ofpbuf *openflow, unsigned int actions_len,
-                     struct ofpbuf *ofpacts,
-                     enum ofperr (*translate)(const union ofp_action *actions,
-                                              size_t max_actions,
-                                              struct ofpbuf *ofpacts))
-{
+/* Attempts to convert 'actions_len' bytes of OpenFlow actions from the
+ * front of 'openflow' into ofpacts.  On success, replaces any existing content
+ * in 'ofpacts' by the converted ofpacts; on failure, clears 'ofpacts'.
+ * Returns 0 if successful, otherwise an OpenFlow error.
+ *
+ * Actions are processed according to their OpenFlow version which
+ * is provided in the 'version' parameter.
+ *
+ * In most places in OpenFlow 1.1 and 1.2, actions appear encapsulated in
+ * instructions, so you should call ofpacts_pull_openflow_instructions()
+ * instead of this function.
+ *
+ * The parsed actions are valid generically, but they may not be valid in a
+ * specific context.  For example, port numbers up to OFPP_MAX are valid
+ * generically, but specific datapaths may only support port numbers in a
+ * smaller range.  Use ofpacts_check() to additional check whether actions are
+ * valid in a specific context. */
+enum ofperr
+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;
@@ -679,7 +717,8 @@ ofpacts_pull_actions(struct ofpbuf *openflow, unsigned int actions_len,
         return OFPERR_OFPBRC_BAD_LEN;
     }
 
-    error = translate(actions, actions_len / OFP_ACTION_ALIGN, ofpacts);
+    error = ofpacts_from_openflow(actions, actions_len / OFP_ACTION_ALIGN,
+                                  version, ofpacts);
     if (error) {
         ofpbuf_clear(ofpacts);
         return error;
@@ -692,23 +731,6 @@ ofpacts_pull_actions(struct ofpbuf *openflow, unsigned int actions_len,
     return error;
 }
 
-/* Attempts to convert 'actions_len' bytes of OpenFlow 1.0 actions from the
- * front of 'openflow' into ofpacts.  On success, replaces any existing content
- * in 'ofpacts' by the converted ofpacts; on failure, clears 'ofpacts'.
- * Returns 0 if successful, otherwise an OpenFlow error.
- *
- * The parsed actions are valid generically, but they may not be valid in a
- * specific context.  For example, port numbers up to OFPP_MAX are valid
- * generically, but specific datapaths may only support port numbers in a
- * smaller range.  Use ofpacts_check() to additional check whether actions are
- * valid in a specific context. */
-enum ofperr
-ofpacts_pull_openflow10(struct ofpbuf *openflow, unsigned int actions_len,
-                        struct ofpbuf *ofpacts)
-{
-    return ofpacts_pull_actions(openflow, actions_len, ofpacts,
-                                ofpacts_from_openflow10);
-}
 \f
 /* OpenFlow 1.1 actions. */
 
@@ -751,6 +773,325 @@ decode_openflow11_action(const union ofp_action *a,
     }
 }
 
+static enum ofperr
+set_field_from_openflow(const struct ofp12_action_set_field *oasf,
+                        struct ofpbuf *ofpacts)
+{
+    uint16_t oasf_len = ntohs(oasf->len);
+    uint32_t oxm_header = ntohl(oasf->dst);
+    uint8_t oxm_length = NXM_LENGTH(oxm_header);
+    struct ofpact_set_field *sf;
+    const struct mf_field *mf;
+
+    /* ofp12_action_set_field is padded to 64 bits by zero */
+    if (oasf_len != ROUND_UP(sizeof *oasf + oxm_length, 8)) {
+        return OFPERR_OFPBAC_BAD_SET_LEN;
+    }
+    if (!is_all_zeros((const uint8_t *)oasf + sizeof *oasf + oxm_length,
+                      oasf_len - oxm_length - sizeof *oasf)) {
+        return OFPERR_OFPBAC_BAD_SET_ARGUMENT;
+    }
+
+    if (NXM_HASMASK(oxm_header)) {
+        return OFPERR_OFPBAC_BAD_SET_TYPE;
+    }
+    mf = mf_from_nxm_header(oxm_header);
+    if (!mf) {
+        return OFPERR_OFPBAC_BAD_SET_TYPE;
+    }
+    ovs_assert(mf->n_bytes == oxm_length);
+    /* oxm_length is now validated to be compatible with mf_value. */
+    if (!mf->writable) {
+        VLOG_WARN_RL(&rl, "destination field %s is not writable", mf->name);
+        return OFPERR_OFPBAC_BAD_SET_ARGUMENT;
+    }
+    sf = ofpact_put_SET_FIELD(ofpacts);
+    sf->field = mf;
+    memcpy(&sf->value, oasf + 1, mf->n_bytes);
+
+    /* The value must be valid for match and must have the OFPVID_PRESENT bit
+     * on for OXM_OF_VLAN_VID. */
+    if (!mf_is_value_valid(mf, &sf->value)
+        || (mf->id == MFF_VLAN_VID
+            && !(sf->value.be16 & htons(OFPVID12_PRESENT)))) {
+        struct ds ds = DS_EMPTY_INITIALIZER;
+        mf_format(mf, &sf->value, NULL, &ds);
+        VLOG_WARN_RL(&rl, "Invalid value for set field %s: %s",
+                     mf->name, ds_cstr(&ds));
+        ds_destroy(&ds);
+
+        return OFPERR_OFPBAC_BAD_SET_ARGUMENT;
+    }
+    return 0;
+}
+
+static void
+set_field_to_openflow12(const struct ofpact_set_field *sf,
+                        struct ofpbuf *openflow)
+{
+    uint16_t padded_value_len = ROUND_UP(sf->field->n_bytes, 8);
+    struct ofp12_action_set_field *oasf;
+    char *value;
+
+    oasf = ofputil_put_OFPAT12_SET_FIELD(openflow);
+    oasf->dst = htonl(sf->field->oxm_header);
+    oasf->len = htons(sizeof *oasf + padded_value_len);
+
+    value = ofpbuf_put_zeros(openflow, padded_value_len);
+    memcpy(value, &sf->value, sf->field->n_bytes);
+}
+
+/* Convert 'sf' to one or two REG_LOADs. */
+static void
+set_field_to_nxast(const struct ofpact_set_field *sf, struct ofpbuf *openflow)
+{
+    const struct mf_field *mf = sf->field;
+    struct nx_action_reg_load *narl;
+
+    if (mf->n_bits > 64) {
+        ovs_assert(mf->n_bytes == 16); /* IPv6 addr. */
+        /* Split into 64bit chunks */
+        /* Lower bits first. */
+        narl = ofputil_put_NXAST_REG_LOAD(openflow);
+        narl->ofs_nbits = nxm_encode_ofs_nbits(0, 64);
+        narl->dst = htonl(mf->nxm_header);
+        memcpy(&narl->value, &sf->value.ipv6.s6_addr[8], sizeof narl->value);
+        /* Higher bits next. */
+        narl = ofputil_put_NXAST_REG_LOAD(openflow);
+        narl->ofs_nbits = nxm_encode_ofs_nbits(64, mf->n_bits - 64);
+        narl->dst = htonl(mf->nxm_header);
+        memcpy(&narl->value, &sf->value.ipv6.s6_addr[0], sizeof narl->value);
+    } else {
+        narl = ofputil_put_NXAST_REG_LOAD(openflow);
+        narl->ofs_nbits = nxm_encode_ofs_nbits(0, mf->n_bits);
+        narl->dst = htonl(mf->nxm_header);
+        memset(&narl->value, 0, 8 - mf->n_bytes);
+        memcpy((char*)&narl->value + (8 - mf->n_bytes),
+               &sf->value, mf->n_bytes);
+    }
+}
+
+/* Convert 'sf' to standard OpenFlow 1.1 actions, if we can, falling back
+ * to Nicira extensions if we must.
+ *
+ * We check only meta-flow types that can appear within set field actions and
+ * that have a mapping to compatible action types.  These struct mf_field
+ * definitions have a defined OXM or NXM header value and specify the field as
+ * writable. */
+static void
+set_field_to_openflow11(const struct ofpact_set_field *sf,
+                        struct ofpbuf *openflow)
+{
+    switch ((int) sf->field->id) {
+    case MFF_VLAN_TCI:
+        /* NXM_OF_VLAN_TCI to OpenFlow 1.1 mapping:
+         *
+         * If CFI=1, Add or modify VLAN VID & PCP.
+         *    OpenFlow 1.1 set actions only apply if the packet
+         *    already has VLAN tags.  To be sure that is the case
+         *    we have to push a VLAN header.  As we do not support
+         *    multiple layers of VLANs, this is a no-op, if a VLAN
+         *    header already exists.  This may backfire, however,
+         *    when we start supporting multiple layers of VLANs.
+         * If CFI=0, strip VLAN header, if any.
+         */
+        if (sf->value.be16 & htons(VLAN_CFI)) {
+            /* Push a VLAN tag, if one was not seen at action validation
+             * time. */
+            if (!sf->flow_has_vlan) {
+                ofputil_put_OFPAT11_PUSH_VLAN(openflow)->ethertype
+                    = htons(ETH_TYPE_VLAN_8021Q);
+            }
+            ofputil_put_OFPAT11_SET_VLAN_VID(openflow)->vlan_vid
+                = sf->value.be16 & htons(VLAN_VID_MASK);
+            ofputil_put_OFPAT11_SET_VLAN_PCP(openflow)->vlan_pcp
+                = vlan_tci_to_pcp(sf->value.be16);
+        } else {
+            /* If the flow did not match on vlan, we have no way of
+             * knowing if the vlan tag exists, so we must POP just to be
+             * sure. */
+            ofputil_put_OFPAT11_POP_VLAN(openflow);
+        }
+        break;
+
+    case MFF_VLAN_VID:
+        /* OXM VLAN_PCP to OpenFlow 1.1.
+         * Set field on OXM_OF_VLAN_VID onlyapplies to an existing vlan
+         * tag.  Clear the OFPVID_PRESENT bit.
+         */
+        ofputil_put_OFPAT11_SET_VLAN_VID(openflow)->vlan_vid
+            = sf->value.be16 & htons(VLAN_VID_MASK);
+        break;
+
+    case MFF_VLAN_PCP:
+        /* OXM VLAN_PCP to OpenFlow 1.1.
+         * OXM_OF_VLAN_PCP only applies to existing vlan tag. */
+        ofputil_put_OFPAT11_SET_VLAN_PCP(openflow)->vlan_pcp = sf->value.u8;
+        break;
+
+    case MFF_ETH_SRC:
+        memcpy(ofputil_put_OFPAT11_SET_DL_SRC(openflow)->dl_addr,
+               sf->value.mac, ETH_ADDR_LEN);
+        break;
+
+    case MFF_ETH_DST:
+        memcpy(ofputil_put_OFPAT11_SET_DL_DST(openflow)->dl_addr,
+               sf->value.mac, ETH_ADDR_LEN);
+        break;
+
+    case MFF_MPLS_LABEL:
+        ofputil_put_OFPAT11_SET_MPLS_LABEL(openflow)->mpls_label =
+            sf->value.be32;
+        break;
+
+    case MFF_MPLS_TC:
+        ofputil_put_OFPAT11_SET_MPLS_TC(openflow)->mpls_tc = sf->value.u8;
+        break;
+
+    case MFF_IPV4_SRC:
+        ofputil_put_OFPAT11_SET_NW_SRC(openflow)->nw_addr = sf->value.be32;
+        break;
+
+    case MFF_IPV4_DST:
+        ofputil_put_OFPAT11_SET_NW_DST(openflow)->nw_addr = sf->value.be32;
+        break;
+
+    case MFF_IP_DSCP:
+        ofputil_put_OFPAT11_SET_NW_TOS(openflow)->nw_tos = sf->value.u8;
+        break;
+
+    case MFF_IP_DSCP_SHIFTED:
+        ofputil_put_OFPAT11_SET_NW_TOS(openflow)->nw_tos = sf->value.u8 << 2;
+        break;
+
+    case MFF_IP_ECN:
+        ofputil_put_OFPAT11_SET_NW_ECN(openflow)->nw_ecn = sf->value.u8;
+        break;
+
+    case MFF_IP_TTL:
+        ofputil_put_OFPAT11_SET_NW_TTL(openflow)->nw_ttl = sf->value.u8;
+        break;
+
+    case MFF_TCP_SRC:
+    case MFF_UDP_SRC:
+    case MFF_SCTP_SRC:
+        ofputil_put_OFPAT11_SET_TP_SRC(openflow)->tp_port = sf->value.be16;
+        break;
+
+    case MFF_TCP_DST:
+    case MFF_UDP_DST:
+    case MFF_SCTP_DST:
+        ofputil_put_OFPAT11_SET_TP_DST(openflow)->tp_port = sf->value.be16;
+        break;
+
+    default:
+        set_field_to_nxast(sf, openflow);
+        break;
+    }
+}
+
+/* Convert 'sf' to standard OpenFlow 1.0 actions, if we can, falling back
+ * to Nicira extensions if we must.
+ *
+ * We check only meta-flow types that can appear within set field actions and
+ * that have a mapping to compatible action types.  These struct mf_field
+ * definitions have a defined OXM or NXM header value and specify the field as
+ * writable. */
+static void
+set_field_to_openflow10(const struct ofpact_set_field *sf,
+                        struct ofpbuf *openflow)
+{
+    switch ((int) sf->field->id) {
+    case MFF_VLAN_TCI:
+        /* NXM_OF_VLAN_TCI to OpenFlow 1.0 mapping:
+         *
+         * If CFI=1, Add or modify VLAN VID & PCP.
+         * If CFI=0, strip VLAN header, if any.
+         */
+        if (sf->value.be16 & htons(VLAN_CFI)) {
+            ofputil_put_OFPAT10_SET_VLAN_VID(openflow)->vlan_vid
+                = sf->value.be16 & htons(VLAN_VID_MASK);
+            ofputil_put_OFPAT10_SET_VLAN_PCP(openflow)->vlan_pcp
+                = vlan_tci_to_pcp(sf->value.be16);
+        } else {
+            ofputil_put_OFPAT10_STRIP_VLAN(openflow);
+        }
+        break;
+
+    case MFF_VLAN_VID:
+        /* OXM VLAN_VID to OpenFlow 1.0.
+         * Set field on OXM_OF_VLAN_VID onlyapplies to an existing vlan
+         * tag.  Clear the OFPVID_PRESENT bit.
+         */
+        ofputil_put_OFPAT10_SET_VLAN_VID(openflow)->vlan_vid
+            = sf->value.be16 & htons(VLAN_VID_MASK);
+        break;
+
+    case MFF_VLAN_PCP:
+        /* OXM VLAN_PCP to OpenFlow 1.0.
+         * OXM_OF_VLAN_PCP only applies to existing vlan tag. */
+        ofputil_put_OFPAT10_SET_VLAN_PCP(openflow)->vlan_pcp = sf->value.u8;
+        break;
+
+    case MFF_ETH_SRC:
+        memcpy(ofputil_put_OFPAT10_SET_DL_SRC(openflow)->dl_addr,
+               sf->value.mac, ETH_ADDR_LEN);
+        break;
+
+    case MFF_ETH_DST:
+        memcpy(ofputil_put_OFPAT10_SET_DL_DST(openflow)->dl_addr,
+               sf->value.mac, ETH_ADDR_LEN);
+        break;
+
+    case MFF_IPV4_SRC:
+        ofputil_put_OFPAT10_SET_NW_SRC(openflow)->nw_addr = sf->value.be32;
+        break;
+
+    case MFF_IPV4_DST:
+        ofputil_put_OFPAT10_SET_NW_DST(openflow)->nw_addr = sf->value.be32;
+        break;
+
+    case MFF_IP_DSCP:
+        ofputil_put_OFPAT10_SET_NW_TOS(openflow)->nw_tos = sf->value.u8;
+        break;
+
+    case MFF_IP_DSCP_SHIFTED:
+        ofputil_put_OFPAT10_SET_NW_TOS(openflow)->nw_tos = sf->value.u8 << 2;
+        break;
+
+    case MFF_TCP_SRC:
+    case MFF_UDP_SRC:
+        ofputil_put_OFPAT10_SET_TP_SRC(openflow)->tp_port = sf->value.be16;
+        break;
+
+    case MFF_TCP_DST:
+    case MFF_UDP_DST:
+        ofputil_put_OFPAT10_SET_TP_DST(openflow)->tp_port = sf->value.be16;
+        break;
+
+    default:
+        set_field_to_nxast(sf, openflow);
+        break;
+    }
+}
+
+static void
+set_field_to_openflow(const struct ofpact_set_field *sf,
+                      struct ofpbuf *openflow)
+{
+    struct ofp_header *oh = (struct ofp_header *)openflow->l2;
+
+    if (oh->version >= OFP12_VERSION) {
+        set_field_to_openflow12(sf, openflow);
+    } else if (oh->version == OFP11_VERSION) {
+        set_field_to_openflow11(sf, openflow);
+    } else if (oh->version == OFP10_VERSION) {
+        set_field_to_openflow10(sf, openflow);
+    } else {
+        NOT_REACHED();
+    }
+}
+
 static enum ofperr
 output_from_openflow11(const struct ofp11_action_output *oao,
                        struct ofpbuf *out)
@@ -766,20 +1107,41 @@ output_from_openflow11(const struct ofp11_action_output *oao,
         return error;
     }
 
-    return ofputil_check_output_port(output->port, OFPP_MAX);
+    return ofpact_check_output_port(output->port, OFPP_MAX);
 }
 
 static enum ofperr
-ofpact_from_openflow11(const union ofp_action *a, struct ofpbuf *out)
+ofpact_from_openflow11(const union ofp_action *a, enum ofp_version version,
+                       struct ofpbuf *out)
 {
     enum ofputil_action_code code;
     enum ofperr error;
+    struct ofpact_vlan_vid *vlan_vid;
+    struct ofpact_vlan_pcp *vlan_pcp;
 
     error = decode_openflow11_action(a, &code);
     if (error) {
         return error;
     }
 
+    if (version >= OFP12_VERSION) {
+        switch ((int)code) {
+        case OFPUTIL_OFPAT11_SET_VLAN_VID:
+        case OFPUTIL_OFPAT11_SET_VLAN_PCP:
+        case OFPUTIL_OFPAT11_SET_DL_SRC:
+        case OFPUTIL_OFPAT11_SET_DL_DST:
+        case OFPUTIL_OFPAT11_SET_NW_SRC:
+        case OFPUTIL_OFPAT11_SET_NW_DST:
+        case OFPUTIL_OFPAT11_SET_NW_TOS:
+        case OFPUTIL_OFPAT11_SET_NW_ECN:
+        case OFPUTIL_OFPAT11_SET_TP_SRC:
+        case OFPUTIL_OFPAT11_SET_TP_DST:
+            VLOG_WARN_RL(&rl, "Deprecated action %s received over %s",
+                         ofputil_action_name_from_code(code),
+                         ofputil_version_to_string(version));
+        }
+    }
+
     switch (code) {
     case OFPUTIL_ACTION_INVALID:
 #define OFPAT10_ACTION(ENUM, STRUCT, NAME) case OFPUTIL_##ENUM:
@@ -793,14 +1155,20 @@ ofpact_from_openflow11(const union ofp_action *a, struct ofpbuf *out)
         if (a->vlan_vid.vlan_vid & ~htons(0xfff)) {
             return OFPERR_OFPBAC_BAD_ARGUMENT;
         }
-        ofpact_put_SET_VLAN_VID(out)->vlan_vid = ntohs(a->vlan_vid.vlan_vid);
+        vlan_vid = ofpact_put_SET_VLAN_VID(out);
+        vlan_vid->vlan_vid = ntohs(a->vlan_vid.vlan_vid);
+        vlan_vid->push_vlan_if_needed = false;
+        vlan_vid->ofpact.compat = code;
         break;
 
     case OFPUTIL_OFPAT11_SET_VLAN_PCP:
         if (a->vlan_pcp.vlan_pcp & ~7) {
             return OFPERR_OFPBAC_BAD_ARGUMENT;
         }
-        ofpact_put_SET_VLAN_PCP(out)->vlan_pcp = a->vlan_pcp.vlan_pcp;
+        vlan_pcp = ofpact_put_SET_VLAN_PCP(out);
+        vlan_pcp->vlan_pcp = a->vlan_pcp.vlan_pcp;
+        vlan_pcp->push_vlan_if_needed = false;
+        vlan_pcp->ofpact.compat = code;
         break;
 
     case OFPUTIL_OFPAT11_PUSH_VLAN:
@@ -812,7 +1180,7 @@ ofpact_from_openflow11(const union ofp_action *a, struct ofpbuf *out)
         break;
 
     case OFPUTIL_OFPAT11_POP_VLAN:
-        ofpact_put_STRIP_VLAN(out);
+        ofpact_put_STRIP_VLAN(out)->ofpact.compat = code;
         break;
 
     case OFPUTIL_OFPAT11_SET_QUEUE:
@@ -869,7 +1237,15 @@ ofpact_from_openflow11(const union ofp_action *a, struct ofpbuf *out)
         break;
 
     case OFPUTIL_OFPAT12_SET_FIELD:
-        return nxm_reg_load_from_openflow12_set_field(&a->set_field, out);
+        return set_field_from_openflow(&a->set_field, out);
+
+    case OFPUTIL_OFPAT11_SET_MPLS_LABEL:
+        ofpact_put_SET_MPLS_LABEL(out)->label = a->ofp11_mpls_label.mpls_label;
+        break;
+
+    case OFPUTIL_OFPAT11_SET_MPLS_TC:
+        ofpact_put_SET_MPLS_TC(out)->tc = a->ofp11_mpls_tc.mpls_tc;
+        break;
 
     case OFPUTIL_OFPAT11_SET_MPLS_TTL:
         ofpact_put_SET_MPLS_TTL(out)->ttl = a->ofp11_mpls_ttl.mpls_ttl;
@@ -880,7 +1256,10 @@ ofpact_from_openflow11(const union ofp_action *a, struct ofpbuf *out)
         break;
 
     case OFPUTIL_OFPAT11_PUSH_MPLS:
+        /* OpenFlow 1.3 has different semantics. */
         error = push_mpls_from_openflow(a->push.ethertype,
+                                        version >= OFP13_VERSION ?
+                                        OFPACT_MPLS_BEFORE_VLAN :
                                         OFPACT_MPLS_AFTER_VLAN, out);
         break;
 
@@ -903,13 +1282,6 @@ ofpact_from_openflow11(const union ofp_action *a, struct ofpbuf *out)
     return error;
 }
 
-static enum ofperr
-ofpacts_from_openflow11(const union ofp_action *in, size_t n_in,
-                        struct ofpbuf *out)
-{
-    return ofpacts_from_openflow(in, n_in, out, ofpact_from_openflow11);
-}
-
 /* True if an action sets the value of a field
  * in a way that is compatibile with the action set.
  * False otherwise. */
@@ -917,6 +1289,7 @@ static bool
 ofpact_is_set_action(const struct ofpact *a)
 {
     switch (a->type) {
+    case OFPACT_SET_FIELD:
     case OFPACT_REG_LOAD:
     case OFPACT_SET_ETH_DST:
     case OFPACT_SET_ETH_SRC:
@@ -927,6 +1300,8 @@ ofpact_is_set_action(const struct ofpact *a)
     case OFPACT_SET_IPV4_SRC:
     case OFPACT_SET_L4_DST_PORT:
     case OFPACT_SET_L4_SRC_PORT:
+    case OFPACT_SET_MPLS_LABEL:
+    case OFPACT_SET_MPLS_TC:
     case OFPACT_SET_MPLS_TTL:
     case OFPACT_SET_QUEUE:
     case OFPACT_SET_TUNNEL:
@@ -981,6 +1356,7 @@ ofpact_is_allowed_in_actions_set(const struct ofpact *a)
     case OFPACT_PUSH_MPLS:
     case OFPACT_PUSH_VLAN:
     case OFPACT_REG_LOAD:
+    case OFPACT_SET_FIELD:
     case OFPACT_SET_ETH_DST:
     case OFPACT_SET_ETH_SRC:
     case OFPACT_SET_IP_DSCP:
@@ -990,6 +1366,8 @@ ofpact_is_allowed_in_actions_set(const struct ofpact *a)
     case OFPACT_SET_IPV4_SRC:
     case OFPACT_SET_L4_DST_PORT:
     case OFPACT_SET_L4_SRC_PORT:
+    case OFPACT_SET_MPLS_LABEL:
+    case OFPACT_SET_MPLS_TC:
     case OFPACT_SET_MPLS_TTL:
     case OFPACT_SET_QUEUE:
     case OFPACT_SET_TUNNEL:
@@ -1119,13 +1497,15 @@ ofpacts_execute_action_set(struct ofpbuf *action_list,
 
 static enum ofperr
 ofpacts_from_openflow11_for_action_set(const union ofp_action *in,
-                                       size_t n_in, struct ofpbuf *out)
+                                       size_t n_in, enum ofp_version version,
+                                       struct ofpbuf *out)
 {
     enum ofperr error;
     struct ofpact *a;
     size_t start = out->size;
 
-    error = ofpacts_from_openflow11(in, n_in, out);
+    error = ofpacts_from_openflow(in, n_in, version, out);
+
     if (error) {
         return error;
     }
@@ -1140,35 +1520,6 @@ ofpacts_from_openflow11_for_action_set(const union ofp_action *in,
     return 0;
 }
 
-\f
-static enum ofperr
-ofpact_from_openflow13(const union ofp_action *a, struct ofpbuf *out)
-{
-    enum ofputil_action_code code;
-    enum ofperr error;
-
-    error = decode_openflow11_action(a, &code);
-    if (error) {
-        return error;
-    }
-
-    if (code == OFPUTIL_OFPAT11_PUSH_MPLS) {
-        struct ofp11_action_push *oap = (struct ofp11_action_push *)a;
-        error = push_mpls_from_openflow(oap->ethertype,
-                                        OFPACT_MPLS_BEFORE_VLAN, out);
-    } else {
-        error = ofpact_from_openflow11(a, out);
-    }
-
-    return error;
-}
-
-static enum ofperr
-ofpacts_from_openflow13(const union ofp_action *in, size_t n_in,
-                        struct ofpbuf *out)
-{
-    return ofpacts_from_openflow(in, n_in, out, ofpact_from_openflow13);
-}
 \f
 /* OpenFlow 1.1 instructions. */
 
@@ -1262,9 +1613,12 @@ ovs_instruction_type_from_ofpact_type(enum ofpact_type type)
     case OFPACT_SET_L4_DST_PORT:
     case OFPACT_REG_MOVE:
     case OFPACT_REG_LOAD:
+    case OFPACT_SET_FIELD:
     case OFPACT_STACK_PUSH:
     case OFPACT_STACK_POP:
     case OFPACT_DEC_TTL:
+    case OFPACT_SET_MPLS_LABEL:
+    case OFPACT_SET_MPLS_TC:
     case OFPACT_SET_MPLS_TTL:
     case OFPACT_DEC_MPLS_TTL:
     case OFPACT_PUSH_MPLS:
@@ -1378,48 +1732,11 @@ get_actions_from_instruction(const struct ofp11_instruction *inst,
     *max_actions = (ntohs(inst->len) - sizeof *inst) / OFP11_INSTRUCTION_ALIGN;
 }
 
-/* Attempts to convert 'actions_len' bytes of OpenFlow actions from the
- * front of 'openflow' into ofpacts.  On success, replaces any existing content
- * in 'ofpacts' by the converted ofpacts; on failure, clears 'ofpacts'.
- * Returns 0 if successful, otherwise an OpenFlow error.
- *
- * Actions are processed according to their OpenFlow version which
- * is provided in the 'version' parameter.
- *
- * In most places in OpenFlow 1.1 and 1.2, actions appear encapsulated in
- * instructions, so you should call ofpacts_pull_openflow11_instructions()
- * instead of this function.
- *
- * The parsed actions are valid generically, but they may not be valid in a
- * specific context.  For example, port numbers up to OFPP_MAX are valid
- * generically, but specific datapaths may only support port numbers in a
- * smaller range.  Use ofpacts_check() to additional check whether actions are
- * valid in a specific context. */
-enum ofperr
-ofpacts_pull_openflow11_actions(struct ofpbuf *openflow,
-                                enum ofp_version version,
-                                unsigned int actions_len,
-                                struct ofpbuf *ofpacts)
-{
-    switch (version) {
-    case OFP10_VERSION:
-    case OFP11_VERSION:
-    case OFP12_VERSION:
-        return ofpacts_pull_actions(openflow, actions_len, ofpacts,
-                                    ofpacts_from_openflow11);
-    case OFP13_VERSION:
-        return ofpacts_pull_actions(openflow, actions_len, ofpacts,
-                                    ofpacts_from_openflow13);
-    default:
-        NOT_REACHED();
-    }
-}
-
 enum ofperr
-ofpacts_pull_openflow11_instructions(struct ofpbuf *openflow,
-                                     enum ofp_version version,
-                                     unsigned int instructions_len,
-                                     struct ofpbuf *ofpacts)
+ofpacts_pull_openflow_instructions(struct ofpbuf *openflow,
+                                   unsigned int instructions_len,
+                                   enum ofp_version version,
+                                   struct ofpbuf *ofpacts)
 {
     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
     const struct ofp11_instruction *instructions;
@@ -1468,18 +1785,7 @@ ofpacts_pull_openflow11_instructions(struct ofpbuf *openflow,
 
         get_actions_from_instruction(insts[OVSINST_OFPIT11_APPLY_ACTIONS],
                                      &actions, &max_actions);
-        switch (version) {
-        case OFP10_VERSION:
-        case OFP11_VERSION:
-        case OFP12_VERSION:
-            error = ofpacts_from_openflow11(actions, max_actions, ofpacts);
-            break;
-        case OFP13_VERSION:
-            error = ofpacts_from_openflow13(actions, max_actions, ofpacts);
-            break;
-        default:
-            NOT_REACHED();
-        }
+        error = ofpacts_from_openflow(actions, max_actions, version, ofpacts);
         if (error) {
             goto exit;
         }
@@ -1502,7 +1808,7 @@ ofpacts_pull_openflow11_instructions(struct ofpbuf *openflow,
         get_actions_from_instruction(insts[OVSINST_OFPIT11_WRITE_ACTIONS],
                                      &actions, &max_actions);
         error = ofpacts_from_openflow11_for_action_set(actions, max_actions,
-                                                       ofpacts);
+                                                       version, ofpacts);
         if (error) {
             goto exit;
         }
@@ -1538,17 +1844,47 @@ exit:
     return error;
 }
 \f
-/* May modify flow->dl_type, caller must restore it. */
+/* Checks that 'port' is a valid output port for OFPACT_OUTPUT, given that the
+ * switch will never have more than 'max_ports' ports.  Returns 0 if 'port' is
+ * valid, otherwise an OpenFlow error code. */
+enum ofperr
+ofpact_check_output_port(ofp_port_t port, ofp_port_t max_ports)
+{
+    switch (port) {
+    case OFPP_IN_PORT:
+    case OFPP_TABLE:
+    case OFPP_NORMAL:
+    case OFPP_FLOOD:
+    case OFPP_ALL:
+    case OFPP_CONTROLLER:
+    case OFPP_NONE:
+    case OFPP_LOCAL:
+        return 0;
+
+    default:
+        if (ofp_to_u16(port) < ofp_to_u16(max_ports)) {
+            return 0;
+        }
+        return OFPERR_OFPBAC_BAD_OUT_PORT;
+    }
+}
+
+/* May modify flow->dl_type and flow->vlan_tci, caller must restore them.
+ *
+ * Modifies some actions, filling in fields that could not be properly set
+ * without context. */
 static enum ofperr
-ofpact_check__(const struct ofpact *a, struct flow *flow, ofp_port_t max_ports,
-               uint8_t table_id, bool enforce_consistency)
+ofpact_check__(struct ofpact *a, struct flow *flow,
+               bool enforce_consistency, ofp_port_t max_ports,
+               uint8_t table_id, uint8_t n_tables)
 {
     const struct ofpact_enqueue *enqueue;
+    const struct mf_field *mf;
 
     switch (a->type) {
     case OFPACT_OUTPUT:
-        return ofputil_check_output_port(ofpact_get_OUTPUT(a)->port,
-                                         max_ports);
+        return ofpact_check_output_port(ofpact_get_OUTPUT(a)->port,
+                                        max_ports);
 
     case OFPACT_CONTROLLER:
         return 0;
@@ -1569,13 +1905,37 @@ ofpact_check__(const struct ofpact *a, struct flow *flow, ofp_port_t max_ports,
         return bundle_check(ofpact_get_BUNDLE(a), max_ports, flow);
 
     case OFPACT_SET_VLAN_VID:
+        /* Remember if we saw a vlan tag in the flow to aid translating to
+         * OpenFlow 1.1+ if need be. */
+        ofpact_get_SET_VLAN_VID(a)->flow_has_vlan =
+            (flow->vlan_tci & htons(VLAN_CFI)) == htons(VLAN_CFI);
+        if (!(flow->vlan_tci & htons(VLAN_CFI)) &&
+            !ofpact_get_SET_VLAN_VID(a)->push_vlan_if_needed) {
+            goto inconsistent;
+        }
+        /* Temporary mark that we have a vlan tag. */
+        flow->vlan_tci |= htons(VLAN_CFI);
+        return 0;
+
     case OFPACT_SET_VLAN_PCP:
+        /* Remember if we saw a vlan tag in the flow to aid translating to
+         * OpenFlow 1.1+ if need be. */
+        ofpact_get_SET_VLAN_PCP(a)->flow_has_vlan =
+            (flow->vlan_tci & htons(VLAN_CFI)) == htons(VLAN_CFI);
+        if (!(flow->vlan_tci & htons(VLAN_CFI)) &&
+            !ofpact_get_SET_VLAN_PCP(a)->push_vlan_if_needed) {
+            goto inconsistent;
+        }
+        /* Temporary mark that we have a vlan tag. */
+        flow->vlan_tci |= htons(VLAN_CFI);
         return 0;
 
     case OFPACT_STRIP_VLAN:
         if (!(flow->vlan_tci & htons(VLAN_CFI))) {
             goto inconsistent;
         }
+        /* Temporary mark that we have no vlan tag. */
+        flow->vlan_tci = htons(0);
         return 0;
 
     case OFPACT_PUSH_VLAN:
@@ -1583,6 +1943,8 @@ ofpact_check__(const struct ofpact *a, struct flow *flow, ofp_port_t max_ports,
             /* Multiple VLAN headers not supported. */
             return OFPERR_OFPBAC_BAD_TAG;
         }
+        /* Temporary mark that we have a vlan tag. */
+        flow->vlan_tci |= htons(VLAN_CFI);
         return 0;
 
     case OFPACT_SET_ETH_SRC:
@@ -1606,12 +1968,27 @@ ofpact_check__(const struct ofpact *a, struct flow *flow, ofp_port_t max_ports,
         return 0;
 
     case OFPACT_SET_L4_SRC_PORT:
+        if (!is_ip_any(flow) ||
+            (flow->nw_proto != IPPROTO_TCP && flow->nw_proto != IPPROTO_UDP
+             && flow->nw_proto != IPPROTO_SCTP)) {
+            goto inconsistent;
+        }
+        /* Note on which transport protocol the port numbers are set.
+         * This allows this set action to be converted to an OF1.2 set field
+         * action. */
+        ofpact_get_SET_L4_SRC_PORT(a)->flow_ip_proto = flow->nw_proto;
+        return 0;
+
     case OFPACT_SET_L4_DST_PORT:
         if (!is_ip_any(flow) ||
             (flow->nw_proto != IPPROTO_TCP && flow->nw_proto != IPPROTO_UDP
              && flow->nw_proto != IPPROTO_SCTP)) {
             goto inconsistent;
         }
+        /* Note on which transport protocol the port numbers are set.
+         * This allows this set action to be converted to an OF1.2 set field
+         * action. */
+        ofpact_get_SET_L4_DST_PORT(a)->flow_ip_proto = flow->nw_proto;
         return 0;
 
     case OFPACT_REG_MOVE:
@@ -1620,12 +1997,34 @@ ofpact_check__(const struct ofpact *a, struct flow *flow, ofp_port_t max_ports,
     case OFPACT_REG_LOAD:
         return nxm_reg_load_check(ofpact_get_REG_LOAD(a), flow);
 
+    case OFPACT_SET_FIELD:
+        mf = ofpact_get_SET_FIELD(a)->field;
+        /* Require OXM_OF_VLAN_VID to have an existing VLAN header. */
+        if (!mf_are_prereqs_ok(mf, flow) ||
+            (mf->id == MFF_VLAN_VID && !(flow->vlan_tci & htons(VLAN_CFI)))) {
+            VLOG_WARN_RL(&rl, "set_field %s lacks correct prerequisities",
+                         mf->name);
+            return OFPERR_OFPBAC_MATCH_INCONSISTENT;
+        }
+        /* Remember if we saw a vlan tag in the flow to aid translating to
+         * OpenFlow 1.1 if need be. */
+        ofpact_get_SET_FIELD(a)->flow_has_vlan =
+            (flow->vlan_tci & htons(VLAN_CFI)) == htons(VLAN_CFI);
+        if (mf->id == MFF_VLAN_TCI) {
+            /* The set field may add or remove the vlan tag,
+             * Mark the status temporarily. */
+            flow->vlan_tci = ofpact_get_SET_FIELD(a)->value.be16;
+        }
+        return 0;
+
     case OFPACT_STACK_PUSH:
         return nxm_stack_push_check(ofpact_get_STACK_PUSH(a), flow);
 
     case OFPACT_STACK_POP:
         return nxm_stack_pop_check(ofpact_get_STACK_POP(a), flow);
 
+    case OFPACT_SET_MPLS_LABEL:
+    case OFPACT_SET_MPLS_TC:
     case OFPACT_SET_MPLS_TTL:
     case OFPACT_DEC_MPLS_TTL:
         if (!eth_type_mpls(flow->dl_type)) {
@@ -1675,7 +2074,7 @@ ofpact_check__(const struct ofpact *a, struct flow *flow, ofp_port_t max_ports,
     case OFPACT_WRITE_ACTIONS: {
         struct ofpact_nest *on = ofpact_get_WRITE_ACTIONS(a);
         return ofpacts_check(on->actions, ofpact_nest_get_action_len(on),
-                             flow, max_ports, table_id, false);
+                             flow, false, max_ports, table_id, n_tables);
     }
 
     case OFPACT_WRITE_METADATA:
@@ -1689,11 +2088,14 @@ ofpact_check__(const struct ofpact *a, struct flow *flow, ofp_port_t max_ports,
         return 0;
     }
 
-    case OFPACT_GOTO_TABLE:
-        if (ofpact_get_GOTO_TABLE(a)->table_id <= table_id) {
+    case OFPACT_GOTO_TABLE: {
+        uint8_t goto_table = ofpact_get_GOTO_TABLE(a)->table_id;
+        if ((table_id != 255 && goto_table <= table_id)
+            || (n_tables != 255 && goto_table >= n_tables)) {
             return OFPERR_OFPBRC_BAD_TABLE_ID;
         }
         return 0;
+    }
 
     case OFPACT_GROUP:
         return 0;
@@ -1713,24 +2115,30 @@ ofpact_check__(const struct ofpact *a, struct flow *flow, ofp_port_t max_ports,
  * appropriate for a packet with the prerequisites satisfied by 'flow' in a
  * switch with no more than 'max_ports' ports.
  *
+ * May annotate ofpacts with information gathered from the 'flow'.
+ *
  * May temporarily modify 'flow', but restores the changes before returning. */
 enum ofperr
-ofpacts_check(const struct ofpact ofpacts[], size_t ofpacts_len,
-              struct flow *flow, ofp_port_t max_ports, uint8_t table_id,
-              bool enforce_consistency)
+ofpacts_check(struct ofpact ofpacts[], size_t ofpacts_len,
+              struct flow *flow, bool enforce_consistency,
+              ofp_port_t max_ports,
+              uint8_t table_id, uint8_t n_tables)
 {
-    const struct ofpact *a;
+    struct ofpact *a;
     ovs_be16 dl_type = flow->dl_type;
+    ovs_be16 vlan_tci = flow->vlan_tci;
     enum ofperr error = 0;
 
     OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
-        error = ofpact_check__(a, flow, max_ports, table_id,
-                               enforce_consistency);
+        error = ofpact_check__(a, flow, enforce_consistency,
+                               max_ports, table_id, n_tables);
         if (error) {
             break;
         }
     }
-    flow->dl_type = dl_type; /* Restore. */
+    /* Restore fields that may have been modified. */
+    flow->dl_type = dl_type;
+    flow->vlan_tci = vlan_tci;
     return error;
 }
 
@@ -1940,6 +2348,16 @@ ofpact_to_nxast(const struct ofpact *a, struct ofpbuf *out)
         ofpact_dec_ttl_to_nxast(ofpact_get_DEC_TTL(a), out);
         break;
 
+    case OFPACT_SET_MPLS_LABEL:
+        ofputil_put_NXAST_SET_MPLS_LABEL(out)->label
+            = ofpact_get_SET_MPLS_LABEL(a)->label;
+        break;
+
+    case OFPACT_SET_MPLS_TC:
+        ofputil_put_NXAST_SET_MPLS_TC(out)->tc
+            = ofpact_get_SET_MPLS_TC(a)->tc;
+        break;
+
     case OFPACT_SET_MPLS_TTL:
         ofputil_put_NXAST_SET_MPLS_TTL(out)->ttl
             = ofpact_get_SET_MPLS_TTL(a)->ttl;
@@ -2024,6 +2442,7 @@ ofpact_to_nxast(const struct ofpact *a, struct ofpbuf *out)
     case OFPACT_CLEAR_ACTIONS:
     case OFPACT_GOTO_TABLE:
     case OFPACT_METER:
+    case OFPACT_SET_FIELD:
         NOT_REACHED();
     }
 }
@@ -2114,6 +2533,10 @@ ofpact_to_openflow10(const struct ofpact *a, struct ofpbuf *out)
         break;
 
     case OFPACT_PUSH_VLAN:
+        /* PUSH is a side effect of a SET_VLAN_VID/PCP, which should
+         * follow this action. */
+        break;
+
     case OFPACT_CLEAR_ACTIONS:
     case OFPACT_WRITE_ACTIONS:
     case OFPACT_GOTO_TABLE:
@@ -2124,6 +2547,10 @@ ofpact_to_openflow10(const struct ofpact *a, struct ofpbuf *out)
     case OFPACT_GROUP:
         break;
 
+    case OFPACT_SET_FIELD:
+        set_field_to_openflow(ofpact_get_SET_FIELD(a), out);
+        break;
+
     case OFPACT_CONTROLLER:
     case OFPACT_OUTPUT_REG:
     case OFPACT_BUNDLE:
@@ -2134,6 +2561,8 @@ ofpact_to_openflow10(const struct ofpact *a, struct ofpbuf *out)
     case OFPACT_DEC_TTL:
     case OFPACT_SET_IP_ECN:
     case OFPACT_SET_IP_TTL:
+    case OFPACT_SET_MPLS_LABEL:
+    case OFPACT_SET_MPLS_TC:
     case OFPACT_SET_MPLS_TTL:
     case OFPACT_DEC_MPLS_TTL:
     case OFPACT_SET_TUNNEL:
@@ -2153,20 +2582,6 @@ ofpact_to_openflow10(const struct ofpact *a, struct ofpbuf *out)
         break;
     }
 }
-
-/* Converts the 'ofpacts_len' bytes of ofpacts in 'ofpacts' into OpenFlow 1.0
- * actions in 'openflow', appending the actions to any existing data in
- * 'openflow'. */
-void
-ofpacts_put_openflow10(const struct ofpact ofpacts[], size_t ofpacts_len,
-                       struct ofpbuf *openflow)
-{
-    const struct ofpact *a;
-
-    OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
-        ofpact_to_openflow10(a, openflow);
-    }
-}
 \f
 /* Converting ofpacts to OpenFlow 1.1. */
 
@@ -2206,11 +2621,23 @@ ofpact_to_openflow11(const struct ofpact *a, struct ofpbuf *out)
         break;
 
     case OFPACT_SET_VLAN_VID:
+        /* Push a VLAN tag, if one was not seen at action validation time. */
+        if (!ofpact_get_SET_VLAN_VID(a)->flow_has_vlan
+            && ofpact_get_SET_VLAN_VID(a)->push_vlan_if_needed) {
+            ofputil_put_OFPAT11_PUSH_VLAN(out)->ethertype
+                = htons(ETH_TYPE_VLAN_8021Q);
+        }
         ofputil_put_OFPAT11_SET_VLAN_VID(out)->vlan_vid
             = htons(ofpact_get_SET_VLAN_VID(a)->vlan_vid);
         break;
 
     case OFPACT_SET_VLAN_PCP:
+        /* Push a VLAN tag, if one was not seen at action validation time. */
+        if (!ofpact_get_SET_VLAN_PCP(a)->flow_has_vlan
+            && ofpact_get_SET_VLAN_PCP(a)->push_vlan_if_needed) {
+            ofputil_put_OFPAT11_PUSH_VLAN(out)->ethertype
+                = htons(ETH_TYPE_VLAN_8021Q);
+        }
         ofputil_put_OFPAT11_SET_VLAN_PCP(out)->vlan_pcp
             = ofpact_get_SET_VLAN_PCP(a)->vlan_pcp;
         break;
@@ -2279,6 +2706,16 @@ ofpact_to_openflow11(const struct ofpact *a, struct ofpbuf *out)
         ofpact_dec_ttl_to_openflow11(ofpact_get_DEC_TTL(a), out);
         break;
 
+    case OFPACT_SET_MPLS_LABEL:
+        ofputil_put_OFPAT11_SET_MPLS_LABEL(out)->mpls_label
+            = ofpact_get_SET_MPLS_LABEL(a)->label;
+        break;
+
+    case OFPACT_SET_MPLS_TC:
+        ofputil_put_OFPAT11_SET_MPLS_TC(out)->mpls_tc
+            = ofpact_get_SET_MPLS_TC(a)->tc;
+        break;
+
     case OFPACT_SET_MPLS_TTL:
         ofputil_put_OFPAT11_SET_MPLS_TTL(out)->mpls_ttl
             = ofpact_get_SET_MPLS_TTL(a)->ttl;
@@ -2314,6 +2751,10 @@ ofpact_to_openflow11(const struct ofpact *a, struct ofpbuf *out)
             htonl(ofpact_get_GROUP(a)->group_id);
         break;
 
+    case OFPACT_SET_FIELD:
+        set_field_to_openflow(ofpact_get_SET_FIELD(a), out);
+        break;
+
     case OFPACT_CONTROLLER:
     case OFPACT_OUTPUT_REG:
     case OFPACT_BUNDLE:
@@ -2335,20 +2776,169 @@ ofpact_to_openflow11(const struct ofpact *a, struct ofpbuf *out)
     }
 }
 
-/* Converts the ofpacts in 'ofpacts' (terminated by OFPACT_END) into OpenFlow
- * 1.1 actions in 'openflow', appending the actions to any existing data in
+/* Output deprecated set actions as set_field actions. */
+static void
+ofpact_to_openflow12(const struct ofpact *a, struct ofpbuf *out)
+{
+    enum mf_field_id field;
+    union mf_value value;
+    struct ofpact_l4_port *l4port;
+    uint8_t proto;
+
+    /*
+     * Convert actions deprecated in OpenFlow 1.2 to Set Field actions,
+     * if possible.
+     */
+    switch ((int)a->type) {
+    case OFPACT_SET_VLAN_VID:
+    case OFPACT_SET_VLAN_PCP:
+    case OFPACT_SET_ETH_SRC:
+    case OFPACT_SET_ETH_DST:
+    case OFPACT_SET_IPV4_SRC:
+    case OFPACT_SET_IPV4_DST:
+    case OFPACT_SET_IP_DSCP:
+    case OFPACT_SET_IP_ECN:
+    case OFPACT_SET_L4_SRC_PORT:
+    case OFPACT_SET_L4_DST_PORT:
+    case OFPACT_SET_MPLS_LABEL:
+    case OFPACT_SET_MPLS_TC:
+    case OFPACT_SET_TUNNEL:  /* Convert to a set_field, too. */
+
+        switch ((int)a->type) {
+
+        case OFPACT_SET_VLAN_VID:
+            if (!ofpact_get_SET_VLAN_VID(a)->flow_has_vlan &&
+                ofpact_get_SET_VLAN_VID(a)->push_vlan_if_needed) {
+                ofputil_put_OFPAT11_PUSH_VLAN(out)->ethertype
+                    = htons(ETH_TYPE_VLAN_8021Q);
+            }
+            field = MFF_VLAN_VID;
+            /* Set-Field on OXM_OF_VLAN_VID must have OFPVID_PRESENT set. */
+            value.be16 = htons(ofpact_get_SET_VLAN_VID(a)->vlan_vid
+                               | OFPVID12_PRESENT);
+            break;
+
+        case OFPACT_SET_VLAN_PCP:
+            if (!ofpact_get_SET_VLAN_PCP(a)->flow_has_vlan &&
+                ofpact_get_SET_VLAN_PCP(a)->push_vlan_if_needed) {
+                ofputil_put_OFPAT11_PUSH_VLAN(out)->ethertype
+                    = htons(ETH_TYPE_VLAN_8021Q);
+            }
+            field = MFF_VLAN_PCP;
+            value.u8 = ofpact_get_SET_VLAN_PCP(a)->vlan_pcp;
+            break;
+
+        case OFPACT_SET_ETH_SRC:
+            field = MFF_ETH_SRC;
+            memcpy(value.mac, ofpact_get_SET_ETH_SRC(a)->mac, ETH_ADDR_LEN);
+            break;
+
+        case OFPACT_SET_ETH_DST:
+            field = MFF_ETH_DST;
+            memcpy(value.mac, ofpact_get_SET_ETH_DST(a)->mac, ETH_ADDR_LEN);
+            break;
+
+        case OFPACT_SET_IPV4_SRC:
+            field = MFF_IPV4_SRC;
+            value.be32 = ofpact_get_SET_IPV4_SRC(a)->ipv4;
+            break;
+
+        case OFPACT_SET_IPV4_DST:
+            field = MFF_IPV4_DST;
+            value.be32 = ofpact_get_SET_IPV4_DST(a)->ipv4;
+            break;
+
+        case OFPACT_SET_IP_DSCP:
+            field = MFF_IP_DSCP_SHIFTED; /* OXM_OF_IP_DSCP */
+            value.u8 = ofpact_get_SET_IP_DSCP(a)->dscp >> 2;
+            break;
+
+        case OFPACT_SET_IP_ECN:
+            field = MFF_IP_ECN;
+            value.u8 = ofpact_get_SET_IP_ECN(a)->ecn;
+            break;
+
+        case OFPACT_SET_L4_SRC_PORT:
+            /* We keep track of IP protocol while translating actions to be
+             * able to translate to the proper OXM type.
+             * If the IP protocol type is unknown, the translation cannot
+             * be performed and we will send the action using the original
+             * action type. */
+            l4port = ofpact_get_SET_L4_SRC_PORT(a);
+            proto = l4port->flow_ip_proto;
+            field = proto == IPPROTO_TCP ? MFF_TCP_SRC
+                : proto == IPPROTO_UDP ? MFF_UDP_SRC
+                : proto == IPPROTO_SCTP ? MFF_SCTP_SRC
+                : MFF_N_IDS; /* RFC: Unknown IP proto, do not translate. */
+            value.be16 = htons(l4port->port);
+            break;
+
+        case OFPACT_SET_L4_DST_PORT:
+            l4port = ofpact_get_SET_L4_DST_PORT(a);
+            proto = l4port->flow_ip_proto;
+            field = proto == IPPROTO_TCP ? MFF_TCP_DST
+                : proto == IPPROTO_UDP ? MFF_UDP_DST
+                : proto == IPPROTO_SCTP ? MFF_SCTP_DST
+                : MFF_N_IDS; /* RFC: Unknown IP proto, do not translate. */
+            value.be16 = htons(l4port->port);
+            break;
+
+        case OFPACT_SET_MPLS_LABEL:
+            field = MFF_MPLS_LABEL;
+            value.be32 = ofpact_get_SET_MPLS_LABEL(a)->label;
+            break;
+
+        case OFPACT_SET_MPLS_TC:
+            field = MFF_MPLS_TC;
+            value.u8 = ofpact_get_SET_MPLS_TC(a)->tc;
+            break;
+
+        case OFPACT_SET_TUNNEL:
+            field = MFF_TUN_ID;
+            value.be64 = htonll(ofpact_get_SET_TUNNEL(a)->tun_id);
+            break;
+
+        default:
+            field = MFF_N_IDS;
+        }
+
+        /* Put the action out as a set field action, if possible. */
+        if (field < MFF_N_IDS) {
+            uint64_t ofpacts_stub[128 / 8];
+            struct ofpbuf sf_act;
+            struct ofpact_set_field *sf;
+
+            ofpbuf_use_stub(&sf_act, ofpacts_stub, sizeof ofpacts_stub);
+            sf = ofpact_put_SET_FIELD(&sf_act);
+            sf->field = mf_from_id(field);
+            memcpy(&sf->value, &value, sf->field->n_bytes);
+            set_field_to_openflow(sf, out);
+            return;
+        }
+    }
+
+    ofpact_to_openflow11(a, out);
+}
+
+/* Converts the 'ofpacts_len' bytes of ofpacts in 'ofpacts' into OpenFlow
+ * actions in 'openflow', appending the actions to any existing data in
  * 'openflow'. */
 size_t
-ofpacts_put_openflow11_actions(const struct ofpact ofpacts[],
-                               size_t ofpacts_len, struct ofpbuf *openflow)
+ofpacts_put_openflow_actions(const struct ofpact ofpacts[], size_t ofpacts_len,
+                             struct ofpbuf *openflow,
+                             enum ofp_version ofp_version)
 {
     const struct ofpact *a;
     size_t start_size = openflow->size;
 
+    void (*translate)(const struct ofpact *a, struct ofpbuf *out) =
+        (ofp_version == OFP10_VERSION) ? ofpact_to_openflow10 :
+        (ofp_version == OFP11_VERSION) ? ofpact_to_openflow11 :
+        ofpact_to_openflow12;
+
     OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
-        ofpact_to_openflow11(a, openflow);
+        translate(a, openflow);
     }
-
     return openflow->size - start_size;
 }
 
@@ -2367,12 +2957,15 @@ ofpacts_update_instruction_actions(struct ofpbuf *openflow, size_t ofs)
 }
 
 void
-ofpacts_put_openflow11_instructions(const struct ofpact ofpacts[],
-                                    size_t ofpacts_len,
-                                    struct ofpbuf *openflow)
+ofpacts_put_openflow_instructions(const struct ofpact ofpacts[],
+                                  size_t ofpacts_len,
+                                  struct ofpbuf *openflow,
+                                  enum ofp_version ofp_version)
 {
     const struct ofpact *a;
 
+    ovs_assert(ofp_version >= OFP11_VERSION);
+
     OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
         switch (ovs_instruction_type_from_ofpact_type(a->type)) {
         case OVSINST_OFPIT11_CLEAR_ACTIONS:
@@ -2398,15 +2991,16 @@ ofpacts_put_openflow11_instructions(const struct ofpact ofpacts[],
             break;
         }
 
-        case OVSINST_OFPIT13_METER: {
-            const struct ofpact_meter *om;
-            struct ofp13_instruction_meter *oim;
+        case OVSINST_OFPIT13_METER:
+            if (ofp_version >= OFP13_VERSION) {
+                const struct ofpact_meter *om;
+                struct ofp13_instruction_meter *oim;
 
-            om = ofpact_get_METER(a);
-            oim = instruction_put_OFPIT13_METER(openflow);
-            oim->meter_id = htonl(om->meter_id);
+                om = ofpact_get_METER(a);
+                oim = instruction_put_OFPIT13_METER(openflow);
+                oim->meter_id = htonl(om->meter_id);
+            }
             break;
-        }
 
         case OVSINST_OFPIT11_APPLY_ACTIONS: {
             const size_t ofs = openflow->size;
@@ -2421,7 +3015,11 @@ ofpacts_put_openflow11_instructions(const struct ofpact ofpacts[],
                     != OVSINST_OFPIT11_APPLY_ACTIONS) {
                     break;
                 }
-                ofpact_to_openflow11(action, openflow);
+                if (ofp_version == OFP11_VERSION) {
+                    ofpact_to_openflow11(action, openflow);
+                } else {
+                    ofpact_to_openflow12(action, openflow);
+                }
                 processed = action;
             }
             ofpacts_update_instruction_actions(openflow, ofs);
@@ -2435,9 +3033,9 @@ ofpacts_put_openflow11_instructions(const struct ofpact ofpacts[],
 
             on = ofpact_get_WRITE_ACTIONS(a);
             instruction_put_OFPIT11_WRITE_ACTIONS(openflow);
-            ofpacts_put_openflow11_actions(on->actions,
-                                           ofpact_nest_get_action_len(on),
-                                           openflow);
+            ofpacts_put_openflow_actions(on->actions,
+                                         ofpact_nest_get_action_len(on),
+                                         openflow, ofp_version);
             ofpacts_update_instruction_actions(openflow, ofs);
 
             break;
@@ -2475,9 +3073,12 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, ofp_port_t port)
     case OFPACT_SET_L4_DST_PORT:
     case OFPACT_REG_MOVE:
     case OFPACT_REG_LOAD:
+    case OFPACT_SET_FIELD:
     case OFPACT_STACK_PUSH:
     case OFPACT_STACK_POP:
     case OFPACT_DEC_TTL:
+    case OFPACT_SET_MPLS_LABEL:
+    case OFPACT_SET_MPLS_TC:
     case OFPACT_SET_MPLS_TTL:
     case OFPACT_DEC_MPLS_TTL:
     case OFPACT_SET_TUNNEL:
@@ -2630,6 +3231,8 @@ ofpact_format(const struct ofpact *a, struct ds *s)
     const struct ofpact_metadata *metadata;
     const struct ofpact_tunnel *tunnel;
     const struct ofpact_sample *sample;
+    const struct ofpact_set_field *set_field;
+    const struct mf_field *mf;
     ofp_port_t port;
 
     switch (a->type) {
@@ -2690,17 +3293,24 @@ ofpact_format(const struct ofpact *a, struct ds *s)
         break;
 
     case OFPACT_SET_VLAN_VID:
-        ds_put_format(s, "mod_vlan_vid:%"PRIu16,
+        ds_put_format(s, "%s:%"PRIu16,
+                      (a->compat == OFPUTIL_OFPAT11_SET_VLAN_VID
+                       ? "set_vlan_vid"
+                       : "mod_vlan_vid"),
                       ofpact_get_SET_VLAN_VID(a)->vlan_vid);
         break;
 
     case OFPACT_SET_VLAN_PCP:
-        ds_put_format(s, "mod_vlan_pcp:%"PRIu8,
+        ds_put_format(s, "%s:%"PRIu8,
+                      (a->compat == OFPUTIL_OFPAT11_SET_VLAN_PCP
+                       ? "set_vlan_pcp"
+                       : "mod_vlan_pcp"),
                       ofpact_get_SET_VLAN_PCP(a)->vlan_pcp);
         break;
 
     case OFPACT_STRIP_VLAN:
-        ds_put_cstr(s, "strip_vlan");
+        ds_put_cstr(s, a->compat == OFPUTIL_OFPAT11_POP_VLAN
+                    ? "pop_vlan" : "strip_vlan");
         break;
 
     case OFPACT_PUSH_VLAN:
@@ -2756,6 +3366,14 @@ ofpact_format(const struct ofpact *a, struct ds *s)
         nxm_format_reg_load(ofpact_get_REG_LOAD(a), s);
         break;
 
+    case OFPACT_SET_FIELD:
+        set_field = ofpact_get_SET_FIELD(a);
+        mf = set_field->field;
+        ds_put_format(s, "set_field:");
+        mf_format(mf, &set_field->value, NULL, s);
+        ds_put_format(s, "->%s", mf->name);
+        break;
+
     case OFPACT_STACK_PUSH:
         nxm_format_stack_push(ofpact_get_STACK_PUSH(a), s);
         break;
@@ -2768,6 +3386,16 @@ ofpact_format(const struct ofpact *a, struct ds *s)
         print_dec_ttl(ofpact_get_DEC_TTL(a), s);
         break;
 
+    case OFPACT_SET_MPLS_LABEL:
+        ds_put_format(s, "set_mpls_label(%"PRIu32")",
+                      ntohl(ofpact_get_SET_MPLS_LABEL(a)->label));
+        break;
+
+    case OFPACT_SET_MPLS_TC:
+        ds_put_format(s, "set_mpls_ttl(%"PRIu8")",
+                      ofpact_get_SET_MPLS_TC(a)->tc);
+        break;
+
     case OFPACT_SET_MPLS_TTL:
         ds_put_format(s, "set_mpls_ttl(%"PRIu8")",
                       ofpact_get_SET_MPLS_TTL(a)->ttl);
@@ -2973,15 +3601,3 @@ ofpact_pad(struct ofpbuf *ofpacts)
         ofpbuf_put_zeros(ofpacts, OFPACT_ALIGNTO - rem);
     }
 }
-
-void
-ofpact_set_field_init(struct ofpact_reg_load *load, const struct mf_field *mf,
-                      const void *src)
-{
-    load->ofpact.compat = OFPUTIL_OFPAT12_SET_FIELD;
-    load->dst.field = mf;
-    load->dst.ofs = 0;
-    load->dst.n_bits = mf->n_bits;
-    bitwise_copy(src, mf->n_bytes, load->dst.ofs,
-                 &load->subvalue, sizeof load->subvalue, 0, mf->n_bits);
-}
index e097468..70ad4b6 100644 (file)
@@ -59,6 +59,7 @@
     DEFINE_OFPACT(BUNDLE,          ofpact_bundle,        slaves)    \
                                                                     \
     /* Header changes. */                                           \
+    DEFINE_OFPACT(SET_FIELD,       ofpact_set_field,     ofpact)    \
     DEFINE_OFPACT(SET_VLAN_VID,    ofpact_vlan_vid,      ofpact)    \
     DEFINE_OFPACT(SET_VLAN_PCP,    ofpact_vlan_pcp,      ofpact)    \
     DEFINE_OFPACT(STRIP_VLAN,      ofpact_null,          ofpact)    \
@@ -77,6 +78,8 @@
     DEFINE_OFPACT(STACK_PUSH,      ofpact_stack,         ofpact)    \
     DEFINE_OFPACT(STACK_POP,       ofpact_stack,         ofpact)    \
     DEFINE_OFPACT(DEC_TTL,         ofpact_cnt_ids,       cnt_ids)   \
+    DEFINE_OFPACT(SET_MPLS_LABEL,  ofpact_mpls_label,    ofpact)    \
+    DEFINE_OFPACT(SET_MPLS_TC,     ofpact_mpls_tc,       ofpact)    \
     DEFINE_OFPACT(SET_MPLS_TTL,    ofpact_mpls_ttl,      ofpact)    \
     DEFINE_OFPACT(DEC_MPLS_TTL,    ofpact_null,          ofpact)    \
     DEFINE_OFPACT(PUSH_MPLS,       ofpact_push_mpls,     ofpact)    \
@@ -257,18 +260,32 @@ struct ofpact_bundle {
 
 /* OFPACT_SET_VLAN_VID.
  *
- * Used for OFPAT10_SET_VLAN_VID. */
+ * We keep track if vlan was present at action validation time to avoid a
+ * PUSH_VLAN when translating to OpenFlow 1.1+.
+ *
+ * We also keep the originating OFPUTIL action code in ofpact.compat.
+ *
+ * Used for OFPAT10_SET_VLAN_VID and OFPAT11_SET_VLAN_VID. */
 struct ofpact_vlan_vid {
     struct ofpact ofpact;
     uint16_t vlan_vid;          /* VLAN VID in low 12 bits, 0 in other bits. */
+    bool push_vlan_if_needed;   /* OF 1.0 semantics if true. */
+    bool flow_has_vlan;         /* VLAN present at action validation time? */
 };
 
 /* OFPACT_SET_VLAN_PCP.
  *
- * Used for OFPAT10_SET_VLAN_PCP. */
+ * We keep track if vlan was present at action validation time to avoid a
+ * PUSH_VLAN when translating to OpenFlow 1.1+.
+ *
+ * We also keep the originating OFPUTIL action code in ofpact.compat.
+ *
+ * Used for OFPAT10_SET_VLAN_PCP and OFPAT11_SET_VLAN_PCP. */
 struct ofpact_vlan_pcp {
     struct ofpact ofpact;
     uint8_t vlan_pcp;           /* VLAN PCP in low 3 bits, 0 in other bits. */
+    bool push_vlan_if_needed;   /* OF 1.0 semantics if true. */
+    bool flow_has_vlan;         /* VLAN present at action validation time? */
 };
 
 /* OFPACT_SET_ETH_SRC, OFPACT_SET_ETH_DST.
@@ -316,7 +333,8 @@ struct ofpact_ip_ttl {
  * Used for OFPAT10_SET_TP_SRC, OFPAT10_SET_TP_DST. */
 struct ofpact_l4_port {
     struct ofpact ofpact;
-    uint16_t port;              /* TCP or UDP port number. */
+    uint16_t port;              /* TCP, UDP or SCTP port number. */
+    uint8_t  flow_ip_proto;     /* IP proto from corresponding match, or 0 */
 };
 
 /* OFPACT_REG_MOVE.
@@ -338,7 +356,7 @@ struct ofpact_stack {
 
 /* OFPACT_REG_LOAD.
  *
- * Used for NXAST_REG_LOAD, OFPAT12_SET_FIELD. */
+ * Used for NXAST_REG_LOAD. */
 struct ofpact_reg_load {
     struct ofpact ofpact;
     struct mf_subfield dst;
@@ -358,6 +376,16 @@ enum ofpact_mpls_position {
    OFPACT_MPLS_AFTER_VLAN
 };
 
+/* OFPACT_SET_FIELD.
+ *
+ * Used for OFPAT12_SET_FIELD. */
+struct ofpact_set_field {
+    struct ofpact ofpact;
+    const struct mf_field *field;
+    bool flow_has_vlan;   /* VLAN present at action validation time. */
+    union mf_value value;
+};
+
 /* OFPACT_PUSH_VLAN/MPLS/PBB
  *
  * Used for NXAST_PUSH_MPLS, OFPAT11_PUSH_MPLS. */
@@ -526,9 +554,27 @@ struct ofpact_cnt_ids {
     uint16_t cnt_ids[];
 };
 
+/* OFPACT_SET_MPLS_LABEL.
+ *
+ * Used for OFPAT11_SET_MPLS_LABEL and NXAST_SET_MPLS_LABEL */
+struct ofpact_mpls_label {
+    struct ofpact ofpact;
+
+    ovs_be32 label;
+};
+
+/* OFPACT_SET_MPLS_TC.
+ *
+ * Used for OFPAT11_SET_MPLS_TC and NXAST_SET_MPLS_TC */
+struct ofpact_mpls_tc {
+    struct ofpact ofpact;
+
+    uint8_t tc;
+};
+
 /* OFPACT_SET_MPLS_TTL.
  *
- * Used for NXAST_SET_MPLS_TTL */
+ * Used for OFPAT11_SET_MPLS_TTL and NXAST_SET_MPLS_TTL */
 struct ofpact_mpls_ttl {
     struct ofpact ofpact;
 
@@ -552,30 +598,28 @@ struct ofpact_group {
 };
 
 /* Converting OpenFlow to ofpacts. */
-enum ofperr ofpacts_pull_openflow10(struct ofpbuf *openflow,
-                                    unsigned int actions_len,
-                                    struct ofpbuf *ofpacts);
-enum ofperr ofpacts_pull_openflow11_actions(struct ofpbuf *openflow,
-                                            enum ofp_version version,
-                                            unsigned int actions_len,
-                                            struct ofpbuf *ofpacts);
-enum ofperr ofpacts_pull_openflow11_instructions(struct ofpbuf *openflow,
-                                                 enum ofp_version version,
-                                                 unsigned int instructions_len,
-                                                 struct ofpbuf *ofpacts);
-enum ofperr ofpacts_check(const struct ofpact[], size_t ofpacts_len,
-                          struct flow *, ofp_port_t max_ports,
-                          uint8_t table_id, bool enforce_consistency);
+enum ofperr ofpacts_pull_openflow_actions(struct ofpbuf *openflow,
+                                          unsigned int actions_len,
+                                          enum ofp_version version,
+                                          struct ofpbuf *ofpacts);
+enum ofperr ofpacts_pull_openflow_instructions(struct ofpbuf *openflow,
+                                               unsigned int instructions_len,
+                                               enum ofp_version version,
+                                               struct ofpbuf *ofpacts);
+enum ofperr ofpacts_check(struct ofpact[], size_t ofpacts_len,
+                          struct flow *, bool enforce_consistency,
+                          ofp_port_t max_ports,
+                          uint8_t table_id, uint8_t n_tables);
 enum ofperr ofpacts_verify(const struct ofpact ofpacts[], size_t ofpacts_len);
+enum ofperr ofpact_check_output_port(ofp_port_t port, ofp_port_t max_ports);
 
 /* Converting ofpacts to OpenFlow. */
-void ofpacts_put_openflow10(const struct ofpact[], size_t ofpacts_len,
-                            struct ofpbuf *openflow);
-size_t ofpacts_put_openflow11_actions(const struct ofpact[], size_t ofpacts_len,
-                                      struct ofpbuf *openflow);
-void ofpacts_put_openflow11_instructions(const struct ofpact[],
-                                         size_t ofpacts_len,
-                                         struct ofpbuf *openflow);
+size_t ofpacts_put_openflow_actions(const struct ofpact[], size_t ofpacts_len,
+                                    struct ofpbuf *openflow, enum ofp_version);
+void ofpacts_put_openflow_instructions(const struct ofpact[],
+                                       size_t ofpacts_len,
+                                       struct ofpbuf *openflow,
+                                       enum ofp_version ofp_version);
 
 /* Working with ofpacts. */
 bool ofpacts_output_to_port(const struct ofpact[], size_t ofpacts_len,
@@ -716,7 +760,4 @@ const char *ovs_instruction_name_from_type(enum ovs_instruction_type type);
 int ovs_instruction_type_from_name(const char *name);
 enum ovs_instruction_type ovs_instruction_type_from_ofpact_type(
     enum ofpact_type);
-
-void ofpact_set_field_init(struct ofpact_reg_load *load,
-                           const struct mf_field *mf, const void *src);
 #endif /* ofp-actions.h */
index aa19fe3..c526a15 100644 (file)
@@ -198,10 +198,14 @@ enum ofpraw {
     /* OFPT 1.1+ (21): void. */
     OFPRAW_OFPT11_BARRIER_REPLY,
 
+    /* OFPT 1.0 (22): struct ofp10_queue_get_config_request. */
+    OFPRAW_OFPT10_QUEUE_GET_CONFIG_REQUEST,
     /* OFPT 1.1+ (22): struct ofp11_queue_get_config_request. */
     OFPRAW_OFPT11_QUEUE_GET_CONFIG_REQUEST,
 
-    /* OFPT 1.1+ (23): struct ofp11_queue_get_config_reply, struct ofp_packet_queue[]. */
+    /* OFPT 1.0 (23): struct ofp10_queue_get_config_reply, uint8_t[8][]. */
+    OFPRAW_OFPT10_QUEUE_GET_CONFIG_REPLY,
+    /* OFPT 1.1+ (23): struct ofp11_queue_get_config_reply, uint8_t[8][]. */
     OFPRAW_OFPT11_QUEUE_GET_CONFIG_REPLY,
 
     /* OFPT 1.2+ (24): struct ofp12_role_request. */
@@ -226,6 +230,9 @@ enum ofpraw {
     /* OFPT 1.3+ (29): struct ofp13_meter_mod, uint8_t[8][]. */
     OFPRAW_OFPT13_METER_MOD,
 
+    /* OFPT 1.4+ (30): struct ofp14_role_status, uint8_t[8][]. */
+    OFPRAW_OFPT14_ROLE_STATUS,
+
 /* Standard statistics. */
 
     /* OFPST 1.0+ (0): void. */
@@ -478,8 +485,10 @@ enum ofptype {
                                   * OFPRAW_OFPT11_BARRIER_REPLY. */
 
     /* Queue Configuration messages. */
-    OFPTYPE_QUEUE_GET_CONFIG_REQUEST, /* OFPRAW_OFPT11_QUEUE_GET_CONFIG_REQUEST. */
-    OFPTYPE_QUEUE_GET_CONFIG_REPLY, /* OFPRAW_OFPT11_QUEUE_GET_CONFIG_REPLY. */
+    OFPTYPE_QUEUE_GET_CONFIG_REQUEST, /* OFPRAW_OFPT10_QUEUE_GET_CONFIG_REQUEST.
+                                       * OFPRAW_OFPT11_QUEUE_GET_CONFIG_REQUEST. */
+    OFPTYPE_QUEUE_GET_CONFIG_REPLY, /* OFPRAW_OFPT10_QUEUE_GET_CONFIG_REPLY.
+                                     * OFPRAW_OFPT11_QUEUE_GET_CONFIG_REPLY. */
 
     /* Controller role change request messages. */
     OFPTYPE_ROLE_REQUEST,         /* OFPRAW_OFPT12_ROLE_REQUEST.
@@ -496,6 +505,9 @@ enum ofptype {
     /* Meters and rate limiters configuration messages. */
     OFPTYPE_METER_MOD,            /* OFPRAW_OFPT13_METER_MOD. */
 
+    /* Controller role change event messages. */
+    OFPTYPE_ROLE_STATUS,          /* OFPRAW_OFPT14_ROLE_STATUS. */
+
     /* Statistics. */
     OFPTYPE_DESC_STATS_REQUEST,      /* OFPRAW_OFPST_DESC_REQUEST. */
     OFPTYPE_DESC_STATS_REPLY,        /* OFPRAW_OFPST_DESC_REPLY. */
index 757b971..915dc90 100644 (file)
@@ -438,6 +438,42 @@ parse_dec_ttl(struct ofpbuf *b, char *arg)
     return NULL;
 }
 
+/* Parses 'arg' as the argument to a "set_mpls_label" action, and appends such
+ * an action to 'b'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
+parse_set_mpls_label(struct ofpbuf *b, const char *arg)
+{
+    struct ofpact_mpls_label *mpls_label = ofpact_put_SET_MPLS_LABEL(b);
+
+    if (*arg == '\0') {
+        return xstrdup("parse_set_mpls_label: expected label.");
+    }
+
+    mpls_label->label = htonl(atoi(arg));
+    return NULL;
+}
+
+/* Parses 'arg' as the argument to a "set_mpls_tc" action, and appends such an
+ * action to 'b'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
+parse_set_mpls_tc(struct ofpbuf *b, const char *arg)
+{
+    struct ofpact_mpls_tc *mpls_tc = ofpact_put_SET_MPLS_TC(b);
+
+    if (*arg == '\0') {
+        return xstrdup("parse_set_mpls_tc: expected tc.");
+    }
+
+    mpls_tc->tc = atoi(arg);
+    return NULL;
+}
+
 /* Parses 'arg' as the argument to a "set_mpls_ttl" action, and appends such an
  * action to 'ofpacts'.
  *
@@ -465,13 +501,12 @@ static char * WARN_UNUSED_RESULT
 set_field_parse__(char *arg, struct ofpbuf *ofpacts,
                   enum ofputil_protocol *usable_protocols)
 {
-    struct ofpact_reg_load *load = ofpact_put_REG_LOAD(ofpacts);
+    struct ofpact_set_field *sf = ofpact_put_SET_FIELD(ofpacts);
     char *value;
     char *delim;
     char *key;
     const struct mf_field *mf;
     char *error;
-    union mf_value mf_value;
 
     value = arg;
     delim = strstr(arg, "->");
@@ -490,16 +525,16 @@ set_field_parse__(char *arg, struct ofpbuf *ofpacts,
     if (!mf->writable) {
         return xasprintf("%s is read-only", key);
     }
-
+    sf->field = mf;
     delim[0] = '\0';
-    error = mf_parse_value(mf, value, &mf_value);
+    error = mf_parse_value(mf, value, &sf->value);
     if (error) {
         return error;
     }
-    if (!mf_is_value_valid(mf, &mf_value)) {
+
+    if (!mf_is_value_valid(mf, &sf->value)) {
         return xasprintf("%s is not a valid value for field %s", value, key);
     }
-    ofpact_set_field_init(load, mf, &mf_value);
 
     *usable_protocols &= mf->usable_protocols;
     return NULL;
@@ -599,6 +634,8 @@ parse_named_action(enum ofputil_action_code code,
 {
     size_t orig_size = ofpacts->size;
     struct ofpact_tunnel *tunnel;
+    struct ofpact_vlan_vid *vlan_vid;
+    struct ofpact_vlan_pcp *vlan_pcp;
     char *error = NULL;
     uint16_t ethertype = 0;
     uint16_t vid = 0;
@@ -624,7 +661,10 @@ parse_named_action(enum ofputil_action_code code,
         if (vid & ~VLAN_VID_MASK) {
             return xasprintf("%s: not a valid VLAN VID", arg);
         }
-        ofpact_put_SET_VLAN_VID(ofpacts)->vlan_vid = vid;
+        vlan_vid = ofpact_put_SET_VLAN_VID(ofpacts);
+        vlan_vid->vlan_vid = vid;
+        vlan_vid->ofpact.compat = code;
+        vlan_vid->push_vlan_if_needed = code == OFPUTIL_OFPAT10_SET_VLAN_VID;
         break;
 
     case OFPUTIL_OFPAT10_SET_VLAN_PCP:
@@ -637,7 +677,10 @@ parse_named_action(enum ofputil_action_code code,
         if (pcp & ~7) {
             return xasprintf("%s: not a valid VLAN PCP", arg);
         }
-        ofpact_put_SET_VLAN_PCP(ofpacts)->vlan_pcp = pcp;
+        vlan_pcp = ofpact_put_SET_VLAN_PCP(ofpacts);
+        vlan_pcp->vlan_pcp = pcp;
+        vlan_pcp->ofpact.compat = code;
+        vlan_pcp->push_vlan_if_needed = code == OFPUTIL_OFPAT10_SET_VLAN_PCP;
         break;
 
     case OFPUTIL_OFPAT12_SET_FIELD:
@@ -645,7 +688,7 @@ parse_named_action(enum ofputil_action_code code,
 
     case OFPUTIL_OFPAT10_STRIP_VLAN:
     case OFPUTIL_OFPAT11_POP_VLAN:
-        ofpact_put_STRIP_VLAN(ofpacts);
+        ofpact_put_STRIP_VLAN(ofpacts)->ofpact.compat = code;
         break;
 
     case OFPUTIL_OFPAT11_PUSH_VLAN:
@@ -804,6 +847,16 @@ parse_named_action(enum ofputil_action_code code,
         error = parse_dec_ttl(ofpacts, arg);
         break;
 
+    case OFPUTIL_NXAST_SET_MPLS_LABEL:
+    case OFPUTIL_OFPAT11_SET_MPLS_LABEL:
+        error = parse_set_mpls_label(ofpacts, arg);
+        break;
+
+    case OFPUTIL_NXAST_SET_MPLS_TC:
+    case OFPUTIL_OFPAT11_SET_MPLS_TC:
+        error = parse_set_mpls_tc(ofpacts, arg);
+        break;
+
     case OFPUTIL_NXAST_SET_MPLS_TTL:
     case OFPUTIL_OFPAT11_SET_MPLS_TTL:
         error = parse_set_mpls_ttl(ofpacts, arg);
@@ -1361,7 +1414,7 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
             enum ofperr err;
 
             err = ofpacts_check(ofpacts.data, ofpacts.size, &fm->match.flow,
-                                OFPP_MAX, 0, true);
+                                true, OFPP_MAX, fm->table_id, 255);
             if (err) {
                 if (!enforce_consistency &&
                     err == OFPERR_OFPBAC_MATCH_INCONSISTENT) {
@@ -1370,7 +1423,8 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
                     /* Try again, allowing for inconsistency.
                      * XXX: As a side effect, logging may be duplicated. */
                     err = ofpacts_check(ofpacts.data, ofpacts.size,
-                                        &fm->match.flow, OFPP_MAX, 0, false);
+                                        &fm->match.flow, false,
+                                        OFPP_MAX, fm->table_id, 255);
                 }
                 if (err) {
                     error = xasprintf("actions are invalid with specified match "
@@ -2278,7 +2332,12 @@ parse_ofp_group_mod_file(const char *file_name, uint16_t command,
         char *error;
 
         if (*n_gms >= allocated_gms) {
+            size_t i;
+
             *gms = x2nrealloc(*gms, &allocated_gms, sizeof **gms);
+            for (i = 0; i < *n_gms; i++) {
+                list_moved(&(*gms)[i].buckets);
+            }
         }
         error = parse_ofp_group_mod_str(&(*gms)[*n_gms], command, ds_cstr(&s),
                                         &usable);
index e4d0303..37e1f4f 100644 (file)
@@ -757,7 +757,8 @@ ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh, int verbosity)
     protocol = ofputil_protocol_set_tid(protocol, true);
 
     ofpbuf_init(&ofpacts, 64);
-    error = ofputil_decode_flow_mod(&fm, oh, protocol, &ofpacts);
+    error = ofputil_decode_flow_mod(&fm, oh, protocol, &ofpacts,
+                                    OFPP_MAX, 255);
     if (error) {
         ofpbuf_uninit(&ofpacts);
         ofp_print_error(s, error);
@@ -1008,6 +1009,71 @@ ofp_print_table_mod(struct ds *string, const struct ofp_header *oh)
     ofp_print_table_miss_config(string, pm.config);
 }
 
+static void
+ofp_print_queue_get_config_request(struct ds *string,
+                                   const struct ofp_header *oh)
+{
+    enum ofperr error;
+    ofp_port_t port;
+
+    error = ofputil_decode_queue_get_config_request(oh, &port);
+    if (error) {
+        ofp_print_error(string, error);
+        return;
+    }
+
+    ds_put_cstr(string, " port=");
+    ofputil_format_port(port, string);
+}
+
+static void
+print_queue_rate(struct ds *string, const char *name, unsigned int rate)
+{
+    if (rate <= 1000) {
+        ds_put_format(string, " %s:%u.%u%%", name, rate / 10, rate % 10);
+    } else if (rate < UINT16_MAX) {
+        ds_put_format(string, " %s:(disabled)", name);
+    }
+}
+
+static void
+ofp_print_queue_get_config_reply(struct ds *string,
+                                 const struct ofp_header *oh)
+{
+    enum ofperr error;
+    struct ofpbuf b;
+    ofp_port_t port;
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    error = ofputil_decode_queue_get_config_reply(&b, &port);
+    if (error) {
+        ofp_print_error(string, error);
+        return;
+    }
+
+    ds_put_cstr(string, " port=");
+    ofputil_format_port(port, string);
+    ds_put_char(string, '\n');
+
+    for (;;) {
+        struct ofputil_queue_config queue;
+        int retval;
+
+        retval = ofputil_pull_queue_get_config_reply(&b, &queue);
+        if (retval) {
+            if (retval != EOF) {
+                ofp_print_error(string, retval);
+            }
+            break;
+        }
+
+        ds_put_format(string, "queue %"PRIu32":", queue.queue_id);
+        print_queue_rate(string, "min_rate", queue.min_rate);
+        print_queue_rate(string, "max_rate", queue.max_rate);
+        ds_put_char(string, '\n');
+    }
+}
+
 static void
 ofp_print_meter_flags(struct ds *s, uint16_t flags)
 {
@@ -1852,20 +1918,12 @@ ofp_print_echo(struct ds *string, const struct ofp_header *oh, int verbosity)
 }
 
 static void
-ofp_print_role_message(struct ds *string, const struct ofp_header *oh)
+ofp_print_role_generic(struct ds *string, enum ofp12_controller_role role,
+                       uint64_t generation_id)
 {
-    struct ofputil_role_request rr;
-    enum ofperr error;
-
-    error = ofputil_decode_role_message(oh, &rr);
-    if (error) {
-        ofp_print_error(string, error);
-        return;
-    }
-
     ds_put_cstr(string, " role=");
 
-    switch (rr.role) {
+    switch (role) {
     case OFPCR12_ROLE_NOCHANGE:
         ds_put_cstr(string, "nochange");
         break;
@@ -1882,8 +1940,54 @@ ofp_print_role_message(struct ds *string, const struct ofp_header *oh)
         NOT_REACHED();
     }
 
-    if (rr.have_generation_id) {
-        ds_put_format(string, " generation_id=%"PRIu64, rr.generation_id);
+    if (generation_id != UINT64_MAX) {
+        ds_put_format(string, " generation_id=%"PRIu64, generation_id);
+    }
+}
+
+static void
+ofp_print_role_message(struct ds *string, const struct ofp_header *oh)
+{
+    struct ofputil_role_request rr;
+    enum ofperr error;
+
+    error = ofputil_decode_role_message(oh, &rr);
+    if (error) {
+        ofp_print_error(string, error);
+        return;
+    }
+
+    ofp_print_role_generic(string, rr.role, rr.have_generation_id ? rr.generation_id : UINT64_MAX);
+}
+
+static void
+ofp_print_role_status_message(struct ds *string, const struct ofp_header *oh)
+{
+    struct ofputil_role_status rs;
+    enum ofperr error;
+
+    error = ofputil_decode_role_status(oh, &rs);
+    if (error) {
+        ofp_print_error(string, error);
+        return;
+    }
+
+    ofp_print_role_generic(string, rs.role, rs.generation_id);
+
+    ds_put_cstr(string, " reason=");
+
+    switch (rs.reason) {
+    case OFPCRR_MASTER_REQUEST:
+        ds_put_cstr(string, "master_request");
+        break;
+    case OFPCRR_CONFIG:
+        ds_put_cstr(string, "configuration_changed");
+        break;
+    case OFPCRR_EXPERIMENTER:
+        ds_put_cstr(string, "experimenter_data_changed");
+        break;
+    default:
+        NOT_REACHED();
     }
 }
 
@@ -2419,8 +2523,6 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
         ofp_print_group_mod(string, oh);
         break;
 
-    case OFPTYPE_QUEUE_GET_CONFIG_REQUEST:
-    case OFPTYPE_QUEUE_GET_CONFIG_REPLY:
     case OFPTYPE_TABLE_FEATURES_STATS_REQUEST:
     case OFPTYPE_TABLE_FEATURES_STATS_REPLY:
         ofp_print_not_implemented(string);
@@ -2490,10 +2592,21 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
     case OFPTYPE_BARRIER_REPLY:
         break;
 
+    case OFPTYPE_QUEUE_GET_CONFIG_REQUEST:
+        ofp_print_queue_get_config_request(string, oh);
+        break;
+
+    case OFPTYPE_QUEUE_GET_CONFIG_REPLY:
+        ofp_print_queue_get_config_reply(string, oh);
+        break;
+
     case OFPTYPE_ROLE_REQUEST:
     case OFPTYPE_ROLE_REPLY:
         ofp_print_role_message(string, oh);
         break;
+    case OFPTYPE_ROLE_STATUS:
+        ofp_print_role_status_message(string, oh);
+        break;
 
     case OFPTYPE_METER_STATS_REQUEST:
     case OFPTYPE_METER_CONFIG_STATS_REQUEST:
index 8c200ce..9645e04 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 == 21);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 22);
 
     /* Initialize most of wc. */
     flow_wildcards_init_catchall(wc);
@@ -436,11 +436,11 @@ ofputil_match_from_ofp11_match(const struct ofp11_match *ofmatch,
     }
 
     if (eth_type_mpls(match->flow.dl_type)) {
-        enum { OFPFW11_MPLS_ALL = OFPFW11_MPLS_LABEL | OFPFW11_MPLS_TC };
-
-        if ((wc & OFPFW11_MPLS_ALL) != OFPFW11_MPLS_ALL) {
-            /* MPLS not supported. */
-            return OFPERR_OFPBMC_BAD_TAG;
+        if (!(wc & OFPFW11_MPLS_LABEL)) {
+            match_set_mpls_label(match, ofmatch->mpls_label);
+        }
+        if (!(wc & OFPFW11_MPLS_TC)) {
+            match_set_mpls_tc(match, ofmatch->mpls_tc);
         }
     }
 
@@ -533,9 +533,17 @@ ofputil_match_to_ofp11_match(const struct match *match,
         ofmatch->tp_dst = match->flow.tp_dst;
     }
 
-    /* MPLS not supported. */
-    wc |= OFPFW11_MPLS_LABEL;
-    wc |= OFPFW11_MPLS_TC;
+    if (!(match->wc.masks.mpls_lse & htonl(MPLS_LABEL_MASK))) {
+        wc |= OFPFW11_MPLS_LABEL;
+    } else {
+        ofmatch->mpls_label = htonl(mpls_lse_to_label(match->flow.mpls_lse));
+    }
+
+    if (!(match->wc.masks.mpls_lse & htonl(MPLS_TC_MASK))) {
+        wc |= OFPFW11_MPLS_TC;
+    } else {
+        ofmatch->mpls_tc = mpls_lse_to_tc(match->flow.mpls_lse);
+    }
 
     ofmatch->metadata = match->flow.metadata;
     ofmatch->metadata_mask = ~match->wc.masks.metadata;
@@ -1484,7 +1492,8 @@ enum ofperr
 ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
                         const struct ofp_header *oh,
                         enum ofputil_protocol protocol,
-                        struct ofpbuf *ofpacts)
+                        struct ofpbuf *ofpacts,
+                        ofp_port_t max_port, uint8_t max_table)
 {
     ovs_be16 raw_flags;
     enum ofperr error;
@@ -1504,8 +1513,8 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
             return error;
         }
 
-        error = ofpacts_pull_openflow11_instructions(&b, oh->version,
-                                                     b.size, ofpacts);
+        error = ofpacts_pull_openflow_instructions(&b, b.size, oh->version,
+                                                   ofpacts);
         if (error) {
             return error;
         }
@@ -1530,7 +1539,19 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
         }
         fm->modify_cookie = false;
         fm->command = ofm->command;
+
+        /* Get table ID.
+         *
+         * OF1.1 entirely forbids table_id == 255.
+         * OF1.2+ allows table_id == 255 only for deletes. */
         fm->table_id = ofm->table_id;
+        if (fm->table_id == 255
+            && (oh->version == OFP11_VERSION
+                || (ofm->command != OFPFC_DELETE &&
+                    ofm->command != OFPFC_DELETE_STRICT))) {
+            return OFPERR_OFPFMFC_BAD_TABLE_ID;
+        }
+
         fm->idle_timeout = ntohs(ofm->idle_timeout);
         fm->hard_timeout = ntohs(ofm->hard_timeout);
         fm->buffer_id = ntohl(ofm->buffer_id);
@@ -1561,7 +1582,8 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
             ofputil_normalize_match(&fm->match);
 
             /* Now get the actions. */
-            error = ofpacts_pull_openflow10(&b, b.size, ofpacts);
+            error = ofpacts_pull_openflow_actions(&b, b.size, oh->version,
+                                                  ofpacts);
             if (error) {
                 return error;
             }
@@ -1594,7 +1616,8 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
             if (error) {
                 return error;
             }
-            error = ofpacts_pull_openflow10(&b, b.size, ofpacts);
+            error = ofpacts_pull_openflow_actions(&b, b.size, oh->version,
+                                                  ofpacts);
             if (error) {
                 return error;
             }
@@ -1649,7 +1672,9 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
                 : OFPERR_OFPFMFC_TABLE_FULL);
     }
 
-    return 0;
+    return ofpacts_check(fm->ofpacts, fm->ofpacts_len, &fm->match.flow,
+                         oh->version > OFP10_VERSION, max_port,
+                         fm->table_id, max_table);
 }
 
 static enum ofperr
@@ -2056,7 +2081,14 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
             ofm->cookie = fm->cookie;
         }
         ofm->cookie_mask = fm->cookie_mask;
-        ofm->table_id = fm->table_id;
+        if (fm->table_id != 255
+            || (protocol != OFPUTIL_P_OF11_STD
+                && (fm->command == OFPFC_DELETE ||
+                    fm->command == OFPFC_DELETE_STRICT))) {
+            ofm->table_id = fm->table_id;
+        } else {
+            ofm->table_id = 0;
+        }
         ofm->command = fm->command;
         ofm->idle_timeout = htons(fm->idle_timeout);
         ofm->hard_timeout = htons(fm->hard_timeout);
@@ -2066,7 +2098,8 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
         ofm->out_group = htonl(fm->out_group);
         ofm->flags = raw_flags;
         ofputil_put_ofp11_match(msg, &fm->match, protocol);
-        ofpacts_put_openflow11_instructions(fm->ofpacts, fm->ofpacts_len, msg);
+        ofpacts_put_openflow_instructions(fm->ofpacts, fm->ofpacts_len, msg,
+                                          version);
         break;
     }
 
@@ -2086,7 +2119,8 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
         ofm->buffer_id = htonl(fm->buffer_id);
         ofm->out_port = htons(ofp_to_u16(fm->out_port));
         ofm->flags = raw_flags;
-        ofpacts_put_openflow10(fm->ofpacts, fm->ofpacts_len, msg);
+        ofpacts_put_openflow_actions(fm->ofpacts, fm->ofpacts_len, msg,
+                                     version);
         break;
     }
 
@@ -2109,7 +2143,8 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
         nfm->out_port = htons(ofp_to_u16(fm->out_port));
         nfm->flags = raw_flags;
         nfm->match_len = htons(match_len);
-        ofpacts_put_openflow10(fm->ofpacts, fm->ofpacts_len, msg);
+        ofpacts_put_openflow_actions(fm->ofpacts, fm->ofpacts_len, msg,
+                                     version);
         break;
     }
 
@@ -2186,6 +2221,283 @@ ofputil_decode_nxst_flow_request(struct ofputil_flow_stats_request *fsr,
     return 0;
 }
 
+/* Constructs and returns an OFPT_QUEUE_GET_CONFIG request for the specified
+ * 'port', suitable for OpenFlow version 'version'. */
+struct ofpbuf *
+ofputil_encode_queue_get_config_request(enum ofp_version version,
+                                        ofp_port_t port)
+{
+    struct ofpbuf *request;
+
+    if (version == OFP10_VERSION) {
+        struct ofp10_queue_get_config_request *qgcr10;
+
+        request = ofpraw_alloc(OFPRAW_OFPT10_QUEUE_GET_CONFIG_REQUEST,
+                               version, 0);
+        qgcr10 = ofpbuf_put_zeros(request, sizeof *qgcr10);
+        qgcr10->port = htons(ofp_to_u16(port));
+    } else {
+        struct ofp11_queue_get_config_request *qgcr11;
+
+        request = ofpraw_alloc(OFPRAW_OFPT11_QUEUE_GET_CONFIG_REQUEST,
+                               version, 0);
+        qgcr11 = ofpbuf_put_zeros(request, sizeof *qgcr11);
+        qgcr11->port = ofputil_port_to_ofp11(port);
+    }
+
+    return request;
+}
+
+/* Parses OFPT_QUEUE_GET_CONFIG request 'oh', storing the port specified by the
+ * request into '*port'.  Returns 0 if successful, otherwise an OpenFlow error
+ * code. */
+enum ofperr
+ofputil_decode_queue_get_config_request(const struct ofp_header *oh,
+                                        ofp_port_t *port)
+{
+    const struct ofp10_queue_get_config_request *qgcr10;
+    const struct ofp11_queue_get_config_request *qgcr11;
+    enum ofpraw raw;
+    struct ofpbuf b;
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    raw = ofpraw_pull_assert(&b);
+
+    switch ((int) raw) {
+    case OFPRAW_OFPT10_QUEUE_GET_CONFIG_REQUEST:
+        qgcr10 = b.data;
+        *port = u16_to_ofp(ntohs(qgcr10->port));
+        return 0;
+
+    case OFPRAW_OFPT11_QUEUE_GET_CONFIG_REQUEST:
+        qgcr11 = b.data;
+        return ofputil_port_from_ofp11(qgcr11->port, port);
+    }
+
+    NOT_REACHED();
+}
+
+/* Constructs and returns the beginning of a reply to
+ * OFPT_QUEUE_GET_CONFIG_REQUEST 'oh'.  The caller may append information about
+ * individual queues with ofputil_append_queue_get_config_reply(). */
+struct ofpbuf *
+ofputil_encode_queue_get_config_reply(const struct ofp_header *oh)
+{
+    struct ofp10_queue_get_config_reply *qgcr10;
+    struct ofp11_queue_get_config_reply *qgcr11;
+    struct ofpbuf *reply;
+    enum ofperr error;
+    struct ofpbuf b;
+    enum ofpraw raw;
+    ofp_port_t port;
+
+    error = ofputil_decode_queue_get_config_request(oh, &port);
+    ovs_assert(!error);
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    raw = ofpraw_pull_assert(&b);
+
+    switch ((int) raw) {
+    case OFPRAW_OFPT10_QUEUE_GET_CONFIG_REQUEST:
+        reply = ofpraw_alloc_reply(OFPRAW_OFPT10_QUEUE_GET_CONFIG_REPLY,
+                                   oh, 0);
+        qgcr10 = ofpbuf_put_zeros(reply, sizeof *qgcr10);
+        qgcr10->port = htons(ofp_to_u16(port));
+        break;
+
+    case OFPRAW_OFPT11_QUEUE_GET_CONFIG_REQUEST:
+        reply = ofpraw_alloc_reply(OFPRAW_OFPT11_QUEUE_GET_CONFIG_REPLY,
+                                   oh, 0);
+        qgcr11 = ofpbuf_put_zeros(reply, sizeof *qgcr11);
+        qgcr11->port = ofputil_port_to_ofp11(port);
+        break;
+
+    default:
+        NOT_REACHED();
+    }
+
+    return reply;
+}
+
+static void
+put_queue_rate(struct ofpbuf *reply, enum ofp_queue_properties property,
+               uint16_t rate)
+{
+    if (rate != UINT16_MAX) {
+        struct ofp_queue_prop_rate *oqpr;
+
+        oqpr = ofpbuf_put_zeros(reply, sizeof *oqpr);
+        oqpr->prop_header.property = htons(property);
+        oqpr->prop_header.len = htons(sizeof *oqpr);
+        oqpr->rate = htons(rate);
+    }
+}
+
+/* Appends a queue description for 'queue_id' to the
+ * OFPT_QUEUE_GET_CONFIG_REPLY already in 'oh'. */
+void
+ofputil_append_queue_get_config_reply(struct ofpbuf *reply,
+                                      const struct ofputil_queue_config *oqc)
+{
+    const struct ofp_header *oh = reply->data;
+    size_t start_ofs, len_ofs;
+    ovs_be16 *len;
+
+    start_ofs = reply->size;
+    if (oh->version < OFP12_VERSION) {
+        struct ofp10_packet_queue *opq10;
+
+        opq10 = ofpbuf_put_zeros(reply, sizeof *opq10);
+        opq10->queue_id = htonl(oqc->queue_id);
+        len_ofs = (char *) &opq10->len - (char *) reply->data;
+    } else {
+        struct ofp11_queue_get_config_reply *qgcr11;
+        struct ofp12_packet_queue *opq12;
+        ovs_be32 port;
+
+        qgcr11 = reply->l3;
+        port = qgcr11->port;
+
+        opq12 = ofpbuf_put_zeros(reply, sizeof *opq12);
+        opq12->port = port;
+        opq12->queue_id = htonl(oqc->queue_id);
+        len_ofs = (char *) &opq12->len - (char *) reply->data;
+    }
+
+    put_queue_rate(reply, OFPQT_MIN_RATE, oqc->min_rate);
+    put_queue_rate(reply, OFPQT_MAX_RATE, oqc->max_rate);
+
+    len = ofpbuf_at(reply, len_ofs, sizeof *len);
+    *len = htons(reply->size - start_ofs);
+}
+
+/* Decodes the initial part of an OFPT_QUEUE_GET_CONFIG_REPLY from 'reply' and
+ * stores in '*port' the port that the reply is about.  The caller may call
+ * ofputil_pull_queue_get_config_reply() to obtain information about individual
+ * queues included in the reply.  Returns 0 if successful, otherwise an
+ * ofperr.*/
+enum ofperr
+ofputil_decode_queue_get_config_reply(struct ofpbuf *reply, ofp_port_t *port)
+{
+    const struct ofp10_queue_get_config_reply *qgcr10;
+    const struct ofp11_queue_get_config_reply *qgcr11;
+    enum ofpraw raw;
+
+    raw = ofpraw_pull_assert(reply);
+    switch ((int) raw) {
+    case OFPRAW_OFPT10_QUEUE_GET_CONFIG_REPLY:
+        qgcr10 = ofpbuf_pull(reply, sizeof *qgcr10);
+        *port = u16_to_ofp(ntohs(qgcr10->port));
+        return 0;
+
+    case OFPRAW_OFPT11_QUEUE_GET_CONFIG_REPLY:
+        qgcr11 = ofpbuf_pull(reply, sizeof *qgcr11);
+        return ofputil_port_from_ofp11(qgcr11->port, port);
+    }
+
+    NOT_REACHED();
+}
+
+static enum ofperr
+parse_queue_rate(const struct ofp_queue_prop_header *hdr, uint16_t *rate)
+{
+    const struct ofp_queue_prop_rate *oqpr;
+
+    if (hdr->len == htons(sizeof *oqpr)) {
+        oqpr = (const struct ofp_queue_prop_rate *) hdr;
+        *rate = ntohs(oqpr->rate);
+        return 0;
+    } else {
+        return OFPERR_OFPBRC_BAD_LEN;
+    }
+}
+
+/* Decodes information about a queue from the OFPT_QUEUE_GET_CONFIG_REPLY in
+ * 'reply' and stores it in '*queue'.  ofputil_decode_queue_get_config_reply()
+ * must already have pulled off the main header.
+ *
+ * This function returns EOF if the last queue has already been decoded, 0 if a
+ * queue was successfully decoded into '*queue', or an ofperr if there was a
+ * problem decoding 'reply'. */
+int
+ofputil_pull_queue_get_config_reply(struct ofpbuf *reply,
+                                    struct ofputil_queue_config *queue)
+{
+    const struct ofp_header *oh;
+    unsigned int opq_len;
+    unsigned int len;
+
+    if (!reply->size) {
+        return EOF;
+    }
+
+    queue->min_rate = UINT16_MAX;
+    queue->max_rate = UINT16_MAX;
+
+    oh = reply->l2;
+    if (oh->version < OFP12_VERSION) {
+        const struct ofp10_packet_queue *opq10;
+
+        opq10 = ofpbuf_try_pull(reply, sizeof *opq10);
+        if (!opq10) {
+            return OFPERR_OFPBRC_BAD_LEN;
+        }
+        queue->queue_id = ntohl(opq10->queue_id);
+        len = ntohs(opq10->len);
+        opq_len = sizeof *opq10;
+    } else {
+        const struct ofp12_packet_queue *opq12;
+
+        opq12 = ofpbuf_try_pull(reply, sizeof *opq12);
+        if (!opq12) {
+            return OFPERR_OFPBRC_BAD_LEN;
+        }
+        queue->queue_id = ntohl(opq12->queue_id);
+        len = ntohs(opq12->len);
+        opq_len = sizeof *opq12;
+    }
+
+    if (len < opq_len || len > reply->size + opq_len || len % 8) {
+        return OFPERR_OFPBRC_BAD_LEN;
+    }
+    len -= opq_len;
+
+    while (len > 0) {
+        const struct ofp_queue_prop_header *hdr;
+        unsigned int property;
+        unsigned int prop_len;
+        enum ofperr error = 0;
+
+        hdr = ofpbuf_at_assert(reply, 0, sizeof *hdr);
+        prop_len = ntohs(hdr->len);
+        if (prop_len < sizeof *hdr || prop_len > reply->size || prop_len % 8) {
+            return OFPERR_OFPBRC_BAD_LEN;
+        }
+
+        property = ntohs(hdr->property);
+        switch (property) {
+        case OFPQT_MIN_RATE:
+            error = parse_queue_rate(hdr, &queue->min_rate);
+            break;
+
+        case OFPQT_MAX_RATE:
+            error = parse_queue_rate(hdr, &queue->max_rate);
+            break;
+
+        default:
+            VLOG_INFO_RL(&bad_ofmsg_rl, "unknown queue property %u", property);
+            break;
+        }
+        if (error) {
+            return error;
+        }
+
+        ofpbuf_pull(reply, prop_len);
+        len -= prop_len;
+    }
+    return 0;
+}
+
 /* Converts an OFPST_FLOW, OFPST_AGGREGATE, NXST_FLOW, or NXST_AGGREGATE
  * request 'oh', into an abstract flow_stats_request in 'fsr'.  Returns 0 if
  * successful, otherwise an OpenFlow error code. */
@@ -2361,9 +2673,9 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
             return EINVAL;
         }
 
-        if (ofpacts_pull_openflow11_instructions(msg, oh->version,
-                                                 length - sizeof *ofs -
-                                                 padded_match_len, ofpacts)) {
+        if (ofpacts_pull_openflow_instructions(msg, length - sizeof *ofs -
+                                               padded_match_len, oh->version,
+                                               ofpacts)) {
             VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply bad instructions");
             return EINVAL;
         }
@@ -2406,7 +2718,8 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
             return EINVAL;
         }
 
-        if (ofpacts_pull_openflow10(msg, length - sizeof *ofs, ofpacts)) {
+        if (ofpacts_pull_openflow_actions(msg, length - sizeof *ofs,
+                                          oh->version, ofpacts)) {
             return EINVAL;
         }
 
@@ -2446,7 +2759,8 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
         }
 
         actions_len = length - sizeof *nfs - ROUND_UP(match_len, 8);
-        if (ofpacts_pull_openflow10(msg, actions_len, ofpacts)) {
+        if (ofpacts_pull_openflow_actions(msg, actions_len, oh->version,
+                                          ofpacts)) {
             return EINVAL;
         }
 
@@ -2500,16 +2814,16 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
     struct ofpbuf *reply = ofpbuf_from_list(list_back(replies));
     size_t start_ofs = reply->size;
     enum ofpraw raw;
+    enum ofp_version version = ((struct ofp_header *)reply->data)->version;
 
     ofpraw_decode_partial(&raw, reply->data, reply->size);
     if (raw == OFPRAW_OFPST11_FLOW_REPLY || raw == OFPRAW_OFPST13_FLOW_REPLY) {
-        const struct ofp_header *oh = reply->data;
         struct ofp11_flow_stats *ofs;
 
         ofpbuf_put_uninit(reply, sizeof *ofs);
         oxm_put_match(reply, &fs->match);
-        ofpacts_put_openflow11_instructions(fs->ofpacts, fs->ofpacts_len,
-                                            reply);
+        ofpacts_put_openflow_instructions(fs->ofpacts, fs->ofpacts_len, reply,
+                                          version);
 
         ofs = ofpbuf_at_assert(reply, start_ofs, sizeof *ofs);
         ofs->length = htons(reply->size - start_ofs);
@@ -2521,7 +2835,7 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
         ofs->idle_timeout = htons(fs->idle_timeout);
         ofs->hard_timeout = htons(fs->hard_timeout);
         if (raw == OFPRAW_OFPST13_FLOW_REPLY) {
-            ofs->flags = ofputil_encode_flow_mod_flags(fs->flags, oh->version);
+            ofs->flags = ofputil_encode_flow_mod_flags(fs->flags, version);
         } else {
             ofs->flags = 0;
         }
@@ -2533,8 +2847,8 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
         struct ofp10_flow_stats *ofs;
 
         ofpbuf_put_uninit(reply, sizeof *ofs);
-        ofpacts_put_openflow10(fs->ofpacts, fs->ofpacts_len, reply);
-
+        ofpacts_put_openflow_actions(fs->ofpacts, fs->ofpacts_len, reply,
+                                     version);
         ofs = ofpbuf_at_assert(reply, start_ofs, sizeof *ofs);
         ofs->length = htons(reply->size - start_ofs);
         ofs->table_id = fs->table_id;
@@ -2557,8 +2871,8 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
 
         ofpbuf_put_uninit(reply, sizeof *nfs);
         match_len = nx_put_match(reply, &fs->match, 0, 0);
-        ofpacts_put_openflow10(fs->ofpacts, fs->ofpacts_len, reply);
-
+        ofpacts_put_openflow_actions(fs->ofpacts, fs->ofpacts_len, reply,
+                                     version);
         nfs = ofpbuf_at_assert(reply, start_ofs, sizeof *nfs);
         nfs->length = htons(reply->size - start_ofs);
         nfs->table_id = fs->table_id;
@@ -3094,9 +3408,8 @@ ofputil_decode_packet_out(struct ofputil_packet_out *po,
             return error;
         }
 
-        error = ofpacts_pull_openflow11_actions(&b, oh->version,
-                                                ntohs(opo->actions_len),
-                                                ofpacts);
+        error = ofpacts_pull_openflow_actions(&b, ntohs(opo->actions_len),
+                                              oh->version, ofpacts);
         if (error) {
             return error;
         }
@@ -3107,7 +3420,8 @@ ofputil_decode_packet_out(struct ofputil_packet_out *po,
         po->buffer_id = ntohl(opo->buffer_id);
         po->in_port = u16_to_ofp(ntohs(opo->in_port));
 
-        error = ofpacts_pull_openflow10(&b, ntohs(opo->actions_len), ofpacts);
+        error = ofpacts_pull_openflow_actions(&b, ntohs(opo->actions_len),
+                                              oh->version, ofpacts);
         if (error) {
             return error;
         }
@@ -3909,6 +4223,51 @@ ofputil_encode_role_reply(const struct ofp_header *request,
     return buf;
 }
 \f
+struct ofpbuf *
+ofputil_encode_role_status(const struct ofputil_role_status *status,
+                           enum ofputil_protocol protocol)
+{
+    struct ofpbuf *buf;
+    enum ofp_version version;
+    struct ofp14_role_status *rstatus;
+
+    version = ofputil_protocol_to_ofp_version(protocol);
+    buf = ofpraw_alloc_xid(OFPRAW_OFPT14_ROLE_STATUS, version, htonl(0), 0);
+    rstatus = ofpbuf_put_zeros(buf, sizeof *rstatus);
+    rstatus->role = htonl(status->role);
+    rstatus->reason = status->reason;
+    rstatus->generation_id = htonll(status->generation_id);
+
+    return buf;
+}
+
+enum ofperr
+ofputil_decode_role_status(const struct ofp_header *oh,
+                           struct ofputil_role_status *rs)
+{
+    struct ofpbuf b;
+    enum ofpraw raw;
+    const struct ofp14_role_status *r;
+
+    ofpbuf_use_const(&b, oh, ntohs(oh->length));
+    raw = ofpraw_pull_assert(&b);
+    ovs_assert(raw == OFPRAW_OFPT14_ROLE_STATUS);
+
+    r = b.l3;
+    if (r->role != htonl(OFPCR12_ROLE_NOCHANGE) &&
+        r->role != htonl(OFPCR12_ROLE_EQUAL) &&
+        r->role != htonl(OFPCR12_ROLE_MASTER) &&
+        r->role != htonl(OFPCR12_ROLE_SLAVE)) {
+        return OFPERR_OFPRRFC_BAD_ROLE;
+    }
+
+    rs->role = ntohl(r->role);
+    rs->generation_id = ntohll(r->generation_id);
+    rs->reason = r->reason;
+
+    return 0;
+}
+
 /* Table stats. */
 
 static void
@@ -4179,6 +4538,7 @@ ofputil_decode_flow_update(struct ofputil_flow_update *update,
 {
     struct nx_flow_update_header *nfuh;
     unsigned int length;
+    struct ofp_header *oh;
 
     if (!msg->l2) {
         msg->l2 = msg->data;
@@ -4193,6 +4553,8 @@ ofputil_decode_flow_update(struct ofputil_flow_update *update,
         goto bad_len;
     }
 
+    oh = msg->l2;
+
     nfuh = msg->data;
     update->event = ntohs(nfuh->event);
     length = ntohs(nfuh->length);
@@ -4241,7 +4603,8 @@ ofputil_decode_flow_update(struct ofputil_flow_update *update,
         }
 
         actions_len = length - sizeof *nfuf - ROUND_UP(match_len, 8);
-        error = ofpacts_pull_openflow10(msg, actions_len, ofpacts);
+        error = ofpacts_pull_openflow_actions(msg, actions_len, oh->version,
+                                              ofpacts);
         if (error) {
             return error;
         }
@@ -4301,9 +4664,11 @@ ofputil_append_flow_update(const struct ofputil_flow_update *update,
     struct nx_flow_update_header *nfuh;
     struct ofpbuf *msg;
     size_t start_ofs;
+    enum ofp_version version;
 
     msg = ofpbuf_from_list(list_back(replies));
     start_ofs = msg->size;
+    version = ((struct ofp_header *)msg->l2)->version;
 
     if (update->event == NXFME_ABBREV) {
         struct nx_flow_update_abbrev *nfua;
@@ -4316,8 +4681,8 @@ ofputil_append_flow_update(const struct ofputil_flow_update *update,
 
         ofpbuf_put_zeros(msg, sizeof *nfuf);
         match_len = nx_put_match(msg, update->match, htonll(0), htonll(0));
-        ofpacts_put_openflow10(update->ofpacts, update->ofpacts_len, msg);
-
+        ofpacts_put_openflow_actions(update->ofpacts, update->ofpacts_len, msg,
+                                     version);
         nfuf = ofpbuf_at_assert(msg, start_ofs, sizeof *nfuf);
         nfuf->reason = htons(update->reason);
         nfuf->priority = htons(update->priority);
@@ -4356,7 +4721,8 @@ ofputil_encode_packet_out(const struct ofputil_packet_out *po,
         msg = ofpraw_alloc(OFPRAW_OFPT10_PACKET_OUT, OFP10_VERSION, size);
         ofpbuf_put_zeros(msg, sizeof *opo);
         actions_ofs = msg->size;
-        ofpacts_put_openflow10(po->ofpacts, po->ofpacts_len, msg);
+        ofpacts_put_openflow_actions(po->ofpacts, po->ofpacts_len, msg,
+                                     ofp_version);
 
         opo = msg->l3;
         opo->buffer_id = htonl(po->buffer_id);
@@ -4373,8 +4739,8 @@ ofputil_encode_packet_out(const struct ofputil_packet_out *po,
 
         msg = ofpraw_alloc(OFPRAW_OFPT11_PACKET_OUT, ofp_version, size);
         ofpbuf_put_zeros(msg, sizeof *opo);
-        len = ofpacts_put_openflow11_actions(po->ofpacts, po->ofpacts_len, msg);
-
+        len = ofpacts_put_openflow_actions(po->ofpacts, po->ofpacts_len, msg,
+                                           ofp_version);
         opo = msg->l3;
         opo->buffer_id = htonl(po->buffer_id);
         opo->in_port = ofputil_port_to_ofp11(po->in_port);
@@ -4511,31 +4877,6 @@ ofputil_port_to_ofp11(ofp_port_t ofp10_port)
                  : ofp_to_u16(ofp10_port) + OFPP11_OFFSET);
 }
 
-/* Checks that 'port' is a valid output port for the OFPAT10_OUTPUT action, given
- * that the switch will never have more than 'max_ports' ports.  Returns 0 if
- * 'port' is valid, otherwise an OpenFlow return code. */
-enum ofperr
-ofputil_check_output_port(ofp_port_t port, ofp_port_t max_ports)
-{
-    switch (port) {
-    case OFPP_IN_PORT:
-    case OFPP_TABLE:
-    case OFPP_NORMAL:
-    case OFPP_FLOOD:
-    case OFPP_ALL:
-    case OFPP_CONTROLLER:
-    case OFPP_NONE:
-    case OFPP_LOCAL:
-        return 0;
-
-    default:
-        if (ofp_to_u16(port) < ofp_to_u16(max_ports)) {
-            return 0;
-        }
-        return OFPERR_OFPBAC_BAD_OUT_PORT;
-    }
-}
-
 #define OFPUTIL_NAMED_PORTS                     \
         OFPUTIL_NAMED_PORT(IN_PORT)             \
         OFPUTIL_NAMED_PORT(TABLE)               \
@@ -4742,22 +5083,21 @@ size_t ofputil_count_phy_ports(uint8_t ofp_version, struct ofpbuf *b)
     return b->size / ofputil_get_phy_port_size(ofp_version);
 }
 
-/* Returns the 'enum ofputil_action_code' corresponding to 'name' (e.g. if
- * 'name' is "output" then the return value is OFPUTIL_OFPAT10_OUTPUT), or -1 if
- * 'name' is not the name of any action.
- *
- * ofp-util.def lists the mapping from names to action. */
-int
-ofputil_action_code_from_name(const char *name)
-{
-    static const char *const names[OFPUTIL_N_ACTIONS] = {
-        NULL,
+/* ofp-util.def lists the mapping from names to action. */
+static const char *const names[OFPUTIL_N_ACTIONS] = {
+    NULL,
 #define OFPAT10_ACTION(ENUM, STRUCT, NAME)             NAME,
 #define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) NAME,
 #define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME)   NAME,
 #include "ofp-util.def"
-    };
+};
 
+/* Returns the 'enum ofputil_action_code' corresponding to 'name' (e.g. if
+ * 'name' is "output" then the return value is OFPUTIL_OFPAT10_OUTPUT), or -1
+ * if 'name' is not the name of any action. */
+int
+ofputil_action_code_from_name(const char *name)
+{
     const char *const *p;
 
     for (p = names; p < &names[ARRAY_SIZE(names)]; p++) {
@@ -4768,6 +5108,15 @@ ofputil_action_code_from_name(const char *name)
     return -1;
 }
 
+/* Returns name corresponding to the 'enum ofputil_action_code',
+ * or "Unkonwn action", if the name is not available. */
+const char *
+ofputil_action_name_from_code(enum ofputil_action_code code)
+{
+    return code < (int)OFPUTIL_N_ACTIONS && names[code] ? names[code]
+        : "Unknown action";
+}
+
 /* Appends an action of the type specified by 'code' to 'buf' and returns the
  * action.  Initializes the parts of 'action' that identify it as having type
  * <ENUM> and length 'sizeof *action' and zeros the rest.  For actions that
@@ -5650,6 +5999,7 @@ ofputil_append_group_desc_reply(const struct ofputil_group_desc *gds,
     struct ofp11_group_desc_stats *ogds;
     struct ofputil_bucket *bucket;
     size_t start_ogds;
+    enum ofp_version version = ((struct ofp_header *)reply->data)->version;
 
     start_ogds = reply->size;
     ofpbuf_put_zeros(reply, sizeof *ogds);
@@ -5659,9 +6009,8 @@ ofputil_append_group_desc_reply(const struct ofputil_group_desc *gds,
 
         start_ob = reply->size;
         ofpbuf_put_zeros(reply, sizeof *ob);
-        ofpacts_put_openflow11_actions(bucket->ofpacts,
-                                       bucket->ofpacts_len, reply);
-
+        ofpacts_put_openflow_actions(bucket->ofpacts, bucket->ofpacts_len,
+                                     reply, version);
         ob = ofpbuf_at_assert(reply, start_ob, sizeof *ob);
         ob->len = htons(reply->size - start_ob);
         ob->weight = htons(bucket->weight);
@@ -5677,8 +6026,8 @@ ofputil_append_group_desc_reply(const struct ofputil_group_desc *gds,
 }
 
 static enum ofperr
-ofputil_pull_buckets(struct ofpbuf *msg, enum ofp_version version,
-                     size_t buckets_length, struct list *buckets)
+ofputil_pull_buckets(struct ofpbuf *msg, size_t buckets_length,
+                     enum ofp_version version, struct list *buckets)
 {
     struct ofp11_bucket *ob;
 
@@ -5711,8 +6060,8 @@ ofputil_pull_buckets(struct ofpbuf *msg, enum ofp_version version,
         buckets_length -= ob_len;
 
         ofpbuf_init(&ofpacts, 0);
-        error = ofpacts_pull_openflow11_actions(msg, version,
-                                                ob_len - sizeof *ob, &ofpacts);
+        error = ofpacts_pull_openflow_actions(msg, ob_len - sizeof *ob,
+                                              version, &ofpacts);
         if (error) {
             ofpbuf_uninit(&ofpacts);
             ofputil_bucket_list_destroy(buckets);
@@ -5777,7 +6126,7 @@ ofputil_decode_group_desc_reply(struct ofputil_group_desc *gd,
         return OFPERR_OFPBRC_BAD_LEN;
     }
 
-    return ofputil_pull_buckets(msg, version, length - sizeof *ogds,
+    return ofputil_pull_buckets(msg, length - sizeof *ogds, version,
                                 &gd->buckets);
 }
 
@@ -5813,14 +6162,15 @@ ofputil_encode_group_mod(enum ofp_version ofp_version,
     case OFP13_VERSION: {
         b = ofpraw_alloc(OFPRAW_OFPT11_GROUP_MOD, ofp_version, 0);
         start_ogm = b->size;
-        ofpbuf_put_uninit(b, sizeof *ogm);
+        ofpbuf_put_zeros(b, sizeof *ogm);
 
         LIST_FOR_EACH (bucket, list_node, &gm->buckets) {
             start_bucket = b->size;
-            ofpbuf_put_uninit(b, sizeof *ob);
+            ofpbuf_put_zeros(b, sizeof *ob);
             if (bucket->ofpacts && bucket->ofpacts_len) {
-                ofpacts_put_openflow11_actions(bucket->ofpacts,
-                                bucket->ofpacts_len, b);
+                ofpacts_put_openflow_actions(bucket->ofpacts,
+                                             bucket->ofpacts_len, b,
+                                             ofp_version);
             }
             ob = ofpbuf_at_assert(b, start_bucket, sizeof *ob);
             ob->len = htons(b->size - start_bucket);;
@@ -5831,7 +6181,6 @@ ofputil_encode_group_mod(enum ofp_version ofp_version,
         ogm = ofpbuf_at_assert(b, start_ogm, sizeof *ogm);
         ogm->command = htons(gm->command);
         ogm->type = gm->type;
-        ogm->pad = 0;
         ogm->group_id = htonl(gm->group_id);
 
         break;
@@ -5852,6 +6201,8 @@ ofputil_decode_group_mod(const struct ofp_header *oh,
 {
     const struct ofp11_group_mod *ogm;
     struct ofpbuf msg;
+    struct ofputil_bucket *bucket;
+    enum ofperr err;
 
     ofpbuf_use_const(&msg, oh, ntohs(oh->length));
     ofpraw_pull_assert(&msg);
@@ -5861,7 +6212,32 @@ ofputil_decode_group_mod(const struct ofp_header *oh,
     gm->type = ogm->type;
     gm->group_id = ntohl(ogm->group_id);
 
-    return ofputil_pull_buckets(&msg, oh->version, msg.size, &gm->buckets);
+    err = ofputil_pull_buckets(&msg, msg.size, oh->version, &gm->buckets);
+    if (err) {
+        return err;
+    }
+
+    LIST_FOR_EACH (bucket, list_node, &gm->buckets) {
+        switch (gm->type) {
+        case OFPGT11_ALL:
+        case OFPGT11_INDIRECT:
+            if (ofputil_bucket_has_liveness(bucket)) {
+                return OFPERR_OFPGMFC_WATCH_UNSUPPORTED;
+            }
+            break;
+        case OFPGT11_SELECT:
+            break;
+        case OFPGT11_FF:
+            if (!ofputil_bucket_has_liveness(bucket)) {
+                return OFPERR_OFPGMFC_INVALID_GROUP;
+            }
+            break;
+        default:
+            NOT_REACHED();
+        }
+    }
+
+    return 0;
 }
 
 /* Parse a queue status request message into 'oqsr'.
index 752fd06..fae2bf2 100644 (file)
@@ -20,8 +20,8 @@ OFPAT10_ACTION(OFPAT10_ENQUEUE,      ofp10_action_enqueue,  "enqueue")
 #define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME)
 #endif
 OFPAT11_ACTION(OFPAT11_OUTPUT,       ofp11_action_output, 0, "output")
-OFPAT11_ACTION(OFPAT11_SET_VLAN_VID, ofp_action_vlan_vid, 0, "mod_vlan_vid")
-OFPAT11_ACTION(OFPAT11_SET_VLAN_PCP, ofp_action_vlan_pcp, 0, "mod_vlan_pcp")
+OFPAT11_ACTION(OFPAT11_SET_VLAN_VID, ofp_action_vlan_vid, 0, "set_vlan_vid")
+OFPAT11_ACTION(OFPAT11_SET_VLAN_PCP, ofp_action_vlan_pcp, 0, "set_vlan_pcp")
 OFPAT11_ACTION(OFPAT11_SET_DL_SRC,   ofp_action_dl_addr,  0, "mod_dl_src")
 OFPAT11_ACTION(OFPAT11_SET_DL_DST,   ofp_action_dl_addr,  0, "mod_dl_dst")
 OFPAT11_ACTION(OFPAT11_SET_NW_SRC,   ofp_action_nw_addr,  0, "mod_nw_src")
@@ -30,6 +30,8 @@ OFPAT11_ACTION(OFPAT11_SET_NW_TOS,   ofp_action_nw_tos,   0, "mod_nw_tos")
 OFPAT11_ACTION(OFPAT11_SET_NW_ECN,   ofp11_action_nw_ecn, 0, "mod_nw_ecn")
 OFPAT11_ACTION(OFPAT11_SET_TP_SRC,   ofp_action_tp_port,  0, "mod_tp_src")
 OFPAT11_ACTION(OFPAT11_SET_TP_DST,   ofp_action_tp_port,  0, "mod_tp_dst")
+OFPAT11_ACTION(OFPAT11_SET_MPLS_LABEL, ofp11_action_mpls_label, 0, "set_mpls_label")
+OFPAT11_ACTION(OFPAT11_SET_MPLS_TC,  ofp11_action_mpls_tc, 0, "set_mpls_tc")
 OFPAT11_ACTION(OFPAT11_SET_MPLS_TTL, ofp11_action_mpls_ttl, 0, "set_mpls_ttl")
 OFPAT11_ACTION(OFPAT11_DEC_MPLS_TTL, ofp_action_header,   0, "dec_mpls_ttl")
 OFPAT11_ACTION(OFPAT11_PUSH_VLAN,    ofp11_action_push,   0, "push_vlan")
@@ -68,6 +70,8 @@ NXAST_ACTION(NXAST_CONTROLLER,      nx_action_controller,   0, "controller")
 NXAST_ACTION(NXAST_DEC_TTL_CNT_IDS, nx_action_cnt_ids,      1, NULL)
 NXAST_ACTION(NXAST_WRITE_METADATA,  nx_action_write_metadata, 0,
              "write_metadata")
+NXAST_ACTION(NXAST_SET_MPLS_LABEL,  nx_action_mpls_label,   0, "set_mpls_label")
+NXAST_ACTION(NXAST_SET_MPLS_TC,     nx_action_mpls_tc,      0, "set_mpls_tc")
 NXAST_ACTION(NXAST_SET_MPLS_TTL,    nx_action_mpls_ttl,     0, "set_mpls_ttl")
 NXAST_ACTION(NXAST_DEC_MPLS_TTL,    nx_action_header,       0, "dec_mpls_ttl")
 NXAST_ACTION(NXAST_PUSH_MPLS,       nx_action_push_mpls,    0, "push_mpls")
index 1f77808..fef85e0 100644 (file)
 
 struct ofpbuf;
 union ofp_action;
+struct ofpact_set_field;
 
 /* Port numbers. */
 enum ofperr ofputil_port_from_ofp11(ovs_be32 ofp11_port,
                                     ofp_port_t *ofp10_port);
 ovs_be32 ofputil_port_to_ofp11(ofp_port_t ofp10_port);
 
-enum ofperr ofputil_check_output_port(ofp_port_t ofp_port,
-                                      ofp_port_t max_ports);
 bool ofputil_port_from_string(const char *, ofp_port_t *portp);
 void ofputil_format_port(ofp_port_t port, struct ds *);
 void ofputil_port_to_string(ofp_port_t, char namebuf[OFP_MAX_PORT_NAME_LEN],
@@ -295,7 +294,9 @@ struct ofputil_flow_mod {
 enum ofperr ofputil_decode_flow_mod(struct ofputil_flow_mod *,
                                     const struct ofp_header *,
                                     enum ofputil_protocol,
-                                    struct ofpbuf *ofpacts);
+                                    struct ofpbuf *ofpacts,
+                                    ofp_port_t max_port,
+                                    uint8_t max_table);
 struct ofpbuf *ofputil_encode_flow_mod(const struct ofputil_flow_mod *,
                                        enum ofputil_protocol);
 
@@ -692,11 +693,23 @@ struct ofputil_role_request {
     uint64_t generation_id;
 };
 
+struct ofputil_role_status {
+    enum ofp12_controller_role role;
+    enum ofp14_controller_role_reason reason;
+    uint64_t generation_id;
+};
+
 enum ofperr ofputil_decode_role_message(const struct ofp_header *,
                                         struct ofputil_role_request *);
 struct ofpbuf *ofputil_encode_role_reply(const struct ofp_header *,
                                          const struct ofputil_role_request *);
 
+struct ofpbuf *ofputil_encode_role_status(
+                                const struct ofputil_role_status *status,
+                                enum ofputil_protocol protocol);
+
+enum ofperr ofputil_decode_role_status(const struct ofp_header *oh,
+                                       struct ofputil_role_status *rs);
 /* Abstract table stats.
  *
  * For now we use ofp12_table_stats as a superset of the other protocol
@@ -706,6 +719,34 @@ struct ofpbuf *ofputil_encode_table_stats_reply(
     const struct ofp12_table_stats[], int n,
     const struct ofp_header *request);
 
+/* Queue configuration request. */
+struct ofpbuf *ofputil_encode_queue_get_config_request(enum ofp_version,
+                                                       ofp_port_t port);
+enum ofperr ofputil_decode_queue_get_config_request(const struct ofp_header *,
+                                                    ofp_port_t *port);
+
+/* Queue configuration reply. */
+struct ofputil_queue_config {
+    uint32_t queue_id;
+
+    /* Each of these optional values is expressed in tenths of a percent.
+     * Values greater than 1000 indicate that the feature is disabled.
+     * UINT16_MAX indicates that the value is omitted. */
+    uint16_t min_rate;
+    uint16_t max_rate;
+};
+
+struct ofpbuf *ofputil_encode_queue_get_config_reply(
+    const struct ofp_header *request);
+void ofputil_append_queue_get_config_reply(
+    struct ofpbuf *reply, const struct ofputil_queue_config *);
+
+enum ofperr ofputil_decode_queue_get_config_reply(struct ofpbuf *reply,
+                                                  ofp_port_t *);
+int ofputil_pull_queue_get_config_reply(struct ofpbuf *reply,
+                                        struct ofputil_queue_config *);
+
+
 /* Abstract nx_flow_monitor_request. */
 struct ofputil_flow_monitor_request {
     uint32_t id;
@@ -821,6 +862,7 @@ enum {
 };
 
 int ofputil_action_code_from_name(const char *);
+const char * ofputil_action_name_from_code(enum ofputil_action_code code);
 
 void *ofputil_put_action(enum ofputil_action_code, struct ofpbuf *buf);
 
@@ -949,12 +991,16 @@ struct ofputil_group_stats {
     struct bucket_counter *bucket_stats;
 };
 
-/* Group features reply, independent of protocol. */
+/* Group features reply, independent of protocol.
+ *
+ * Only OF1.2 and later support group features replies. */
 struct ofputil_group_features {
     uint32_t  types;           /* Bitmap of OFPGT_* values supported. */
     uint32_t  capabilities;    /* Bitmap of OFPGFC12_* capability supported. */
     uint32_t  max_groups[4];   /* Maximum number of groups for each type. */
-    uint32_t  actions[4];      /* Bitmaps of OFPAT_* that are supported. */
+
+    /* Bitmaps of OFPAT_* that are supported.  OF1.2+ actions only. */
+    uint32_t  actions[4];
 };
 
 /* Group desc reply, independent of protocol. */
@@ -966,6 +1012,13 @@ struct ofputil_group_desc {
 
 void ofputil_bucket_list_destroy(struct list *buckets);
 
+static inline bool
+ofputil_bucket_has_liveness(const struct ofputil_bucket *bucket)
+{
+    return (bucket->watch_port != OFPP_ANY ||
+            bucket->watch_group != OFPG_ANY);
+}
+
 struct ofpbuf *ofputil_encode_group_stats_request(enum ofp_version,
                                                   uint32_t group_id);
 enum ofperr ofputil_decode_group_stats_request(
index 922c5db..dc1970a 100644 (file)
@@ -898,7 +898,7 @@ packet_set_sctp_port(struct ofpbuf *packet, ovs_be16 src, ovs_be16 dst)
  *
  * 'flow' must be the flow corresponding to 'packet' and 'packet''s header
  * pointers must be properly initialized (e.g. with flow_extract()). */
-uint8_t
+uint16_t
 packet_get_tcp_flags(const struct ofpbuf *packet, const struct flow *flow)
 {
     if (dl_type_is_ip_any(flow->dl_type) &&
@@ -914,7 +914,7 @@ packet_get_tcp_flags(const struct ofpbuf *packet, const struct flow *flow)
  * (e.g. obtained via packet_get_tcp_flags() or TCP_FLAGS) to 's', in the
  * format used by tcpdump. */
 void
-packet_format_tcp_flags(struct ds *s, uint8_t tcp_flags)
+packet_format_tcp_flags(struct ds *s, uint16_t tcp_flags)
 {
     if (!tcp_flags) {
         ds_put_cstr(s, "none");
@@ -939,10 +939,22 @@ packet_format_tcp_flags(struct ds *s, uint8_t tcp_flags)
     if (tcp_flags & TCP_ACK) {
         ds_put_char(s, '.');
     }
-    if (tcp_flags & 0x40) {
-        ds_put_cstr(s, "[40]");
+    if (tcp_flags & TCP_ECE) {
+        ds_put_cstr(s, "E");
     }
-    if (tcp_flags & 0x80) {
-        ds_put_cstr(s, "[80]");
+    if (tcp_flags & TCP_CWR) {
+        ds_put_cstr(s, "C");
+    }
+    if (tcp_flags & TCP_NS) {
+        ds_put_cstr(s, "N");
+    }
+    if (tcp_flags & 0x200) {
+        ds_put_cstr(s, "[200]");
+    }
+    if (tcp_flags & 0x400) {
+        ds_put_cstr(s, "[400]");
+    }
+    if (tcp_flags & 0x800) {
+        ds_put_cstr(s, "[800]");
     }
 }
index 7388152..f4f00ce 100644 (file)
@@ -487,15 +487,18 @@ struct udp_header {
 };
 BUILD_ASSERT_DECL(UDP_HEADER_LEN == sizeof(struct udp_header));
 
-#define TCP_FIN 0x01
-#define TCP_SYN 0x02
-#define TCP_RST 0x04
-#define TCP_PSH 0x08
-#define TCP_ACK 0x10
-#define TCP_URG 0x20
+#define TCP_FIN 0x001
+#define TCP_SYN 0x002
+#define TCP_RST 0x004
+#define TCP_PSH 0x008
+#define TCP_ACK 0x010
+#define TCP_URG 0x020
+#define TCP_ECE 0x040
+#define TCP_CWR 0x080
+#define TCP_NS  0x100
 
 #define TCP_CTL(flags, offset) (htons((flags) | ((offset) << 12)))
-#define TCP_FLAGS(tcp_ctl) (ntohs(tcp_ctl) & 0x003f)
+#define TCP_FLAGS(tcp_ctl) (ntohs(tcp_ctl) & 0x0fff)
 #define TCP_OFFSET(tcp_ctl) (ntohs(tcp_ctl) >> 12)
 
 #define TCP_HEADER_LEN 20
@@ -641,7 +644,7 @@ void packet_set_tcp_port(struct ofpbuf *, ovs_be16 src, ovs_be16 dst);
 void packet_set_udp_port(struct ofpbuf *, ovs_be16 src, ovs_be16 dst);
 void packet_set_sctp_port(struct ofpbuf *, ovs_be16 src, ovs_be16 dst);
 
-uint8_t packet_get_tcp_flags(const struct ofpbuf *, const struct flow *);
-void packet_format_tcp_flags(struct ds *, uint8_t);
+uint16_t packet_get_tcp_flags(const struct ofpbuf *, const struct flow *);
+void packet_format_tcp_flags(struct ds *, uint16_t);
 
 #endif /* packets.h */
index 96b3579..f7f90f7 100644 (file)
@@ -1418,6 +1418,7 @@ is_admitted_msg(const struct ofpbuf *b)
     case OFPTYPE_METER_FEATURES_STATS_REPLY:
     case OFPTYPE_ROLE_REQUEST:
     case OFPTYPE_ROLE_REPLY:
+    case OFPTYPE_ROLE_STATUS:
     case OFPTYPE_SET_FLOW_FORMAT:
     case OFPTYPE_FLOW_MOD_TABLE_ID:
     case OFPTYPE_SET_PACKET_IN_FORMAT:
index 37ed791..c112f9a 100644 (file)
@@ -14,9 +14,7 @@ for bootstrapping.
 .IP
 This option is only useful if the SSL peer sends its CA certificate as
 part of the SSL certificate chain.  The SSL protocol does not require
-the server to send the CA certificate, but
-\fB\*(SN\fR(8) can be configured to do so with the
-\fB\-\-peer\-ca\-cert\fR option.
+the server to send the CA certificate.
 .IP
 This option is mutually exclusive with \fB\-C\fR and
 \fB\-\-ca\-cert\fR.
index b800455..a32c80a 100644 (file)
@@ -399,14 +399,26 @@ get_subprogram_name(void)
     return name ? name : "";
 }
 
-/* Sets 'name' as the name of the currently running thread or process.  (This
- * appears in log messages and may also be visible in system process listings
- * and debuggers.) */
+/* Sets the formatted value of 'format' as the name of the currently running
+ * thread or process.  (This appears in log messages and may also be visible in
+ * system process listings and debuggers.) */
 void
-set_subprogram_name(const char *name)
+set_subprogram_name(const char *format, ...)
 {
-    const char *pname = name[0] ? name : program_name;
-    free(subprogram_name_set(xstrdup(name)));
+    char *pname;
+
+    if (format) {
+        va_list args;
+
+        va_start(args, format);
+        pname = xvasprintf(format, args);
+        va_end(args);
+    } else {
+        pname = xstrdup(program_name);
+    }
+
+    free(subprogram_name_set(pname));
+
 #if HAVE_GLIBC_PTHREAD_SETNAME_NP
     pthread_setname_np(pthread_self(), pname);
 #elif HAVE_NETBSD_PTHREAD_SETNAME_NP
index a899065..32a7c8c 100644 (file)
@@ -222,7 +222,7 @@ void set_program_name__(const char *name, const char *version,
         set_program_name__(name, VERSION, __DATE__, __TIME__)
 
 const char *get_subprogram_name(void);
-void set_subprogram_name(const char *name);
+void set_subprogram_name(const char *format, ...) PRINTF_FORMAT(1, 2);
 
 const char *get_program_version(void);
 void ovs_print_version(uint8_t min_ofp, uint8_t max_ofp);
index 5bfcdaa..7f59019 100644 (file)
@@ -80,24 +80,8 @@ lib/common.man:
 lib/vlog-syn.man:
 lib/vlog.man:
 
-utilities/bugtool/ovs-bugtool.8: \
-       utilities/bugtool/ovs-bugtool.8.in
-utilities/bugtool/ovs-bugtool.8.in:
-
-utilities/ovs-appctl.8: \
-       utilities/ovs-appctl.8.in \
-       lib/common.man
-utilities/ovs-appctl.8.in:
-lib/common.man:
-
-utilities/ovs-benchmark.1: \
-       utilities/ovs-benchmark.1.in \
-       lib/ovs.tmac
-utilities/ovs-benchmark.1.in:
-lib/ovs.tmac:
-
-utilities/ovs-controller.8: \
-       utilities/ovs-controller.8.in \
+tests/test-controller.8: \
+       tests/test-controller.8.in \
        lib/common.man \
        lib/daemon.man \
        lib/ssl-peer-ca-cert.man \
@@ -106,7 +90,7 @@ utilities/ovs-controller.8: \
        lib/vconn-active.man \
        lib/vconn-passive.man \
        lib/vlog.man
-utilities/ovs-controller.8.in:
+tests/test-controller.8.in:
 lib/common.man:
 lib/daemon.man:
 lib/ssl-peer-ca-cert.man:
@@ -116,6 +100,22 @@ lib/vconn-active.man:
 lib/vconn-passive.man:
 lib/vlog.man:
 
+utilities/bugtool/ovs-bugtool.8: \
+       utilities/bugtool/ovs-bugtool.8.in
+utilities/bugtool/ovs-bugtool.8.in:
+
+utilities/ovs-appctl.8: \
+       utilities/ovs-appctl.8.in \
+       lib/common.man
+utilities/ovs-appctl.8.in:
+lib/common.man:
+
+utilities/ovs-benchmark.1: \
+       utilities/ovs-benchmark.1.in \
+       lib/ovs.tmac
+utilities/ovs-benchmark.1.in:
+lib/ovs.tmac:
+
 utilities/ovs-dpctl-top.8: \
        utilities/ovs-dpctl-top.8.in
 utilities/ovs-dpctl-top.8.in:
index c9feae5..da25930 100644 (file)
@@ -912,17 +912,33 @@ ofconn_get_role(const struct ofconn *ofconn)
     return ofconn->role;
 }
 
+void
+ofconn_send_role_status(struct ofconn *ofconn, uint32_t role, uint8_t reason)
+{
+    struct ofputil_role_status status;
+    struct ofpbuf *buf;
+
+    status.reason = reason;
+    status.role = role;
+    ofconn_get_master_election_id(ofconn, &status.generation_id);
+
+    buf = ofputil_encode_role_status(&status, ofconn_get_protocol(ofconn));
+
+    ofconn_send(ofconn, buf, NULL);
+}
+
 /* Changes 'ofconn''s role to 'role'.  If 'role' is OFPCR12_ROLE_MASTER then
  * any existing master is demoted to a slave. */
 void
 ofconn_set_role(struct ofconn *ofconn, enum ofp12_controller_role role)
 {
-    if (role == OFPCR12_ROLE_MASTER) {
+    if (role != ofconn->role && role == OFPCR12_ROLE_MASTER) {
         struct ofconn *other;
 
         HMAP_FOR_EACH (other, hmap_node, &ofconn->connmgr->controllers) {
             if (other->role == OFPCR12_ROLE_MASTER) {
                 other->role = OFPCR12_ROLE_SLAVE;
+                ofconn_send_role_status(other, OFPCR12_ROLE_SLAVE, OFPCRR_MASTER_REQUEST);
             }
         }
     }
index 10caaf4..170d872 100644 (file)
@@ -153,6 +153,8 @@ void connmgr_send_flow_removed(struct connmgr *,
                                const struct ofputil_flow_removed *);
 void connmgr_send_packet_in(struct connmgr *,
                             const struct ofproto_packet_in *);
+void ofconn_send_role_status(struct ofconn *ofconn, uint32_t role,
+                             uint8_t reason);
 
 /* Fail-open settings. */
 enum ofproto_fail_mode connmgr_get_fail_mode(const struct connmgr *);
index 19ca80f..a094bac 100644 (file)
@@ -123,7 +123,7 @@ gen_netflow_rec(struct netflow *nf, struct netflow_flow *nf_flow,
         nf_rec->src_port = expired->flow.tp_src;
         nf_rec->dst_port = expired->flow.tp_dst;
     }
-    nf_rec->tcp_flags = nf_flow->tcp_flags;
+    nf_rec->tcp_flags = (uint8_t)nf_flow->tcp_flags;
     nf_rec->ip_proto = expired->flow.nw_proto;
     nf_rec->ip_tos = expired->flow.nw_tos & IP_DSCP_MASK;
 
@@ -302,7 +302,7 @@ netflow_flow_update_time(struct netflow *nf, struct netflow_flow *nf_flow,
 }
 
 void
-netflow_flow_update_flags(struct netflow_flow *nf_flow, uint8_t tcp_flags)
+netflow_flow_update_flags(struct netflow_flow *nf_flow, uint16_t tcp_flags)
 {
     nf_flow->tcp_flags |= tcp_flags;
 }
index e2366f6..e1a2443 100644 (file)
@@ -50,7 +50,7 @@ struct netflow_flow {
     uint64_t byte_count_off;      /* Byte count at last time out. */
 
     ofp_port_t output_iface;      /* Output interface index. */
-    uint8_t tcp_flags;            /* Bitwise-OR of all TCP flags seen. */
+    uint16_t tcp_flags;           /* Bitwise-OR of all TCP flags seen. */
 };
 
 struct netflow *netflow_create(void);
@@ -68,7 +68,7 @@ void netflow_flow_init(struct netflow_flow *);
 void netflow_flow_clear(struct netflow_flow *);
 void netflow_flow_update_time(struct netflow *, struct netflow_flow *,
                               long long int used);
-void netflow_flow_update_flags(struct netflow_flow *, uint8_t tcp_flags);
+void netflow_flow_update_flags(struct netflow_flow *, uint16_t tcp_flags);
 bool netflow_active_timeout_expired(struct netflow *, struct netflow_flow *);
 
 #endif /* netflow.h */
index 24a5729..dba3d3b 100644 (file)
@@ -394,7 +394,7 @@ udpif_upcall_handler(void *arg)
 {
     struct handler *handler = arg;
 
-    set_subprogram_name("upcall_handler");
+    set_subprogram_name("upcall_%u", ovsthread_id_self());
     for (;;) {
         struct list misses = LIST_INITIALIZER(&misses);
         size_t i;
index 7be691c..367dd88 100644 (file)
@@ -49,6 +49,7 @@
 #include "vlog.h"
 
 COVERAGE_DEFINE(xlate_actions);
+COVERAGE_DEFINE(xlate_actions_oversize);
 
 VLOG_DEFINE_THIS_MODULE(ofproto_dpif_xlate);
 
@@ -126,6 +127,7 @@ struct xport {
     struct xport *peer;              /* Patch port peer or null. */
 
     enum ofputil_port_config config; /* OpenFlow port configuration. */
+    enum ofputil_port_state state;   /* OpenFlow port state. */
     int stp_port_no;                 /* STP port number or -1 if not in use. */
 
     struct hmap skb_priorities;      /* Map of 'skb_priority_to_dscp's. */
@@ -396,7 +398,8 @@ xlate_ofport_set(struct ofproto_dpif *ofproto, struct ofbundle *ofbundle,
                  const struct cfm *cfm, const struct bfd *bfd,
                  struct ofport_dpif *peer, int stp_port_no,
                  const struct ofproto_port_queue *qdscp_list, size_t n_qdscp,
-                 enum ofputil_port_config config, bool is_tunnel,
+                 enum ofputil_port_config config,
+                 enum ofputil_port_state state, bool is_tunnel,
                  bool may_enable)
 {
     struct xport *xport = xport_lookup(ofport);
@@ -417,6 +420,7 @@ xlate_ofport_set(struct ofproto_dpif *ofproto, struct ofbundle *ofbundle,
     ovs_assert(xport->ofp_port == ofp_port);
 
     xport->config = config;
+    xport->state = state;
     xport->stp_port_no = stp_port_no;
     xport->is_tunnel = is_tunnel;
     xport->may_enable = may_enable;
@@ -719,6 +723,106 @@ ofp_port_to_odp_port(const struct xbridge *xbridge, ofp_port_t ofp_port)
     return xport ? xport->odp_port : ODPP_NONE;
 }
 
+static bool
+odp_port_is_alive(const struct xlate_ctx *ctx, ofp_port_t ofp_port)
+{
+    struct xport *xport;
+
+    xport = get_ofp_port(ctx->xbridge, ofp_port);
+    if (!xport || xport->config & OFPUTIL_PC_PORT_DOWN ||
+        xport->state & OFPUTIL_PS_LINK_DOWN) {
+        return false;
+    }
+
+    return true;
+}
+
+static const struct ofputil_bucket *
+group_first_live_bucket(const struct xlate_ctx *, const struct group_dpif *,
+                        int depth);
+
+static bool
+group_is_alive(const struct xlate_ctx *ctx, uint32_t group_id, int depth)
+{
+    struct group_dpif *group;
+    bool hit;
+
+    hit = group_dpif_lookup(ctx->xbridge->ofproto, group_id, &group);
+    if (!hit) {
+        return false;
+    }
+
+    hit = group_first_live_bucket(ctx, group, depth) != NULL;
+
+    group_dpif_release(group);
+    return hit;
+}
+
+#define MAX_LIVENESS_RECURSION 128 /* Arbitrary limit */
+
+static bool
+bucket_is_alive(const struct xlate_ctx *ctx,
+                const struct ofputil_bucket *bucket, int depth)
+{
+    if (depth >= MAX_LIVENESS_RECURSION) {
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+
+        VLOG_WARN_RL(&rl, "bucket chaining exceeded %d links",
+                     MAX_LIVENESS_RECURSION);
+        return false;
+    }
+
+    return !ofputil_bucket_has_liveness(bucket) ||
+           (bucket->watch_port != OFPP_ANY &&
+            odp_port_is_alive(ctx, bucket->watch_port)) ||
+           (bucket->watch_group != OFPG_ANY &&
+            group_is_alive(ctx, bucket->watch_group, depth + 1));
+}
+
+static const struct ofputil_bucket *
+group_first_live_bucket(const struct xlate_ctx *ctx,
+                        const struct group_dpif *group, int depth)
+{
+    struct ofputil_bucket *bucket;
+    const struct list *buckets;
+
+    group_dpif_get_buckets(group, &buckets);
+    LIST_FOR_EACH (bucket, list_node, buckets) {
+        if (bucket_is_alive(ctx, bucket, depth)) {
+            return bucket;
+        }
+    }
+
+    return NULL;
+}
+
+static const struct ofputil_bucket *
+group_best_live_bucket(const struct xlate_ctx *ctx,
+                       const struct group_dpif *group,
+                       uint32_t basis)
+{
+    const struct ofputil_bucket *best_bucket = NULL;
+    uint32_t best_score = 0;
+    int i = 0;
+
+    const struct ofputil_bucket *bucket;
+    const struct list *buckets;
+
+    group_dpif_get_buckets(group, &buckets);
+    LIST_FOR_EACH (bucket, list_node, buckets) {
+        if (bucket_is_alive(ctx, bucket, 0)) {
+            uint32_t score = (hash_int(i, basis) & 0xffff) * bucket->weight;
+            if (score >= best_score) {
+                best_bucket = bucket;
+                best_score = score;
+            }
+        }
+        i++;
+    }
+
+    return best_bucket;
+}
+
 static bool
 xbundle_trunks_vlan(const struct xbundle *bundle, uint16_t vlan)
 {
@@ -1559,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 == 21);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 22);
 
     if (!xport) {
         xlate_report(ctx, "Nonexistent output port");
@@ -1714,9 +1818,8 @@ xlate_recursively(struct xlate_ctx *ctx, struct rule_dpif *rule)
     ctx->recurse--;
 }
 
-static void
-xlate_table_action(struct xlate_ctx *ctx,
-                   ofp_port_t in_port, uint8_t table_id, bool may_packet_in)
+static bool
+xlate_resubmit_resource_check(struct xlate_ctx *ctx)
 {
     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
 
@@ -1730,6 +1833,17 @@ xlate_table_action(struct xlate_ctx *ctx,
     } else if (ctx->stack.size >= 65536) {
         VLOG_ERR_RL(&rl, "resubmits yielded over 64 kB of stack");
     } else {
+        return true;
+    }
+
+    return false;
+}
+
+static void
+xlate_table_action(struct xlate_ctx *ctx,
+                   ofp_port_t in_port, uint8_t table_id, bool may_packet_in)
+{
+    if (xlate_resubmit_resource_check(ctx)) {
         struct rule_dpif *rule;
         ofp_port_t old_in_port = ctx->xin->flow.in_port.ofp_port;
         uint8_t old_table_id = ctx->table_id;
@@ -1775,6 +1889,109 @@ xlate_table_action(struct xlate_ctx *ctx,
     ctx->exit = true;
 }
 
+static void
+xlate_group_bucket(struct xlate_ctx *ctx, const struct ofputil_bucket *bucket)
+{
+    uint64_t action_list_stub[1024 / 8];
+    struct ofpbuf action_list, action_set;
+
+    ofpbuf_use_const(&action_set, bucket->ofpacts, bucket->ofpacts_len);
+    ofpbuf_use_stub(&action_list, action_list_stub, sizeof action_list_stub);
+
+    ofpacts_execute_action_set(&action_list, &action_set);
+    ctx->recurse++;
+    do_xlate_actions(action_list.data, action_list.size, ctx);
+    ctx->recurse--;
+
+    ofpbuf_uninit(&action_set);
+    ofpbuf_uninit(&action_list);
+}
+
+static void
+xlate_all_group(struct xlate_ctx *ctx, struct group_dpif *group)
+{
+    const struct ofputil_bucket *bucket;
+    const struct list *buckets;
+    struct flow old_flow = ctx->xin->flow;
+
+    group_dpif_get_buckets(group, &buckets);
+
+    LIST_FOR_EACH (bucket, list_node, buckets) {
+        xlate_group_bucket(ctx, bucket);
+        /* Roll back flow to previous state.
+         * This is equivalent to cloning the packet for each bucket.
+         *
+         * As a side effect any subsequently applied actions will
+         * also effectively be applied to a clone of the packet taken
+         * just before applying the all or indirect group. */
+        ctx->xin->flow = old_flow;
+    }
+}
+
+static void
+xlate_ff_group(struct xlate_ctx *ctx, struct group_dpif *group)
+{
+    const struct ofputil_bucket *bucket;
+
+    bucket = group_first_live_bucket(ctx, group, 0);
+    if (bucket) {
+        xlate_group_bucket(ctx, bucket);
+    }
+}
+
+static void
+xlate_select_group(struct xlate_ctx *ctx, struct group_dpif *group)
+{
+    struct flow_wildcards *wc = &ctx->xout->wc;
+    const struct ofputil_bucket *bucket;
+    uint32_t basis;
+
+    basis = hash_bytes(ctx->xin->flow.dl_dst, sizeof ctx->xin->flow.dl_dst, 0);
+    bucket = group_best_live_bucket(ctx, group, basis);
+    if (bucket) {
+        memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst);
+        xlate_group_bucket(ctx, bucket);
+    }
+}
+
+static void
+xlate_group_action__(struct xlate_ctx *ctx, struct group_dpif *group)
+{
+    switch (group_dpif_get_type(group)) {
+    case OFPGT11_ALL:
+    case OFPGT11_INDIRECT:
+        xlate_all_group(ctx, group);
+        break;
+    case OFPGT11_SELECT:
+        xlate_select_group(ctx, group);
+        break;
+    case OFPGT11_FF:
+        xlate_ff_group(ctx, group);
+        break;
+    default:
+        NOT_REACHED();
+    }
+    group_dpif_release(group);
+}
+
+static bool
+xlate_group_action(struct xlate_ctx *ctx, uint32_t group_id)
+{
+    if (xlate_resubmit_resource_check(ctx)) {
+        struct group_dpif *group;
+        bool got_group;
+
+        got_group = group_dpif_lookup(ctx->xbridge->ofproto, group_id, &group);
+        if (got_group) {
+            xlate_group_action__(ctx, group);
+        } else {
+            return true;
+        }
+    }
+
+    return false;
+}
+
 static void
 xlate_ofpact_resubmit(struct xlate_ctx *ctx,
                       const struct ofpact_resubmit *resubmit)
@@ -1983,6 +2200,54 @@ compose_dec_ttl(struct xlate_ctx *ctx, struct ofpact_cnt_ids *ids)
     }
 }
 
+static bool
+compose_set_mpls_label_action(struct xlate_ctx *ctx, ovs_be32 label)
+{
+    if (!eth_type_mpls(ctx->xin->flow.dl_type)) {
+        return true;
+    }
+
+    /* If mpls_depth_delta is negative then an MPLS POP action has been
+     * executed and the resulting MPLS label stack is unknown.  This means
+     * a SET MPLS LABEL action can't be executed as it needs to manipulate
+     * the top-most MPLS LSE. Thus, stop processing.
+     *
+     * It is planned that in the future this case will be handled
+     * by recirculation.
+     */
+    if (ctx->mpls_depth_delta < 0) {
+        return true;
+    }
+
+    ctx->xout->wc.masks.mpls_lse |= htonl(MPLS_LABEL_MASK);
+    set_mpls_lse_label(&ctx->xin->flow.mpls_lse, label);
+    return false;
+}
+
+static bool
+compose_set_mpls_tc_action(struct xlate_ctx *ctx, uint8_t tc)
+{
+    if (!eth_type_mpls(ctx->xin->flow.dl_type)) {
+        return true;
+    }
+
+    /* If mpls_depth_delta is negative then an MPLS POP action has been
+     * executed and the resulting MPLS label stack is unknown.  This means
+     * a SET MPLS TC action can't be executed as it needs to manipulate
+     * the top-most MPLS LSE. Thus, stop processing.
+     *
+     * It is planned that in the future this case will be handled
+     * by recirculation.
+     */
+    if (ctx->mpls_depth_delta < 0) {
+        return true;
+    }
+
+    ctx->xout->wc.masks.mpls_lse |= htonl(MPLS_TC_MASK);
+    set_mpls_lse_tc(&ctx->xin->flow.mpls_lse, tc);
+    return false;
+}
+
 static bool
 compose_set_mpls_ttl_action(struct xlate_ctx *ctx, uint8_t ttl)
 {
@@ -2292,6 +2557,8 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
     OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
         struct ofpact_controller *controller;
         const struct ofpact_metadata *metadata;
+        const struct ofpact_set_field *set_field;
+        const struct mf_field *mf;
 
         if (ctx->exit) {
             break;
@@ -2304,7 +2571,9 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             break;
 
         case OFPACT_GROUP:
-            /* XXX not yet implemented */
+            if (xlate_group_action(ctx, ofpact_get_GROUP(a)->group_id)) {
+                return;
+            }
             break;
 
         case OFPACT_CONTROLLER:
@@ -2320,17 +2589,22 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
 
         case OFPACT_SET_VLAN_VID:
             wc->masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI);
-            flow->vlan_tci &= ~htons(VLAN_VID_MASK);
-            flow->vlan_tci |= (htons(ofpact_get_SET_VLAN_VID(a)->vlan_vid)
-                               | htons(VLAN_CFI));
+            if (flow->vlan_tci & htons(VLAN_CFI) ||
+                ofpact_get_SET_VLAN_VID(a)->push_vlan_if_needed) {
+                flow->vlan_tci &= ~htons(VLAN_VID_MASK);
+                flow->vlan_tci |= (htons(ofpact_get_SET_VLAN_VID(a)->vlan_vid)
+                                   | htons(VLAN_CFI));
+            }
             break;
 
         case OFPACT_SET_VLAN_PCP:
             wc->masks.vlan_tci |= htons(VLAN_PCP_MASK | VLAN_CFI);
-            flow->vlan_tci &= ~htons(VLAN_PCP_MASK);
-            flow->vlan_tci |=
-                htons((ofpact_get_SET_VLAN_PCP(a)->vlan_pcp << VLAN_PCP_SHIFT)
-                      | VLAN_CFI);
+            if (flow->vlan_tci & htons(VLAN_CFI) ||
+                ofpact_get_SET_VLAN_PCP(a)->push_vlan_if_needed) {
+                flow->vlan_tci &= ~htons(VLAN_PCP_MASK);
+                flow->vlan_tci |= htons((ofpact_get_SET_VLAN_PCP(a)->vlan_pcp
+                                         << VLAN_PCP_SHIFT) | VLAN_CFI);
+            }
             break;
 
         case OFPACT_STRIP_VLAN:
@@ -2431,6 +2705,20 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             nxm_execute_reg_load(ofpact_get_REG_LOAD(a), flow, wc);
             break;
 
+        case OFPACT_SET_FIELD:
+            set_field = ofpact_get_SET_FIELD(a);
+            mf = set_field->field;
+            mf_mask_field_and_prereqs(mf, &wc->masks);
+
+            /* Set field action only ever overwrites packet's outermost
+             * applicable header fields.  Do nothing if no header exists. */
+            if ((mf->id != MFF_VLAN_VID || flow->vlan_tci & htons(VLAN_CFI))
+                && ((mf->id != MFF_MPLS_LABEL && mf->id != MFF_MPLS_TC)
+                    || flow->mpls_lse)) {
+                mf_set_flow_value(mf, &set_field->value, flow);
+            }
+            break;
+
         case OFPACT_STACK_PUSH:
             nxm_execute_stack_push(ofpact_get_STACK_PUSH(a), flow, wc,
                                    &ctx->stack);
@@ -2455,6 +2743,20 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             }
             break;
 
+        case OFPACT_SET_MPLS_LABEL:
+            if (compose_set_mpls_label_action(ctx,
+                                              ofpact_get_SET_MPLS_LABEL(a)->label)) {
+                return;
+            }
+            break;
+
+        case OFPACT_SET_MPLS_TC:
+            if (compose_set_mpls_tc_action(ctx,
+                                           ofpact_get_SET_MPLS_TC(a)->tc)) {
+                return;
+            }
+            break;
+
         case OFPACT_SET_MPLS_TTL:
             if (compose_set_mpls_ttl_action(ctx,
                                             ofpact_get_SET_MPLS_TTL(a)->ttl)) {
@@ -2524,7 +2826,6 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             break;
 
         case OFPACT_GOTO_TABLE: {
-            /* It is assumed that goto-table is the last action. */
             struct ofpact_goto_table *ogt = ofpact_get_GOTO_TABLE(a);
 
             ovs_assert(ctx->table_id < ogt->table_id);
@@ -2543,7 +2844,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
 void
 xlate_in_init(struct xlate_in *xin, struct ofproto_dpif *ofproto,
               const struct flow *flow, struct rule_dpif *rule,
-              uint8_t tcp_flags, const struct ofpbuf *packet)
+              uint16_t tcp_flags, const struct ofpbuf *packet)
 {
     xin->ofproto = ofproto;
     xin->flow = *flow;
@@ -2887,11 +3188,11 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout)
 
     if (nl_attr_oversized(ctx.xout->odp_actions.size)) {
         /* These datapath actions are too big for a Netlink attribute, so we
-         * can't execute them. */
-        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
-
-        VLOG_ERR_RL(&rl, "discarding oversize datapath actions");
-        ofpbuf_clear(&ctx.xout->odp_actions);
+         * can't hand them to the kernel directly.  dpif_execute() can execute
+         * them one by one with help, so just mark the result as SLOW_ACTION to
+         * prevent the flow from being installed. */
+        COVERAGE_INC(xlate_actions_oversize);
+        ctx.xout->slow |= SLOW_ACTION;
     }
 
     ofpbuf_uninit(&ctx.stack);
index 40712f9..7dd3534 100644 (file)
@@ -84,7 +84,7 @@ struct xlate_in {
     /* Union of the set of TCP flags seen so far in this flow.  (Used only by
      * NXAST_FIN_TIMEOUT.  Set to zero to avoid updating updating rules'
      * timeouts.) */
-    uint8_t tcp_flags;
+    uint16_t tcp_flags;
 
     /* If nonnull, flow translation calls this function just before executing a
      * resubmit or OFPP_TABLE action.  In addition, disables logging of traces
@@ -138,7 +138,8 @@ void xlate_ofport_set(struct ofproto_dpif *, struct ofbundle *,
                       const struct netdev *, const struct cfm *,
                       const struct bfd *, struct ofport_dpif *peer,
                       int stp_port_no, const struct ofproto_port_queue *qdscp,
-                      size_t n_qdscp, enum ofputil_port_config, bool is_tunnel,
+                      size_t n_qdscp, enum ofputil_port_config,
+                      enum ofputil_port_state, bool is_tunnel,
                       bool may_enable) OVS_REQ_WRLOCK(xlate_rwlock);
 void xlate_ofport_remove(struct ofport_dpif *) OVS_REQ_WRLOCK(xlate_rwlock);
 
@@ -152,7 +153,7 @@ void xlate_actions(struct xlate_in *, struct xlate_out *)
     OVS_EXCLUDED(xlate_rwlock);
 void xlate_in_init(struct xlate_in *, struct ofproto_dpif *,
                    const struct flow *, struct rule_dpif *,
-                   uint8_t tcp_flags, const struct ofpbuf *packet);
+                   uint16_t tcp_flags, const struct ofpbuf *packet);
 void xlate_out_uninit(struct xlate_out *);
 void xlate_actions_for_side_effects(struct xlate_in *);
 void xlate_out_copy(struct xlate_out *dst, const struct xlate_out *src);
index be7f807..9756480 100644 (file)
@@ -110,6 +110,28 @@ struct rule_dpif {
 static void rule_get_stats(struct rule *, uint64_t *packets, uint64_t *bytes);
 static struct rule_dpif *rule_dpif_cast(const struct rule *);
 
+struct group_dpif {
+    struct ofgroup up;
+
+    /* These statistics:
+     *
+     *   - Do include packets and bytes from facets that have been deleted or
+     *     whose own statistics have been folded into the rule.
+     *
+     *   - Do include packets and bytes sent "by hand" that were accounted to
+     *     the rule without any facet being involved (this is a rare corner
+     *     case in rule_execute()).
+     *
+     *   - Do not include packet or bytes that can be obtained from any facet's
+     *     packet_count or byte_count member or that can be obtained from the
+     *     datapath by, e.g., dpif_flow_get() for any subfacet.
+     */
+    struct ovs_mutex stats_mutex;
+    uint64_t packet_count OVS_GUARDED;  /* Number of packets received. */
+    uint64_t byte_count OVS_GUARDED;    /* Number of bytes received. */
+    struct bucket_counter *bucket_stats OVS_GUARDED;  /* Bucket statistics. */
+};
+
 struct ofbundle {
     struct hmap_node hmap_node; /* In struct ofproto's "bundles" hmap. */
     struct ofproto_dpif *ofproto; /* Owning ofproto. */
@@ -263,7 +285,7 @@ struct facet {
     /* Accounting. */
     uint64_t accounted_bytes;    /* Bytes processed by facet_account(). */
     struct netflow_flow nf_flow; /* Per-flow NetFlow tracking data. */
-    uint8_t tcp_flags;           /* TCP flags seen for this 'rule'. */
+    uint16_t tcp_flags;          /* TCP flags seen for this 'rule'. */
 
     struct xlate_out xout;
 
@@ -802,8 +824,8 @@ type_run(const char *type)
                                  ofport->up.netdev, ofport->cfm,
                                  ofport->bfd, ofport->peer, stp_port,
                                  ofport->qdscp, ofport->n_qdscp,
-                                 ofport->up.pp.config, ofport->is_tunnel,
-                                 ofport->may_enable);
+                                 ofport->up.pp.config, ofport->up.pp.state,
+                                 ofport->is_tunnel, ofport->may_enable);
             }
             ovs_rwlock_unlock(&xlate_rwlock);
 
@@ -1505,14 +1527,14 @@ run(struct ofproto *ofproto_)
     if (time_msec() >= ofproto->consistency_rl
         && !classifier_is_empty(&ofproto->facets)
         && !ofproto->backer->need_revalidate) {
-        struct cls_table *table;
+        struct cls_subtable *table;
         struct cls_rule *cr;
         struct facet *facet;
 
         ofproto->consistency_rl = time_msec() + 250;
 
-        table = CONTAINER_OF(hmap_random_node(&ofproto->facets.tables),
-                             struct cls_table, hmap_node);
+        table = CONTAINER_OF(hmap_random_node(&ofproto->facets.subtables),
+                             struct cls_subtable, hmap_node);
         cr = CONTAINER_OF(hmap_random_node(&table->rules), struct cls_rule,
                           hmap_node);
         facet = CONTAINER_OF(cr, struct facet, cr);
@@ -1931,6 +1953,7 @@ get_cfm_status(const struct ofport *ofport_,
 
     if (ofport->cfm) {
         status->faults = cfm_get_fault(ofport->cfm);
+        status->flap_count = cfm_get_flap_count(ofport->cfm);
         status->remote_opstate = cfm_get_opup(ofport->cfm);
         status->health = cfm_get_health(ofport->cfm);
         cfm_get_remote_mpids(ofport->cfm, &status->rmps, &status->n_rmps);
@@ -4789,6 +4812,137 @@ rule_modify_actions(struct rule *rule_, bool reset_counters)
 
     complete_operation(rule);
 }
+
+static struct group_dpif *group_dpif_cast(const struct ofgroup *group)
+{
+    return group ? CONTAINER_OF(group, struct group_dpif, up) : NULL;
+}
+
+static struct ofgroup *
+group_alloc(void)
+{
+    struct group_dpif *group = xzalloc(sizeof *group);
+    return &group->up;
+}
+
+static void
+group_dealloc(struct ofgroup *group_)
+{
+    struct group_dpif *group = group_dpif_cast(group_);
+    free(group);
+}
+
+static void
+group_construct_stats(struct group_dpif *group)
+    OVS_REQUIRES(group->stats_mutex)
+{
+    group->packet_count = 0;
+    group->byte_count = 0;
+    if (!group->bucket_stats) {
+        group->bucket_stats = xcalloc(group->up.n_buckets,
+                                      sizeof *group->bucket_stats);
+    } else {
+        memset(group->bucket_stats, 0, group->up.n_buckets *
+               sizeof *group->bucket_stats);
+    }
+}
+
+static enum ofperr
+group_construct(struct ofgroup *group_)
+{
+    struct group_dpif *group = group_dpif_cast(group_);
+    ovs_mutex_init(&group->stats_mutex);
+    ovs_mutex_lock(&group->stats_mutex);
+    group_construct_stats(group);
+    ovs_mutex_unlock(&group->stats_mutex);
+    return 0;
+}
+
+static void
+group_destruct__(struct group_dpif *group)
+    OVS_REQUIRES(group->stats_mutex)
+{
+    free(group->bucket_stats);
+    group->bucket_stats = NULL;
+}
+
+static void
+group_destruct(struct ofgroup *group_)
+{
+    struct group_dpif *group = group_dpif_cast(group_);
+    ovs_mutex_lock(&group->stats_mutex);
+    group_destruct__(group);
+    ovs_mutex_unlock(&group->stats_mutex);
+    ovs_mutex_destroy(&group->stats_mutex);
+}
+
+static enum ofperr
+group_modify(struct ofgroup *group_, struct ofgroup *victim_)
+{
+    struct group_dpif *group = group_dpif_cast(group_);
+    struct group_dpif *victim = group_dpif_cast(victim_);
+
+    ovs_mutex_lock(&group->stats_mutex);
+    if (victim->up.n_buckets < group->up.n_buckets) {
+        group_destruct__(group);
+    }
+    group_construct_stats(group);
+    ovs_mutex_unlock(&group->stats_mutex);
+
+    return 0;
+}
+
+static enum ofperr
+group_get_stats(const struct ofgroup *group_, struct ofputil_group_stats *ogs)
+{
+    struct group_dpif *group = group_dpif_cast(group_);
+
+    /* Start from historical data for 'group' itself that are no longer tracked
+     * in facets.  This counts, for example, facets that have expired. */
+    ovs_mutex_lock(&group->stats_mutex);
+    ogs->packet_count = group->packet_count;
+    ogs->byte_count = group->byte_count;
+    memcpy(ogs->bucket_stats, group->bucket_stats,
+           group->up.n_buckets * sizeof *group->bucket_stats);
+    ovs_mutex_unlock(&group->stats_mutex);
+
+    return 0;
+}
+
+bool
+group_dpif_lookup(struct ofproto_dpif *ofproto, uint32_t group_id,
+                  struct group_dpif **group)
+    OVS_TRY_RDLOCK(true, (*group)->up.rwlock)
+{
+    struct ofgroup *ofgroup;
+    bool found;
+
+    *group = NULL;
+    found = ofproto_group_lookup(&ofproto->up, group_id, &ofgroup);
+    *group = found ?  group_dpif_cast(ofgroup) : NULL;
+
+    return found;
+}
+
+void
+group_dpif_release(struct group_dpif *group)
+    OVS_RELEASES(group->up.rwlock)
+{
+    ofproto_group_release(&group->up);
+}
+
+void
+group_dpif_get_buckets(const struct group_dpif *group,
+                       const struct list **buckets)
+{
+    *buckets = &group->up.buckets;
+}
+
+enum ofp11_group_type
+group_dpif_get_type(const struct group_dpif *group)
+{
+    return group->up.type;
+}
 \f
 /* Sends 'packet' out 'ofport'.
  * May modify 'packet'.
@@ -5282,7 +5436,7 @@ ofproto_trace(struct ofproto_dpif *ofproto, const struct flow *flow,
         struct ofpbuf odp_actions;
         struct trace_ctx trace;
         struct match match;
-        uint8_t tcp_flags;
+        uint16_t tcp_flags;
 
         tcp_flags = packet ? packet_get_tcp_flags(packet, flow) : 0;
         trace.result = ds;
@@ -6076,10 +6230,10 @@ const struct ofproto_class ofproto_dpif_class = {
     NULL,                       /* meter_set */
     NULL,                       /* meter_get */
     NULL,                       /* meter_del */
-    NULL,                       /* group_alloc */
-    NULL,                       /* group_construct */
-    NULL,                       /* group_destruct */
-    NULL,                       /* group_dealloc */
-    NULL,                       /* group_modify */
-    NULL,                       /* group_get_stats */
+    group_alloc,                /* group_alloc */
+    group_construct,            /* group_construct */
+    group_destruct,             /* group_destruct */
+    group_dealloc,              /* group_dealloc */
+    group_modify,               /* group_modify */
+    group_get_stats,            /* group_get_stats */
 };
index c93d37c..51cb38f 100644 (file)
@@ -32,6 +32,7 @@ struct ofproto_packet_in;
 struct ofport_dpif;
 struct dpif_backer;
 struct OVS_LOCKABLE rule_dpif;
+struct OVS_LOCKABLE group_dpif;
 
 /* Ofproto-dpif -- DPIF based ofproto implementation.
  *
@@ -89,6 +90,15 @@ void choose_miss_rule(enum ofputil_port_config,
                       struct rule_dpif *no_packet_in_rule,
                       struct rule_dpif **rule);
 
+bool group_dpif_lookup(struct ofproto_dpif *ofproto, uint32_t group_id,
+                       struct group_dpif **group);
+
+void group_dpif_release(struct group_dpif *group);
+
+void group_dpif_get_buckets(const struct group_dpif *group,
+                            const struct list **buckets);
+enum ofp11_group_type group_dpif_get_type(const struct group_dpif *group);
+
 bool ofproto_has_vlan_splinters(const struct ofproto_dpif *);
 ofp_port_t vsp_realdev_to_vlandev(const struct ofproto_dpif *,
                                   ofp_port_t realdev_ofp_port,
index 07bb266..2844e4c 100644 (file)
@@ -306,7 +306,7 @@ struct oftable {
  *
  * 'rule->ref_count' protects 'rule' from being freed.  It doesn't protect the
  * rule from being deleted from 'cls' (that's 'cls->rwlock') and it doesn't
- * protect members of 'rule' from modification (that's 'rule->rwlock').
+ * protect members of 'rule' from modification (that's 'rule->mutex').
  *
  * 'rule->mutex' protects the members of 'rule' from modification.  It doesn't
  * protect the rule from being deleted from 'cls' (that's 'cls->rwlock') and it
index 8fa5e84..0c66a59 100644 (file)
@@ -49,6 +49,7 @@
 #include "random.h"
 #include "shash.h"
 #include "simap.h"
+#include "smap.h"
 #include "sset.h"
 #include "timeval.h"
 #include "unaligned.h"
@@ -270,6 +271,12 @@ static enum ofperr modify_flows__(struct ofproto *, struct ofconn *,
 static void delete_flow__(struct rule *rule, struct ofopgroup *,
                           enum ofp_flow_removed_reason)
     OVS_REQUIRES(ofproto_mutex);
+static bool ofproto_group_exists__(const struct ofproto *ofproto,
+                                   uint32_t group_id)
+    OVS_REQ_RDLOCK(ofproto->groups_rwlock);
+static bool ofproto_group_exists(const struct ofproto *ofproto,
+                                 uint32_t group_id)
+    OVS_EXCLUDED(ofproto->groups_rwlock);
 static enum ofperr add_group(struct ofproto *, struct ofputil_group_mod *);
 static bool handle_openflow(struct ofconn *, const struct ofpbuf *);
 static enum ofperr handle_flow_mod__(struct ofproto *, struct ofconn *,
@@ -524,6 +531,30 @@ ofproto_create(const char *datapath_name, const char *datapath_type,
     ovs_rwlock_init(&ofproto->groups_rwlock);
     hmap_init(&ofproto->groups);
     ovs_mutex_unlock(&ofproto_mutex);
+    ofproto->ogf.capabilities = OFPGFC_CHAINING | OFPGFC_SELECT_LIVENESS |
+                                OFPGFC_SELECT_WEIGHT;
+    ofproto->ogf.max_groups[OFPGT11_ALL] = OFPG_MAX;
+    ofproto->ogf.max_groups[OFPGT11_SELECT] = OFPG_MAX;
+    ofproto->ogf.max_groups[OFPGT11_INDIRECT] = OFPG_MAX;
+    ofproto->ogf.max_groups[OFPGT11_FF] = OFPG_MAX;
+    ofproto->ogf.actions[0] =
+        (1 << OFPAT11_OUTPUT) |
+        (1 << OFPAT11_COPY_TTL_OUT) |
+        (1 << OFPAT11_COPY_TTL_IN) |
+        (1 << OFPAT11_SET_MPLS_TTL) |
+        (1 << OFPAT11_DEC_MPLS_TTL) |
+        (1 << OFPAT11_PUSH_VLAN) |
+        (1 << OFPAT11_POP_VLAN) |
+        (1 << OFPAT11_PUSH_MPLS) |
+        (1 << OFPAT11_POP_MPLS) |
+        (1 << OFPAT11_SET_QUEUE) |
+        (1 << OFPAT11_GROUP) |
+        (1 << OFPAT11_SET_NW_TTL) |
+        (1 << OFPAT11_DEC_NW_TTL) |
+        (1 << OFPAT12_SET_FIELD);
+/* not supported:
+ *      (1 << OFPAT13_PUSH_PBB) |
+ *      (1 << OFPAT13_POP_PBB) */
 
     error = ofproto->ofproto_class->construct(ofproto);
     if (error) {
@@ -1798,6 +1829,30 @@ ofproto_port_del(struct ofproto *ofproto, ofp_port_t ofp_port)
     return error;
 }
 
+static void
+flow_mod_init(struct ofputil_flow_mod *fm,
+              const struct match *match, unsigned int priority,
+              const struct ofpact *ofpacts, size_t ofpacts_len,
+              enum ofp_flow_mod_command command)
+{
+    memset(fm, 0, sizeof *fm);
+    fm->match = *match;
+    fm->priority = priority;
+    fm->cookie = 0;
+    fm->new_cookie = 0;
+    fm->modify_cookie = false;
+    fm->table_id = 0;
+    fm->command = command;
+    fm->idle_timeout = 0;
+    fm->hard_timeout = 0;
+    fm->buffer_id = UINT32_MAX;
+    fm->out_port = OFPP_ANY;
+    fm->out_group = OFPG_ANY;
+    fm->flags = 0;
+    fm->ofpacts = CONST_CAST(struct ofpact *, ofpacts);
+    fm->ofpacts_len = ofpacts_len;
+}
+
 static int
 simple_flow_mod(struct ofproto *ofproto,
                 const struct match *match, unsigned int priority,
@@ -1806,22 +1861,7 @@ simple_flow_mod(struct ofproto *ofproto,
 {
     struct ofputil_flow_mod fm;
 
-    memset(&fm, 0, sizeof fm);
-    fm.match = *match;
-    fm.priority = priority;
-    fm.cookie = 0;
-    fm.new_cookie = 0;
-    fm.modify_cookie = false;
-    fm.table_id = 0;
-    fm.command = command;
-    fm.idle_timeout = 0;
-    fm.hard_timeout = 0;
-    fm.buffer_id = UINT32_MAX;
-    fm.out_port = OFPP_ANY;
-    fm.out_group = OFPG_ANY;
-    fm.flags = 0;
-    fm.ofpacts = CONST_CAST(struct ofpact *, ofpacts);
-    fm.ofpacts_len = ofpacts_len;
+    flow_mod_init(&fm, match, priority, ofpacts, ofpacts_len, command);
 
     return handle_flow_mod__(ofproto, NULL, &fm, NULL);
 }
@@ -2816,30 +2856,33 @@ reject_slave_controller(struct ofconn *ofconn)
     }
 }
 
-/* Checks that the 'ofpacts_len' bytes of actions in 'ofpacts' are appropriate
- * for a packet with the prerequisites satisfied by 'flow' in table 'table_id'.
- * 'flow' may be temporarily modified, but is restored at return.
- */
+/* Checks that the 'ofpacts_len' bytes of action in 'ofpacts' are appropriate
+ * for 'ofproto':
+ *
+ *    - If they use a meter, then 'ofproto' has that meter configured.
+ *
+ *    - If they use any groups, then 'ofproto' has that group configured.
+ *
+ * Returns 0 if successful, otherwise an OpenFlow error. */
 static enum ofperr
 ofproto_check_ofpacts(struct ofproto *ofproto,
-                      const struct ofpact ofpacts[], size_t ofpacts_len,
-                      struct flow *flow, uint8_t table_id,
-                      bool enforce_consistency)
+                      const struct ofpact ofpacts[], size_t ofpacts_len)
 {
-    enum ofperr error;
+    const struct ofpact *a;
     uint32_t mid;
 
-    error = ofpacts_check(ofpacts, ofpacts_len, flow,
-                          u16_to_ofp(ofproto->max_ports), table_id,
-                          enforce_consistency);
-    if (error) {
-        return error;
-    }
-
     mid = ofpacts_get_meter(ofpacts, ofpacts_len);
     if (mid && get_provider_meter_id(ofproto, mid) == UINT32_MAX) {
         return OFPERR_OFPMMFC_INVALID_METER;
     }
+
+    OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
+        if (a->type == OFPACT_GROUP
+            && !ofproto_group_exists(ofproto, ofpact_get_GROUP(a)->group_id)) {
+            return OFPERR_OFPBAC_BAD_OUT_GROUP;
+        }
+    }
+
     return 0;
 }
 
@@ -2874,7 +2917,6 @@ handle_packet_out(struct ofconn *ofconn, const struct ofp_header *oh)
         goto exit_free_ofpacts;
     }
 
-
     /* Get payload. */
     if (po.buffer_id != UINT32_MAX) {
         error = ofconn_pktbuf_retrieve(ofconn, po.buffer_id, &payload, NULL);
@@ -2889,8 +2931,7 @@ handle_packet_out(struct ofconn *ofconn, const struct ofp_header *oh)
     /* Verify actions against packet, then send packet if successful. */
     in_port_.ofp_port = po.in_port;
     flow_extract(payload, 0, 0, NULL, &in_port_, &flow);
-    error = ofproto_check_ofpacts(p, po.ofpacts, po.ofpacts_len, &flow, 0,
-                                  oh->version > OFP10_VERSION);
+    error = ofproto_check_ofpacts(p, po.ofpacts, po.ofpacts_len);
     if (!error) {
         error = p->ofproto_class->packet_out(p, payload, &flow,
                                              po.ofpacts, po.ofpacts_len);
@@ -3938,15 +3979,6 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
         }
     }
 
-    /* Verify actions. */
-    error = ofproto_check_ofpacts(ofproto, fm->ofpacts, fm->ofpacts_len,
-                                  &fm->match.flow, table_id,
-                                  request && request->version > OFP10_VERSION);
-    if (error) {
-        cls_rule_destroy(&cr);
-        return error;
-    }
-
     /* Serialize against pending deletion. */
     if (is_flow_deletion_pending(ofproto, &cr, table_id)) {
         cls_rule_destroy(&cr);
@@ -4063,14 +4095,6 @@ modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
             continue;
         }
 
-        /* Verify actions, enforce consistency check on OF1.1+. */
-        error = ofpacts_check(fm->ofpacts, fm->ofpacts_len, &fm->match.flow,
-                              u16_to_ofp(ofproto->max_ports), rule->table_id,
-                              request && request->version > OFP10_VERSION);
-        if (error) {
-            return error;
-        }
-
         actions_changed = !ofpacts_equal(fm->ofpacts, fm->ofpacts_len,
                                          rule->actions->ofpacts,
                                          rule->actions->ofpacts_len);
@@ -4385,7 +4409,12 @@ handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh)
 
     ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
     error = ofputil_decode_flow_mod(&fm, oh, ofconn_get_protocol(ofconn),
-                                    &ofpacts);
+                                    &ofpacts,
+                                    u16_to_ofp(ofproto->max_ports),
+                                    ofproto->n_tables);
+    if (!error) {
+        error = ofproto_check_ofpacts(ofproto, fm.ofpacts, fm.ofpacts_len);
+    }
     if (!error) {
         error = handle_flow_mod__(ofproto, ofconn, &fm, oh);
     }
@@ -5289,7 +5318,7 @@ ofproto_group_write_lookup(const struct ofproto *ofproto, uint32_t group_id,
 }
 
 static bool
-ofproto_group_exists(const struct ofproto *ofproto, uint32_t group_id)
+ofproto_group_exists__(const struct ofproto *ofproto, uint32_t group_id)
     OVS_REQ_RDLOCK(ofproto->groups_rwlock)
 {
     struct ofgroup *grp;
@@ -5303,6 +5332,44 @@ ofproto_group_exists(const struct ofproto *ofproto, uint32_t group_id)
     return false;
 }
 
+static bool
+ofproto_group_exists(const struct ofproto *ofproto, uint32_t group_id)
+    OVS_EXCLUDED(ofproto->groups_rwlock)
+{
+    bool exists;
+
+    ovs_rwlock_rdlock(&ofproto->groups_rwlock);
+    exists = ofproto_group_exists__(ofproto, group_id);
+    ovs_rwlock_unlock(&ofproto->groups_rwlock);
+
+    return exists;
+}
+
+static uint32_t
+group_get_ref_count(struct ofgroup *group)
+    OVS_EXCLUDED(ofproto_mutex)
+{
+    struct ofproto *ofproto = group->ofproto;
+    struct rule_criteria criteria;
+    struct rule_collection rules;
+    struct match match;
+    enum ofperr error;
+    uint32_t count;
+
+    match_init_catchall(&match);
+    rule_criteria_init(&criteria, 0xff, &match, 0, htonll(0), htonll(0),
+                       OFPP_ANY, group->group_id);
+    ovs_mutex_lock(&ofproto_mutex);
+    error = collect_rules_loose(ofproto, &criteria, &rules);
+    ovs_mutex_unlock(&ofproto_mutex);
+    rule_criteria_destroy(&criteria);
+
+    count = !error && rules.n < UINT32_MAX ? rules.n : UINT32_MAX;
+
+    rule_collection_destroy(&rules);
+    return count;
+}
+
 static void
 append_group_stats(struct ofgroup *group, struct list *replies)
     OVS_REQ_RDLOCK(group->rwlock)
@@ -5314,14 +5381,16 @@ append_group_stats(struct ofgroup *group, struct list *replies)
 
     ogs.bucket_stats = xmalloc(group->n_buckets * sizeof *ogs.bucket_stats);
 
+    /* Provider sets the packet and byte counts, we do the rest. */
+    ogs.ref_count = group_get_ref_count(group);
+    ogs.n_buckets = group->n_buckets;
+
     error = (ofproto->ofproto_class->group_get_stats
              ? ofproto->ofproto_class->group_get_stats(group, &ogs)
              : EOPNOTSUPP);
     if (error) {
-        ogs.ref_count = UINT32_MAX;
         ogs.packet_count = UINT64_MAX;
         ogs.byte_count = UINT64_MAX;
-        ogs.n_buckets = group->n_buckets;
         memset(ogs.bucket_stats, 0xff,
                ogs.n_buckets * sizeof *ogs.bucket_stats);
     }
@@ -5410,6 +5479,49 @@ handle_group_features_stats_request(struct ofconn *ofconn,
     return 0;
 }
 
+static enum ofperr
+handle_queue_get_config_request(struct ofconn *ofconn,
+                                const struct ofp_header *oh)
+{
+   struct ofproto *p = ofconn_get_ofproto(ofconn);
+   struct netdev_queue_dump queue_dump;
+   struct ofport *ofport;
+   unsigned int queue_id;
+   struct ofpbuf *reply;
+   struct smap details;
+   ofp_port_t request;
+   enum ofperr error;
+
+   error = ofputil_decode_queue_get_config_request(oh, &request);
+   if (error) {
+       return error;
+   }
+
+   ofport = ofproto_get_port(p, request);
+   if (!ofport) {
+      return OFPERR_OFPQOFC_BAD_PORT;
+   }
+
+   reply = ofputil_encode_queue_get_config_reply(oh);
+
+   smap_init(&details);
+   NETDEV_QUEUE_FOR_EACH (&queue_id, &details, &queue_dump, ofport->netdev) {
+       struct ofputil_queue_config queue;
+
+       /* None of the existing queues have compatible properties, so we
+        * hard-code omitting min_rate and max_rate. */
+       queue.queue_id = queue_id;
+       queue.min_rate = UINT16_MAX;
+       queue.max_rate = UINT16_MAX;
+       ofputil_append_queue_get_config_reply(reply, &queue);
+   }
+   smap_destroy(&details);
+
+   ofconn_send_reply(ofconn, reply);
+
+   return 0;
+}
+
 /* Implements OFPGC11_ADD
  * in which no matching flow already exists in the flow table.
  *
@@ -5467,7 +5579,7 @@ add_group(struct ofproto *ofproto, struct ofputil_group_mod *gm)
         goto unlock_out;
     }
 
-    if (ofproto_group_exists(ofproto, gm->group_id)) {
+    if (ofproto_group_exists__(ofproto, gm->group_id)) {
         error = OFPERR_OFPGMFC_GROUP_EXISTS;
         goto unlock_out;
     }
@@ -5562,6 +5674,15 @@ static void
 delete_group__(struct ofproto *ofproto, struct ofgroup *ofgroup)
     OVS_RELEASES(ofproto->groups_rwlock)
 {
+    struct match match;
+    struct ofputil_flow_mod fm;
+
+    /* Delete all flow entries containing this group in a group action */
+    match_init_catchall(&match);
+    flow_mod_init(&fm, &match, 0, NULL, 0, OFPFC_DELETE);
+    fm.out_group = ofgroup->group_id;
+    handle_flow_mod__(ofproto, NULL, &fm, NULL);
+
     /* Must wait until existing readers are done,
      * while holding the container's write lock at the same time. */
     ovs_rwlock_wrlock(&ofgroup->rwlock);
@@ -5794,10 +5915,8 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
     case OFPTYPE_GROUP_FEATURES_STATS_REQUEST:
         return handle_group_features_stats_request(ofconn, oh);
 
-        /* FIXME: Change the following once they are implemented: */
     case OFPTYPE_QUEUE_GET_CONFIG_REQUEST:
-    case OFPTYPE_TABLE_FEATURES_STATS_REQUEST:
-        /* fallthrough */
+        return handle_queue_get_config_request(ofconn, oh);
 
     case OFPTYPE_HELLO:
     case OFPTYPE_ERROR:
@@ -5826,7 +5945,9 @@ handle_openflow__(struct ofconn *ofconn, const struct ofpbuf *msg)
     case OFPTYPE_METER_STATS_REPLY:
     case OFPTYPE_METER_CONFIG_STATS_REPLY:
     case OFPTYPE_METER_FEATURES_STATS_REPLY:
+    case OFPTYPE_TABLE_FEATURES_STATS_REQUEST:
     case OFPTYPE_TABLE_FEATURES_STATS_REPLY:
+    case OFPTYPE_ROLE_STATUS:
     default:
         if (ofpmsg_is_stat_request(oh)) {
             return OFPERR_OFPBRC_BAD_STAT;
@@ -6720,10 +6841,10 @@ ofproto_get_vlan_usage(struct ofproto *ofproto, unsigned long int *vlan_bitmap)
     ofproto->vlans_changed = false;
 
     OFPROTO_FOR_EACH_TABLE (oftable, ofproto) {
-        const struct cls_table *table;
+        const struct cls_subtable *table;
 
         ovs_rwlock_rdlock(&oftable->cls.rwlock);
-        HMAP_FOR_EACH (table, hmap_node, &oftable->cls.tables) {
+        HMAP_FOR_EACH (table, hmap_node, &oftable->cls.subtables) {
             if (minimask_get_vid_mask(&table->mask) == VLAN_VID_MASK) {
                 const struct cls_rule *rule;
 
index c42b068..903d1f4 100644 (file)
@@ -408,6 +408,8 @@ struct ofproto_cfm_status {
      * mode. */
     int remote_opstate;
 
+    uint64_t flap_count;
+
     /* Ordinarily a "health status" in the range 0...100 inclusive, with 0
      * being worst and 100 being best, or -1 if the health status is not
      * well-defined. */
index 53f3579..0cbd091 100644 (file)
@@ -8,8 +8,6 @@
 .TH ovsdb\-client 1 "@VERSION@" "Open vSwitch" "Open vSwitch Manual"
 .\" This program's name:
 .ds PN ovsdb\-client
-.\" SSL peer program's name:
-.ds SN ovsdb\-server
 .
 .SH NAME
 ovsdb\-client \- command-line interface to \fBovsdb-server\fR(1)
index f86e8f3..269d4f4 100644 (file)
@@ -7,8 +7,6 @@
 .TH ovsdb\-server 1 "@VERSION@" "Open vSwitch" "Open vSwitch Manual"
 .\" This program's name:
 .ds PN ovsdb\-server
-.\" SSL peer program's name:
-.ds SN ovsdb\-client
 .
 .SH NAME
 ovsdb\-server \- Open vSwitch database server
index ab44b3a..f64122e 100644 (file)
@@ -983,6 +983,7 @@ ovsdb_server_compact(struct unixctl_conn *conn, int argc,
                 char *s = ovsdb_error_to_string(error);
                 ds_put_format(&reply, "%s\n", s);
                 free(s);
+                ovsdb_error_destroy(error);
             }
 
             n++;
index 11e61e6..5e2b71b 100644 (file)
@@ -524,7 +524,7 @@ do_show_log(int argc, char *argv[])
                     t *= 1000;
                 }
 
-               s = xastrftime_msec(" %Y-%m-%d %H:%M:%S.###", t, true);
+                s = xastrftime_msec(" %Y-%m-%d %H:%M:%S.###", t, true);
                 fputs(s, stdout);
                 free(s);
             }
index 73b09ba..6ce08af 100644 (file)
@@ -157,12 +157,10 @@ systemctl start openvswitch.service
 /usr/bin/ovs-vsctl
 /usr/bin/ovsdb-client
 /usr/bin/ovsdb-tool
-/usr/bin/ovs-controller
 /usr/bin/ovs-pki
 /usr/bin/ovs-test
 /usr/bin/ovs-l3ping
 /usr/bin/vtep-ctl
-%doc /usr/share/man/man8/ovs-controller.8.gz
 %doc /usr/share/man/man8/ovs-pki.8.gz
 %doc /usr/share/man/man1/ovsdb-client.1.gz
 %doc /usr/share/man/man1/ovsdb-server.1.gz
index 3faf463..02857f4 100644 (file)
@@ -56,8 +56,6 @@ install python/compat/argparse.py $RPM_BUILD_ROOT/usr/share/openvswitch/python
 
 # Get rid of stuff we don't want to make RPM happy.
 rm \
-    $RPM_BUILD_ROOT/usr/bin/ovs-controller \
-    $RPM_BUILD_ROOT/usr/share/man/man8/ovs-controller.8 \
     $RPM_BUILD_ROOT/usr/bin/ovs-test \
     $RPM_BUILD_ROOT/usr/bin/ovs-l3ping \
     $RPM_BUILD_ROOT/usr/share/man/man8/ovs-test.8 \
index 2b1049a..34187c4 100644 (file)
@@ -13,6 +13,8 @@
 /test-bundle
 /test-byte-order
 /test-classifier
+/test-controller.8
+/test-controller
 /test-csum
 /test-file_name
 /test-flows
index f2e0edc..099398a 100644 (file)
@@ -190,6 +190,13 @@ noinst_PROGRAMS += tests/test-classifier
 tests_test_classifier_SOURCES = tests/test-classifier.c
 tests_test_classifier_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
 
+noinst_PROGRAMS += tests/test-controller
+MAN_ROOTS += tests/test-controller.8.in
+DISTCLEANFILES += utilities/test-controller.8
+noinst_man_MANS += tests/test-controller.8
+tests_test_controller_SOURCES = tests/test-controller.c
+tests_test_controller_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+
 noinst_PROGRAMS += tests/test-csum
 tests_test_csum_SOURCES = tests/test-csum.c
 tests_test_csum_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
index 638e03f..9e351d0 100644 (file)
@@ -14,9 +14,16 @@ Remote MPID $7
 ])
 ])
 
+m4_define([CFM_VSCTL_LIST_IFACE], [
+AT_CHECK([ovs-vsctl list interface $1 | sed -n '/$2/p'],[0],
+[dnl
+$3
+])
+])
+
 # test cfm under demand mode.
 AT_SETUP([cfm - demand mode])
-#Create 2 bridges connected by patch ports and enable BFD
+#Create 2 bridges connected by patch ports and enable cfm
 OVS_VSWITCHD_START([add-br br1 -- \
                     set bridge br1 datapath-type=dummy \
                     other-config:hwaddr=aa:55:aa:56:00:00 -- \
@@ -55,3 +62,38 @@ done
 
 OVS_VSWITCHD_STOP
 AT_CLEANUP
+
+# test cfm_flap_count.
+AT_SETUP([cfm - flap_count])
+#Create 2 bridges connected by patch ports and enable cfm
+OVS_VSWITCHD_START([add-br br1 -- \
+                    set bridge br1 datapath-type=dummy \
+                    other-config:hwaddr=aa:55:aa:56:00:00 -- \
+                    add-port br1 p1 -- set Interface p1 type=patch \
+                    options:peer=p0 -- \
+                    add-port br0 p0 -- set Interface p0 type=patch \
+                    options:peer=p1 -- \
+                    set Interface p0 cfm_mpid=1 other_config:cfm_interval=100 other_config:cfm_extended=true -- \
+                    set Interface p1 cfm_mpid=2 other_config:cfm_interval=100 other_config:cfm_extended=true])
+
+ovs-appctl time/stop
+
+# wait for a while to stablize cfm.
+for i in `seq 0 100`; do ovs-appctl time/warp 100; done
+CFM_CHECK_EXTENDED([p0], [1], [100], [up], [up], [100ms], [2], [up])
+CFM_CHECK_EXTENDED([p1], [2], [100], [up], [up], [100ms], [1], [up])
+
+# turn cfm on p1 off, should increment the cfm_flap_count on p1.
+AT_CHECK([ovs-vsctl remove interface p1 cfm_mpid 2])
+for i in `seq 0 10`; do ovs-appctl time/warp 100; done
+CFM_VSCTL_LIST_IFACE([p0], [cfm_flap_count], [cfm_flap_count      : 1])
+CFM_VSCTL_LIST_IFACE([p1], [cfm_flap_count], [cfm_flap_count      : [[]]])
+
+# turn cfm on p1 on again, should increment the cfm_flap_count on p1.
+AT_CHECK([ovs-vsctl set interface p1 cfm_mpid=2])
+for i in `seq 0 10`; do ovs-appctl time/warp 100; done
+CFM_VSCTL_LIST_IFACE([p0], [cfm_flap_count], [cfm_flap_count      : 2])
+CFM_VSCTL_LIST_IFACE([p1], [cfm_flap_count], [cfm_flap_count      : 0])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
\ No newline at end of file
index 7a4bda5..491cd5e 100644 (file)
@@ -32,8 +32,8 @@ actions=learn(table=1, in_port=1, load:OXM_OF_IN_PORT[]->NXM_NX_REG1[], load:0xf
 AT_CHECK([ovs-ofctl -O OpenFlow12 parse-flows flows.txt], [0],
 [[usable protocols: any
 chosen protocol: OXM-OpenFlow12
-OFPT_FLOW_MOD (OF1.2) (xid=0x1): ADD table:255 actions=learn(table=1,output:OXM_OF_IN_PORT[])
-OFPT_FLOW_MOD (OF1.2) (xid=0x2): ADD table:255 actions=learn(table=1,in_port=1,load:OXM_OF_IN_PORT[]->NXM_NX_REG1[],load:0xfffffffe->OXM_OF_IN_PORT[])
+OFPT_FLOW_MOD (OF1.2) (xid=0x1): ADD actions=learn(table=1,output:OXM_OF_IN_PORT[])
+OFPT_FLOW_MOD (OF1.2) (xid=0x2): ADD actions=learn(table=1,in_port=1,load:OXM_OF_IN_PORT[]->NXM_NX_REG1[],load:0xfffffffe->OXM_OF_IN_PORT[])
 ]])
 AT_CLEANUP
 
index 0909ce1..244c9ee 100644 (file)
@@ -138,10 +138,10 @@ AT_DATA([test-data], [dnl
 # actions=CONTROLLER:1234
 0000 0010 fffffffd 04d2 000000000000
 
-# actions=mod_vlan_vid:9
+# actions=set_vlan_vid:9
 0001 0008 0009 0000
 
-# actions=mod_vlan_pcp:6
+# actions=set_vlan_pcp:6
 0002 0008 06 000000
 
 # actions=mod_dl_src:00:11:22:33:44:55
@@ -165,7 +165,7 @@ AT_DATA([test-data], [dnl
 # actions=mod_tp_dst:443
 000a 0008 01bb 0000
 
-# actions=strip_vlan
+# actions=pop_vlan
 0012 0008 00000000
 
 # actions=set_queue:2309737729
index 87d5da8..8701f54 100644 (file)
@@ -444,10 +444,10 @@ AT_CHECK([ovs-ofctl ofp-print "\
 00 00 50 54 00 00 00 06 50 54 00 00 00 05 08 00 \
 45 00 00 28 bd 12 00 00 40 06 3c 6a c0 a8 00 01 \
 c0 a8 00 02 27 2f 00 00 78 50 cc 5b 57 af 42 1e \
-50 00 02 00 26 e8 00 00 00 00 00 00 00 00 \
+50 02 02 00 26 e8 00 00 00 00 00 00 00 00 \
 "], [0], [dnl
 OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=3 (via no_match) data_len=60 buffer=0x00000111
-tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=10031,tp_dst=0 tcp_csum:26e8
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=10031,tp_dst=0,tcp_flags=0x002 tcp_csum:26e8
 ])
 AT_CLEANUP
 
@@ -458,13 +458,13 @@ AT_CHECK([ovs-ofctl ofp-print "\
 00 00 50 54 00 00 00 06 50 54 00 00 00 05 08 00 \
 45 00 00 28 bd 12 00 00 40 06 3c 6a c0 a8 00 01 \
 c0 a8 00 02 27 2f 00 00 78 50 cc 5b 57 af 42 1e \
-50 00 02 00 26 e8 00 00 00 00 00 00 00 00 \
+50 10 02 00 26 e8 00 00 00 00 00 00 00 00 \
 " 3], [0], [dnl
 OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=3 (via no_match) data_len=60 buffer=0x00000111
-tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=10031,tp_dst=0 tcp_csum:26e8
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=10031,tp_dst=0,tcp_flags=0x010 tcp_csum:26e8
 00000000  50 54 00 00 00 06 50 54-00 00 00 05 08 00 45 00
 00000010  00 28 bd 12 00 00 40 06-3c 6a c0 a8 00 01 c0 a8
-00000020  00 02 27 2f 00 00 78 50-cc 5b 57 af 42 1e 50 00
+00000020  00 02 27 2f 00 00 78 50-cc 5b 57 af 42 1e 50 10
 00000030  02 00 26 e8 00 00 00 00-00 00 00 00
 ])
 AT_CLEANUP
@@ -631,7 +631,7 @@ b9 7c c0 a8 00 02 c0 a8 00 01 00 00 2b 60 00 00 \
 00 00 00 00 \
 "], [0], [dnl
 OFPT_PACKET_OUT (xid=0x0): in_port=1 actions=output:3 data_len=60
-tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=0,tp_dst=11104 tcp_csum:6d75
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=0,tp_dst=11104,tcp_flags=0x014 tcp_csum:6d75
 ])
 AT_CLEANUP
 
@@ -646,7 +646,7 @@ b9 7c c0 a8 00 02 c0 a8 00 01 00 00 2b 60 00 00 \
 00 00 00 00 \
 " 3], [0], [dnl
 OFPT_PACKET_OUT (xid=0x0): in_port=1 actions=output:3 data_len=60
-tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=0,tp_dst=11104 tcp_csum:6d75
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=0,tp_dst=11104,tcp_flags=0x014 tcp_csum:6d75
 00000000  50 54 00 00 00 05 50 54-00 00 00 06 08 00 45 00
 00000010  00 28 00 00 40 00 40 06-b9 7c c0 a8 00 02 c0 a8
 00000020  00 01 00 00 2b 60 00 00-00 00 6a 4f 2b 58 50 14
@@ -677,7 +677,7 @@ b9 7c c0 a8 00 02 c0 a8 00 01 00 00 2b 60 00 00 \
 00 00 00 00 \
 "], [0], [dnl
 OFPT_PACKET_OUT (OF1.2) (xid=0x8858dfc5): in_port=LOCAL actions=FLOOD data_len=60
-tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=0,tp_dst=11104 tcp_csum:6d75
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=0,tp_dst=11104,tcp_flags=0x014 tcp_csum:6d75
 ])
 AT_CLEANUP
 
@@ -724,7 +724,7 @@ AT_SETUP([OFPT_FLOW_MOD - OF1.2 - low verbosity])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\
 03 0e 00 90 00 00 00 02 00 00 00 00 00 00 00 00 \
-00 00 00 00 00 00 00 00 ff 00 00 00 00 00 ff ff \
+00 00 00 00 00 00 00 00 01 00 00 00 00 00 ff ff \
 ff ff ff ff ff ff ff ff ff ff ff ff 00 00 00 00 \
 00 01 00 42 80 00 00 04 00 00 00 01 80 00 08 06 \
 50 54 00 00 00 06 80 00 06 06 50 54 00 00 00 05 \
@@ -733,7 +733,7 @@ ff ff ff ff ff ff ff ff ff ff ff ff 00 00 00 00 \
 00 01 00 00 00 00 00 00 00 04 00 18 00 00 00 00 \
 00 00 00 10 00 00 00 03 00 00 00 00 00 00 00 00 \
 " 2], [0], [dnl
-OFPT_FLOW_MOD (OF1.2) (xid=0x2): ADD table:255 priority=65535,arp,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,arp_spa=192.168.0.2,arp_tpa=192.168.0.1,arp_op=2 actions=output:3
+OFPT_FLOW_MOD (OF1.2) (xid=0x2): ADD table:1 priority=65535,arp,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,arp_spa=192.168.0.2,arp_tpa=192.168.0.1,arp_op=2 actions=output:3
 ], [dnl
 ])
 AT_CLEANUP
@@ -762,7 +762,7 @@ AT_SETUP([OFPT_FLOW_MOD - OF1.2 - low verbosity])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\
 03 0e 00 90 00 00 00 02 00 00 00 00 00 00 00 00 \
-00 00 00 00 00 00 00 00 ff 00 00 00 00 00 ff ff \
+00 00 00 00 00 00 00 00 01 00 00 00 00 00 ff ff \
 ff ff ff ff ff ff ff ff ff ff ff ff 00 00 00 00 \
 00 01 00 42 80 00 00 04 00 00 00 01 80 00 08 06 \
 50 54 00 00 00 06 80 00 06 06 50 54 00 00 00 05 \
@@ -771,7 +771,7 @@ ff ff ff ff ff ff ff ff ff ff ff ff 00 00 00 00 \
 00 01 00 00 00 00 00 00 00 04 00 18 00 00 00 00 \
 00 00 00 10 00 00 00 03 00 00 00 00 00 00 00 00 \
 " 2], [0], [dnl
-OFPT_FLOW_MOD (OF1.2) (xid=0x2): ADD table:255 priority=65535,arp,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,arp_spa=192.168.0.2,arp_tpa=192.168.0.1,arp_op=2 actions=output:3
+OFPT_FLOW_MOD (OF1.2) (xid=0x2): ADD table:1 priority=65535,arp,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,arp_spa=192.168.0.2,arp_tpa=192.168.0.1,arp_op=2 actions=output:3
 ], [dnl
 ])
 AT_CLEANUP
@@ -781,7 +781,7 @@ AT_SETUP([OFPT_FLOW_MOD - OF1.3 - flags - low verbosity])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\
 04 0e 00 90 00 00 00 02 00 00 00 00 00 00 00 00 \
-00 00 00 00 00 00 00 00 ff 00 00 00 00 00 ff ff \
+00 00 00 00 00 00 00 00 01 00 00 00 00 00 ff ff \
 ff ff ff ff ff ff ff ff ff ff ff ff 00 1f 00 00 \
 00 01 00 42 80 00 00 04 00 00 00 01 80 00 08 06 \
 50 54 00 00 00 06 80 00 06 06 50 54 00 00 00 05 \
@@ -790,7 +790,7 @@ ff ff ff ff ff ff ff ff ff ff ff ff 00 1f 00 00 \
 00 01 00 00 00 00 00 00 00 04 00 18 00 00 00 00 \
 00 00 00 10 00 00 00 03 00 00 00 00 00 00 00 00 \
 " 2], [0], [dnl
-OFPT_FLOW_MOD (OF1.3) (xid=0x2): ADD table:255 priority=65535,arp,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,arp_spa=192.168.0.2,arp_tpa=192.168.0.1,arp_op=2 send_flow_rem check_overlap reset_counts no_packet_counts no_byte_counts actions=output:3
+OFPT_FLOW_MOD (OF1.3) (xid=0x2): ADD table:1 priority=65535,arp,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,arp_spa=192.168.0.2,arp_tpa=192.168.0.1,arp_op=2 send_flow_rem check_overlap reset_counts no_packet_counts no_byte_counts actions=output:3
 ], [dnl
 ])
 AT_CLEANUP
@@ -1978,6 +1978,50 @@ OFPT_BARRIER_REPLY (OF1.3) (xid=0x1):
 ])
 AT_CLEANUP
 
+AT_SETUP([OFPT_QUEUE_GET_CONFIG_REQUEST - OF1.0])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "01 16 00 0c 00 00 00 01 00 01 00 00"], [0], [dnl
+OFPT_QUEUE_GET_CONFIG_REQUEST (xid=0x1): port=1
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_QUEUE_GET_CONFIG_REQUEST - OF1.2])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+03 16 00 10 00 00 00 01 00 00 00 01 00 00 00 00"], [0], [dnl
+OFPT_QUEUE_GET_CONFIG_REQUEST (OF1.2) (xid=0x1): port=1
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_QUEUE_GET_CONFIG_REPLY - OF1.0])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "01 17 00 40 00 00 00 01 \
+00 01 00 00 00 00 00 00 \
+00 00 55 55 00 28 00 00 \
+00 01 00 10 00 00 00 00 01 f4 00 00 00 00 00 00 \
+00 02 00 10 00 00 00 00 02 ee 00 00 00 00 00 00 \
+00 00 44 44 00 08 00 00 \
+"], [0], [dnl
+OFPT_QUEUE_GET_CONFIG_REPLY (xid=0x1): port=1
+queue 21845: min_rate:50.0% max_rate:75.0%
+queue 17476:
+])
+AT_CLEANUP
+
+AT_SETUP([OFPT_QUEUE_GET_CONFIG_REPLY - OF1.2])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "03 17 00 50 00 00 00 01 \
+00 00 00 01 00 00 00 00 \
+00 00 55 55 00 00 00 01 00 30 00 00 00 00 00 00 \
+00 01 00 10 00 00 00 00 01 f4 00 00 00 00 00 00 \
+00 02 00 10 00 00 00 00 02 ee 00 00 00 00 00 00 \
+00 00 44 44 00 08 00 01 00 10 00 00 00 00 00 00 \
+"], [0], [dnl
+OFPT_QUEUE_GET_CONFIG_REPLY (OF1.2) (xid=0x1): port=1
+queue 21845: min_rate:50.0% max_rate:75.0%
+queue 17476:
+])
+AT_CLEANUP
 
 AT_SETUP([OFPT_SET_ASYNC - OF1.3])
 AT_KEYWORDS([ofp-print])
@@ -2048,6 +2092,36 @@ NXT_ROLE_REPLY (xid=0x2): role=slave
 ])
 AT_CLEANUP
 
+AT_SETUP([OFP_ROLE_STATUS - master, experimenter - OF1.4])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+05 1e 00 18 00 00 00 0a \
+00 00 00 02 02 00 00 00 ff ff ff ff ff ff ff ff \
+"], [0], [dnl
+OFPT_ROLE_STATUS (OF 0x05) (xid=0xa): role=master reason=experimenter_data_changed
+])
+AT_CLEANUP
+
+AT_SETUP([OFP_ROLE_STATUS - master, config - OF1.4])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+05 1e 00 18 00 00 00 0a \
+00 00 00 02 01 00 00 00 ff ff ff ff ff ff ff ff \
+"], [0], [dnl
+OFPT_ROLE_STATUS (OF 0x05) (xid=0xa): role=master reason=configuration_changed
+])
+AT_CLEANUP
+
+AT_SETUP([OFP_ROLE_STATUS - master, config,generation - OF1.4])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl ofp-print "\
+05 1e 00 18 00 00 00 0a \
+00 00 00 02 01 00 00 00 00 00 00 00 00 00 00 10 \
+"], [0], [dnl
+OFPT_ROLE_STATUS (OF 0x05) (xid=0xa): role=master generation_id=16 reason=configuration_changed
+])
+AT_CLEANUP
+
 AT_SETUP([NXT_SET_PACKET_IN])
 AT_KEYWORDS([ofp-print])
 AT_CHECK([ovs-ofctl ofp-print "\
@@ -2071,11 +2145,11 @@ ff ff ff ff 00 40 01 07 00 00 00 00 00 00 00 09 \
 ff ff ff ff ff ff 00 00 00 00 82 82 82 82 82 82 \
 80 81 81 81 81 81 81 00 00 50 08 00 45 00 00 28 \
 00 00 00 00 00 06 32 05 53 53 53 53 54 54 54 54 \
-00 55 00 56 00 00 00 00 00 00 00 00 50 00 00 00 \
+00 55 00 56 00 00 00 00 00 00 00 00 50 02 00 00 \
 31 6d 00 00 00 00 00 00 00 00 \
 "], [0], [dnl
 NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 in_port=1 tun_id=0x6 metadata=0x5a5a5a5a5a5a5a5a reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
-tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=85,tp_dst=86 tcp_csum:316d
+tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=85,tp_dst=86,tcp_flags=0x002 tcp_csum:316d
 ])
 AT_CLEANUP
 
@@ -2092,15 +2166,15 @@ ff ff ff ff 00 40 01 07 00 00 00 00 00 00 00 09 \
 ff ff ff ff ff ff 00 00 00 00 82 82 82 82 82 82 \
 80 81 81 81 81 81 81 00 00 50 08 00 45 00 00 28 \
 00 00 00 00 00 06 32 05 53 53 53 53 54 54 54 54 \
-00 55 00 56 00 00 00 00 00 00 00 00 50 00 00 00 \
+00 55 00 56 00 00 00 00 00 00 00 00 50 01 00 00 \
 31 6d 00 00 00 00 00 00 00 00 \
 " 3], [0], [dnl
 NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 in_port=1 tun_id=0x6 metadata=0x5a5a5a5a5a5a5a5a reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
-tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=85,tp_dst=86 tcp_csum:316d
+tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=85,tp_dst=86,tcp_flags=0x001 tcp_csum:316d
 00000000  82 82 82 82 82 82 80 81-81 81 81 81 81 00 00 50
 00000010  08 00 45 00 00 28 00 00-00 00 00 06 32 05 53 53
 00000020  53 53 54 54 54 54 00 55-00 56 00 00 00 00 00 00
-00000030  00 00 50 00 00 00 31 6d-00 00 00 00 00 00 00 00
+00000030  00 00 50 01 00 00 31 6d-00 00 00 00 00 00 00 00
 ])
 AT_CLEANUP
 
index c569463..2a2d325 100644 (file)
@@ -65,6 +65,110 @@ AT_CHECK([tail -1 stdout], [0],
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([ofproto-dpif - all group in action list])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [10], [11])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-group br0 'group_id=1234,type=all,bucket=output:10,set_field:192.168.3.90->ip_src,bucket=output:11'])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 'ip actions=group:1234'])
+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=1,nw_tos=0,nw_ttl=128,icmp_type=8,icmp_code=0'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: set(ipv4(src=192.168.3.90,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no)),10,set(ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no)),11
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - indirect group in action list])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [10])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-group br0 group_id=1234,type=indirect,bucket=output:10])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 'ip actions=group:1234'])
+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=1,nw_tos=0,nw_ttl=128,icmp_type=8,icmp_code=0'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: 10
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - all group in action set])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [10], [11])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-group br0 'group_id=1234,type=all,bucket=output:10,set_field:192.168.3.90->ip_src,bucket=output:11'])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 'ip actions=write_actions(group:1234)'])
+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=1,nw_tos=0,nw_ttl=128,icmp_type=8,icmp_code=0'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: set(ipv4(src=192.168.3.90,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no)),10,set(ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no)),11
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - indirect group in action set])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [10])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-group br0 group_id=1234,type=indirect,bucket=output:10])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 'ip actions=write_actions(group:1234)'])
+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=1,nw_tos=0,nw_ttl=128,icmp_type=8,icmp_code=0'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: 10
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - select group])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [10], [11])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-group br0 'group_id=1234,type=select,bucket=output:10,bucket=output:11'])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 'ip actions=write_actions(group:1234)'])
+
+# Try a bunch of different flows and make sure that they get distributed
+# at least somewhat.
+for d in 0 1 2 3 4 5 6 7 8 9 a b c d e f; do
+    AT_CHECK([ovs-appctl ofproto/trace br0 "in_port=1,dl_src=50:54:00:00:00:07,dl_dst=50:54:00:00:00:0$d,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=1,nw_tos=0,nw_ttl=128,icmp_type=8,icmp_code=0"], [0], [stdout])
+    tail -1 stdout >> results
+done
+sort results | uniq -c
+AT_CHECK([sort results | uniq], [0],
+  [Datapath actions: 10
+Datapath actions: 11
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - select group with watch port])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [10], [11])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-group br0 'group_id=1234,type=select,bucket=watch_port:10,output:10,bucket=output:11'])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 'ip actions=write_actions(group:1234)'])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:07,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=1,nw_tos=0,nw_ttl=128,icmp_type=8,icmp_code=0'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: 11
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - select group with weight])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [10], [11], [12])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-group br0 'group_id=1234,type=select,bucket=output:10,bucket=output:11,weight=2,bucket=output:12,weight=0'])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 'ip actions=write_actions(group:1234)'])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:07,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=1,nw_tos=0,nw_ttl=128,icmp_type=8,icmp_code=0'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: 11
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - fast failover group])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [10], [11])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-group br0 'group_id=1234,type=ff,bucket=watch_port:10,output:10,bucket=watch_port:11,output:11'])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 'ip actions=write_actions(group:1234)'])
+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=1,nw_tos=0,nw_ttl=128,icmp_type=8,icmp_code=0'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: drop
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([ofproto-dpif - registers])
 OVS_VSWITCHD_START
 ADD_OF_PORTS([br0], [20], [21], [22], [33], [90])
@@ -317,60 +421,60 @@ dnl Flow miss.
 AT_CHECK([ovs-ofctl monitor -P openflow10 br0 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log])
 
 for i in 1 2 3 ; do
-    ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'
+    ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9),tcp_flags(0x010)'
 done
 OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
 ovs-appctl -t ovs-ofctl exit
 
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via no_match) data_len=60 (unbuffered)
-tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=9 tcp_csum:0
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=9,tcp_flags=0x010 tcp_csum:0
 dnl
 OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via no_match) data_len=60 (unbuffered)
-tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=9 tcp_csum:0
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=9,tcp_flags=0x010 tcp_csum:0
 dnl
 OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via no_match) data_len=60 (unbuffered)
-tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=9 tcp_csum:0
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=9,tcp_flags=0x010 tcp_csum:0
 ])
 
 dnl Singleton controller action.
 AT_CHECK([ovs-ofctl monitor -P openflow10 br0 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log])
 
 for i in 1 2 3 ; do
-    ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=10:11:11:11:11:11,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=10)'
+    ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=10:11:11:11:11:11,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=10),tcp_flags(0x002)'
 done
 OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
 ovs-appctl -t ovs-ofctl exit
 
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
-tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10 tcp_csum:0
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10,tcp_flags=0x002 tcp_csum:0
 dnl
 OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
-tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10 tcp_csum:0
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10,tcp_flags=0x002 tcp_csum:0
 dnl
 OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
-tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10 tcp_csum:0
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=10:11:11:11:11:11,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10,tcp_flags=0x002 tcp_csum:0
 ])
 
 dnl Modified controller action.
 AT_CHECK([ovs-ofctl monitor -P openflow10 br0 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log])
 
 for i in 1 2 3 ; do
-    ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=30:33:33:33:33:33,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=10)'
+    ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=30:33:33:33:33:33,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=10),tcp_flags(0x001)'
 done
 OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 6])
 ovs-appctl -t ovs-ofctl exit
 
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 OFPT_PACKET_IN (xid=0x0): total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
-tcp,metadata=0,in_port=0,dl_vlan=15,dl_vlan_pcp=0,dl_src=30:33:33:33:33:33,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10 tcp_csum:0
+tcp,metadata=0,in_port=0,dl_vlan=15,dl_vlan_pcp=0,dl_src=30:33:33:33:33:33,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10,tcp_flags=0x001 tcp_csum:0
 dnl
 OFPT_PACKET_IN (xid=0x0): total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
-tcp,metadata=0,in_port=0,dl_vlan=15,dl_vlan_pcp=0,dl_src=30:33:33:33:33:33,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10 tcp_csum:0
+tcp,metadata=0,in_port=0,dl_vlan=15,dl_vlan_pcp=0,dl_src=30:33:33:33:33:33,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10,tcp_flags=0x001 tcp_csum:0
 dnl
 OFPT_PACKET_IN (xid=0x0): total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
-tcp,metadata=0,in_port=0,dl_vlan=15,dl_vlan_pcp=0,dl_src=30:33:33:33:33:33,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10 tcp_csum:0
+tcp,metadata=0,in_port=0,dl_vlan=15,dl_vlan_pcp=0,dl_src=30:33:33:33:33:33,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10,tcp_flags=0x001 tcp_csum:0
 ])
 
 dnl Modified VLAN controller action.
@@ -384,13 +488,13 @@ ovs-appctl -t ovs-ofctl exit
 
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
-tcp,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:44:41,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64 tcp_csum:0
+tcp,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:44:41,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tcp_flags=0x000 tcp_csum:0
 dnl
 NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
-tcp,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:44:41,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64 tcp_csum:0
+tcp,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:44:41,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tcp_flags=0x000 tcp_csum:0
 dnl
 NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
-tcp,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:44:41,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64 tcp_csum:0
+tcp,metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=1,dl_src=40:44:44:44:44:41,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tcp_flags=0x000 tcp_csum:0
 ])
 
 dnl Modified MPLS controller action.
@@ -424,13 +528,13 @@ ovs-appctl -t ovs-ofctl exit
 
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
-tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=41:44:44:44:44:42,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64 tcp_csum:0
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=41:44:44:44:44:42,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tcp_flags=0x000 tcp_csum:0
 dnl
 NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
-tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=41:44:44:44:44:42,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64 tcp_csum:0
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=41:44:44:44:44:42,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tcp_flags=0x000 tcp_csum:0
 dnl
 NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
-tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=41:44:44:44:44:42,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64 tcp_csum:0
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=41:44:44:44:44:42,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tcp_flags=0x000 tcp_csum:0
 ])
 
 dnl Modified MPLS controller action.
@@ -613,51 +717,51 @@ ovs-appctl -t ovs-ofctl exit
 
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered)
-tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=60:66:66:66:66:66,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=80,tp_dst=0 tcp_csum:7744
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=60:66:66:66:66:66,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=80,tp_dst=0,tcp_flags=0x000 tcp_csum:7744
 dnl
 NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered)
-tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=60:66:66:66:66:66,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=80,tp_dst=0 tcp_csum:7744
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=60:66:66:66:66:66,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=80,tp_dst=0,tcp_flags=0x000 tcp_csum:7744
 dnl
 NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered)
-tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=60:66:66:66:66:66,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=80,tp_dst=0 tcp_csum:7744
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=60:66:66:66:66:66,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=80,tp_dst=0,tcp_flags=0x000 tcp_csum:7744
 ])
 
 dnl Checksum TCP.
 AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --no-chdir --pidfile 2> ofctl_monitor.log])
 
 for i in 1 ; do
-    ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=20:22:22:22:22:22,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=11)'
+    ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=20:22:22:22:22:22,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=11),tcp_flags(0x001)'
 done
 OVS_WAIT_UNTIL([test `wc -l < ofctl_monitor.log` -ge 18])
 ovs-appctl -t ovs-ofctl exit
 
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 NXT_PACKET_IN (xid=0x0): cookie=0x1 total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
-tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=20:22:22:22:22:22,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=11 tcp_csum:0
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=20:22:22:22:22:22,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=11,tcp_flags=0x001 tcp_csum:0
 dnl
 NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x3 total_len=64 in_port=1 reg0=0x1 (via action) data_len=64 (unbuffered)
-tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=20:22:22:22:22:22,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=11 tcp_csum:0
+tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=20:22:22:22:22:22,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=11,tcp_flags=0x001 tcp_csum:0
 dnl
 NXT_PACKET_IN (xid=0x0): table_id=2 cookie=0x4 total_len=64 in_port=1 reg0=0x1 reg1=0x2 (via action) data_len=64 (unbuffered)
-tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=11 tcp_csum:0
+tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=11,tcp_flags=0x001 tcp_csum:0
 dnl
 NXT_PACKET_IN (xid=0x0): table_id=3 cookie=0x5 total_len=64 in_port=1 reg0=0x1 reg1=0x2 reg2=0x3 (via action) data_len=64 (unbuffered)
-tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=11 tcp_csum:0
+tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=11,tcp_flags=0x001 tcp_csum:0
 dnl
 NXT_PACKET_IN (xid=0x0): table_id=4 cookie=0x6 total_len=64 in_port=1 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 (via action) data_len=64 (unbuffered)
-tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=11 tcp_csum:1a03
+tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=11,tcp_flags=0x001 tcp_csum:1a03
 dnl
 NXT_PACKET_IN (xid=0x0): table_id=5 cookie=0x7 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
-tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=11 tcp_csum:3205
+tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=11,tcp_flags=0x001 tcp_csum:3205
 dnl
 NXT_PACKET_IN (xid=0x0): table_id=6 cookie=0x8 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
-tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=85,tp_dst=11 tcp_csum:31b8
+tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=85,tp_dst=11,tcp_flags=0x001 tcp_csum:31b8
 dnl
 NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
-tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=85,tp_dst=86 tcp_csum:316d
+tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=85,tp_dst=86,tcp_flags=0x001 tcp_csum:316d
 dnl
 NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
-tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=85,tp_dst=86 tcp_csum:316d
+tcp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=85,tp_dst=86,tcp_flags=0x001 tcp_csum:316d
 ])
 
 dnl Checksum UDP.
@@ -3048,12 +3152,12 @@ ADD_OF_PORTS([br0], 1)
  echo "in_port=13, actions=local,local,local,local,local,local,local,local") > flows
 AT_CHECK([ovs-ofctl add-flows br0 flows])
 AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1'], [0], [stdout])
-AT_CHECK([grep -c 'resubmits yielded over 64 kB of actions' ovs-vswitchd.log], [0], [1
+AT_CHECK([grep -c -e '- Uses action(s) not supported by datapath' stdout],
+  [0], [1
 ])
-AT_CHECK([grep -c 'discarding oversize datapath actions' ovs-vswitchd.log], [0], [1
+AT_CHECK([grep -c 'resubmits yielded over 64 kB of actions' ovs-vswitchd.log], [0], [1
 ])
-OVS_VSWITCHD_STOP(["/resubmits yielded over 64 kB of actions/d
-/discarding oversize datapath actions/d"])
+OVS_VSWITCHD_STOP(["/resubmits yielded over 64 kB of actions/d"])
 AT_CLEANUP
 
 AT_SETUP([ofproto-dpif - stack too deep])
index 0e1d41b..27b6b34 100644 (file)
@@ -144,6 +144,197 @@ OFPST_QUEUE request (OF1.2) (xid=0x2):port=10 queue=ALL
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+dnl This is really bare-bones.
+dnl It at least checks request and reply serialization and deserialization.
+AT_SETUP([ofproto - queue configuration - (OpenFlow 1.0)])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [2])
+AT_CHECK([ovs-ofctl queue-get-config br0 1], [0], [stdout])
+AT_CHECK([STRIP_XIDS stdout], [0], [dnl
+OFPT_QUEUE_GET_CONFIG_REPLY: port=1
+])
+AT_CHECK([ovs-ofctl queue-get-config br0 10], [0],
+  [OFPT_ERROR (xid=0x2): OFPQOFC_BAD_PORT
+OFPT_QUEUE_GET_CONFIG_REQUEST (xid=0x2): port=10
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto - queue configuration - (OpenFlow 1.2)])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [2])
+AT_CHECK([ovs-ofctl -O OpenFlow12 queue-get-config br0 1], [0], [stdout])
+AT_CHECK([STRIP_XIDS stdout], [0], [dnl
+OFPT_QUEUE_GET_CONFIG_REPLY (OF1.2): port=1
+])
+AT_CHECK([ovs-ofctl -O OpenFlow12 queue-get-config br0 10], [0],
+  [OFPT_ERROR (OF1.2) (xid=0x2): OFPQOFC_BAD_PORT
+OFPT_QUEUE_GET_CONFIG_REQUEST (OF1.2) (xid=0x2): port=10
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+dnl This is really bare-bones.
+dnl It at least checks request and reply serialization and deserialization.
+AT_SETUP([ofproto - del group])
+OVS_VSWITCHD_START
+AT_DATA([groups.txt], [dnl
+group_id=1234,type=all,bucket=output:10
+group_id=1235,type=all,bucket=output:10
+])
+AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn add-groups br0 groups.txt])
+AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn del-groups br0 group_id=1234])
+AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn dump-groups br0], [0], [stdout])
+AT_CHECK([STRIP_XIDS stdout], [0], [dnl
+OFPST_GROUP_DESC reply (OF1.1):
+ group_id=1235,type=all,bucket=actions=output:10
+])
+AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn del-groups br0 group_id=1234])
+AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn dump-groups br0], [0], [stdout])
+AT_CHECK([STRIP_XIDS stdout], [0], [dnl
+OFPST_GROUP_DESC reply (OF1.1):
+ group_id=1235,type=all,bucket=actions=output:10
+])
+AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn del-groups br0], [0])
+AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn dump-groups br0], [0], [stdout])
+AT_CHECK([STRIP_XIDS stdout], [0], [dnl
+OFPST_GROUP_DESC reply (OF1.1):
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+dnl This is really bare-bones.
+dnl It at least checks request and reply serialization and deserialization.
+AT_SETUP([ofproto - del group deletes flows])
+OVS_VSWITCHD_START
+AT_DATA([groups.txt], [dnl
+group_id=1234,type=all,bucket=output:10
+group_id=1235,type=all,bucket=output:10
+])
+AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn add-groups br0 groups.txt])
+AT_DATA([flows.txt], [dnl
+tcp actions=group:1234
+udp actions=group:1235
+])
+AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn add-flows br0 flows.txt])
+AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn dump-flows br0 | ofctl_strip | sort],
+[0], [dnl
+ tcp actions=group:1234
+ udp actions=group:1235
+OFPST_FLOW reply (OF1.1):
+])
+AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn del-groups br0 group_id=1234])
+AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn dump-flows br0 | ofctl_strip | sort],
+[0], [dnl
+ udp actions=group:1235
+OFPST_FLOW reply (OF1.1):
+])
+AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn del-groups br0 group_id=1234])
+AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn dump-flows br0 | ofctl_strip | sort],
+[0], [dnl
+ udp actions=group:1235
+OFPST_FLOW reply (OF1.1):
+])
+AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn del-groups br0])
+AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn dump-flows br0 | ofctl_strip | sort],
+[0], [dnl
+OFPST_FLOW reply (OF1.1):
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+dnl This is really bare-bones.
+dnl It at least checks request and reply serialization and deserialization.
+AT_SETUP([ofproto - flow mod checks group availability])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn add-group br0 group_id=1234,type=all,bucket=output:10])
+AT_DATA([flows.txt], [dnl
+tcp actions=group:1234
+udp actions=group:1235
+])
+AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn add-flow br0 'tcp actions=group:1234'])
+AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn add-flow br0 'tcp actions=group:1235'], [1], [], [stderr])
+
+# The output should look like this:
+#
+# 00000000  02 0e 00 98 00 00 00 02-00 00 00 00 00 00 00 00 |................|
+# 00000010  00 00 00 00 00 00 00 00-ff 00 00 00 00 00 80 00 |................|
+# 00000020  ff ff ff ff ff ff ff ff-ff ff ff ff 00 00 00 00 |................|
+# 00000030  00 00 00 58 00 00 00 00-00 00 03 d7 00 00 00 00 |...X............|
+#
+# This 'sed' command captures the error message but drops details.
+AT_CHECK([sed '/truncated/d
+/^000000.0/d' stderr | STRIP_XIDS], [0],
+  [OFPT_ERROR (OF1.1): OFPBAC_BAD_OUT_GROUP
+OFPT_FLOW_MOD (OF1.1):
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+dnl This is really bare-bones.
+dnl It at least checks request and reply serialization and deserialization.
+AT_SETUP([ofproto - group description])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn add-group br0 group_id=1234,type=all,bucket=output:10], [0], [stdout])
+AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn dump-groups br0], [0], [stdout])
+AT_CHECK([STRIP_XIDS stdout], [0], [dnl
+OFPST_GROUP_DESC reply (OF1.1):
+ group_id=1234,type=all,bucket=actions=output:10
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+dnl This is really bare-bones.
+dnl It at least checks request and reply serialization and deserialization.
+AT_SETUP([ofproto - group description])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn add-group br0 group_id=1234,type=all,bucket=output:10])
+AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn dump-groups br0], [0], [stdout])
+AT_CHECK([STRIP_XIDS stdout], [0], [dnl
+OFPST_GROUP_DESC reply (OF1.1):
+ group_id=1234,type=all,bucket=actions=output:10
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+dnl This is really bare-bones.
+dnl It at least checks request and reply serialization and deserialization.
+AT_SETUP([ofproto - group features])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl -O OpenFlow12 -vwarn dump-group-features br0], [0], [stdout])
+AT_CHECK([STRIP_XIDS stdout], [0], [dnl
+OFPST_GROUP_FEATURES reply (OF1.2):
+ Group table:
+    Types:  0x0
+    Capabilities:  0x7
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+dnl This is really bare-bones.
+dnl It at least checks request and reply serialization and deserialization.
+AT_SETUP([ofproto - group stats])
+OVS_VSWITCHD_START
+AT_DATA([groups.txt], [dnl
+group_id=1234,type=all,bucket=output:10
+group_id=1235,type=all,bucket=output:10
+])
+AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn add-groups br0 groups.txt])
+AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn add-flow br0 'tcp actions=group:1234'])
+AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn dump-group-stats br0 group_id=1234], [0], [stdout])
+AT_CHECK([STRIP_XIDS stdout | sort], [0], [dnl
+ group_id=1234,ref_count=1,packet_count=0,byte_count=0,bucket0:packet_count=0,byte_count=0
+OFPST_GROUP reply (OF1.1):
+])
+AT_CHECK([ovs-ofctl -O OpenFlow11 -vwarn dump-group-stats br0], [0], [stdout])
+AT_CHECK([STRIP_XIDS stdout | sort], [0], [dnl
+ group_id=1234,ref_count=1,packet_count=0,byte_count=0,bucket0:packet_count=0,byte_count=0
+ group_id=1235,ref_count=0,packet_count=0,byte_count=0,bucket0:packet_count=0,byte_count=0
+OFPST_GROUP reply (OF1.1):
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([ofproto - mod-port (OpenFlow 1.0)])
 OVS_VSWITCHD_START
 for command_config_state in \
@@ -271,6 +462,21 @@ OFPST_AGGREGATE reply (OF1.1): packet_count=0 byte_count=0 flow_count=2
 ])
 AT_CHECK([ovs-ofctl -O OpenFlow11 del-flows br0])
 AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip], [0], [OFPST_FLOW reply (OF1.1):
+ table=1, in_port=4 actions=output:3
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto - flow_mod negative test (OpenFlow 1.1)])
+OVS_VSWITCHD_START(
+  [set bridge br0 protocols=OpenFlow10,OpenFlow11,OpenFlow12,OpenFlow13])
+AT_CHECK([ovs-ofctl add-flow -O OpenFlow11 br0 table=1,action=goto_table:2])
+
+# The error message here actually comes from ovs-ofctl, not from ovs-vswitchd,
+# but at least it's the same code in ofpacts_check() that issues the error.
+AT_CHECK([ovs-ofctl add-flow -O OpenFlow11 br0 table=1,action=goto_table:1],
+  [1], [],
+  [ovs-ofctl: actions are invalid with specified match (OFPBRC_BAD_TABLE_ID)
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -655,8 +861,9 @@ AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip | sort], [0], [dn
 OFPST_FLOW reply (OF1.1):
 ])
 AT_CHECK([ovs-ofctl -O OpenFlow11 del-flows br0])
-AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip | sort], [0], [dnl
+AT_CHECK([ovs-ofctl -O OpenFlow11 dump-flows br0 | ofctl_strip], [0], [dnl
 OFPST_FLOW reply (OF1.1):
+ cookie=0x2, table=1, in_port=2 actions=output:1
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
index 131beaf..a99a161 100644 (file)
@@ -4,23 +4,37 @@ AT_SETUP([ovs-ofctl parse-flows choice of protocol])
 # This doesn't cover some potential vlan_tci test cases.
 for test_case in \
     'tun_id=0                                    NXM,OXM' \
+    'tun_id=0/0x1                                NXM,OXM' \
     'tun_src=1.2.3.4                             NXM,OXM' \
+    'tun_src=1.2.3.4/0.0.0.1                     NXM,OXM' \
     'tun_dst=1.2.3.4                             NXM,OXM' \
+    'tun_dst=1.2.3.4/0.0.0.1                     NXM,OXM' \
     'tun_flags=0                                 none' \
+    'tun_flags=1/1                               none' \
     'tun_tos=0                                   none' \
     'tun_ttl=0                                   none' \
     'metadata=0                                  NXM,OXM,OpenFlow11' \
+    'metadata=1/1                                NXM,OXM,OpenFlow11' \
     'in_port=1                                   any' \
     'skb_priority=0                              none' \
     'pkt_mark=1                                  NXM,OXM' \
+    'pkt_mark=1/1                                NXM,OXM' \
     'reg0=0                                      NXM,OXM' \
+    'reg0=0/1                                    NXM,OXM' \
     'reg1=1                                      NXM,OXM' \
+    'reg1=1/1                                    NXM,OXM' \
     'reg2=2                                      NXM,OXM' \
+    'reg2=2/1                                    NXM,OXM' \
     'reg3=3                                      NXM,OXM' \
+    'reg3=3/1                                    NXM,OXM' \
     'reg4=4                                      NXM,OXM' \
+    'reg4=4/1                                    NXM,OXM' \
     'reg5=5                                      NXM,OXM' \
+    'reg5=5/1                                    NXM,OXM' \
     'reg6=6                                      NXM,OXM' \
+    'reg6=6/1                                    NXM,OXM' \
     'reg7=7                                      NXM,OXM' \
+    'reg7=7/1                                    NXM,OXM' \
     'dl_src=00:11:22:33:44:55                    any' \
     'dl_src=00:11:22:33:44:55/00:ff:ff:ff:ff:ff  NXM,OXM,OpenFlow11' \
     'dl_dst=00:11:22:33:44:55                    any' \
@@ -31,8 +45,10 @@ for test_case in \
     'dl_type=0x86dd                              any' \
     'vlan_tci=0                                  any' \
     'vlan_tci=0x1009                             any' \
+    'vlan_tci=0x1009/0x1                         NXM,OXM' \
     'dl_vlan=9                                   any' \
     'vlan_vid=11                                 any' \
+    'vlan_vid=11/0x1                             NXM,OXM' \
     'dl_vlan_pcp=6                               any' \
     'vlan_pcp=5                                  any' \
     'mpls,mpls_label=5                           NXM,OXM,OpenFlow11' \
@@ -45,14 +61,17 @@ for test_case in \
     'ip,ip_dst=192.168.0.0/24                    any' \
     'ip,ip_dst=192.0.168.0/255.0.255.0           NXM,OXM,OpenFlow11' \
     'ipv6,ipv6_src=::1                           NXM,OXM' \
+    'ipv6,ipv6_src=::1/::1                       NXM,OXM' \
     'ipv6,ipv6_dst=::1                           NXM,OXM' \
+    'ipv6,ipv6_dst=::1/::1                       NXM,OXM' \
     'ipv6,ipv6_label=5                           NXM,OXM' \
+    'ipv6,ipv6_label=5/1                         NXM,OXM' \
     'ip,nw_proto=1                               any' \
     'ipv6,nw_proto=1                             NXM,OXM' \
     'ip,nw_tos=0xf0                              any' \
     'ipv6,nw_tos=0xf0                            NXM,OXM' \
-    'ip,nw_tos_shifted=0x3c                      any' \
-    'ipv6,nw_tos_shifted=0x3c                    NXM,OXM' \
+    'ip,ip_dscp=0x3c                             any' \
+    'ipv6,ip_dscp=0x3c                           NXM,OXM' \
     'ip,nw_ecn=1                                 NXM,OXM' \
     'ipv6,nw_ecn=1                               NXM,OXM' \
     'ip,nw_ttl=5                                 NXM,OXM' \
@@ -61,9 +80,13 @@ for test_case in \
     'ipv6,ip_frag=no                             NXM,OXM' \
     'arp,arp_op=0                                any' \
     'arp,arp_spa=1.2.3.4                         any' \
+    'arp,arp_spa=1.2.3.4/0.0.0.1                 NXM,OXM,OpenFlow11' \
     'arp,arp_tpa=1.2.3.4                         any' \
+    'arp,arp_tpa=1.2.3.4/0.0.0.1                 NXM,OXM,OpenFlow11' \
     'arp,arp_sha=00:11:22:33:44:55               NXM,OXM' \
+    'arp,arp_sha=00:11:22:33:44:55/00:ff:ff:ff:ff:ff NXM,OXM' \
     'arp,arp_tha=00:11:22:33:44:55               NXM,OXM' \
+    'arp,arp_tha=00:11:22:33:44:55/00:ff:ff:ff:ff:ff NXM,OXM' \
     'tcp,tcp_src=80                              any' \
     'tcp,tcp_src=0x1000/0x1000                   NXM,OXM' \
     'tcp6,tcp_src=80                             NXM,OXM' \
@@ -81,7 +104,7 @@ for test_case in \
     'udp6,udp_dst=80                             NXM,OXM' \
     'udp6,udp_dst=0x1000/0x1000                  NXM,OXM' \
     'icmp,icmp_type=1                            any' \
-    'icmp,icmp_type=1                            any' \
+    'icmp,icmp_code=2                            any' \
     'icmp6,icmpv6_type=1                         NXM,OXM' \
     'icmp6,icmpv6_code=2                         NXM,OXM'
 do
@@ -131,7 +154,7 @@ OFPT_FLOW_MOD: ADD tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:
 OFPT_FLOW_MOD: ADD udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1
 OFPT_FLOW_MOD: ADD priority=60000 cookie:0x123456789abcdef hard:10 actions=CONTROLLER:65535
 OFPT_FLOW_MOD: ADD actions=note:41.42.43.00.00.00,note:00.01.02.03.04.05.06.07.00.00.00.00.00.00,note:00.00.00.00.00.00
-OFPT_FLOW_MOD: ADD ip actions=load:0xa04034d->NXM_OF_IP_SRC[]
+OFPT_FLOW_MOD: ADD ip actions=mod_nw_src:10.4.3.77
 OFPT_FLOW_MOD: ADD sctp actions=drop
 OFPT_FLOW_MOD: ADD sctp actions=drop
 OFPT_FLOW_MOD: ADD in_port=0 actions=resubmit:0
@@ -161,18 +184,18 @@ AT_CHECK([ovs-ofctl --protocols OpenFlow11 parse-flows flows.txt
 AT_CHECK([[sed 's/ (xid=0x[0-9a-fA-F]*)//' stdout]], [0],
 [[usable protocols: any
 chosen protocol: OpenFlow11
-OFPT_FLOW_MOD (OF1.1): ADD table:255 tcp,tp_src=123 out_port:5 actions=FLOOD
-OFPT_FLOW_MOD (OF1.1): ADD table:255 in_port=LOCAL,dl_vlan=9,dl_src=00:0a:e4:25:6b:b0 actions=drop
-OFPT_FLOW_MOD (OF1.1): ADD table:255 udp,dl_vlan_pcp=7 idle:5 actions=strip_vlan,output:0
-OFPT_FLOW_MOD (OF1.1): ADD table:255 tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1
-OFPT_FLOW_MOD (OF1.1): ADD table:255 udp,nw_src=192.168.0.3,tp_dst=53 actions=mod_nw_ecn:2,output:1
-OFPT_FLOW_MOD (OF1.1): ADD table:255 priority=60000 cookie:0x123456789abcdef hard:10 actions=CONTROLLER:65535
-OFPT_FLOW_MOD (OF1.1): ADD table:255 actions=note:41.42.43.00.00.00,note:00.01.02.03.04.05.06.07.00.00.00.00.00.00,note:00.00.00.00.00.00
-OFPT_FLOW_MOD (OF1.1): ADD table:255 ip actions=mod_nw_ttl:1,load:0xa04034d->NXM_OF_IP_SRC[]
-OFPT_FLOW_MOD (OF1.1): ADD table:255 sctp actions=drop
-OFPT_FLOW_MOD (OF1.1): ADD table:255 sctp actions=drop
-OFPT_FLOW_MOD (OF1.1): ADD table:255 in_port=0 actions=resubmit:0
-OFPT_FLOW_MOD (OF1.1): ADD table:255 actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
+OFPT_FLOW_MOD (OF1.1): ADD tcp,tp_src=123 out_port:5 actions=FLOOD
+OFPT_FLOW_MOD (OF1.1): ADD in_port=LOCAL,dl_vlan=9,dl_src=00:0a:e4:25:6b:b0 actions=drop
+OFPT_FLOW_MOD (OF1.1): ADD udp,dl_vlan_pcp=7 idle:5 actions=pop_vlan,output:0
+OFPT_FLOW_MOD (OF1.1): ADD tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1
+OFPT_FLOW_MOD (OF1.1): ADD udp,nw_src=192.168.0.3,tp_dst=53 actions=mod_nw_ecn:2,output:1
+OFPT_FLOW_MOD (OF1.1): ADD priority=60000 cookie:0x123456789abcdef hard:10 actions=CONTROLLER:65535
+OFPT_FLOW_MOD (OF1.1): ADD actions=note:41.42.43.00.00.00,note:00.01.02.03.04.05.06.07.00.00.00.00.00.00,note:00.00.00.00.00.00
+OFPT_FLOW_MOD (OF1.1): ADD ip actions=mod_nw_ttl:1,mod_nw_src:10.4.3.77
+OFPT_FLOW_MOD (OF1.1): ADD sctp actions=drop
+OFPT_FLOW_MOD (OF1.1): ADD sctp actions=drop
+OFPT_FLOW_MOD (OF1.1): ADD in_port=0 actions=resubmit:0
+OFPT_FLOW_MOD (OF1.1): ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
 ]])
 AT_CLEANUP
 
@@ -180,7 +203,7 @@ AT_SETUP([ovs-ofctl parse-flows (OpenFlow 1.2)])
 AT_DATA([flows.txt], [[
 # comment
 tcp,tp_src=123,actions=flood
-in_port=LOCAL dl_vlan=9 dl_src=00:0A:E4:25:6B:B0 actions=drop
+in_port=LOCAL dl_vlan=9 dl_src=00:0A:E4:25:6B:B0 actions=mod_vlan_vid:7,mod_vlan_pcp:2
 udp dl_vlan_pcp=7 idle_timeout=5 actions=strip_vlan output:0
 tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1
 udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1
@@ -189,6 +212,10 @@ actions=note:41.42.43,note:00.01.02.03.04.05.06.07,note
 ipv6,actions=set_field:fe80:0123:4567:890a:a6ba:dbff:fefe:59fa->ipv6_src
 sctp actions=set_field:3334->sctp_src
 sctp actions=set_field:4445->sctp_dst
+tcp actions=mod_tp_dst:1234
+udp actions=mod_tp_src:1111
+ip actions=mod_nw_src:10.1.1.2,mod_nw_dst:192.168.10.1,mod_nw_ttl:1,mod_nw_tos:16,mod_nw_ecn:2
+in_port=0 actions=mod_dl_src:11:22:33:44:55:66,mod_dl_dst:10:20:30:40:50:60
 in_port=0 actions=resubmit:0
 actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
 ]])
@@ -198,21 +225,64 @@ AT_CHECK([ovs-ofctl --protocols OpenFlow12 parse-flows flows.txt
 AT_CHECK([[sed 's/ (xid=0x[0-9a-fA-F]*)//' stdout]], [0],
 [[usable protocols: NXM,OXM
 chosen protocol: OXM-OpenFlow12
-OFPT_FLOW_MOD (OF1.2): ADD table:255 tcp,tp_src=123 actions=FLOOD
-OFPT_FLOW_MOD (OF1.2): ADD table:255 in_port=LOCAL,dl_vlan=9,dl_src=00:0a:e4:25:6b:b0 actions=drop
-OFPT_FLOW_MOD (OF1.2): ADD table:255 udp,dl_vlan_pcp=7 idle:5 actions=strip_vlan,output:0
-OFPT_FLOW_MOD (OF1.2): ADD table:255 tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1
-OFPT_FLOW_MOD (OF1.2): ADD table:255 udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1
-OFPT_FLOW_MOD (OF1.2): ADD table:255 priority=60000 cookie:0x123456789abcdef hard:10 actions=CONTROLLER:65535
-OFPT_FLOW_MOD (OF1.2): ADD table:255 actions=note:41.42.43.00.00.00,note:00.01.02.03.04.05.06.07.00.00.00.00.00.00,note:00.00.00.00.00.00
-OFPT_FLOW_MOD (OF1.2): ADD table:255 ipv6 actions=set_field:fe80:123:4567:890a:a6ba:dbff:fefe:59fa->ipv6_src
-OFPT_FLOW_MOD (OF1.2): ADD table:255 sctp actions=set_field:3334->sctp_src
-OFPT_FLOW_MOD (OF1.2): ADD table:255 sctp actions=set_field:4445->sctp_dst
-OFPT_FLOW_MOD (OF1.2): ADD table:255 in_port=0 actions=resubmit:0
-OFPT_FLOW_MOD (OF1.2): ADD table:255 actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
+OFPT_FLOW_MOD (OF1.2): ADD tcp,tp_src=123 actions=FLOOD
+OFPT_FLOW_MOD (OF1.2): ADD in_port=LOCAL,dl_vlan=9,dl_src=00:0a:e4:25:6b:b0 actions=set_field:4103->vlan_vid,set_field:2->vlan_pcp
+OFPT_FLOW_MOD (OF1.2): ADD udp,dl_vlan_pcp=7 idle:5 actions=pop_vlan,output:0
+OFPT_FLOW_MOD (OF1.2): ADD tcp,nw_src=192.168.0.3,tp_dst=80 actions=set_queue:37,output:1
+OFPT_FLOW_MOD (OF1.2): ADD udp,nw_src=192.168.0.3,tp_dst=53 actions=pop_queue,output:1
+OFPT_FLOW_MOD (OF1.2): ADD priority=60000 cookie:0x123456789abcdef hard:10 actions=CONTROLLER:65535
+OFPT_FLOW_MOD (OF1.2): ADD actions=note:41.42.43.00.00.00,note:00.01.02.03.04.05.06.07.00.00.00.00.00.00,note:00.00.00.00.00.00
+OFPT_FLOW_MOD (OF1.2): ADD ipv6 actions=set_field:fe80:123:4567:890a:a6ba:dbff:fefe:59fa->ipv6_src
+OFPT_FLOW_MOD (OF1.2): ADD sctp actions=set_field:3334->sctp_src
+OFPT_FLOW_MOD (OF1.2): ADD sctp actions=set_field:4445->sctp_dst
+OFPT_FLOW_MOD (OF1.2): ADD tcp actions=set_field:1234->tcp_dst
+OFPT_FLOW_MOD (OF1.2): ADD udp actions=set_field:1111->udp_src
+OFPT_FLOW_MOD (OF1.2): ADD ip actions=set_field:10.1.1.2->ip_src,set_field:192.168.10.1->ip_dst,mod_nw_ttl:1,set_field:4->ip_dscp,set_field:2->nw_ecn
+OFPT_FLOW_MOD (OF1.2): ADD in_port=0 actions=set_field:11:22:33:44:55:66->eth_src,set_field:10:20:30:40:50:60->eth_dst
+OFPT_FLOW_MOD (OF1.2): ADD in_port=0 actions=resubmit:0
+OFPT_FLOW_MOD (OF1.2): ADD actions=sample(probability=12345,collector_set_id=23456,obs_domain_id=34567,obs_point_id=45678)
 ]])
 AT_CLEANUP
 
+AT_SETUP([ovs-ofctl parse-flow with invalid mask])
+for test_case in \
+    'tun_tos 1/1' \
+    'tun_ttl 1/1' \
+    'skb_priority 1/1' \
+    'eth_type 0x1234/0x1' \
+    'dl_vlan 9/0x1' \
+    'dl_vlan_pcp 6/0x1' \
+    'vlan_pcp 5/0x1' \
+    'mpls mpls_label 5/0x1' \
+    'mpls mpls_tc 1/0x1' \
+    'mpls mpls_bos 1/0x1' \
+    'ip nw_proto 1/1' \
+    'ipv6 nw_proto 1/1' \
+    'ip nw_tos 0xf0/0xf0' \
+    'ipv6 nw_tos 0xf0/0xf0' \
+    'ip ip_dscp 0x3c/0xf0' \
+    'ipv6 ip_dscp 0x3c/0xf0' \
+    'ip nw_ecn 1/1' \
+    'ipv6 nw_ecn 1/1' \
+    'ip nw_ttl 5/1' \
+    'ipv6 nw_ttl 5/1' \
+    'arp arp_op 0/1' \
+    'icmp icmp_type 1/1' \
+    'icmp icmp_code 2/1' \
+    'icmp6 icmpv6_code 2/1'
+do
+    set $test_case
+    if test $# = 3; then
+        prereq=$1, field=$2 value=$3
+    else
+        prereq= field=$1 value=$2
+    fi
+    AT_CHECK_UNQUOTED([ovs-ofctl parse-flow "$prereq$field=$value,actions=drop"], [1], [],
+[ovs-ofctl: $value: invalid mask for field $field
+])
+done
+AT_CLEANUP
+
 AT_SETUP([ovs-ofctl action inconsistency (OpenFlow 1.1)])
 AT_CHECK([ovs-ofctl --protocols OpenFlow11 add-flow br0 'ip actions=mod_tp_dst:1234'
 ], [1], [stdout], [ovs-ofctl: actions are invalid with specified match (OFPBAC_MATCH_INCONSISTENT)
@@ -539,6 +609,13 @@ NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_TCP_DST_W(FDE0/ffff)
 NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_TCP_DST_W(FDE0/0000)
 NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(07) NXM_OF_TCP_DST(4231)
 
+# TCP flags
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_NX_TCP_FLAGS(0131)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_NX_TCP_FLAGS_W(00F0/0FF0)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_NX_TCP_FLAGS_W(01E2/ffff)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_NX_TCP_FLAGS_W(00E1/0000)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(07) NXM_NX_TCP_FLAGS(4321)
+
 # UDP source port
 NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(11) NXM_OF_UDP_SRC(8732)
 NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(11) NXM_OF_UDP_SRC_W(0132/01FF)
@@ -826,6 +903,13 @@ NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_DST(fde0)
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06)
 nx_pull_match() returned error OFPBMC_BAD_PREREQ
 
+# TCP flags
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_NX_TCP_FLAGS(0131)
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_NX_TCP_FLAGS_W(00f0/0ff0)
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_NX_TCP_FLAGS(01e2)
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
 # UDP source port
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11), NXM_OF_UDP_SRC(8732)
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11), NXM_OF_UDP_SRC_W(0132/01ff)
@@ -1509,15 +1593,16 @@ dnl Ignore tp_dst if not TCP/UDP/SCTP:
 0000 00 00 0800 00 16 00000000ffffffff 00000000ffffffff 0000 01bb dnl
 00000000 00 000000 0000000000000000ffffffffffffffff
 
-dnl mpls_label not yet supported:
-# bad ofp11_match: OFPBMC_BAD_TAG
+# mpls,mpls_label=284280
+# 64: 12 -> 00
+# 65: 34 -> 04
 0000 0058 00000000 000002f7 dnl
 000000000000ffffffffffff 000000000000ffffffffffff dnl
 0000 00 00 8847 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
 12345678 00 000000 0000000000000000ffffffffffffffff
 
-dnl mpls_tc not yet supported:
-# bad ofp11_match: OFPBMC_BAD_TAG
+# mplsm,mpls_tc=2
+# 68: 5a -> 02
 0000 0058 00000000 000001f7 dnl
 000000000000ffffffffffff 000000000000ffffffffffff dnl
 0000 00 00 8848 00 00 00000000ffffffff 00000000ffffffff 0000 0000 dnl
@@ -2380,3 +2465,62 @@ OFPT_HELLO (xid=0x1):
 00000000  01 00 00 0b 00 00 00 01-41 42 43                |........ABC     |
 ])
 AT_CLEANUP
+
+AT_SETUP([tcp flags - filtering])
+OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 \
+                    -- add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2])
+AT_DATA([flows.txt], [dnl
+       in_port=1,tcp,tp_dst=80,tcp_flags=0x02/0x17,action=2  # Allow outbound web traffic bare-SYN
+       in_port=1,tcp,tp_dst=80,tcp_flags=0x10/0x10,action=2  # Allow outbound web traffic with ACK bit
+       in_port=1,tcp,tp_dst=80,tcp_flags=0x04/0x04,action=2  # Allow outbound web traffic with RST bit
+       in_port=2,tcp,tp_src=80,tcp_flags=0x10/0x10,action=1  # Allow inbound web traffic with ACK bit
+       in_port=2,tcp,tp_src=80,tcp_flags=0x04/0x04,action=1  # Allow inbound web traffic with RST bit
+       priority=0,in_port=1,action=drop  # Default drop outbound
+       priority=0,in_port=2,action=drop  # Default drop inbound
+])
+
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+AT_CHECK([ovs-appctl dpif/show | tail -n +5], [0], [dnl
+               p1 1/1: (dummy)
+               p2 2/2: (dummy)
+])
+
+dnl Outbound web traffic with base-SYN
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=80),tcp_flags(0x002)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: 2
+])
+
+dnl Outbopund web traffic with ACK bit
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=80),tcp_flags(0x110)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: 2
+])
+
+dnl Outbound web traffic with RST bit
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=80),tcp_flags(0x104)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: 2
+])
+
+dnl Inbound web traffic with ACK bit
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=6,tos=0,ttl=64,frag=no),tcp(src=80,dst=8),tcp_flags(0x010)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: 1
+])
+
+dnl Inbound web traffic with RST bit
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=6,tos=0,ttl=64,frag=no),tcp(src=80,dst=8),tcp_flags(0x014)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: 1
+])
+
+dnl Inbound web traffic with SYN bit without ACK or RST bits
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=6,tos=0,ttl=64,frag=no),tcp(src=80,dst=8),tcp_flags(0xfeb)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: drop
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
index 1e04550..3f39f8f 100644 (file)
@@ -459,7 +459,7 @@ static void
 check_tables(const struct classifier *cls, int n_tables, int n_rules,
              int n_dups) OVS_REQ_RDLOCK(cls->rwlock)
 {
-    const struct cls_table *table;
+    const struct cls_subtable *table;
     struct test_rule *test_rule;
     struct cls_cursor cursor;
     int found_tables = 0;
@@ -467,7 +467,7 @@ check_tables(const struct classifier *cls, int n_tables, int n_rules,
     int found_dups = 0;
     int found_rules2 = 0;
 
-    HMAP_FOR_EACH (table, hmap_node, &cls->tables) {
+    HMAP_FOR_EACH (table, hmap_node, &cls->subtables) {
         const struct cls_rule *head;
         unsigned int max_priority = 0;
         unsigned int max_count = 0;
@@ -501,8 +501,8 @@ check_tables(const struct classifier *cls, int n_tables, int n_rules,
         assert(table->max_count == max_count);
     }
 
-    assert(found_tables == hmap_count(&cls->tables));
-    assert(n_tables == -1 || n_tables == hmap_count(&cls->tables));
+    assert(found_tables == hmap_count(&cls->subtables));
+    assert(n_tables == -1 || n_tables == hmap_count(&cls->subtables));
     assert(n_rules == -1 || found_rules == n_rules);
     assert(n_dups == -1 || found_dups == n_dups);
 
similarity index 77%
rename from utilities/ovs-controller.8.in
rename to tests/test-controller.8.in
index f29de6a..62bfa0f 100644 (file)
@@ -1,25 +1,38 @@
-.\" -*- nroff -*-
+                         .\" -*- nroff -*-
 .de IQ
 .  br
 .  ns
 .  IP "\\$1"
 ..
-.TH ovs\-controller 8 "@VERSION@" "Open vSwitch" "Open vSwitch Manual"
-.ds PN ovs\-controller
+.TH test\-controller 8 "@VERSION@" "Open vSwitch" "Open vSwitch Manual"
+.ds PN test\-controller
 .
 .SH NAME
-ovs\-controller \- simple OpenFlow controller reference implementation
+test\-controller \- simple OpenFlow controller for testing
 .
 .SH SYNOPSIS
-.B ovs\-controller
+.B test\-controller
 [\fIoptions\fR] \fImethod\fR \fB[\fImethod\fR]\&...
 .
 .SH DESCRIPTION
-\fBovs\-controller\fR manages any number of remote switches over OpenFlow
-protocol, causing them to function as L2 MAC-learning switches or hub.
 .PP
-\fBovs\-controller\fR controls one or more OpenFlow switches, specified as
-one or more of the following OpenFlow connection methods:
+\fBtest\-controller\fR is a simple OpenFlow controller.  It is very
+easy to set up, so it may be suitable for initial testing of
+connectivity between an OpenFlow switch and a controller.  It may also
+be useful for developer testing and debugging of some Open vSwitch
+features.
+.PP
+\fBtest\-controller\fR is not a general-purpose OpenFlow controller.
+It does not make sense to deploy it routinely or in production.
+\fBtest\-controller\fR does not provide any features that are not
+built into Open vSwitch, and lacks many that are built in to Open
+vSwitch, so adding it to an Open vSwitch deployment actually reduces
+functionality and performance while increasing latency.
+.PP
+\fBtest\-controller\fR manages any number of remote switches over
+OpenFlow protocol, causing them to function as L2 MAC-learning
+switches or hub.  The switches it controls are specified as one or
+more of the following OpenFlow connection methods:
 .
 .RS
 .so lib/vconn-passive.man
@@ -29,7 +42,7 @@ one or more of the following OpenFlow connection methods:
 .SH OPTIONS
 .IP "\fB\-n\fR"
 .IQ "\fB\-\-noflow\fR"
-By default, \fBovs\-controller\fR sets up a flow in each OpenFlow switch
+By default, \fBtest\-controller\fR sets up a flow in each OpenFlow switch
 whenever it receives a packet whose destination is known due through
 MAC learning.  This option disables flow setup, so that every packet
 in the network passes through the controller.
@@ -62,7 +75,7 @@ performance, so it should not be used in production.
 .
 .IP "\fB\-w\fR[\fIwildcard_mask\fR]"
 .IQ "\fB\-\-wildcards\fR[\fB=\fIwildcard_mask\fR]\fR"
-By default, \fBovs\-controller\fR sets up exact-match flows.  This
+By default, \fBtest\-controller\fR sets up exact-match flows.  This
 option allows it to set up wildcarded flows, which may reduce
 flow setup latency by causing less traffic to be sent up to the
 controller.
@@ -79,7 +92,7 @@ This option has no effect when \fB\-n\fR (or \fB\-\-noflow\fR) is in use
 .
 .IP "\fB\-N\fR"
 .IQ "\fB\-\-normal\fR"
-By default, \fBovs\-controller\fR directs packets to a particular port
+By default, \fBtest\-controller\fR directs packets to a particular port
 or floods them.  This option causes it to direct non-flooded packets
 to the OpenFlow \fBOFPP_NORMAL\fR port.  This allows the switch itself
 to make decisions about packet destinations.  Support for
@@ -87,7 +100,7 @@ to make decisions about packet destinations.  Support for
 with some non-Open vSwitch switches.
 .
 .IP "\fB\-\-mute\fR"
-Prevents ovs\-controller from replying to any OpenFlow messages sent
+Prevents test\-controller from replying to any OpenFlow messages sent
 to it by switches.
 .IP
 This option is only for debugging the Open vSwitch implementation of
@@ -95,7 +108,7 @@ This option is only for debugging the Open vSwitch implementation of
 .
 .IP "\fB\-q \fIid\fR"
 .IQ "\fB\-\-queue=\fIid\fR"
-By default, \fBovs\-controller\fR uses the default OpenFlow queue for
+By default, \fBtest\-controller\fR uses the default OpenFlow queue for
 sending packets and setting up flows.  Use one of these options,
 supplying \fIid\fR as an OpenFlow queue ID as a decimal number, to
 instead use that specific queue.
@@ -147,7 +160,7 @@ Use this option more than once to add flows from multiple files.
 To bind locally to port 6633 (the default) and wait for incoming
 connections from OpenFlow switches:
 .IP
-\fB% ovs\-controller ptcp:\fR
+\fB% test\-controller ptcp:\fR
 .PP
 In the future, the default port number will change to 6653, which is the
 IANA-defined value.
@@ -157,7 +170,7 @@ Configuring a Citrix XenServer to connect to a particular controller
 only points the remote OVSDB management connection to that controller.
 It does not also configure OpenFlow connections, because the manager
 is expected to do that over the management protocol.
-\fBovs\-controller\fR is not an Open vSwitch manager and does not know
+\fBtest\-controller\fR is not an Open vSwitch manager and does not know
 how to do that.
 .PP
 As a stopgap workaround, \fBovs\-vsctl\fR can wait for an OVSDB
index 4972685..d2baa66 100644 (file)
@@ -8,8 +8,6 @@
 /ovs-cfg-mod
 /ovs-cfg-mod.8
 /ovs-check-dead-ifs
-/ovs-controller
-/ovs-controller.8
 /ovs-ctl
 /ovs-dpctl
 /ovs-dpctl.8
index ff50a34..d26f961 100644 (file)
@@ -1,6 +1,5 @@
 bin_PROGRAMS += \
        utilities/ovs-appctl \
-       utilities/ovs-controller \
        utilities/ovs-dpctl \
        utilities/ovs-ofctl \
        utilities/ovs-vsctl
@@ -38,7 +37,6 @@ EXTRA_DIST += \
 MAN_ROOTS += \
        utilities/ovs-appctl.8.in \
        utilities/ovs-benchmark.1.in \
-       utilities/ovs-controller.8.in \
        utilities/ovs-ctl.8 \
        utilities/ovs-dpctl.8.in \
        utilities/ovs-dpctl-top.8.in \
@@ -58,7 +56,6 @@ DISTCLEANFILES += \
        utilities/ovs-ctl \
        utilities/ovs-benchmark.1 \
        utilities/ovs-check-dead-ifs \
-       utilities/ovs-controller.8 \
        utilities/ovs-dpctl.8 \
        utilities/ovs-dpctl-top \
        utilities/ovs-dpctl-top.8 \
@@ -83,7 +80,6 @@ DISTCLEANFILES += \
 man_MANS += \
        utilities/ovs-appctl.8 \
        utilities/ovs-benchmark.1 \
-       utilities/ovs-controller.8 \
        utilities/ovs-dpctl.8 \
        utilities/ovs-dpctl-top.8 \
        utilities/ovs-l3ping.8 \
@@ -101,9 +97,6 @@ dist_man_MANS += utilities/ovs-ctl.8
 utilities_ovs_appctl_SOURCES = utilities/ovs-appctl.c
 utilities_ovs_appctl_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
 
-utilities_ovs_controller_SOURCES = utilities/ovs-controller.c
-utilities_ovs_controller_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
-
 utilities_ovs_dpctl_SOURCES = utilities/ovs-dpctl.c
 utilities_ovs_dpctl_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
 
index 35b8aef..f4bf383 100755 (executable)
@@ -14,7 +14,7 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 #
 # Copyright (c) 2005, 2007 XenSource Ltd.
-# Copyright (c) 2010, 2011, 2012 Nicira, Inc.
+# Copyright (c) 2010, 2011, 2012, 2013 Nicira, Inc.
 
 #
 # To add new entries to the bugtool, you need to:
@@ -111,7 +111,6 @@ DHCP_LEASE_DIR = ['/var/lib/dhclient', '/var/lib/dhcp3']
 OPENVSWITCH_LOG_DIR = '@LOGDIR@/'
 OPENVSWITCH_DEFAULT_SWITCH = '/etc/default/openvswitch-switch' # Debian
 OPENVSWITCH_SYSCONFIG_SWITCH = '/etc/sysconfig/openvswitch'    # RHEL
-OPENVSWITCH_DEFAULT_CONTROLLER = '/etc/default/openvswitch-controller'
 OPENVSWITCH_CONF_DB = '@DBDIR@/conf.db'
 OPENVSWITCH_COMPACT_DB = '@DBDIR@/bugtool-compact-conf.db'
 OPENVSWITCH_VSWITCHD_PID = '@RUNDIR@/ovs-vswitchd.pid'
index 75ea43b..dc86619 100644 (file)
@@ -594,6 +594,7 @@ When \fBdl_type\fR is wildcarded or set to a value other than 0x0800,
 (see \fBFlow Syntax\fR above).
 .
 .IP \fBnw_proto=\fIproto\fR
+.IQ \fBip_proto=\fIproto\fR
 When \fBip\fR or \fBdl_type=0x0800\fR is specified, matches IP
 protocol type \fIproto\fR, which is specified as a decimal number
 between 0 and 255, inclusive (e.g. 1 to match ICMP packets or 6 to match
@@ -626,7 +627,16 @@ When \fBdl_type\fR is wildcarded or set to a value other than 0x0800 or
 0x86dd, the value of \fBnw_tos\fR is ignored (see \fBFlow Syntax\fR
 above).
 .
+.IP \fBip_dscp=\fIdscp\fR
+Matches IP ToS/DSCP or IPv6 traffic class field \fIdscp\fR, which is
+specified as a decimal number between 0 and 63, inclusive.
+.IP
+When \fBdl_type\fR is wildcarded or set to a value other than 0x0800 or
+0x86dd, the value of \fBip_dscp\fR is ignored (see \fBFlow Syntax\fR
+above).
+.
 .IP \fBnw_ecn=\fIecn\fR
+.IQ \fBip_ecn=\fIecn\fR
 Matches \fIecn\fR bits in IP ToS or IPv6 traffic class fields, which is
 specified as a decimal number between 0 and 3, inclusive.
 .IP
@@ -716,6 +726,40 @@ Like the exact-match forms of \fBtp_src\fR and \fBtp_dst\fR described
 above, the bitwise match forms apply only when \fBdl_type\fR and
 \fBnw_proto\fR specify TCP or UDP or SCTP.
 .
+.IP \fBtcp_flags=\fIflags\fB/\fImask\fR
+Bitwise match on TCP flags.  The \fIflags\fR and \fImask\fR are 16-bit
+numbers written in decimal or in hexadecimal prefixed by \fB0x\fR.
+Each 1-bit in \fImask\fR requires that the corresponding bit in
+\fIflags\fR must match.  Each 0-bit in \fImask\fR causes the corresponding
+bit to be ignored.
+.IP
+TCP protocol currently defines 9 flag bits, and additional 3 bits are
+reserved (must be transmitted as zero), see RFCs 793, 3168, and 3540.
+The flag bits are, numbering from the least significant bit:
+.RS
+.IP "\fB0: FIN\fR"
+No more data from sender.
+.IP "\fB1: SYN\fR"
+Synchronize sequence numbers.
+.IP "\fB2: RST\fR"
+Reset the connection.
+.IP "\fB3: PSH\fR"
+Push function.
+.IP "\fB4: ACK\fR"
+Acknowledgement field significant.
+.IP "\fB5: URG\fR"
+Urgent pointer field significant.
+.IP "\fB6: ECE\fR"
+ECN Echo.
+.IP "\fB7: CWR\fR"
+Congestion Windows Reduced.
+.IP "\fB8: NS\fR"
+Nonce Sum.
+.IP "\fB9-11:\fR"
+Reserved.
+.IP "\fB12-15:\fR"
+Not matchable, must be zero.
+.RE
 .IP \fBicmp_type=\fItype\fR
 .IQ \fBicmp_code=\fIcode\fR
 When \fBdl_type\fR and \fBnw_proto\fR specify ICMP or ICMPv6, \fItype\fR
@@ -726,23 +770,60 @@ When \fBdl_type\fR and \fBnw_proto\fR take other values, the values of
 these settings are ignored (see \fBFlow Syntax\fR above).
 .
 .IP \fBtable=\fInumber\fR
-If specified, limits the flow manipulation and flow dump commands to
-only apply to the table with the given \fInumber\fR between 0 and 254.
-.
-Behavior varies if \fBtable\fR is not specified (equivalent to
-specifying 255 as \fInumber\fR).  For flow table
-modification commands without \fB\-\-strict\fR, the switch will choose
-the table for these commands to operate on.  For flow table
-modification commands with \fB\-\-strict\fR, the command will operate
-on any single matching flow in any table; it will do nothing if there
-are matches in more than one table.  The \fBdump-flows\fR and
-\fBdump-aggregate\fR commands will gather statistics about flows from
-all tables.
-.IP
-When this field is specified in \fBadd-flow\fR, \fBadd-flows\fR,
-\fBmod-flows\fR and \fBdel-flows\fR commands, it activates a Nicira
-extension to OpenFlow, which as of this writing is only known to be
-implemented by Open vSwitch.
+For flow dump commands, limits the flows dumped to those in the table
+with the given \fInumber\fR between 0 and 254.  If not specified (or if
+255 is specified as \fInumber\fR), then flows in all tables are
+dumped.
+.
+.IP
+For flow table modification commands, behavior varies based on the
+OpenFlow version used to connect to the switch:
+.
+.RS
+.IP "OpenFlow 1.0"
+OpenFlow 1.0 does not support \fBtable\fR for modifying flows.
+\fBovs\-ofctl\fR will exit with an error if \fBtable\fR (other than
+\fBtable=255\fR) is specified for a switch that only supports OpenFlow
+1.0.
+.IP
+In OpenFlow 1.0, the switch chooses the table into which to insert a
+new flow.  The Open vSwitch software switch always chooses table 0.
+Other Open vSwitch datapaths and other OpenFlow implementations may
+choose different tables.
+.IP
+The OpenFlow 1.0 behavior in Open vSwitch for modifying or removing
+flows depends on whether \fB\-\-strict\fR is used.  Without
+\fB\-\-strict\fR, the command applies to matching flows in all tables.
+With \fB\-\-strict\fR, the command will operate on any single matching
+flow in any table; it will do nothing if there are matches in more
+than one table.  (The distinction between these behaviors only matters
+if non-OpenFlow 1.0 commands were also used, because OpenFlow 1.0
+alone cannot add flows with the same matching criteria to multiple
+tables.)
+.
+.IP "OpenFlow 1.0 with table_id extension"
+Open vSwitch implements an OpenFlow extension that allows the
+controller to specify the table on which to operate.  \fBovs\-ofctl\fR
+automatically enables the extension when \fBtable\fR is specified and
+OpenFlow 1.0 is used.  \fBovs\-ofctl\fR automatically detects whether
+the switch supports the extension.  As of this writing, this extension
+is only known to be implemented by Open vSwitch.
+.
+.IP
+With this extension, \fBovs\-ofctl\fR operates on the requested table
+when \fBtable\fR is specified, and acts as described for OpenFlow 1.0
+above when no \fBtable\fR is specified (or for \fBtable=255\fR).
+.
+.IP "OpenFlow 1.1"
+OpenFlow 1.1 requires flow table modification commands to specify a
+table.  When \fBtable\fR is not specified (or \fBtable=255\fR is
+specified), \fBovs\-ofctl\fR defaults to table 0.
+.
+.IP "OpenFlow 1.2 and later"
+OpenFlow 1.2 and later allow flow deletion commands, but not other
+flow table modification commands, to operate on all flow tables, with
+the behavior described above for OpenFlow 1.0.
+.RE
 .
 .IP \fBmetadata=\fIvalue\fR[\fB/\fImask\fR]
 Matches \fIvalue\fR either exactly or with optional \fImask\fR in the metadata
@@ -886,6 +967,7 @@ address option.  An address is specified as 6 pairs of hexadecimal
 digits delimited by colons.
 .
 .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
 over a tunnel that carries a key (e.g. GRE with the RFC 2890 key
 extension and a nonzero key value) will have a nonzero tunnel ID.
@@ -1947,6 +2029,5 @@ Prints the flow entries in the switch.
 .SH "SEE ALSO"
 .
 .BR ovs\-appctl (8),
-.BR ovs\-controller (8),
 .BR ovs\-vswitchd (8)
 .BR ovs\-vswitchd.conf.db (8)
index 1a1d423..a0dc5c8 100644 (file)
@@ -310,6 +310,7 @@ usage(void)
            "  dump-group-features SWITCH  print group features\n"
            "  dump-groups SWITCH          print group description\n"
            "  dump-group-stats SWITCH [GROUP]  print group statistics\n"
+           "  queue-get-config SWITCH PORT  print queue information for port\n"
            "  add-meter SWITCH METER      add meter described by METER\n"
            "  mod-meter SWITCH METER      modify specific METER\n"
            "  del-meter SWITCH METER      delete METER\n"
@@ -1048,6 +1049,26 @@ ofctl_queue_stats(int argc, char *argv[])
     vconn_close(vconn);
 }
 
+static void
+ofctl_queue_get_config(int argc OVS_UNUSED, char *argv[])
+{
+    const char *vconn_name = argv[1];
+    const char *port_name = argv[2];
+    enum ofputil_protocol protocol;
+    enum ofp_version version;
+    struct ofpbuf *request;
+    struct vconn *vconn;
+    ofp_port_t port;
+
+    port = str_to_port_no(vconn_name, port_name);
+
+    protocol = open_vconn(vconn_name, &vconn);
+    version = ofputil_protocol_to_ofp_version(protocol);
+    request = ofputil_encode_queue_get_config_request(version, port);
+    dump_transaction(vconn, request);
+    vconn_close(vconn);
+}
+
 static enum ofputil_protocol
 open_vconn_for_flow_mod(const char *remote, struct vconn **vconnp,
                         enum ofputil_protocol usable_protocols)
@@ -2792,7 +2813,8 @@ ofctl_parse_ofp10_actions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
         /* Convert to ofpacts. */
         ofpbuf_init(&ofpacts, 0);
         size = of10_in.size;
-        error = ofpacts_pull_openflow10(&of10_in, of10_in.size, &ofpacts);
+        error = ofpacts_pull_openflow_actions(&of10_in, of10_in.size,
+                                              OFP10_VERSION, &ofpacts);
         if (error) {
             printf("bad OF1.1 actions: %s\n\n", ofperr_get_name(error));
             ofpbuf_uninit(&ofpacts);
@@ -2810,7 +2832,8 @@ ofctl_parse_ofp10_actions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 
         /* Convert back to ofp10 actions and print differences from input. */
         ofpbuf_init(&of10_out, 0);
-        ofpacts_put_openflow10(ofpacts.data, ofpacts.size, &of10_out);
+        ofpacts_put_openflow_actions(ofpacts.data, ofpacts.size, &of10_out,
+                                     OFP10_VERSION);
 
         print_differences("", of10_in.data, of10_in.size,
                           of10_out.data, of10_out.size);
@@ -2978,8 +3001,8 @@ ofctl_parse_ofp11_actions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
         /* Convert to ofpacts. */
         ofpbuf_init(&ofpacts, 0);
         size = of11_in.size;
-        error = ofpacts_pull_openflow11_actions(&of11_in, OFP11_VERSION,
-                                                of11_in.size, &ofpacts);
+        error = ofpacts_pull_openflow_actions(&of11_in, of11_in.size,
+                                              OFP11_VERSION, &ofpacts);
         if (error) {
             printf("bad OF1.1 actions: %s\n\n", ofperr_get_name(error));
             ofpbuf_uninit(&ofpacts);
@@ -2997,7 +3020,8 @@ ofctl_parse_ofp11_actions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 
         /* Convert back to ofp11 actions and print differences from input. */
         ofpbuf_init(&of11_out, 0);
-        ofpacts_put_openflow11_actions(ofpacts.data, ofpacts.size, &of11_out);
+        ofpacts_put_openflow_actions(ofpacts.data, ofpacts.size, &of11_out,
+                                     OFP11_VERSION);
 
         print_differences("", of11_in.data, of11_in.size,
                           of11_out.data, of11_out.size);
@@ -3047,14 +3071,15 @@ ofctl_parse_ofp11_instructions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
         /* Convert to ofpacts. */
         ofpbuf_init(&ofpacts, 0);
         size = of11_in.size;
-        error = ofpacts_pull_openflow11_instructions(&of11_in, OFP11_VERSION,
-                                                     of11_in.size, &ofpacts);
+        error = ofpacts_pull_openflow_instructions(&of11_in, of11_in.size,
+                                                   OFP11_VERSION, &ofpacts);
         if (!error) {
             /* Verify actions, enforce consistency. */
             struct flow flow;
             memset(&flow, 0, sizeof flow);
-            error = ofpacts_check(ofpacts.data, ofpacts.size, &flow, OFPP_MAX,
-                                  table_id ? atoi(table_id) : 0, true);
+            error = ofpacts_check(ofpacts.data, ofpacts.size, &flow,
+                                  true, OFPP_MAX,
+                                  table_id ? atoi(table_id) : 0, 255);
         }
         if (error) {
             printf("bad OF1.1 instructions: %s\n\n", ofperr_get_name(error));
@@ -3074,8 +3099,8 @@ ofctl_parse_ofp11_instructions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
         /* Convert back to ofp11 instructions and print differences from
          * input. */
         ofpbuf_init(&of11_out, 0);
-        ofpacts_put_openflow11_instructions(ofpacts.data, ofpacts.size,
-                                            &of11_out);
+        ofpacts_put_openflow_instructions(ofpacts.data, ofpacts.size,
+                                          &of11_out, OFP13_VERSION);
 
         print_differences("", of11_in.data, of11_in.size,
                           of11_out.data, of11_out.size);
@@ -3326,6 +3351,7 @@ static const struct command all_commands[] = {
     { "dump-flows", 1, 2, ofctl_dump_flows },
     { "dump-aggregate", 1, 2, ofctl_dump_aggregate },
     { "queue-stats", 1, 3, ofctl_queue_stats },
+    { "queue-get-config", 2, 2, ofctl_queue_get_config },
     { "add-flow", 2, 2, ofctl_add_flow },
     { "add-flows", 2, 2, ofctl_add_flows },
     { "mod-flows", 2, 2, ofctl_mod_flows },
index 6d042b4..9c3019b 100644 (file)
@@ -236,7 +236,3 @@ Sets the log file to \fIfile\fR.  Default:
 .IP "\fB\-h\fR"
 .IQ "\fB\-\^\-help\fR"
 Prints a help usage message and exits.
-
-.SH "SEE ALSO"
-
-.BR ovs\-controller (8).
index bdeb348..2223d04 100644 (file)
@@ -13,8 +13,6 @@
 .TH ovs\-vsctl 8 "@VERSION@" "Open vSwitch" "Open vSwitch Manual"
 .\" This program's name:
 .ds PN ovs\-vsctl
-.\" SSL peer program's name:
-.ds SN ovsdb\-server
 .
 .SH NAME
 ovs\-vsctl \- utility for querying and configuring \fBovs\-vswitchd\fR
@@ -468,9 +466,7 @@ for bootstrapping.
 .PP
 This option is only useful if the controller sends its CA certificate
 as part of the SSL certificate chain.  The SSL protocol does not
-require the controller to send the CA certificate, but
-\fBovs\-controller\fR(8) can be configured to do so with the
-\fB\-\-peer\-ca\-cert\fR option.
+require the controller to send the CA certificate.
 .
 .SS "Database Commands"
 .
index ec3633c..fecae60 100644 (file)
@@ -376,6 +376,7 @@ bridge_init(const char *remote)
     ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_cfm_fault);
     ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_cfm_fault_status);
     ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_cfm_remote_mpids);
+    ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_cfm_flap_count);
     ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_cfm_health);
     ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_cfm_remote_opstate);
     ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_bfd_status);
@@ -1890,11 +1891,13 @@ iface_refresh_cfm_stats(struct iface *iface)
         ovsrec_interface_set_cfm_fault(cfg, NULL, 0);
         ovsrec_interface_set_cfm_fault_status(cfg, NULL, 0);
         ovsrec_interface_set_cfm_remote_opstate(cfg, NULL);
+        ovsrec_interface_set_cfm_flap_count(cfg, NULL, 0);
         ovsrec_interface_set_cfm_health(cfg, NULL, 0);
         ovsrec_interface_set_cfm_remote_mpids(cfg, NULL, 0);
     } else {
         const char *reasons[CFM_FAULT_N_REASONS];
         int64_t cfm_health = status.health;
+        int64_t cfm_flap_count = status.flap_count;
         bool faulted = status.faults != 0;
         size_t i, j;
 
@@ -1909,6 +1912,8 @@ iface_refresh_cfm_stats(struct iface *iface)
         }
         ovsrec_interface_set_cfm_fault_status(cfg, (char **) reasons, j);
 
+        ovsrec_interface_set_cfm_flap_count(cfg, &cfm_flap_count, 1);
+
         if (status.remote_opstate >= 0) {
             const char *remote_opstate = status.remote_opstate ? "up" : "down";
             ovsrec_interface_set_cfm_remote_opstate(cfg, remote_opstate);
index 4fb3ae3..0dd091f 100644 (file)
@@ -7,8 +7,6 @@
 .TH ovs\-vswitchd 8 "@VERSION@" "Open vSwitch" "Open vSwitch Manual"
 .\" This program's name:
 .ds PN ovs\-vswitchd
-.\" SSL peer program's name:
-.ds SN ovs\-controller
 .
 .SH NAME
 ovs\-vswitchd \- Open vSwitch daemon
index 538dad3..78ebc89 100644 (file)
@@ -1,6 +1,6 @@
 {"name": "Open_vSwitch",
  "version": "7.3.0",
- "cksum": "2483452374 20182",
+ "cksum": "2811681289 20311",
  "tables": {
    "Open_vSwitch": {
      "columns": {
            "min": 0,
            "max": "unlimited"},
          "ephemeral": true},
+       "cfm_flap_count": {
+         "type": {
+           "key": {"type": "integer"},
+           "min": 0,
+           "max": 1}},
        "cfm_fault": {
          "type": {
            "key": { "type": "boolean"},
index c12fd8f..52924ee 100644 (file)
     </group>
 
     <group title="Bidirectional Forwarding Detection (BFD)">
-        <p>
-            BFD, defined in RFC 5880 and RFC 5881, allows point to point
-            detection of connectivity failures by occasional transmission of
-            BFD control messages.  It is implemented in Open vSwitch to serve
-            as a more popular and standards compliant alternative to CFM.
-        </p>
-
-        <p>
-            BFD operates by regularly transmitting BFD control messages at a
-            rate negotiated independently in each direction.  Each endpoint
-            specifies the rate at which it expects to receive control messages,
-            and the rate at which it's willing to transmit them.  Open vSwitch
-            uses a detection multiplier of three, meaning that an endpoint
-            which fails to receive BFD control messages for a period of three
-            times the expected reception rate, will signal a connectivity
-            fault.  In the case of a unidirectional connectivity issue, the
-            system not receiving BFD control messages will signal the problem
-            to its peer in the messages it transmits.
-        </p>
-
-        <p>
-            The Open vSwitch implementation of BFD aims to comply faithfully
-            with the requirements put forth in RFC 5880.  Currently, the only
-            known omission is ``Demand Mode'', which we hope to include in
-            future.  Open vSwitch does not implement the optional
-            Authentication or ``Echo Mode'' features.
-        </p>
-
-      <column name="bfd" key="enable">
-          When <code>true</code> BFD is enabled on this
-          <ref table="Interface"/>, otherwise it's disabled.  Defaults to
-          <code>false</code>.
-      </column>
-
-      <column name="bfd" key="min_rx"
-          type='{"type": "integer", "minInteger": 1}'>
-          The fastest rate, in milliseconds, at which this BFD session is
-          willing to receive BFD control messages.  The actual rate may be
-          slower if the remote endpoint isn't willing to transmit as quickly as
-          specified.  Defaults to <code>1000</code>.
-      </column>
-
-      <column name="bfd" key="min_tx"
-          type='{"type": "integer", "minInteger": 1}'>
-          The fastest rate, in milliseconds, at which this BFD session is
-          willing to transmit BFD control messages.  The actual rate may be
-          slower if the remote endpoint isn't willing to receive as quickly as
-          specified.  Defaults to <code>100</code>.
-      </column>
-
-      <column name="bfd" key="decay_min_rx" type='{"type": "integer"}'>
-          <code>decay_min_rx</code> is used to set the <code>min_rx</code>,
-          when there is no obvious incoming data traffic at the interface.
-          It cannot be set less than the <code>min_rx</code>. The decay feature
-          is disabled by setting the <code>decay_min_rx</code> to 0. And the
-          feature is reset everytime itself or <code>min_rx</code> is
-          reconfigured.
-      </column>
-
-      <column name="bfd" key="forwarding_if_rx" type='{"type": "boolean"}'>
-          When <code>forwarding_if_rx</code> is true the interface will be
-          considered capable of packet I/O as long as there is packet
-          received at interface.  This is important in that when link becomes
-          temporarily conjested, consecutive BFD control packets can be lost.
-          And the <code>forwarding_if_rx</code> can prevent link failover by
-          detecting non-control packets received at interface.
-      </column>
-
-      <column name="bfd" key="cpath_down" type='{"type": "boolean"}'>
-          Concatenated path down may be used when the local system should not
-          have traffic forwarded to it for some reason other than a connectivty
-          failure on the interface being monitored.  When a controller thinks
-          this may be the case, it may set <code>cpath_down</code> to
-          <code>true</code> which may cause the remote BFD session not to
-          forward traffic to this <ref table="Interface"/>. Defaults to
-          <code>false</code>.
-      </column>
-
-      <column name="bfd" key="check_tnl_key" type='{"type": "boolean"}'>
-          When set to true, Check Tunnel Key will make BFD only accept control
-          messages with an <code>in_key</code> of zero. Defaults to
-          <code>false</code>.
-      </column>
-
-      <column name="bfd" key="bfd_dst_mac">
-        An Ethernet address in the form
-        <var>xx</var>:<var>xx</var>:<var>xx</var>:<var>xx</var>:<var>xx</var>:<var>xx</var>
-        to set the destination mac address of the bfd packet. If this
-        field is set, it is assumed that all the bfd packets destined to this
-        interface also has the same destination mac address. If not set, a
-        default value of <code>00:23:20:00:00:01</code> is used.
-      </column>
-
-      <column name="bfd_status" key="state"
-          type='{"type": "string",
-          "enum": ["set", ["admin_down", "down", "init", "up"]]}'>
-          State of the BFD session.  The BFD session is fully healthy and
-          negotiated if <code>UP</code>.
-      </column>
+      <p>
+       BFD, defined in RFC 5880 and RFC 5881, allows point-to-point
+       detection of connectivity failures by occasional transmission of
+       BFD control messages.  Open vSwitch implements BFD to serve
+       as a more popular and standards compliant alternative to CFM.
+      </p>
 
-      <column name="bfd_status" key="forwarding" type='{"type": "boolean"}'>
-          True if the BFD session believes this <ref table="Interface"/> may be
-          used to forward traffic.  Typically this means the local session is
-          signaling <code>UP</code>, and the remote system isn't signaling a
-          problem such as concatenated path down.
-      </column>
+      <p>
+       BFD operates by regularly transmitting BFD control messages at a rate
+       negotiated independently in each direction.  Each endpoint specifies
+       the rate at which it expects to receive control messages, and the rate
+       at which it is willing to transmit them.  Open vSwitch uses a detection
+       multiplier of three, meaning that an endpoint signals a connectivity
+       fault if three consecutive BFD control messages fail to arrive.  In the
+       case of a unidirectional connectivity issue, the system not receiving
+       BFD control messages signals the problem to its peer in the messages it
+       transmits.
+      </p>
 
-      <column name="bfd_status" key="diagnostic">
-          A short message indicating what the BFD session thinks is wrong in
-          case of a problem.
-      </column>
+      <p>
+       The Open vSwitch implementation of BFD aims to comply faithfully
+       with RFC 5880 requirements.  Open vSwitch does not implement the
+       optional Authentication or ``Echo Mode'' features.
+      </p>
 
-      <column name="bfd_status" key="remote_state"
-          type='{"type": "string",
-          "enum": ["set", ["admin_down", "down", "init", "up"]]}'>
-          State of the remote endpoint's BFD session.
-      </column>
+      <group title="BFD Configuration">
+       <p>
+         A controller sets up key-value pairs in the <ref column="bfd"/>
+         column to enable and configure BFD.
+       </p>
+
+       <column name="bfd" key="enable" type='{"type": "boolean"}'>
+          True to enable BFD on this <ref table="Interface"/>.
+       </column>
+
+       <column name="bfd" key="min_rx"
+               type='{"type": "integer", "minInteger": 1}'>
+          The shortest interval, in milliseconds, at which this BFD session
+          offers to receive BFD control messages.  The remote endpoint may
+          choose to send messages at a slower rate.  Defaults to
+          <code>1000</code>.
+       </column>
+
+       <column name="bfd" key="min_tx"
+               type='{"type": "integer", "minInteger": 1}'>
+          The shortest interval, in milliseconds, at which this BFD session is
+          willing to transmit BFD control messages.  Messages will actually be
+          transmitted at a slower rate if the remote endpoint is not willing to
+          receive as quickly as specified.  Defaults to <code>100</code>.
+       </column>
+
+       <column name="bfd" key="decay_min_rx" type='{"type": "integer"}'>
+         An alternate receive interval, in milliseconds, that must be greater
+         than or equal to <ref column="bfd" key="min_rx"/>.  The
+         implementation switches from <ref column="bfd" key="min_rx"/> to <ref
+         column="bfd" key="decay_min_rx"/> when there is no obvious incoming
+         data traffic at the interface, to reduce the CPU and bandwidth cost
+         of monitoring an idle interface.  This feature may be disabled by
+         setting a value of 0.  This feature is reset whenever <ref
+         column="bfd" key="decay_min_rx"/> or <ref column="bfd" key="min_rx"/>
+         changes.
+       </column>
+
+       <column name="bfd" key="forwarding_if_rx" type='{"type": "boolean"}'>
+          True to consider the interface capable of packet I/O as long as it
+          continues to receive any packets (not just BFD packets).  This
+          prevents link congestion that causes consecutive BFD control packets
+          to be lost from marking the interface down.
+       </column>
+
+       <column name="bfd" key="cpath_down" type='{"type": "boolean"}'>
+         Set to true to notify the remote endpoint that traffic should not be
+         forwarded to this system for some reason other than a connectivty
+         failure on the interface being monitored.  The typical underlying
+         reason is ``concatenated path down,'' that is, that connectivity
+         beyond the local system is down.  Defaults to false.
+       </column>
+
+       <column name="bfd" key="check_tnl_key" type='{"type": "boolean"}'>
+          Set to true to make BFD accept only control messages with a tunnel
+          key of zero.  By default, BFD accepts control messages with any
+          tunnel key.
+       </column>
+
+       <column name="bfd" key="bfd_dst_mac">
+         Set to an Ethernet address in the form
+         <var>xx</var>:<var>xx</var>:<var>xx</var>:<var>xx</var>:<var>xx</var>:<var>xx</var>
+         to set the MAC used as destination for transmitted BFD packets and
+         expected as destination for received BFD packets.  The default is
+         <code>00:23:20:00:00:01</code>.
+       </column>
+      </group>
 
-      <column name="bfd_status" key="remote_diagnostic">
-          A short message indicating what the remote endpoint's BFD session
-          thinks is wrong in case of a problem.
-      </column>
+      <group title="BFD Status">
+       <p>
+         The switch sets key-value pairs in the <ref column="bfd_status"/>
+         column to report the status of BFD on this interface.  When BFD is
+         not enabled, with <ref column="bfd" key="enable"/>, the switch clears
+         all key-value pairs from <ref column="bfd_status"/>.
+       </p>
+
+       <column name="bfd_status" key="state"
+               type='{"type": "string",
+                     "enum": ["set", ["admin_down", "down", "init", "up"]]}'>
+         Reports the state of the BFD session.  The BFD session is fully
+         healthy and negotiated if <code>UP</code>.
+       </column>
+
+       <column name="bfd_status" key="forwarding" type='{"type": "boolean"}'>
+         Reports whether the BFD session believes this <ref
+         table="Interface"/> may be used to forward traffic.  Typically this
+         means the local session is signaling <code>UP</code>, and the remote
+         system isn't signaling a problem such as concatenated path down.
+       </column>
+
+       <column name="bfd_status" key="diagnostic">
+         In case of a problem, set to a short message that reports what the
+         local BFD session thinks is wrong.
+       </column>
+
+       <column name="bfd_status" key="remote_state"
+               type='{"type": "string",
+                     "enum": ["set", ["admin_down", "down", "init", "up"]]}'>
+         Reports the state of the remote endpoint's BFD session.
+       </column>
+
+       <column name="bfd_status" key="remote_diagnostic">
+         In case of a problem, set to a short message that reports what the
+         remote endpoint's BFD session thinks is wrong.
+       </column>
+      </group>
     </group>
 
     <group title="Connectivity Fault Management">
         CFM on this <ref table="Interface"/>.
       </column>
 
+      <column name="cfm_flap_count">
+        Counts the number of cfm fault flapps since boot.  A flap is
+        considered to be a change of the <ref column="cfm_fault"/> value.
+      </column>
+
       <column name="cfm_fault">
         <p>
           Indicates a connectivity fault triggered by an inability to receive
index 57bfa65..5ffcc62 100644 (file)
@@ -13,8 +13,6 @@
 .TH vtep\-ctl 8 "March 2013" "Open vSwitch" "Open vSwitch Manual"
 .\" This program's name:
 .ds PN vtep\-ctl
-.\" SSL peer program's name:
-.ds SN ovsdb\-server
 .
 .SH NAME
 vtep\-ctl \- utility for querying and configuring a VTEP database
index d03d96d..017183a 100644 (file)
@@ -1,6 +1,6 @@
 {
   "name": "hardware_vtep",
-  "cksum": "825115144 5318",
+  "cksum": "1365749839 5604",
   "tables": {
     "Global": {
       "columns": {
         "management_ips": {
          "type": {"key": {"type": "string"}, "min": 0, "max": "unlimited"}},
         "tunnel_ips": {
-         "type": {"key": {"type": "string"}, "min": 0, "max": "unlimited"}}},
+         "type": {"key": {"type": "string"}, "min": 0, "max": "unlimited"}},
+        "switch_fault_status": {
+          "type": {
+            "key": "string", "min": 0, "max": "unlimited"},
+           "ephemeral": true}},
       "indexes": [["name"]]},
     "Physical_Port": {
       "columns": {
                           "minInteger": 0, "maxInteger": 4095},
                   "value": {"type": "uuid",
                             "refTable": "Logical_Binding_Stats"},
-                  "min": 0, "max": "unlimited"}}}},
+                  "min": 0, "max": "unlimited"}},
+        "port_fault_status": {
+          "type": {
+            "key": "string", "min": 0, "max": "unlimited"},
+           "ephemeral": true}}},
     "Logical_Binding_Stats": {
       "columns": {
         "bytes_from_local": {"type": "integer"},
           "ephemeral": true}},
       "indexes": [["target"]],
       "isRoot": false}},
-  "version": "1.0.0"}
+  "version": "1.1.0"}
index 9fd7495..db8d408 100644 (file)
        banner.
       </column>
     </group>
+    <group title="Error Notification">
+      <p>
+       An entry in this column indicates to the NVC that this switch
+       has encountered a fault. The switch must clear this column
+       when the fault has been cleared.
+      </p>
+
+      <column name="switch_fault_status" key="mac_table_exhaustion">
+        Indicates that the switch has been unable to process MAC
+        entries requested by the NVC due to lack of table resources.
+      </column>
+
+      <column name="switch_fault_status" key="tunnel_exhaustion">
+        Indicates that the switch has been unable to create tunnels
+        requested by the NVC due to lack of resources.
+      </column>
+
+      <column name="switch_fault_status" key="unspecified_fault">
+        Indicates that an error has occurred in the switch but that no
+        more specific information is available.
+      </column>
+
+    </group>
   </table>
 
   <table name="Physical_Port" title="A port within a physical switch.">
        An extended description for the port.
       </column>
     </group>
+    <group title="Error Notification">
+      <p>
+       An entry in this column indicates to the NVC that the physical port has
+       encountered a fault. The switch must clear this column when the errror
+       has been cleared.
+      </p>
+      <column name="port_fault_status" key="invalid_vlan_map">
+       <p>
+         Indicates that a VLAN-to-logical-switch mapping requested by
+         the controller could not be instantiated by the switch
+         because of a conflict with local configuration.
+       </p>
+      </column>
+      <column name="port_fault_status" key="unspecified_fault">
+       <p>
+         Indicates that an error has occurred on the port but that no
+         more specific information is available.
+       </p>
+      </column>
+    </group>
+
   </table>
 
   <table name="Logical_Binding_Stats" title="Statistics for a VLAN on a physical port bound to a logical network.">
     </p>
 
     <column name="MAC">
-      A MAC address that has been learned by the NSC.
+      A MAC address that has been learned by the NVC.
     </column>
 
     <column name="logical_switch">
 
     <column name="MAC">
       <p>
-       A MAC address that has been learned by the NSC.
+       A MAC address that has been learned by the NVC.
       </p>
       <p>
        The keyword <code>unknown-dst</code> is used as a special
         encapsulations to be introduced later.
       </p>
     </column>
+
     <group title="Bidirectional Forwarding Detection (BFD)">
       <p>
        BFD, defined in RFC 5880, allows point to point detection of
        connectivity failures by occasional transmission of BFD control
-       messages.
+       messages. VTEPs are expected to implement BFD.
       </p>
       
       <p>
        specifies the rate at which it expects to receive control messages,
        and the rate at which it's willing to transmit them.  An endpoint
        which fails to receive BFD control messages for a period of three
-       times the expected reception rate, will signal a connectivity
+       times the expected reception rate will signal a connectivity
        fault.  In the case of a unidirectional connectivity issue, the
        system not receiving BFD control messages will signal the problem
-       to its peer in the messages is transmists.
+       to its peer in the messages it transmits.
       </p>
       
-      <column name="bfd" key="min_rx">
-       The minimum rate, in milliseconds, at which this BFD session is
-       willing to receive BFD control messages.  The actual rate may slower
-       if the remote endpoint isn't willing to transmit as quickly as
-       specified.  Defaults to <code>1000</code>.
-      </column>
-      
-      <column name="bfd" key="min_tx">
-       The minimum rate, in milliseconds, at which this BFD session is
-       willing to transmit BFD control messages.  The actual rate may be
-       slower if the remote endpoint isn't willing to receive as quickly as
-       specified.  Defaults to <code>100</code>.
-      </column>
-      
-      <column name="bfd" key="cpath_down">
-       Concatenated path down may be used when the local system should not
-       have traffic forwarded to it for some reason other than a connectivty
-       failure on the interface being monitored.  The local BFD session will
-       notify the remote session of the connectivity problem, at which time
-       the remote session may choose not to forward traffic.  Defaults to
-       <code>false</code>.
-      </column>
-      
-      <column name="bfd_status" key="state">
-       State of the BFD session.  One of <code>ADMIN_DOWN</code>,
-       <code>DOWN</code>, <code>INIT</code>, or <code>UP</code>.
-      </column>
-      
-      <column name="bfd_status" key="forwarding">
-       True if the BFD session believes this <ref table="Physical_Locator"/> may be
-       used to forward traffic.  Typically this means the local session is
-       up, and the remote system isn't signalling a problem such as
-       concatenated path down.
-      </column>
-      
-      <column name="bfd_status" key="diagnostic">
-       A short message indicating what the BFD session thinks is wrong in
-       case of a problem.
-      </column>
-      
-      <column name="bfd_status" key="remote state">
-       State of the remote endpoint's BFD session.
-      </column>
-      
-      <column name="bfd_status" key="remote diagnostic">
-       A short message indicating what the remote endpoint's BFD session
-       thinks is wrong in case of a problem.
-      </column>
+      <p>
+       A hardware VTEP is expected to use BFD to determine reachability of
+       devices at the end of the tunnels with which it exchanges data. This
+       can enable the VTEP to choose a functioning service node among a set of
+       service nodes providing high availability. It also enables the NVC to
+       report the health status of tunnels.
+      </p>
+
+      <p>
+       In most cases the BFD peer of a hardware VTEP will be an Open vSwitch
+       instance. The Open vSwitch implementation of BFD aims to comply
+       faithfully with the requirements put forth in RFC 5880.  Open vSwitch
+       does not implement the optional Authentication or ``Echo Mode''
+       features.
+      </p>
+
+      <group title="BFD Configuration">
+       <p>
+         A controller sets up key-value pairs in the <ref column="bfd"/>
+         column to enable and configure BFD.
+        </p>
+
+        <column name="bfd" key="enable" type='{"type": "boolean"}'>
+          True to enable BFD on this <ref table="Physical_Locator"/>.
+        </column>
+        
+        <column name="bfd" key="min_rx"
+                type='{"type": "integer", "minInteger": 1}'>
+          The shortest interval, in milliseconds, at which this BFD session
+          offers to receive BFD control messages.  The remote endpoint may
+          choose to send messages at a slower rate.  Defaults to
+          <code>1000</code>.
+        </column>
+
+        <column name="bfd" key="min_tx"
+                type='{"type": "integer", "minInteger": 1}'>
+          The shortest interval, in milliseconds, at which this BFD session is
+          willing to transmit BFD control messages.  Messages will actually be
+          transmitted at a slower rate if the remote endpoint is not willing to
+          receive as quickly as specified.  Defaults to <code>100</code>.
+        </column>
+
+        <column name="bfd" key="decay_min_rx" type='{"type": "integer"}'>
+          An alternate receive interval, in milliseconds, that must be greater
+          than or equal to <ref column="bfd" key="min_rx"/>.  The
+          implementation switches from <ref column="bfd" key="min_rx"/> to <ref
+          column="bfd" key="decay_min_rx"/> when there is no obvious incoming
+          data traffic at the interface, to reduce the CPU and bandwidth cost
+          of monitoring an idle interface.  This feature may be disabled by
+          setting a value of 0.  This feature is reset whenever <ref
+          column="bfd" key="decay_min_rx"/> or <ref column="bfd" key="min_rx"/>
+          changes.
+        </column>
+
+        <column name="bfd" key="forwarding_if_rx" type='{"type": "boolean"}'>
+          True to consider the interface capable of packet I/O as long as it
+          continues to receive any packets (not just BFD packets).  This
+          prevents link congestion that causes consecutive BFD control packets
+          to be lost from marking the interface down.
+        </column>
+
+        <column name="bfd" key="cpath_down" type='{"type": "boolean"}'>
+          Set to true to notify the remote endpoint that traffic should not be
+          forwarded to this system for some reason other than a connectivty
+          failure on the interface being monitored.  The typical underlying
+          reason is ``concatenated path down,'' that is, that connectivity
+          beyond the local system is down.  Defaults to false.
+        </column>
+
+        <column name="bfd" key="check_tnl_key" type='{"type": "boolean"}'>
+          Set to true to make BFD accept only control messages with a tunnel
+          key of zero.  By default, BFD accepts control messages with any
+          tunnel key.
+        </column>
+
+        <column name="bfd" key="bfd_dst_mac">
+          Set to an Ethernet address in the form
+          <var>xx</var>:<var>xx</var>:<var>xx</var>:<var>xx</var>:<var>xx</var>:<var>xx</var>
+          to set the MAC used as destination for transmitted BFD packets and
+          expected as destination for received BFD packets.  The default is
+          <code>00:23:20:00:00:01</code>.
+        </column>
+      </group>
+
+      <group title="BFD Status">
+       <p>
+         The VTEP sets key-value pairs in the <ref column="bfd_status"/>
+         column to report the status of BFD on this interface.  When BFD is
+         not enabled, with <ref column="bfd" key="enable"/>, the switch clears
+         all key-value pairs from <ref column="bfd_status"/>.
+       </p>
+
+       <column name="bfd_status" key="state"
+               type='{"type": "string",
+                     "enum": ["set", ["admin_down", "down", "init", "up"]]}'>
+         Reports the state of the BFD session.  The BFD session is fully
+         healthy and negotiated if <code>UP</code>.
+       </column>
+
+       <column name="bfd_status" key="forwarding" type='{"type": "boolean"}'>
+         Reports whether the BFD session believes this <ref
+         table="Physical_Locator"/> may be used to forward traffic.  Typically
+         this means the local session is signaling <code>UP</code>, and the
+         remote system isn't signaling a problem such as concatenated path
+         down.
+       </column>
+
+       <column name="bfd_status" key="diagnostic">
+         In case of a problem, set to a short message that reports what the
+         local BFD session thinks is wrong.
+       </column>
+
+       <column name="bfd_status" key="remote_state"
+               type='{"type": "string",
+                     "enum": ["set", ["admin_down", "down", "init", "up"]]}'>
+         Reports the state of the remote endpoint's BFD session.
+       </column>
+
+       <column name="bfd_status" key="remote_diagnostic">
+         In case of a problem, set to a short message that reports what the
+         remote endpoint's BFD session thinks is wrong.
+       </column>
+      </group>
     </group>
   </table>
 
index e9c2d16..2702601 100644 (file)
@@ -123,12 +123,10 @@ cp -rf $RPM_BUILD_ROOT/usr/share/openvswitch/bugtool-plugins/* $RPM_BUILD_ROOT/e
 # Get rid of stuff we don't want to make RPM happy.
 rm \
     $RPM_BUILD_ROOT/usr/bin/ovs-benchmark \
-    $RPM_BUILD_ROOT/usr/bin/ovs-controller \
     $RPM_BUILD_ROOT/usr/bin/ovs-l3ping \
     $RPM_BUILD_ROOT/usr/bin/ovs-pki \
     $RPM_BUILD_ROOT/usr/bin/ovs-test \
     $RPM_BUILD_ROOT/usr/share/man/man1/ovs-benchmark.1 \
-    $RPM_BUILD_ROOT/usr/share/man/man8/ovs-controller.8 \
     $RPM_BUILD_ROOT/usr/share/man/man8/ovs-l3ping.8 \
     $RPM_BUILD_ROOT/usr/share/man/man8/ovs-pki.8 \
     $RPM_BUILD_ROOT/usr/share/man/man8/ovs-test.8