From b31dc21bbdae0ddeb096bf53d79af804cca4a4ea Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" <w.terpstra@gsi.de> Date: Thu, 8 Mar 2012 16:31:06 +0000 Subject: [PATCH] Implement dual-stack using separate IPv4/6 sockets. IPv6 should now work under Windows. --- api/transport/posix-ip.c | 18 ++++++++ api/transport/posix-tcp.c | 49 ++++++++++++++------- api/transport/posix-tcp.h | 5 ++- api/transport/posix-udp.c | 91 +++++++++++++++++++++++++++------------ api/transport/posix-udp.h | 3 +- 5 files changed, 122 insertions(+), 44 deletions(-) diff --git a/api/transport/posix-ip.c b/api/transport/posix-ip.c index 89a3e18..1593a15 100644 --- a/api/transport/posix-ip.c +++ b/api/transport/posix-ip.c @@ -36,6 +36,7 @@ #define EB_DEFAULT_PORT_STR "60368" /* 0xEBD0 */ void eb_posix_ip_close(eb_posix_sock_t sock) { + if (sock == -1) return; #ifdef __WIN32 closesocket(sock); #else @@ -47,6 +48,7 @@ eb_posix_sock_t eb_posix_ip_open(int family, int type, const char* port) { struct addrinfo hints, *match, *i; eb_posix_sock_t sock; int protocol; + int optval; switch (type) { case SOCK_DGRAM: protocol = IPPROTO_UDP; break; @@ -68,6 +70,15 @@ eb_posix_sock_t eb_posix_ip_open(int family, int type, const char* port) { for (i = match; i; i = i->ai_next) { sock = socket(i->ai_family, i->ai_socktype, i->ai_protocol); if (sock == -1) continue; + +#ifndef EB_DISABLE_IPV6 + /* We bind IPv6 sockets to only IPv6... for compatability with Windows */ + if (i->ai_family == PF_INET6) { + optval = 1; + setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&optval, sizeof(optval)); + } +#endif + if (bind(sock, i->ai_addr, i->ai_addrlen) == 0) break; eb_posix_ip_close(sock); } @@ -75,6 +86,12 @@ eb_posix_sock_t eb_posix_ip_open(int family, int type, const char* port) { freeaddrinfo(match); if (!i) return -1; + /* Etherbone can broadcast over UDP */ + if (sock != -1 && protocol == IPPROTO_UDP) { + optval = 1; + setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*)&optval, sizeof(optval)); + } + return sock; } @@ -129,6 +146,7 @@ socklen_t eb_posix_ip_resolve(const char* prefix, const char* address, int famil } void eb_posix_ip_force_non_blocking(eb_posix_sock_t sock, unsigned long on) { + if (sock == -1) return; #if defined(__WIN32) ioctlsocket(sock, FIONBIO, &on); #else diff --git a/api/transport/posix-tcp.c b/api/transport/posix-tcp.c index 4043c94..075e22f 100644 --- a/api/transport/posix-tcp.c +++ b/api/transport/posix-tcp.c @@ -36,20 +36,29 @@ eb_status_t eb_posix_tcp_open(struct eb_transport* transportp, const char* port) { struct eb_posix_tcp_transport* transport; - eb_posix_sock_t sock; - - sock = eb_posix_ip_open(PF_INET6, SOCK_STREAM, port); - if (sock == -1) return EB_BUSY; - - if (listen(sock, 5) != 0) { - eb_posix_ip_close(sock); + eb_posix_sock_t sock4, sock6; + + sock4 = eb_posix_ip_open(PF_INET, SOCK_STREAM, port); +#ifdef EB_DISABLE_IPV6 + sock6 = -1; +#else + sock6 = eb_posix_ip_open(PF_INET6, SOCK_STREAM, port); +#endif + if (sock4 == -1 && sock6 == -1) return EB_BUSY; + + if ((sock4 != -1 && listen(sock4, 5) != 0) || + (sock6 != -1 && listen(sock6, 5) != 0)) { + eb_posix_ip_close(sock4); + eb_posix_ip_close(sock6); return EB_ADDRESS; } - eb_posix_ip_force_non_blocking(sock, 1); + eb_posix_ip_force_non_blocking(sock4, 1); + eb_posix_ip_force_non_blocking(sock6, 1); transport = (struct eb_posix_tcp_transport*)transportp; - transport->port = sock; + transport->port4 = sock4; + transport->port6 = sock6; return EB_OK; } @@ -58,7 +67,8 @@ void eb_posix_tcp_close(struct eb_transport* transportp) { struct eb_posix_tcp_transport* transport; transport = (struct eb_posix_tcp_transport*)transportp; - eb_posix_ip_close(transport->port); + eb_posix_ip_close(transport->port4); + eb_posix_ip_close(transport->port6); } eb_status_t eb_posix_tcp_connect(struct eb_transport* transportp, struct eb_link* linkp, const char* address) { @@ -104,7 +114,8 @@ void eb_posix_tcp_fdes(struct eb_transport* transportp, struct eb_link* linkp, e (*cb)(data, link->socket); } else { transport = (struct eb_posix_tcp_transport*)transportp; - (*cb)(data, transport->port); + if (transport->port4 != -1) (*cb)(data, transport->port4); + if (transport->port6 != -1) (*cb)(data, transport->port6); } } @@ -162,14 +173,22 @@ int eb_posix_tcp_accept(struct eb_transport* transportp, struct eb_link* result_ struct eb_posix_tcp_link* result_link; eb_posix_sock_t sock; + sock = -1; transport = (struct eb_posix_tcp_transport*)transportp; - sock = accept(transport->port, 0, 0); - if (sock == -1) { - if (errno != EAGAIN) return -1; - return 0; + if (sock == -1 && transport->port4 != -1) { + sock = accept(transport->port4, 0, 0); + if (sock == -1 && errno != EAGAIN) return -1; } + if (sock == -1 && transport->port6 != -1) { + sock = accept(transport->port6, 0, 0); + if (sock == -1 && errno != EAGAIN) return -1; + } + + if (sock == -1) + return 0; + if (result_linkp != 0) { result_link = (struct eb_posix_tcp_link*)result_linkp; result_link->socket = sock; diff --git a/api/transport/posix-tcp.h b/api/transport/posix-tcp.h index 30e84db..a0b6e39 100644 --- a/api/transport/posix-tcp.h +++ b/api/transport/posix-tcp.h @@ -46,7 +46,10 @@ EB_PRIVATE int eb_posix_tcp_accept(struct eb_transport*, struct eb_link* result_ struct eb_posix_tcp_transport { /* Contents must fit in 9 bytes */ - eb_posix_sock_t port; + eb_posix_sock_t port4; /* IPv4 */ +#ifndef EB_DISABLE_IPV6 + eb_posix_sock_t port6; /* IPv6 */ +#endif }; struct eb_posix_tcp_link { diff --git a/api/transport/posix-udp.c b/api/transport/posix-udp.c index 5fa7a37..e0de162 100644 --- a/api/transport/posix-udp.c +++ b/api/transport/posix-udp.c @@ -45,21 +45,22 @@ eb_status_t eb_posix_udp_open(struct eb_transport* transportp, const char* port) { struct eb_posix_udp_transport* transport; - eb_posix_sock_t sock; - int optval; + eb_posix_sock_t sock4, sock6; - sock = eb_posix_ip_open(PF_INET6, SOCK_DGRAM, port); - if (sock == -1) return EB_BUSY; - - /* Etherbone can broadcast */ - optval = 1; - if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*)&optval, sizeof(optval)) != 0) { - eb_posix_ip_close(sock); - return EB_FAIL; - } + sock4 = eb_posix_ip_open(PF_INET, SOCK_DGRAM, port); +#ifdef EB_DISABLE_IPV6 + sock6 = -1; +#else + sock6 = eb_posix_ip_open(PF_INET6, SOCK_DGRAM, port); +#endif + + /* Failure if we can't get either protocol */ + if (sock4 == -1 && sock6 == -1) + return EB_BUSY; transport = (struct eb_posix_udp_transport*)transportp; - transport->socket = sock; + transport->socket4 = sock4; + transport->socket6 = sock6; return EB_OK; } @@ -68,22 +69,34 @@ void eb_posix_udp_close(struct eb_transport* transportp) { struct eb_posix_udp_transport* transport; transport = (struct eb_posix_udp_transport*)transportp; - eb_posix_ip_close(transport->socket); + eb_posix_ip_close(transport->socket4); + eb_posix_ip_close(transport->socket6); } eb_status_t eb_posix_udp_connect(struct eb_transport* transportp, struct eb_link* linkp, const char* address) { + struct eb_posix_udp_transport* transport; struct eb_posix_udp_link* link; struct sockaddr_storage sa; socklen_t len; len = -1; - if (len == -1) len = eb_posix_ip_resolve("udp6/", address, PF_INET6, SOCK_DGRAM, &sa); if (len == -1) len = eb_posix_ip_resolve("udp4/", address, PF_INET, SOCK_DGRAM, &sa); +#ifndef EB_DISABLE_IPV6 + if (len == -1) len = eb_posix_ip_resolve("udp6/", address, PF_INET6, SOCK_DGRAM, &sa); if (len == -1) len = eb_posix_ip_resolve("udp/", address, PF_INET6, SOCK_DGRAM, &sa); +#endif if (len == -1) len = eb_posix_ip_resolve("udp/", address, PF_INET, SOCK_DGRAM, &sa); if (len == -1) return EB_ADDRESS; + transport = (struct eb_posix_udp_transport*)transportp; link = (struct eb_posix_udp_link*)linkp; + + /* Do we have support for the socket? */ + if (sa.ss_family == PF_INET && transport->socket4 == -1) return EB_FAIL; +#ifndef EB_DISABLE_IPV6 + if (sa.ss_family == PF_INET6 && transport->socket6 == -1) return EB_FAIL; +#endif + link->sa = (struct sockaddr_storage*)malloc(sizeof(struct sockaddr_storage)); link->sa_len = len; @@ -104,7 +117,10 @@ void eb_posix_udp_fdes(struct eb_transport* transportp, struct eb_link* linkp, e transport = (struct eb_posix_udp_transport*)transportp; if (linkp == 0) { - (*cb)(data, transport->socket); + if (transport->socket4 != -1) (*cb)(data, transport->socket4); +#ifndef EB_DISABLE_IPV6 + if (transport->socket6 != -1) (*cb)(data, transport->socket6); +#endif } else { /* no per-link socket */ } @@ -123,14 +139,24 @@ int eb_posix_udp_poll(struct eb_transport* transportp, struct eb_link* linkp, ui transport = (struct eb_posix_udp_transport*)transportp; /* Set non-blocking */ - eb_posix_ip_non_blocking(transport->socket, 1); + eb_posix_ip_non_blocking(transport->socket4, 1); + eb_posix_ip_non_blocking(transport->socket6, 1); + + if (transport->socket4 != -1) { + eb_posix_udp_sa_len = sizeof(eb_posix_udp_sa); + result = recvfrom(transport->socket4, (char*)buf, len, MSG_DONTWAIT, (struct sockaddr*)&eb_posix_udp_sa, &eb_posix_udp_sa_len); + if (result == -1 && errno != EAGAIN) return -1; + if (result != -1) return result; + } - eb_posix_udp_sa_len = sizeof(eb_posix_udp_sa); - result = recvfrom(transport->socket, (char*)buf, len, MSG_DONTWAIT, (struct sockaddr*)&eb_posix_udp_sa, &eb_posix_udp_sa_len); + if (transport->socket6 != -1) { + eb_posix_udp_sa_len = sizeof(eb_posix_udp_sa); + result = recvfrom(transport->socket6, (char*)buf, len, MSG_DONTWAIT, (struct sockaddr*)&eb_posix_udp_sa, &eb_posix_udp_sa_len); + if (result == -1 && errno != EAGAIN) return -1; + if (result != -1) return result; + } - if (result == -1 && errno == EAGAIN) return 0; - if (result == 0) return -1; - return result; + return 0; } int eb_posix_udp_recv(struct eb_transport* transportp, struct eb_link* linkp, uint8_t* buf, int len) { @@ -152,13 +178,24 @@ void eb_posix_udp_send(struct eb_transport* transportp, struct eb_link* linkp, c transport = (struct eb_posix_udp_transport*)transportp; link = (struct eb_posix_udp_link*)linkp; - /* Set blocking */ - eb_posix_ip_non_blocking(transport->socket, 0); - if (link == 0) - sendto(transport->socket, (const char*)buf, len, 0, (struct sockaddr*)&eb_posix_udp_sa, eb_posix_udp_sa_len); - else - sendto(transport->socket, (const char*)buf, len, 0, (struct sockaddr*)link->sa, link->sa_len); + if (link == 0) { + if (eb_posix_udp_sa.ss_family == PF_INET6) { + eb_posix_ip_non_blocking(transport->socket6, 0); + sendto(transport->socket6, (const char*)buf, len, 0, (struct sockaddr*)&eb_posix_udp_sa, eb_posix_udp_sa_len); + } else { + eb_posix_ip_non_blocking(transport->socket4, 0); + sendto(transport->socket4, (const char*)buf, len, 0, (struct sockaddr*)&eb_posix_udp_sa, eb_posix_udp_sa_len); + } + } else { + if (link->sa->ss_family == PF_INET6) { + eb_posix_ip_non_blocking(transport->socket6, 0); + sendto(transport->socket6, (const char*)buf, len, 0, (struct sockaddr*)link->sa, link->sa_len); + } else { + eb_posix_ip_non_blocking(transport->socket4, 0); + sendto(transport->socket4, (const char*)buf, len, 0, (struct sockaddr*)link->sa, link->sa_len); + } + } } int eb_posix_udp_accept(struct eb_transport* transportp, struct eb_link* result_linkp) { diff --git a/api/transport/posix-udp.h b/api/transport/posix-udp.h index 13587e6..98184c1 100644 --- a/api/transport/posix-udp.h +++ b/api/transport/posix-udp.h @@ -46,7 +46,8 @@ EB_PRIVATE int eb_posix_udp_accept(struct eb_transport*, struct eb_link* result_ struct eb_posix_udp_transport { /* Contents must fit in 9 bytes */ - eb_posix_sock_t socket; + eb_posix_sock_t socket4; /* IPv4 */ + eb_posix_sock_t socket6; /* IPv6 */ }; struct eb_posix_udp_link { -- GitLab