]> git.neil.brown.name Git - history.git/commitdiff
[IPV6]: Stricter checks on link-locals in bind and sendmsg
authorVille Nuorvala <vnuorval@tcs.hut.fi>
Mon, 19 Jan 2004 13:37:58 +0000 (05:37 -0800)
committerDavid S. Miller <davem@nuts.ninka.net>
Mon, 19 Jan 2004 13:37:58 +0000 (05:37 -0800)
When binding to a link-local address, inet6_bind() and raw6_bind() only
check that an interface is specified and that the address exists, but
they don't check if it actually exists on the specified interface.

Similarly, in datagram_sent_ctl() we don't check for the possibility of a
link-local address when we receive the source address from userspace.

net/ipv6/af_inet6.c
net/ipv6/datagram.c
net/ipv6/raw.c

index 6d450f2909e910a1228028aa958b94a78213d65c..4bde99b2257e1cd22c541046e11bcb9160467a83 100644 (file)
@@ -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)
index 6b2f90f02fbe37b5cf2df51d5f4d4f3b902c6240..f0d26709c56f0396af49c0ebef769b1f0a91cf01 100644 (file)
@@ -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:
index 78e92ecc02a7264ff8830a043d20975a3a081758..4f61e8da6bd0c5e447d33efc916771b6bd8a70c3 100644 (file)
@@ -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;