netconf: add the handler to dump entries
authorNicolas Dichtel <nicolas.dichtel@6wind.com>
Tue, 5 Mar 2013 23:42:06 +0000 (23:42 +0000)
committerDavid S. Miller <davem@davemloft.net>
Wed, 6 Mar 2013 20:40:53 +0000 (15:40 -0500)
It's useful to be able to get the initial state of all entries. The patch adds
the support for IPv4 and IPv6.

Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/ipv4/devinet.c
net/ipv6/addrconf.c

index f678507bc8291f7df43108f470e41c3929af50e8..af57bbae05b9b4dcc4fd985460933531cf6ef18c 100644 (file)
@@ -1791,6 +1791,74 @@ errout:
        return err;
 }
 
+static int inet_netconf_dump_devconf(struct sk_buff *skb,
+                                    struct netlink_callback *cb)
+{
+       struct net *net = sock_net(skb->sk);
+       int h, s_h;
+       int idx, s_idx;
+       struct net_device *dev;
+       struct in_device *in_dev;
+       struct hlist_head *head;
+
+       s_h = cb->args[0];
+       s_idx = idx = cb->args[1];
+
+       for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
+               idx = 0;
+               head = &net->dev_index_head[h];
+               rcu_read_lock();
+               hlist_for_each_entry_rcu(dev, head, index_hlist) {
+                       if (idx < s_idx)
+                               goto cont;
+                       in_dev = __in_dev_get_rcu(dev);
+                       if (!in_dev)
+                               goto cont;
+
+                       if (inet_netconf_fill_devconf(skb, dev->ifindex,
+                                                     &in_dev->cnf,
+                                                     NETLINK_CB(cb->skb).portid,
+                                                     cb->nlh->nlmsg_seq,
+                                                     RTM_NEWNETCONF,
+                                                     NLM_F_MULTI,
+                                                     -1) <= 0) {
+                               rcu_read_unlock();
+                               goto done;
+                       }
+cont:
+                       idx++;
+               }
+               rcu_read_unlock();
+       }
+       if (h == NETDEV_HASHENTRIES) {
+               if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL,
+                                             net->ipv4.devconf_all,
+                                             NETLINK_CB(cb->skb).portid,
+                                             cb->nlh->nlmsg_seq,
+                                             RTM_NEWNETCONF, NLM_F_MULTI,
+                                             -1) <= 0)
+                       goto done;
+               else
+                       h++;
+       }
+       if (h == NETDEV_HASHENTRIES + 1) {
+               if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT,
+                                             net->ipv4.devconf_dflt,
+                                             NETLINK_CB(cb->skb).portid,
+                                             cb->nlh->nlmsg_seq,
+                                             RTM_NEWNETCONF, NLM_F_MULTI,
+                                             -1) <= 0)
+                       goto done;
+               else
+                       h++;
+       }
+done:
+       cb->args[0] = h;
+       cb->args[1] = idx;
+
+       return skb->len;
+}
+
 #ifdef CONFIG_SYSCTL
 
 static void devinet_copy_dflt_conf(struct net *net, int i)
@@ -2195,6 +2263,6 @@ void __init devinet_init(void)
        rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, NULL);
        rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, NULL);
        rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf,
-                     NULL, NULL);
+                     inet_netconf_dump_devconf, NULL);
 }
 
index f2c7e615f902d861d99529536ecebff35fbd7fd5..fa36a677490f2c46996222d04e4c7f8691540cb0 100644 (file)
@@ -605,6 +605,74 @@ errout:
        return err;
 }
 
+static int inet6_netconf_dump_devconf(struct sk_buff *skb,
+                                     struct netlink_callback *cb)
+{
+       struct net *net = sock_net(skb->sk);
+       int h, s_h;
+       int idx, s_idx;
+       struct net_device *dev;
+       struct inet6_dev *idev;
+       struct hlist_head *head;
+
+       s_h = cb->args[0];
+       s_idx = idx = cb->args[1];
+
+       for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
+               idx = 0;
+               head = &net->dev_index_head[h];
+               rcu_read_lock();
+               hlist_for_each_entry_rcu(dev, head, index_hlist) {
+                       if (idx < s_idx)
+                               goto cont;
+                       idev = __in6_dev_get(dev);
+                       if (!idev)
+                               goto cont;
+
+                       if (inet6_netconf_fill_devconf(skb, dev->ifindex,
+                                                      &idev->cnf,
+                                                      NETLINK_CB(cb->skb).portid,
+                                                      cb->nlh->nlmsg_seq,
+                                                      RTM_NEWNETCONF,
+                                                      NLM_F_MULTI,
+                                                      -1) <= 0) {
+                               rcu_read_unlock();
+                               goto done;
+                       }
+cont:
+                       idx++;
+               }
+               rcu_read_unlock();
+       }
+       if (h == NETDEV_HASHENTRIES) {
+               if (inet6_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL,
+                                              net->ipv6.devconf_all,
+                                              NETLINK_CB(cb->skb).portid,
+                                              cb->nlh->nlmsg_seq,
+                                              RTM_NEWNETCONF, NLM_F_MULTI,
+                                              -1) <= 0)
+                       goto done;
+               else
+                       h++;
+       }
+       if (h == NETDEV_HASHENTRIES + 1) {
+               if (inet6_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT,
+                                              net->ipv6.devconf_dflt,
+                                              NETLINK_CB(cb->skb).portid,
+                                              cb->nlh->nlmsg_seq,
+                                              RTM_NEWNETCONF, NLM_F_MULTI,
+                                              -1) <= 0)
+                       goto done;
+               else
+                       h++;
+       }
+done:
+       cb->args[0] = h;
+       cb->args[1] = idx;
+
+       return skb->len;
+}
+
 #ifdef CONFIG_SYSCTL
 static void dev_forward_change(struct inet6_dev *idev)
 {
@@ -4940,7 +5008,7 @@ int __init addrconf_init(void)
        __rtnl_register(PF_INET6, RTM_GETANYCAST, NULL,
                        inet6_dump_ifacaddr, NULL);
        __rtnl_register(PF_INET6, RTM_GETNETCONF, inet6_netconf_get_devconf,
-                       NULL, NULL);
+                       inet6_netconf_dump_devconf, NULL);
 
        ipv6_addr_label_rtnl_register();