net: proc: change proc_net_remove to remove_proc_entry
[firefly-linux-kernel-4.4.55.git] / net / ipv4 / ipmr.c
index a9454cbd953ce700139fdb010a333128b565b800..5f95b3aa579ef03f01ca4a1c5900eb16eecff512 100644 (file)
@@ -828,6 +828,49 @@ static struct mfc_cache *ipmr_cache_find(struct mr_table *mrt,
        return NULL;
 }
 
+/* Look for a (*,*,oif) entry */
+static struct mfc_cache *ipmr_cache_find_any_parent(struct mr_table *mrt,
+                                                   int vifi)
+{
+       int line = MFC_HASH(htonl(INADDR_ANY), htonl(INADDR_ANY));
+       struct mfc_cache *c;
+
+       list_for_each_entry_rcu(c, &mrt->mfc_cache_array[line], list)
+               if (c->mfc_origin == htonl(INADDR_ANY) &&
+                   c->mfc_mcastgrp == htonl(INADDR_ANY) &&
+                   c->mfc_un.res.ttls[vifi] < 255)
+                       return c;
+
+       return NULL;
+}
+
+/* Look for a (*,G) entry */
+static struct mfc_cache *ipmr_cache_find_any(struct mr_table *mrt,
+                                            __be32 mcastgrp, int vifi)
+{
+       int line = MFC_HASH(mcastgrp, htonl(INADDR_ANY));
+       struct mfc_cache *c, *proxy;
+
+       if (mcastgrp == htonl(INADDR_ANY))
+               goto skip;
+
+       list_for_each_entry_rcu(c, &mrt->mfc_cache_array[line], list)
+               if (c->mfc_origin == htonl(INADDR_ANY) &&
+                   c->mfc_mcastgrp == mcastgrp) {
+                       if (c->mfc_un.res.ttls[vifi] < 255)
+                               return c;
+
+                       /* It's ok if the vifi is part of the static tree */
+                       proxy = ipmr_cache_find_any_parent(mrt,
+                                                          c->mfc_parent);
+                       if (proxy && proxy->mfc_un.res.ttls[vifi] < 255)
+                               return c;
+               }
+
+skip:
+       return ipmr_cache_find_any_parent(mrt, vifi);
+}
+
 /*
  *     Allocate a multicast cache entry
  */
@@ -1053,7 +1096,7 @@ ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi, struct sk_buff *skb)
  *     MFC cache manipulation by user space mroute daemon
  */
 
