Commit fd9f1934 authored by Ville Nuorvala's avatar Ville Nuorvala Committed by Linus Torvalds

[IPV6]: Add and use new 'strict' parameter to ip6_chk_addr().

RFC 2461 requires that the source address of Neighbor Discovery messages
is an address assigned to the sending interface.

Duplicate Address Detection should also be interface specific. We don't,
for example, want a node to DoS itself just because it has two interfaces
on the same link and both happen to listen to the same multicast group. If
there is a true duplicate on the link, the interface doing DAD will notice
it anyway.

The attached patch adds a 'strict' parameter to ip6_chk_addr() and
ip6_get_ifaddr() to allow link-local protocols like ND and DAD to do
strict address checks even on addresses with greater scope than
link-local.
parent 6716de09
...@@ -57,9 +57,11 @@ extern int addrconf_del_ifaddr(void *arg); ...@@ -57,9 +57,11 @@ extern int addrconf_del_ifaddr(void *arg);
extern int addrconf_set_dstaddr(void *arg); extern int addrconf_set_dstaddr(void *arg);
extern int ipv6_chk_addr(struct in6_addr *addr, extern int ipv6_chk_addr(struct in6_addr *addr,
struct net_device *dev); struct net_device *dev,
int strict);
extern struct inet6_ifaddr * ipv6_get_ifaddr(struct in6_addr *addr, extern struct inet6_ifaddr * ipv6_get_ifaddr(struct in6_addr *addr,
struct net_device *dev); struct net_device *dev,
int strict);
extern int ipv6_get_saddr(struct dst_entry *dst, extern int ipv6_get_saddr(struct dst_entry *dst,
struct in6_addr *daddr, struct in6_addr *daddr,
struct in6_addr *saddr); struct in6_addr *saddr);
......
...@@ -910,7 +910,7 @@ static int ipv6_count_addresses(struct inet6_dev *idev) ...@@ -910,7 +910,7 @@ static int ipv6_count_addresses(struct inet6_dev *idev)
return cnt; return cnt;
} }
int ipv6_chk_addr(struct in6_addr *addr, struct net_device *dev) int ipv6_chk_addr(struct in6_addr *addr, struct net_device *dev, int strict)
{ {
struct inet6_ifaddr * ifp; struct inet6_ifaddr * ifp;
u8 hash = ipv6_addr_hash(addr); u8 hash = ipv6_addr_hash(addr);
...@@ -920,7 +920,7 @@ int ipv6_chk_addr(struct in6_addr *addr, struct net_device *dev) ...@@ -920,7 +920,7 @@ int ipv6_chk_addr(struct in6_addr *addr, struct net_device *dev)
if (ipv6_addr_cmp(&ifp->addr, addr) == 0 && if (ipv6_addr_cmp(&ifp->addr, addr) == 0 &&
!(ifp->flags&IFA_F_TENTATIVE)) { !(ifp->flags&IFA_F_TENTATIVE)) {
if (dev == NULL || ifp->idev->dev == dev || if (dev == NULL || ifp->idev->dev == dev ||
!(ifp->scope&(IFA_LINK|IFA_HOST))) !(ifp->scope&(IFA_LINK|IFA_HOST) || strict))
break; break;
} }
} }
...@@ -945,7 +945,7 @@ int ipv6_chk_same_addr(const struct in6_addr *addr, struct net_device *dev) ...@@ -945,7 +945,7 @@ int ipv6_chk_same_addr(const struct in6_addr *addr, struct net_device *dev)
return ifp != NULL; return ifp != NULL;
} }
struct inet6_ifaddr * ipv6_get_ifaddr(struct in6_addr *addr, struct net_device *dev) struct inet6_ifaddr * ipv6_get_ifaddr(struct in6_addr *addr, struct net_device *dev, int strict)
{ {
struct inet6_ifaddr * ifp; struct inet6_ifaddr * ifp;
u8 hash = ipv6_addr_hash(addr); u8 hash = ipv6_addr_hash(addr);
...@@ -954,7 +954,7 @@ struct inet6_ifaddr * ipv6_get_ifaddr(struct in6_addr *addr, struct net_device * ...@@ -954,7 +954,7 @@ struct inet6_ifaddr * ipv6_get_ifaddr(struct in6_addr *addr, struct net_device *
for(ifp = inet6_addr_lst[hash]; ifp; ifp=ifp->lst_next) { for(ifp = inet6_addr_lst[hash]; ifp; ifp=ifp->lst_next) {
if (ipv6_addr_cmp(&ifp->addr, addr) == 0) { if (ipv6_addr_cmp(&ifp->addr, addr) == 0) {
if (dev == NULL || ifp->idev->dev == dev || if (dev == NULL || ifp->idev->dev == dev ||
!(ifp->scope&(IFA_LINK|IFA_HOST))) { !(ifp->scope&(IFA_LINK|IFA_HOST) || strict)) {
in6_ifa_hold(ifp); in6_ifa_hold(ifp);
break; break;
} }
...@@ -1393,7 +1393,7 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len) ...@@ -1393,7 +1393,7 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len)
ok: ok:
ifp = ipv6_get_ifaddr(&addr, dev); ifp = ipv6_get_ifaddr(&addr, dev, 1);
if (ifp == NULL && valid_lft) { if (ifp == NULL && valid_lft) {
int max_addresses = in6_dev->cnf.max_addresses; int max_addresses = in6_dev->cnf.max_addresses;
...@@ -2952,7 +2952,7 @@ static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp) ...@@ -2952,7 +2952,7 @@ static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
if (!ipv6_addr_any(&addr)) if (!ipv6_addr_any(&addr))
ipv6_dev_ac_dec(ifp->idev->dev, &addr); ipv6_dev_ac_dec(ifp->idev->dev, &addr);
} }
if (!ipv6_chk_addr(&ifp->addr, NULL)) if (!ipv6_chk_addr(&ifp->addr, ifp->idev->dev, 1))
ip6_rt_addr_del(&ifp->addr, ifp->idev->dev); ip6_rt_addr_del(&ifp->addr, ifp->idev->dev);
break; break;
} }
......
...@@ -355,7 +355,7 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) ...@@ -355,7 +355,7 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
*/ */
v4addr = LOOPBACK4_IPV6; v4addr = LOOPBACK4_IPV6;
if (!(addr_type & IPV6_ADDR_MULTICAST)) { if (!(addr_type & IPV6_ADDR_MULTICAST)) {
if (!ipv6_chk_addr(&addr->sin6_addr, dev)) { if (!ipv6_chk_addr(&addr->sin6_addr, dev, 0)) {
if (dev) if (dev)
dev_put(dev); dev_put(dev);
err = -EADDRNOTAVAIL; err = -EADDRNOTAVAIL;
......
...@@ -113,7 +113,7 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, struct in6_addr *addr) ...@@ -113,7 +113,7 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, struct in6_addr *addr)
return -EPERM; return -EPERM;
if (ipv6_addr_type(addr) & IPV6_ADDR_MULTICAST) if (ipv6_addr_type(addr) & IPV6_ADDR_MULTICAST)
return -EINVAL; return -EINVAL;
if (ipv6_chk_addr(addr, NULL)) if (ipv6_chk_addr(addr, NULL, 0))
return -EINVAL; return -EINVAL;
pac = sock_kmalloc(sk, sizeof(struct ipv6_ac_socklist), GFP_KERNEL); pac = sock_kmalloc(sk, sizeof(struct ipv6_ac_socklist), GFP_KERNEL);
......
...@@ -307,7 +307,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl, ...@@ -307,7 +307,7 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
return -ENODEV; return -ENODEV;
} }
} }
if (!ipv6_chk_addr(&src_info->ipi6_addr, dev)) { if (!ipv6_chk_addr(&src_info->ipi6_addr, dev, 0)) {
if (dev) if (dev)
dev_put(dev); dev_put(dev);
err = -EINVAL; err = -EINVAL;
......
...@@ -298,7 +298,7 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, ...@@ -298,7 +298,7 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
*/ */
addr_type = ipv6_addr_type(&hdr->daddr); addr_type = ipv6_addr_type(&hdr->daddr);
if (ipv6_chk_addr(&hdr->daddr, skb->dev)) if (ipv6_chk_addr(&hdr->daddr, skb->dev, 0))
saddr = &hdr->daddr; saddr = &hdr->daddr;
/* /*
......
...@@ -777,10 +777,10 @@ static void ip6_tnl_set_cap(struct ip6_tnl *t) ...@@ -777,10 +777,10 @@ static void ip6_tnl_set_cap(struct ip6_tnl *t)
if (p->link) if (p->link)
ldev = dev_get_by_index(p->link); ldev = dev_get_by_index(p->link);
if ((ltype&IPV6_ADDR_UNICAST) && !ipv6_chk_addr(laddr, ldev)) if (ltype&IPV6_ADDR_UNICAST && !ipv6_chk_addr(laddr, ldev, 0))
l_ok = 0; l_ok = 0;
if ((rtype&IPV6_ADDR_UNICAST) && ipv6_chk_addr(raddr, NULL)) if (rtype&IPV6_ADDR_UNICAST && ipv6_chk_addr(raddr, NULL, 0))
r_ok = 0; r_ok = 0;
if (l_ok && r_ok) { if (l_ok && r_ok) {
......
...@@ -434,7 +434,7 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, ...@@ -434,7 +434,7 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr); len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
/* for anycast or proxy, solicited_addr != src_addr */ /* for anycast or proxy, solicited_addr != src_addr */
ifp = ipv6_get_ifaddr(solicited_addr, dev); ifp = ipv6_get_ifaddr(solicited_addr, dev, 1);
if (ifp) { if (ifp) {
src_addr = solicited_addr; src_addr = solicited_addr;
in6_ifa_put(ifp); in6_ifa_put(ifp);
...@@ -680,7 +680,7 @@ static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb) ...@@ -680,7 +680,7 @@ static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
struct in6_addr *target = (struct in6_addr *)&neigh->primary_key; struct in6_addr *target = (struct in6_addr *)&neigh->primary_key;
int probes = atomic_read(&neigh->probes); int probes = atomic_read(&neigh->probes);
if (skb && ipv6_chk_addr(&skb->nh.ipv6h->saddr, dev)) if (skb && ipv6_chk_addr(&skb->nh.ipv6h->saddr, dev, 1))
saddr = &skb->nh.ipv6h->saddr; saddr = &skb->nh.ipv6h->saddr;
if ((probes -= neigh->parms->ucast_probes) < 0) { if ((probes -= neigh->parms->ucast_probes) < 0) {
...@@ -758,7 +758,7 @@ static void ndisc_recv_ns(struct sk_buff *skb) ...@@ -758,7 +758,7 @@ static void ndisc_recv_ns(struct sk_buff *skb)
} }
} }
if ((ifp = ipv6_get_ifaddr(&msg->target, dev)) != NULL) { if ((ifp = ipv6_get_ifaddr(&msg->target, dev, 1)) != NULL) {
if (ifp->flags & IFA_F_TENTATIVE) { if (ifp->flags & IFA_F_TENTATIVE) {
/* Address is tentative. If the source /* Address is tentative. If the source
is unspecified address, it is someone is unspecified address, it is someone
...@@ -955,7 +955,7 @@ static void ndisc_recv_na(struct sk_buff *skb) ...@@ -955,7 +955,7 @@ static void ndisc_recv_na(struct sk_buff *skb)
return; return;
} }
} }
if ((ifp = ipv6_get_ifaddr(&msg->target, dev))) { if ((ifp = ipv6_get_ifaddr(&msg->target, dev, 1))) {
if (ifp->flags & IFA_F_TENTATIVE) { if (ifp->flags & IFA_F_TENTATIVE) {
addrconf_dad_failure(ifp); addrconf_dad_failure(ifp);
return; return;
......
...@@ -227,7 +227,7 @@ static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) ...@@ -227,7 +227,7 @@ static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
v4addr = LOOPBACK4_IPV6; v4addr = LOOPBACK4_IPV6;
if (!(addr_type & IPV6_ADDR_MULTICAST)) { if (!(addr_type & IPV6_ADDR_MULTICAST)) {
err = -EADDRNOTAVAIL; err = -EADDRNOTAVAIL;
if (!ipv6_chk_addr(&addr->sin6_addr, dev)) { if (!ipv6_chk_addr(&addr->sin6_addr, dev, 0)) {
if (dev) if (dev)
dev_put(dev); dev_put(dev);
goto out; goto out;
......
...@@ -510,7 +510,7 @@ static int sctp_v6_available(union sctp_addr *addr, struct sctp_opt *sp) ...@@ -510,7 +510,7 @@ static int sctp_v6_available(union sctp_addr *addr, struct sctp_opt *sp)
if (!(type & IPV6_ADDR_UNICAST)) if (!(type & IPV6_ADDR_UNICAST))
return 0; return 0;
return ipv6_chk_addr(in6, NULL); return ipv6_chk_addr(in6, NULL, 0);
} }
/* This function checks if the address is a valid address to be used for /* This function checks if the address is a valid address to be used for
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment