]> git.proxmox.com Git - mirror_ovs.git/commitdiff
datapath: add processing of L3 packets
authorYang, Yi Y <yi.y.yang@intel.com>
Mon, 6 Feb 2017 13:04:39 +0000 (21:04 +0800)
committerJoe Stringer <joe@ovn.org>
Thu, 2 Mar 2017 23:51:39 +0000 (15:51 -0800)
Upstream commit:
    commit 5108bbaddc37c1c8583f0cf2562d7d3463cd12cb
    Author: Jiri Benc <jbenc@redhat.com>
    Date:   Thu Nov 10 16:28:21 2016 +0100

    openvswitch: add processing of L3 packets

    Support receiving, extracting flow key and sending of L3 packets (packets
    without an Ethernet header).

    Note that even after this patch, non-Ethernet interfaces are still not
    allowed to be added to bridges. Similarly, netlink interface for sending and
    receiving L3 packets to/from user space is not in place yet.

    Based on previous versions by Lorand Jakab and Simon Horman.

Signed-off-by: Lorand Jakab <lojakab@cisco.com>
Signed-off-by: Simon Horman <simon.horman@netronome.com>
Signed-off-by: Jiri Benc <jbenc@redhat.com>
Acked-by: Pravin B Shelar <pshelar@ovn.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Yi Yang <yi.y.yang@intel.com>
Signed-off-by: Joe Stringer <joe@ovn.org>
acinclude.m4
datapath/datapath.c
datapath/flow.c
datapath/linux/compat/include/linux/skbuff.h
datapath/linux/compat/skbuff-openvswitch.c
datapath/vport.c

index fc5922c3f805bd7d3484afff97533fa11f103252..19cffe08c8948240899942470c70e97ea9eb1aa2 100644 (file)
@@ -602,6 +602,7 @@ AC_DEFUN([OVS_CHECK_LINUX_COMPAT], [
                   [OVS_DEFINE([HAVE_L4_RXHASH])])
   OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_ensure_writable])
   OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_vlan_pop])
+  OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [__skb_vlan_pop])
   OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_vlan_push])
   OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_clear_hash_if_not_l4])
   OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_postpush_rcsum])
index 64cd781554d5b8e395ceda16a8e2aeceb93bd730..fefc7953091b53577674eeeb898572ad339d61a3 100644 (file)
@@ -569,7 +569,6 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
        struct sw_flow *flow;
        struct sw_flow_actions *sf_acts;
        struct datapath *dp;
-       struct ethhdr *eth;
        struct vport *input_vport;
        u16 mru = 0;
        int len;
@@ -590,18 +589,6 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
 
        nla_memcpy(__skb_put(packet, len), a[OVS_PACKET_ATTR_PACKET], len);
 
-       skb_reset_mac_header(packet);
-       eth = eth_hdr(packet);
-
-       /* Normally, setting the skb 'protocol' field would be handled by a
-        * call to eth_type_trans(), but it assumes there's a sending
-        * device, which we may not have.
-        */
-       if (eth_proto_is_802_3(eth->h_proto))
-               packet->protocol = eth->h_proto;
-       else
-               packet->protocol = htons(ETH_P_802_2);
-
        /* Set packet's mru */
        if (a[OVS_PACKET_ATTR_MRU]) {
                mru = nla_get_u16(a[OVS_PACKET_ATTR_MRU]);
@@ -628,6 +615,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
        rcu_assign_pointer(flow->sf_acts, acts);
        packet->priority = flow->key.phy.priority;
        packet->mark = flow->key.phy.skb_mark;
+       packet->protocol = flow->key.eth.type;
 
        rcu_read_lock();
        dp = get_dp_rcu(net, ovs_header->dp_ifindex);
index 2e23d2a989f7e6051260c8b58b7b7d300c5d6e98..05940ce369786599a8816c6cd55651c0341bba18 100644 (file)
@@ -333,6 +333,14 @@ static int parse_vlan_tag(struct sk_buff *skb, struct vlan_head *key_vh)
        return 1;
 }
 
