Commit 0a6410fb authored by Jiri Benc's avatar Jiri Benc Committed by David S. Miller

openvswitch: netlink: support L3 packets

Extend the ovs flow netlink protocol to support L3 packets. Packets without
OVS_KEY_ATTR_ETHERNET attribute specify L3 packets; for those, the
OVS_KEY_ATTR_ETHERTYPE attribute is mandatory.

Push/pop vlan actions are only supported for Ethernet packets.

Based on previous versions by Lorand Jakab and Simon Horman.
Signed-off-by: default avatarLorand Jakab <lojakab@cisco.com>
Signed-off-by: default avatarSimon Horman <simon.horman@netronome.com>
Signed-off-by: default avatarJiri Benc <jbenc@redhat.com>
Acked-by: default avatarPravin B Shelar <pshelar@ovn.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 5108bbad
...@@ -123,7 +123,7 @@ static void update_range(struct sw_flow_match *match, ...@@ -123,7 +123,7 @@ static void update_range(struct sw_flow_match *match,
static bool match_validate(const struct sw_flow_match *match, static bool match_validate(const struct sw_flow_match *match,
u64 key_attrs, u64 mask_attrs, bool log) u64 key_attrs, u64 mask_attrs, bool log)
{ {
u64 key_expected = 1 << OVS_KEY_ATTR_ETHERNET; u64 key_expected = 0;
u64 mask_allowed = key_attrs; /* At most allow all key attributes */ u64 mask_allowed = key_attrs; /* At most allow all key attributes */
/* The following mask attributes allowed only if they /* The following mask attributes allowed only if they
...@@ -969,10 +969,33 @@ static int parse_vlan_from_nlattrs(struct sw_flow_match *match, ...@@ -969,10 +969,33 @@ static int parse_vlan_from_nlattrs(struct sw_flow_match *match,
return 0; return 0;
} }
static int parse_eth_type_from_nlattrs(struct sw_flow_match *match,
u64 *attrs, const struct nlattr **a,
bool is_mask, bool log)
{
__be16 eth_type;
eth_type = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]);
if (is_mask) {
/* Always exact match EtherType. */
eth_type = htons(0xffff);
} else if (!eth_proto_is_802_3(eth_type)) {
OVS_NLERR(log, "EtherType %x is less than min %x",
ntohs(eth_type), ETH_P_802_3_MIN);
return -EINVAL;
}
SW_FLOW_KEY_PUT(match, eth.type, eth_type, is_mask);
*attrs &= ~(1 << OVS_KEY_ATTR_ETHERTYPE);
return 0;
}
static int metadata_from_nlattrs(struct net *net, struct sw_flow_match *match, static int metadata_from_nlattrs(struct net *net, struct sw_flow_match *match,
u64 *attrs, const struct nlattr **a, u64 *attrs, const struct nlattr **a,
bool is_mask, bool log) bool is_mask, bool log)
{ {
u8 mac_proto = MAC_PROTO_ETHERNET;
if (*attrs & (1 << OVS_KEY_ATTR_DP_HASH)) { if (*attrs & (1 << OVS_KEY_ATTR_DP_HASH)) {
u32 hash_val = nla_get_u32(a[OVS_KEY_ATTR_DP_HASH]); u32 hash_val = nla_get_u32(a[OVS_KEY_ATTR_DP_HASH]);
...@@ -1060,9 +1083,19 @@ static int metadata_from_nlattrs(struct net *net, struct sw_flow_match *match, ...@@ -1060,9 +1083,19 @@ static int metadata_from_nlattrs(struct net *net, struct sw_flow_match *match,
*attrs &= ~(1ULL << OVS_KEY_ATTR_CT_LABELS); *attrs &= ~(1ULL << OVS_KEY_ATTR_CT_LABELS);
} }
/* For layer 3 packets the Ethernet type is provided
* and treated as metadata but no MAC addresses are provided.
*/
if (!(*attrs & (1ULL << OVS_KEY_ATTR_ETHERNET)) &&
(*attrs & (1ULL << OVS_KEY_ATTR_ETHERTYPE)))
mac_proto = MAC_PROTO_NONE;
/* Always exact match mac_proto */ /* Always exact match mac_proto */
SW_FLOW_KEY_PUT(match, mac_proto, is_mask ? 0xff : MAC_PROTO_ETHERNET, SW_FLOW_KEY_PUT(match, mac_proto, is_mask ? 0xff : mac_proto, is_mask);
is_mask);
if (mac_proto == MAC_PROTO_NONE)
return parse_eth_type_from_nlattrs(match, attrs, a, is_mask,
log);
return 0; return 0;
} }
...@@ -1086,33 +1119,26 @@ static int ovs_key_from_nlattrs(struct net *net, struct sw_flow_match *match, ...@@ -1086,33 +1119,26 @@ static int ovs_key_from_nlattrs(struct net *net, struct sw_flow_match *match,
SW_FLOW_KEY_MEMCPY(match, eth.dst, SW_FLOW_KEY_MEMCPY(match, eth.dst,
eth_key->eth_dst, ETH_ALEN, is_mask); eth_key->eth_dst, ETH_ALEN, is_mask);
attrs &= ~(1 << OVS_KEY_ATTR_ETHERNET); attrs &= ~(1 << OVS_KEY_ATTR_ETHERNET);
}
if (attrs & (1 << OVS_KEY_ATTR_VLAN)) {
/* VLAN attribute is always parsed before getting here since it
* may occur multiple times.
*/
OVS_NLERR(log, "VLAN attribute unexpected.");
return -EINVAL;
}
if (attrs & (1 << OVS_KEY_ATTR_ETHERTYPE)) {
__be16 eth_type;
eth_type = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]); if (attrs & (1 << OVS_KEY_ATTR_VLAN)) {
if (is_mask) { /* VLAN attribute is always parsed before getting here since it
/* Always exact match EtherType. */ * may occur multiple times.
eth_type = htons(0xffff); */
} else if (!eth_proto_is_802_3(eth_type)) { OVS_NLERR(log, "VLAN attribute unexpected.");
OVS_NLERR(log, "EtherType %x is less than min %x",
ntohs(eth_type), ETH_P_802_3_MIN);
return -EINVAL; return -EINVAL;
} }
SW_FLOW_KEY_PUT(match, eth.type, eth_type, is_mask); if (attrs & (1 << OVS_KEY_ATTR_ETHERTYPE)) {
attrs &= ~(1 << OVS_KEY_ATTR_ETHERTYPE); err = parse_eth_type_from_nlattrs(match, &attrs, a, is_mask,
} else if (!is_mask) { log);
SW_FLOW_KEY_PUT(match, eth.type, htons(ETH_P_802_2), is_mask); if (err)
return err;
} else if (!is_mask) {
SW_FLOW_KEY_PUT(match, eth.type, htons(ETH_P_802_2), is_mask);
}
} else if (!match->key->eth.type) {
OVS_NLERR(log, "Either Ethernet header or EtherType is required.");
return -EINVAL;
} }
if (attrs & (1 << OVS_KEY_ATTR_IPV4)) { if (attrs & (1 << OVS_KEY_ATTR_IPV4)) {
...@@ -1561,42 +1587,44 @@ static int __ovs_nla_put_key(const struct sw_flow_key *swkey, ...@@ -1561,42 +1587,44 @@ static int __ovs_nla_put_key(const struct sw_flow_key *swkey,
if (ovs_ct_put_key(output, skb)) if (ovs_ct_put_key(output, skb))
goto nla_put_failure; goto nla_put_failure;
nla = nla_reserve(skb, OVS_KEY_ATTR_ETHERNET, sizeof(*eth_key)); if (ovs_key_mac_proto(swkey) == MAC_PROTO_ETHERNET) {
if (!nla) nla = nla_reserve(skb, OVS_KEY_ATTR_ETHERNET, sizeof(*eth_key));
goto nla_put_failure; if (!nla)
eth_key = nla_data(nla);
ether_addr_copy(eth_key->eth_src, output->eth.src);
ether_addr_copy(eth_key->eth_dst, output->eth.dst);
if (swkey->eth.vlan.tci || eth_type_vlan(swkey->eth.type)) {
if (ovs_nla_put_vlan(skb, &output->eth.vlan, is_mask))
goto nla_put_failure; goto nla_put_failure;
encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP);
if (!swkey->eth.vlan.tci)
goto unencap;
if (swkey->eth.cvlan.tci || eth_type_vlan(swkey->eth.type)) { eth_key = nla_data(nla);
if (ovs_nla_put_vlan(skb, &output->eth.cvlan, is_mask)) ether_addr_copy(eth_key->eth_src, output->eth.src);
ether_addr_copy(eth_key->eth_dst, output->eth.dst);
if (swkey->eth.vlan.tci || eth_type_vlan(swkey->eth.type)) {
if (ovs_nla_put_vlan(skb, &output->eth.vlan, is_mask))
goto nla_put_failure; goto nla_put_failure;
in_encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP); encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP);
if (!swkey->eth.cvlan.tci) if (!swkey->eth.vlan.tci)
goto unencap; goto unencap;
if (swkey->eth.cvlan.tci || eth_type_vlan(swkey->eth.type)) {
if (ovs_nla_put_vlan(skb, &output->eth.cvlan, is_mask))
goto nla_put_failure;
in_encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP);
if (!swkey->eth.cvlan.tci)
goto unencap;
}
} }
}
if (swkey->eth.type == htons(ETH_P_802_2)) { if (swkey->eth.type == htons(ETH_P_802_2)) {
/* /*
* Ethertype 802.2 is represented in the netlink with omitted * Ethertype 802.2 is represented in the netlink with omitted
* OVS_KEY_ATTR_ETHERTYPE in the flow key attribute, and * OVS_KEY_ATTR_ETHERTYPE in the flow key attribute, and
* 0xffff in the mask attribute. Ethertype can also * 0xffff in the mask attribute. Ethertype can also
* be wildcarded. * be wildcarded.
*/ */
if (is_mask && output->eth.type) if (is_mask && output->eth.type)
if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE,
output->eth.type)) output->eth.type))
goto nla_put_failure; goto nla_put_failure;
goto unencap; goto unencap;
}
} }
if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, output->eth.type)) if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, output->eth.type))
...@@ -2131,8 +2159,8 @@ static bool validate_masked(u8 *data, int len) ...@@ -2131,8 +2159,8 @@ static bool validate_masked(u8 *data, int len)
static int validate_set(const struct nlattr *a, static int validate_set(const struct nlattr *a,
const struct sw_flow_key *flow_key, const struct sw_flow_key *flow_key,
struct sw_flow_actions **sfa, struct sw_flow_actions **sfa, bool *skip_copy,
bool *skip_copy, __be16 eth_type, bool masked, bool log) u8 mac_proto, __be16 eth_type, bool masked, bool log)
{ {
const struct nlattr *ovs_key = nla_data(a); const struct nlattr *ovs_key = nla_data(a);
int key_type = nla_type(ovs_key); int key_type = nla_type(ovs_key);
...@@ -2162,9 +2190,12 @@ static int validate_set(const struct nlattr *a, ...@@ -2162,9 +2190,12 @@ static int validate_set(const struct nlattr *a,
case OVS_KEY_ATTR_SKB_MARK: case OVS_KEY_ATTR_SKB_MARK:
case OVS_KEY_ATTR_CT_MARK: case OVS_KEY_ATTR_CT_MARK:
case OVS_KEY_ATTR_CT_LABELS: case OVS_KEY_ATTR_CT_LABELS:
case OVS_KEY_ATTR_ETHERNET:
break; break;
case OVS_KEY_ATTR_ETHERNET:
if (mac_proto != MAC_PROTO_ETHERNET)
return -EINVAL;
case OVS_KEY_ATTR_TUNNEL: case OVS_KEY_ATTR_TUNNEL:
if (masked) if (masked)
return -EINVAL; /* Masked tunnel set not supported. */ return -EINVAL; /* Masked tunnel set not supported. */
...@@ -2329,6 +2360,7 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, ...@@ -2329,6 +2360,7 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
int depth, struct sw_flow_actions **sfa, int depth, struct sw_flow_actions **sfa,
__be16 eth_type, __be16 vlan_tci, bool log) __be16 eth_type, __be16 vlan_tci, bool log)
{ {
u8 mac_proto = ovs_key_mac_proto(key);
const struct nlattr *a; const struct nlattr *a;
int rem, err; int rem, err;
...@@ -2399,10 +2431,14 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, ...@@ -2399,10 +2431,14 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
} }
case OVS_ACTION_ATTR_POP_VLAN: case OVS_ACTION_ATTR_POP_VLAN:
if (mac_proto != MAC_PROTO_ETHERNET)
return -EINVAL;
vlan_tci = htons(0); vlan_tci = htons(0);
break; break;
case OVS_ACTION_ATTR_PUSH_VLAN: case OVS_ACTION_ATTR_PUSH_VLAN:
if (mac_proto != MAC_PROTO_ETHERNET)
return -EINVAL;
vlan = nla_data(a); vlan = nla_data(a);
if (!eth_type_vlan(vlan->vlan_tpid)) if (!eth_type_vlan(vlan->vlan_tpid))
return -EINVAL; return -EINVAL;
...@@ -2452,14 +2488,16 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, ...@@ -2452,14 +2488,16 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
case OVS_ACTION_ATTR_SET: case OVS_ACTION_ATTR_SET:
err = validate_set(a, key, sfa, err = validate_set(a, key, sfa,
&skip_copy, eth_type, false, log); &skip_copy, mac_proto, eth_type,
false, log);
if (err) if (err)
return err; return err;
break; break;
case OVS_ACTION_ATTR_SET_MASKED: case OVS_ACTION_ATTR_SET_MASKED:
err = validate_set(a, key, sfa, err = validate_set(a, key, sfa,
&skip_copy, eth_type, true, log); &skip_copy, mac_proto, eth_type,
true, log);
if (err) if (err)
return err; return err;
break; break;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment