Merge tag 'regmap-v4.3' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie...
[firefly-linux-kernel-4.4.55.git] / drivers / net / vrf.c
index 01dc91562a889f4e5ec6996a91b29dd76112a304..e7094fbd75685998bb5ee492367e686c133a2e15 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/hashtable.h>
 
 #include <linux/inetdevice.h>
+#include <net/arp.h>
 #include <net/ip.h>
 #include <net/ip_fib.h>
 #include <net/ip6_route.h>
@@ -219,6 +220,9 @@ err:
 
 static netdev_tx_t is_ip_tx_frame(struct sk_buff *skb, struct net_device *dev)
 {
+       /* strip the ethernet header added for pass through VRF device */
+       __skb_pull(skb, skb_network_offset(skb));
+
        switch (skb->protocol) {
        case htons(ETH_P_IP):
                return vrf_process_v4_outbound(skb, dev);
@@ -248,9 +252,47 @@ static netdev_tx_t vrf_xmit(struct sk_buff *skb, struct net_device *dev)
        return ret;
 }
 
-static netdev_tx_t vrf_finish(struct sock *sk, struct sk_buff *skb)
+/* modelled after ip_finish_output2 */
+static int vrf_finish_output(struct sock *sk, struct sk_buff *skb)
 {
-       return dev_queue_xmit(skb);
+       struct dst_entry *dst = skb_dst(skb);
+       struct rtable *rt = (struct rtable *)dst;
+       struct net_device *dev = dst->dev;
+       unsigned int hh_len = LL_RESERVED_SPACE(dev);
+       struct neighbour *neigh;
+       u32 nexthop;
+       int ret = -EINVAL;
+
+       /* Be paranoid, rather than too clever. */
+       if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) {
+               struct sk_buff *skb2;
+
+               skb2 = skb_realloc_headroom(skb, LL_RESERVED_SPACE(dev));
+               if (!skb2) {
+                       ret = -ENOMEM;
+                       goto err;
+               }
+               if (skb->sk)
+                       skb_set_owner_w(skb2, skb->sk);
+
+               consume_skb(skb);
+               skb = skb2;
+       }
+
+       rcu_read_lock_bh();
+
+       nexthop = (__force u32)rt_nexthop(rt, ip_hdr(skb)->daddr);
+       neigh = __ipv4_neigh_lookup_noref(dev, nexthop);
+       if (unlikely(!neigh))
+               neigh = __neigh_create(&arp_tbl, &nexthop, dev, false);
+       if (!IS_ERR(neigh))
+               ret = dst_neigh_output(dst, neigh, skb);
+
+       rcu_read_unlock_bh();
+err:
+       if (unlikely(ret < 0))
+               vrf_tx_error(skb->dev, skb);
+       return ret;
 }
 
 static int vrf_output(struct sock *sk, struct sk_buff *skb)
@@ -264,7 +306,7 @@ static int vrf_output(struct sock *sk, struct sk_buff *skb)
 
        return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, sk, skb,
                            NULL, dev,
-                           vrf_finish,
+                           vrf_finish_output,
                            !(IPCB(skb)->flags & IPSKB_REROUTED));
 }
 
@@ -295,7 +337,6 @@ static struct rtable *vrf_rtable_create(struct net_device *dev)
                rth->rt_uses_gateway = 0;
                INIT_LIST_HEAD(&rth->rt_uncached);
                rth->rt_uncached_list = NULL;
-               rth->rt_lwtstate = NULL;
        }
 
        return rth;
@@ -352,7 +393,6 @@ static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev)
 {
        struct net_vrf_dev *vrf_ptr = kmalloc(sizeof(*vrf_ptr), GFP_KERNEL);
        struct slave *slave = kzalloc(sizeof(*slave), GFP_KERNEL);
-       struct slave *duplicate_slave;
        struct net_vrf *vrf = netdev_priv(dev);
        struct slave_queue *queue = &vrf->queue;
        int ret = -ENOMEM;
@@ -361,25 +401,16 @@ static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev)
                goto out_fail;
 
        slave->dev = port_dev;
-
        vrf_ptr->ifindex = dev->ifindex;
        vrf_ptr->tb_id = vrf->tb_id;
 
-       duplicate_slave = __vrf_find_slave_dev(queue, port_dev);
-       if (duplicate_slave) {
-               ret = -EBUSY;
-               goto out_fail;
-       }
-
-       __vrf_insert_slave(queue, slave);
-
        /* register the packet handler for slave ports */
        ret = netdev_rx_handler_register(port_dev, vrf_handle_frame, dev);
        if (ret) {
                netdev_err(port_dev,
                           "Device %s failed to register rx_handler\n",
                           port_dev->name);
-               goto out_remove;
+               goto out_fail;
        }
 
        ret = netdev_master_upper_dev_link(port_dev, dev);
@@ -387,7 +418,7 @@ static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev)
                goto out_unregister;
 
        port_dev->flags |= IFF_SLAVE;
-
+       __vrf_insert_slave(queue, slave);
        rcu_assign_pointer(port_dev->vrf_ptr, vrf_ptr);
        cycle_netdev(port_dev);
 
@@ -395,8 +426,6 @@ static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev)
 
 out_unregister:
        netdev_rx_handler_unregister(port_dev);
-out_remove:
-       __vrf_remove_slave(queue, slave);
 out_fail:
        kfree(vrf_ptr);
        kfree(slave);
@@ -405,8 +434,7 @@ out_fail:
 
 static int vrf_add_slave(struct net_device *dev, struct net_device *port_dev)
 {
-       if (!netif_is_vrf(dev) || netif_is_vrf(port_dev) ||
-           vrf_is_slave(port_dev))
+       if (netif_is_vrf(port_dev) || vrf_is_slave(port_dev))
                return -EINVAL;
 
        return do_vrf_add_slave(dev, port_dev);
@@ -443,9 +471,6 @@ static int do_vrf_del_slave(struct net_device *dev, struct net_device *port_dev)
 
 static int vrf_del_slave(struct net_device *dev, struct net_device *port_dev)
 {
-       if (!netif_is_vrf(dev))
-               return -EINVAL;
-
        return do_vrf_del_slave(dev, port_dev);
 }