diff options
Diffstat (limited to 'net/ipv6/addrconf.c')
-rw-r--r-- | net/ipv6/addrconf.c | 169 |
1 files changed, 108 insertions, 61 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index e048ec62d109..5b189c97c2fc 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c | |||
@@ -98,7 +98,11 @@ | |||
98 | #endif | 98 | #endif |
99 | 99 | ||
100 | #define INFINITY_LIFE_TIME 0xFFFFFFFF | 100 | #define INFINITY_LIFE_TIME 0xFFFFFFFF |
101 | #define TIME_DELTA(a, b) ((unsigned long)((long)(a) - (long)(b))) | 101 | |
102 | static inline u32 cstamp_delta(unsigned long cstamp) | ||
103 | { | ||
104 | return (cstamp - INITIAL_JIFFIES) * 100UL / HZ; | ||
105 | } | ||
102 | 106 | ||
103 | #define ADDRCONF_TIMER_FUZZ_MINUS (HZ > 50 ? HZ/50 : 1) | 107 | #define ADDRCONF_TIMER_FUZZ_MINUS (HZ > 50 ? HZ/50 : 1) |
104 | #define ADDRCONF_TIMER_FUZZ (HZ / 4) | 108 | #define ADDRCONF_TIMER_FUZZ (HZ / 4) |
@@ -2665,8 +2669,9 @@ static int addrconf_ifdown(struct net_device *dev, int how) | |||
2665 | 2669 | ||
2666 | ASSERT_RTNL(); | 2670 | ASSERT_RTNL(); |
2667 | 2671 | ||
2668 | rt6_ifdown(net, dev); | 2672 | /* Flush routes if device is being removed or it is not loopback */ |
2669 | neigh_ifdown(&nd_tbl, dev); | 2673 | if (how || !(dev->flags & IFF_LOOPBACK)) |
2674 | rt6_ifdown(net, dev); | ||
2670 | 2675 | ||
2671 | idev = __in6_dev_get(dev); | 2676 | idev = __in6_dev_get(dev); |
2672 | if (idev == NULL) | 2677 | if (idev == NULL) |
@@ -2740,10 +2745,6 @@ static int addrconf_ifdown(struct net_device *dev, int how) | |||
2740 | /* Flag it for later restoration when link comes up */ | 2745 | /* Flag it for later restoration when link comes up */ |
2741 | ifa->flags |= IFA_F_TENTATIVE; | 2746 | ifa->flags |= IFA_F_TENTATIVE; |
2742 | ifa->state = INET6_IFADDR_STATE_DAD; | 2747 | ifa->state = INET6_IFADDR_STATE_DAD; |
2743 | |||
2744 | write_unlock_bh(&idev->lock); | ||
2745 | |||
2746 | in6_ifa_hold(ifa); | ||
2747 | } else { | 2748 | } else { |
2748 | list_del(&ifa->if_list); | 2749 | list_del(&ifa->if_list); |
2749 | 2750 | ||
@@ -2758,19 +2759,15 @@ static int addrconf_ifdown(struct net_device *dev, int how) | |||
2758 | ifa->state = INET6_IFADDR_STATE_DEAD; | 2759 | ifa->state = INET6_IFADDR_STATE_DEAD; |
2759 | spin_unlock_bh(&ifa->state_lock); | 2760 | spin_unlock_bh(&ifa->state_lock); |
2760 | 2761 | ||
2761 | if (state == INET6_IFADDR_STATE_DEAD) | 2762 | if (state != INET6_IFADDR_STATE_DEAD) { |
2762 | goto put_ifa; | 2763 | __ipv6_ifa_notify(RTM_DELADDR, ifa); |
2763 | } | 2764 | atomic_notifier_call_chain(&inet6addr_chain, |
2764 | 2765 | NETDEV_DOWN, ifa); | |
2765 | __ipv6_ifa_notify(RTM_DELADDR, ifa); | 2766 | } |
2766 | if (ifa->state == INET6_IFADDR_STATE_DEAD) | ||
2767 | atomic_notifier_call_chain(&inet6addr_chain, | ||
2768 | NETDEV_DOWN, ifa); | ||
2769 | |||
2770 | put_ifa: | ||
2771 | in6_ifa_put(ifa); | ||
2772 | 2767 | ||
2773 | write_lock_bh(&idev->lock); | 2768 | in6_ifa_put(ifa); |
2769 | write_lock_bh(&idev->lock); | ||
2770 | } | ||
2774 | } | 2771 | } |
2775 | 2772 | ||
2776 | list_splice(&keep_list, &idev->addr_list); | 2773 | list_splice(&keep_list, &idev->addr_list); |
@@ -3452,10 +3449,8 @@ static int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp, | |||
3452 | { | 3449 | { |
3453 | struct ifa_cacheinfo ci; | 3450 | struct ifa_cacheinfo ci; |
3454 | 3451 | ||
3455 | ci.cstamp = (u32)(TIME_DELTA(cstamp, INITIAL_JIFFIES) / HZ * 100 | 3452 | ci.cstamp = cstamp_delta(cstamp); |
3456 | + TIME_DELTA(cstamp, INITIAL_JIFFIES) % HZ * 100 / HZ); | 3453 | ci.tstamp = cstamp_delta(tstamp); |
3457 | ci.tstamp = (u32)(TIME_DELTA(tstamp, INITIAL_JIFFIES) / HZ * 100 | ||
3458 | + TIME_DELTA(tstamp, INITIAL_JIFFIES) % HZ * 100 / HZ); | ||
3459 | ci.ifa_prefered = preferred; | 3454 | ci.ifa_prefered = preferred; |
3460 | ci.ifa_valid = valid; | 3455 | ci.ifa_valid = valid; |
3461 | 3456 | ||
@@ -3806,8 +3801,10 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, | |||
3806 | array[DEVCONF_AUTOCONF] = cnf->autoconf; | 3801 | array[DEVCONF_AUTOCONF] = cnf->autoconf; |
3807 | array[DEVCONF_DAD_TRANSMITS] = cnf->dad_transmits; | 3802 | array[DEVCONF_DAD_TRANSMITS] = cnf->dad_transmits; |
3808 | array[DEVCONF_RTR_SOLICITS] = cnf->rtr_solicits; | 3803 | array[DEVCONF_RTR_SOLICITS] = cnf->rtr_solicits; |
3809 | array[DEVCONF_RTR_SOLICIT_INTERVAL] = cnf->rtr_solicit_interval; | 3804 | array[DEVCONF_RTR_SOLICIT_INTERVAL] = |
3810 | array[DEVCONF_RTR_SOLICIT_DELAY] = cnf->rtr_solicit_delay; | 3805 | jiffies_to_msecs(cnf->rtr_solicit_interval); |
3806 | array[DEVCONF_RTR_SOLICIT_DELAY] = | ||
3807 | jiffies_to_msecs(cnf->rtr_solicit_delay); | ||
3811 | array[DEVCONF_FORCE_MLD_VERSION] = cnf->force_mld_version; | 3808 | array[DEVCONF_FORCE_MLD_VERSION] = cnf->force_mld_version; |
3812 | #ifdef CONFIG_IPV6_PRIVACY | 3809 | #ifdef CONFIG_IPV6_PRIVACY |
3813 | array[DEVCONF_USE_TEMPADDR] = cnf->use_tempaddr; | 3810 | array[DEVCONF_USE_TEMPADDR] = cnf->use_tempaddr; |
@@ -3821,7 +3818,8 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, | |||
3821 | array[DEVCONF_ACCEPT_RA_PINFO] = cnf->accept_ra_pinfo; | 3818 | array[DEVCONF_ACCEPT_RA_PINFO] = cnf->accept_ra_pinfo; |
3822 | #ifdef CONFIG_IPV6_ROUTER_PREF | 3819 | #ifdef CONFIG_IPV6_ROUTER_PREF |
3823 | array[DEVCONF_ACCEPT_RA_RTR_PREF] = cnf->accept_ra_rtr_pref; | 3820 | array[DEVCONF_ACCEPT_RA_RTR_PREF] = cnf->accept_ra_rtr_pref; |
3824 | array[DEVCONF_RTR_PROBE_INTERVAL] = cnf->rtr_probe_interval; | 3821 | array[DEVCONF_RTR_PROBE_INTERVAL] = |
3822 | jiffies_to_msecs(cnf->rtr_probe_interval); | ||
3825 | #ifdef CONFIG_IPV6_ROUTE_INFO | 3823 | #ifdef CONFIG_IPV6_ROUTE_INFO |
3826 | array[DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN] = cnf->accept_ra_rt_info_max_plen; | 3824 | array[DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN] = cnf->accept_ra_rt_info_max_plen; |
3827 | #endif | 3825 | #endif |
@@ -3839,6 +3837,15 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, | |||
3839 | array[DEVCONF_FORCE_TLLAO] = cnf->force_tllao; | 3837 | array[DEVCONF_FORCE_TLLAO] = cnf->force_tllao; |
3840 | } | 3838 | } |
3841 | 3839 | ||
3840 | static inline size_t inet6_ifla6_size(void) | ||
3841 | { | ||
3842 | return nla_total_size(4) /* IFLA_INET6_FLAGS */ | ||
3843 | + nla_total_size(sizeof(struct ifla_cacheinfo)) | ||
3844 | + nla_total_size(DEVCONF_MAX * 4) /* IFLA_INET6_CONF */ | ||
3845 | + nla_total_size(IPSTATS_MIB_MAX * 8) /* IFLA_INET6_STATS */ | ||
3846 | + nla_total_size(ICMP6_MIB_MAX * 8); /* IFLA_INET6_ICMP6STATS */ | ||
3847 | } | ||
3848 | |||
3842 | static inline size_t inet6_if_nlmsg_size(void) | 3849 | static inline size_t inet6_if_nlmsg_size(void) |
3843 | { | 3850 | { |
3844 | return NLMSG_ALIGN(sizeof(struct ifinfomsg)) | 3851 | return NLMSG_ALIGN(sizeof(struct ifinfomsg)) |
@@ -3846,13 +3853,7 @@ static inline size_t inet6_if_nlmsg_size(void) | |||
3846 | + nla_total_size(MAX_ADDR_LEN) /* IFLA_ADDRESS */ | 3853 | + nla_total_size(MAX_ADDR_LEN) /* IFLA_ADDRESS */ |
3847 | + nla_total_size(4) /* IFLA_MTU */ | 3854 | + nla_total_size(4) /* IFLA_MTU */ |
3848 | + nla_total_size(4) /* IFLA_LINK */ | 3855 | + nla_total_size(4) /* IFLA_LINK */ |
3849 | + nla_total_size( /* IFLA_PROTINFO */ | 3856 | + nla_total_size(inet6_ifla6_size()); /* IFLA_PROTINFO */ |
3850 | nla_total_size(4) /* IFLA_INET6_FLAGS */ | ||
3851 | + nla_total_size(sizeof(struct ifla_cacheinfo)) | ||
3852 | + nla_total_size(DEVCONF_MAX * 4) /* IFLA_INET6_CONF */ | ||
3853 | + nla_total_size(IPSTATS_MIB_MAX * 8) /* IFLA_INET6_STATS */ | ||
3854 | + nla_total_size(ICMP6_MIB_MAX * 8) /* IFLA_INET6_ICMP6STATS */ | ||
3855 | ); | ||
3856 | } | 3857 | } |
3857 | 3858 | ||
3858 | static inline void __snmp6_fill_stats(u64 *stats, void __percpu **mib, | 3859 | static inline void __snmp6_fill_stats(u64 *stats, void __percpu **mib, |
@@ -3899,15 +3900,70 @@ static void snmp6_fill_stats(u64 *stats, struct inet6_dev *idev, int attrtype, | |||
3899 | } | 3900 | } |
3900 | } | 3901 | } |
3901 | 3902 | ||
3903 | static int inet6_fill_ifla6_attrs(struct sk_buff *skb, struct inet6_dev *idev) | ||
3904 | { | ||
3905 | struct nlattr *nla; | ||
3906 | struct ifla_cacheinfo ci; | ||
3907 | |||
3908 | NLA_PUT_U32(skb, IFLA_INET6_FLAGS, idev->if_flags); | ||
3909 | |||
3910 | ci.max_reasm_len = IPV6_MAXPLEN; | ||
3911 | ci.tstamp = cstamp_delta(idev->tstamp); | ||
3912 | ci.reachable_time = jiffies_to_msecs(idev->nd_parms->reachable_time); | ||
3913 | ci.retrans_time = jiffies_to_msecs(idev->nd_parms->retrans_time); | ||
3914 | NLA_PUT(skb, IFLA_INET6_CACHEINFO, sizeof(ci), &ci); | ||
3915 | |||
3916 | nla = nla_reserve(skb, IFLA_INET6_CONF, DEVCONF_MAX * sizeof(s32)); | ||
3917 | if (nla == NULL) | ||
3918 | goto nla_put_failure; | ||
3919 | ipv6_store_devconf(&idev->cnf, nla_data(nla), nla_len(nla)); | ||
3920 | |||
3921 | /* XXX - MC not implemented */ | ||
3922 | |||
3923 | nla = nla_reserve(skb, IFLA_INET6_STATS, IPSTATS_MIB_MAX * sizeof(u64)); | ||
3924 | if (nla == NULL) | ||
3925 | goto nla_put_failure; | ||
3926 | snmp6_fill_stats(nla_data(nla), idev, IFLA_INET6_STATS, nla_len(nla)); | ||
3927 | |||
3928 | nla = nla_reserve(skb, IFLA_INET6_ICMP6STATS, ICMP6_MIB_MAX * sizeof(u64)); | ||
3929 | if (nla == NULL) | ||
3930 | goto nla_put_failure; | ||
3931 | snmp6_fill_stats(nla_data(nla), idev, IFLA_INET6_ICMP6STATS, nla_len(nla)); | ||
3932 | |||
3933 | return 0; | ||
3934 | |||
3935 | nla_put_failure: | ||
3936 | return -EMSGSIZE; | ||
3937 | } | ||
3938 | |||
3939 | static size_t inet6_get_link_af_size(const struct net_device *dev) | ||
3940 | { | ||
3941 | if (!__in6_dev_get(dev)) | ||
3942 | return 0; | ||
3943 | |||
3944 | return inet6_ifla6_size(); | ||
3945 | } | ||
3946 | |||
3947 | static int inet6_fill_link_af(struct sk_buff *skb, const struct net_device *dev) | ||
3948 | { | ||
3949 | struct inet6_dev *idev = __in6_dev_get(dev); | ||
3950 | |||
3951 | if (!idev) | ||
3952 | return -ENODATA; | ||
3953 | |||
3954 | if (inet6_fill_ifla6_attrs(skb, idev) < 0) | ||
3955 | return -EMSGSIZE; | ||
3956 | |||
3957 | return 0; | ||
3958 | } | ||
3959 | |||
3902 | static int inet6_fill_ifinfo(struct sk_buff *skb, struct inet6_dev *idev, | 3960 | static int inet6_fill_ifinfo(struct sk_buff *skb, struct inet6_dev *idev, |
3903 | u32 pid, u32 seq, int event, unsigned int flags) | 3961 | u32 pid, u32 seq, int event, unsigned int flags) |
3904 | { | 3962 | { |
3905 | struct net_device *dev = idev->dev; | 3963 | struct net_device *dev = idev->dev; |
3906 | struct nlattr *nla; | ||
3907 | struct ifinfomsg *hdr; | 3964 | struct ifinfomsg *hdr; |
3908 | struct nlmsghdr *nlh; | 3965 | struct nlmsghdr *nlh; |
3909 | void *protoinfo; | 3966 | void *protoinfo; |
3910 | struct ifla_cacheinfo ci; | ||
3911 | 3967 | ||
3912 | nlh = nlmsg_put(skb, pid, seq, event, sizeof(*hdr), flags); | 3968 | nlh = nlmsg_put(skb, pid, seq, event, sizeof(*hdr), flags); |
3913 | if (nlh == NULL) | 3969 | if (nlh == NULL) |
@@ -3934,31 +3990,8 @@ static int inet6_fill_ifinfo(struct sk_buff *skb, struct inet6_dev *idev, | |||
3934 | if (protoinfo == NULL) | 3990 | if (protoinfo == NULL) |
3935 | goto nla_put_failure; | 3991 | goto nla_put_failure; |
3936 | 3992 | ||
3937 | NLA_PUT_U32(skb, IFLA_INET6_FLAGS, idev->if_flags); | 3993 | if (inet6_fill_ifla6_attrs(skb, idev) < 0) |
3938 | |||
3939 | ci.max_reasm_len = IPV6_MAXPLEN; | ||
3940 | ci.tstamp = (__u32)(TIME_DELTA(idev->tstamp, INITIAL_JIFFIES) / HZ * 100 | ||
3941 | + TIME_DELTA(idev->tstamp, INITIAL_JIFFIES) % HZ * 100 / HZ); | ||
3942 | ci.reachable_time = idev->nd_parms->reachable_time; | ||
3943 | ci.retrans_time = idev->nd_parms->retrans_time; | ||
3944 | NLA_PUT(skb, IFLA_INET6_CACHEINFO, sizeof(ci), &ci); | ||
3945 | |||
3946 | nla = nla_reserve(skb, IFLA_INET6_CONF, DEVCONF_MAX * sizeof(s32)); | ||
3947 | if (nla == NULL) | ||
3948 | goto nla_put_failure; | ||
3949 | ipv6_store_devconf(&idev->cnf, nla_data(nla), nla_len(nla)); | ||
3950 | |||
3951 | /* XXX - MC not implemented */ | ||
3952 | |||
3953 | nla = nla_reserve(skb, IFLA_INET6_STATS, IPSTATS_MIB_MAX * sizeof(u64)); | ||
3954 | if (nla == NULL) | ||
3955 | goto nla_put_failure; | 3994 | goto nla_put_failure; |
3956 | snmp6_fill_stats(nla_data(nla), idev, IFLA_INET6_STATS, nla_len(nla)); | ||
3957 | |||
3958 | nla = nla_reserve(skb, IFLA_INET6_ICMP6STATS, ICMP6_MIB_MAX * sizeof(u64)); | ||
3959 | if (nla == NULL) | ||
3960 | goto nla_put_failure; | ||
3961 | snmp6_fill_stats(nla_data(nla), idev, IFLA_INET6_ICMP6STATS, nla_len(nla)); | ||
3962 | 3995 | ||
3963 | nla_nest_end(skb, protoinfo); | 3996 | nla_nest_end(skb, protoinfo); |
3964 | return nlmsg_end(skb, nlh); | 3997 | return nlmsg_end(skb, nlh); |
@@ -4025,11 +4058,11 @@ void inet6_ifinfo_notify(int event, struct inet6_dev *idev) | |||
4025 | kfree_skb(skb); | 4058 | kfree_skb(skb); |
4026 | goto errout; | 4059 | goto errout; |
4027 | } | 4060 | } |
4028 | rtnl_notify(skb, net, 0, RTNLGRP_IPV6_IFADDR, NULL, GFP_ATOMIC); | 4061 | rtnl_notify(skb, net, 0, RTNLGRP_IPV6_IFINFO, NULL, GFP_ATOMIC); |
4029 | return; | 4062 | return; |
4030 | errout: | 4063 | errout: |
4031 | if (err < 0) | 4064 | if (err < 0) |
4032 | rtnl_set_sk_err(net, RTNLGRP_IPV6_IFADDR, err); | 4065 | rtnl_set_sk_err(net, RTNLGRP_IPV6_IFINFO, err); |
4033 | } | 4066 | } |
4034 | 4067 | ||
4035 | static inline size_t inet6_prefix_nlmsg_size(void) | 4068 | static inline size_t inet6_prefix_nlmsg_size(void) |
@@ -4629,6 +4662,12 @@ int unregister_inet6addr_notifier(struct notifier_block *nb) | |||
4629 | } | 4662 | } |
4630 | EXPORT_SYMBOL(unregister_inet6addr_notifier); | 4663 | EXPORT_SYMBOL(unregister_inet6addr_notifier); |
4631 | 4664 | ||
4665 | static struct rtnl_af_ops inet6_ops = { | ||
4666 | .family = AF_INET6, | ||
4667 | .fill_link_af = inet6_fill_link_af, | ||
4668 | .get_link_af_size = inet6_get_link_af_size, | ||
4669 | }; | ||
4670 | |||
4632 | /* | 4671 | /* |
4633 | * Init / cleanup code | 4672 | * Init / cleanup code |
4634 | */ | 4673 | */ |
@@ -4680,6 +4719,10 @@ int __init addrconf_init(void) | |||
4680 | 4719 | ||
4681 | addrconf_verify(0); | 4720 | addrconf_verify(0); |
4682 | 4721 | ||
4722 | err = rtnl_af_register(&inet6_ops); | ||
4723 | if (err < 0) | ||
4724 | goto errout_af; | ||
4725 | |||
4683 | err = __rtnl_register(PF_INET6, RTM_GETLINK, NULL, inet6_dump_ifinfo); | 4726 | err = __rtnl_register(PF_INET6, RTM_GETLINK, NULL, inet6_dump_ifinfo); |
4684 | if (err < 0) | 4727 | if (err < 0) |
4685 | goto errout; | 4728 | goto errout; |
@@ -4695,6 +4738,8 @@ int __init addrconf_init(void) | |||
4695 | 4738 | ||
4696 | return 0; | 4739 | return 0; |
4697 | errout: | 4740 | errout: |
4741 | rtnl_af_unregister(&inet6_ops); | ||
4742 | errout_af: | ||
4698 | unregister_netdevice_notifier(&ipv6_dev_notf); | 4743 | unregister_netdevice_notifier(&ipv6_dev_notf); |
4699 | errlo: | 4744 | errlo: |
4700 | unregister_pernet_subsys(&addrconf_ops); | 4745 | unregister_pernet_subsys(&addrconf_ops); |
@@ -4715,6 +4760,8 @@ void addrconf_cleanup(void) | |||
4715 | 4760 | ||
4716 | rtnl_lock(); | 4761 | rtnl_lock(); |
4717 | 4762 | ||
4763 | __rtnl_af_unregister(&inet6_ops); | ||
4764 | |||
4718 | /* clean dev list */ | 4765 | /* clean dev list */ |
4719 | for_each_netdev(&init_net, dev) { | 4766 | for_each_netdev(&init_net, dev) { |
4720 | if (__in6_dev_get(dev) == NULL) | 4767 | if (__in6_dev_get(dev) == NULL) |