[ovs-dev] [PATCH] Add ODP level handling of OVS_KEY_ATTR_IPV4_TUNNEL.

Jarno Rajahalme jarno.rajahalme at nsn.com
Sat Dec 15 08:26:58 UTC 2012


Backwards compatibility is maintained by using OVS_KEY_ATTR_TUN_ID when 
OVS_KEY_ATTR_IPV4_TUNNEL would be invalid.

Copy tun_key in datapath/actions.c only if needed.

Signed-off-by: Jarno Rajahalme <jarno.rajahalme at nsn.com>
---
 datapath/actions.c          |   27 +++++++----
 include/linux/openvswitch.h |    1 +
 lib/odp-util.c              |  105 +++++++++++++++++++++++++++++++++++++------
 tests/odp.at                |   15 +++++++
 4 files changed, 127 insertions(+), 21 deletions(-)

diff --git a/datapath/actions.c b/datapath/actions.c
index faa6a00..be8dfc0 100644
--- a/datapath/actions.c
+++ b/datapath/actions.c
@@ -439,22 +439,33 @@ static int execute_set_action(struct sk_buff *skb,
 		skb_set_mark(skb, nla_get_u32(nested_attr));
 		break;
 
-	case OVS_KEY_ATTR_TUN_ID:
+	case OVS_KEY_ATTR_TUN_ID: {
 		/* If we're only using the TUN_ID action, store the value in a
-		 * temporary instance of struct ovs_key_ipv4_tunnel on the stack.
+		 * temporary instance of struct ovs_key_ipv4_tunnel on the
+		 * stack.
 		 * If both IPV4_TUNNEL and TUN_ID are being used together we
 		 * can't write into the IPV4_TUNNEL action, so make a copy and
-		 * write into that version.
+		 * write into that version, but only if the TUN_ID is actually
+		 * different.
+		 * A possible later IPV4_TUNNEL will override anything done
+		 * here.
 		 */
+
+		__be64 tun_id = nla_get_be64(nested_attr);
+
 		if (!OVS_CB(skb)->tun_key)
 			memset(tun_key, 0, sizeof(*tun_key));
-		else if (OVS_CB(skb)->tun_key != tun_key)
-			memcpy(tun_key, OVS_CB(skb)->tun_key, sizeof(*tun_key));
+		else {
+			if (OVS_CB(skb)->tun_key->tun_id == tun_id)
+				break;
+			if (OVS_CB(skb)->tun_key != tun_key)
+				memcpy(tun_key, OVS_CB(skb)->tun_key,
+				       sizeof *tun_key);
+		}
+		tun_key->tun_id = tun_id;
 		OVS_CB(skb)->tun_key = tun_key;
-
-		OVS_CB(skb)->tun_key->tun_id = nla_get_be64(nested_attr);
 		break;
-
+	}
 	case OVS_KEY_ATTR_IPV4_TUNNEL:
 		OVS_CB(skb)->tun_key = nla_data(nested_attr);
 		break;
diff --git a/include/linux/openvswitch.h b/include/linux/openvswitch.h
index 968631d..18779e6 100644
--- a/include/linux/openvswitch.h
+++ b/include/linux/openvswitch.h
@@ -372,6 +372,7 @@ struct ovs_key_nd {
 #define OVS_TNL_F_DONT_FRAGMENT (1 << 0)
 #define OVS_TNL_F_CSUM (1 << 1)
 #define OVS_TNL_F_KEY (1 << 2)
+#define OVS_TNL_F_MASK ((1 << 3) - 1) /* All known bits defined above */
 
 struct ovs_key_ipv4_tunnel {
 	__be64 tun_id;
diff --git a/lib/odp-util.c b/lib/odp-util.c
index de97fd2..445cd46 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -1342,6 +1342,23 @@ ovs_to_odp_frag(uint8_t nw_frag)
           : OVS_FRAG_TYPE_LATER);
 }
 
+/* These allow the flow view of the flags to change in future */
+static uint32_t
+flow_to_odp_flags(uint16_t flags)
+{
+    return (flags & FLOW_TNL_F_DONT_FRAGMENT ? OVS_TNL_F_DONT_FRAGMENT : 0)
+        | (flags & FLOW_TNL_F_CSUM ? OVS_TNL_F_CSUM : 0)
+        | (flags & FLOW_TNL_F_KEY ? OVS_TNL_F_KEY : 0);
+}
+
+static uint16_t
+odp_to_flow_flags(uint32_t tun_flags)
+{
+    return (tun_flags & OVS_TNL_F_DONT_FRAGMENT ? FLOW_TNL_F_DONT_FRAGMENT : 0)
+        | (tun_flags & OVS_TNL_F_CSUM ? FLOW_TNL_F_CSUM : 0)
+        | (tun_flags & OVS_TNL_F_KEY ? FLOW_TNL_F_KEY : 0);
+}
+
 /* Appends a representation of 'flow' as OVS_KEY_ATTR_* attributes to 'buf'.
  * 'flow->in_port' is ignored (since it is likely to be an OpenFlow port
  * number rather than a datapath port number).  Instead, if 'odp_in_port'
@@ -1361,7 +1378,21 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow,
         nl_msg_put_u32(buf, OVS_KEY_ATTR_PRIORITY, flow->skb_priority);
     }
 
-    if (flow->tunnel.tun_id != htonll(0)) {
+    if (flow->tunnel.ip_dst) {
+        struct ovs_key_ipv4_tunnel *ipv4_tun_key;
+
+        ipv4_tun_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_IPV4_TUNNEL,
+                                            sizeof *ipv4_tun_key);
+        /* layouts differ, flags has different size */
+        ipv4_tun_key->tun_id = flow->tunnel.tun_id;
+        ipv4_tun_key->tun_flags = flow_to_odp_flags(flow->tunnel.flags);
+        ipv4_tun_key->ipv4_src = flow->tunnel.ip_src;
+        ipv4_tun_key->ipv4_dst = flow->tunnel.ip_dst;
+        ipv4_tun_key->ipv4_tos = flow->tunnel.ip_tos;
+        ipv4_tun_key->ipv4_ttl = flow->tunnel.ip_ttl;
+        memset(ipv4_tun_key->pad, 0, sizeof ipv4_tun_key->pad);
+    }
+    else if (flow->tunnel.tun_id != htonll(0)) {
         nl_msg_put_be64(buf, OVS_KEY_ATTR_TUN_ID, flow->tunnel.tun_id);
     }
 
@@ -1845,6 +1876,8 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len,
     uint64_t expected_attrs;
     uint64_t present_attrs;
     int out_of_range_attr;
+    enum odp_key_fitness fitness = ODP_FIT_PERFECT; /* assume */
+    enum odp_key_fitness fitness2;
 
     memset(flow, 0, sizeof *flow);
 
@@ -1871,6 +1904,25 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len,
         expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TUN_ID;
     }
 
+    if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV4_TUNNEL)) {
+        const struct ovs_key_ipv4_tunnel *ipv4_tun_key;
+
+        ipv4_tun_key = nl_attr_get(attrs[OVS_KEY_ATTR_IPV4_TUNNEL]);
+
+        /* check for unknown flags */
+        if (ipv4_tun_key->tun_flags & ~OVS_TNL_F_MASK) {
+            fitness = ODP_FIT_TOO_MUCH; /* perfect -> too much */
+        }
+
+        flow->tunnel.tun_id = ipv4_tun_key->tun_id;
+        flow->tunnel.ip_src = ipv4_tun_key->ipv4_src;
+        flow->tunnel.ip_dst = ipv4_tun_key->ipv4_dst;
+        flow->tunnel.flags = odp_to_flow_flags(ipv4_tun_key->tun_flags);
+        flow->tunnel.ip_tos = ipv4_tun_key->ipv4_tos;
+        flow->tunnel.ip_ttl = ipv4_tun_key->ipv4_ttl;
+        expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IPV4_TUNNEL;
+    }
+
     if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IN_PORT)) {
         flow->in_port = nl_attr_get_u32(attrs[OVS_KEY_ATTR_IN_PORT]);
         expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IN_PORT;
@@ -1894,11 +1946,17 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len,
     }
 
     if (flow->dl_type == htons(ETH_TYPE_VLAN)) {
-        return parse_8021q_onward(attrs, present_attrs, out_of_range_attr,
-                                  expected_attrs, flow, key, key_len);
+        fitness2 = parse_8021q_onward(attrs, present_attrs,
+                                      out_of_range_attr, expected_attrs,
+                                      flow, key, key_len);
+    } else {
+        fitness2 = parse_l3_onward(attrs, present_attrs, out_of_range_attr,
+                                   expected_attrs, flow, key, key_len);
     }
-    return parse_l3_onward(attrs, present_attrs, out_of_range_attr,
-                           expected_attrs, flow, key, key_len);
+    if (fitness2 > fitness) {
+        return fitness2;
+    }
+    return fitness;
 }
 
 /* Returns 'fitness' as a string, for use in debug messages. */
@@ -1953,16 +2011,37 @@ commit_set_action(struct ofpbuf *odp_actions, enum ovs_key_attr key_type,
 }
 
 static void
