blob: 2ed5e2fdbaf7f93b6416321a1a4b7704341e714b [file] [log] [blame]
/*
* Copyright (c) 2017
* Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
/*
* Support ping using the system ping binary on Linux and Mac
* On Linux/Mac, icmp protocol requires the process to have system permission.
* Our emulator, by default, does not have system permission, so we have to
* resort to the built-in ping binary to handle icmp messages.
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/error-report.h"
#include "slirp.h"
#include "ip_icmp.h"
#include "ip6_icmp.h"
int ping_binary_send(struct socket* so, struct mbuf* m, int hlen) {
char ping_cmd[1024];
if (so->so_type == IPPROTO_ICMPV6) {
struct ip6* ip = mtod(m, struct ip6*);
char dest_ip_addr[INET6_ADDRSTRLEN];
if (inet_ntop(AF_INET6, &ip->ip_dst, dest_ip_addr, sizeof(dest_ip_addr)) == NULL)
return -1;
snprintf(ping_cmd, sizeof(ping_cmd), "ping6 -c 1 %s 2>&1", dest_ip_addr);
} else {
struct ip* ip = mtod(m, struct ip*);
const char* dest_ip_addr = inet_ntoa(ip->ip_dst);
if (dest_ip_addr == NULL)
return -1;
snprintf(ping_cmd, sizeof(ping_cmd), "ping -c 1 -t %d %s 2>&1", ip->ip_ttl, dest_ip_addr);
}
FILE* p = popen(ping_cmd, "r");
if (!p) {
error_report("Failed to popen: %s due to: %s", ping_cmd, strerror(errno));
return -1;
}
so->ping_pipe = p;
// this is rather critical, otherwise emulator blocks when pinging
// an unreachable host
so->s = fileno(p);
fcntl(so->s, F_SETFL, O_NONBLOCK);
return 0;
}
/*
ping -c 1 www.google.com
PING www.google.com (172.217.6.36) 56(84) bytes of data.
64 bytes from sfo03s08-in-f36.1e100.net (172.217.6.36): icmp_seq=1 ttl=57
time=1.31 ms
--- www.google.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 1.316/1.316/1.316/0.000 ms
*/
int ping_binary_recv(struct socket* so, struct ip* ip, struct icmp* icp) {
if (so == NULL || so->ping_pipe == NULL) {
return -1;
}
int seq = -1, ttl = -1;
char line[4096] = "";
while (fgets(line, sizeof(line), so->ping_pipe)) {
char* p = strstr(line, "icmp_seq=");
if (p != NULL) {
p += strlen("icmp_seq=");
seq = atoi(p);
}
p = strstr(line, "ttl=");
if (p != NULL) {
p += strlen("ttl=");
ttl = atoi(p);
}
if (seq != -1 && ttl != -1) {
break;
}
}
if (seq != -1 && ttl != -1) {
ip->ip_ttl = ttl;
ip->ip_p = IPPROTO_ICMP;
icp->icmp_type = ICMP_ECHOREPLY;
icp->icmp_code = 0;
icp->icmp_cksum = 0;
return sizeof(*icp);
}
return -1;
}
/*
ping6 -c 1 2607:f8b0:4005:807::200e
PING 2607:f8b0:4005:807::200e(2607:f8b0:4005:807::200e) 56 data bytes
64 bytes from 2607:f8b0:4005:807::200e: icmp_seq=1 ttl=57 time=1.31 ms
--- 2607:f8b0:4005:807::200e ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 1.314/1.314/1.314/0.000 ms
*/
int ping6_binary_recv(struct socket* so, struct ip6* ip, struct icmp6* icp) {
if (so == NULL || so->ping_pipe == NULL) {
return -1;
}
int seq = -1, ttl = -1;
char line[4096] = "";
while (fgets(line, sizeof(line), so->ping_pipe)) {
char* p = strstr(line, "icmp_seq=");
if (p != NULL) {
p += strlen("icmp_seq=");
seq = atoi(p);
}
p = strstr(line, "ttl=");
if (p != NULL) {
p += strlen("ttl=");
ttl = atoi(p);
}
if (seq != -1 && ttl != -1) {
break;
}
}
if (seq != -1 && ttl != -1) {
ip->ip_nh = IPPROTO_ICMPV6;
ip->ip_hl = ttl;
icp->icmp6_type = ICMP6_ECHO_REPLY;
icp->icmp6_code = 0;
icp->icmp6_cksum = 0;
return sizeof(*icp);
}
return -1;
}
void ping_binary_close(struct socket* so) {
if (so != NULL && so->ping_pipe) {
pclose(so->ping_pipe);
so->ping_pipe = NULL;
}
}