/* 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);
 /* 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);
        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);
                /* 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);
        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?  */
        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? */
                                return 0;
                        }
                        rcu_read_unlock();
-               } else if (type == IPV6_ADDR_MAPPED) {
-                       if (!opt->v4mapped)
-                               return 0;
                }
 
                af = opt->pf->af;
        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,
        .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,
        .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,
        .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,
 };
 
 
        if (id_asoc && (id_asoc != addr_asoc))
                return NULL;
 
-       sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk),
+       sctp_get_pf_specific(sk->sk_family)->addr_to_user(sctp_sk(sk),
                                                (union sctp_addr *)addr);
 
        return transport;
        /* Copy back into socket for getsockname() use. */
        if (!ret) {
                inet_sk(sk)->inet_sport = htons(inet_sk(sk)->inet_num);
-               af->to_sk_saddr(addr, sk);
+               sp->pf->to_sk_saddr(addr, sk);
        }
 
        return ret;
        struct sctp_association *asoc2;
        struct sctp_transport *transport;
        union sctp_addr to;
-       struct sctp_af *af;
        sctp_scope_t scope;
        long timeo;
        int err = 0;
        /* Walk through the addrs buffer and count the number of addresses. */
        addr_buf = kaddrs;
        while (walk_size < addrs_size) {
+               struct sctp_af *af;
+
                if (walk_size + sizeof(sa_family_t) > addrs_size) {
                        err = -EINVAL;
                        goto out_free;
 
        /* Initialize sk's dport and daddr for getpeername() */
        inet_sk(sk)->inet_dport = htons(asoc->peer.port);
-       af = sctp_get_af_specific(sa_addr->sa.sa_family);
-       af->to_sk_daddr(sa_addr, sk);
+       sp->pf->to_sk_daddr(sa_addr, sk);
        sk->sk_err = 0;
 
        /* in-kernel sockets don't generally have a file allocated to them
        memcpy(&status.sstat_primary.spinfo_address, &transport->ipaddr,
                        transport->af_specific->sockaddr_len);
        /* Map ipv4 address into v4-mapped-on-v6 address.  */
-       sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk),
+       sctp_get_pf_specific(sk->sk_family)->addr_to_user(sctp_sk(sk),
                (union sctp_addr *)&status.sstat_primary.spinfo_address);
        status.sstat_primary.spinfo_state = transport->state;
        status.sstat_primary.spinfo_cwnd = transport->cwnd;
 int sctp_do_peeloff(struct sock *sk, sctp_assoc_t id, struct socket **sockp)
 {
        struct sctp_association *asoc = sctp_id2assoc(sk, id);
+       struct sctp_sock *sp = sctp_sk(sk);
        struct socket *sock;
-       struct sctp_af *af;
        int err = 0;
 
        if (!asoc)
        /* Make peeled-off sockets more like 1-1 accepted sockets.
         * Set the daddr and initialize id to something more random
         */
-       af = sctp_get_af_specific(asoc->peer.primary_addr.sa.sa_family);
-       af->to_sk_daddr(&asoc->peer.primary_addr, sk);
+       sp->pf->to_sk_daddr(&asoc->peer.primary_addr, sk);
 
        /* Populate the fields of the newsk from the oldsk and migrate the
         * asoc to the newsk.
        list_for_each_entry(from, &asoc->peer.transport_addr_list,
                                transports) {
                memcpy(&temp, &from->ipaddr, sizeof(temp));
-               sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp);
-               addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
+               addrlen = sctp_get_pf_specific(sk->sk_family)
+                             ->addr_to_user(sp, &temp);
                if (space_left < addrlen)
                        return -ENOMEM;
                if (copy_to_user(to, &temp, addrlen))
                if (!temp.v4.sin_port)
                        temp.v4.sin_port = htons(port);
 
-               sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk),
-                                                               &temp);
-               addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
+               addrlen = sctp_get_pf_specific(sk->sk_family)
+                             ->addr_to_user(sctp_sk(sk), &temp);
+
                if (space_left < addrlen) {
                        cnt =  -ENOMEM;
                        break;
         */
        list_for_each_entry(addr, &bp->address_list, list) {
                memcpy(&temp, &addr->a, sizeof(temp));
-               sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp);
-               addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
+               addrlen = sctp_get_pf_specific(sk->sk_family)
+                             ->addr_to_user(sp, &temp);
                if (space_left < addrlen) {
                        err =  -ENOMEM; /*fixme: right error?*/
                        goto out;
        memcpy(&prim.ssp_addr, &asoc->peer.primary_path->ipaddr,
                asoc->peer.primary_path->af_specific->sockaddr_len);
 
-       sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp,
+       sctp_get_pf_specific(sk->sk_family)->addr_to_user(sp,
                        (union sctp_addr *)&prim.ssp_addr);
 
        if (put_user(len, optlen))