-commit_set_tun_id_action(const struct flow *flow, struct flow *base,
+commit_set_tunnel_action(const struct flow *flow, struct flow *base,
                          struct ofpbuf *odp_actions)
 {
-    if (base->tunnel.tun_id == flow->tunnel.tun_id) {
-        return;
-    }
-    base->tunnel.tun_id = flow->tunnel.tun_id;
+    /*
+     * A valid IPV4_TUNNEL must have non-zero ip_dst.
+     */
+    if (flow->tunnel.ip_dst) {
+        struct ovs_key_ipv4_tunnel ipv4_tun_key;
+
+        if (!memcmp(&base->tunnel, &flow->tunnel, sizeof base->tunnel)) {
+            return;
+        }
+        memcpy(&base->tunnel, &flow->tunnel, sizeof base->tunnel);
+
+        ipv4_tun_key.tun_id = base->tunnel.tun_id;
+        ipv4_tun_key.tun_flags = flow_to_odp_flags(base->tunnel.flags);
+        ipv4_tun_key.ipv4_src = base->tunnel.ip_src;
+        ipv4_tun_key.ipv4_dst = base->tunnel.ip_dst;
+        ipv4_tun_key.ipv4_tos = base->tunnel.ip_tos;
+        ipv4_tun_key.ipv4_ttl = base->tunnel.ip_ttl;
+        memset(&ipv4_tun_key.pad, 0, sizeof ipv4_tun_key.pad);
+
+        commit_set_action(odp_actions, OVS_KEY_ATTR_IPV4_TUNNEL,
+                          &ipv4_tun_key, sizeof ipv4_tun_key);
+    } else if (base->tunnel.tun_id != flow->tunnel.tun_id) {
+        base->tunnel.tun_id = flow->tunnel.tun_id;
 
-    commit_set_action(odp_actions, OVS_KEY_ATTR_TUN_ID,
-                      &base->tunnel.tun_id, sizeof(base->tunnel.tun_id));
+        commit_set_action(odp_actions, OVS_KEY_ATTR_TUN_ID,
+                      &base->tunnel.tun_id, sizeof base->tunnel.tun_id);
+
+    }
 }
 
 static void
@@ -2145,7 +2224,7 @@ void
 commit_odp_actions(const struct flow *flow, struct flow *base,
                    struct ofpbuf *odp_actions)
 {
-    commit_set_tun_id_action(flow, base, odp_actions);
+    commit_set_tunnel_action(flow, base, odp_actions);
     commit_set_ether_addr_action(flow, base, odp_actions);
     commit_vlan_action(flow, base, odp_actions);
     commit_set_nw_action(flow, base, odp_actions);
diff --git a/tests/odp.at b/tests/odp.at
index a5f6dbe..37fed2d 100644
--- a/tests/odp.at
+++ b/tests/odp.at
@@ -34,6 +34,9 @@ skb_mark(17185),in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_
  echo '# Valid forms with tun_id header.'
  sed 's/^/tun_id(0x7f10354),/' odp-base.txt
 
+ echo '# Valid forms with tunnel header.'
+ sed 's/^/ipv4_tunnel(tun_id=0x7f10354,src=10.10.10.10,dst=20.20.20.20,tos=0x0,ttl=64,flags(df,key)),/' odp-base.txt
+
  echo
  echo '# Valid forms with VLAN header.'
  sed 's/\(eth([[^)]]*)\),*/\1,eth_type(0x8100),vlan(vid=99,pcp=7),encap(/
@@ -50,12 +53,24 @@ s/\(eth([[^)]]*)\),*/\1,eth_type(0x8100),vlan(vid=99,pcp=7),encap(/
 s/$/)/' odp-base.txt
 
  echo
+ echo '# Valid forms with tunnel and VLAN headers.'
+ sed 's/^/ipv4_tunnel(tun_id=0x7f10354,src=10.10.10.10,dst=20.20.20.20,tos=0x0,ttl=64,flags(key)),/
+s/\(eth([[^)]]*)\),*/\1,eth_type(0x8100),vlan(vid=99,pcp=7),encap(/
+s/$/)/' odp-base.txt
+
+ echo
  echo '# Valid forms with QOS priority, tun_id, and VLAN headers.'
  sed 's/^/priority(1234),tun_id(0xfedcba9876543210),/
 s/\(eth([[^)]]*)\),*/\1,eth_type(0x8100),vlan(vid=99,pcp=7),encap(/
 s/$/)/' odp-base.txt
 
  echo
+ echo '# Valid forms with QOS priority, tunnel, and VLAN headers.'
+ sed 's/^/priority(1234),ipv4_tunnel(tun_id=0x7f10354,src=10.10.10.10,dst=20.20.20.20,tos=0x0,ttl=64,flags(key)),/
+s/\(eth([[^)]]*)\),*/\1,eth_type(0x8100),vlan(vid=99,pcp=7),encap(/
+s/$/)/' odp-base.txt
+
+ echo
  echo '# Valid forms with IP first fragment.'
 sed -n 's/,frag=no),/,frag=first),/p' odp-base.txt
 
-- 
1.7.10.4




More information about the dev mailing list