-static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc)
+static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc, int parent)
 {
        int line;
        struct mfc_cache *c, *next;
@@ -1062,7 +1105,8 @@ static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc)
 
        list_for_each_entry_safe(c, next, &mrt->mfc_cache_array[line], list) {
                if (c->mfc_origin == mfc->mfcc_origin.s_addr &&
-                   c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr) {
+                   c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr &&
+                   (parent == -1 || parent == c->mfc_parent)) {
                        list_del_rcu(&c->list);
                        mroute_netlink_event(mrt, c, RTM_DELROUTE);
                        ipmr_cache_free(c);
@@ -1073,7 +1117,7 @@ static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc)
 }
 
 static int ipmr_mfc_add(struct net *net, struct mr_table *mrt,
-                       struct mfcctl *mfc, int mrtsock)
+                       struct mfcctl *mfc, int mrtsock, int parent)
 {
        bool found = false;
        int line;
@@ -1086,7 +1130,8 @@ static int ipmr_mfc_add(struct net *net, struct mr_table *mrt,
 
        list_for_each_entry(c, &mrt->mfc_cache_array[line], list) {
                if (c->mfc_origin == mfc->mfcc_origin.s_addr &&
-                   c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr) {
+                   c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr &&
+                   (parent == -1 || parent == c->mfc_parent)) {
                        found = true;
                        break;
                }
@@ -1103,7 +1148,8 @@ static int ipmr_mfc_add(struct net *net, struct mr_table *mrt,
                return 0;
        }
 
-       if (!ipv4_is_multicast(mfc->mfcc_mcastgrp.s_addr))
+       if (mfc->mfcc_mcastgrp.s_addr != htonl(INADDR_ANY) &&
+           !ipv4_is_multicast(mfc->mfcc_mcastgrp.s_addr))
                return -EINVAL;
 
        c = ipmr_cache_alloc();
@@ -1218,7 +1264,7 @@ static void mrtsock_destruct(struct sock *sk)
 
 int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsigned int optlen)
 {
-       int ret;
+       int ret, parent = 0;
        struct vifctl vif;
        struct mfcctl mfc;
        struct net *net = sock_net(sk);
@@ -1287,16 +1333,22 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsi
                 */
        case MRT_ADD_MFC:
        case MRT_DEL_MFC:
+               parent = -1;
+       case MRT_ADD_MFC_PROXY:
+       case MRT_DEL_MFC_PROXY:
                if (optlen != sizeof(mfc))
                        return -EINVAL;
                if (copy_from_user(&mfc, optval, sizeof(mfc)))
                        return -EFAULT;
+               if (parent == 0)
+                       parent = mfc.mfcc_parent;
                rtnl_lock();
-               if (optname == MRT_DEL_MFC)
-                       ret = ipmr_mfc_delete(mrt, &mfc);
+               if (optname == MRT_DEL_MFC || optname == MRT_DEL_MFC_PROXY)
+                       ret = ipmr_mfc_delete(mrt, &mfc, parent);
                else
                        ret = ipmr_mfc_add(net, mrt, &mfc,
-                                          sk == rtnl_dereference(mrt->mroute_sk));
+                                          sk == rtnl_dereference(mrt->mroute_sk),
+                                          parent);
                rtnl_unlock();
                return ret;
                /*
@@ -1749,17 +1801,28 @@ static int ip_mr_forward(struct net *net, struct mr_table *mrt,
 {
        int psend = -1;
        int vif, ct;
+       int true_vifi = ipmr_find_vif(mrt, skb->dev);
 
        vif = cache->mfc_parent;
        cache->mfc_un.res.pkt++;
        cache->mfc_un.res.bytes += skb->len;
 
+       if (cache->mfc_origin == htonl(INADDR_ANY) && true_vifi >= 0) {
+               struct mfc_cache *cache_proxy;
+
+               /* For an (*,G) entry, we only check that the incomming
+                * interface is part of the static tree.
+                */
+               cache_proxy = ipmr_cache_find_any_parent(mrt, vif);
+               if (cache_proxy &&
+                   cache_proxy->mfc_un.res.ttls[true_vifi] < 255)
+                       goto forward;
+       }
+
        /*
         * Wrong interface: drop packet and (maybe) send PIM assert.
         */
        if (mrt->vif_table[vif].dev != skb->dev) {
-               int true_vifi;
-
                if (rt_is_output_route(skb_rtable(skb))) {
                        /* It is our own packet, looped back.
                         * Very complicated situation...
@@ -1776,7 +1839,6 @@ static int ip_mr_forward(struct net *net, struct mr_table *mrt,
                }
 
                cache->mfc_un.res.wrong_if++;
-               true_vifi = ipmr_find_vif(mrt, skb->dev);
 
                if (true_vifi >= 0 && mrt->mroute_do_assert &&
                    /* pimsm uses asserts, when switching from RPT to SPT,
@@ -1794,15 +1856,34 @@ static int ip_mr_forward(struct net *net, struct mr_table *mrt,
                goto dont_forward;
        }
 
+forward:
        mrt->vif_table[vif].pkt_in++;
        mrt->vif_table[vif].bytes_in += skb->len;
 
        /*
         *      Forward the frame
         */
+       if (cache->mfc_origin == htonl(INADDR_ANY) &&
+           cache->mfc_mcastgrp == htonl(INADDR_ANY)) {
+               if (true_vifi >= 0 &&
+                   true_vifi != cache->mfc_parent &&
+                   ip_hdr(skb)->ttl >
+                               cache->mfc_un.res.ttls[cache->mfc_parent]) {
+                       /* It's an (*,*) entry and the packet is not coming from
+                        * the upstream: forward the packet to the upstream
+                        * only.
+                        */
+                       psend = cache->mfc_parent;
+                       goto last_forward;
+               }
+               goto dont_forward;
+       }
        for (ct = cache->mfc_un.res.maxvif - 1;
             ct >= cache->mfc_un.res.minvif; ct--) {
-               if (ip_hdr(skb)->ttl > cache->mfc_un.res.ttls[ct]) {
+               /* For (*,G) entry, don't forward to the incoming interface */
+               if ((cache->mfc_origin != htonl(INADDR_ANY) ||
+                    ct != true_vifi) &&
+                   ip_hdr(skb)->ttl > cache->mfc_un.res.ttls[ct]) {
                        if (psend != -1) {
                                struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
 
@@ -1813,6 +1894,7 @@ static int ip_mr_forward(struct net *net, struct mr_table *mrt,
                        psend = ct;
                }
        }
+last_forward:
        if (psend != -1) {
                if (local) {
                        struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
@@ -1902,6 +1984,13 @@ int ip_mr_input(struct sk_buff *skb)
 
        /* already under rcu_read_lock() */
        cache = ipmr_cache_find(mrt, ip_hdr(skb)->saddr, ip_hdr(skb)->daddr);
+       if (cache == NULL) {
+               int vif = ipmr_find_vif(mrt, skb->dev);
+
+               if (vif >= 0)
+                       cache = ipmr_cache_find_any(mrt, ip_hdr(skb)->daddr,
+                                                   vif);
+       }
 
        /*
         *      No usable cache entry
@@ -2107,7 +2196,12 @@ int ipmr_get_route(struct net *net, struct sk_buff *skb,
 
        rcu_read_lock();
        cache = ipmr_cache_find(mrt, saddr, daddr);
+       if (cache == NULL && skb->dev) {
+               int vif = ipmr_find_vif(mrt, skb->dev);
 
+               if (vif >= 0)
+                       cache = ipmr_cache_find_any(mrt, daddr, vif);
+       }
        if (cache == NULL) {
                struct sk_buff *skb2;
                struct iphdr *iph;
@@ -2609,16 +2703,16 @@ static int __net_init ipmr_net_init(struct net *net)
 
 #ifdef CONFIG_PROC_FS
        err = -ENOMEM;
-       if (!proc_net_fops_create(net, "ip_mr_vif", 0, &ipmr_vif_fops))
+       if (!proc_create("ip_mr_vif", 0, net->proc_net, &ipmr_vif_fops))
                goto proc_vif_fail;
-       if (!proc_net_fops_create(net, "ip_mr_cache", 0, &ipmr_mfc_fops))
+       if (!proc_create("ip_mr_cache", 0, net->proc_net, &ipmr_mfc_fops))
                goto proc_cache_fail;
 #endif
        return 0;
 
 #ifdef CONFIG_PROC_FS
 proc_cache_fail:
-       proc_net_remove(net, "ip_mr_vif");
+       remove_proc_entry("ip_mr_vif", net->proc_net);
 proc_vif_fail:
        ipmr_rules_exit(net);
 #endif
@@ -2629,8 +2723,8 @@ fail:
 static void __net_exit ipmr_net_exit(struct net *net)
 {
 #ifdef CONFIG_PROC_FS
-       proc_net_remove(net, "ip_mr_cache");
-       proc_net_remove(net, "ip_mr_vif");
+       remove_proc_entry("ip_mr_cache", net->proc_net);
+       remove_proc_entry("ip_mr_vif", net->proc_net);
 #endif
        ipmr_rules_exit(net);
 }