summaryrefslogtreecommitdiffstats
path: root/net/ipv6/addrconf.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/addrconf.c')
-rw-r--r--net/ipv6/addrconf.c138
1 files changed, 80 insertions, 58 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 36806def8cf..c02280a4d12 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -429,7 +429,7 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev)
ndev->tstamp = jiffies;
addrconf_sysctl_register(ndev);
/* protected by rtnl_lock */
- RCU_INIT_POINTER(dev->ip6_ptr, ndev);
+ rcu_assign_pointer(dev->ip6_ptr, ndev);
/* Join all-node multicast group */
ipv6_dev_mc_inc(dev, &in6addr_linklocal_allnodes);
@@ -502,29 +502,31 @@ static void addrconf_forward_change(struct net *net, __s32 newf)
rcu_read_unlock();
}
-static int addrconf_fixup_forwarding(struct ctl_table *table, int *p, int old)
+static int addrconf_fixup_forwarding(struct ctl_table *table, int *p, int newf)
{
struct net *net;
+ int old;
+
+ if (!rtnl_trylock())
+ return restart_syscall();
net = (struct net *)table->extra2;
- if (p == &net->ipv6.devconf_dflt->forwarding)
- return 0;
+ old = *p;
+ *p = newf;
- if (!rtnl_trylock()) {
- /* Restore the original values before restarting */
- *p = old;
- return restart_syscall();
+ if (p == &net->ipv6.devconf_dflt->forwarding) {
+ rtnl_unlock();
+ return 0;
}
if (p == &net->ipv6.devconf_all->forwarding) {
- __s32 newf = net->ipv6.devconf_all->forwarding;
net->ipv6.devconf_dflt->forwarding = newf;
addrconf_forward_change(net, newf);
- } else if ((!*p) ^ (!old))
+ } else if ((!newf) ^ (!old))
dev_forward_change((struct inet6_dev *)table->extra1);
rtnl_unlock();
- if (*p)
+ if (newf)
rt6_purge_dflt_routers(net);
return 1;
}
@@ -630,13 +632,13 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
goto out;
}
- rt = addrconf_dst_alloc(idev, addr, 0);
+ rt = addrconf_dst_alloc(idev, addr, false);
if (IS_ERR(rt)) {
err = PTR_ERR(rt);
goto out;
}
- ipv6_addr_copy(&ifa->addr, addr);
+ ifa->addr = *addr;
spin_lock_init(&ifa->lock);
spin_lock_init(&ifa->state_lock);
@@ -650,16 +652,6 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
ifa->rt = rt;
- /*
- * part one of RFC 4429, section 3.3
- * We should not configure an address as
- * optimistic if we do not yet know the link
- * layer address of our nexhop router
- */
-
- if (dst_get_neighbour_raw(&rt->dst) == NULL)
- ifa->flags &= ~IFA_F_OPTIMISTIC;
-
ifa->idev = idev;
in6_dev_hold(idev);
/* For caller */
@@ -807,7 +799,7 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)
ip6_del_rt(rt);
rt = NULL;
} else if (!(rt->rt6i_flags & RTF_EXPIRES)) {
- rt->rt6i_expires = expires;
+ rt->dst.expires = expires;
rt->rt6i_flags |= RTF_EXPIRES;
}
}
@@ -1228,7 +1220,7 @@ try_nextdev:
if (!hiscore->ifa)
return -EADDRNOTAVAIL;
- ipv6_addr_copy(saddr, &hiscore->ifa->addr);
+ *saddr = hiscore->ifa->addr;
in6_ifa_put(hiscore->ifa);
return 0;
}
@@ -1249,7 +1241,7 @@ int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr,
list_for_each_entry(ifp, &idev->addr_list, if_list) {
if (ifp->scope == IFA_LINK &&
!(ifp->flags & banned_flags)) {
- ipv6_addr_copy(addr, &ifp->addr);
+ *addr = ifp->addr;
err = 0;
break;
}
@@ -1700,7 +1692,7 @@ addrconf_prefix_route(struct in6_addr *pfx, int plen, struct net_device *dev,
.fc_protocol = RTPROT_KERNEL,
};
- ipv6_addr_copy(&cfg.fc_dst, pfx);
+ cfg.fc_dst = *pfx;
/* Prevent useless cloning on PtP SIT.
This thing is done here expecting that the whole
@@ -1733,7 +1725,7 @@ static struct rt6_info *addrconf_get_prefix_route(const struct in6_addr *pfx,
if (!fn)
goto out;
for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
- if (rt->rt6i_dev->ifindex != dev->ifindex)
+ if (rt->dst.dev->ifindex != dev->ifindex)
continue;
if ((rt->rt6i_flags & flags) != flags)
continue;
@@ -1813,7 +1805,7 @@ static struct inet6_dev *addrconf_add_dev(struct net_device *dev)
return idev;
}
-void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len)
+void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
{
struct prefix_info *pinfo;
__u32 valid_lft;
@@ -1891,11 +1883,11 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len)
rt = NULL;
} else if (addrconf_finite_timeout(rt_expires)) {
/* not infinity */
- rt->rt6i_expires = jiffies + rt_expires;
+ rt->dst.expires = jiffies + rt_expires;
rt->rt6i_flags |= RTF_EXPIRES;
} else {
rt->rt6i_flags &= ~RTF_EXPIRES;
- rt->rt6i_expires = 0;
+ rt->dst.expires = 0;
}
} else if (valid_lft) {
clock_t expires = 0;
@@ -1944,7 +1936,7 @@ ok:
#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
if (in6_dev->cnf.optimistic_dad &&
- !net->ipv6.devconf_all->forwarding)
+ !net->ipv6.devconf_all->forwarding && sllao)
addr_flags = IFA_F_OPTIMISTIC;
#endif
@@ -3078,20 +3070,39 @@ static void addrconf_dad_run(struct inet6_dev *idev)
struct if6_iter_state {
struct seq_net_private p;
int bucket;
+ int offset;
};
-static struct inet6_ifaddr *if6_get_first(struct seq_file *seq)
+static struct inet6_ifaddr *if6_get_first(struct seq_file *seq, loff_t pos)
{
struct inet6_ifaddr *ifa = NULL;
struct if6_iter_state *state = seq->private;
struct net *net = seq_file_net(seq);
+ int p = 0;
+
+ /* initial bucket if pos is 0 */
+ if (pos == 0) {
+ state->bucket = 0;
+ state->offset = 0;
+ }
- for (state->bucket = 0; state->bucket < IN6_ADDR_HSIZE; ++state->bucket) {
+ for (; state->bucket < IN6_ADDR_HSIZE; ++state->bucket) {
struct hlist_node *n;
hlist_for_each_entry_rcu_bh(ifa, n, &inet6_addr_lst[state->bucket],
- addr_lst)
+ addr_lst) {
+ /* sync with offset */
+ if (p < state->offset) {
+ p++;
+ continue;
+ }
+ state->offset++;
if (net_eq(dev_net(ifa->idev->dev), net))
return ifa;
+ }
+
+ /* prepare for next bucket */
+ state->offset = 0;
+ p = 0;
}
return NULL;
}
@@ -3103,13 +3114,17 @@ static struct inet6_ifaddr *if6_get_next(struct seq_file *seq,
struct net *net = seq_file_net(seq);
struct hlist_node *n = &ifa->addr_lst;
- hlist_for_each_entry_continue_rcu_bh(ifa, n, addr_lst)
+ hlist_for_each_entry_continue_rcu_bh(ifa, n, addr_lst) {
+ state->offset++;
if (net_eq(dev_net(ifa->idev->dev), net))
return ifa;
+ }
while (++state->bucket < IN6_ADDR_HSIZE) {
+ state->offset = 0;
hlist_for_each_entry_rcu_bh(ifa, n,
&inet6_addr_lst[state->bucket], addr_lst) {
+ state->offset++;
if (net_eq(dev_net(ifa->idev->dev), net))
return ifa;
}
@@ -3118,21 +3133,11 @@ static struct inet6_ifaddr *if6_get_next(struct seq_file *seq,
return NULL;
}
-static struct inet6_ifaddr *if6_get_idx(struct seq_file *seq, loff_t pos)
-{
- struct inet6_ifaddr *ifa = if6_get_first(seq);
-
- if (ifa)
- while (pos && (ifa = if6_get_next(seq, ifa)) != NULL)
- --pos;
- return pos ? NULL : ifa;
-}
-
static void *if6_seq_start(struct seq_file *seq, loff_t *pos)
__acquires(rcu_bh)
{
rcu_read_lock_bh();
- return if6_get_idx(seq, *pos);
+ return if6_get_first(seq, *pos);
}
static void *if6_seq_next(struct seq_file *seq, void *v, loff_t *pos)
@@ -4257,9 +4262,17 @@ int addrconf_sysctl_forward(ctl_table *ctl, int write,
int *valp = ctl->data;
int val = *valp;
loff_t pos = *ppos;
+ ctl_table lctl;
int ret;
- ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
+ /*
+ * ctl->data points to idev->cnf.forwarding, we should
+ * not modify it until we get the rtnl lock.
+ */
+ lctl = *ctl;
+ lctl.data = &val;
+
+ ret = proc_dointvec(&lctl, write, buffer, lenp, ppos);
if (write)
ret = addrconf_fixup_forwarding(ctl, valp, val);
@@ -4297,26 +4310,27 @@ static void addrconf_disable_change(struct net *net, __s32 newf)
rcu_read_unlock();
}
-static int addrconf_disable_ipv6(struct ctl_table *table, int *p, int old)
+static int addrconf_disable_ipv6(struct ctl_table *table, int *p, int newf)
{
struct net *net;
+ int old;
+
+ if (!rtnl_trylock())
+ return restart_syscall();
net = (struct net *)table->extra2;
+ old = *p;
+ *p = newf;
- if (p == &net->ipv6.devconf_dflt->disable_ipv6)
+ if (p == &net->ipv6.devconf_dflt->disable_ipv6) {
+ rtnl_unlock();
return 0;
-
- if (!rtnl_trylock()) {
- /* Restore the original values before restarting */
- *p = old;
- return restart_syscall();
}
if (p == &net->ipv6.devconf_all->disable_ipv6) {
- __s32 newf = net->ipv6.devconf_all->disable_ipv6;
net->ipv6.devconf_dflt->disable_ipv6 = newf;
addrconf_disable_change(net, newf);
- } else if ((!*p) ^ (!old))
+ } else if ((!newf) ^ (!old))
dev_disable_change((struct inet6_dev *)table->extra1);
rtnl_unlock();
@@ -4330,9 +4344,17 @@ int addrconf_sysctl_disable(ctl_table *ctl, int write,
int *valp = ctl->data;
int val = *valp;
loff_t pos = *ppos;
+ ctl_table lctl;
int ret;
- ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
+ /*
+ * ctl->data points to idev->cnf.disable_ipv6, we should
+ * not modify it until we get the rtnl lock.
+ */
+ lctl = *ctl;
+ lctl.data = &val;
+
+ ret = proc_dointvec(&lctl, write, buffer, lenp, ppos);
if (write)
ret = addrconf_disable_ipv6(ctl, valp, val);