[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