summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--net/ipv6/af_inet6.c86
-rw-r--r--net/ipv6/datagram.c30
-rw-r--r--net/ipv6/raw.c43
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;