|
@@ -46,24 +46,22 @@
|
|
|
#include <net/ip.h>
|
|
|
#include <net/ipv6.h>
|
|
|
#include <net/ndisc.h>
|
|
|
+#include <net/mpls.h>
|
|
|
|
|
|
#include "flow_netlink.h"
|
|
|
|
|
|
-static void update_range__(struct sw_flow_match *match,
|
|
|
- size_t offset, size_t size, bool is_mask)
|
|
|
+static void update_range(struct sw_flow_match *match,
|
|
|
+ size_t offset, size_t size, bool is_mask)
|
|
|
{
|
|
|
- struct sw_flow_key_range *range = NULL;
|
|
|
+ struct sw_flow_key_range *range;
|
|
|
size_t start = rounddown(offset, sizeof(long));
|
|
|
size_t end = roundup(offset + size, sizeof(long));
|
|
|
|
|
|
if (!is_mask)
|
|
|
range = &match->range;
|
|
|
- else if (match->mask)
|
|
|
+ else
|
|
|
range = &match->mask->range;
|
|
|
|
|
|
- if (!range)
|
|
|
- return;
|
|
|
-
|
|
|
if (range->start == range->end) {
|
|
|
range->start = start;
|
|
|
range->end = end;
|
|
@@ -79,22 +77,20 @@ static void update_range__(struct sw_flow_match *match,
|
|
|
|
|
|
#define SW_FLOW_KEY_PUT(match, field, value, is_mask) \
|
|
|
do { \
|
|
|
- update_range__(match, offsetof(struct sw_flow_key, field), \
|
|
|
- sizeof((match)->key->field), is_mask); \
|
|
|
- if (is_mask) { \
|
|
|
- if ((match)->mask) \
|
|
|
- (match)->mask->key.field = value; \
|
|
|
- } else { \
|
|
|
+ update_range(match, offsetof(struct sw_flow_key, field), \
|
|
|
+ sizeof((match)->key->field), is_mask); \
|
|
|
+ if (is_mask) \
|
|
|
+ (match)->mask->key.field = value; \
|
|
|
+ else \
|
|
|
(match)->key->field = value; \
|
|
|
- } \
|
|
|
} while (0)
|
|
|
|
|
|
#define SW_FLOW_KEY_MEMCPY_OFFSET(match, offset, value_p, len, is_mask) \
|
|
|
do { \
|
|
|
- update_range__(match, offset, len, is_mask); \
|
|
|
+ update_range(match, offset, len, is_mask); \
|
|
|
if (is_mask) \
|
|
|
memcpy((u8 *)&(match)->mask->key + offset, value_p, \
|
|
|
- len); \
|
|
|
+ len); \
|
|
|
else \
|
|
|
memcpy((u8 *)(match)->key + offset, value_p, len); \
|
|
|
} while (0)
|
|
@@ -103,18 +99,16 @@ static void update_range__(struct sw_flow_match *match,
|
|
|
SW_FLOW_KEY_MEMCPY_OFFSET(match, offsetof(struct sw_flow_key, field), \
|
|
|
value_p, len, is_mask)
|
|
|
|
|
|
-#define SW_FLOW_KEY_MEMSET_FIELD(match, field, value, is_mask) \
|
|
|
- do { \
|
|
|
- update_range__(match, offsetof(struct sw_flow_key, field), \
|
|
|
- sizeof((match)->key->field), is_mask); \
|
|
|
- if (is_mask) { \
|
|
|
- if ((match)->mask) \
|
|
|
- memset((u8 *)&(match)->mask->key.field, value,\
|
|
|
- sizeof((match)->mask->key.field)); \
|
|
|
- } else { \
|
|
|
+#define SW_FLOW_KEY_MEMSET_FIELD(match, field, value, is_mask) \
|
|
|
+ do { \
|
|
|
+ update_range(match, offsetof(struct sw_flow_key, field), \
|
|
|
+ sizeof((match)->key->field), is_mask); \
|
|
|
+ if (is_mask) \
|
|
|
+ memset((u8 *)&(match)->mask->key.field, value, \
|
|
|
+ sizeof((match)->mask->key.field)); \
|
|
|
+ else \
|
|
|
memset((u8 *)&(match)->key->field, value, \
|
|
|
sizeof((match)->key->field)); \
|
|
|
- } \
|
|
|
} while (0)
|
|
|
|
|
|
static bool match_validate(const struct sw_flow_match *match,
|
|
@@ -134,7 +128,8 @@ static bool match_validate(const struct sw_flow_match *match,
|
|
|
| (1 << OVS_KEY_ATTR_ICMP)
|
|
|
| (1 << OVS_KEY_ATTR_ICMPV6)
|
|
|
| (1 << OVS_KEY_ATTR_ARP)
|
|
|
- | (1 << OVS_KEY_ATTR_ND));
|
|
|
+ | (1 << OVS_KEY_ATTR_ND)
|
|
|
+ | (1 << OVS_KEY_ATTR_MPLS));
|
|
|
|
|
|
/* Always allowed mask fields. */
|
|
|
mask_allowed |= ((1 << OVS_KEY_ATTR_TUNNEL)
|
|
@@ -149,6 +144,12 @@ static bool match_validate(const struct sw_flow_match *match,
|
|
|
mask_allowed |= 1 << OVS_KEY_ATTR_ARP;
|
|
|
}
|
|
|
|
|
|
+ if (eth_p_mpls(match->key->eth.type)) {
|
|
|
+ key_expected |= 1 << OVS_KEY_ATTR_MPLS;
|
|
|
+ if (match->mask && (match->mask->key.eth.type == htons(0xffff)))
|
|
|
+ mask_allowed |= 1 << OVS_KEY_ATTR_MPLS;
|
|
|
+ }
|
|
|
+
|
|
|
if (match->key->eth.type == htons(ETH_P_IP)) {
|
|
|
key_expected |= 1 << OVS_KEY_ATTR_IPV4;
|
|
|
if (match->mask && (match->mask->key.eth.type == htons(0xffff)))
|
|
@@ -244,6 +245,38 @@ static bool match_validate(const struct sw_flow_match *match,
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
+size_t ovs_key_attr_size(void)
|
|
|
+{
|
|
|
+ /* Whenever adding new OVS_KEY_ FIELDS, we should consider
|
|
|
+ * updating this function.
|
|
|
+ */
|
|
|
+ BUILD_BUG_ON(OVS_KEY_ATTR_TUNNEL_INFO != 22);
|
|
|
+
|
|
|
+ return nla_total_size(4) /* OVS_KEY_ATTR_PRIORITY */
|
|
|
+ + nla_total_size(0) /* OVS_KEY_ATTR_TUNNEL */
|
|
|
+ + nla_total_size(8) /* OVS_TUNNEL_KEY_ATTR_ID */
|
|
|
+ + nla_total_size(4) /* OVS_TUNNEL_KEY_ATTR_IPV4_SRC */
|
|
|
+ + nla_total_size(4) /* OVS_TUNNEL_KEY_ATTR_IPV4_DST */
|
|
|
+ + nla_total_size(1) /* OVS_TUNNEL_KEY_ATTR_TOS */
|
|
|
+ + nla_total_size(1) /* OVS_TUNNEL_KEY_ATTR_TTL */
|
|
|
+ + nla_total_size(0) /* OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT */
|
|
|
+ + nla_total_size(0) /* OVS_TUNNEL_KEY_ATTR_CSUM */
|
|
|
+ + nla_total_size(0) /* OVS_TUNNEL_KEY_ATTR_OAM */
|
|
|
+ + nla_total_size(256) /* OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS */
|
|
|
+ + nla_total_size(4) /* OVS_KEY_ATTR_IN_PORT */
|
|
|
+ + nla_total_size(4) /* OVS_KEY_ATTR_SKB_MARK */
|
|
|
+ + nla_total_size(4) /* OVS_KEY_ATTR_DP_HASH */
|
|
|
+ + nla_total_size(4) /* OVS_KEY_ATTR_RECIRC_ID */
|
|
|
+ + nla_total_size(12) /* OVS_KEY_ATTR_ETHERNET */
|
|
|
+ + nla_total_size(2) /* OVS_KEY_ATTR_ETHERTYPE */
|
|
|
+ + nla_total_size(4) /* OVS_KEY_ATTR_VLAN */
|
|
|
+ + nla_total_size(0) /* OVS_KEY_ATTR_ENCAP */
|
|
|
+ + nla_total_size(2) /* OVS_KEY_ATTR_ETHERTYPE */
|
|
|
+ + nla_total_size(40) /* OVS_KEY_ATTR_IPV6 */
|
|
|
+ + nla_total_size(2) /* OVS_KEY_ATTR_ICMPV6 */
|
|
|
+ + nla_total_size(28); /* OVS_KEY_ATTR_ND */
|
|
|
+}
|
|
|
+
|
|
|
/* The size of the argument for each %OVS_KEY_ATTR_* Netlink attribute. */
|
|
|
static const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = {
|
|
|
[OVS_KEY_ATTR_ENCAP] = -1,
|
|
@@ -266,6 +299,7 @@ static const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = {
|
|
|
[OVS_KEY_ATTR_RECIRC_ID] = sizeof(u32),
|
|
|
[OVS_KEY_ATTR_DP_HASH] = sizeof(u32),
|
|
|
[OVS_KEY_ATTR_TUNNEL] = -1,
|
|
|
+ [OVS_KEY_ATTR_MPLS] = sizeof(struct ovs_key_mpls),
|
|
|
};
|
|
|
|
|
|
static bool is_all_zero(const u8 *fp, size_t size)
|
|
@@ -572,10 +606,13 @@ static int metadata_from_nlattrs(struct sw_flow_match *match, u64 *attrs,
|
|
|
if (*attrs & (1 << OVS_KEY_ATTR_IN_PORT)) {
|
|
|
u32 in_port = nla_get_u32(a[OVS_KEY_ATTR_IN_PORT]);
|
|
|
|
|
|
- if (is_mask)
|
|
|
+ if (is_mask) {
|
|
|
in_port = 0xffffffff; /* Always exact match in_port. */
|
|
|
- else if (in_port >= DP_MAX_PORTS)
|
|
|
+ } else if (in_port >= DP_MAX_PORTS) {
|
|
|
+ OVS_NLERR("Port (%d) exceeds maximum allowable (%d).\n",
|
|
|
+ in_port, DP_MAX_PORTS);
|
|
|
return -EINVAL;
|
|
|
+ }
|
|
|
|
|
|
SW_FLOW_KEY_PUT(match, phy.in_port, in_port, is_mask);
|
|
|
*attrs &= ~(1 << OVS_KEY_ATTR_IN_PORT);
|
|
@@ -602,7 +639,6 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs,
|
|
|
const struct nlattr **a, bool is_mask)
|
|
|
{
|
|
|
int err;
|
|
|
- u64 orig_attrs = attrs;
|
|
|
|
|
|
err = metadata_from_nlattrs(match, &attrs, a, is_mask);
|
|
|
if (err)
|
|
@@ -634,8 +670,7 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs,
|
|
|
|
|
|
SW_FLOW_KEY_PUT(match, eth.tci, tci, is_mask);
|
|
|
attrs &= ~(1 << OVS_KEY_ATTR_VLAN);
|
|
|
- } else if (!is_mask)
|
|
|
- SW_FLOW_KEY_PUT(match, eth.tci, htons(0xffff), true);
|
|
|
+ }
|
|
|
|
|
|
if (attrs & (1 << OVS_KEY_ATTR_ETHERTYPE)) {
|
|
|
__be16 eth_type;
|
|
@@ -735,6 +770,16 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs,
|
|
|
attrs &= ~(1 << OVS_KEY_ATTR_ARP);
|
|
|
}
|
|
|
|
|
|
+ if (attrs & (1 << OVS_KEY_ATTR_MPLS)) {
|
|
|
+ const struct ovs_key_mpls *mpls_key;
|
|
|
+
|
|
|
+ mpls_key = nla_data(a[OVS_KEY_ATTR_MPLS]);
|
|
|
+ SW_FLOW_KEY_PUT(match, mpls.top_lse,
|
|
|
+ mpls_key->mpls_lse, is_mask);
|
|
|
+
|
|
|
+ attrs &= ~(1 << OVS_KEY_ATTR_MPLS);
|
|
|
+ }
|
|
|
+
|
|
|
if (attrs & (1 << OVS_KEY_ATTR_TCP)) {
|
|
|
const struct ovs_key_tcp *tcp_key;
|
|
|
|
|
@@ -745,15 +790,9 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs,
|
|
|
}
|
|
|
|
|
|
if (attrs & (1 << OVS_KEY_ATTR_TCP_FLAGS)) {
|
|
|
- if (orig_attrs & (1 << OVS_KEY_ATTR_IPV4)) {
|
|
|
- SW_FLOW_KEY_PUT(match, tp.flags,
|
|
|
- nla_get_be16(a[OVS_KEY_ATTR_TCP_FLAGS]),
|
|
|
- is_mask);
|
|
|
- } else {
|
|
|
- SW_FLOW_KEY_PUT(match, tp.flags,
|
|
|
- nla_get_be16(a[OVS_KEY_ATTR_TCP_FLAGS]),
|
|
|
- is_mask);
|
|
|
- }
|
|
|
+ SW_FLOW_KEY_PUT(match, tp.flags,
|
|
|
+ nla_get_be16(a[OVS_KEY_ATTR_TCP_FLAGS]),
|
|
|
+ is_mask);
|
|
|
attrs &= ~(1 << OVS_KEY_ATTR_TCP_FLAGS);
|
|
|
}
|
|
|
|
|
@@ -812,8 +851,11 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs,
|
|
|
attrs &= ~(1 << OVS_KEY_ATTR_ND);
|
|
|
}
|
|
|
|
|
|
- if (attrs != 0)
|
|
|
+ if (attrs != 0) {
|
|
|
+ OVS_NLERR("Unknown key attributes (%llx).\n",
|
|
|
+ (unsigned long long)attrs);
|
|
|
return -EINVAL;
|
|
|
+ }
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -853,8 +895,8 @@ static void mask_set_nlattr(struct nlattr *attr, u8 val)
|
|
|
* attribute specifies the mask field of the wildcarded flow.
|
|
|
*/
|
|
|
int ovs_nla_get_match(struct sw_flow_match *match,
|
|
|
- const struct nlattr *key,
|
|
|
- const struct nlattr *mask)
|
|
|
+ const struct nlattr *nla_key,
|
|
|
+ const struct nlattr *nla_mask)
|
|
|
{
|
|
|
const struct nlattr *a[OVS_KEY_ATTR_MAX + 1];
|
|
|
const struct nlattr *encap;
|
|
@@ -864,7 +906,7 @@ int ovs_nla_get_match(struct sw_flow_match *match,
|
|
|
bool encap_valid = false;
|
|
|
int err;
|
|
|
|
|
|
- err = parse_flow_nlattrs(key, a, &key_attrs);
|
|
|
+ err = parse_flow_nlattrs(nla_key, a, &key_attrs);
|
|
|
if (err)
|
|
|
return err;
|
|
|
|
|
@@ -905,36 +947,43 @@ int ovs_nla_get_match(struct sw_flow_match *match,
|
|
|
if (err)
|
|
|
return err;
|
|
|
|
|
|
- if (match->mask && !mask) {
|
|
|
- /* Create an exact match mask. We need to set to 0xff all the
|
|
|
- * 'match->mask' fields that have been touched in 'match->key'.
|
|
|
- * We cannot simply memset 'match->mask', because padding bytes
|
|
|
- * and fields not specified in 'match->key' should be left to 0.
|
|
|
- * Instead, we use a stream of netlink attributes, copied from
|
|
|
- * 'key' and set to 0xff: ovs_key_from_nlattrs() will take care
|
|
|
- * of filling 'match->mask' appropriately.
|
|
|
- */
|
|
|
- newmask = kmemdup(key, nla_total_size(nla_len(key)),
|
|
|
- GFP_KERNEL);
|
|
|
- if (!newmask)
|
|
|
- return -ENOMEM;
|
|
|
+ if (match->mask) {
|
|
|
+ if (!nla_mask) {
|
|
|
+ /* Create an exact match mask. We need to set to 0xff
|
|
|
+ * all the 'match->mask' fields that have been touched
|
|
|
+ * in 'match->key'. We cannot simply memset
|
|
|
+ * 'match->mask', because padding bytes and fields not
|
|
|
+ * specified in 'match->key' should be left to 0.
|
|
|
+ * Instead, we use a stream of netlink attributes,
|
|
|
+ * copied from 'key' and set to 0xff.
|
|
|
+ * ovs_key_from_nlattrs() will take care of filling
|
|
|
+ * 'match->mask' appropriately.
|
|
|
+ */
|
|
|
+ newmask = kmemdup(nla_key,
|
|
|
+ nla_total_size(nla_len(nla_key)),
|
|
|
+ GFP_KERNEL);
|
|
|
+ if (!newmask)
|
|
|
+ return -ENOMEM;
|
|
|
|
|
|
- mask_set_nlattr(newmask, 0xff);
|
|
|
+ mask_set_nlattr(newmask, 0xff);
|
|
|
|
|
|
- /* The userspace does not send tunnel attributes that are 0,
|
|
|
- * but we should not wildcard them nonetheless.
|
|
|
- */
|
|
|
- if (match->key->tun_key.ipv4_dst)
|
|
|
- SW_FLOW_KEY_MEMSET_FIELD(match, tun_key, 0xff, true);
|
|
|
+ /* The userspace does not send tunnel attributes that
|
|
|
+ * are 0, but we should not wildcard them nonetheless.
|
|
|
+ */
|
|
|
+ if (match->key->tun_key.ipv4_dst)
|
|
|
+ SW_FLOW_KEY_MEMSET_FIELD(match, tun_key,
|
|
|
+ 0xff, true);
|
|
|
|
|
|
- mask = newmask;
|
|
|
- }
|
|
|
+ nla_mask = newmask;
|
|
|
+ }
|
|
|
|
|
|
- if (mask) {
|
|
|
- err = parse_flow_mask_nlattrs(mask, a, &mask_attrs);
|
|
|
+ err = parse_flow_mask_nlattrs(nla_mask, a, &mask_attrs);
|
|
|
if (err)
|
|
|
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;
|
|
@@ -1140,6 +1189,14 @@ int ovs_nla_put_flow(const struct sw_flow_key *swkey,
|
|
|
arp_key->arp_op = htons(output->ip.proto);
|
|
|
ether_addr_copy(arp_key->arp_sha, output->ipv4.arp.sha);
|
|
|
ether_addr_copy(arp_key->arp_tha, output->ipv4.arp.tha);
|
|
|
+ } else if (eth_p_mpls(swkey->eth.type)) {
|
|
|
+ struct ovs_key_mpls *mpls_key;
|
|
|
+
|
|
|
+ nla = nla_reserve(skb, OVS_KEY_ATTR_MPLS, sizeof(*mpls_key));
|
|
|
+ if (!nla)
|
|
|
+ goto nla_put_failure;
|
|
|
+ mpls_key = nla_data(nla);
|
|
|
+ mpls_key->mpls_lse = output->mpls.top_lse;
|
|
|
}
|
|
|
|
|
|
if ((swkey->eth.type == htons(ETH_P_IP) ||
|
|
@@ -1226,12 +1283,14 @@ nla_put_failure:
|
|
|
|
|
|
#define MAX_ACTIONS_BUFSIZE (32 * 1024)
|
|
|
|
|
|
-struct sw_flow_actions *ovs_nla_alloc_flow_actions(int size)
|
|
|
+static struct sw_flow_actions *nla_alloc_flow_actions(int size)
|
|
|
{
|
|
|
struct sw_flow_actions *sfa;
|
|
|
|
|
|
- if (size > MAX_ACTIONS_BUFSIZE)
|
|
|
+ if (size > MAX_ACTIONS_BUFSIZE) {
|
|
|
+ OVS_NLERR("Flow action size (%u bytes) exceeds maximum", size);
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
+ }
|
|
|
|
|
|
sfa = kmalloc(sizeof(*sfa) + size, GFP_KERNEL);
|
|
|
if (!sfa)
|
|
@@ -1269,7 +1328,7 @@ static struct nlattr *reserve_sfa_size(struct sw_flow_actions **sfa,
|
|
|
new_acts_size = MAX_ACTIONS_BUFSIZE;
|
|
|
}
|
|
|
|
|
|
- acts = ovs_nla_alloc_flow_actions(new_acts_size);
|
|
|
+ acts = nla_alloc_flow_actions(new_acts_size);
|
|
|
if (IS_ERR(acts))
|
|
|
return (void *)acts;
|
|
|
|
|
@@ -1336,9 +1395,15 @@ static inline void add_nested_action_end(struct sw_flow_actions *sfa,
|
|
|
a->nla_len = sfa->actions_len - st_offset;
|
|
|
}
|
|
|
|
|
|
+static int __ovs_nla_copy_actions(const struct nlattr *attr,
|
|
|
+ const struct sw_flow_key *key,
|
|
|
+ int depth, struct sw_flow_actions **sfa,
|
|
|
+ __be16 eth_type, __be16 vlan_tci);
|
|
|
+
|
|
|
static int validate_and_copy_sample(const struct nlattr *attr,
|
|
|
const struct sw_flow_key *key, int depth,
|
|
|
- struct sw_flow_actions **sfa)
|
|
|
+ struct sw_flow_actions **sfa,
|
|
|
+ __be16 eth_type, __be16 vlan_tci)
|
|
|
{
|
|
|
const struct nlattr *attrs[OVS_SAMPLE_ATTR_MAX + 1];
|
|
|
const struct nlattr *probability, *actions;
|
|
@@ -1375,7 +1440,8 @@ static int validate_and_copy_sample(const struct nlattr *attr,
|
|
|
if (st_acts < 0)
|
|
|
return st_acts;
|
|
|
|
|
|
- err = ovs_nla_copy_actions(actions, key, depth + 1, sfa);
|
|
|
+ err = __ovs_nla_copy_actions(actions, key, depth + 1, sfa,
|
|
|
+ eth_type, vlan_tci);
|
|
|
if (err)
|
|
|
return err;
|
|
|
|
|
@@ -1385,10 +1451,10 @@ static int validate_and_copy_sample(const struct nlattr *attr,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int validate_tp_port(const struct sw_flow_key *flow_key)
|
|
|
+static int validate_tp_port(const struct sw_flow_key *flow_key,
|
|
|
+ __be16 eth_type)
|
|
|
{
|
|
|
- if ((flow_key->eth.type == htons(ETH_P_IP) ||
|
|
|
- flow_key->eth.type == htons(ETH_P_IPV6)) &&
|
|
|
+ if ((eth_type == htons(ETH_P_IP) || eth_type == htons(ETH_P_IPV6)) &&
|
|
|
(flow_key->tp.src || flow_key->tp.dst))
|
|
|
return 0;
|
|
|
|
|
@@ -1483,7 +1549,7 @@ static int validate_and_copy_set_tun(const struct nlattr *attr,
|
|
|
static int validate_set(const struct nlattr *a,
|
|
|
const struct sw_flow_key *flow_key,
|
|
|
struct sw_flow_actions **sfa,
|
|
|
- bool *set_tun)
|
|
|
+ bool *set_tun, __be16 eth_type)
|
|
|
{
|
|
|
const struct nlattr *ovs_key = nla_data(a);
|
|
|
int key_type = nla_type(ovs_key);
|
|
@@ -1508,6 +1574,9 @@ static int validate_set(const struct nlattr *a,
|
|
|
break;
|
|
|
|
|
|
case OVS_KEY_ATTR_TUNNEL:
|
|
|
+ if (eth_p_mpls(eth_type))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
*set_tun = true;
|
|
|
err = validate_and_copy_set_tun(a, sfa);
|
|
|
if (err)
|
|
@@ -1515,7 +1584,7 @@ static int validate_set(const struct nlattr *a,
|
|
|
break;
|
|
|
|
|
|
case OVS_KEY_ATTR_IPV4:
|
|
|
- if (flow_key->eth.type != htons(ETH_P_IP))
|
|
|
+ if (eth_type != htons(ETH_P_IP))
|
|
|
return -EINVAL;
|
|
|
|
|
|
if (!flow_key->ip.proto)
|
|
@@ -1531,7 +1600,7 @@ static int validate_set(const struct nlattr *a,
|
|
|
break;
|
|
|
|
|
|
case OVS_KEY_ATTR_IPV6:
|
|
|
- if (flow_key->eth.type != htons(ETH_P_IPV6))
|
|
|
+ if (eth_type != htons(ETH_P_IPV6))
|
|
|
return -EINVAL;
|
|
|
|
|
|
if (!flow_key->ip.proto)
|
|
@@ -1553,19 +1622,24 @@ static int validate_set(const struct nlattr *a,
|
|
|
if (flow_key->ip.proto != IPPROTO_TCP)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- return validate_tp_port(flow_key);
|
|
|
+ return validate_tp_port(flow_key, eth_type);
|
|
|
|
|
|
case OVS_KEY_ATTR_UDP:
|
|
|
if (flow_key->ip.proto != IPPROTO_UDP)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- return validate_tp_port(flow_key);
|
|
|
+ return validate_tp_port(flow_key, eth_type);
|
|
|
+
|
|
|
+ case OVS_KEY_ATTR_MPLS:
|
|
|
+ if (!eth_p_mpls(eth_type))
|
|
|
+ return -EINVAL;
|
|
|
+ break;
|
|
|
|
|
|
case OVS_KEY_ATTR_SCTP:
|
|
|
if (flow_key->ip.proto != IPPROTO_SCTP)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- return validate_tp_port(flow_key);
|
|
|
+ return validate_tp_port(flow_key, eth_type);
|
|
|
|
|
|
default:
|
|
|
return -EINVAL;
|
|
@@ -1609,12 +1683,13 @@ static int copy_action(const struct nlattr *from,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-int ovs_nla_copy_actions(const struct nlattr *attr,
|
|
|
- const struct sw_flow_key *key,
|
|
|
- int depth,
|
|
|
- struct sw_flow_actions **sfa)
|
|
|
+static int __ovs_nla_copy_actions(const struct nlattr *attr,
|
|
|
+ const struct sw_flow_key *key,
|
|
|
+ int depth, struct sw_flow_actions **sfa,
|
|
|
+ __be16 eth_type, __be16 vlan_tci)
|
|
|
{
|
|
|
const struct nlattr *a;
|
|
|
+ bool out_tnl_port = false;
|
|
|
int rem, err;
|
|
|
|
|
|
if (depth >= SAMPLE_ACTION_DEPTH)
|
|
@@ -1626,6 +1701,8 @@ int ovs_nla_copy_actions(const struct nlattr *attr,
|
|
|
[OVS_ACTION_ATTR_OUTPUT] = sizeof(u32),
|
|
|
[OVS_ACTION_ATTR_RECIRC] = sizeof(u32),
|
|
|
[OVS_ACTION_ATTR_USERSPACE] = (u32)-1,
|
|
|
+ [OVS_ACTION_ATTR_PUSH_MPLS] = sizeof(struct ovs_action_push_mpls),
|
|
|
+ [OVS_ACTION_ATTR_POP_MPLS] = sizeof(__be16),
|
|
|
[OVS_ACTION_ATTR_PUSH_VLAN] = sizeof(struct ovs_action_push_vlan),
|
|
|
[OVS_ACTION_ATTR_POP_VLAN] = 0,
|
|
|
[OVS_ACTION_ATTR_SET] = (u32)-1,
|
|
@@ -1655,6 +1732,8 @@ int ovs_nla_copy_actions(const struct nlattr *attr,
|
|
|
case OVS_ACTION_ATTR_OUTPUT:
|
|
|
if (nla_get_u32(a) >= DP_MAX_PORTS)
|
|
|
return -EINVAL;
|
|
|
+ out_tnl_port = false;
|
|
|
+
|
|
|
break;
|
|
|
|
|
|
case OVS_ACTION_ATTR_HASH: {
|
|
@@ -1671,6 +1750,7 @@ int ovs_nla_copy_actions(const struct nlattr *attr,
|
|
|
}
|
|
|
|
|
|
case OVS_ACTION_ATTR_POP_VLAN:
|
|
|
+ vlan_tci = htons(0);
|
|
|
break;
|
|
|
|
|
|
case OVS_ACTION_ATTR_PUSH_VLAN:
|
|
@@ -1679,25 +1759,73 @@ int ovs_nla_copy_actions(const struct nlattr *attr,
|
|
|
return -EINVAL;
|
|
|
if (!(vlan->vlan_tci & htons(VLAN_TAG_PRESENT)))
|
|
|
return -EINVAL;
|
|
|
+ vlan_tci = vlan->vlan_tci;
|
|
|
break;
|
|
|
|
|
|
case OVS_ACTION_ATTR_RECIRC:
|
|
|
break;
|
|
|
|
|
|
+ case OVS_ACTION_ATTR_PUSH_MPLS: {
|
|
|
+ const struct ovs_action_push_mpls *mpls = nla_data(a);
|
|
|
+
|
|
|
+ /* Networking stack do not allow simultaneous Tunnel
|
|
|
+ * and MPLS GSO.
|
|
|
+ */
|
|
|
+ if (out_tnl_port)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (!eth_p_mpls(mpls->mpls_ethertype))
|
|
|
+ return -EINVAL;
|
|
|
+ /* Prohibit push MPLS other than to a white list
|
|
|
+ * for packets that have a known tag order.
|
|
|
+ */
|
|
|
+ if (vlan_tci & htons(VLAN_TAG_PRESENT) ||
|
|
|
+ (eth_type != htons(ETH_P_IP) &&
|
|
|
+ eth_type != htons(ETH_P_IPV6) &&
|
|
|
+ eth_type != htons(ETH_P_ARP) &&
|
|
|
+ eth_type != htons(ETH_P_RARP) &&
|
|
|
+ !eth_p_mpls(eth_type)))
|
|
|
+ return -EINVAL;
|
|
|
+ eth_type = mpls->mpls_ethertype;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ case OVS_ACTION_ATTR_POP_MPLS:
|
|
|
+ if (vlan_tci & htons(VLAN_TAG_PRESENT) ||
|
|
|
+ !eth_p_mpls(eth_type))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ /* Disallow subsequent L2.5+ set and mpls_pop actions
|
|
|
+ * as there is no check here to ensure that the new
|
|
|
+ * eth_type is valid and thus set actions could
|
|
|
+ * write off the end of the packet or otherwise
|
|
|
+ * corrupt it.
|
|
|
+ *
|
|
|
+ * Support for these actions is planned using packet
|
|
|
+ * recirculation.
|
|
|
+ */
|
|
|
+ eth_type = htons(0);
|
|
|
+ break;
|
|
|
+
|
|
|
case OVS_ACTION_ATTR_SET:
|
|
|
- err = validate_set(a, key, sfa, &skip_copy);
|
|
|
+ err = validate_set(a, key, sfa,
|
|
|
+ &out_tnl_port, eth_type);
|
|
|
if (err)
|
|
|
return err;
|
|
|
+
|
|
|
+ skip_copy = out_tnl_port;
|
|
|
break;
|
|
|
|
|
|
case OVS_ACTION_ATTR_SAMPLE:
|
|
|
- err = validate_and_copy_sample(a, key, depth, sfa);
|
|
|
+ err = validate_and_copy_sample(a, key, depth, sfa,
|
|
|
+ eth_type, vlan_tci);
|
|
|
if (err)
|
|
|
return err;
|
|
|
skip_copy = true;
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
+ OVS_NLERR("Unknown tunnel attribute (%d).\n", type);
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
if (!skip_copy) {
|
|
@@ -1713,6 +1841,24 @@ int ovs_nla_copy_actions(const struct nlattr *attr,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+int ovs_nla_copy_actions(const struct nlattr *attr,
|
|
|
+ const struct sw_flow_key *key,
|
|
|
+ struct sw_flow_actions **sfa)
|
|
|
+{
|
|
|
+ int err;
|
|
|
+
|
|
|
+ *sfa = nla_alloc_flow_actions(nla_len(attr));
|
|
|
+ if (IS_ERR(*sfa))
|
|
|
+ return PTR_ERR(*sfa);
|
|
|
+
|
|
|
+ err = __ovs_nla_copy_actions(attr, key, 0, sfa, key->eth.type,
|
|
|
+ key->eth.tci);
|
|
|
+ if (err)
|
|
|
+ kfree(*sfa);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
static int sample_action_to_attr(const struct nlattr *attr, struct sk_buff *skb)
|
|
|
{
|
|
|
const struct nlattr *a;
|