[ovs-dev] [PATCH 01/16] tunneling: Add support for tunnel ID.

Jesse Gross jesse at nicira.com
Tue Apr 13 14:41:03 UTC 2010


Add a tun_id field which contains the ID of the encapsulating tunnel
on which a packet was received (0 if not received on a tunnel).  Also
add an action which allows the tunnel ID to be set for outgoing
packets.  At this point there aren't any tunnel implementations so
these fields don't have any effect.

The matching is exposed to OpenFlow by overloading the high 32 bits
of the cookie as the tunnel ID.  ovs-ofctl is capable of turning
on this special behavior using a new "tun-cookie" command but this
command is intentially undocumented to avoid it being used without
a full understanding of the consequences.
---
 datapath/actions.c                      |   11 ++++
 datapath/datapath.c                     |    4 +-
 datapath/datapath.h                     |    3 +-
 datapath/flow.c                         |    3 +-
 include/openflow/nicira-ext.h           |   32 ++++++++++++-
 include/openvswitch/datapath-protocol.h |   34 +++++++++-----
 lib/classifier.c                        |   17 ++++---
 lib/classifier.h                        |   13 +++--
 lib/dhcp-client.c                       |    2 +-
 lib/dpif-netdev.c                       |    4 +-
 lib/flow.c                              |   54 ++++++++++++++++-----
 lib/flow.h                              |   12 +++--
 lib/learning-switch.c                   |    2 +-
 lib/odp-util.c                          |    3 +
 lib/ofp-print.c                         |   14 +++++-
 lib/vconn.c                             |    3 +-
 ofproto/fail-open.c                     |    4 +-
 ofproto/in-band.c                       |    2 +-
 ofproto/ofproto-sflow.c                 |    2 +-
 ofproto/ofproto.c                       |   78 ++++++++++++++++++++++---------
 tests/test-classifier.c                 |   11 ++++-
 tests/test-flows.c                      |    4 +-
 utilities/ovs-ofctl.8.in                |   14 ++++-
 utilities/ovs-ofctl.c                   |   30 +++++++++++-
 vswitchd/bridge.c                       |    5 +-
 25 files changed, 269 insertions(+), 92 deletions(-)

diff --git a/datapath/actions.c b/datapath/actions.c
index bef7d10..2049801 100644
--- a/datapath/actions.c
+++ b/datapath/actions.c
@@ -58,6 +58,11 @@ make_writable(struct sk_buff *skb, unsigned min_headroom, gfp_t gfp)
 	return NULL;
 }
 
+static void set_tunnel(struct sk_buff *skb, struct odp_flow_key *key,
+		       const struct odp_action_tunnel *a)
+{
+	OVS_CB(skb)->tun_id = key->tun_id = a->tun_id;
+}
 
 static struct sk_buff *
 vlan_pull_tag(struct sk_buff *skb)
@@ -477,6 +482,8 @@ int execute_actions(struct datapath *dp, struct sk_buff *skb,
 		}
 	}
 
+	OVS_CB(skb)->tun_id = 0;
+
 	for (; n_actions > 0; a++, n_actions--) {
 		WARN_ON_ONCE(skb_shared(skb));
 		if (prev_port != -1) {
@@ -502,6 +509,10 @@ int execute_actions(struct datapath *dp, struct sk_buff *skb,
 			}
 			break;
 
+		case ODPAT_SET_TUNNEL:
+			set_tunnel(skb, key, &a->tunnel);
+			break;
+
 		case ODPAT_SET_VLAN_VID:
 		case ODPAT_SET_VLAN_PCP:
 			skb = modify_vlan_tci(dp, skb, key, a, n_actions, gfp);
diff --git a/datapath/datapath.c b/datapath/datapath.c
index 6365f94..9dfd604 100644
--- a/datapath/datapath.c
+++ b/datapath/datapath.c
@@ -537,6 +537,7 @@ void dp_process_received_packet(struct sk_buff *skb, struct net_bridge_port *p)
 	WARN_ON_ONCE(skb_shared(skb));
 
 	compute_ip_summed(skb, false);
+	OVS_CB(skb)->tun_id = 0;
 
 	/* BHs are off so we don't have to use get_cpu()/put_cpu() here. */
 	stats = percpu_ptr(dp->stats_percpu, smp_processor_id());
@@ -558,7 +559,7 @@ void dp_process_received_packet(struct sk_buff *skb, struct net_bridge_port *p)
 		stats->n_hit++;
 	} else {
 		stats->n_missed++;
-		dp_output_control(dp, skb, _ODPL_MISS_NR, 0);
+		dp_output_control(dp, skb, _ODPL_MISS_NR, OVS_CB(skb)->tun_id);
 	}
 }
 
@@ -1302,6 +1303,7 @@ static int do_execute(struct datapath *dp, const struct odp_execute *executep)
 	skb = alloc_skb(execute.length, GFP_KERNEL);
 	if (!skb)
 		goto error_free_actions;
+
 	if (execute.in_port < DP_MAX_PORTS) {
 		struct net_bridge_port *p = dp->ports[execute.in_port];
 		if (p)
diff --git a/datapath/datapath.h b/datapath/datapath.h
index 38c8475..faf2470 100644
--- a/datapath/datapath.h
+++ b/datapath/datapath.h
@@ -195,7 +195,8 @@ enum csum_type {
  * kernel versions.
  */
 struct ovs_skb_cb {
-	enum csum_type	ip_summed;
+	enum csum_type		ip_summed;
+	__be32			tun_id;
 };
 #define OVS_CB(skb) ((struct ovs_skb_cb *)(skb)->cb)
 
diff --git a/datapath/flow.c b/datapath/flow.c
index 9a94d0b..094a2c8 100644
--- a/datapath/flow.c
+++ b/datapath/flow.c
@@ -202,8 +202,9 @@ int flow_extract(struct sk_buff *skb, u16 in_port, struct odp_flow_key *key)
 	int nh_ofs;
 
 	memset(key, 0, sizeof *key);
-	key->dl_vlan = htons(ODP_VLAN_NONE);
+	key->tun_id = OVS_CB(skb)->tun_id;
 	key->in_port = in_port;
+	key->dl_vlan = htons(ODP_VLAN_NONE);
 
 	if (skb->len < sizeof *eth)
 		return 0;
diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h
index 7232d57..2454335 100644
--- a/include/openflow/nicira-ext.h
+++ b/include/openflow/nicira-ext.h
@@ -37,6 +37,10 @@ enum nicira_type {
      * pairs in the form "key=value\n". */
     NXT_STATUS_REPLY,
 
+    /* Use the high 32 bits of the cookie field as the tunnel ID in the flow
+     * match. */
+    NXT_TUN_ID_FROM_COOKIE,
+
     /* No longer used. */
     NXT_ACT_SET_CONFIG__OBSOLETE,
     NXT_ACT_GET_CONFIG__OBSOLETE,
@@ -54,10 +58,19 @@ struct nicira_header {
 };
 OFP_ASSERT(sizeof(struct nicira_header) == 16);
 
+struct nxt_tun_id_cookie {
+    struct ofp_header header;
+    uint32_t vendor;            /* NX_VENDOR_ID. */
+    uint32_t subtype;           /* NXT_TUN_ID_FROM_COOKIE */
+    uint8_t set;                /* Nonzero to enable, zero to disable. */
+    uint8_t pad[7];
+};
+OFP_ASSERT(sizeof(struct nxt_tun_id_cookie) == 24);
 
 enum nx_action_subtype {
     NXAST_SNAT__OBSOLETE,           /* No longer used. */
-    NXAST_RESUBMIT                  /* Throw against flow table again. */
+    NXAST_RESUBMIT,                 /* Throw against flow table again. */
+    NXAST_SET_TUNNEL                /* Set encapsulating tunnel ID. */
 };
 
 /* Action structure for NXAST_RESUBMIT. */
@@ -71,6 +84,17 @@ struct nx_action_resubmit {
 };
 OFP_ASSERT(sizeof(struct nx_action_resubmit) == 16);
 
+/* Action structure for NXAST_SET_TUNNEL. */
+struct nx_action_set_tunnel {
+    uint16_t type;                  /* OFPAT_VENDOR. */
+    uint16_t len;                   /* Length is 8. */
+    uint32_t vendor;                /* NX_VENDOR_ID. */
+    uint16_t subtype;               /* NXAST_SET_TUNNEL. */
+    uint8_t pad[2];
+    uint32_t tun_id;                /* Tunnel ID. */
+};
+OFP_ASSERT(sizeof(struct nx_action_set_tunnel) == 16);
+
 /* Header for Nicira-defined actions. */
 struct nx_action_header {
     uint16_t type;                  /* OFPAT_VENDOR. */
@@ -81,4 +105,10 @@ struct nx_action_header {
 };
 OFP_ASSERT(sizeof(struct nx_action_header) == 16);
 
+/* Wildcard for tunnel ID. */
+#define NXFW_TUN_ID  (1 << 25)
+
+#define NXFW_ALL NXFW_TUN_ID
+#define OVSFW_ALL (OFPFW_ALL | NXFW_ALL)
+
 #endif /* openflow/nicira-ext.h */
diff --git a/include/openvswitch/datapath-protocol.h b/include/openvswitch/datapath-protocol.h
index 6c53545..d97f4fa 100644
--- a/include/openvswitch/datapath-protocol.h
+++ b/include/openvswitch/datapath-protocol.h
@@ -127,7 +127,8 @@ struct odp_stats {
  * @arg: Argument value whose meaning depends on @type.
  *
  * For @type == %_ODPL_MISS_NR, the header is followed by packet data.  The
- * @arg member is unused and set to 0.
+ * @arg member is the ID of the tunnel that encapsulated this packet. It is 0
+ * if the packet was not received on a tunnel.
  *
  * For @type == %_ODPL_ACTION_NR, the header is followed by packet data.  The
  * @arg member is copied from the &struct odp_action_controller that caused
@@ -191,6 +192,7 @@ struct odp_flow_stats {
 };
 
 struct odp_flow_key {
+    __be32 tun_id;               /* Encapsulating tunnel ID. */
     __be32 nw_src;               /* IP source address. */
     __be32 nw_dst;               /* IP destination address. */
     __u16  in_port;              /* Input switch port. */
@@ -243,17 +245,18 @@ struct odp_flowvec {
 #define ODPAT_OUTPUT            0    /* Output to switch port. */
 #define ODPAT_OUTPUT_GROUP      1    /* Output to all ports in group. */
 #define ODPAT_CONTROLLER        2    /* Send copy to controller. */
-#define ODPAT_SET_VLAN_VID      3    /* Set the 802.1q VLAN id. */
-#define ODPAT_SET_VLAN_PCP      4    /* Set the 802.1q priority. */
-#define ODPAT_STRIP_VLAN        5    /* Strip the 802.1q header. */
-#define ODPAT_SET_DL_SRC        6    /* Ethernet source address. */
-#define ODPAT_SET_DL_DST        7    /* Ethernet destination address. */
-#define ODPAT_SET_NW_SRC        8    /* IP source address. */
-#define ODPAT_SET_NW_DST        9    /* IP destination address. */
-#define ODPAT_SET_NW_TOS        10   /* IP ToS/DSCP field (6 bits). */
-#define ODPAT_SET_TP_SRC        11   /* TCP/UDP source port. */
-#define ODPAT_SET_TP_DST        12   /* TCP/UDP destination port. */
-#define ODPAT_N_ACTIONS         13
+#define ODPAT_SET_TUNNEL        3    /* Set the encapsulating tunnel ID. */
+#define ODPAT_SET_VLAN_VID      4    /* Set the 802.1q VLAN id. */
+#define ODPAT_SET_VLAN_PCP      5    /* Set the 802.1q priority. */
+#define ODPAT_STRIP_VLAN        6    /* Strip the 802.1q header. */
+#define ODPAT_SET_DL_SRC        7    /* Ethernet source address. */
+#define ODPAT_SET_DL_DST        8    /* Ethernet destination address. */
+#define ODPAT_SET_NW_SRC        9    /* IP source address. */
+#define ODPAT_SET_NW_DST        10   /* IP destination address. */
+#define ODPAT_SET_NW_TOS        11   /* IP ToS/DSCP field (6 bits). */
+#define ODPAT_SET_TP_SRC        12   /* TCP/UDP source port. */
+#define ODPAT_SET_TP_DST        13   /* TCP/UDP destination port. */
+#define ODPAT_N_ACTIONS         14
 
 struct odp_action_output {
     __u16 type;                  /* ODPAT_OUTPUT. */
@@ -275,6 +278,12 @@ struct odp_action_controller {
     __u32 arg;                  /* Copied to struct odp_msg 'arg' member. */
 };
 
+struct odp_action_tunnel {
+    __u16 type;                 /* ODPAT_SET_TUNNEL. */
+    __u16 reserved;
+    __u32 tun_id;               /* Tunnel ID. */
+};
+
 /* Action structure for ODPAT_SET_VLAN_VID. */
 struct odp_action_vlan_vid {
     __u16 type;                  /* ODPAT_SET_VLAN_VID. */
@@ -326,6 +335,7 @@ union odp_action {
     struct odp_action_output output;
     struct odp_action_output_group output_group;
     struct odp_action_controller controller;
+    struct odp_action_tunnel tunnel;
     struct odp_action_vlan_vid vlan_vid;
     struct odp_action_vlan_pcp vlan_pcp;
     struct odp_action_dl_addr dl_addr;
diff --git a/lib/classifier.c b/lib/classifier.c
index ee78dad..3f624d9 100644
--- a/lib/classifier.c
+++ b/lib/classifier.c
@@ -55,8 +55,8 @@ static bool rules_match_2wild(const struct cls_rule *wild1,
 /* Converts the flow in 'flow' into a cls_rule in 'rule', with the given
  * 'wildcards' and 'priority'.*/
 void
-cls_rule_from_flow(struct cls_rule *rule, const flow_t *flow,
-                   uint32_t wildcards, unsigned int priority)
+cls_rule_from_flow(const flow_t *flow, uint32_t wildcards,
+                   unsigned int priority, struct cls_rule *rule)
 {
     assert(!flow->reserved[0] && !flow->reserved[1] && !flow->reserved[2]);
     rule->flow = *flow;
@@ -68,11 +68,12 @@ cls_rule_from_flow(struct cls_rule *rule, const flow_t *flow,
 /* Converts the ofp_match in 'match' into a cls_rule in 'rule', with the given
  * 'priority'. */
 void
-cls_rule_from_match(struct cls_rule *rule, const struct ofp_match *match,
-                    unsigned int priority)
+cls_rule_from_match(const struct ofp_match *match, unsigned int priority,
+                    bool tun_id_from_cookie, uint64_t cookie,
+                    struct cls_rule *rule)
 {
     uint32_t wildcards;
-    flow_from_match(&rule->flow, &wildcards, match);
+    flow_from_match(match, tun_id_from_cookie, cookie, &rule->flow, &wildcards);
     flow_wildcards_init(&rule->wc, wildcards);
     rule->priority = rule->wc.wildcards ? priority : UINT16_MAX;
     rule->table_idx = table_idx_from_wildcards(rule->wc.wildcards);
@@ -305,7 +306,7 @@ classifier_lookup_wild(const struct classifier *cls, const flow_t *flow)
         struct cls_rule target;
         int i;
 
-        cls_rule_from_flow(&target, flow, 0, 0);
+        cls_rule_from_flow(flow, 0, 0, &target);
         for (i = 0; i < CLS_N_FIELDS; i++) {
             struct cls_rule *rule = search_table(&cls->tables[i], i, &target);
             if (rule && (!best || rule->priority > best->priority)) {
@@ -330,7 +331,7 @@ classifier_find_rule_exactly(const struct classifier *cls,
         return search_exact_table(cls, flow_hash(target, 0), target);
     }
 
-    assert(wildcards == (wildcards & OFPFW_ALL));
+    assert(wildcards == (wildcards & OVSFW_ALL));
     table_idx = table_idx_from_wildcards(wildcards);
     hash = hash_fields(target, table_idx);
     HMAP_FOR_EACH_WITH_HASH (bucket, struct cls_bucket, hmap_node, hash,
@@ -367,7 +368,7 @@ classifier_rule_overlaps(const struct classifier *cls,
             true : false;
     }
 
-    cls_rule_from_flow(&target_rule, target, wildcards, priority);
+    cls_rule_from_flow(target, wildcards, priority, &target_rule);
 
     for (tbl = &cls->tables[0]; tbl < &cls->tables[CLS_N_FIELDS]; tbl++) {
         struct cls_bucket *bucket;
diff --git a/lib/classifier.h b/lib/classifier.h
index 126d149..3551602 100644
--- a/lib/classifier.h
+++ b/lib/classifier.h
@@ -44,10 +44,11 @@
 #include "flow.h"
 #include "hmap.h"
 #include "list.h"
+#include "openflow/nicira-ext.h"
 #include "openflow/openflow.h"
 
 /* Number of bytes of fields in a rule. */
-#define CLS_N_BYTES 31
+#define CLS_N_BYTES 37
 
 /* Fields in a rule.
  *
@@ -59,6 +60,7 @@
     /*        wildcard bit(s)    member name  name     */   \
     /*        -----------------  -----------  -------- */   \
     CLS_FIELD(OFPFW_IN_PORT,     in_port,     IN_PORT)      \
+    CLS_FIELD(NXFW_TUN_ID,       tun_id,      TUN_ID)       \
     CLS_FIELD(OFPFW_DL_VLAN,     dl_vlan,     DL_VLAN)      \
     CLS_FIELD(OFPFW_DL_VLAN_PCP, dl_vlan_pcp, DL_VLAN_PCP)  \
     CLS_FIELD(OFPFW_DL_SRC,      dl_src,      DL_SRC)       \
@@ -121,10 +123,11 @@ struct cls_rule {
     unsigned int table_idx;     /* Index into struct classifier 'tables'. */
 };
 
-void cls_rule_from_flow(struct cls_rule *, const flow_t *, uint32_t wildcards,
-                        unsigned int priority);
-void cls_rule_from_match(struct cls_rule *, const struct ofp_match *,
-                         unsigned int priority);
+void cls_rule_from_flow(const flow_t *, uint32_t wildcards,
+                        unsigned int priority, struct cls_rule *);
+void cls_rule_from_match(const struct ofp_match *, unsigned int priority,
+                         bool tun_id_from_cookie, uint64_t cookie,
+                         struct cls_rule *);
 char *cls_rule_to_string(const struct cls_rule *);
 void cls_rule_print(const struct cls_rule *);
 void cls_rule_moved(struct classifier *,
diff --git a/lib/dhcp-client.c b/lib/dhcp-client.c
index 30ac56c..0abf115 100644
--- a/lib/dhcp-client.c
+++ b/lib/dhcp-client.c
@@ -931,7 +931,7 @@ do_receive_msg(struct dhclient *cli, struct dhcp_msg *msg)
             goto drained;
         }
 
-        flow_extract(&b, 0, &flow);
+        flow_extract(&b, 0, 0, &flow);
         if (flow.dl_type != htons(ETH_TYPE_IP)
             || flow.nw_proto != IP_TYPE_UDP
             || flow.tp_dst != htons(DHCP_CLIENT_PORT)
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 1cc4ed4..7d31a69 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -932,7 +932,7 @@ dpif_netdev_execute(struct dpif *dpif, uint16_t in_port,
          * if we don't. */
         copy = *packet;
     }
-    flow_extract(&copy, in_port, &flow);
+    flow_extract(&copy, 0, in_port, &flow);
     error = dp_netdev_execute_actions(dp, &copy, &flow, actions, n_actions);
     if (mutates) {
         ofpbuf_uninit(&copy);
@@ -1026,7 +1026,7 @@ dp_netdev_port_input(struct dp_netdev *dp, struct dp_netdev_port *port,
     struct dp_netdev_flow *flow;
     flow_t key;
 
-    if (flow_extract(packet, port->port_no, &key) && dp->drop_frags) {
+    if (flow_extract(packet, 0, port->port_no, &key) && dp->drop_frags) {
         dp->n_frags++;
         return;
     }
diff --git a/lib/flow.c b/lib/flow.c
index 7d368bb..aed584b 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -27,6 +27,7 @@
 #include "openflow/openflow.h"
 #include "openvswitch/datapath-protocol.h"
 #include "packets.h"
+#include "xtoxll.h"
 
 #include "vlog.h"
 #define THIS_MODULE VLM_flow
@@ -89,7 +90,8 @@ pull_vlan(struct ofpbuf *packet)
 
 /* Returns 1 if 'packet' is an IP fragment, 0 otherwise. */
 int
-flow_extract(struct ofpbuf *packet, uint16_t in_port, flow_t *flow)
+flow_extract(struct ofpbuf *packet, uint32_t tun_id, uint16_t in_port,
+             flow_t *flow)
 {
     struct ofpbuf b = *packet;
     struct eth_header *eth;
@@ -98,8 +100,9 @@ flow_extract(struct ofpbuf *packet, uint16_t in_port, flow_t *flow)
     COVERAGE_INC(flow_extract);
 
     memset(flow, 0, sizeof *flow);
-    flow->dl_vlan = htons(OFP_VLAN_NONE);
+    flow->tun_id = tun_id;
     flow->in_port = in_port;
+    flow->dl_vlan = htons(OFP_VLAN_NONE);
 
     packet->l2 = b.data;
     packet->l3 = NULL;
@@ -240,9 +243,14 @@ flow_extract_stats(const flow_t *flow, struct ofpbuf *packet,
 /* Extract 'flow' with 'wildcards' into the OpenFlow match structure
  * 'match'. */
 void
-flow_to_match(const flow_t *flow, uint32_t wildcards, struct ofp_match *match)
+flow_to_match(const flow_t *flow, uint32_t wildcards, bool tun_id_from_cookie,
+              struct ofp_match *match)
 {
+    if (!tun_id_from_cookie) {
+        wildcards &= OFPFW_ALL;
+    }
     match->wildcards = htonl(wildcards);
+
     match->in_port = htons(flow->in_port == ODPP_LOCAL ? OFPP_LOCAL
                            : flow->in_port);
     match->dl_vlan = flow->dl_vlan;
@@ -261,14 +269,21 @@ flow_to_match(const flow_t *flow, uint32_t wildcards, struct ofp_match *match)
 }
 
 void
-flow_from_match(flow_t *flow, uint32_t *wildcards,
-                const struct ofp_match *match)
+flow_from_match(const struct ofp_match *match, bool tun_id_from_cookie,
+                uint64_t cookie, flow_t *flow, uint32_t *wildcards)
 {
     if (wildcards) {
         *wildcards = ntohl(match->wildcards);
+
+        if (!tun_id_from_cookie) {
+            *wildcards |= NXFW_TUN_ID;
+        }
     }
     flow->nw_src = match->nw_src;
     flow->nw_dst = match->nw_dst;
+    if (tun_id_from_cookie) {
+        flow->tun_id = htonl(ntohll(cookie) >> 32);
+    }
     flow->in_port = (match->in_port == htons(OFPP_LOCAL) ? ODPP_LOCAL
                      : ntohs(match->in_port));
     flow->dl_vlan = match->dl_vlan;
@@ -294,14 +309,27 @@ flow_to_string(const flow_t *flow)
 void
 flow_format(struct ds *ds, const flow_t *flow)
 {
-    ds_put_format(ds, "in_port%04x:vlan%d:pcp%d mac"ETH_ADDR_FMT
-                  "->"ETH_ADDR_FMT" type%04x proto%"PRId8" tos%"PRIu8
-                  " ip"IP_FMT"->"IP_FMT" port%d->%d",
-                  flow->in_port, ntohs(flow->dl_vlan), flow->dl_vlan_pcp,
-                  ETH_ADDR_ARGS(flow->dl_src), ETH_ADDR_ARGS(flow->dl_dst),
-                  ntohs(flow->dl_type), flow->nw_proto, flow->nw_tos,
-                  IP_ARGS(&flow->nw_src), IP_ARGS(&flow->nw_dst),
-                  ntohs(flow->tp_src), ntohs(flow->tp_dst));
+    ds_put_format(ds, "tunnel%08"PRIx32":in_port%04"PRIx16
+                      ":vlan%"PRIu16":pcp%"PRIu8
+                      " mac"ETH_ADDR_FMT"->"ETH_ADDR_FMT
+                      " type%04"PRIx16
+                      " proto%"PRIu8
+                      " tos%"PRIu8
+                      " ip"IP_FMT"->"IP_FMT
+                      " port%"PRIu16"->%"PRIu16,
+                  ntohl(flow->tun_id),
+                  flow->in_port,
+                  ntohs(flow->dl_vlan),
+                  flow->dl_vlan_pcp,
+                  ETH_ADDR_ARGS(flow->dl_src),
+                  ETH_ADDR_ARGS(flow->dl_dst),
+                  ntohs(flow->dl_type),
+                  flow->nw_proto,
+                  flow->nw_tos,
+                  IP_ARGS(&flow->nw_src),
+                  IP_ARGS(&flow->nw_dst),
+                  ntohs(flow->tp_src),
+                  ntohs(flow->tp_dst));
 }
 
 void
diff --git a/lib/flow.h b/lib/flow.h
index ca140af..058404c 100644
--- a/lib/flow.h
+++ b/lib/flow.h
@@ -21,9 +21,9 @@
 #include <stdbool.h>
 #include <stdint.h>
 #include <string.h>
+#include "openflow/nicira-ext.h"
 #include "openflow/openflow.h"
 #include "hash.h"
-#include "openflow/openflow.h"
 #include "openvswitch/datapath-protocol.h"
 #include "util.h"
 
@@ -33,11 +33,13 @@ struct ofpbuf;
 
 typedef struct odp_flow_key flow_t;
 
-int flow_extract(struct ofpbuf *, uint16_t in_port, flow_t *);
+int flow_extract(struct ofpbuf *, uint32_t tun_id, uint16_t in_port, flow_t *);
 void flow_extract_stats(const flow_t *flow, struct ofpbuf *packet, 
         struct odp_flow_stats *stats);
-void flow_to_match(const flow_t *, uint32_t wildcards, struct ofp_match *);
-void flow_from_match(flow_t *, uint32_t *wildcards, const struct ofp_match *);
+void flow_to_match(const flow_t *, uint32_t wildcards, bool tun_id_cookie,
+                   struct ofp_match *);
+void flow_from_match(const struct ofp_match *, bool tun_id_from_cookie,
+                     uint64_t cookie, flow_t *, uint32_t *wildcards);
 char *flow_to_string(const flow_t *);
 void flow_format(struct ds *, const flow_t *);
 void flow_print(FILE *, const flow_t *);
@@ -94,7 +96,7 @@ flow_nw_bits_to_mask(uint32_t wildcards, int shift)
 static inline void
 flow_wildcards_init(struct flow_wildcards *wc, uint32_t wildcards)
 {
-    wc->wildcards = wildcards & OFPFW_ALL;
+    wc->wildcards = wildcards & OVSFW_ALL;
     wc->nw_src_mask = flow_nw_bits_to_mask(wc->wildcards, OFPFW_NW_SRC_SHIFT);
     wc->nw_dst_mask = flow_nw_bits_to_mask(wc->wildcards, OFPFW_NW_DST_SHIFT);
 }
diff --git a/lib/learning-switch.c b/lib/learning-switch.c
index 092274c..126cc6b 100644
--- a/lib/learning-switch.c
+++ b/lib/learning-switch.c
@@ -404,7 +404,7 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn, void *opi_)
     pkt_len = ntohs(opi->header.length) - pkt_ofs;
     pkt.data = opi->data;
     pkt.size = pkt_len;
-    flow_extract(&pkt, in_port, &flow);
+    flow_extract(&pkt, 0, in_port, &flow);
 
     if (may_learn(sw, in_port) && sw->ml) {
         if (mac_learning_learn(sw->ml, flow.dl_src, 0, in_port)) {
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 87ac92b..21b79de 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -55,6 +55,9 @@ format_odp_action(struct ds *ds, const union odp_action *a)
     case ODPAT_CONTROLLER:
         ds_put_format(ds, "ctl(%"PRIu32")", a->controller.arg);
         break;
+    case ODPAT_SET_TUNNEL:
+        ds_put_format(ds, "set_tunnel(0x%08"PRIx32")", ntohl(a->tunnel.tun_id));
+        break;
     case ODPAT_SET_VLAN_VID:
         ds_put_format(ds, "set_vlan(%"PRIu16")", ntohs(a->vlan_vid.vlan_vid));
         break;
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index bcaf230..7c1ebd0 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -134,8 +134,8 @@ ofp_packet_in(struct ds *string, const void *oh, size_t len, int verbosity)
         struct ofp_match match;
         packet.data = (void *) op->data;
         packet.size = data_len;
-        flow_extract(&packet, ntohs(op->in_port), &flow);
-        flow_to_match(&flow, 0, &match);
+        flow_extract(&packet, 0, ntohs(op->in_port), &flow);
+        flow_to_match(&flow, 0, false, &match);
         ofp_print_match(string, &match, verbosity);
         ds_put_char(string, '\n');
     }
@@ -193,6 +193,13 @@ ofp_print_nx_action(struct ds *string, const struct nx_action_header *nah)
         break;
     }
 
+    case NXAST_SET_TUNNEL: {
+        const struct nx_action_set_tunnel *nast =
+                                            (struct nx_action_set_tunnel *)nah;
+        ds_put_format(string, "set_tunnel:0x%08"PRIx32, ntohl(nast->tun_id));
+        break;
+    }
+
     default:
         ds_put_format(string, "***unknown Nicira action:%d***\n",
                       ntohs(nah->subtype));
@@ -672,6 +679,9 @@ ofp_match_to_string(const struct ofp_match *om, int verbosity)
             skip_type = false;
         }
     }
+    if (w & NXFW_TUN_ID) {
+        ds_put_cstr(&f, "tun_id_wild,");
+    }
     print_wild(&f, "in_port=", w & OFPFW_IN_PORT, verbosity,
                "%d", ntohs(om->in_port));
     print_wild(&f, "dl_vlan=", w & OFPFW_DL_VLAN, verbosity,
diff --git a/lib/vconn.c b/lib/vconn.c
index d8807fd..f2ec510 100644
--- a/lib/vconn.c
+++ b/lib/vconn.c
@@ -1349,6 +1349,7 @@ check_nicira_action(const union ofp_action *a, unsigned int len)
 
     switch (ntohs(nah->subtype)) {
     case NXAST_RESUBMIT:
+    case NXAST_SET_TUNNEL:
         return check_action_exact_len(a, len, 16);
     default:
         return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR_TYPE);
@@ -1457,7 +1458,7 @@ normalize_match(struct ofp_match *m)
     enum { OFPFW_TP = OFPFW_TP_SRC | OFPFW_TP_DST };
     uint32_t wc;
 
-    wc = ntohl(m->wildcards) & OFPFW_ALL;
+    wc = ntohl(m->wildcards) & OVSFW_ALL;
     if (wc & OFPFW_DL_TYPE) {
         m->dl_type = 0;
 
diff --git a/ofproto/fail-open.c b/ofproto/fail-open.c
index ff77de8..e866c57 100644
--- a/ofproto/fail-open.c
+++ b/ofproto/fail-open.c
@@ -173,7 +173,7 @@ fail_open_recover(struct fail_open *fo)
         fo->next_bogus_packet_in = LLONG_MAX;
 
         memset(&flow, 0, sizeof flow);
-        ofproto_delete_flow(fo->ofproto, &flow, OFPFW_ALL, FAIL_OPEN_PRIORITY);
+        ofproto_delete_flow(fo->ofproto, &flow, OVSFW_ALL, FAIL_OPEN_PRIORITY);
     }
 }
 
@@ -201,7 +201,7 @@ fail_open_flushed(struct fail_open *fo)
         action.output.len = htons(sizeof action);
         action.output.port = htons(OFPP_NORMAL);
         memset(&flow, 0, sizeof flow);
-        ofproto_add_flow(fo->ofproto, &flow, OFPFW_ALL, FAIL_OPEN_PRIORITY,
+        ofproto_add_flow(fo->ofproto, &flow, OVSFW_ALL, FAIL_OPEN_PRIORITY,
                          &action, 1, 0);
     }
 }
diff --git a/ofproto/in-band.c b/ofproto/in-band.c
index 857618f..2118809 100644
--- a/ofproto/in-band.c
+++ b/ofproto/in-band.c
@@ -370,7 +370,7 @@ set_up_flow(struct in_band *in_band, int rule_idx, const flow_t *flow,
 
         rule->installed = true;
         rule->flow = *flow;
-        rule->wildcards = OFPFW_ALL & ~fixed_fields;
+        rule->wildcards = OVSFW_ALL & ~fixed_fields;
         rule->priority = IB_BASE_PRIORITY + (N_IB_RULES - rule_idx);
 
         action.type = htons(OFPAT_OUTPUT);
diff --git a/ofproto/ofproto-sflow.c b/ofproto/ofproto-sflow.c
index 4537200..85f9f9f 100644
--- a/ofproto/ofproto-sflow.c
+++ b/ofproto/ofproto-sflow.c
@@ -496,7 +496,7 @@ ofproto_sflow_received(struct ofproto_sflow *os, struct odp_msg *msg)
     /* Get packet payload and extract flow. */
     payload.data = (union odp_action *) (actions + n_actions);
     payload.size = msg->length - min_size;
-    flow_extract(&payload, msg->port, &flow);
+    flow_extract(&payload, 0, msg->port, &flow);
 
     /* Build a flow sample */
     memset(&fs, 0, sizeof fs);
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index fd1256f..ad89b52 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -219,6 +219,7 @@ struct ofproto {
     bool need_revalidate;
     long long int next_expiration;
     struct tag_set revalidate_set;
+    bool tun_id_from_cookie;
 
     /* OpenFlow connections. */
     struct list all_conns;
@@ -1029,7 +1030,7 @@ ofproto_add_flow(struct ofproto *p,
     rule = rule_create(p, NULL, actions, n_actions,
                        idle_timeout >= 0 ? idle_timeout : 5 /* XXX */, 
                        0, 0, false);
-    cls_rule_from_flow(&rule->cr, flow, wildcards, priority);
+    cls_rule_from_flow(flow, wildcards, priority, &rule->cr);
     rule_insert(p, rule, NULL, 0);
 }
 
@@ -1583,7 +1584,7 @@ rule_insert(struct ofproto *p, struct rule *rule, struct ofpbuf *packet,
     /* Send the packet and credit it to the rule. */
     if (packet) {
         flow_t flow;
-        flow_extract(packet, in_port, &flow);
+        flow_extract(packet, 0, in_port, &flow);
         rule_execute(p, rule, packet, &flow);
     }
 
@@ -1610,9 +1611,8 @@ rule_create_subrule(struct ofproto *ofproto, struct rule *rule,
                                        rule->idle_timeout, rule->hard_timeout,
                                        0, false);
     COVERAGE_INC(ofproto_subrule_create);
-    cls_rule_from_flow(&subrule->cr, flow, 0,
-                       (rule->cr.priority <= UINT16_MAX ? UINT16_MAX
-                        : rule->cr.priority));
+    cls_rule_from_flow(flow, 0, (rule->cr.priority <= UINT16_MAX ? UINT16_MAX
+                        : rule->cr.priority), &subrule->cr);
     classifier_insert_exact(&ofproto->cls, &subrule->cr);
 
     return subrule;
@@ -2142,6 +2142,8 @@ xlate_nicira_action(struct action_xlate_ctx *ctx,
                     const struct nx_action_header *nah)
 {
     const struct nx_action_resubmit *nar;
+    const struct nx_action_set_tunnel *nast;
+    union odp_action *oa;
     int subtype = ntohs(nah->subtype);
 
     assert(nah->vendor == htonl(NX_VENDOR_ID));
@@ -2151,6 +2153,12 @@ xlate_nicira_action(struct action_xlate_ctx *ctx,
         xlate_table_action(ctx, ofp_port_to_odp_port(ntohs(nar->in_port)));
         break;
 
+    case NXAST_SET_TUNNEL:
+        nast = (const struct nx_action_set_tunnel *) nah;
+        oa = odp_actions_add(ctx->out, ODPAT_SET_TUNNEL);
+        oa->tunnel.tun_id = nast->tun_id;
+        break;
+
     default:
         VLOG_DBG_RL(&rl, "unknown Nicira action type %"PRIu16, subtype);
         break;
@@ -2314,7 +2322,7 @@ handle_packet_out(struct ofproto *p, struct ofconn *ofconn,
         buffer = NULL;
     }
 
-    flow_extract(&payload, ofp_port_to_odp_port(ntohs(opo->in_port)), &flow);
+    flow_extract(&payload, 0, ofp_port_to_odp_port(ntohs(opo->in_port)), &flow);
     error = xlate_actions((const union ofp_action *) opo->actions, n_actions,
                           &flow, p, &payload, &actions, NULL, NULL, NULL);
     if (error) {
@@ -2483,7 +2491,8 @@ handle_table_stats_request(struct ofproto *p, struct ofconn *ofconn,
     memset(ots, 0, sizeof *ots);
     ots->table_id = TABLEID_CLASSIFIER;
     strcpy(ots->name, "classifier");
-    ots->wildcards = htonl(OFPFW_ALL);
+    ots->wildcards = p->tun_id_from_cookie ? htonl(OVSFW_ALL)
+                                           : htonl(OFPFW_ALL);
     ots->max_entries = htonl(65536);
     ots->active_count = htonl(n_wild);
     ots->lookup_count = htonll(0);              /* XXX */
@@ -2641,7 +2650,8 @@ flow_stats_cb(struct cls_rule *rule_, void *cbdata_)
     ofs->length = htons(len);
     ofs->table_id = rule->cr.wc.wildcards ? TABLEID_CLASSIFIER : TABLEID_HASH;
     ofs->pad = 0;
-    flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards, &ofs->match);
+    flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards,
+                  cbdata->ofproto->tun_id_from_cookie, &ofs->match);
     ofs->duration_sec = htonl(sec);
     ofs->duration_nsec = htonl(msec * 1000000);
     ofs->cookie = rule->flow_cookie;
@@ -2682,7 +2692,7 @@ handle_flow_stats_request(struct ofproto *p, struct ofconn *ofconn,
     cbdata.ofconn = ofconn;
     cbdata.out_port = fsr->out_port;
     cbdata.msg = start_stats_reply(osr, 1024);
-    cls_rule_from_match(&target, &fsr->match, 0);
+    cls_rule_from_match(&fsr->match, 0, false, 0, &target);
     classifier_for_each_match(&p->cls, &target,
                               table_id_to_include(fsr->table_id),
                               flow_stats_cb, &cbdata);
@@ -2711,7 +2721,8 @@ flow_stats_ds_cb(struct cls_rule *rule_, void *cbdata_)
     }
 
     query_stats(cbdata->ofproto, rule, &packet_count, &byte_count);
-    flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards, &match);
+    flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards,
+                  cbdata->ofproto->tun_id_from_cookie, &match);
 
     ds_put_format(results, "duration=%llds, ",
                   (time_msec() - rule->created) / 1000);
@@ -2733,12 +2744,12 @@ ofproto_get_all_flows(struct ofproto *p, struct ds *results)
     struct flow_stats_ds_cbdata cbdata;
 
     memset(&match, 0, sizeof match);
-    match.wildcards = htonl(OFPFW_ALL);
+    match.wildcards = htonl(OVSFW_ALL);
 
     cbdata.ofproto = p;
     cbdata.results = results;
 
-    cls_rule_from_match(&target, &match, 0);
+    cls_rule_from_match(&match, 0, false, 0, &target);
     classifier_for_each_match(&p->cls, &target, CLS_INC_ALL,
                               flow_stats_ds_cb, &cbdata);
 }
@@ -2791,7 +2802,7 @@ handle_aggregate_stats_request(struct ofproto *p, struct ofconn *ofconn,
     cbdata.packet_count = 0;
     cbdata.byte_count = 0;
     cbdata.n_flows = 0;
-    cls_rule_from_match(&target, &asr->match, 0);
+    cls_rule_from_match(&asr->match, 0, false, 0, &target);
     classifier_for_each_match(&p->cls, &target,
                               table_id_to_include(asr->table_id),
                               aggregate_stats_cb, &cbdata);
@@ -2899,7 +2910,8 @@ add_flow(struct ofproto *p, struct ofconn *ofconn,
         flow_t flow;
         uint32_t wildcards;
 
-        flow_from_match(&flow, &wildcards, &ofm->match);
+        flow_from_match(&ofm->match, p->tun_id_from_cookie, ofm->cookie,
+                        &flow, &wildcards);
         if (classifier_rule_overlaps(&p->cls, &flow, wildcards,
                                      ntohs(ofm->priority))) {
             return ofp_mkerr(OFPET_FLOW_MOD_FAILED, OFPFMFC_OVERLAP);
@@ -2910,7 +2922,8 @@ add_flow(struct ofproto *p, struct ofconn *ofconn,
                        n_actions, ntohs(ofm->idle_timeout),
                        ntohs(ofm->hard_timeout),  ofm->cookie,
                        ofm->flags & htons(OFPFF_SEND_FLOW_REM));
-    cls_rule_from_match(&rule->cr, &ofm->match, ntohs(ofm->priority));
+    cls_rule_from_match(&ofm->match, ntohs(ofm->priority),
+                        p->tun_id_from_cookie, ofm->cookie, &rule->cr);
 
     error = 0;
     if (ofm->buffer_id != htonl(UINT32_MAX)) {
@@ -2932,7 +2945,8 @@ find_flow_strict(struct ofproto *p, const struct ofp_flow_mod *ofm)
     uint32_t wildcards;
     flow_t flow;
 
-    flow_from_match(&flow, &wildcards, &ofm->match);
+    flow_from_match(&ofm->match, p->tun_id_from_cookie, ofm->cookie,
+                    &flow, &wildcards);
     return rule_from_cls_rule(classifier_find_rule_exactly(
                                   &p->cls, &flow, wildcards,
                                   ntohs(ofm->priority)));
@@ -2957,7 +2971,7 @@ send_buffered_packet(struct ofproto *ofproto, struct ofconn *ofconn,
         return error;
     }
 
-    flow_extract(packet, in_port, &flow);
+    flow_extract(packet, 0, in_port, &flow);
     rule_execute(ofproto, rule, packet, &flow);
     ofpbuf_delete(packet);
 
@@ -2994,7 +3008,8 @@ modify_flows_loose(struct ofproto *p, struct ofconn *ofconn,
     cbdata.n_actions = n_actions;
     cbdata.match = NULL;
 
-    cls_rule_from_match(&target, &ofm->match, 0);
+    cls_rule_from_match(&ofm->match, 0, p->tun_id_from_cookie, ofm->cookie,
+                        &target);
 
     classifier_for_each_match(&p->cls, &target, CLS_INC_ALL,
                               modify_flows_cb, &cbdata);
@@ -3095,7 +3110,8 @@ delete_flows_loose(struct ofproto *p, const struct ofp_flow_mod *ofm)
     cbdata.ofproto = p;
     cbdata.out_port = ofm->out_port;
 
-    cls_rule_from_match(&target, &ofm->match, 0);
+    cls_rule_from_match(&ofm->match, 0, p->tun_id_from_cookie, ofm->cookie,
+                        &target);
 
     classifier_for_each_match(&p->cls, &target, CLS_INC_ALL,
                               delete_flows_cb, &cbdata);
@@ -3200,6 +3216,17 @@ handle_flow_mod(struct ofproto *p, struct ofconn *ofconn,
 }
 
 static int
+handle_tun_id_from_cookie(struct ofproto *p, struct nxt_tun_id_cookie *msg)
+{
+    if (ntohs(msg->header.length) < sizeof(struct nxt_tun_id_cookie)) {
+        return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LEN);
+    }
+
+    p->tun_id_from_cookie = !!msg->set;
+    return 0;
+}
+
+static int
 handle_vendor(struct ofproto *p, struct ofconn *ofconn, void *msg)
 {
     struct ofp_vendor_header *ovh = msg;
@@ -3220,6 +3247,9 @@ handle_vendor(struct ofproto *p, struct ofconn *ofconn, void *msg)
     case NXT_STATUS_REQUEST:
         return switch_status_handle_request(p->switch_status, ofconn->rconn,
                                             msg);
+
+    case NXT_TUN_ID_FROM_COOKIE:
+        return handle_tun_id_from_cookie(p, msg);
     }
 
     return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE);
@@ -3317,7 +3347,7 @@ handle_odp_miss_msg(struct ofproto *p, struct ofpbuf *packet)
 
     payload.data = msg + 1;
     payload.size = msg->length - sizeof *msg;
-    flow_extract(&payload, msg->port, &flow);
+    flow_extract(&payload, msg->arg, msg->port, &flow);
 
     /* Check with in-band control to see if this packet should be sent
      * to the local port regardless of the flow table. */
@@ -3457,7 +3487,8 @@ revalidate_rule(struct ofproto *p, struct rule *rule)
 }
 
 static struct ofpbuf *
-compose_flow_removed(const struct rule *rule, long long int now, uint8_t reason)
+compose_flow_removed(struct ofproto *p, const struct rule *rule,
+                     long long int now, uint8_t reason)
 {
     struct ofp_flow_removed *ofr;
     struct ofpbuf *buf;
@@ -3466,7 +3497,8 @@ compose_flow_removed(const struct rule *rule, long long int now, uint8_t reason)
     uint32_t msec = tdiff - (sec * 1000);
 
     ofr = make_openflow(sizeof *ofr, OFPT_FLOW_REMOVED, &buf);
-    flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards, &ofr->match);
+    flow_to_match(&rule->cr.flow, rule->cr.wc.wildcards, p->tun_id_from_cookie,
+                  &ofr->match);
     ofr->cookie = rule->flow_cookie;
     ofr->priority = htons(rule->cr.priority);
     ofr->reason = reason;
@@ -3511,7 +3543,7 @@ send_flow_removed(struct ofproto *p, struct rule *rule,
             if (prev) {
                 queue_tx(ofpbuf_clone(buf), prev, prev->reply_counter);
             } else {
-                buf = compose_flow_removed(rule, now, reason);
+                buf = compose_flow_removed(p, rule, now, reason);
             }
             prev = ofconn;
         }
diff --git a/tests/test-classifier.c b/tests/test-classifier.c
index 563690d..c831559 100644
--- a/tests/test-classifier.c
+++ b/tests/test-classifier.c
@@ -238,6 +238,7 @@ static uint32_t nw_src_values[] = { T_HTONL(0xc0a80001),
                                     T_HTONL(0xc0a04455) };
 static uint32_t nw_dst_values[] = { T_HTONL(0xc0a80002),
                                     T_HTONL(0xc0a04455) };
+static uint32_t tun_id_values[] = { 0, 0xffff0000 };
 static uint16_t in_port_values[] = { T_HTONS(1), T_HTONS(OFPP_LOCAL) };
 static uint16_t dl_vlan_values[] = { T_HTONS(101), T_HTONS(0) };
 static uint8_t dl_vlan_pcp_values[] = { 7, 0 };
@@ -257,6 +258,9 @@ static void *values[CLS_N_FIELDS][2];
 static void
 init_values(void)
 {
+    values[CLS_F_IDX_TUN_ID][0] = &tun_id_values[0];
+    values[CLS_F_IDX_TUN_ID][1] = &tun_id_values[1];
+
     values[CLS_F_IDX_IN_PORT][0] = &in_port_values[0];
     values[CLS_F_IDX_IN_PORT][1] = &in_port_values[1];
 
@@ -296,6 +300,7 @@ init_values(void)
 
 #define N_NW_SRC_VALUES ARRAY_SIZE(nw_src_values)
 #define N_NW_DST_VALUES ARRAY_SIZE(nw_dst_values)
+#define N_TUN_ID_VALUES ARRAY_SIZE(tun_id_values)
 #define N_IN_PORT_VALUES ARRAY_SIZE(in_port_values)
 #define N_DL_VLAN_VALUES ARRAY_SIZE(dl_vlan_values)
 #define N_DL_VLAN_PCP_VALUES ARRAY_SIZE(dl_vlan_pcp_values)
@@ -309,6 +314,7 @@ init_values(void)
 
 #define N_FLOW_VALUES (N_NW_SRC_VALUES *        \
                        N_NW_DST_VALUES *        \
+                       N_TUN_ID_VALUES *        \
                        N_IN_PORT_VALUES *       \
                        N_DL_VLAN_VALUES *       \
                        N_DL_VLAN_PCP_VALUES *   \
@@ -360,6 +366,7 @@ compare_classifiers(struct classifier *cls, struct tcls *tcls)
         x = i;
         flow.nw_src = nw_src_values[get_value(&x, N_NW_SRC_VALUES)];
         flow.nw_dst = nw_dst_values[get_value(&x, N_NW_DST_VALUES)];
+        flow.tun_id = tun_id_values[get_value(&x, N_TUN_ID_VALUES)];
         flow.in_port = in_port_values[get_value(&x, N_IN_PORT_VALUES)];
         flow.dl_vlan = dl_vlan_values[get_value(&x, N_DL_VLAN_VALUES)];
         flow.dl_vlan_pcp = dl_vlan_pcp_values[get_value(&x,
@@ -462,8 +469,8 @@ make_rule(int wc_fields, unsigned int priority, int value_pat)
     }
 
     rule = xzalloc(sizeof *rule);
-    cls_rule_from_flow(&rule->cls_rule, &flow, wildcards,
-                       !wildcards ? UINT_MAX : priority);
+    cls_rule_from_flow(&flow, wildcards, !wildcards ? UINT_MAX : priority,
+                       &rule->cls_rule);
     return rule;
 }
 
diff --git a/tests/test-flows.c b/tests/test-flows.c
index 451ca1a..424dd7b 100644
--- a/tests/test-flows.c
+++ b/tests/test-flows.c
@@ -67,8 +67,8 @@ main(int argc OVS_UNUSED, char *argv[])
             ovs_fatal(retval, "error reading pcap file");
         }
 
-        flow_extract(packet, 1, &flow);
-        flow_to_match(&flow, 0, &extracted_match);
+        flow_extract(packet, 0, 1, &flow);
+        flow_to_match(&flow, 0, false, &extracted_match);
 
         if (memcmp(&expected_match, &extracted_match, sizeof expected_match)) {
             char *exp_s = ofp_match_to_string(&expected_match, 2);
diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
index 948df51..04b0a98 100644
--- a/utilities/ovs-ofctl.8.in
+++ b/utilities/ovs-ofctl.8.in
@@ -391,14 +391,22 @@ Sets the IP ToS/DSCP field to \fItos\fR.  Valid values are between 0 and
 255, inclusive.  Note that the two lower reserved bits are never
 modified.
 .
+.RE
+.IP
+The following actions are Nicira vendor extensions that, as of this writing, are
+only known to be implemented by Open vSwitch:
+.
+.RS
+.
 .IP \fBresubmit\fB:\fIport\fR
 Re-searches the OpenFlow flow table with the \fBin_port\fR field
 replaced by \fIport\fR and executes the actions found, if any, in
 addition to any other actions in this flow entry.  Recursive
 \fBresubmit\fR actions are ignored.
-.IP
-This action is a Nicira vendor extension that, as of this writing, is
-only known to be implemented by Open vSwitch.
+.
+.IP \fBset_tunnel\fB:\fIid\fR
+If outputting to a port that encapsulates the packet in a tunnel and supports
+an identifier (such as GRE), sets the identifier to \fBid\fR.
 .
 .RE
 .
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index 360f881..98174e9 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -647,6 +647,12 @@ str_to_action(char *str, struct ofpbuf *b)
             nar->vendor = htonl(NX_VENDOR_ID);
             nar->subtype = htons(NXAST_RESUBMIT);
             nar->in_port = htons(str_to_u32(arg));
+        } else if (!strcasecmp(act, "set_tunnel")) {
+            struct nx_action_set_tunnel *nast;
+            nast = put_action(b, sizeof *nast, OFPAT_VENDOR);
+            nast->vendor = htonl(NX_VENDOR_ID);
+            nast->subtype = htons(NXAST_SET_TUNNEL);
+            nast->tun_id = htonl(str_to_u32(arg));
         } else if (!strcasecmp(act, "output")) {
             put_output_action(b, str_to_u32(arg));
         } else if (!strcasecmp(act, "drop")) {
@@ -717,7 +723,7 @@ static bool
 parse_field(const char *name, const struct field **f_out) 
 {
 #define F_OFS(MEMBER) offsetof(struct ofp_match, MEMBER)
-    static const struct field fields[] = { 
+    static const struct field fields[] = {
         { "in_port", OFPFW_IN_PORT, F_U16, F_OFS(in_port), 0 },
         { "dl_vlan", OFPFW_DL_VLAN, F_U16, F_OFS(dl_vlan), 0 },
         { "dl_vlan_pcp", OFPFW_DL_VLAN_PCP, F_U8, F_OFS(dl_vlan_pcp), 0 },
@@ -825,6 +831,8 @@ str_to_flow(char *string, struct ofp_match *match, struct ofpbuf *actions,
                 *hard_timeout = atoi(value);
             } else if (cookie && !strcmp(name, "cookie")) {
                 *cookie = str_to_u64(value);
+            } else if (!strcmp(name, "tun_id_wild") && !strcmp(value, "true")) {
+                wildcards |= NXFW_TUN_ID;
             } else if (parse_field(name, &f)) {
                 void *data = (char *) match + f->offset;
                 if (!strcmp(value, "*") || !strcmp(value, "ANY")) {
@@ -1034,6 +1042,25 @@ static void do_del_flows(int argc, char *argv[])
 }
 
 static void
+do_tun_cookie(int argc OVS_UNUSED, char *argv[])
+{
+    struct nxt_tun_id_cookie *tun_id_cookie;
+    struct ofpbuf *buffer;
+    struct vconn *vconn;
+
+    tun_id_cookie = make_openflow(sizeof *tun_id_cookie, OFPT_VENDOR, &buffer);
+
+    tun_id_cookie->vendor = htonl(NX_VENDOR_ID);
+    tun_id_cookie->subtype = htonl(NXT_TUN_ID_FROM_COOKIE);
+    tun_id_cookie->set = !strcmp(argv[2], "true");
+    memset(tun_id_cookie->pad, 0, sizeof tun_id_cookie->pad);
+
+    open_vconn(argv[1], &vconn);
+    send_openflow_buffer(vconn, buffer);
+    vconn_close(vconn);
+}
+
+static void
 do_monitor(int argc OVS_UNUSED, char *argv[])
 {
     struct vconn *vconn;
@@ -1274,6 +1301,7 @@ static const struct command all_commands[] = {
     { "add-flows", 2, 2, do_add_flows },
     { "mod-flows", 2, 2, do_mod_flows },
     { "del-flows", 1, 2, do_del_flows },
+    { "tun-cookie", 2, 2, do_tun_cookie },
     { "dump-ports", 1, 2, do_dump_ports },
     { "mod-port", 3, 3, do_mod_port },
     { "probe", 1, 1, do_probe },
diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
index 620689f..8e3508c 100644
--- a/vswitchd/bridge.c
+++ b/vswitchd/bridge.c
@@ -1590,8 +1590,7 @@ bridge_reconfigure_controller(const struct ovsrec_open_vswitch *ovs_cfg,
         action.output.len = htons(sizeof action);
         action.output.port = htons(OFPP_NORMAL);
         memset(&flow, 0, sizeof flow);
-        ofproto_add_flow(br->ofproto, &flow, OFPFW_ALL, 0,
-                         &action, 1, 0);
+        ofproto_add_flow(br->ofproto, &flow, OVSFW_ALL, 0, &action, 1, 0);
 
         ofproto_set_in_band(br->ofproto, false);
         ofproto_set_max_backoff(br->ofproto, 1);
@@ -2742,7 +2741,7 @@ bond_send_learning_packets(struct port *port)
         n_packets++;
         compose_benign_packet(&packet, "Open vSwitch Bond Failover", 0xf177,
                               e->mac);
-        flow_extract(&packet, ODPP_NONE, &flow);
+        flow_extract(&packet, 0, ODPP_NONE, &flow);
         retval = ofproto_send_packet(br->ofproto, &flow, actions, a - actions,
                                      &packet);
         if (retval) {
-- 
1.6.3.3





More information about the dev mailing list