diff options
author | Samuel Thibault <samuel.thibault@ens-lyon.org> | 2020-07-19 04:45:05 +0200 |
---|---|---|
committer | Samuel Thibault <samuel.thibault@ens-lyon.org> | 2020-07-19 04:54:32 +0200 |
commit | 7a4840a57ec7dbc37cca1ab96f058a9610b26950 (patch) | |
tree | 4a6b43753abda6a0ff80ab98f1670f655d3f6045 /src | |
parent | 73ed49ab71998d4288e71e954ef6214b70f23d79 (diff) | |
download | slirp-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>
Diffstat (limited to 'src')
-rw-r--r-- | src/socket.c | 60 | ||||
-rw-r--r-- | src/udp.c | 17 |
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); @@ -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); } |