diff options
| -rw-r--r-- | net/ipv6/af_inet6.c | 86 | ||||
| -rw-r--r-- | net/ipv6/datagram.c | 30 | ||||
| -rw-r--r-- | net/ipv6/raw.c | 43 |
3 files changed, 102 insertions, 57 deletions
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 6d450f2909e9..4bde99b2257e 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -294,6 +294,7 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) __u32 v4addr = 0; unsigned short snum; int addr_type = 0; + int err = 0; /* If the socket has its own bind function then use it. */ if (sk->sk_prot->bind) @@ -305,24 +306,6 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) if ((addr_type & IPV6_ADDR_MULTICAST) && sock->type == SOCK_STREAM) return -EINVAL; - /* Check if the address belongs to the host. */ - if (addr_type == IPV6_ADDR_MAPPED) { - v4addr = addr->sin6_addr.s6_addr32[3]; - if (inet_addr_type(v4addr) != RTN_LOCAL) - return -EADDRNOTAVAIL; - } else { - if (addr_type != IPV6_ADDR_ANY) { - /* ipv4 addr of the socket is invalid. Only the - * unspecified and mapped address have a v4 equivalent. - */ - v4addr = LOOPBACK4_IPV6; - if (!(addr_type & IPV6_ADDR_MULTICAST)) { - if (!ipv6_chk_addr(&addr->sin6_addr, NULL)) - return -EADDRNOTAVAIL; - } - } - } - snum = ntohs(addr->sin6_port); if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE)) return -EACCES; @@ -331,23 +314,56 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) /* Check these errors (active socket, double bind). */ if (sk->sk_state != TCP_CLOSE || inet->num) { - release_sock(sk); - return -EINVAL; + err = -EINVAL; + goto out; } - if (addr_type & IPV6_ADDR_LINKLOCAL) { - if (addr_len >= sizeof(struct sockaddr_in6) && - addr->sin6_scope_id) { - /* Override any existing binding, if another one - * is supplied by user. - */ - sk->sk_bound_dev_if = addr->sin6_scope_id; + /* Check if the address belongs to the host. */ + if (addr_type == IPV6_ADDR_MAPPED) { + v4addr = addr->sin6_addr.s6_addr32[3]; + if (inet_addr_type(v4addr) != RTN_LOCAL) { + err = -EADDRNOTAVAIL; + goto out; } + } else { + if (addr_type != IPV6_ADDR_ANY) { + struct net_device *dev = NULL; + + if (addr_type & IPV6_ADDR_LINKLOCAL) { + if (addr_len >= sizeof(struct sockaddr_in6) && + addr->sin6_scope_id) { + /* Override any existing binding, if another one + * is supplied by user. + */ + sk->sk_bound_dev_if = addr->sin6_scope_id; + } + + /* Binding to link-local address requires an interface */ + if (!sk->sk_bound_dev_if) { + err = -EINVAL; + goto out; + } + dev = dev_get_by_index(sk->sk_bound_dev_if); + if (!dev) { + err = -ENODEV; + goto out; + } + } - /* Binding to link-local address requires an interface */ - if (!sk->sk_bound_dev_if) { - release_sock(sk); - return -EINVAL; + /* ipv4 addr of the socket is invalid. Only the + * unspecified and mapped address have a v4 equivalent. + */ + v4addr = LOOPBACK4_IPV6; + if (!(addr_type & IPV6_ADDR_MULTICAST)) { + if (!ipv6_chk_addr(&addr->sin6_addr, dev)) { + if (dev) + dev_put(dev); + err = -EADDRNOTAVAIL; + goto out; + } + } + if (dev) + dev_put(dev); } } @@ -362,8 +378,8 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) /* Make sure we are allowed to bind here. */ if (sk->sk_prot->get_port(sk, snum)) { inet_reset_saddr(sk); - release_sock(sk); - return -EADDRINUSE; + err = -EADDRINUSE; + goto out; } if (addr_type != IPV6_ADDR_ANY) @@ -373,9 +389,9 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) inet->sport = ntohs(inet->num); inet->dport = 0; inet->daddr = 0; +out: release_sock(sk); - - return 0; + return err; } int inet6_release(struct socket *sock) diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index 6b2f90f02fbe..f0d26709c56f 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -265,6 +265,8 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl, int err = 0; for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { + int addr_type; + struct net_device *dev = NULL; if (cmsg->cmsg_len < sizeof(struct cmsghdr) || (unsigned long)(((char*)cmsg - (char*)msg->msg_control) @@ -291,16 +293,30 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl, fl->oif = src_info->ipi6_ifindex; } - if (!ipv6_addr_any(&src_info->ipi6_addr)) { - if (!ipv6_chk_addr(&src_info->ipi6_addr, NULL)) { - err = -EINVAL; - goto exit_f; - } + addr_type = ipv6_addr_type(&src_info->ipi6_addr); - ipv6_addr_copy(&fl->fl6_src, - &src_info->ipi6_addr); + if (ipv6_addr_type == IPV6_ADDR_ANY) + break; + + if (addr_type & IPV6_ADDR_LINKLOCAL) { + if (!src_info->ipi6_ifindex) + return -EINVAL; + else { + dev = dev_get_by_index(src_info->ipi6_ifindex); + if (!dev) + return -ENODEV; + } + } + if (!ipv6_chk_addr(&src_info->ipi6_addr, dev)) { + if (dev) + dev_put(dev); + err = -EINVAL; + goto exit_f; } + if (dev) + dev_put(dev); + ipv6_addr_copy(&fl->fl6_src, &src_info->ipi6_addr); break; case IPV6_FLOWINFO: diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 78e92ecc02a7..4f61e8da6bd0 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -197,31 +197,44 @@ static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) if (sk->sk_state != TCP_CLOSE) goto out; - if (addr_type & IPV6_ADDR_LINKLOCAL) { - if (addr_len >= sizeof(struct sockaddr_in6) && - addr->sin6_scope_id) { - /* Override any existing binding, if another one - * is supplied by user. - */ - sk->sk_bound_dev_if = addr->sin6_scope_id; - } - - /* Binding to link-local address requires an interface */ - if (!sk->sk_bound_dev_if) - goto out; - } - /* Check if the address belongs to the host. */ if (addr_type != IPV6_ADDR_ANY) { + struct net_device *dev = NULL; + + if (addr_type & IPV6_ADDR_LINKLOCAL) { + if (addr_len >= sizeof(struct sockaddr_in6) && + addr->sin6_scope_id) { + /* Override any existing binding, if another + * one is supplied by user. + */ + sk->sk_bound_dev_if = addr->sin6_scope_id; + } + + /* Binding to link-local address requires an interface */ + if (!sk->sk_bound_dev_if) + goto out; + + dev = dev_get_by_index(sk->sk_bound_dev_if); + if (!dev) { + err = -ENODEV; + goto out; + } + } + /* ipv4 addr of the socket is invalid. Only the * unpecified and mapped address have a v4 equivalent. */ v4addr = LOOPBACK4_IPV6; if (!(addr_type & IPV6_ADDR_MULTICAST)) { err = -EADDRNOTAVAIL; - if (!ipv6_chk_addr(&addr->sin6_addr, NULL)) + if (!ipv6_chk_addr(&addr->sin6_addr, dev)) { + if (dev) + dev_put(dev); goto out; + } } + if (dev) + dev_put(dev); } inet->rcv_saddr = inet->saddr = v4addr; |
