openvswitch: New MPLS actions for layer 2 tunnelling
authorMartin Varghese <martin.varghese@nokia.com>
Sat, 21 Dec 2019 03:20:46 +0000 (08:50 +0530)
committerDavid S. Miller <davem@davemloft.net>
Wed, 25 Dec 2019 06:24:45 +0000 (22:24 -0800)
The existing PUSH MPLS action inserts MPLS header between ethernet header
and the IP header. Though this behaviour is fine for L3 VPN where an IP
packet is encapsulated inside a MPLS tunnel, it does not suffice the L2
VPN (l2 tunnelling) requirements. In L2 VPN the MPLS header should
encapsulate the ethernet packet.

The new mpls action ADD_MPLS inserts MPLS header at the start of the
packet or at the start of the l3 header depending on the value of l3 tunnel
flag in the ADD_MPLS arguments.

POP_MPLS action is extended to support ethertype 0x6558.

Signed-off-by: Martin Varghese <martin.varghese@nokia.com>
Acked-by: Pravin B Shelar <pshelar@ovn.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/uapi/linux/openvswitch.h
net/openvswitch/actions.c
net/openvswitch/flow_netlink.c

index a87b44cd5590a215f60baa34b65a099f3d110b4f..ae2bff14e7e14460c0fab8d00bf8770beff5a9db 100644 (file)
@@ -672,6 +672,32 @@ struct ovs_action_push_mpls {
        __be16 mpls_ethertype; /* Either %ETH_P_MPLS_UC or %ETH_P_MPLS_MC */
 };
 
+/**
+ * struct ovs_action_add_mpls - %OVS_ACTION_ATTR_ADD_MPLS action
+ * argument.
+ * @mpls_lse: MPLS label stack entry to push.
+ * @mpls_ethertype: Ethertype to set in the encapsulating ethernet frame.
+ * @tun_flags: MPLS tunnel attributes.
+ *
+ * The only values @mpls_ethertype should ever be given are %ETH_P_MPLS_UC and
+ * %ETH_P_MPLS_MC, indicating MPLS unicast or multicast. Other are rejected.
+ */
+struct ovs_action_add_mpls {
+       __be32 mpls_lse;
+       __be16 mpls_ethertype; /* Either %ETH_P_MPLS_UC or %ETH_P_MPLS_MC */
+       __u16 tun_flags;
+};
+
+#define OVS_MPLS_L3_TUNNEL_FLAG_MASK  (1 << 0) /* Flag to specify the place of
+                                               * insertion of MPLS header.
+                                               * When false, the MPLS header
+                                               * will be inserted at the start
+                                               * of the packet.
+                                               * When true, the MPLS header
+                                               * will be inserted at the start
+                                               * of the l3 header.
+                                               */
+
 /**
  * struct ovs_action_push_vlan - %OVS_ACTION_ATTR_PUSH_VLAN action argument.
  * @vlan_tpid: Tag protocol identifier (TPID) to push.
@@ -892,6 +918,10 @@ struct check_pkt_len_arg {
  * @OVS_ACTION_ATTR_CHECK_PKT_LEN: Check the packet length and execute a set
  * of actions if greater than the specified packet length, else execute
  * another set of actions.
+ * @OVS_ACTION_ATTR_ADD_MPLS: Push a new MPLS label stack entry at the
+ * start of the packet or at the start of the l3 header depending on the value
+ * of l3 tunnel flag in the tun_flags field of OVS_ACTION_ATTR_ADD_MPLS
+ * argument.
  *
  * Only a single header can be set with a single %OVS_ACTION_ATTR_SET.  Not all
  * fields within a header are modifiable, e.g. the IPv4 protocol and fragment
@@ -927,6 +957,7 @@ enum ovs_action_attr {
        OVS_ACTION_ATTR_METER,        /* u32 meter ID. */
        OVS_ACTION_ATTR_CLONE,        /* Nested OVS_CLONE_ATTR_*.  */
        OVS_ACTION_ATTR_CHECK_PKT_LEN, /* Nested OVS_CHECK_PKT_LEN_ATTR_*. */
+       OVS_ACTION_ATTR_ADD_MPLS,     /* struct ovs_action_add_mpls. */
 
        __OVS_ACTION_ATTR_MAX,        /* Nothing past this will be accepted
                                       * from userspace. */
