net: Add support for l3mdev ops to VRF driver
[cascardo/linux.git] / drivers / net / vrf.c
index e7094fb..72f1892 100644 (file)
@@ -35,6 +35,7 @@
 #include <net/route.h>
 #include <net/addrconf.h>
 #include <net/vrf.h>
+#include <net/l3mdev.h>
 
 #define DRV_NAME       "vrf"
 #define DRV_VERSION    "1.0"
@@ -193,7 +194,8 @@ static netdev_tx_t vrf_process_v4_outbound(struct sk_buff *skb,
                .flowi4_oif = vrf_dev->ifindex,
                .flowi4_iif = LOOPBACK_IFINDEX,
                .flowi4_tos = RT_TOS(ip4h->tos),
-               .flowi4_flags = FLOWI_FLAG_ANYSRC | FLOWI_FLAG_VRFSRC,
+               .flowi4_flags = FLOWI_FLAG_ANYSRC | FLOWI_FLAG_VRFSRC |
+                               FLOWI_FLAG_SKIP_NH_OIF,
                .daddr = ip4h->daddr,
        };
 
@@ -253,7 +255,7 @@ static netdev_tx_t vrf_xmit(struct sk_buff *skb, struct net_device *dev)
 }
 
 /* modelled after ip_finish_output2 */
-static int vrf_finish_output(struct sock *sk, struct sk_buff *skb)
+static int vrf_finish_output(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
        struct dst_entry *dst = skb_dst(skb);
        struct rtable *rt = (struct rtable *)dst;
@@ -298,14 +300,15 @@ err:
 static int vrf_output(struct sock *sk, struct sk_buff *skb)
 {
        struct net_device *dev = skb_dst(skb)->dev;
+       struct net *net = dev_net(dev);
 
-       IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUT, skb->len);
+       IP_UPD_PO_STATS(net, IPSTATS_MIB_OUT, skb->len);
 
        skb->dev = dev;
        skb->protocol = htons(ETH_P_IP);
 
-       return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, sk, skb,
-                           NULL, dev,
+       return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING,
+                           net, sk, skb, NULL, dev,
                            vrf_finish_output,
                            !(IPCB(skb)->flags & IPSKB_REROUTED));
 }
@@ -320,6 +323,7 @@ static void vrf_rtable_destroy(struct net_vrf *vrf)
 
 static struct rtable *vrf_rtable_create(struct net_device *dev)
 {
+       struct net_vrf *vrf = netdev_priv(dev);
        struct rtable *rth;
 
        rth = dst_alloc(&vrf_dst_ops, dev, 2,
@@ -335,6 +339,7 @@ static struct rtable *vrf_rtable_create(struct net_device *dev)
                rth->rt_pmtu    = 0;
                rth->rt_gateway = 0;
                rth->rt_uses_gateway = 0;
+               rth->rt_table_id = vrf->tb_id;
                INIT_LIST_HEAD(&rth->rt_uncached);
                rth->rt_uncached_list = NULL;
        }
@@ -434,7 +439,7 @@ out_fail:
 
 static int vrf_add_slave(struct net_device *dev, struct net_device *port_dev)
 {
-       if (netif_is_vrf(port_dev) || vrf_is_slave(port_dev))
+       if (netif_is_l3_master(port_dev) || vrf_is_slave(port_dev))
                return -EINVAL;
 
        return do_vrf_add_slave(dev, port_dev);
@@ -525,6 +530,33 @@ static const struct net_device_ops vrf_netdev_ops = {
        .ndo_del_slave          = vrf_del_slave,
 };
 
+static u32 vrf_fib_table(const struct net_device *dev)
+{
+       struct net_vrf *vrf = netdev_priv(dev);
+
+       return vrf->tb_id;
+}
+
+static struct rtable *vrf_get_rtable(const struct net_device *dev,
+                                    const struct flowi4 *fl4)
+{
+       struct rtable *rth = NULL;
+
+       if (!(fl4->flowi4_flags & FLOWI_FLAG_VRFSRC)) {
+               struct net_vrf *vrf = netdev_priv(dev);
+
+               rth = vrf->rth;
+               atomic_inc(&rth->dst.__refcnt);
+       }
+
+       return rth;
+}
+
+static const struct l3mdev_ops vrf_l3mdev_ops = {
+       .l3mdev_fib_table       = vrf_fib_table,
+       .l3mdev_get_rtable      = vrf_get_rtable,
+};
+
 static void vrf_get_drvinfo(struct net_device *dev,
                            struct ethtool_drvinfo *info)
 {
@@ -542,6 +574,7 @@ static void vrf_setup(struct net_device *dev)
 
        /* Initialize the device structure. */
        dev->netdev_ops = &vrf_netdev_ops;
+       dev->l3mdev_ops = &vrf_l3mdev_ops;
        dev->ethtool_ops = &vrf_ethtool_ops;
        dev->destructor = free_netdev;
 
@@ -587,7 +620,7 @@ static int vrf_newlink(struct net *src_net, struct net_device *dev,
 
        vrf->tb_id = nla_get_u32(data[IFLA_VRF_TABLE]);
 
-       dev->priv_flags |= IFF_VRF_MASTER;
+       dev->priv_flags |= IFF_L3MDEV_MASTER;
 
        err = -ENOMEM;
        vrf_ptr = kmalloc(sizeof(*dev->vrf_ptr), GFP_KERNEL);
@@ -653,7 +686,7 @@ static int vrf_device_event(struct notifier_block *unused,
                struct net_vrf_dev *vrf_ptr = rtnl_dereference(dev->vrf_ptr);
                struct net_device *vrf_dev;
 
-               if (!vrf_ptr || netif_is_vrf(dev))
+               if (!vrf_ptr || netif_is_l3_master(dev))
                        goto out;
 
                vrf_dev = netdev_master_upper_dev_get(dev);