]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
sdp: Fix get getsockname/getpeername in IPv6
authorAmir Vadai <amirv@mellanox.co.il>
Wed, 5 Jan 2011 09:33:21 +0000 (11:33 +0200)
committerMukesh Kacker <mukesh.kacker@oracle.com>
Tue, 6 Oct 2015 12:05:39 +0000 (05:05 -0700)
Signed-off-by: Amir Vadai <amirv@mellanox.co.il>
drivers/infiniband/ulp/sdp/sdp.h
drivers/infiniband/ulp/sdp/sdp_cma.c
drivers/infiniband/ulp/sdp/sdp_main.c

index 828f3c58021094aef81f08dc9455649574620077..d969dc687d7c409d0bec26da92592549c93f3d6c 100644 (file)
 
 #ifndef NIPQUAD
 #define NIPQUAD(addr) \
-        ((unsigned char *)&addr)[0], \
-        ((unsigned char *)&addr)[1], \
-        ((unsigned char *)&addr)[2], \
-        ((unsigned char *)&addr)[3]
+        ((unsigned char *)&(addr))[0], \
+        ((unsigned char *)&(addr))[1], \
+        ((unsigned char *)&(addr))[2], \
+        ((unsigned char *)&(addr))[3]
 #endif
 
 #ifndef NIPQUAD_FMT
@@ -214,6 +214,9 @@ union cma_ip_addr {
        } ip4;
 } __attribute__((__packed__));
 
+#define HH_IPV_MASK 0xf0
+#define HH_IPV4     0x40
+#define HH_IPV6     0x60
 /* TODO: too much? Can I avoid having the src/dst and port here? */
 struct sdp_hh {
        struct sdp_bsdh bsdh;
@@ -930,6 +933,15 @@ static inline int somebody_is_waiting(struct sock *sk)
                test_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags);
 }
 
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+static inline struct ipv6_pinfo *sdp_inet6_sk_generic(struct sock *sk)
+{
+       const int offset = sk->sk_prot->obj_size - sizeof(struct ipv6_pinfo);
+
+       return (struct ipv6_pinfo *)(((u8 *)sk) + offset);
+}
+#endif
+
 /* sdp_main.c */
 void sdp_set_default_moderation(struct sdp_sock *ssk);
 int sdp_init_sock(struct sock *sk);
index d06c01b6f9676ac52f4066f8ebf27b340491a07f..c400ac5ceea5da2f6e6e140788740a4f6a606ca2 100644 (file)
 #include <rdma/rdma_cm.h>
 #include <net/tcp_states.h>
 #include <rdma/sdp_socket.h>
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+#include <net/ipv6.h>
+#include <net/transp_v6.h>
+#endif
 #include "sdp.h"
 
 #define SDP_MAJV_MINV 0x22
@@ -151,13 +155,20 @@ static int sdp_connect_handler(struct sock *sk, struct rdma_cm_id *id,
        struct sockaddr_in *dst_addr;
        struct sock *child;
        const struct sdp_hh *h;
-       int rc;
+       struct inet_sock *newinet;
+       int rc = 0;
 
        sdp_dbg(sk, "%s %p -> %p\n", __func__, sdp_sk(sk)->id, id);
 
        h = event->param.conn.private_data;
        SDP_DUMP_PACKET(sk, "RX", NULL, &h->bsdh);
 
+       if (h->ipv_cap & HH_IPV_MASK & ~(HH_IPV4 | HH_IPV6)) {
+               sdp_warn(sk, "Bad IPV field in SDP Hello header: 0x%x\n",
+                               h->ipv_cap & HH_IPV_MASK);
+               return -EINVAL;
+       }
+
        if (!h->max_adverts)
                return -EINVAL;
 
@@ -167,9 +178,46 @@ static int sdp_connect_handler(struct sock *sk, struct rdma_cm_id *id,
 
        sdp_init_sock(child);
 
+       newinet = inet_sk(child);
        dst_addr = (struct sockaddr_in *)&id->route.addr.dst_addr;
-       inet_sk(child)->dport = dst_addr->sin_port;
-       inet_sk(child)->daddr = dst_addr->sin_addr.s_addr;
+       newinet->dport = dst_addr->sin_port;
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+       if (inet6_sk(sk)) {
+               struct ipv6_pinfo *newnp;
+              
+               newnp = newinet->pinet6 = sdp_inet6_sk_generic(child);
+
+               memcpy(newnp, inet6_sk(sk), sizeof(struct ipv6_pinfo));
+
+               if ((h->ipv_cap & HH_IPV_MASK) == HH_IPV4) {
+                       /* V6 mapped */
+                       newinet->daddr = dst_addr->sin_addr.s_addr;
+                       ipv6_addr_set(&newnp->daddr, 0, 0, htonl(0x0000FFFF),
+                                       h->src_addr.ip4.addr);
+
+                       ipv6_addr_set(&newnp->saddr, 0, 0, htonl(0x0000FFFF),
+                                       h->dst_addr.ip4.addr);
+
+                       ipv6_addr_copy(&newnp->rcv_saddr, &newnp->saddr);
+               } else if ((h->ipv_cap & HH_IPV_MASK) == HH_IPV6) {
+                       struct sockaddr_in6 *dst_addr6 = (struct sockaddr_in6 *)dst_addr;
+                       struct sockaddr_in6 *src_addr6 = 
+                               (struct sockaddr_in6 *)&id->route.addr.src_addr;
+
+                       ipv6_addr_copy(&newnp->daddr, &dst_addr6->sin6_addr);
+                       ipv6_addr_copy(&newnp->saddr, &src_addr6->sin6_addr);
+                       ipv6_addr_copy(&newnp->rcv_saddr, &src_addr6->sin6_addr);
+               } else {
+                       sdp_warn(child, "Bad IPV field: 0x%x\n", h->ipv_cap & HH_IPV_MASK);
+               }
+
+               newinet->daddr = newinet->saddr = newinet->rcv_saddr = LOOPBACK4_IPV6;
+       } else 
+#endif
+       {
+               newinet->daddr = dst_addr->sin_addr.s_addr;
+       }
 
 #ifdef SDP_SOCK_HISTORY
        sdp_ssk_hist_rename(sk);
@@ -382,11 +430,6 @@ int sdp_cma_handler(struct rdma_cm_id *id, struct rdma_cm_event *event)
                hh.bsdh.len = htonl(sizeof(struct sdp_hh));
                hh.max_adverts = 1;
        
-               hh.ipv_cap = 
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-                       inet6_sk(sk) ? 0x60 : 
-#endif
-                       0x40;
                hh.majv_minv = SDP_MAJV_MINV;
                sdp_init_buffers(sdp_sk(sk), rcvbuf_initial_size);
                hh.bsdh.bufs = htons(rx_ring_posted(sdp_sk(sk)));
@@ -396,8 +439,18 @@ int sdp_cma_handler(struct rdma_cm_id *id, struct rdma_cm_event *event)
                                PAGE_SIZE + sizeof(struct sdp_bsdh));
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
                if (inet6_sk(sk)) {
-                       inet6_sk(sk)->saddr = inet6_sk(sk)->rcv_saddr =
-                               ((struct sockaddr_in6 *)&id->route.addr.src_addr)->sin6_addr;
+                       struct sockaddr *src_addr = (struct sockaddr *)&id->route.addr.src_addr;
+                       struct sockaddr_in *addr4 = (struct sockaddr_in *)src_addr;
+                       struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)src_addr;
+
+                       if (src_addr->sa_family == AF_INET) {
+                               /* IPv4 over IPv6 */
+                               ipv6_addr_set(&inet6_sk(sk)->rcv_saddr, 0, 0, htonl(0xFFFF),
+                                               addr4->sin_addr.s_addr);
+                       } else {
+                               inet6_sk(sk)->rcv_saddr = inet6_sk(sk)->rcv_saddr = addr6->sin6_addr;
+                       }
+                       inet6_sk(sk)->saddr = inet6_sk(sk)->rcv_saddr;
                }
                        else 
 #endif
index 40fe1a93e3986105296f1c2e498a1e58821e860a..470fdc9f2853fc9f6babf76c8d57c8fb740dfcf9 100644 (file)
@@ -179,9 +179,6 @@ static int sdp_get_port(struct sock *sk, unsigned short snum)
                        ipv6_addr_copy(&addr6->sin6_addr, &inet6_sk(sk)->rcv_saddr);
                        addr_len = sizeof(*addr6);
                }
-
-               sdp_dbg(sk, "%s: " NIP6_FMT ":%u\n", __func__,
-                               NIP6(inet6_sk(sk)->rcv_saddr), snum);
        }
                else 
 #endif
@@ -794,27 +791,54 @@ out:
 static int sdp_ipv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
 {
        struct sdp_sock *ssk = sdp_sk(sk);
+       struct sockaddr_in6 *usin = (struct sockaddr_in6 *)uaddr;
        struct sockaddr_in6 src_addr = {
                .sin6_family = AF_INET6,
                .sin6_port = htons(inet_sk(sk)->sport),
        };
        int rc;
+       int addr_type;
 
        if (addr_len < SIN6_LEN_RFC2133) 
                return -EINVAL;
 
-       if (!inet6_sk(sk))
-               return -EAFNOSUPPORT;
+       if (uaddr->sa_family == AF_INET6_SDP)
+               uaddr->sa_family = AF_INET6;
+
+       /*
+        *      connect() to INADDR_ANY means loopback (BSD'ism).
+        */
+       if(ipv6_addr_any(&usin->sin6_addr))
+               usin->sin6_addr.s6_addr[15] = 0x1; 
+
+       addr_type = ipv6_addr_type(&usin->sin6_addr);
+
+       if(addr_type & IPV6_ADDR_MULTICAST)
+               return -ENETUNREACH;
 
        src_addr.sin6_addr = inet6_sk(sk)->saddr;
 
        if (!ssk->id) {
+               /* If IPv4 over IPv6, make sure rdma_bind will expect ipv4 address */
+               if (addr_type == IPV6_ADDR_MAPPED)
+                       ipv6_addr_set(&inet6_sk(sk)->rcv_saddr, 0, 0, htonl(0x0000FFFF), 0);
+
                rc = sdp_get_port(sk, 0);
                if (rc)
                        return rc;
                inet_sk(sk)->sport = htons(inet_sk(sk)->num);
        }
 
+       ipv6_addr_copy(&inet6_sk(sk)->daddr, &usin->sin6_addr);
+
+       if (addr_type == IPV6_ADDR_MAPPED) {
+               struct sockaddr_in *addr4 = (struct sockaddr_in *)uaddr;
+               struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)uaddr;
+
+               addr4->sin_addr.s_addr = addr6->sin6_addr.s6_addr32[3];
+               addr4->sin_family = AF_INET;
+       }
+
        rc = rdma_resolve_addr(ssk->id, (struct sockaddr *)&src_addr,
                               uaddr, SDP_RESOLVE_TIMEOUT);
        if (rc) {
@@ -845,11 +869,10 @@ static int sdp_ipv4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_le
         if (addr_len < sizeof(struct sockaddr_in))
                 return -EINVAL;
 
-       if (!ssk->id) {
-               /* If IPv4 over IPv6, make sure rdma_bind will expect ipv4 address */
-               if (inet6_sk(sk))
-                       inet6_sk(sk)->rcv_saddr.s6_addr32[2] = htonl(0x0000ffff);
+       if (uaddr->sa_family == AF_INET_SDP)
+               uaddr->sa_family = AF_INET;
 
+       if (!ssk->id) {
                rc = sdp_get_port(sk, 0);
                if (rc)
                        return rc;
@@ -863,7 +886,7 @@ static int sdp_ipv4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_le
                return rc;
        }
 
-       sdp_dbg(sk, "%s NIPQUAD_FMT:%hu -> NIPQUAD_FMT:%hu\n", __func__,
+       sdp_dbg(sk, "%s " NIPQUAD_FMT ":%hu -> " NIPQUAD_FMT ":%hu\n", __func__,
                NIPQUAD(src_addr.sin_addr.s_addr),
                ntohs(src_addr.sin_port),
                NIPQUAD(((struct sockaddr_in *)uaddr)->sin_addr.s_addr),
@@ -888,41 +911,17 @@ static int sdp_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
                return -sk->sk_err;
        }
 
-       /* Treat AF_INET_SDP as if it is AF_INET */
-       if (uaddr->sa_family == AF_INET_SDP)
-               uaddr->sa_family = AF_INET;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       else if (uaddr->sa_family == AF_INET6_SDP)
-               uaddr->sa_family = AF_INET6;
-
-       if (uaddr->sa_family == AF_INET6) {
-               struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)uaddr;
-               int addr_type = ipv6_addr_type(&(addr6->sin6_addr));
-
-               if (addr_type == IPV6_ADDR_MAPPED) {
-                       struct sockaddr_in *addr4 = (struct sockaddr_in *)uaddr;
-
-                       addr4->sin_addr.s_addr = addr6->sin6_addr.s6_addr32[3];
-                       addr4->sin_family = AF_INET;
-                       addr_len = sizeof(*addr4);
-               }
-       }
-#endif
-
-       if (uaddr->sa_family == AF_INET)
-               rc = sdp_ipv4_connect(sk, uaddr, addr_len);
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-       else if (uaddr->sa_family == AF_INET6)
+       if (inet6_sk(sk))
                rc = sdp_ipv6_connect(sk, uaddr, addr_len);
-#endif
        else
-               rc = -EAFNOSUPPORT;
+#endif
+               rc = sdp_ipv4_connect(sk, uaddr, addr_len);
 
-       if (rc)
-               return rc;
+       if (!rc)
+               sdp_exch_state(sk, TCPF_CLOSE, TCP_SYN_SENT);
 
-       sdp_exch_state(sk, TCPF_CLOSE, TCP_SYN_SENT);
-       return 0;
+       return rc;
 }
 
 static int sdp_disconnect(struct sock *sk, int flags)
@@ -2699,43 +2698,6 @@ recv_urg:
        goto out;
 }
 
-static int sdp_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
-{
-       struct sock *sk = sock->sk;
-       int rc = -EAFNOSUPPORT; 
-
-       switch (uaddr->sa_family) {
-               case AF_INET_SDP:
-                       uaddr->sa_family = AF_INET;
-                       break;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-               case AF_INET6_SDP:
-                       uaddr->sa_family = AF_INET6;
-                       break;
-#endif
-       }
-
-       switch (uaddr->sa_family) {
-               case AF_INET:
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-                       if (inet6_sk(sk))
-                               rc = -EINVAL;
-                       else
-#endif
-                               rc = inet_bind(sock, uaddr, addr_len);
-                       break;
-
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-               case AF_INET6:
-                       if (inet6_sk(sk))
-                               rc = inet6_bind(sock, uaddr, addr_len);
-                       break;
-#endif
-       }
-
-       return rc;
-}
-
 static int sdp_listen(struct sock *sk, int backlog)
 {
        struct sdp_sock *ssk = sdp_sk(sk);
@@ -2902,7 +2864,7 @@ static struct proto_ops sdp_ipv4_proto_ops = {
        .family     = PF_INET,
        .owner      = THIS_MODULE,
        .release    = inet_release,
-       .bind       = sdp_bind,
+       .bind       = inet_bind,
        .connect    = inet_stream_connect, /* TODO: inet_datagram connect would
                                              autobind, but need to fix get_port
                                              with port 0 first. */
@@ -2926,7 +2888,7 @@ static struct proto_ops sdp_ipv6_proto_ops = {
        .family     = PF_INET6,
        .owner      = THIS_MODULE,
        .release    = inet6_release,
-       .bind       = sdp_bind,
+       .bind       = inet6_bind,
        .connect    = inet_stream_connect, /* TODO: inet_datagram connect would
                                              autobind, but need to fix get_port
                                              with port 0 first. */
@@ -2946,15 +2908,6 @@ static struct proto_ops sdp_ipv6_proto_ops = {
 };
 #endif
 
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-static inline struct ipv6_pinfo *inet6_sk_generic(struct sock *sk)
-{
-       const int offset = sk->sk_prot->obj_size - sizeof(struct ipv6_pinfo);
-
-       return (struct ipv6_pinfo *)(((u8 *)sk) + offset);
-}
-#endif
-
 static int sdp_create_ipvx_socket(struct net *net, struct socket *sock, int protocol,
                struct proto_ops *proto_ops)
 {
@@ -3001,7 +2954,7 @@ static int sdp_create_ipvx_socket(struct net *net, struct socket *sock, int prot
 
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
        if (proto_ops->family == PF_INET6)
-               inet_sk(sock->sk)->pinet6 = inet6_sk_generic(sock->sk);
+               inet_sk(sock->sk)->pinet6 = sdp_inet6_sk_generic(sock->sk);
 #endif
 
        sock->state = SS_UNCONNECTED;