index 4c839546230341404a93cef306adef67d3df8121..7fbfe2adfffa6c63b1accf8841d7d279f807e287 100644 (file)
@@ -161,16 +161,17 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
                              const struct nlattr *attr, int len);
 
 static int push_mpls(struct sk_buff *skb, struct sw_flow_key *key,
-                    const struct ovs_action_push_mpls *mpls)
+                    __be32 mpls_lse, __be16 mpls_ethertype, __u16 mac_len)
 {
        int err;
 
-       err = skb_mpls_push(skb, mpls->mpls_lse, mpls->mpls_ethertype,
-                           skb->mac_len,
-                           ovs_key_mac_proto(key) == MAC_PROTO_ETHERNET);
+       err = skb_mpls_push(skb, mpls_lse, mpls_ethertype, mac_len, !!mac_len);
        if (err)
                return err;
 
+       if (!mac_len)
+               key->mac_proto = MAC_PROTO_NONE;
+
        invalidate_flow_key(key);
        return 0;
 }
@@ -185,6 +186,9 @@ static int pop_mpls(struct sk_buff *skb, struct sw_flow_key *key,
        if (err)
                return err;
 
+       if (ethertype == htons(ETH_P_TEB))
+               key->mac_proto = MAC_PROTO_ETHERNET;
+
        invalidate_flow_key(key);
        return 0;
 }
@@ -1229,10 +1233,24 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
                        execute_hash(skb, key, a);
                        break;
 
-               case OVS_ACTION_ATTR_PUSH_MPLS:
-                       err = push_mpls(skb, key, nla_data(a));
+               case OVS_ACTION_ATTR_PUSH_MPLS: {
+                       struct ovs_action_push_mpls *mpls = nla_data(a);
+
+                       err = push_mpls(skb, key, mpls->mpls_lse,
+                                       mpls->mpls_ethertype, skb->mac_len);
                        break;
+               }
+               case OVS_ACTION_ATTR_ADD_MPLS: {
+                       struct ovs_action_add_mpls *mpls = nla_data(a);
+                       __u16 mac_len = 0;
+
+                       if (mpls->tun_flags & OVS_MPLS_L3_TUNNEL_FLAG_MASK)
+                               mac_len = skb->mac_len;
 
+                       err = push_mpls(skb, key, mpls->mpls_lse,
+                                       mpls->mpls_ethertype, mac_len);
+                       break;
+               }
                case OVS_ACTION_ATTR_POP_MPLS:
                        err = pop_mpls(skb, key, nla_get_be16(a));
                        break;
index 65c2e3458ff586bac669b8d478ce252f58b1c018..7da4230627f51489707fc199c4333702c344282e 100644 (file)
@@ -79,6 +79,7 @@ static bool actions_may_change_flow(const struct nlattr *actions)
                case OVS_ACTION_ATTR_SET_MASKED:
                case OVS_ACTION_ATTR_METER:
                case OVS_ACTION_ATTR_CHECK_PKT_LEN:
+               case OVS_ACTION_ATTR_ADD_MPLS:
                default:
                        return true;
                }
@@ -3005,6 +3006,7 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
                        [OVS_ACTION_ATTR_METER] = sizeof(u32),
                        [OVS_ACTION_ATTR_CLONE] = (u32)-1,
                        [OVS_ACTION_ATTR_CHECK_PKT_LEN] = (u32)-1,
+                       [OVS_ACTION_ATTR_ADD_MPLS] = sizeof(struct ovs_action_add_mpls),
                };
                const struct ovs_action_push_vlan *vlan;
                int type = nla_type(a);
@@ -3072,6 +3074,33 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
                case OVS_ACTION_ATTR_RECIRC:
                        break;
 
+               case OVS_ACTION_ATTR_ADD_MPLS: {
+                       const struct ovs_action_add_mpls *mpls = nla_data(a);
+
+                       if (!eth_p_mpls(mpls->mpls_ethertype))
+                               return -EINVAL;
+
+                       if (mpls->tun_flags & OVS_MPLS_L3_TUNNEL_FLAG_MASK) {
+                               if (vlan_tci & htons(VLAN_CFI_MASK) ||
+                                   (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;
+                               mpls_label_count++;
+                       } else {
+                               if (mac_proto == MAC_PROTO_ETHERNET) {
+                                       mpls_label_count = 1;
+                                       mac_proto = MAC_PROTO_NONE;
+                               } else {
+                                       mpls_label_count++;
+                               }
+                       }
+                       eth_type = mpls->mpls_ethertype;
+                       break;
+               }
+
                case OVS_ACTION_ATTR_PUSH_MPLS: {
                        const struct ovs_action_push_mpls *mpls = nla_data(a);
 
@@ -3109,6 +3138,11 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
                         * recirculation.
                         */
                        proto = nla_get_be16(a);
+
+                       if (proto == htons(ETH_P_TEB) &&
+                           mac_proto != MAC_PROTO_NONE)
+                               return -EINVAL;
+
                        mpls_label_count--;
 
                        if (!eth_p_mpls(proto) || !mpls_label_count)