sctp: Fixup v4mapped behaviour to comply with Sock API
[pandora-kernel.git] / net / sctp / ipv6.c
index 1999592..0e4198e 100644 (file)
@@ -434,7 +434,7 @@ static void sctp_v6_from_sk(union sctp_addr *addr, struct sock *sk)
 /* Initialize sk->sk_rcv_saddr from sctp_addr. */
 static void sctp_v6_to_sk_saddr(union sctp_addr *addr, struct sock *sk)
 {
-       if (addr->sa.sa_family == AF_INET && sctp_sk(sk)->v4mapped) {
+       if (addr->sa.sa_family == AF_INET) {
                sk->sk_v6_rcv_saddr.s6_addr32[0] = 0;
                sk->sk_v6_rcv_saddr.s6_addr32[1] = 0;
                sk->sk_v6_rcv_saddr.s6_addr32[2] = htonl(0x0000ffff);
@@ -448,7 +448,7 @@ static void sctp_v6_to_sk_saddr(union sctp_addr *addr, struct sock *sk)
 /* Initialize sk->sk_daddr from sctp_addr. */
 static void sctp_v6_to_sk_daddr(union sctp_addr *addr, struct sock *sk)
 {
-       if (addr->sa.sa_family == AF_INET && sctp_sk(sk)->v4mapped) {
+       if (addr->sa.sa_family == AF_INET) {
                sk->sk_v6_daddr.s6_addr32[0] = 0;
                sk->sk_v6_daddr.s6_addr32[1] = 0;
                sk->sk_v6_daddr.s6_addr32[2] = htonl(0x0000ffff);
@@ -556,8 +556,6 @@ static int sctp_v6_available(union sctp_addr *addr, struct sctp_sock *sp)
        if (IPV6_ADDR_ANY == type)
                return 1;
        if (type == IPV6_ADDR_MAPPED) {
-               if (sp && !sp->v4mapped)
-                       return 0;
                if (sp && ipv6_only_sock(sctp_opt2sk(sp)))
                        return 0;
                sctp_v6_map_v4(addr);
@@ -587,8 +585,6 @@ static int sctp_v6_addr_valid(union sctp_addr *addr,
                /* Note: This routine is used in input, so v4-mapped-v6
                 * are disallowed here when there is no sctp_sock.
                 */
-               if (!sp || !sp->v4mapped)
-                       return 0;
                if (sp && ipv6_only_sock(sctp_opt2sk(sp)))
                        return 0;
                sctp_v6_map_v4(addr);
@@ -675,11 +671,23 @@ out:
        return newsk;
 }
 
-/* Map v4 address to mapped v6 address */
-static void sctp_v6_addr_v4map(struct sctp_sock *sp, union sctp_addr *addr)
+/* Format a sockaddr for return to user space. This makes sure the return is
+ * AF_INET or AF_INET6 depending on the SCTP_I_WANT_MAPPED_V4_ADDR option.
+ */
+static int sctp_v6_addr_to_user(struct sctp_sock *sp, union sctp_addr *addr)
 {
-       if (sp->v4mapped && AF_INET == addr->sa.sa_family)
-               sctp_v4_map_v6(addr);
+       if (sp->v4mapped) {
+               if (addr->sa.sa_family == AF_INET)
+                       sctp_v4_map_v6(addr);
+       } else {
+               if (addr->sa.sa_family == AF_INET6 &&
+                   ipv6_addr_v4mapped(&addr->v6.sin6_addr))
+                       sctp_v6_map_v4(addr);
+       }
+
+       if (addr->sa.sa_family == AF_INET)
+               return sizeof(struct sockaddr_in);
+       return sizeof(struct sockaddr_in6);
 }
 
 /* Where did this skb come from?  */
@@ -706,82 +714,68 @@ static void sctp_v6_ecn_capable(struct sock *sk)
        inet6_sk(sk)->tclass |= INET_ECN_ECT_0;
 }
 
-/* Initialize a PF_INET6 socket msg_name. */
-static void sctp_inet6_msgname(char *msgname, int *addr_len)
-{
-       struct sockaddr_in6 *sin6;
-
-       sin6 = (struct sockaddr_in6 *)msgname;
-       sin6->sin6_family = AF_INET6;
-       sin6->sin6_flowinfo = 0;
-       sin6->sin6_scope_id = 0; /*FIXME */
-       *addr_len = sizeof(struct sockaddr_in6);
-}
-
 /* Initialize a PF_INET msgname from a ulpevent. */
 static void sctp_inet6_event_msgname(struct sctp_ulpevent *event,
                                     char *msgname, int *addrlen)
 {
-       struct sockaddr_in6 *sin6, *sin6from;
-
-       if (msgname) {
-               union sctp_addr *addr;
-               struct sctp_association *asoc;
-
-               asoc = event->asoc;
-               sctp_inet6_msgname(msgname, addrlen);
-               sin6 = (struct sockaddr_in6 *)msgname;
-               sin6->sin6_port = htons(asoc->peer.port);
-               addr = &asoc->peer.primary_addr;
+       union sctp_addr *addr;
+       struct sctp_association *asoc;
+       union sctp_addr *paddr;
 
-               /* Note: If we go to a common v6 format, this code
-                * will change.
-                */
+       if (!msgname)
+               return;
 
-               /* Map ipv4 address into v4-mapped-on-v6 address.  */
-               if (sctp_sk(asoc->base.sk)->v4mapped &&
-                   AF_INET == addr->sa.sa_family) {
-                       sctp_v4_map_v6((union sctp_addr *)sin6);
-                       sin6->sin6_addr.s6_addr32[3] =
-                               addr->v4.sin_addr.s_addr;
-                       return;
-               }
+       addr = (union sctp_addr *)msgname;
+       asoc = event->asoc;
+       paddr = &asoc->peer.primary_addr;
 
-               sin6from = &asoc->peer.primary_addr.v6;
-               sin6->sin6_addr = sin6from->sin6_addr;
-               if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL)
-                       sin6->sin6_scope_id = sin6from->sin6_scope_id;
+       if (paddr->sa.sa_family == AF_INET) {
+               addr->v4.sin_family = AF_INET;
+               addr->v4.sin_port = htons(asoc->peer.port);
+               addr->v4.sin_addr = paddr->v4.sin_addr;
+       } else {
+               addr->v6.sin6_family = AF_INET6;
+               addr->v6.sin6_flowinfo = 0;
+               if (ipv6_addr_type(&paddr->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL)
+                       addr->v6.sin6_scope_id = paddr->v6.sin6_scope_id;
+               else
+                       addr->v6.sin6_scope_id = 0;
+               addr->v6.sin6_port = htons(asoc->peer.port);
+               addr->v6.sin6_addr = paddr->v6.sin6_addr;
        }
+
+       *addrlen = sctp_v6_addr_to_user(sctp_sk(asoc->base.sk), addr);
 }
 
 /* Initialize a msg_name from an inbound skb. */
 static void sctp_inet6_skb_msgname(struct sk_buff *skb, char *msgname,
                                   int *addr_len)
 {
+       union sctp_addr *addr;
        struct sctphdr *sh;
-       struct sockaddr_in6 *sin6;
-
-       if (msgname) {
-               sctp_inet6_msgname(msgname, addr_len);
-               sin6 = (struct sockaddr_in6 *)msgname;
-               sh = sctp_hdr(skb);
-               sin6->sin6_port = sh->source;
-
-               /* Map ipv4 address into v4-mapped-on-v6 address. */
-               if (sctp_sk(skb->sk)->v4mapped &&
-                   ip_hdr(skb)->version == 4) {
-                       sctp_v4_map_v6((union sctp_addr *)sin6);
-                       sin6->sin6_addr.s6_addr32[3] = ip_hdr(skb)->saddr;
-                       return;
-               }
 
-               /* Otherwise, just copy the v6 address. */
-               sin6->sin6_addr = ipv6_hdr(skb)->saddr;
-               if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) {
+       if (!msgname)
+               return;
+
+       addr = (union sctp_addr *)msgname;
+       sh = sctp_hdr(skb);
+
+       if (ip_hdr(skb)->version == 4) {
+               addr->v4.sin_family = AF_INET;
+               addr->v4.sin_port = sh->source;
+               addr->v4.sin_addr.s_addr =  ip_hdr(skb)->saddr;
+       } else {
+               addr->v6.sin6_family = AF_INET6;
+               addr->v6.sin6_flowinfo = 0;
+               addr->v6.sin6_port = sh->source;
+               addr->v6.sin6_addr = ipv6_hdr(skb)->saddr;
+               if (ipv6_addr_type(&addr->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL) {
                        struct sctp_ulpevent *ev = sctp_skb2event(skb);
-                       sin6->sin6_scope_id = ev->iif;
+                       addr->v6.sin6_scope_id = ev->iif;
                }
        }
+
+       *addr_len = sctp_v6_addr_to_user(sctp_sk(skb->sk), addr);
 }
 
 /* Do we support this AF? */
@@ -857,9 +851,6 @@ static int sctp_inet6_bind_verify(struct sctp_sock *opt, union sctp_addr *addr)
                                return 0;
                        }
                        rcu_read_unlock();
-               } else if (type == IPV6_ADDR_MAPPED) {
-                       if (!opt->v4mapped)
-                               return 0;
                }
 
                af = opt->pf->af;
@@ -914,6 +905,23 @@ static int sctp_inet6_supported_addrs(const struct sctp_sock *opt,
        return 1;
 }
 
+/* Handle SCTP_I_WANT_MAPPED_V4_ADDR for getpeername() and getsockname() */
+static int sctp_getname(struct socket *sock, struct sockaddr *uaddr,
+                       int *uaddr_len, int peer)
+{
+       int rc;
+
+       rc = inet6_getname(sock, uaddr, uaddr_len, peer);
+
+       if (rc != 0)
+               return rc;
+
+       *uaddr_len = sctp_v6_addr_to_user(sctp_sk(sock->sk),
+                                         (union sctp_addr *)uaddr);
+
+       return rc;
+}
+
 static const struct proto_ops inet6_seqpacket_ops = {
        .family            = PF_INET6,
        .owner             = THIS_MODULE,
@@ -922,7 +930,7 @@ static const struct proto_ops inet6_seqpacket_ops = {
        .connect           = inet_dgram_connect,
        .socketpair        = sock_no_socketpair,
        .accept            = inet_accept,
-       .getname           = inet6_getname,
+       .getname           = sctp_getname,
        .poll              = sctp_poll,
        .ioctl             = inet6_ioctl,
        .listen            = sctp_inet_listen,
@@ -974,8 +982,6 @@ static struct sctp_af sctp_af_inet6 = {
        .copy_addrlist     = sctp_v6_copy_addrlist,
        .from_skb          = sctp_v6_from_skb,
        .from_sk           = sctp_v6_from_sk,
-       .to_sk_saddr       = sctp_v6_to_sk_saddr,
-       .to_sk_daddr       = sctp_v6_to_sk_daddr,
        .from_addr_param   = sctp_v6_from_addr_param,
        .to_addr_param     = sctp_v6_to_addr_param,
        .cmp_addr          = sctp_v6_cmp_addr,
@@ -1005,7 +1011,9 @@ static struct sctp_pf sctp_pf_inet6 = {
        .send_verify   = sctp_inet6_send_verify,
        .supported_addrs = sctp_inet6_supported_addrs,
        .create_accept_sk = sctp_v6_create_accept_sk,
-       .addr_v4map    = sctp_v6_addr_v4map,
+       .addr_to_user  = sctp_v6_addr_to_user,
+       .to_sk_saddr   = sctp_v6_to_sk_saddr,
+       .to_sk_daddr   = sctp_v6_to_sk_daddr,
        .af            = &sctp_af_inet6,
 };