diff options
Diffstat (limited to 'net/ipv6/addrconf.c')
-rw-r--r-- | net/ipv6/addrconf.c | 82 |
1 files changed, 65 insertions, 17 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index f7c8bbeb27b7..98e4a63d72bb 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c | |||
@@ -201,6 +201,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { | |||
201 | .disable_ipv6 = 0, | 201 | .disable_ipv6 = 0, |
202 | .accept_dad = 1, | 202 | .accept_dad = 1, |
203 | .suppress_frag_ndisc = 1, | 203 | .suppress_frag_ndisc = 1, |
204 | .accept_ra_mtu = 1, | ||
204 | }; | 205 | }; |
205 | 206 | ||
206 | static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { | 207 | static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { |
@@ -238,6 +239,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { | |||
238 | .disable_ipv6 = 0, | 239 | .disable_ipv6 = 0, |
239 | .accept_dad = 1, | 240 | .accept_dad = 1, |
240 | .suppress_frag_ndisc = 1, | 241 | .suppress_frag_ndisc = 1, |
242 | .accept_ra_mtu = 1, | ||
241 | }; | 243 | }; |
242 | 244 | ||
243 | /* Check if a valid qdisc is available */ | 245 | /* Check if a valid qdisc is available */ |
@@ -489,7 +491,8 @@ static int inet6_netconf_fill_devconf(struct sk_buff *skb, int ifindex, | |||
489 | nla_put_s32(skb, NETCONFA_PROXY_NEIGH, devconf->proxy_ndp) < 0) | 491 | nla_put_s32(skb, NETCONFA_PROXY_NEIGH, devconf->proxy_ndp) < 0) |
490 | goto nla_put_failure; | 492 | goto nla_put_failure; |
491 | 493 | ||
492 | return nlmsg_end(skb, nlh); | 494 | nlmsg_end(skb, nlh); |
495 | return 0; | ||
493 | 496 | ||
494 | nla_put_failure: | 497 | nla_put_failure: |
495 | nlmsg_cancel(skb, nlh); | 498 | nlmsg_cancel(skb, nlh); |
@@ -619,7 +622,7 @@ static int inet6_netconf_dump_devconf(struct sk_buff *skb, | |||
619 | cb->nlh->nlmsg_seq, | 622 | cb->nlh->nlmsg_seq, |
620 | RTM_NEWNETCONF, | 623 | RTM_NEWNETCONF, |
621 | NLM_F_MULTI, | 624 | NLM_F_MULTI, |
622 | -1) <= 0) { | 625 | -1) < 0) { |
623 | rcu_read_unlock(); | 626 | rcu_read_unlock(); |
624 | goto done; | 627 | goto done; |
625 | } | 628 | } |
@@ -635,7 +638,7 @@ cont: | |||
635 | NETLINK_CB(cb->skb).portid, | 638 | NETLINK_CB(cb->skb).portid, |
636 | cb->nlh->nlmsg_seq, | 639 | cb->nlh->nlmsg_seq, |
637 | RTM_NEWNETCONF, NLM_F_MULTI, | 640 | RTM_NEWNETCONF, NLM_F_MULTI, |
638 | -1) <= 0) | 641 | -1) < 0) |
639 | goto done; | 642 | goto done; |
640 | else | 643 | else |
641 | h++; | 644 | h++; |
@@ -646,7 +649,7 @@ cont: | |||
646 | NETLINK_CB(cb->skb).portid, | 649 | NETLINK_CB(cb->skb).portid, |
647 | cb->nlh->nlmsg_seq, | 650 | cb->nlh->nlmsg_seq, |
648 | RTM_NEWNETCONF, NLM_F_MULTI, | 651 | RTM_NEWNETCONF, NLM_F_MULTI, |
649 | -1) <= 0) | 652 | -1) < 0) |
650 | goto done; | 653 | goto done; |
651 | else | 654 | else |
652 | h++; | 655 | h++; |
@@ -1519,15 +1522,30 @@ static int ipv6_count_addresses(struct inet6_dev *idev) | |||
1519 | int ipv6_chk_addr(struct net *net, const struct in6_addr *addr, | 1522 | int ipv6_chk_addr(struct net *net, const struct in6_addr *addr, |
1520 | const struct net_device *dev, int strict) | 1523 | const struct net_device *dev, int strict) |
1521 | { | 1524 | { |
1525 | return ipv6_chk_addr_and_flags(net, addr, dev, strict, IFA_F_TENTATIVE); | ||
1526 | } | ||
1527 | EXPORT_SYMBOL(ipv6_chk_addr); | ||
1528 | |||
1529 | int ipv6_chk_addr_and_flags(struct net *net, const struct in6_addr *addr, | ||
1530 | const struct net_device *dev, int strict, | ||
1531 | u32 banned_flags) | ||
1532 | { | ||
1522 | struct inet6_ifaddr *ifp; | 1533 | struct inet6_ifaddr *ifp; |
1523 | unsigned int hash = inet6_addr_hash(addr); | 1534 | unsigned int hash = inet6_addr_hash(addr); |
1535 | u32 ifp_flags; | ||
1524 | 1536 | ||
1525 | rcu_read_lock_bh(); | 1537 | rcu_read_lock_bh(); |
1526 | hlist_for_each_entry_rcu(ifp, &inet6_addr_lst[hash], addr_lst) { | 1538 | hlist_for_each_entry_rcu(ifp, &inet6_addr_lst[hash], addr_lst) { |
1527 | if (!net_eq(dev_net(ifp->idev->dev), net)) | 1539 | if (!net_eq(dev_net(ifp->idev->dev), net)) |
1528 | continue; | 1540 | continue; |
1541 | /* Decouple optimistic from tentative for evaluation here. | ||
1542 | * Ban optimistic addresses explicitly, when required. | ||
1543 | */ | ||
1544 | ifp_flags = (ifp->flags&IFA_F_OPTIMISTIC) | ||
1545 | ? (ifp->flags&~IFA_F_TENTATIVE) | ||
1546 | : ifp->flags; | ||
1529 | if (ipv6_addr_equal(&ifp->addr, addr) && | 1547 | if (ipv6_addr_equal(&ifp->addr, addr) && |
1530 | !(ifp->flags&IFA_F_TENTATIVE) && | 1548 | !(ifp_flags&banned_flags) && |
1531 | (dev == NULL || ifp->idev->dev == dev || | 1549 | (dev == NULL || ifp->idev->dev == dev || |
1532 | !(ifp->scope&(IFA_LINK|IFA_HOST) || strict))) { | 1550 | !(ifp->scope&(IFA_LINK|IFA_HOST) || strict))) { |
1533 | rcu_read_unlock_bh(); | 1551 | rcu_read_unlock_bh(); |
@@ -1538,7 +1556,7 @@ int ipv6_chk_addr(struct net *net, const struct in6_addr *addr, | |||
1538 | rcu_read_unlock_bh(); | 1556 | rcu_read_unlock_bh(); |
1539 | return 0; | 1557 | return 0; |
1540 | } | 1558 | } |
1541 | EXPORT_SYMBOL(ipv6_chk_addr); | 1559 | EXPORT_SYMBOL(ipv6_chk_addr_and_flags); |
1542 | 1560 | ||
1543 | static bool ipv6_chk_same_addr(struct net *net, const struct in6_addr *addr, | 1561 | static bool ipv6_chk_same_addr(struct net *net, const struct in6_addr *addr, |
1544 | struct net_device *dev) | 1562 | struct net_device *dev) |
@@ -4047,7 +4065,8 @@ static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa, | |||
4047 | if (nla_put_u32(skb, IFA_FLAGS, ifa->flags) < 0) | 4065 | if (nla_put_u32(skb, IFA_FLAGS, ifa->flags) < 0) |
4048 | goto error; | 4066 | goto error; |
4049 | 4067 | ||
4050 | return nlmsg_end(skb, nlh); | 4068 | nlmsg_end(skb, nlh); |
4069 | return 0; | ||
4051 | 4070 | ||
4052 | error: | 4071 | error: |
4053 | nlmsg_cancel(skb, nlh); | 4072 | nlmsg_cancel(skb, nlh); |
@@ -4076,7 +4095,8 @@ static int inet6_fill_ifmcaddr(struct sk_buff *skb, struct ifmcaddr6 *ifmca, | |||
4076 | return -EMSGSIZE; | 4095 | return -EMSGSIZE; |
4077 | } | 4096 | } |
4078 | 4097 | ||
4079 | return nlmsg_end(skb, nlh); | 4098 | nlmsg_end(skb, nlh); |
4099 | return 0; | ||
4080 | } | 4100 | } |
4081 | 4101 | ||
4082 | static int inet6_fill_ifacaddr(struct sk_buff *skb, struct ifacaddr6 *ifaca, | 4102 | static int inet6_fill_ifacaddr(struct sk_buff *skb, struct ifacaddr6 *ifaca, |
@@ -4101,7 +4121,8 @@ static int inet6_fill_ifacaddr(struct sk_buff *skb, struct ifacaddr6 *ifaca, | |||
4101 | return -EMSGSIZE; | 4121 | return -EMSGSIZE; |
4102 | } | 4122 | } |
4103 | 4123 | ||
4104 | return nlmsg_end(skb, nlh); | 4124 | nlmsg_end(skb, nlh); |
4125 | return 0; | ||
4105 | } | 4126 | } |
4106 | 4127 | ||
4107 | enum addr_type_t { | 4128 | enum addr_type_t { |
@@ -4134,7 +4155,7 @@ static int in6_dump_addrs(struct inet6_dev *idev, struct sk_buff *skb, | |||
4134 | cb->nlh->nlmsg_seq, | 4155 | cb->nlh->nlmsg_seq, |
4135 | RTM_NEWADDR, | 4156 | RTM_NEWADDR, |
4136 | NLM_F_MULTI); | 4157 | NLM_F_MULTI); |
4137 | if (err <= 0) | 4158 | if (err < 0) |
4138 | break; | 4159 | break; |
4139 | nl_dump_check_consistent(cb, nlmsg_hdr(skb)); | 4160 | nl_dump_check_consistent(cb, nlmsg_hdr(skb)); |
4140 | } | 4161 | } |
@@ -4151,7 +4172,7 @@ static int in6_dump_addrs(struct inet6_dev *idev, struct sk_buff *skb, | |||
4151 | cb->nlh->nlmsg_seq, | 4172 | cb->nlh->nlmsg_seq, |
4152 | RTM_GETMULTICAST, | 4173 | RTM_GETMULTICAST, |
4153 | NLM_F_MULTI); | 4174 | NLM_F_MULTI); |
4154 | if (err <= 0) | 4175 | if (err < 0) |
4155 | break; | 4176 | break; |
4156 | } | 4177 | } |
4157 | break; | 4178 | break; |
@@ -4166,7 +4187,7 @@ static int in6_dump_addrs(struct inet6_dev *idev, struct sk_buff *skb, | |||
4166 | cb->nlh->nlmsg_seq, | 4187 | cb->nlh->nlmsg_seq, |
4167 | RTM_GETANYCAST, | 4188 | RTM_GETANYCAST, |
4168 | NLM_F_MULTI); | 4189 | NLM_F_MULTI); |
4169 | if (err <= 0) | 4190 | if (err < 0) |
4170 | break; | 4191 | break; |
4171 | } | 4192 | } |
4172 | break; | 4193 | break; |
@@ -4209,7 +4230,7 @@ static int inet6_dump_addr(struct sk_buff *skb, struct netlink_callback *cb, | |||
4209 | goto cont; | 4230 | goto cont; |
4210 | 4231 | ||
4211 | if (in6_dump_addrs(idev, skb, cb, type, | 4232 | if (in6_dump_addrs(idev, skb, cb, type, |
4212 | s_ip_idx, &ip_idx) <= 0) | 4233 | s_ip_idx, &ip_idx) < 0) |
4213 | goto done; | 4234 | goto done; |
4214 | cont: | 4235 | cont: |
4215 | idx++; | 4236 | idx++; |
@@ -4376,6 +4397,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, | |||
4376 | array[DEVCONF_NDISC_NOTIFY] = cnf->ndisc_notify; | 4397 | array[DEVCONF_NDISC_NOTIFY] = cnf->ndisc_notify; |
4377 | array[DEVCONF_SUPPRESS_FRAG_NDISC] = cnf->suppress_frag_ndisc; | 4398 | array[DEVCONF_SUPPRESS_FRAG_NDISC] = cnf->suppress_frag_ndisc; |
4378 | array[DEVCONF_ACCEPT_RA_FROM_LOCAL] = cnf->accept_ra_from_local; | 4399 | array[DEVCONF_ACCEPT_RA_FROM_LOCAL] = cnf->accept_ra_from_local; |
4400 | array[DEVCONF_ACCEPT_RA_MTU] = cnf->accept_ra_mtu; | ||
4379 | } | 4401 | } |
4380 | 4402 | ||
4381 | static inline size_t inet6_ifla6_size(void) | 4403 | static inline size_t inet6_ifla6_size(void) |
@@ -4572,6 +4594,22 @@ static int inet6_set_iftoken(struct inet6_dev *idev, struct in6_addr *token) | |||
4572 | return 0; | 4594 | return 0; |
4573 | } | 4595 | } |
4574 | 4596 | ||
4597 | static const struct nla_policy inet6_af_policy[IFLA_INET6_MAX + 1] = { | ||
4598 | [IFLA_INET6_ADDR_GEN_MODE] = { .type = NLA_U8 }, | ||
4599 | [IFLA_INET6_TOKEN] = { .len = sizeof(struct in6_addr) }, | ||
4600 | }; | ||
4601 | |||
4602 | static int inet6_validate_link_af(const struct net_device *dev, | ||
4603 | const struct nlattr *nla) | ||
4604 | { | ||
4605 | struct nlattr *tb[IFLA_INET6_MAX + 1]; | ||
4606 | |||
4607 | if (dev && !__in6_dev_get(dev)) | ||
4608 | return -EAFNOSUPPORT; | ||
4609 | |||
4610 | return nla_parse_nested(tb, IFLA_INET6_MAX, nla, inet6_af_policy); | ||
4611 | } | ||
4612 | |||
4575 | static int inet6_set_link_af(struct net_device *dev, const struct nlattr *nla) | 4613 | static int inet6_set_link_af(struct net_device *dev, const struct nlattr *nla) |
4576 | { | 4614 | { |
4577 | int err = -EINVAL; | 4615 | int err = -EINVAL; |
@@ -4638,7 +4676,8 @@ static int inet6_fill_ifinfo(struct sk_buff *skb, struct inet6_dev *idev, | |||
4638 | goto nla_put_failure; | 4676 | goto nla_put_failure; |
4639 | 4677 | ||
4640 | nla_nest_end(skb, protoinfo); | 4678 | nla_nest_end(skb, protoinfo); |
4641 | return nlmsg_end(skb, nlh); | 4679 | nlmsg_end(skb, nlh); |
4680 | return 0; | ||
4642 | 4681 | ||
4643 | nla_put_failure: | 4682 | nla_put_failure: |
4644 | nlmsg_cancel(skb, nlh); | 4683 | nlmsg_cancel(skb, nlh); |
@@ -4670,7 +4709,7 @@ static int inet6_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) | |||
4670 | if (inet6_fill_ifinfo(skb, idev, | 4709 | if (inet6_fill_ifinfo(skb, idev, |
4671 | NETLINK_CB(cb->skb).portid, | 4710 | NETLINK_CB(cb->skb).portid, |
4672 | cb->nlh->nlmsg_seq, | 4711 | cb->nlh->nlmsg_seq, |
4673 | RTM_NEWLINK, NLM_F_MULTI) <= 0) | 4712 | RTM_NEWLINK, NLM_F_MULTI) < 0) |
4674 | goto out; | 4713 | goto out; |
4675 | cont: | 4714 | cont: |
4676 | idx++; | 4715 | idx++; |
@@ -4747,7 +4786,8 @@ static int inet6_fill_prefix(struct sk_buff *skb, struct inet6_dev *idev, | |||
4747 | ci.valid_time = ntohl(pinfo->valid); | 4786 | ci.valid_time = ntohl(pinfo->valid); |
4748 | if (nla_put(skb, PREFIX_CACHEINFO, sizeof(ci), &ci)) | 4787 | if (nla_put(skb, PREFIX_CACHEINFO, sizeof(ci), &ci)) |
4749 | goto nla_put_failure; | 4788 | goto nla_put_failure; |
4750 | return nlmsg_end(skb, nlh); | 4789 | nlmsg_end(skb, nlh); |
4790 | return 0; | ||
4751 | 4791 | ||
4752 | nla_put_failure: | 4792 | nla_put_failure: |
4753 | nlmsg_cancel(skb, nlh); | 4793 | nlmsg_cancel(skb, nlh); |
@@ -5253,6 +5293,13 @@ static struct addrconf_sysctl_table | |||
5253 | .proc_handler = proc_dointvec, | 5293 | .proc_handler = proc_dointvec, |
5254 | }, | 5294 | }, |
5255 | { | 5295 | { |
5296 | .procname = "accept_ra_mtu", | ||
5297 | .data = &ipv6_devconf.accept_ra_mtu, | ||
5298 | .maxlen = sizeof(int), | ||
5299 | .mode = 0644, | ||
5300 | .proc_handler = proc_dointvec, | ||
5301 | }, | ||
5302 | { | ||
5256 | /* sentinel */ | 5303 | /* sentinel */ |
5257 | } | 5304 | } |
5258 | }, | 5305 | }, |
@@ -5389,10 +5436,11 @@ static struct pernet_operations addrconf_ops = { | |||
5389 | .exit = addrconf_exit_net, | 5436 | .exit = addrconf_exit_net, |
5390 | }; | 5437 | }; |
5391 | 5438 | ||
5392 | static struct rtnl_af_ops inet6_ops = { | 5439 | static struct rtnl_af_ops inet6_ops __read_mostly = { |
5393 | .family = AF_INET6, | 5440 | .family = AF_INET6, |
5394 | .fill_link_af = inet6_fill_link_af, | 5441 | .fill_link_af = inet6_fill_link_af, |
5395 | .get_link_af_size = inet6_get_link_af_size, | 5442 | .get_link_af_size = inet6_get_link_af_size, |
5443 | .validate_link_af = inet6_validate_link_af, | ||
5396 | .set_link_af = inet6_set_link_af, | 5444 | .set_link_af = inet6_set_link_af, |
5397 | }; | 5445 | }; |
5398 | 5446 | ||