]> git.hungrycats.org Git - linux/commitdiff
[IPV6]: Process ipv4-mapped addresses properly on UDPv6 sockets.
authorHideaki Yoshifuji <yoshfuji@linux-ipv6.org>
Sun, 26 Oct 2003 16:00:07 +0000 (08:00 -0800)
committerDavid S. Miller <davem@nuts.ninka.net>
Sun, 26 Oct 2003 16:00:07 +0000 (08:00 -0800)
net/ipv4/udp.c
net/ipv6/udp.c

index fabe268b99855c7dbef6004c0e875229f0b9bf7a..2114b86335563e94f66e872fc42ad9700edf7b25 100644 (file)
@@ -522,8 +522,13 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
                 * The socket lock must be held while it's corked.
                 */
                lock_sock(sk);
-               if (likely(up->pending))
+               if (likely(up->pending)) {
+                       if (unlikely(up->pending != AF_INET)) {
+                               release_sock(sk);
+                               return -EINVAL;
+                       }
                        goto do_append_data;
+               }
                release_sock(sk);
        }
        ulen += sizeof(struct udphdr);
@@ -642,7 +647,7 @@ back_from_confirm:
        inet->cork.fl.fl_ip_dport = dport;
        inet->cork.fl.fl4_src = saddr;
        inet->cork.fl.fl_ip_sport = inet->sport;
-       up->pending = 1;
+       up->pending = AF_INET;
 
 do_append_data:
        up->len += ulen;
index 1bd8e3ab03b881c45de8928d3ba828d8a0322866..814dfb06be3b72a60e300083012f7f4eefe3b230 100644 (file)
@@ -787,11 +787,56 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg
        struct dst_entry *dst;
        int addr_len = msg->msg_namelen;
        int ulen = len;
-       int addr_type;
        int hlimit = -1;
        int corkreq = up->corkflag || msg->msg_flags&MSG_MORE;
        int err;
-       
+
+       /* destination address check */
+       if (sin6) {
+               if (addr_len < offsetof(struct sockaddr, sa_data))
+                       return -EINVAL;
+
+               switch (sin6->sin6_family) {
+               case AF_INET6:
+                       if (addr_len < SIN6_LEN_RFC2133)
+                               return -EINVAL;
+                       daddr = &sin6->sin6_addr;
+                       break;
+               case AF_INET:
+                       goto do_udp_sendmsg;
+               case AF_UNSPEC:
+                       msg->msg_name = sin6 = NULL;
+                       msg->msg_namelen = addr_len = 0;
+                       daddr = NULL;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+       } else if (!up->pending) {
+               if (sk->sk_state != TCP_ESTABLISHED)
+                       return -EDESTADDRREQ;
+               daddr = &np->daddr;
+       } else 
+               daddr = NULL;
+
+       if (daddr) {
+               if (ipv6_addr_type(daddr) == IPV6_ADDR_MAPPED) {
+                       struct sockaddr_in sin;
+                       sin.sin_family = AF_INET;
+                       sin.sin_port = sin6 ? sin6->sin6_port : inet->dport;
+                       sin.sin_addr.s_addr = daddr->s6_addr[3];
+                       msg->msg_name = &sin;
+                       msg->msg_namelen = sizeof(sin);
+do_udp_sendmsg:
+                       if (__ipv6_only_sock(sk))
+                               return -ENETUNREACH;
+                       return udp_sendmsg(iocb, sk, msg, len);
+               }
+       }
+
+       if (up->pending == AF_INET)
+               return udp_sendmsg(iocb, sk, msg, len);
+
        /* Rough check on arithmetic overflow,
           better check is made in ip6_build_xmit
           */
@@ -805,6 +850,10 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg
                 */
                lock_sock(sk);
                if (likely(up->pending)) {
+                       if (unlikely(up->pending != AF_INET6)) {
+                               release_sock(sk);
+                               return -EINVAL;
+                       }
                        dst = NULL;
                        goto do_append_data;
                }
@@ -815,18 +864,6 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg
        memset(fl, 0, sizeof(*fl));
 
        if (sin6) {
-               if (sin6->sin6_family == AF_INET) {
-                       if (__ipv6_only_sock(sk))
-                               return -ENETUNREACH;
-                       return udp_sendmsg(iocb, sk, msg, len);
-               }
-
-               if (addr_len < SIN6_LEN_RFC2133)
-                       return -EINVAL;
-
-               if (sin6->sin6_family && sin6->sin6_family != AF_INET6)
-                       return -EINVAL;
-
                if (sin6->sin6_port == 0)
                        return -EINVAL;
 
@@ -864,24 +901,6 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg
                fl->fl6_flowlabel = np->flow_label;
        }
 
-       addr_type = ipv6_addr_type(daddr);
-
-       if (addr_type == IPV6_ADDR_MAPPED) {
-               struct sockaddr_in sin;
-
-               if (__ipv6_only_sock(sk))
-                       return -ENETUNREACH;
-
-               sin.sin_family = AF_INET;
-               sin.sin_addr.s_addr = daddr->s6_addr32[3];
-               sin.sin_port = inet->cork.fl.fl_ip_dport;
-               msg->msg_name = (struct sockaddr *)(&sin);
-               msg->msg_namelen = sizeof(sin);
-               fl6_sock_release(flowlabel);
-
-               return udp_sendmsg(iocb, sk, msg, len);
-       }
-
        if (!fl->oif)
                fl->oif = sk->sk_bound_dev_if;
 
@@ -950,7 +969,7 @@ back_from_confirm:
                goto out;
        }
 
-       up->pending = 1;
+       up->pending = AF_INET6;
 
 do_append_data:
        up->len += ulen;