diff options
author | Samuel Thibault <samuel.thibault@ens-lyon.org> | 2021-06-07 03:05:25 +0200 |
---|---|---|
committer | Samuel Thibault <samuel.thibault@ens-lyon.org> | 2021-06-07 03:05:25 +0200 |
commit | 242de58ec73f77b6b7dada205d31b0828140b589 (patch) | |
tree | fe6104d5b88c83e4261febb0641dcc88590629f5 | |
parent | 5758d835e431886e862a8b849ac2236b7cfed067 (diff) | |
download | slirp-242de58ec73f77b6b7dada205d31b0828140b589.zip slirp-242de58ec73f77b6b7dada205d31b0828140b589.tar.gz slirp-242de58ec73f77b6b7dada205d31b0828140b589.tar.bz2 |
icmp: Support falling back on trying a SOCK_RAW socket
This allows pings provided that the process is running as root (or has some
capability to create raw sockets).
Signed-off-by: Samuel Thibault <samuel.thibault@ens-lyon.org>
-rw-r--r-- | src/ip_icmp.c | 60 |
1 files changed, 38 insertions, 22 deletions
diff --git a/src/ip_icmp.c b/src/ip_icmp.c index 9fba653..4607721 100644 --- a/src/ip_icmp.c +++ b/src/ip_icmp.c @@ -91,8 +91,32 @@ static int icmp_send(struct socket *so, struct mbuf *m, int hlen) struct ip *ip = mtod(m, struct ip *); struct sockaddr_in addr; + /* + * The behavior of reading SOCK_DGRAM+IPPROTO_ICMP sockets is inconsistent + * between host OSes. On Linux, only the ICMP header and payload is + * included. On macOS/Darwin, the socket acts like a raw socket and + * includes the IP header as well. On other BSDs, SOCK_DGRAM+IPPROTO_ICMP + * sockets aren't supported at all, so we treat them like raw sockets. It + * isn't possible to detect this difference at runtime, so we must use an + * #ifdef to determine if we need to remove the IP header. + */ +#ifdef CONFIG_BSD + so->so_type = IPPROTO_IP; +#else + so->so_type = IPPROTO_ICMP; +#endif + so->s = slirp_socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); if (so->s == -1) { + if (errno == EAFNOSUPPORT + || errno == EPROTONOSUPPORT + || errno == EACCES) { + /* Kernel doesn't support or allow ping sockets. */ + so->so_type = IPPROTO_IP; + so->s = slirp_socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); + } + } + if (so->s == -1) { return -1; } so->slirp->cb->register_poll_fd(so->s, so->slirp->opaque); @@ -108,7 +132,6 @@ static int icmp_send(struct socket *so, struct mbuf *m, int hlen) so->so_faddr = ip->ip_dst; so->so_laddr = ip->ip_src; so->so_iptos = ip->ip_tos; - so->so_type = IPPROTO_ICMP; so->so_state = SS_ISFCONNECTED; so->so_expire = curtime + SO_EXPIRE; @@ -478,31 +501,24 @@ void icmp_receive(struct socket *so) id = icp->icmp_id; len = recv(so->s, icp, M_ROOM(m), 0); - /* - * The behavior of reading SOCK_DGRAM+IPPROTO_ICMP sockets is inconsistent - * between host OSes. On Linux, only the ICMP header and payload is - * included. On macOS/Darwin, the socket acts like a raw socket and - * includes the IP header as well. On other BSDs, SOCK_DGRAM+IPPROTO_ICMP - * sockets aren't supported at all, so we treat them like raw sockets. It - * isn't possible to detect this difference at runtime, so we must use an - * #ifdef to determine if we need to remove the IP header. - */ -#ifdef CONFIG_BSD - if (len >= sizeof(struct ip)) { - struct ip *inner_ip = mtod(m, struct ip *); - int inner_hlen = inner_ip->ip_hl << 2; - if (inner_hlen > len) { + + if (so->so_type == IPPROTO_IP) { + if (len >= sizeof(struct ip)) { + struct ip *inner_ip = mtod(m, struct ip *); + int inner_hlen = inner_ip->ip_hl << 2; + if (inner_hlen > len) { + len = -1; + errno = -EINVAL; + } else { + len -= inner_hlen; + memmove(icp, (unsigned char *)icp + inner_hlen, len); + } + } else { len = -1; errno = -EINVAL; - } else { - len -= inner_hlen; - memmove(icp, (unsigned char *)icp + inner_hlen, len); } - } else { - len = -1; - errno = -EINVAL; } -#endif + icp->icmp_id = id; m->m_data -= hlen; |