summaryrefslogtreecommitdiff
path: root/src/common/net/unix.h
diff options
context:
space:
mode:
authorAndrey Nazarov <skuller@skuller.net>2014-11-05 01:20:39 +0300
committerAndrey Nazarov <skuller@skuller.net>2014-12-01 23:04:15 +0300
commit3798d858a5f64c17dd31bf34b28b3cc3f868e4b9 (patch)
tree3914e99166adda02e1624a071b8d2dfc21801aeb /src/common/net/unix.h
parent6ed835d943063ea4a81071e7b7d8dae50ff700ba (diff)
Add experimental IPv6 support.
Enabled by default when resolving addresses, but IPv4 is prioritized over IPv6. Listening for incoming IPv6 connections disabled by default. No IPv6 multicast support yet, overcomplicated for no good reason.
Diffstat (limited to 'src/common/net/unix.h')
-rw-r--r--src/common/net/unix.h120
1 files changed, 40 insertions, 80 deletions
diff --git a/src/common/net/unix.h b/src/common/net/unix.h
index 920ca87..a5ad510 100644
--- a/src/common/net/unix.h
+++ b/src/common/net/unix.h
@@ -25,36 +25,12 @@ static const char *os_error_string(int err)
return strerror(err);
}
-// Receiving ICMP errors on unconnected UDP sockets is tricky...
-// Linux 2.2 and higher supports this via IP_RECVERR option, see ip(7).
-// What about BSD?
-
-#if USE_ICMP && (defined __linux__)
-
-static qboolean check_offender(const struct sockaddr_in *from,
- const struct sockaddr_in *to)
-{
- if (!to)
- return qfalse;
-
- if (from->sin_addr.s_addr != to->sin_addr.s_addr)
- return qfalse;
-
- if (from->sin_port && from->sin_port != to->sin_port)
- return qfalse;
-
- Com_DPrintf("%s: found offending address %s:%d\n", "process_error_queue",
- inet_ntoa(from->sin_addr), ntohs(from->sin_port));
-
- return qtrue;
-}
-
-// Returns true if failed socket operation should be retried.
-// May get called from NET_SendPacket() path, avoid interfering with net_from.
-static qboolean process_error_queue(netsrc_t sock, const struct sockaddr_in *to_addr)
+// returns true if failed socket operation should be retried.
+static qboolean process_error_queue(qsocket_t sock, const netadr_t *to)
{
+#ifdef IP_RECVERR
byte buffer[1024];
- struct sockaddr_in from_addr;
+ struct sockaddr_storage from_addr;
struct msghdr msg;
struct cmsghdr *cmsg;
struct sock_extended_err *ee;
@@ -71,7 +47,7 @@ static qboolean process_error_queue(netsrc_t sock, const struct sockaddr_in *to_
msg.msg_control = buffer;
msg.msg_controllen = sizeof(buffer);
- if (recvmsg(udp_sockets[sock], &msg, MSG_ERRQUEUE) == -1) {
+ if (recvmsg(sock, &msg, MSG_ERRQUEUE) == -1) {
if (errno != EWOULDBLOCK)
Com_DPrintf("%s: %s\n", __func__, strerror(errno));
break;
@@ -86,14 +62,17 @@ static qboolean process_error_queue(netsrc_t sock, const struct sockaddr_in *to_
for (cmsg = CMSG_FIRSTHDR(&msg);
cmsg != NULL;
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
- if (cmsg->cmsg_level != IPPROTO_IP) {
+ if (cmsg->cmsg_level != IPPROTO_IP &&
+ cmsg->cmsg_level != IPPROTO_IPV6) {
continue;
}
- if (cmsg->cmsg_type != IP_RECVERR) {
+ if (cmsg->cmsg_type != IP_RECVERR &&
+ cmsg->cmsg_type != IPV6_RECVERR) {
continue;
}
ee = (struct sock_extended_err *)CMSG_DATA(cmsg);
- if (ee->ee_origin == SO_EE_ORIGIN_ICMP) {
+ if (ee->ee_origin == SO_EE_ORIGIN_ICMP ||
+ ee->ee_origin == SO_EE_ORIGIN_ICMP6) {
break;
}
}
@@ -103,33 +82,38 @@ static qboolean process_error_queue(netsrc_t sock, const struct sockaddr_in *to_
break;
}
- found |= check_offender(&from_addr, to_addr);
-
NET_SockadrToNetadr(&from_addr, &from);
+ // check for offender address being current packet destination
+ if (to != NULL && NET_IsEqualBaseAdr(&from, to) &&
+ (from.port == 0 || from.port == to->port)) {
+ Com_DPrintf("%s: found offending address: %s\n", __func__,
+ NET_AdrToString(&from));
+ found = qtrue;
+ }
+
// handle ICMP error
NET_ErrorEvent(sock, &from, ee->ee_errno, ee->ee_info);
}
return !!tries && !found;
+#else
+ return qfalse;
+#endif
}
-#endif // !__linux__
-
-static ssize_t os_udp_recv(netsrc_t sock, void *data,
+static ssize_t os_udp_recv(qsocket_t sock, void *data,
size_t len, netadr_t *from)
{
- struct sockaddr_in addr;
+ struct sockaddr_storage addr;
socklen_t addrlen;
ssize_t ret;
-
-#if USE_ICMP && (defined __linux__)
int tries;
+
for (tries = 0; tries < MAX_ERROR_RETRIES; tries++) {
-#endif
memset(&addr, 0, sizeof(addr));
addrlen = sizeof(addr);
- ret = recvfrom(udp_sockets[sock], data, len, 0,
+ ret = recvfrom(sock, data, len, 0,
(struct sockaddr *)&addr, &addrlen);
NET_SockadrToNetadr(&addr, from);
@@ -143,32 +127,26 @@ static ssize_t os_udp_recv(netsrc_t sock, void *data,
if (net_error == EWOULDBLOCK)
return NET_AGAIN;
-#if USE_ICMP && (defined __linux__)
- // recvfrom() fails on Linux if there is an ICMP originated pending
- // error on socket. Suck up error queue and retry...
-
if (!process_error_queue(sock, NULL))
break;
}
-#endif
return NET_ERROR;
}
-static ssize_t os_udp_send(netsrc_t sock, const void *data,
+static ssize_t os_udp_send(qsocket_t sock, const void *data,
size_t len, const netadr_t *to)
{
- struct sockaddr_in addr;
+ struct sockaddr_storage addr;
+ socklen_t addrlen;
ssize_t ret;
+ int tries;
- NET_NetadrToSockadr(to, &addr);
+ addrlen = NET_NetadrToSockadr(to, &addr);
-#if USE_ICMP && (defined __linux__)
- int tries;
for (tries = 0; tries < MAX_ERROR_RETRIES; tries++) {
-#endif
- ret = sendto(udp_sockets[sock], data, len, 0,
- (struct sockaddr *)&addr, sizeof(addr));
+ ret = sendto(sock, data, len, 0,
+ (struct sockaddr *)&addr, addrlen);
if (ret >= 0)
return ret;
@@ -178,28 +156,9 @@ static ssize_t os_udp_send(netsrc_t sock, const void *data,
if (net_error == EWOULDBLOCK)
return NET_AGAIN;
-#if USE_ICMP && (defined __linux__)
- // sendto() fails on Linux if there is an ICMP originated pending error
- // on socket. Suck up error queue and retry...
- //
- // But how do I distingiush between a failure caused by completely
- // unrelated ICMP error sitting in the queue and an error directly
- // related to this sendto() call?
- //
- // On one hand, I don't want to drop packets to legitimate clients, and
- // have to retry sendto() after processing error queue. On other
- // hand, infinite loop should be avoided if sendto() call regenerates
- // the message in error queue.
- //
- // This mess is worked around by passing destination address to
- // process_error_queue() and checking if this address/port pair is
- // found in the queue. If it is found, or the queue is empty, do not
- // retry.
-
- if (!process_error_queue(sock, &addr))
+ if (!process_error_queue(sock, to))
break;
}
-#endif
return NET_ERROR;
}
@@ -245,7 +204,7 @@ static neterr_t os_listen(qsocket_t sock, int backlog)
static neterr_t os_accept(qsocket_t sock, qsocket_t *newsock, netadr_t *from)
{
- struct sockaddr_in addr;
+ struct sockaddr_storage addr;
socklen_t addrlen;
int s;
@@ -266,11 +225,12 @@ static neterr_t os_accept(qsocket_t sock, qsocket_t *newsock, netadr_t *from)
static neterr_t os_connect(qsocket_t sock, const netadr_t *to)
{
- struct sockaddr_in addr;
+ struct sockaddr_storage addr;
+ socklen_t addrlen;
- NET_NetadrToSockadr(to, &addr);
+ addrlen = NET_NetadrToSockadr(to, &addr);
- if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+ if (connect(sock, (struct sockaddr *)&addr, addrlen) == -1) {
net_error = errno;
if (net_error == EINPROGRESS)
return NET_OK;
@@ -325,7 +285,7 @@ static neterr_t os_bind(qsocket_t sock, const struct sockaddr *addr, size_t addr
static neterr_t os_getsockname(qsocket_t sock, netadr_t *name)
{
- struct sockaddr_in addr;
+ struct sockaddr_storage addr;
socklen_t addrlen;
memset(&addr, 0, sizeof(addr));