]> git.proxmox.com Git - mirror_ovs.git/blobdiff - datapath/vport.c
cirrus: Use FreeBSD 12.2.
[mirror_ovs.git] / datapath / vport.c
index 0951cbbdb4e67fd0ae2cdfc8f309c44830f4a7dd..bd62c5612398022542b087bded992e3f5cc8b161 100644 (file)
@@ -33,8 +33,8 @@
 #include <net/lisp.h>
 #include <net/gre.h>
 #include <net/geneve.h>
-#include <net/vxlan.h>
 #include <net/stt.h>
+#include <net/vxlan.h>
 
 #include "datapath.h"
 #include "gso.h"
@@ -42,6 +42,8 @@
 #include "vport-internal_dev.h"
 
 static LIST_HEAD(vport_ops_list);
+static bool compat_gre_loaded = false;
+static bool compat_ip6_tunnel_loaded = false;
 
 /* Protected by RCU read lock for reading, ovs_mutex for writing. */
 static struct hlist_head *dev_table;
@@ -56,7 +58,7 @@ int ovs_vport_init(void)
 {
        int err;
 
-       dev_table = kzalloc(VPORT_HASH_BUCKETS * sizeof(struct hlist_head),
+       dev_table = kcalloc(VPORT_HASH_BUCKETS, sizeof(struct hlist_head),
                            GFP_KERNEL);
        if (!dev_table)
                return -ENOMEM;
@@ -64,27 +66,62 @@ int ovs_vport_init(void)
        err = lisp_init_module();
        if (err)
                goto err_lisp;
-       err = ipgre_init();
-       if (err)
+       err = gre_init();
+       if (err && err != -EEXIST) {
                goto err_gre;
+       } else {
+               if (err == -EEXIST) {
+                       pr_warn("Cannot take GRE protocol rx entry"\
+                               "- The GRE/ERSPAN rx feature not supported\n");
+                       /* continue GRE tx */
+               }
+
+               err = ipgre_init();
+               if (err && err != -EEXIST) 
+                       goto err_ipgre;
+               compat_gre_loaded = true;
+       }
+       err = ip6gre_init();
+       if (err && err != -EEXIST) {
+               goto err_ip6gre;
+       } else {
+               if (err == -EEXIST) {
+                       pr_warn("IPv6 GRE/ERSPAN Rx mode is not supported\n");
+                       goto skip_ip6_tunnel_init;
+               }
+       }
+
+       err = ip6_tunnel_init();
+       if (err)
+               goto err_ip6_tunnel;
+       else
+               compat_ip6_tunnel_loaded = true;
+
+skip_ip6_tunnel_init:
        err = geneve_init_module();
        if (err)
                goto err_geneve;
-
        err = vxlan_init_module();
        if (err)
                goto err_vxlan;
        err = ovs_stt_init_module();
        if (err)
                goto err_stt;
-       return 0;
 
+       return 0;
+       ovs_stt_cleanup_module();
 err_stt:
        vxlan_cleanup_module();
 err_vxlan:
        geneve_cleanup_module();
 err_geneve:
+       ip6_tunnel_cleanup();
+err_ip6_tunnel:
+       ip6gre_fini();
+err_ip6gre:
        ipgre_fini();
+err_ipgre:
+       gre_exit();
 err_gre:
        lisp_cleanup_module();
 err_lisp:
@@ -99,10 +136,16 @@ err_lisp:
  */
 void ovs_vport_exit(void)
 {
+       if (compat_gre_loaded) {
+               gre_exit();
+               ipgre_fini();
+       }
        ovs_stt_cleanup_module();
        vxlan_cleanup_module();
        geneve_cleanup_module();
-       ipgre_fini();
+       if (compat_ip6_tunnel_loaded)
+               ip6_tunnel_cleanup();
+       ip6gre_fini();
        lisp_cleanup_module();
        kfree(dev_table);
 }
@@ -113,7 +156,7 @@ static struct hlist_head *hash_bucket(const struct net *net, const char *name)
        return &dev_table[hash & (VPORT_HASH_BUCKETS - 1)];
 }
 
-int ovs_vport_ops_register(struct vport_ops *ops)
+int __ovs_vport_ops_register(struct vport_ops *ops)
 {
        int err = -EEXIST;
        struct vport_ops *o;
@@ -129,7 +172,7 @@ errout:
        ovs_unlock();
        return err;
 }
-EXPORT_SYMBOL_GPL(ovs_vport_ops_register);
+EXPORT_SYMBOL_GPL(__ovs_vport_ops_register);
 
 void ovs_vport_ops_unregister(struct vport_ops *ops)
 {
@@ -263,6 +306,10 @@ struct vport *ovs_vport_add(const struct vport_parms *parms)
                return vport;
        }
 
+       if (parms->type == OVS_VPORT_TYPE_GRE && !compat_gre_loaded) {
+               pr_warn("GRE protocol already loaded!\n");
+               return ERR_PTR(-EAFNOSUPPORT);
+       }
        /* Unlock to attempt module load and return -EAGAIN if load
         * was successful as we need to restart the port addition
         * workflow.
@@ -298,8 +345,8 @@ int ovs_vport_set_options(struct vport *vport, struct nlattr *options)
  *
  * @vport: vport to delete.
  *
- * Detaches @vport from its datapath and destroys it.  It is possible to fail
- * for reasons such as lack of memory.  ovs_mutex must be held.
+ * Detaches @vport from its datapath and destroys it.  ovs_mutex must be
+ * held.
  */
 void ovs_vport_del(struct vport *vport)
 {
@@ -361,7 +408,7 @@ int ovs_vport_get_options(const struct vport *vport, struct sk_buff *skb)
        if (!vport->ops->get_options)
                return 0;
 
-       nla = nla_nest_start(skb, OVS_VPORT_ATTR_OPTIONS);
+       nla = nla_nest_start_noflag(skb, OVS_VPORT_ATTR_OPTIONS);
        if (!nla)
                return -EMSGSIZE;
 
@@ -375,14 +422,6 @@ int ovs_vport_get_options(const struct vport *vport, struct sk_buff *skb)
        return 0;
 }
 
-static void vport_portids_destroy_rcu_cb(struct rcu_head *rcu)
-{
-       struct vport_portids *ids = container_of(rcu, struct vport_portids,
-                                                rcu);
-
-       kfree(ids);
-}
-
 /**
  *     ovs_vport_set_upcall_portids - set upcall portids of @vport.
  *
@@ -417,7 +456,7 @@ int ovs_vport_set_upcall_portids(struct vport *vport, const struct nlattr *ids)
        rcu_assign_pointer(vport->upcall_portids, vport_portids);
 
        if (old)
-               call_rcu(&old->rcu, vport_portids_destroy_rcu_cb);
+               kfree_rcu(old, rcu);
        return 0;
 }
 
@@ -468,8 +507,9 @@ u32 ovs_vport_find_upcall_portid(const struct vport *vport, struct sk_buff *skb)
 
        ids = rcu_dereference(vport->upcall_portids);
 
-       if (ids->n_ids == 1 && ids->ids[0] == 0)
-               return 0;
+       /* If there is only one portid, select it in the fast-path. */
+       if (ids->n_ids == 1)
+               return ids->ids[0];
 
        hash = skb_get_hash(skb);
        ids_index = hash - ids->n_ids * reciprocal_divide(hash, ids->rn_ids);
@@ -494,6 +534,16 @@ int ovs_vport_receive(struct vport *vport, struct sk_buff *skb,
 
        OVS_CB(skb)->input_vport = vport;
        OVS_CB(skb)->mru = 0;
+       OVS_CB(skb)->cutlen = 0;
+       if (unlikely(dev_net(skb->dev) != ovs_dp_get_net(vport->dp))) {
+               u32 mark;
+
+               mark = skb->mark;
+               skb_scrub_packet(skb, true);
+               skb->mark = mark;
+               tun_info = NULL;
+       }
+
        ovs_skb_init_inner_protocol(skb);
        skb_clear_ovs_gso_cb(skb);
        /* Extract flow from 'skb' into 'key'. */
@@ -505,100 +555,52 @@ int ovs_vport_receive(struct vport *vport, struct sk_buff *skb,
        ovs_dp_process_packet(skb, &key);
        return 0;
 }
-EXPORT_SYMBOL_GPL(ovs_vport_receive);
-
-static void free_vport_rcu(struct rcu_head *rcu)
-{
-       struct vport *vport = container_of(rcu, struct vport, rcu);
-
-       ovs_vport_free(vport);
-}
 
-void ovs_vport_deferred_free(struct vport *vport)
-{
-       if (!vport)
-               return;
-
-       call_rcu(&vport->rcu, free_vport_rcu);
-}
-EXPORT_SYMBOL_GPL(ovs_vport_deferred_free);
-
-int ovs_tunnel_get_egress_info(struct dp_upcall_info *upcall,
-                              struct net *net,
-                              struct sk_buff *skb,
-                              u8 ipproto,
-                              __be16 tp_src,
-                              __be16 tp_dst)
+static int packet_length(const struct sk_buff *skb,
+                        struct net_device *dev)
 {
-       struct ip_tunnel_info *egress_tun_info = upcall->egress_tun_info;
-       struct ip_tunnel_info *tun_info = skb_tunnel_info(skb);
-       const struct ip_tunnel_key *tun_key;
-       u32 skb_mark = skb->mark;
-       struct rtable *rt;
-       struct flowi4 fl;
-
-       if (unlikely(!tun_info))
-               return -EINVAL;
-       if (ip_tunnel_info_af(tun_info) != AF_INET)
-               return -EINVAL;
+       int length = skb->len - dev->hard_header_len;
 
-       tun_key = &tun_info->key;
-
-       /* Route lookup to get srouce IP address.
-        * The process may need to be changed if the corresponding process
-        * in vports ops changed.
-        */
-       rt = ovs_tunnel_route_lookup(net, tun_key, skb_mark, &fl, ipproto);
-       if (IS_ERR(rt))
-               return PTR_ERR(rt);
-
-       ip_rt_put(rt);
+       if (!skb_vlan_tag_present(skb) &&
+           eth_type_vlan(skb->protocol))
+               length -= VLAN_HLEN;
 
-       /* Generate egress_tun_info based on tun_info,
-        * saddr, tp_src and tp_dst
+       /* Don't subtract for multiple VLAN tags. Most (all?) drivers allow
+        * (ETH_LEN + VLAN_HLEN) in addition to the mtu value, but almost none
+        * account for 802.1ad. e.g. is_skb_forwardable().
         */
-       ip_tunnel_key_init(&egress_tun_info->key,
-                          fl.saddr, tun_key->u.ipv4.dst,
-                          tun_key->tos,
-                          tun_key->ttl,
-                          tp_src, tp_dst,
-                          tun_key->tun_id,
-                          tun_key->tun_flags);
-       egress_tun_info->options_len = tun_info->options_len;
-       egress_tun_info->mode = tun_info->mode;
-       upcall->egress_tun_opts = ip_tunnel_info_opts(tun_info);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(ovs_tunnel_get_egress_info);
-
-int ovs_vport_get_egress_tun_info(struct vport *vport, struct sk_buff *skb,
-                                 struct dp_upcall_info *upcall)
-{
-       /* get_egress_tun_info() is only implemented on tunnel ports. */
-       if (unlikely(!vport->ops->get_egress_tun_info))
-               return -EINVAL;
-
-       return vport->ops->get_egress_tun_info(vport, skb, upcall);
-}
-
-static unsigned int packet_length(const struct sk_buff *skb)
-{
-       unsigned int length = skb->len - ETH_HLEN;
-
-       if (skb->protocol == htons(ETH_P_8021Q))
-               length -= VLAN_HLEN;
 
-       return length;
+       return length > 0 ? length: 0;
 }
 
-void ovs_vport_send(struct vport *vport, struct sk_buff *skb)
+void ovs_vport_send(struct vport *vport, struct sk_buff *skb, u8 mac_proto)
 {
        int mtu = vport->dev->mtu;
 
-       if (unlikely(packet_length(skb) > mtu && !skb_is_gso(skb))) {
+       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",
                                     vport->dev->name,
-                                    packet_length(skb), mtu);
+                                    packet_length(skb, vport->dev), mtu);
                vport->dev->stats.tx_errors++;
                goto drop;
        }