Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[pandora-kernel.git] / net / ipv4 / ipmr.c
index 5df5fd7..7a7ee1c 100644 (file)
@@ -71,6 +71,9 @@
 
 struct mr_table {
        struct list_head        list;
+#ifdef CONFIG_NET_NS
+       struct net              *net;
+#endif
        u32                     id;
        struct sock             *mroute_sk;
        struct timer_list       ipmr_expire_timer;
@@ -125,8 +128,8 @@ static int ip_mr_forward(struct net *net, struct mr_table *mrt,
                         int local);
 static int ipmr_cache_report(struct mr_table *mrt,
                             struct sk_buff *pkt, vifi_t vifi, int assert);
-static int ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
-                           struct mfc_cache *c, struct rtmsg *rtm);
+static int __ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
+                             struct mfc_cache *c, struct rtmsg *rtm);
 static void ipmr_expire_process(unsigned long arg);
 
 #ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES
@@ -213,8 +216,8 @@ static int ipmr_rule_fill(struct fib_rule *rule, struct sk_buff *skb,
        return 0;
 }
 
-static struct fib_rules_ops ipmr_rules_ops_template = {
-       .family         = FIB_RULES_IPMR,
+static const struct fib_rules_ops __net_initdata ipmr_rules_ops_template = {
+       .family         = RTNL_FAMILY_IPMR,
        .rule_size      = sizeof(struct ipmr_rule),
        .addr_size      = sizeof(u32),
        .action         = ipmr_rule_action,
@@ -308,6 +311,7 @@ static struct mr_table *ipmr_new_table(struct net *net, u32 id)
        mrt = kzalloc(sizeof(*mrt), GFP_KERNEL);
        if (mrt == NULL)
                return NULL;
+       write_pnet(&mrt->net, net);
        mrt->id = id;
 
        /* Forwarding cache */
@@ -580,7 +584,7 @@ static inline void ipmr_cache_free(struct mfc_cache *c)
 
 static void ipmr_destroy_unres(struct mr_table *mrt, struct mfc_cache *c)
 {
-       struct net *net = NULL; //mrt->net;
+       struct net *net = read_pnet(&mrt->net);
        struct sk_buff *skb;
        struct nlmsgerr *e;
 
@@ -827,7 +831,7 @@ static void ipmr_cache_resolve(struct net *net, struct mr_table *mrt,
                if (ip_hdr(skb)->version == 0) {
                        struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr));
 
-                       if (ipmr_fill_mroute(mrt, skb, c, NLMSG_DATA(nlh)) > 0) {
+                       if (__ipmr_fill_mroute(mrt, skb, c, NLMSG_DATA(nlh)) > 0) {
                                nlh->nlmsg_len = (skb_tail_pointer(skb) -
                                                  (u8 *)nlh);
                        } else {
@@ -994,7 +998,8 @@ ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi, struct sk_buff *skb)
                atomic_inc(&mrt->cache_resolve_queue_len);
                list_add(&c->list, &mrt->mfc_unres_queue);
 
-               mod_timer(&mrt->ipmr_expire_timer, c->mfc_un.unres.expires);
+               if (atomic_read(&mrt->cache_resolve_queue_len) == 1)
+                       mod_timer(&mrt->ipmr_expire_timer, c->mfc_un.unres.expires);
        }
 
        /*
@@ -1089,12 +1094,14 @@ static int ipmr_mfc_add(struct net *net, struct mr_table *mrt,
         *      Check to see if we resolved a queued list. If so we
         *      need to send on the frames and tidy up.
         */
+       found = false;
        spin_lock_bh(&mfc_unres_lock);
        list_for_each_entry(uc, &mrt->mfc_unres_queue, list) {
                if (uc->mfc_origin == c->mfc_origin &&
                    uc->mfc_mcastgrp == c->mfc_mcastgrp) {
                        list_del(&uc->list);
                        atomic_dec(&mrt->cache_resolve_queue_len);
+                       found = true;
                        break;
                }
        }
@@ -1102,7 +1109,7 @@ static int ipmr_mfc_add(struct net *net, struct mr_table *mrt,
                del_timer(&mrt->ipmr_expire_timer);
        spin_unlock_bh(&mfc_unres_lock);
 
-       if (uc) {
+       if (found) {
                ipmr_cache_resolve(net, mrt, uc, c);
                ipmr_cache_free(uc);
        }
@@ -1593,7 +1600,7 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
         * not mrouter) cannot join to more than one interface - it will
         * result in receiving multiple packets.
         */
-       NF_HOOK(PF_INET, NF_INET_FORWARD, skb, skb->dev, dev,
+       NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD, skb, skb->dev, dev,
                ipmr_forward_finish);
        return;
 
@@ -1766,10 +1773,10 @@ int ip_mr_input(struct sk_buff *skb)
 
                vif = ipmr_find_vif(mrt, skb->dev);
                if (vif >= 0) {
-                       int err = ipmr_cache_unresolved(mrt, vif, skb);
+                       int err2 = ipmr_cache_unresolved(mrt, vif, skb);
                        read_unlock(&mrt_lock);
 
-                       return err;
+                       return err2;
                }
                read_unlock(&mrt_lock);
                kfree_skb(skb);
@@ -1898,9 +1905,8 @@ drop:
 }
 #endif
 
-static int
-ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb, struct mfc_cache *c,
-                struct rtmsg *rtm)
+static int __ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
+                             struct mfc_cache *c, struct rtmsg *rtm)
 {
        int ct;
        struct rtnexthop *nhp;
@@ -1988,11 +1994,93 @@ int ipmr_get_route(struct net *net,
 
        if (!nowait && (rtm->rtm_flags&RTM_F_NOTIFY))
                cache->mfc_flags |= MFC_NOTIFY;
-       err = ipmr_fill_mroute(mrt, skb, cache, rtm);
+       err = __ipmr_fill_mroute(mrt, skb, cache, rtm);
        read_unlock(&mrt_lock);
        return err;
 }
 
+static int ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
+                           u32 pid, u32 seq, struct mfc_cache *c)
+{
+       struct nlmsghdr *nlh;
+       struct rtmsg *rtm;
+
+       nlh = nlmsg_put(skb, pid, seq, RTM_NEWROUTE, sizeof(*rtm), NLM_F_MULTI);
+       if (nlh == NULL)
+               return -EMSGSIZE;
+
+       rtm = nlmsg_data(nlh);
+       rtm->rtm_family   = RTNL_FAMILY_IPMR;
+       rtm->rtm_dst_len  = 32;
+       rtm->rtm_src_len  = 32;
+       rtm->rtm_tos      = 0;
+       rtm->rtm_table    = mrt->id;
+       NLA_PUT_U32(skb, RTA_TABLE, mrt->id);
+       rtm->rtm_type     = RTN_MULTICAST;
+       rtm->rtm_scope    = RT_SCOPE_UNIVERSE;
+       rtm->rtm_protocol = RTPROT_UNSPEC;
+       rtm->rtm_flags    = 0;
+
+       NLA_PUT_BE32(skb, RTA_SRC, c->mfc_origin);
+       NLA_PUT_BE32(skb, RTA_DST, c->mfc_mcastgrp);
+
+       if (__ipmr_fill_mroute(mrt, skb, c, rtm) < 0)
+               goto nla_put_failure;
+
+       return nlmsg_end(skb, nlh);
+
+nla_put_failure:
+       nlmsg_cancel(skb, nlh);
+       return -EMSGSIZE;
+}
+
+static int ipmr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
+{
+       struct net *net = sock_net(skb->sk);
+       struct mr_table *mrt;
+       struct mfc_cache *mfc;
+       unsigned int t = 0, s_t;
+       unsigned int h = 0, s_h;
+       unsigned int e = 0, s_e;
+
+       s_t = cb->args[0];
+       s_h = cb->args[1];
+       s_e = cb->args[2];
+
+       read_lock(&mrt_lock);
+       ipmr_for_each_table(mrt, net) {
+               if (t < s_t)
+                       goto next_table;
+               if (t > s_t)
+                       s_h = 0;
+               for (h = s_h; h < MFC_LINES; h++) {
+                       list_for_each_entry(mfc, &mrt->mfc_cache_array[h], list) {
+                               if (e < s_e)
+                                       goto next_entry;
+                               if (ipmr_fill_mroute(mrt, skb,
+                                                    NETLINK_CB(cb->skb).pid,
+                                                    cb->nlh->nlmsg_seq,
+                                                    mfc) < 0)
+                                       goto done;
+next_entry:
+                               e++;
+                       }
+                       e = s_e = 0;
+               }
+               s_h = 0;
+next_table:
+               t++;
+       }
+done:
+       read_unlock(&mrt_lock);
+
+       cb->args[2] = e;
+       cb->args[1] = h;
+       cb->args[0] = t;
+
+       return skb->len;
+}
+
 #ifdef CONFIG_PROC_FS
 /*
  *     The /proc interfaces to multicast routing /proc/ip_mr_cache /proc/ip_mr_vif
@@ -2221,9 +2309,9 @@ static int ipmr_mfc_seq_show(struct seq_file *seq, void *v)
                const struct ipmr_mfc_iter *it = seq->private;
                const struct mr_table *mrt = it->mrt;
 
-               seq_printf(seq, "%08lX %08lX %-3hd",
-                          (unsigned long) mfc->mfc_mcastgrp,
-                          (unsigned long) mfc->mfc_origin,
+               seq_printf(seq, "%08X %08X %-3hd",
+                          (__force u32) mfc->mfc_mcastgrp,
+                          (__force u32) mfc->mfc_origin,
                           mfc->mfc_parent);
 
                if (it->cache != &mrt->mfc_unres_queue) {
@@ -2349,6 +2437,7 @@ int __init ip_mr_init(void)
                goto add_proto_fail;
        }
 #endif
+       rtnl_register(RTNL_FAMILY_IPMR, RTM_GETROUTE, NULL, ipmr_rtm_dumproute);
        return 0;
 
 #ifdef CONFIG_IP_PIMSM_V2