+static void clear_vlan(struct sw_flow_key *key)
+{
+       key->eth.vlan.tci = 0;
+       key->eth.vlan.tpid = 0;
+       key->eth.cvlan.tci = 0;
+       key->eth.cvlan.tpid = 0;
+}
+
 static int parse_vlan(struct sk_buff *skb, struct sw_flow_key *key)
 {
        int res;
@@ -482,17 +490,20 @@ invalid:
  *
  * Returns 0 if successful, otherwise a negative errno value.
  *
- * Initializes @skb header pointers as follows:
+ * Initializes @skb header fields as follows:
  *
- *    - skb->mac_header: the Ethernet header.
+ *    - skb->mac_header: the L2 header.
  *
- *    - skb->network_header: just past the Ethernet header, or just past the
- *      VLAN header, to the first byte of the Ethernet payload.
+ *    - skb->network_header: just past the L2 header, or just past the
+ *      VLAN header, to the first byte of the L2 payload.
  *
  *    - skb->transport_header: If key->eth.type is ETH_P_IP or ETH_P_IPV6
  *      on output, then just past the IP header, if one is present and
  *      of a correct length, otherwise the same as skb->network_header.
  *      For other key->eth.type values it is left untouched.
+ *
+ *    - skb->protocol: the type of the data starting at skb->network_header.
+ *      Equals to key->eth.type.
  */
 static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
 {
@@ -504,28 +515,36 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
 
        skb_reset_mac_header(skb);
 
-       /* Link layer.  We are guaranteed to have at least the 14 byte Ethernet
-        * header in the linear data area.
-        */
-       eth = eth_hdr(skb);
-       ether_addr_copy(key->eth.src, eth->h_source);
-       ether_addr_copy(key->eth.dst, eth->h_dest);
+       /* Link layer. */
+       clear_vlan(key);
+       if (key->mac_proto == MAC_PROTO_NONE) {
+               if (unlikely(eth_type_vlan(skb->protocol)))
+                       return -EINVAL;
 
-       __skb_pull(skb, 2 * ETH_ALEN);
-       /* We are going to push all headers that we pull, so no need to
-        * update skb->csum here.
-        */
+               skb_reset_network_header(skb);
+       } else {
+               eth = eth_hdr(skb);
+               ether_addr_copy(key->eth.src, eth->h_source);
+               ether_addr_copy(key->eth.dst, eth->h_dest);
 
-       if (unlikely(parse_vlan(skb, key)))
-               return -ENOMEM;
+               __skb_pull(skb, 2 * ETH_ALEN);
+               /* We are going to push all headers that we pull, so no need to
+                * update skb->csum here.
+                */
 
-       key->eth.type = parse_ethertype(skb);
-       if (unlikely(key->eth.type == htons(0)))
-               return -ENOMEM;
+               if (unlikely(parse_vlan(skb, key)))
+                       return -ENOMEM;
+
+               skb->protocol = parse_ethertype(skb);
+               if (unlikely(skb->protocol == htons(0)))
+                       return -ENOMEM;
+
+               skb_reset_network_header(skb);
+               __skb_push(skb, skb->data - skb_mac_header(skb));
+       }
 
-       skb_reset_network_header(skb);
        skb_reset_mac_len(skb);
-       __skb_push(skb, skb->data - skb_mac_header(skb));
+       key->eth.type = skb->protocol;
 
        /* Network layer. */
        if (key->eth.type == htons(ETH_P_IP)) {
@@ -726,9 +745,25 @@ int ovs_flow_key_update(struct sk_buff *skb, struct sw_flow_key *key)
        return key_extract(skb, key);
 }
 
+static int key_extract_mac_proto(struct sk_buff *skb)
+{
+       switch (skb->dev->type) {
+       case ARPHRD_ETHER:
+               return MAC_PROTO_ETHERNET;
+       case ARPHRD_NONE:
+               if (skb->protocol == htons(ETH_P_TEB))
+                       return MAC_PROTO_ETHERNET;
+               return MAC_PROTO_NONE;
+       }
+       WARN_ON_ONCE(1);
+       return -EINVAL;
+}
+
 int ovs_flow_key_extract(const struct ip_tunnel_info *tun_info,
                         struct sk_buff *skb, struct sw_flow_key *key)
 {
+       int res;
+
        /* Extract metadata from packet. */
        if (tun_info) {
                key->tun_proto = ip_tunnel_info_af(tun_info);
@@ -754,7 +789,10 @@ int ovs_flow_key_extract(const struct ip_tunnel_info *tun_info,
        key->phy.skb_mark = skb->mark;
        ovs_ct_fill_key(skb, key);
        key->ovs_flow_hash = 0;
-       key->mac_proto = MAC_PROTO_ETHERNET;
+       res = key_extract_mac_proto(skb);
+       if (res < 0)
+               return res;
+       key->mac_proto = res;
        key->recirc_id = 0;
 
        return key_extract(skb, key);
@@ -771,5 +809,29 @@ int ovs_flow_key_extract_userspace(struct net *net, const struct nlattr *attr,
        if (err)
                return err;
 
+       if (ovs_key_mac_proto(key) == MAC_PROTO_NONE) {
+               /* key_extract assumes that skb->protocol is set-up for
+                * layer 3 packets which is the case for other callers,
+                * in particular packets recieved from the network stack.
+                * Here the correct value can be set from the metadata
+                * extracted above.
+                */
+               skb->protocol = key->eth.type;
+       } else {
+               struct ethhdr *eth;
+
+               skb_reset_mac_header(skb);
+               eth = eth_hdr(skb);
+
+               /* Normally, setting the skb 'protocol' field would be
+                * handled by a call to eth_type_trans(), but it assumes
+                * there's a sending device, which we may not have.
+                */
+               if (eth_proto_is_802_3(eth->h_proto))
+                       skb->protocol = eth->h_proto;
+               else
+                       skb->protocol = htons(ETH_P_802_2);
+       }
+
        return key_extract(skb, key);
 }
index a2cbd780cd9feb67c6038d4dfe1d5d84dca3d0dc..6c45939242b02a0d3568044387ee601e13d0d75a 100644 (file)
@@ -263,6 +263,11 @@ static inline void __skb_fill_page_desc(struct sk_buff *skb, int i,
 int rpl_skb_ensure_writable(struct sk_buff *skb, int write_len);
 #endif
 
+#ifndef HAVE___SKB_VLAN_POP
+#define __skb_vlan_pop rpl___skb_vlan_pop
+int rpl___skb_vlan_pop(struct sk_buff *skb, u16 *vlan_tci);
+#endif
+
 #ifndef HAVE_SKB_VLAN_POP
 #define skb_vlan_pop rpl_skb_vlan_pop
 int rpl_skb_vlan_pop(struct sk_buff *skb);
index aa6ff42c88e68d53cd7ba1e4509790780810391e..4cdeedc58bc14e23df47cfcb11f5681b5e57e434 100644 (file)
@@ -141,9 +141,9 @@ int rpl_skb_ensure_writable(struct sk_buff *skb, int write_len)
 EXPORT_SYMBOL_GPL(rpl_skb_ensure_writable);
 #endif
 
-#ifndef HAVE_SKB_VLAN_POP
+#if !defined(HAVE___SKB_VLAN_POP) || !defined(HAVE_SKB_VLAN_POP)
 /* remove VLAN header from packet and update csum accordingly. */
-static int __skb_vlan_pop(struct sk_buff *skb, u16 *vlan_tci)
+int rpl___skb_vlan_pop(struct sk_buff *skb, u16 *vlan_tci)
 {
        struct vlan_hdr *vhdr;
        unsigned int offset = skb->data - skb_mac_header(skb);
@@ -174,7 +174,9 @@ pull:
 
        return err;
 }
+#endif
 
+#ifndef HAVE_SKB_VLAN_POP
 int rpl_skb_vlan_pop(struct sk_buff *skb)
 {
        u16 vlan_tci;
@@ -189,7 +191,7 @@ int rpl_skb_vlan_pop(struct sk_buff *skb)
                             skb->len < VLAN_ETH_HLEN))
                        return 0;
 
-               err = __skb_vlan_pop(skb, &vlan_tci);
+               err = rpl___skb_vlan_pop(skb, &vlan_tci);
                if (err)
                        return err;
        }
index 6169c2369fb7b2f4cb1437a100dcf9fc2b90eb53..711c29f1217739c48e289d411f6521854c45eb4a 100644 (file)
@@ -529,6 +529,25 @@ void ovs_vport_send(struct vport *vport, struct sk_buff *skb, u8 mac_proto)
 {
        int mtu = vport->dev->mtu;
 
+       switch (vport->dev->type) {
+       case ARPHRD_NONE:
+               if (mac_proto == MAC_PROTO_ETHERNET) {
+                       skb_reset_network_header(skb);
+                       skb_reset_mac_len(skb);
+                       skb->protocol = htons(ETH_P_TEB);
+               } else if (mac_proto != MAC_PROTO_NONE) {
+                       WARN_ON_ONCE(1);
+                       goto drop;
+               }
+               break;
+       case ARPHRD_ETHER:
+               if (mac_proto != MAC_PROTO_ETHERNET)
+                       goto drop;
+               break;
+       default:
+               goto drop;
+       }
+
        if (unlikely(packet_length(skb, vport->dev) > mtu &&
                     !skb_is_gso(skb))) {
                net_warn_ratelimited("%s: dropped over-mtu packet: %d > %d\n",