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.rajaha...@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 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev