diff options
Diffstat (limited to 'net/ipv4/devinet.c')
| -rw-r--r-- | net/ipv4/devinet.c | 83 | 
1 files changed, 78 insertions, 5 deletions
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index c6287cd978c..dfc39d4d48b 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -536,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]; @@ -801,7 +801,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; @@ -1529,6 +1529,8 @@ static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)  		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; @@ -1549,6 +1551,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++; @@ -1760,8 +1763,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]; @@ -1821,6 +1823,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) @@ -2225,6 +2298,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);  }  |