|
@@ -808,6 +808,167 @@ int ovs_nla_put_tunnel_info(struct sk_buff *skb,
|
|
|
ip_tunnel_info_af(tun_info));
|
|
|
}
|
|
|
|
|
|
+static int encode_vlan_from_nlattrs(struct sw_flow_match *match,
|
|
|
+ const struct nlattr *a[],
|
|
|
+ bool is_mask, bool inner)
|
|
|
+{
|
|
|
+ __be16 tci = 0;
|
|
|
+ __be16 tpid = 0;
|
|
|
+
|
|
|
+ if (a[OVS_KEY_ATTR_VLAN])
|
|
|
+ tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]);
|
|
|
+
|
|
|
+ if (a[OVS_KEY_ATTR_ETHERTYPE])
|
|
|
+ tpid = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]);
|
|
|
+
|
|
|
+ if (likely(!inner)) {
|
|
|
+ SW_FLOW_KEY_PUT(match, eth.vlan.tpid, tpid, is_mask);
|
|
|
+ SW_FLOW_KEY_PUT(match, eth.vlan.tci, tci, is_mask);
|
|
|
+ } else {
|
|
|
+ SW_FLOW_KEY_PUT(match, eth.cvlan.tpid, tpid, is_mask);
|
|
|
+ SW_FLOW_KEY_PUT(match, eth.cvlan.tci, tci, is_mask);
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int validate_vlan_from_nlattrs(const struct sw_flow_match *match,
|
|
|
+ u64 key_attrs, bool inner,
|
|
|
+ const struct nlattr **a, bool log)
|
|
|
+{
|
|
|
+ __be16 tci = 0;
|
|
|
+
|
|
|
+ if (!((key_attrs & (1 << OVS_KEY_ATTR_ETHERNET)) &&
|
|
|
+ (key_attrs & (1 << OVS_KEY_ATTR_ETHERTYPE)) &&
|
|
|
+ eth_type_vlan(nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE])))) {
|
|
|
+ /* Not a VLAN. */
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!((key_attrs & (1 << OVS_KEY_ATTR_VLAN)) &&
|
|
|
+ (key_attrs & (1 << OVS_KEY_ATTR_ENCAP)))) {
|
|
|
+ OVS_NLERR(log, "Invalid %s frame", (inner) ? "C-VLAN" : "VLAN");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (a[OVS_KEY_ATTR_VLAN])
|
|
|
+ tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]);
|
|
|
+
|
|
|
+ if (!(tci & htons(VLAN_TAG_PRESENT))) {
|
|
|
+ if (tci) {
|
|
|
+ OVS_NLERR(log, "%s TCI does not have VLAN_TAG_PRESENT bit set.",
|
|
|
+ (inner) ? "C-VLAN" : "VLAN");
|
|
|
+ return -EINVAL;
|
|
|
+ } else if (nla_len(a[OVS_KEY_ATTR_ENCAP])) {
|
|
|
+ /* Corner case for truncated VLAN header. */
|
|
|
+ OVS_NLERR(log, "Truncated %s header has non-zero encap attribute.",
|
|
|
+ (inner) ? "C-VLAN" : "VLAN");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+static int validate_vlan_mask_from_nlattrs(const struct sw_flow_match *match,
|
|
|
+ u64 key_attrs, bool inner,
|
|
|
+ const struct nlattr **a, bool log)
|
|
|
+{
|
|
|
+ __be16 tci = 0;
|
|
|
+ __be16 tpid = 0;
|
|
|
+ bool encap_valid = !!(match->key->eth.vlan.tci &
|
|
|
+ htons(VLAN_TAG_PRESENT));
|
|
|
+ bool i_encap_valid = !!(match->key->eth.cvlan.tci &
|
|
|
+ htons(VLAN_TAG_PRESENT));
|
|
|
+
|
|
|
+ if (!(key_attrs & (1 << OVS_KEY_ATTR_ENCAP))) {
|
|
|
+ /* Not a VLAN. */
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((!inner && !encap_valid) || (inner && !i_encap_valid)) {
|
|
|
+ OVS_NLERR(log, "Encap mask attribute is set for non-%s frame.",
|
|
|
+ (inner) ? "C-VLAN" : "VLAN");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (a[OVS_KEY_ATTR_VLAN])
|
|
|
+ tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]);
|
|
|
+
|
|
|
+ if (a[OVS_KEY_ATTR_ETHERTYPE])
|
|
|
+ tpid = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]);
|
|
|
+
|
|
|
+ if (tpid != htons(0xffff)) {
|
|
|
+ OVS_NLERR(log, "Must have an exact match on %s TPID (mask=%x).",
|
|
|
+ (inner) ? "C-VLAN" : "VLAN", ntohs(tpid));
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ if (!(tci & htons(VLAN_TAG_PRESENT))) {
|
|
|
+ OVS_NLERR(log, "%s TCI mask does not have exact match for VLAN_TAG_PRESENT bit.",
|
|
|
+ (inner) ? "C-VLAN" : "VLAN");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+static int __parse_vlan_from_nlattrs(struct sw_flow_match *match,
|
|
|
+ u64 *key_attrs, bool inner,
|
|
|
+ const struct nlattr **a, bool is_mask,
|
|
|
+ bool log)
|
|
|
+{
|
|
|
+ int err;
|
|
|
+ const struct nlattr *encap;
|
|
|
+
|
|
|
+ if (!is_mask)
|
|
|
+ err = validate_vlan_from_nlattrs(match, *key_attrs, inner,
|
|
|
+ a, log);
|
|
|
+ else
|
|
|
+ err = validate_vlan_mask_from_nlattrs(match, *key_attrs, inner,
|
|
|
+ a, log);
|
|
|
+ if (err <= 0)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ err = encode_vlan_from_nlattrs(match, a, is_mask, inner);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ *key_attrs &= ~(1 << OVS_KEY_ATTR_ENCAP);
|
|
|
+ *key_attrs &= ~(1 << OVS_KEY_ATTR_VLAN);
|
|
|
+ *key_attrs &= ~(1 << OVS_KEY_ATTR_ETHERTYPE);
|
|
|
+
|
|
|
+ encap = a[OVS_KEY_ATTR_ENCAP];
|
|
|
+
|
|
|
+ if (!is_mask)
|
|
|
+ err = parse_flow_nlattrs(encap, a, key_attrs, log);
|
|
|
+ else
|
|
|
+ err = parse_flow_mask_nlattrs(encap, a, key_attrs, log);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static int parse_vlan_from_nlattrs(struct sw_flow_match *match,
|
|
|
+ u64 *key_attrs, const struct nlattr **a,
|
|
|
+ bool is_mask, bool log)
|
|
|
+{
|
|
|
+ int err;
|
|
|
+ bool encap_valid = false;
|
|
|
+
|
|
|
+ err = __parse_vlan_from_nlattrs(match, key_attrs, false, a,
|
|
|
+ is_mask, log);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ encap_valid = !!(match->key->eth.vlan.tci & htons(VLAN_TAG_PRESENT));
|
|
|
+ if (encap_valid) {
|
|
|
+ err = __parse_vlan_from_nlattrs(match, key_attrs, true, a,
|
|
|
+ is_mask, log);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int metadata_from_nlattrs(struct net *net, struct sw_flow_match *match,
|
|
|
u64 *attrs, const struct nlattr **a,
|
|
|
bool is_mask, bool log)
|
|
@@ -923,20 +1084,11 @@ static int ovs_key_from_nlattrs(struct net *net, struct sw_flow_match *match,
|
|
|
}
|
|
|
|
|
|
if (attrs & (1 << OVS_KEY_ATTR_VLAN)) {
|
|
|
- __be16 tci;
|
|
|
-
|
|
|
- tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]);
|
|
|
- if (!(tci & htons(VLAN_TAG_PRESENT))) {
|
|
|
- if (is_mask)
|
|
|
- OVS_NLERR(log, "VLAN TCI mask does not have exact match for VLAN_TAG_PRESENT bit.");
|
|
|
- else
|
|
|
- OVS_NLERR(log, "VLAN TCI does not have VLAN_TAG_PRESENT bit set.");
|
|
|
-
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
-
|
|
|
- SW_FLOW_KEY_PUT(match, eth.tci, tci, is_mask);
|
|
|
- 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)) {
|
|
@@ -1182,49 +1334,18 @@ int ovs_nla_get_match(struct net *net, struct sw_flow_match *match,
|
|
|
bool log)
|
|
|
{
|
|
|
const struct nlattr *a[OVS_KEY_ATTR_MAX + 1];
|
|
|
- const struct nlattr *encap;
|
|
|
struct nlattr *newmask = NULL;
|
|
|
u64 key_attrs = 0;
|
|
|
u64 mask_attrs = 0;
|
|
|
- bool encap_valid = false;
|
|
|
int err;
|
|
|
|
|
|
err = parse_flow_nlattrs(nla_key, a, &key_attrs, log);
|
|
|
if (err)
|
|
|
return err;
|
|
|
|
|
|
- if ((key_attrs & (1 << OVS_KEY_ATTR_ETHERNET)) &&
|
|
|
- (key_attrs & (1 << OVS_KEY_ATTR_ETHERTYPE)) &&
|
|
|
- (nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]) == htons(ETH_P_8021Q))) {
|
|
|
- __be16 tci;
|
|
|
-
|
|
|
- if (!((key_attrs & (1 << OVS_KEY_ATTR_VLAN)) &&
|
|
|
- (key_attrs & (1 << OVS_KEY_ATTR_ENCAP)))) {
|
|
|
- OVS_NLERR(log, "Invalid Vlan frame.");
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
-
|
|
|
- key_attrs &= ~(1 << OVS_KEY_ATTR_ETHERTYPE);
|
|
|
- tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]);
|
|
|
- encap = a[OVS_KEY_ATTR_ENCAP];
|
|
|
- key_attrs &= ~(1 << OVS_KEY_ATTR_ENCAP);
|
|
|
- encap_valid = true;
|
|
|
-
|
|
|
- if (tci & htons(VLAN_TAG_PRESENT)) {
|
|
|
- err = parse_flow_nlattrs(encap, a, &key_attrs, log);
|
|
|
- if (err)
|
|
|
- return err;
|
|
|
- } else if (!tci) {
|
|
|
- /* Corner case for truncated 802.1Q header. */
|
|
|
- if (nla_len(encap)) {
|
|
|
- OVS_NLERR(log, "Truncated 802.1Q header has non-zero encap attribute.");
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
- } else {
|
|
|
- OVS_NLERR(log, "Encap attr is set for non-VLAN frame");
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
- }
|
|
|
+ err = parse_vlan_from_nlattrs(match, &key_attrs, a, false, log);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
|
|
|
err = ovs_key_from_nlattrs(net, match, key_attrs, a, false, log);
|
|
|
if (err)
|
|
@@ -1265,46 +1386,12 @@ int ovs_nla_get_match(struct net *net, struct sw_flow_match *match,
|
|
|
goto free_newmask;
|
|
|
|
|
|
/* Always match on tci. */
|
|
|
- SW_FLOW_KEY_PUT(match, eth.tci, htons(0xffff), true);
|
|
|
-
|
|
|
- if (mask_attrs & 1 << OVS_KEY_ATTR_ENCAP) {
|
|
|
- __be16 eth_type = 0;
|
|
|
- __be16 tci = 0;
|
|
|
-
|
|
|
- if (!encap_valid) {
|
|
|
- OVS_NLERR(log, "Encap mask attribute is set for non-VLAN frame.");
|
|
|
- err = -EINVAL;
|
|
|
- goto free_newmask;
|
|
|
- }
|
|
|
-
|
|
|
- mask_attrs &= ~(1 << OVS_KEY_ATTR_ENCAP);
|
|
|
- if (a[OVS_KEY_ATTR_ETHERTYPE])
|
|
|
- eth_type = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]);
|
|
|
-
|
|
|
- if (eth_type == htons(0xffff)) {
|
|
|
- mask_attrs &= ~(1 << OVS_KEY_ATTR_ETHERTYPE);
|
|
|
- encap = a[OVS_KEY_ATTR_ENCAP];
|
|
|
- err = parse_flow_mask_nlattrs(encap, a,
|
|
|
- &mask_attrs, log);
|
|
|
- if (err)
|
|
|
- goto free_newmask;
|
|
|
- } else {
|
|
|
- OVS_NLERR(log, "VLAN frames must have an exact match on the TPID (mask=%x).",
|
|
|
- ntohs(eth_type));
|
|
|
- err = -EINVAL;
|
|
|
- goto free_newmask;
|
|
|
- }
|
|
|
-
|
|
|
- if (a[OVS_KEY_ATTR_VLAN])
|
|
|
- tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]);
|
|
|
+ SW_FLOW_KEY_PUT(match, eth.vlan.tci, htons(0xffff), true);
|
|
|
+ SW_FLOW_KEY_PUT(match, eth.cvlan.tci, htons(0xffff), true);
|
|
|
|
|
|
- if (!(tci & htons(VLAN_TAG_PRESENT))) {
|
|
|
- OVS_NLERR(log, "VLAN tag present bit must have an exact match (tci_mask=%x).",
|
|
|
- ntohs(tci));
|
|
|
- err = -EINVAL;
|
|
|
- goto free_newmask;
|
|
|
- }
|
|
|
- }
|
|
|
+ err = parse_vlan_from_nlattrs(match, &mask_attrs, a, true, log);
|
|
|
+ if (err)
|
|
|
+ goto free_newmask;
|
|
|
|
|
|
err = ovs_key_from_nlattrs(net, match, mask_attrs, a, true,
|
|
|
log);
|
|
@@ -1410,12 +1497,25 @@ int ovs_nla_get_flow_metadata(struct net *net, const struct nlattr *attr,
|
|
|
return metadata_from_nlattrs(net, &match, &attrs, a, false, log);
|
|
|
}
|
|
|
|
|
|
+static int ovs_nla_put_vlan(struct sk_buff *skb, const struct vlan_head *vh,
|
|
|
+ bool is_mask)
|
|
|
+{
|
|
|
+ __be16 eth_type = !is_mask ? vh->tpid : htons(0xffff);
|
|
|
+
|
|
|
+ if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, eth_type) ||
|
|
|
+ nla_put_be16(skb, OVS_KEY_ATTR_VLAN, vh->tci))
|
|
|
+ return -EMSGSIZE;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int __ovs_nla_put_key(const struct sw_flow_key *swkey,
|
|
|
const struct sw_flow_key *output, bool is_mask,
|
|
|
struct sk_buff *skb)
|
|
|
{
|
|
|
struct ovs_key_ethernet *eth_key;
|
|
|
- struct nlattr *nla, *encap;
|
|
|
+ struct nlattr *nla;
|
|
|
+ struct nlattr *encap = NULL;
|
|
|
+ struct nlattr *in_encap = NULL;
|
|
|
|
|
|
if (nla_put_u32(skb, OVS_KEY_ATTR_RECIRC_ID, output->recirc_id))
|
|
|
goto nla_put_failure;
|
|
@@ -1464,17 +1564,21 @@ static int __ovs_nla_put_key(const struct sw_flow_key *swkey,
|
|
|
ether_addr_copy(eth_key->eth_src, output->eth.src);
|
|
|
ether_addr_copy(eth_key->eth_dst, output->eth.dst);
|
|
|
|
|
|
- if (swkey->eth.tci || swkey->eth.type == htons(ETH_P_8021Q)) {
|
|
|
- __be16 eth_type;
|
|
|
- eth_type = !is_mask ? htons(ETH_P_8021Q) : htons(0xffff);
|
|
|
- if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, eth_type) ||
|
|
|
- nla_put_be16(skb, OVS_KEY_ATTR_VLAN, output->eth.tci))
|
|
|
+ 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;
|
|
|
encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP);
|
|
|
- if (!swkey->eth.tci)
|
|
|
+ if (!swkey->eth.vlan.tci)
|
|
|
goto unencap;
|
|
|
- } else
|
|
|
- encap = NULL;
|
|
|
+
|
|
|
+ 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)) {
|
|
|
/*
|
|
@@ -1493,6 +1597,14 @@ static int __ovs_nla_put_key(const struct sw_flow_key *swkey,
|
|
|
if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, output->eth.type))
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
+ if (eth_type_vlan(swkey->eth.type)) {
|
|
|
+ /* There are 3 VLAN tags, we don't know anything about the rest
|
|
|
+ * of the packet, so truncate here.
|
|
|
+ */
|
|
|
+ WARN_ON_ONCE(!(encap && in_encap));
|
|
|
+ goto unencap;
|
|
|
+ }
|
|
|
+
|
|
|
if (swkey->eth.type == htons(ETH_P_IP)) {
|
|
|
struct ovs_key_ipv4 *ipv4_key;
|
|
|
|
|
@@ -1619,6 +1731,8 @@ static int __ovs_nla_put_key(const struct sw_flow_key *swkey,
|
|
|
}
|
|
|
|
|
|
unencap:
|
|
|
+ if (in_encap)
|
|
|
+ nla_nest_end(skb, in_encap);
|
|
|
if (encap)
|
|
|
nla_nest_end(skb, encap);
|
|
|
|
|
@@ -2283,7 +2397,7 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
|
|
|
|
|
|
case OVS_ACTION_ATTR_PUSH_VLAN:
|
|
|
vlan = nla_data(a);
|
|
|
- if (vlan->vlan_tpid != htons(ETH_P_8021Q))
|
|
|
+ if (!eth_type_vlan(vlan->vlan_tpid))
|
|
|
return -EINVAL;
|
|
|
if (!(vlan->vlan_tci & htons(VLAN_TAG_PRESENT)))
|
|
|
return -EINVAL;
|
|
@@ -2388,7 +2502,7 @@ int ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
|
|
|
|
|
|
(*sfa)->orig_len = nla_len(attr);
|
|
|
err = __ovs_nla_copy_actions(net, attr, key, 0, sfa, key->eth.type,
|
|
|
- key->eth.tci, log);
|
|
|
+ key->eth.vlan.tci, log);
|
|
|
if (err)
|
|
|
ovs_nla_free_flow_actions(*sfa);
|
|
|
|