From: Amir Vadai Date: Wed, 5 Jan 2011 09:33:21 +0000 (+0200) Subject: sdp: Fix get getsockname/getpeername in IPv6 X-Git-Tag: v4.1.12-92~264^2~5^2~48 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=65dc6cfdf6d9bfabd8626994d5f3c883c5922c09;p=users%2Fjedix%2Flinux-maple.git sdp: Fix get getsockname/getpeername in IPv6 Signed-off-by: Amir Vadai --- diff --git a/drivers/infiniband/ulp/sdp/sdp.h b/drivers/infiniband/ulp/sdp/sdp.h index 828f3c5802109..d969dc687d7c4 100644 --- a/drivers/infiniband/ulp/sdp/sdp.h +++ b/drivers/infiniband/ulp/sdp/sdp.h @@ -14,10 +14,10 @@ #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); diff --git a/drivers/infiniband/ulp/sdp/sdp_cma.c b/drivers/infiniband/ulp/sdp/sdp_cma.c index d06c01b6f9676..c400ac5ceea5d 100644 --- a/drivers/infiniband/ulp/sdp/sdp_cma.c +++ b/drivers/infiniband/ulp/sdp/sdp_cma.c @@ -44,6 +44,10 @@ #include #include #include +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +#include +#include +#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 diff --git a/drivers/infiniband/ulp/sdp/sdp_main.c b/drivers/infiniband/ulp/sdp/sdp_main.c index 40fe1a93e3986..470fdc9f2853f 100644 --- a/drivers/infiniband/ulp/sdp/sdp_main.c +++ b/drivers/infiniband/ulp/sdp/sdp_main.c @@ -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;