Skip to content
Snippets Groups Projects
Commit b31dc21b authored by Wesley W. Terpstra's avatar Wesley W. Terpstra
Browse files

Implement dual-stack using separate IPv4/6 sockets.

IPv6 should now work under Windows.
parent 0f616d32
Branches
No related merge requests found
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#define EB_DEFAULT_PORT_STR "60368" /* 0xEBD0 */ #define EB_DEFAULT_PORT_STR "60368" /* 0xEBD0 */
void eb_posix_ip_close(eb_posix_sock_t sock) { void eb_posix_ip_close(eb_posix_sock_t sock) {
if (sock == -1) return;
#ifdef __WIN32 #ifdef __WIN32
closesocket(sock); closesocket(sock);
#else #else
...@@ -47,6 +48,7 @@ eb_posix_sock_t eb_posix_ip_open(int family, int type, const char* port) { ...@@ -47,6 +48,7 @@ eb_posix_sock_t eb_posix_ip_open(int family, int type, const char* port) {
struct addrinfo hints, *match, *i; struct addrinfo hints, *match, *i;
eb_posix_sock_t sock; eb_posix_sock_t sock;
int protocol; int protocol;
int optval;
switch (type) { switch (type) {
case SOCK_DGRAM: protocol = IPPROTO_UDP; break; 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) { ...@@ -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) { for (i = match; i; i = i->ai_next) {
sock = socket(i->ai_family, i->ai_socktype, i->ai_protocol); sock = socket(i->ai_family, i->ai_socktype, i->ai_protocol);
if (sock == -1) continue; 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; if (bind(sock, i->ai_addr, i->ai_addrlen) == 0) break;
eb_posix_ip_close(sock); eb_posix_ip_close(sock);
} }
...@@ -75,6 +86,12 @@ eb_posix_sock_t eb_posix_ip_open(int family, int type, const char* port) { ...@@ -75,6 +86,12 @@ eb_posix_sock_t eb_posix_ip_open(int family, int type, const char* port) {
freeaddrinfo(match); freeaddrinfo(match);
if (!i) return -1; 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; return sock;
} }
...@@ -129,6 +146,7 @@ socklen_t eb_posix_ip_resolve(const char* prefix, const char* address, int famil ...@@ -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) { void eb_posix_ip_force_non_blocking(eb_posix_sock_t sock, unsigned long on) {
if (sock == -1) return;
#if defined(__WIN32) #if defined(__WIN32)
ioctlsocket(sock, FIONBIO, &on); ioctlsocket(sock, FIONBIO, &on);
#else #else
......
...@@ -36,20 +36,29 @@ ...@@ -36,20 +36,29 @@
eb_status_t eb_posix_tcp_open(struct eb_transport* transportp, const char* port) { eb_status_t eb_posix_tcp_open(struct eb_transport* transportp, const char* port) {
struct eb_posix_tcp_transport* transport; struct eb_posix_tcp_transport* transport;
eb_posix_sock_t sock; eb_posix_sock_t sock4, sock6;
sock = eb_posix_ip_open(PF_INET6, SOCK_STREAM, port); sock4 = eb_posix_ip_open(PF_INET, SOCK_STREAM, port);
if (sock == -1) return EB_BUSY; #ifdef EB_DISABLE_IPV6
sock6 = -1;
if (listen(sock, 5) != 0) { #else
eb_posix_ip_close(sock); 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; 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 = (struct eb_posix_tcp_transport*)transportp;
transport->port = sock; transport->port4 = sock4;
transport->port6 = sock6;
return EB_OK; return EB_OK;
} }
...@@ -58,7 +67,8 @@ void eb_posix_tcp_close(struct eb_transport* transportp) { ...@@ -58,7 +67,8 @@ void eb_posix_tcp_close(struct eb_transport* transportp) {
struct eb_posix_tcp_transport* transport; struct eb_posix_tcp_transport* transport;
transport = (struct eb_posix_tcp_transport*)transportp; 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) { 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 ...@@ -104,7 +114,8 @@ void eb_posix_tcp_fdes(struct eb_transport* transportp, struct eb_link* linkp, e
(*cb)(data, link->socket); (*cb)(data, link->socket);
} else { } else {
transport = (struct eb_posix_tcp_transport*)transportp; 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_ ...@@ -162,14 +173,22 @@ int eb_posix_tcp_accept(struct eb_transport* transportp, struct eb_link* result_
struct eb_posix_tcp_link* result_link; struct eb_posix_tcp_link* result_link;
eb_posix_sock_t sock; eb_posix_sock_t sock;
sock = -1;
transport = (struct eb_posix_tcp_transport*)transportp; transport = (struct eb_posix_tcp_transport*)transportp;
sock = accept(transport->port, 0, 0); if (sock == -1 && transport->port4 != -1) {
if (sock == -1) { sock = accept(transport->port4, 0, 0);
if (errno != EAGAIN) return -1; if (sock == -1 && errno != EAGAIN) return -1;
return 0;
} }
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) { if (result_linkp != 0) {
result_link = (struct eb_posix_tcp_link*)result_linkp; result_link = (struct eb_posix_tcp_link*)result_linkp;
result_link->socket = sock; result_link->socket = sock;
......
...@@ -46,7 +46,10 @@ EB_PRIVATE int eb_posix_tcp_accept(struct eb_transport*, struct eb_link* result_ ...@@ -46,7 +46,10 @@ EB_PRIVATE int eb_posix_tcp_accept(struct eb_transport*, struct eb_link* result_
struct eb_posix_tcp_transport { struct eb_posix_tcp_transport {
/* Contents must fit in 9 bytes */ /* 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 { struct eb_posix_tcp_link {
......
...@@ -45,21 +45,22 @@ ...@@ -45,21 +45,22 @@
eb_status_t eb_posix_udp_open(struct eb_transport* transportp, const char* port) { eb_status_t eb_posix_udp_open(struct eb_transport* transportp, const char* port) {
struct eb_posix_udp_transport* transport; struct eb_posix_udp_transport* transport;
eb_posix_sock_t sock; eb_posix_sock_t sock4, sock6;
int optval;
sock = eb_posix_ip_open(PF_INET6, SOCK_DGRAM, port); sock4 = eb_posix_ip_open(PF_INET, SOCK_DGRAM, port);
if (sock == -1) return EB_BUSY; #ifdef EB_DISABLE_IPV6
sock6 = -1;
/* Etherbone can broadcast */ #else
optval = 1; sock6 = eb_posix_ip_open(PF_INET6, SOCK_DGRAM, port);
if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*)&optval, sizeof(optval)) != 0) { #endif
eb_posix_ip_close(sock);
return EB_FAIL; /* Failure if we can't get either protocol */
} if (sock4 == -1 && sock6 == -1)
return EB_BUSY;
transport = (struct eb_posix_udp_transport*)transportp; transport = (struct eb_posix_udp_transport*)transportp;
transport->socket = sock; transport->socket4 = sock4;
transport->socket6 = sock6;
return EB_OK; return EB_OK;
} }
...@@ -68,22 +69,34 @@ void eb_posix_udp_close(struct eb_transport* transportp) { ...@@ -68,22 +69,34 @@ void eb_posix_udp_close(struct eb_transport* transportp) {
struct eb_posix_udp_transport* transport; struct eb_posix_udp_transport* transport;
transport = (struct eb_posix_udp_transport*)transportp; 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) { 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 eb_posix_udp_link* link;
struct sockaddr_storage sa; struct sockaddr_storage sa;
socklen_t len; socklen_t len;
len = -1; 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); 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); 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) len = eb_posix_ip_resolve("udp/", address, PF_INET, SOCK_DGRAM, &sa);
if (len == -1) return EB_ADDRESS; if (len == -1) return EB_ADDRESS;
transport = (struct eb_posix_udp_transport*)transportp;
link = (struct eb_posix_udp_link*)linkp; 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 = (struct sockaddr_storage*)malloc(sizeof(struct sockaddr_storage));
link->sa_len = len; link->sa_len = len;
...@@ -104,7 +117,10 @@ void eb_posix_udp_fdes(struct eb_transport* transportp, struct eb_link* linkp, e ...@@ -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; transport = (struct eb_posix_udp_transport*)transportp;
if (linkp == 0) { 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 { } else {
/* no per-link socket */ /* no per-link socket */
} }
...@@ -123,14 +139,24 @@ int eb_posix_udp_poll(struct eb_transport* transportp, struct eb_link* linkp, ui ...@@ -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; transport = (struct eb_posix_udp_transport*)transportp;
/* Set non-blocking */ /* 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); if (transport->socket6 != -1) {
result = recvfrom(transport->socket, (char*)buf, len, MSG_DONTWAIT, (struct sockaddr*)&eb_posix_udp_sa, &eb_posix_udp_sa_len); 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; return 0;
if (result == 0) return -1;
return result;
} }
int eb_posix_udp_recv(struct eb_transport* transportp, struct eb_link* linkp, uint8_t* buf, int len) { 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 ...@@ -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; transport = (struct eb_posix_udp_transport*)transportp;
link = (struct eb_posix_udp_link*)linkp; link = (struct eb_posix_udp_link*)linkp;
/* Set blocking */
eb_posix_ip_non_blocking(transport->socket, 0);
if (link == 0) if (link == 0) {
sendto(transport->socket, (const char*)buf, len, 0, (struct sockaddr*)&eb_posix_udp_sa, eb_posix_udp_sa_len); if (eb_posix_udp_sa.ss_family == PF_INET6) {
else eb_posix_ip_non_blocking(transport->socket6, 0);
sendto(transport->socket, (const char*)buf, len, 0, (struct sockaddr*)link->sa, link->sa_len); 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) { int eb_posix_udp_accept(struct eb_transport* transportp, struct eb_link* result_linkp) {
......
...@@ -46,7 +46,8 @@ EB_PRIVATE int eb_posix_udp_accept(struct eb_transport*, struct eb_link* result_ ...@@ -46,7 +46,8 @@ EB_PRIVATE int eb_posix_udp_accept(struct eb_transport*, struct eb_link* result_
struct eb_posix_udp_transport { struct eb_posix_udp_transport {
/* Contents must fit in 9 bytes */ /* 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 { struct eb_posix_udp_link {
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment