|
@@ -46,6 +46,7 @@
|
|
|
#include <net/ip.h>
|
|
|
#include <net/ipv6.h>
|
|
|
#include <net/ndisc.h>
|
|
|
+#include <net/mpls.h>
|
|
|
|
|
|
#include "flow_netlink.h"
|
|
|
|
|
@@ -134,7 +135,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 +151,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)))
|
|
@@ -266,6 +274,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)
|
|
@@ -735,6 +744,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;
|
|
|
|
|
@@ -1140,6 +1159,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) ||
|
|
@@ -1336,9 +1363,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 +1408,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 +1419,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 +1517,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 +1542,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 +1552,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 +1568,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 +1590,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 +1651,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 +1669,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 +1700,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 +1718,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,19 +1727,66 @@ 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;
|
|
@@ -1713,6 +1808,14 @@ 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)
|
|
|
+{
|
|
|
+ return ovs_nla_copy_actions__(attr, key, 0, sfa, key->eth.type,
|
|
|
+ key->eth.tci);
|
|
|
+}
|
|
|
+
|
|
|
static int sample_action_to_attr(const struct nlattr *attr, struct sk_buff *skb)
|
|
|
{
|
|
|
const struct nlattr *a;
|