From 5887c0c21c9ade2e9c5fe755511d80fef8d4ccd1 Mon Sep 17 00:00:00 2001 From: Stefan Reinauer Date: Wed, 27 May 2026 17:21:35 -0700 Subject: [PATCH] slirp: forward ICMP through datagram sockets Enable datagram ICMP forwarding, keep the host sockets nonblocking, and strip host IP headers before delivering ICMP replies back to the emulated Ethernet device. --- slirp/icmp_var.h | 2 +- slirp/ip_icmp.cpp | 41 ++++++++++++++++++++++++++++++++++++++--- slirp/slirp.cpp | 4 ++-- slirp/slirp.h | 2 +- slirp/socket.cpp | 6 ++++++ slirp/udp.cpp | 1 + 6 files changed, 49 insertions(+), 7 deletions(-) diff --git a/slirp/icmp_var.h b/slirp/icmp_var.h index a50fd65c..8f0fede5 100644 --- a/slirp/icmp_var.h +++ b/slirp/icmp_var.h @@ -61,7 +61,7 @@ struct icmpstat { } extern struct icmpstat icmpstat; -#if 0 +#if SLIRP_ICMP extern struct socket icmp; extern struct socket *icmp_last_so; #endif diff --git a/slirp/ip_icmp.cpp b/slirp/ip_icmp.cpp index 94378f70..c39f0f02 100644 --- a/slirp/ip_icmp.cpp +++ b/slirp/ip_icmp.cpp @@ -88,6 +88,7 @@ static int icmp_send(struct socket *so, struct mbuf *m, int hlen) if (so->s == -1) { return -1; } + fd_nonblock(so->s); so->so_m = m; so->so_faddr = ip->ip_dst; @@ -164,9 +165,9 @@ void icmp_input(struct mbuf *m, int hlen) DEBUG_ARG("icmp_type = %d", icp->icmp_type); switch (icp->icmp_type) { case ICMP_ECHO: - icp->icmp_type = ICMP_ECHOREPLY; ip->ip_len += hlen; /* since ip_input subtracts this */ if (ip->ip_dst.s_addr == alias_addr.s_addr) { + icp->icmp_type = ICMP_ECHOREPLY; icmp_reflect(m); } else { struct socket *so; @@ -176,6 +177,7 @@ void icmp_input(struct mbuf *m, int hlen) if (icmp_send(so, m, hlen) == 0) return; #endif + icp->icmp_type = ICMP_ECHOREPLY; if(udp_attach(so) == -1) { DEBUG_MISC(("icmp_input udp_attach errno = %d-%s\n", errno,strerror(errno))); @@ -431,6 +433,7 @@ void icmp_receive(struct socket *so) int hlen = ip->ip_hl << 2; u_char error_code; struct icmp *icp; + uae_u8 reply[2048]; int id, len; m->m_data += hlen; @@ -438,8 +441,40 @@ void icmp_receive(struct socket *so) icp = mtod(m, struct icmp *); id = icp->icmp_id; - len = recv(so->s, (char*)icp, m->m_len, 0); - icp->icmp_id = id; + len = recv(so->s, (char*)reply, sizeof(reply), 0); + if (len > 0) { + int offset = 0; + int reply_len; + + if (len >= (int)sizeof(struct ip) && (reply[0] >> 4) == 4) { + int iphlen = (reply[0] & 0x0f) << 2; + struct ip *reply_ip = (struct ip*)reply; + if (iphlen >= (int)sizeof(struct ip) && iphlen <= len && + reply_ip->ip_p == IPPROTO_ICMP) { + offset = iphlen; + } + } + + reply_len = len - offset; + if (reply_len <= 0) { + errno = EPROTO; + len = -1; + } else { + if ((size_t)reply_len > M_ROOM(m)) { + char *base = (m->m_flags & M_EXT) ? m->m_ext : m->m_dat; + m_inc(m, (int)(m->m_data - base + reply_len)); + } + if ((size_t)reply_len > M_ROOM(m)) { + errno = ENOMEM; + len = -1; + } else { + memcpy(m->m_data, reply + offset, reply_len); + m->m_len = reply_len; + icp = mtod(m, struct icmp *); + icp->icmp_id = id; + } + } + } m->m_data -= hlen; m->m_len += hlen; diff --git a/slirp/slirp.cpp b/slirp/slirp.cpp index 7798273a..d8e5b23f 100644 --- a/slirp/slirp.cpp +++ b/slirp/slirp.cpp @@ -302,7 +302,7 @@ int slirp_select_fill(INT_PTR *pnfds, } } -#if 0 +#if SLIRP_ICMP /* * ICMP sockets */ @@ -533,7 +533,7 @@ void slirp_select_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds) } } -#if 0 +#if SLIRP_ICMP /* * Check incoming ICMP relies. */ diff --git a/slirp/slirp.h b/slirp/slirp.h index 255f827a..40b737c1 100644 --- a/slirp/slirp.h +++ b/slirp/slirp.h @@ -11,7 +11,7 @@ #include "sysconfig.h" #include "slirp_config.h" -#define SLIRP_ICMP 0 +#define SLIRP_ICMP 1 #ifdef _WIN32 #include diff --git a/slirp/socket.cpp b/slirp/socket.cpp index cd4d5dec..80fd8f3b 100644 --- a/slirp/socket.cpp +++ b/slirp/socket.cpp @@ -397,6 +397,8 @@ void sorecvfrom(struct socket *so) u_char code=ICMP_UNREACH_PORT; int error = WSAGetLastError(); + if(len == -1 && IS_EAGAIN(error)) + return; if(error == WSAEHOSTUNREACH) code=ICMP_UNREACH_HOST; else if(error == WSAENETUNREACH) code=ICMP_UNREACH_NET; @@ -440,6 +442,10 @@ void sorecvfrom(struct socket *so) u_char code=ICMP_UNREACH_PORT; int error = WSAGetLastError(); + if (IS_EAGAIN(error)) { + m_free(m); + return; + } if(error == WSAEHOSTUNREACH) code=ICMP_UNREACH_HOST; else if(error == WSAENETUNREACH) code=ICMP_UNREACH_NET; diff --git a/slirp/udp.cpp b/slirp/udp.cpp index 3fea58b6..0c204c06 100644 --- a/slirp/udp.cpp +++ b/slirp/udp.cpp @@ -334,6 +334,7 @@ SLIRP_SOCKET udp_attach(struct socket *so) * (sendto() on an unbound socket will bind it), it's done * here so that emulation of ytalk etc. don't have to do it */ + fd_nonblock(so->s); memset(&addr, 0, sizeof(struct sockaddr_in)); addr.sin_family = AF_INET; addr.sin_port = 0; -- 2.47.3