aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Thibault <samuel.thibault@ens-lyon.org>2020-07-19 04:45:05 +0200
committerSamuel Thibault <samuel.thibault@ens-lyon.org>2020-07-19 04:54:32 +0200
commit7a4840a57ec7dbc37cca1ab96f058a9610b26950 (patch)
tree4a6b43753abda6a0ff80ab98f1670f655d3f6045
parent73ed49ab71998d4288e71e954ef6214b70f23d79 (diff)
downloadslirp-7a4840a57ec7dbc37cca1ab96f058a9610b26950.zip
slirp-7a4840a57ec7dbc37cca1ab96f058a9610b26950.tar.gz
slirp-7a4840a57ec7dbc37cca1ab96f058a9610b26950.tar.bz2
udp, udp6, icmp, icmp6: Enable forwarding errors on Linux
Not all icmp errors are reported as errno errors. Linux however lets us get them through a message error queue. Signed-off-by: Samuel Thibault <samuel.thibault@ens-lyon.org>
-rw-r--r--src/socket.c60
-rw-r--r--src/udp.c17
2 files changed, 76 insertions, 1 deletions
diff --git a/src/socket.c b/src/socket.c
index 81a499f..590eb11 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -8,6 +8,9 @@
#ifdef __sun__
#include <sys/filio.h>
#endif
+#ifdef __linux__
+#include <linux/errqueue.h>
+#endif
static void sofcantrcvmore(struct socket *so);
static void sofcantsendmore(struct socket *so);
@@ -493,12 +496,67 @@ void sorecvfrom(struct socket *so)
struct sockaddr_storage addr;
struct sockaddr_storage saddr, daddr;
socklen_t addrlen = sizeof(struct sockaddr_storage);
+ char buff[256];
+
+#ifdef __linux__
+ ssize_t size;
+ struct msghdr msg;
+ struct iovec iov;
+ char control[1024];
+
+ /* First look for errors */
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_name = &saddr;
+ msg.msg_namelen = sizeof(saddr);
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof(control);
+ iov.iov_base = buff;
+ iov.iov_len = sizeof(buff);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ size = recvmsg(so->s, &msg, MSG_ERRQUEUE);
+ if (size >= 0) {
+ struct cmsghdr *cmsg;
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+
+ if (cmsg->cmsg_level == IPPROTO_IP &&
+ cmsg->cmsg_type == IP_RECVERR) {
+ struct sock_extended_err *ee =
+ (struct sock_extended_err *) CMSG_DATA(cmsg);
+
+ if (ee->ee_origin == SO_EE_ORIGIN_ICMP) {
+ /* Got an ICMP error, forward it */
+ struct sockaddr_in *sin;
+
+ sin = (struct sockaddr_in *) SO_EE_OFFENDER(ee);
+ icmp_forward_error(so->so_m, ee->ee_type, ee->ee_code,
+ 0, NULL, &sin->sin_addr);
+ }
+ }
+ else if (cmsg->cmsg_level == IPPROTO_IPV6 &&
+ cmsg->cmsg_type == IPV6_RECVERR) {
+ struct sock_extended_err *ee =
+ (struct sock_extended_err *) CMSG_DATA(cmsg);
+
+ if (ee->ee_origin == SO_EE_ORIGIN_ICMP6) {
+ /* Got an ICMPv6 error, forward it */
+ struct sockaddr_in6 *sin6;
+
+ sin6 = (struct sockaddr_in6 *) SO_EE_OFFENDER(ee);
+ icmp6_forward_error(so->so_m, ee->ee_type, ee->ee_code,
+ &sin6->sin6_addr);
+ }
+ }
+ }
+ return;
+ }
+#endif
DEBUG_CALL("sorecvfrom");
DEBUG_ARG("so = %p", so);
if (so->so_type == IPPROTO_ICMP) { /* This is a "ping" reply */
- char buff[256];
int len;
len = recvfrom(so->s, buff, 256, 0, (struct sockaddr *)&addr, &addrlen);
diff --git a/src/udp.c b/src/udp.c
index 0ad44d7..46a0c34 100644
--- a/src/udp.c
+++ b/src/udp.c
@@ -288,6 +288,23 @@ int udp_attach(struct socket *so, unsigned short af)
so->s = -1;
return -1;
}
+
+#ifdef __linux__
+ {
+ int opt = 1;
+ switch (af) {
+ case AF_INET:
+ setsockopt(so->s, IPPROTO_IP, IP_RECVERR, &opt, sizeof(opt));
+ break;
+ case AF_INET6:
+ setsockopt(so->s, IPPROTO_IPV6, IPV6_RECVERR, &opt, sizeof(opt));
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ }
+#endif
+
so->so_expire = curtime + SO_EXPIRE;
insque(so, &so->slirp->udb);
}