#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
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;
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);
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)));
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
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
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) {
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;
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),
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)
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);
.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. */
.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. */
};
#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)
{
#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;