diff options
Diffstat (limited to 'net/ipv6/addrconf.c')
-rw-r--r-- | net/ipv6/addrconf.c | 244 |
1 files changed, 143 insertions, 101 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index f918399c985c..de7a194a64ab 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c | |||
@@ -481,9 +481,8 @@ static void addrconf_forward_change(struct net *net, __s32 newf) | |||
481 | struct net_device *dev; | 481 | struct net_device *dev; |
482 | struct inet6_dev *idev; | 482 | struct inet6_dev *idev; |
483 | 483 | ||
484 | read_lock(&dev_base_lock); | 484 | rcu_read_lock(); |
485 | for_each_netdev(net, dev) { | 485 | for_each_netdev_rcu(net, dev) { |
486 | rcu_read_lock(); | ||
487 | idev = __in6_dev_get(dev); | 486 | idev = __in6_dev_get(dev); |
488 | if (idev) { | 487 | if (idev) { |
489 | int changed = (!idev->cnf.forwarding) ^ (!newf); | 488 | int changed = (!idev->cnf.forwarding) ^ (!newf); |
@@ -491,9 +490,8 @@ static void addrconf_forward_change(struct net *net, __s32 newf) | |||
491 | if (changed) | 490 | if (changed) |
492 | dev_forward_change(idev); | 491 | dev_forward_change(idev); |
493 | } | 492 | } |
494 | rcu_read_unlock(); | ||
495 | } | 493 | } |
496 | read_unlock(&dev_base_lock); | 494 | rcu_read_unlock(); |
497 | } | 495 | } |
498 | 496 | ||
499 | static int addrconf_fixup_forwarding(struct ctl_table *table, int *p, int old) | 497 | static int addrconf_fixup_forwarding(struct ctl_table *table, int *p, int old) |
@@ -1137,10 +1135,9 @@ int ipv6_dev_get_saddr(struct net *net, struct net_device *dst_dev, | |||
1137 | hiscore->rule = -1; | 1135 | hiscore->rule = -1; |
1138 | hiscore->ifa = NULL; | 1136 | hiscore->ifa = NULL; |
1139 | 1137 | ||
1140 | read_lock(&dev_base_lock); | ||
1141 | rcu_read_lock(); | 1138 | rcu_read_lock(); |
1142 | 1139 | ||
1143 | for_each_netdev(net, dev) { | 1140 | for_each_netdev_rcu(net, dev) { |
1144 | struct inet6_dev *idev; | 1141 | struct inet6_dev *idev; |
1145 | 1142 | ||
1146 | /* Candidate Source Address (section 4) | 1143 | /* Candidate Source Address (section 4) |
@@ -1235,7 +1232,6 @@ try_nextdev: | |||
1235 | read_unlock_bh(&idev->lock); | 1232 | read_unlock_bh(&idev->lock); |
1236 | } | 1233 | } |
1237 | rcu_read_unlock(); | 1234 | rcu_read_unlock(); |
1238 | read_unlock(&dev_base_lock); | ||
1239 | 1235 | ||
1240 | if (!hiscore->ifa) | 1236 | if (!hiscore->ifa) |
1241 | return -EADDRNOTAVAIL; | 1237 | return -EADDRNOTAVAIL; |
@@ -3485,85 +3481,114 @@ enum addr_type_t | |||
3485 | ANYCAST_ADDR, | 3481 | ANYCAST_ADDR, |
3486 | }; | 3482 | }; |
3487 | 3483 | ||
3484 | /* called with rcu_read_lock() */ | ||
3485 | static int in6_dump_addrs(struct inet6_dev *idev, struct sk_buff *skb, | ||
3486 | struct netlink_callback *cb, enum addr_type_t type, | ||
3487 | int s_ip_idx, int *p_ip_idx) | ||
3488 | { | ||
3489 | struct inet6_ifaddr *ifa; | ||
3490 | struct ifmcaddr6 *ifmca; | ||
3491 | struct ifacaddr6 *ifaca; | ||
3492 | int err = 1; | ||
3493 | int ip_idx = *p_ip_idx; | ||
3494 | |||
3495 | read_lock_bh(&idev->lock); | ||
3496 | switch (type) { | ||
3497 | case UNICAST_ADDR: | ||
3498 | /* unicast address incl. temp addr */ | ||
3499 | for (ifa = idev->addr_list; ifa; | ||
3500 | ifa = ifa->if_next, ip_idx++) { | ||
3501 | if (ip_idx < s_ip_idx) | ||
3502 | continue; | ||
3503 | err = inet6_fill_ifaddr(skb, ifa, | ||
3504 | NETLINK_CB(cb->skb).pid, | ||
3505 | cb->nlh->nlmsg_seq, | ||
3506 | RTM_NEWADDR, | ||
3507 | NLM_F_MULTI); | ||
3508 | if (err <= 0) | ||
3509 | break; | ||
3510 | } | ||
3511 | break; | ||
3512 | case MULTICAST_ADDR: | ||
3513 | /* multicast address */ | ||
3514 | for (ifmca = idev->mc_list; ifmca; | ||
3515 | ifmca = ifmca->next, ip_idx++) { | ||
3516 | if (ip_idx < s_ip_idx) | ||
3517 | continue; | ||
3518 | err = inet6_fill_ifmcaddr(skb, ifmca, | ||
3519 | NETLINK_CB(cb->skb).pid, | ||
3520 | cb->nlh->nlmsg_seq, | ||
3521 | RTM_GETMULTICAST, | ||
3522 | NLM_F_MULTI); | ||
3523 | if (err <= 0) | ||
3524 | break; | ||
3525 | } | ||
3526 | break; | ||
3527 | case ANYCAST_ADDR: | ||
3528 | /* anycast address */ | ||
3529 | for (ifaca = idev->ac_list; ifaca; | ||
3530 | ifaca = ifaca->aca_next, ip_idx++) { | ||
3531 | if (ip_idx < s_ip_idx) | ||
3532 | continue; | ||
3533 | err = inet6_fill_ifacaddr(skb, ifaca, | ||
3534 | NETLINK_CB(cb->skb).pid, | ||
3535 | cb->nlh->nlmsg_seq, | ||
3536 | RTM_GETANYCAST, | ||
3537 | NLM_F_MULTI); | ||
3538 | if (err <= 0) | ||
3539 | break; | ||
3540 | } | ||
3541 | break; | ||
3542 | default: | ||
3543 | break; | ||
3544 | } | ||
3545 | read_unlock_bh(&idev->lock); | ||
3546 | *p_ip_idx = ip_idx; | ||
3547 | return err; | ||
3548 | } | ||
3549 | |||
3488 | static int inet6_dump_addr(struct sk_buff *skb, struct netlink_callback *cb, | 3550 | static int inet6_dump_addr(struct sk_buff *skb, struct netlink_callback *cb, |
3489 | enum addr_type_t type) | 3551 | enum addr_type_t type) |
3490 | { | 3552 | { |
3553 | struct net *net = sock_net(skb->sk); | ||
3554 | int h, s_h; | ||
3491 | int idx, ip_idx; | 3555 | int idx, ip_idx; |
3492 | int s_idx, s_ip_idx; | 3556 | int s_idx, s_ip_idx; |
3493 | int err = 1; | ||
3494 | struct net_device *dev; | 3557 | struct net_device *dev; |
3495 | struct inet6_dev *idev = NULL; | 3558 | struct inet6_dev *idev; |
3496 | struct inet6_ifaddr *ifa; | 3559 | struct hlist_head *head; |
3497 | struct ifmcaddr6 *ifmca; | 3560 | struct hlist_node *node; |
3498 | struct ifacaddr6 *ifaca; | ||
3499 | struct net *net = sock_net(skb->sk); | ||
3500 | |||
3501 | s_idx = cb->args[0]; | ||
3502 | s_ip_idx = ip_idx = cb->args[1]; | ||
3503 | 3561 | ||
3504 | idx = 0; | 3562 | s_h = cb->args[0]; |
3505 | for_each_netdev(net, dev) { | 3563 | s_idx = idx = cb->args[1]; |
3506 | if (idx < s_idx) | 3564 | s_ip_idx = ip_idx = cb->args[2]; |
3507 | goto cont; | ||
3508 | if (idx > s_idx) | ||
3509 | s_ip_idx = 0; | ||
3510 | ip_idx = 0; | ||
3511 | if ((idev = in6_dev_get(dev)) == NULL) | ||
3512 | goto cont; | ||
3513 | read_lock_bh(&idev->lock); | ||
3514 | switch (type) { | ||
3515 | case UNICAST_ADDR: | ||
3516 | /* unicast address incl. temp addr */ | ||
3517 | for (ifa = idev->addr_list; ifa; | ||
3518 | ifa = ifa->if_next, ip_idx++) { | ||
3519 | if (ip_idx < s_ip_idx) | ||
3520 | continue; | ||
3521 | err = inet6_fill_ifaddr(skb, ifa, | ||
3522 | NETLINK_CB(cb->skb).pid, | ||
3523 | cb->nlh->nlmsg_seq, | ||
3524 | RTM_NEWADDR, | ||
3525 | NLM_F_MULTI); | ||
3526 | } | ||
3527 | break; | ||
3528 | case MULTICAST_ADDR: | ||
3529 | /* multicast address */ | ||
3530 | for (ifmca = idev->mc_list; ifmca; | ||
3531 | ifmca = ifmca->next, ip_idx++) { | ||
3532 | if (ip_idx < s_ip_idx) | ||
3533 | continue; | ||
3534 | err = inet6_fill_ifmcaddr(skb, ifmca, | ||
3535 | NETLINK_CB(cb->skb).pid, | ||
3536 | cb->nlh->nlmsg_seq, | ||
3537 | RTM_GETMULTICAST, | ||
3538 | NLM_F_MULTI); | ||
3539 | } | ||
3540 | break; | ||
3541 | case ANYCAST_ADDR: | ||
3542 | /* anycast address */ | ||
3543 | for (ifaca = idev->ac_list; ifaca; | ||
3544 | ifaca = ifaca->aca_next, ip_idx++) { | ||
3545 | if (ip_idx < s_ip_idx) | ||
3546 | continue; | ||
3547 | err = inet6_fill_ifacaddr(skb, ifaca, | ||
3548 | NETLINK_CB(cb->skb).pid, | ||
3549 | cb->nlh->nlmsg_seq, | ||
3550 | RTM_GETANYCAST, | ||
3551 | NLM_F_MULTI); | ||
3552 | } | ||
3553 | break; | ||
3554 | default: | ||
3555 | break; | ||
3556 | } | ||
3557 | read_unlock_bh(&idev->lock); | ||
3558 | in6_dev_put(idev); | ||
3559 | 3565 | ||
3560 | if (err <= 0) | 3566 | rcu_read_lock(); |
3561 | break; | 3567 | for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { |
3568 | idx = 0; | ||
3569 | head = &net->dev_index_head[h]; | ||
3570 | hlist_for_each_entry_rcu(dev, node, head, index_hlist) { | ||
3571 | if (idx < s_idx) | ||
3572 | goto cont; | ||
3573 | if (idx > s_idx) | ||
3574 | s_ip_idx = 0; | ||
3575 | ip_idx = 0; | ||
3576 | if ((idev = __in6_dev_get(dev)) == NULL) | ||
3577 | goto cont; | ||
3578 | |||
3579 | if (in6_dump_addrs(idev, skb, cb, type, | ||
3580 | s_ip_idx, &ip_idx) <= 0) | ||
3581 | goto done; | ||
3562 | cont: | 3582 | cont: |
3563 | idx++; | 3583 | idx++; |
3584 | } | ||
3564 | } | 3585 | } |
3565 | cb->args[0] = idx; | 3586 | done: |
3566 | cb->args[1] = ip_idx; | 3587 | rcu_read_unlock(); |
3588 | cb->args[0] = h; | ||
3589 | cb->args[1] = idx; | ||
3590 | cb->args[2] = ip_idx; | ||
3591 | |||
3567 | return skb->len; | 3592 | return skb->len; |
3568 | } | 3593 | } |
3569 | 3594 | ||
@@ -3708,6 +3733,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, | |||
3708 | #endif | 3733 | #endif |
3709 | array[DEVCONF_DISABLE_IPV6] = cnf->disable_ipv6; | 3734 | array[DEVCONF_DISABLE_IPV6] = cnf->disable_ipv6; |
3710 | array[DEVCONF_ACCEPT_DAD] = cnf->accept_dad; | 3735 | array[DEVCONF_ACCEPT_DAD] = cnf->accept_dad; |
3736 | array[DEVCONF_FORCE_TLLAO] = cnf->force_tllao; | ||
3711 | } | 3737 | } |
3712 | 3738 | ||
3713 | static inline size_t inet6_if_nlmsg_size(void) | 3739 | static inline size_t inet6_if_nlmsg_size(void) |
@@ -3826,28 +3852,39 @@ nla_put_failure: | |||
3826 | static int inet6_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) | 3852 | static int inet6_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) |
3827 | { | 3853 | { |
3828 | struct net *net = sock_net(skb->sk); | 3854 | struct net *net = sock_net(skb->sk); |
3829 | int idx, err; | 3855 | int h, s_h; |
3830 | int s_idx = cb->args[0]; | 3856 | int idx = 0, s_idx; |
3831 | struct net_device *dev; | 3857 | struct net_device *dev; |
3832 | struct inet6_dev *idev; | 3858 | struct inet6_dev *idev; |
3859 | struct hlist_head *head; | ||
3860 | struct hlist_node *node; | ||
3833 | 3861 | ||
3834 | read_lock(&dev_base_lock); | 3862 | s_h = cb->args[0]; |
3835 | idx = 0; | 3863 | s_idx = cb->args[1]; |
3836 | for_each_netdev(net, dev) { | 3864 | |
3837 | if (idx < s_idx) | 3865 | rcu_read_lock(); |
3838 | goto cont; | 3866 | for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { |
3839 | if ((idev = in6_dev_get(dev)) == NULL) | 3867 | idx = 0; |
3840 | goto cont; | 3868 | head = &net->dev_index_head[h]; |
3841 | err = inet6_fill_ifinfo(skb, idev, NETLINK_CB(cb->skb).pid, | 3869 | hlist_for_each_entry_rcu(dev, node, head, index_hlist) { |
3842 | cb->nlh->nlmsg_seq, RTM_NEWLINK, NLM_F_MULTI); | 3870 | if (idx < s_idx) |
3843 | in6_dev_put(idev); | 3871 | goto cont; |
3844 | if (err <= 0) | 3872 | idev = __in6_dev_get(dev); |
3845 | break; | 3873 | if (!idev) |
3874 | goto cont; | ||
3875 | if (inet6_fill_ifinfo(skb, idev, | ||
3876 | NETLINK_CB(cb->skb).pid, | ||
3877 | cb->nlh->nlmsg_seq, | ||
3878 | RTM_NEWLINK, NLM_F_MULTI) <= 0) | ||
3879 | goto out; | ||
3846 | cont: | 3880 | cont: |
3847 | idx++; | 3881 | idx++; |
3882 | } | ||
3848 | } | 3883 | } |
3849 | read_unlock(&dev_base_lock); | 3884 | out: |
3850 | cb->args[0] = idx; | 3885 | rcu_read_unlock(); |
3886 | cb->args[1] = idx; | ||
3887 | cb->args[0] = h; | ||
3851 | 3888 | ||
3852 | return skb->len; | 3889 | return skb->len; |
3853 | } | 3890 | } |
@@ -4016,9 +4053,8 @@ static void addrconf_disable_change(struct net *net, __s32 newf) | |||
4016 | struct net_device *dev; | 4053 | struct net_device *dev; |
4017 | struct inet6_dev *idev; | 4054 | struct inet6_dev *idev; |
4018 | 4055 | ||
4019 | read_lock(&dev_base_lock); | 4056 | rcu_read_lock(); |
4020 | for_each_netdev(net, dev) { | 4057 | for_each_netdev_rcu(net, dev) { |
4021 | rcu_read_lock(); | ||
4022 | idev = __in6_dev_get(dev); | 4058 | idev = __in6_dev_get(dev); |
4023 | if (idev) { | 4059 | if (idev) { |
4024 | int changed = (!idev->cnf.disable_ipv6) ^ (!newf); | 4060 | int changed = (!idev->cnf.disable_ipv6) ^ (!newf); |
@@ -4026,9 +4062,8 @@ static void addrconf_disable_change(struct net *net, __s32 newf) | |||
4026 | if (changed) | 4062 | if (changed) |
4027 | dev_disable_change(idev); | 4063 | dev_disable_change(idev); |
4028 | } | 4064 | } |
4029 | rcu_read_unlock(); | ||
4030 | } | 4065 | } |
4031 | read_unlock(&dev_base_lock); | 4066 | rcu_read_unlock(); |
4032 | } | 4067 | } |
4033 | 4068 | ||
4034 | static int addrconf_disable_ipv6(struct ctl_table *table, int *p, int old) | 4069 | static int addrconf_disable_ipv6(struct ctl_table *table, int *p, int old) |
@@ -4285,6 +4320,13 @@ static struct addrconf_sysctl_table | |||
4285 | .proc_handler = proc_dointvec, | 4320 | .proc_handler = proc_dointvec, |
4286 | }, | 4321 | }, |
4287 | { | 4322 | { |
4323 | .procname = "force_tllao", | ||
4324 | .data = &ipv6_devconf.force_tllao, | ||
4325 | .maxlen = sizeof(int), | ||
4326 | .mode = 0644, | ||
4327 | .proc_handler = proc_dointvec | ||
4328 | }, | ||
4329 | { | ||
4288 | /* sentinel */ | 4330 | /* sentinel */ |
4289 | } | 4331 | } |
4290 | }, | 4332 | }, |
@@ -4385,7 +4427,7 @@ static int addrconf_init_net(struct net *net) | |||
4385 | all = &ipv6_devconf; | 4427 | all = &ipv6_devconf; |
4386 | dflt = &ipv6_devconf_dflt; | 4428 | dflt = &ipv6_devconf_dflt; |
4387 | 4429 | ||
4388 | if (net != &init_net) { | 4430 | if (!net_eq(net, &init_net)) { |
4389 | all = kmemdup(all, sizeof(ipv6_devconf), GFP_KERNEL); | 4431 | all = kmemdup(all, sizeof(ipv6_devconf), GFP_KERNEL); |
4390 | if (all == NULL) | 4432 | if (all == NULL) |
4391 | goto err_alloc_all; | 4433 | goto err_alloc_all; |
@@ -4431,7 +4473,7 @@ static void addrconf_exit_net(struct net *net) | |||
4431 | __addrconf_sysctl_unregister(net->ipv6.devconf_dflt); | 4473 | __addrconf_sysctl_unregister(net->ipv6.devconf_dflt); |
4432 | __addrconf_sysctl_unregister(net->ipv6.devconf_all); | 4474 | __addrconf_sysctl_unregister(net->ipv6.devconf_all); |
4433 | #endif | 4475 | #endif |
4434 | if (net != &init_net) { | 4476 | if (!net_eq(net, &init_net)) { |
4435 | kfree(net->ipv6.devconf_dflt); | 4477 | kfree(net->ipv6.devconf_dflt); |
4436 | kfree(net->ipv6.devconf_all); | 4478 | kfree(net->ipv6.devconf_all); |
4437 | } | 4479 | } |