]> Pileus Git - ~andy/linux/blobdiff - net/ipv4/devinet.c
ipv4: provide addr and netconf dump consistency info
[~andy/linux] / net / ipv4 / devinet.c
index 5281314886c1fc3d0bb0488b9338a5a2124158e9..5d985e36753526f4b9cfb37cde3be352556a4b0c 100644 (file)
@@ -139,10 +139,9 @@ struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref)
        u32 hash = inet_addr_hash(net, addr);
        struct net_device *result = NULL;
        struct in_ifaddr *ifa;
-       struct hlist_node *node;
 
        rcu_read_lock();
-       hlist_for_each_entry_rcu(ifa, node, &inet_addr_lst[hash], hash) {
+       hlist_for_each_entry_rcu(ifa, &inet_addr_lst[hash], hash) {
                if (ifa->ifa_local == addr) {
                        struct net_device *dev = ifa->ifa_dev->dev;
 
@@ -537,7 +536,7 @@ struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
        return NULL;
 }
 
-static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
        struct nlattr *tb[IFA_MAX+1];
@@ -588,7 +587,6 @@ static void check_lifetime(struct work_struct *work)
 {
        unsigned long now, next, next_sec, next_sched;
        struct in_ifaddr *ifa;
-       struct hlist_node *node;
        int i;
 
        now = jiffies;
@@ -596,8 +594,7 @@ static void check_lifetime(struct work_struct *work)
 
        rcu_read_lock();
        for (i = 0; i < IN4_ADDR_HSIZE; i++) {
-               hlist_for_each_entry_rcu(ifa, node,
-                                        &inet_addr_lst[i], hash) {
+               hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) {
                        unsigned long age;
 
                        if (ifa->ifa_flags & IFA_F_PERMANENT)
@@ -778,7 +775,7 @@ static struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa)
        return NULL;
 }
 
-static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(skb->sk);
        struct in_ifaddr *ifa;
@@ -1493,7 +1490,6 @@ static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
        struct in_device *in_dev;
        struct in_ifaddr *ifa;
        struct hlist_head *head;
-       struct hlist_node *node;
 
        s_h = cb->args[0];
        s_idx = idx = cb->args[1];
@@ -1503,7 +1499,9 @@ static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
                idx = 0;
                head = &net->dev_index_head[h];
                rcu_read_lock();
-               hlist_for_each_entry_rcu(dev, node, head, index_hlist) {
+               cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^
+                         net->dev_base_seq;
+               hlist_for_each_entry_rcu(dev, head, index_hlist) {
                        if (idx < s_idx)
                                goto cont;
                        if (h > s_h || idx > s_idx)
@@ -1523,6 +1521,7 @@ static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
                                        rcu_read_unlock();
                                        goto done;
                                }
+                               nl_dump_check_consistent(cb, nlmsg_hdr(skb));
                        }
 cont:
                        idx++;
@@ -1734,8 +1733,7 @@ static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = {
 };
 
 static int inet_netconf_get_devconf(struct sk_buff *in_skb,
-                                   struct nlmsghdr *nlh,
-                                   void *arg)
+                                   struct nlmsghdr *nlh)
 {
        struct net *net = sock_net(in_skb->sk);
        struct nlattr *tb[NETCONFA_MAX+1];
@@ -1795,6 +1793,77 @@ 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();
+               cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^
+                         net->dev_base_seq;
+               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;
+                       }
+                       nl_dump_check_consistent(cb, nlmsg_hdr(skb));
+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)
@@ -2199,6 +2268,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);
 }