1 /* $NetBSD: t_ping.c,v 1.23 2018/03/26 09:11:15 roy Exp $ */ 2 3 /*- 4 * Copyright (c) 2010 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND 17 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 18 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 23 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 26 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 27 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #include <sys/cdefs.h> 31 #ifndef lint 32 __RCSID("$NetBSD: t_ping.c,v 1.23 2018/03/26 09:11:15 roy Exp $"); 33 #endif /* not lint */ 34 35 #include <sys/types.h> 36 #include <sys/resource.h> 37 #include <sys/sysctl.h> 38 #include <sys/wait.h> 39 40 #include <atf-c.h> 41 #include <assert.h> 42 #include <fcntl.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <string.h> 46 #include <unistd.h> 47 #include <signal.h> 48 49 #include <netinet/in.h> 50 #include <netinet/ip_var.h> 51 52 #include <rump/rump.h> 53 #include <rump/rump_syscalls.h> 54 55 #include "h_macros.h" 56 #include "../config/netconfig.c" 57 58 ATF_TC(simpleping); 59 ATF_TC_HEAD(simpleping, tc) 60 { 61 62 atf_tc_set_md_var(tc, "descr", "check that kernel responds to ping"); 63 atf_tc_set_md_var(tc, "timeout", "20"); 64 } 65 66 ATF_TC_BODY(simpleping, tc) 67 { 68 char ifname[IFNAMSIZ]; 69 pid_t cpid; 70 bool win, win2; 71 char token; 72 int channel[2]; 73 74 RL(pipe(channel)); 75 76 cpid = fork(); 77 rump_init(); 78 netcfg_rump_makeshmif("but-can-i-buy-your-ether-bus", ifname); 79 80 switch (cpid) { 81 case -1: 82 atf_tc_fail_errno("fork failed"); 83 case 0: 84 netcfg_rump_if(ifname, "1.1.1.10", "255.255.255.0"); 85 close(channel[0]); 86 ATF_CHECK(write(channel[1], "U", 1) == 1); 87 close(channel[1]); 88 pause(); 89 break; 90 default: 91 break; 92 } 93 94 close(channel[1]); 95 ATF_CHECK(read(channel[0], &token, 1) == 1 && token == 'U'); 96 close(channel[0]); 97 98 netcfg_rump_if(ifname, "1.1.1.20", "255.255.255.0"); 99 100 /* 101 * The beauty of shmif is that we don't have races here. 102 */ 103 win = netcfg_rump_pingtest("1.1.1.10", 500); 104 win2 = netcfg_rump_pingtest("1.1.1.30", 500); 105 106 kill(cpid, SIGKILL); 107 108 if (!win) 109 atf_tc_fail("ping failed"); 110 if (win2) 111 atf_tc_fail("non-existent host responded"); 112 } 113 114 ATF_TC(floodping); 115 ATF_TC_HEAD(floodping, tc) 116 { 117 118 atf_tc_set_md_var(tc, "descr", "see how kernel responds to floodping"); 119 } 120 121 /* why the hell isn't this available in userspace??? */ 122 static uint16_t 123 in_cksum(void *data, size_t len) 124 { 125 uint16_t *buf = data; 126 unsigned sum; 127 128 for (sum = 0; len > 1; len -= 2) 129 sum += *buf++; 130 if (len) 131 sum += *(uint8_t *)buf; 132 133 sum = (sum >> 16) + (sum & 0xffff); 134 sum += (sum >> 16); 135 136 return ~sum; 137 } 138 139 static int 140 doping(const char *target, int loops, u_int pktsize) 141 { 142 union { 143 char buf[IP_MAXPACKET - sizeof(struct ip)]; 144 struct icmp i; /* ensure proper alignment */ 145 } sndbuf; 146 char recvbuf[IP_MAXPACKET]; 147 struct sockaddr_in dst, pingee; 148 struct icmp *icmp; 149 socklen_t slen; 150 ssize_t n; 151 int loop, succ; 152 int x, xnon, s; 153 154 RL(s = rump_sys_socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)); 155 RL(x = rump_sys_fcntl(s, F_GETFL, 0)); 156 xnon = x | O_NONBLOCK; 157 158 memset(&dst, 0, sizeof(dst)); 159 dst.sin_len = sizeof(dst); 160 dst.sin_family = AF_INET; 161 dst.sin_addr.s_addr = inet_addr(target); 162 163 icmp = (struct icmp *)&sndbuf; 164 memset(icmp, 0, sizeof(*icmp)); 165 icmp->icmp_type = ICMP_ECHO; 166 icmp->icmp_id = htons(37); 167 168 if (pktsize < sizeof(*icmp)) 169 pktsize = sizeof(*icmp); 170 if (pktsize > sizeof(sndbuf.buf)) 171 pktsize = sizeof(sndbuf.buf); 172 173 RL(rump_sys_setsockopt(s, SOL_SOCKET, SO_SNDBUF, 174 &pktsize, sizeof(pktsize))); 175 RL(rump_sys_setsockopt(s, SOL_SOCKET, SO_RCVBUF, 176 &pktsize, sizeof(pktsize))); 177 178 slen = sizeof(pingee); 179 succ = 0; 180 for (loop = 0; loop < loops; loop++) { 181 RL(rump_sys_fcntl(s, F_SETFL, x)); 182 icmp->icmp_seq = htons(loop); 183 icmp->icmp_cksum = 0; 184 icmp->icmp_cksum = in_cksum(icmp, pktsize); 185 186 n = rump_sys_sendto(s, icmp, pktsize, 0, 187 (struct sockaddr *)&dst, sizeof(dst)); 188 if (n == -1) { 189 if (errno == ENOBUFS) 190 continue; 191 atf_tc_fail_errno("sendto failed"); 192 } 193 194 RL(rump_sys_fcntl(s, F_SETFL, xnon)); 195 while ((n = rump_sys_recvfrom(s, recvbuf, sizeof(recvbuf), 0, 196 (struct sockaddr *)&pingee, &slen)) > 0) { 197 succ++; 198 } 199 if (n == -1 && (errno == EAGAIN || errno == ENOBUFS)) 200 continue; 201 atf_tc_fail_errno("recv failed (n == %zd)", n); 202 } 203 204 rump_sys_close(s); 205 return succ; 206 } 207 208 #define LOOPS 10000 209 210 ATF_TC_BODY(floodping, tc) 211 { 212 char ifname[IFNAMSIZ]; 213 pid_t cpid; 214 int succ; 215 216 cpid = fork(); 217 rump_init(); 218 netcfg_rump_makeshmif("thank-you-driver-for-getting-me-here", ifname); 219 220 switch (cpid) { 221 case -1: 222 atf_tc_fail_errno("fork failed"); 223 case 0: 224 netcfg_rump_if(ifname, "1.1.1.10", "255.255.255.0"); 225 pause(); 226 break; 227 default: 228 break; 229 } 230 231 netcfg_rump_if(ifname, "1.1.1.20", "255.255.255.0"); 232 233 succ = doping("1.1.1.10", LOOPS, 56); 234 printf("got %d/%d\n", succ, LOOPS); 235 236 kill(cpid, SIGKILL); 237 } 238 239 ATF_TC(floodping2); 240 ATF_TC_HEAD(floodping2, tc) 241 { 242 243 atf_tc_set_md_var(tc, "descr", "two hosts floodpinging each other"); 244 } 245 246 ATF_TC_BODY(floodping2, tc) 247 { 248 char ifname[IFNAMSIZ]; 249 pid_t cpid; 250 int succ; 251 252 cpid = fork(); 253 rump_init(); 254 netcfg_rump_makeshmif("floodping2", ifname); 255 256 switch (cpid) { 257 case -1: 258 atf_tc_fail_errno("fork failed"); 259 case 0: 260 netcfg_rump_if(ifname, "1.1.1.10", "255.255.255.0"); 261 succ = doping("1.1.1.20", LOOPS, 56); 262 break; 263 default: 264 netcfg_rump_if(ifname, "1.1.1.20", "255.255.255.0"); 265 succ = doping("1.1.1.10", LOOPS, 56); 266 break; 267 } 268 269 printf("got %d/%d\n", succ, LOOPS); 270 } 271 272 ATF_TC(pingsize); 273 ATF_TC_HEAD(pingsize, tc) 274 { 275 276 atf_tc_set_md_var(tc, "descr", "ping with packets min <= size <= max"); 277 } 278 279 ATF_TC_BODY(pingsize, tc) 280 { 281 char ifname[IFNAMSIZ]; 282 pid_t cpid; 283 int succ, i; 284 285 cpid = fork(); 286 rump_init(); 287 netcfg_rump_makeshmif("jippikaiee", ifname); 288 289 switch (cpid) { 290 case -1: 291 atf_tc_fail_errno("fork failed"); 292 case 0: 293 netcfg_rump_if(ifname, "1.1.1.10", "255.255.255.0"); 294 pause(); 295 break; 296 default: 297 break; 298 } 299 300 netcfg_rump_if(ifname, "1.1.1.20", "255.255.255.0"); 301 302 succ = 0; 303 304 /* small sizes */ 305 for (i = 0 ; i < IP_MAXPACKET - 60000; i++) 306 succ += doping("1.1.1.10", 1, i); 307 308 /* medium sizes */ 309 for (i = IP_MAXPACKET - 60000; i < IP_MAXPACKET - 100; i += 1000) 310 succ += doping("1.1.1.10", 1, i); 311 312 /* big sizes */ 313 for (i = IP_MAXPACKET - 100; i < IP_MAXPACKET; i += 10) 314 succ += doping("1.1.1.10", 1, i); 315 316 printf("got %d/%d\n", succ, IP_MAXPACKET); 317 kill(cpid, SIGKILL); 318 } 319 320 ATF_TC(ping_of_death); 321 ATF_TC_HEAD(ping_of_death, tc) 322 { 323 324 atf_tc_set_md_var(tc, "descr", "send a \"ping of death\""); 325 atf_tc_set_md_var(tc, "timeout", "20"); 326 } 327 328 ATF_TC_BODY(ping_of_death, tc) 329 { 330 char data[1500]; 331 struct sockaddr_in dst; 332 struct ip *ip; 333 struct icmp *icmp; 334 char ifname[IFNAMSIZ]; 335 pid_t cpid; 336 size_t tot, frag; 337 int s, x, loop; 338 ssize_t error; 339 340 cpid = fork(); 341 rump_init(); 342 netcfg_rump_makeshmif("jippikaiee", ifname); 343 344 switch (cpid) { 345 case -1: 346 atf_tc_fail_errno("fork failed"); 347 case 0: 348 /* wait until we receive a too long IP packet */ 349 for (loop = 0;; loop++) { 350 uint64_t ipstat[IP_NSTATS]; 351 size_t arglen; 352 int mib[4]; 353 354 if (loop == 1) 355 netcfg_rump_if(ifname, 356 "1.1.1.10", "255.255.255.0"); 357 358 mib[0] = CTL_NET; 359 mib[1] = PF_INET; 360 mib[2] = IPPROTO_IP; 361 mib[3] = IPCTL_STATS; 362 363 arglen = sizeof(ipstat); 364 RL(rump_sys___sysctl(mib, 4, &ipstat, &arglen, 365 NULL, 0)); 366 if (loop == 0 && ipstat[IP_STAT_TOOLONG] != 0) 367 _exit(1); 368 if (ipstat[IP_STAT_TOOLONG]) 369 break; 370 usleep(10000); 371 } 372 373 _exit(0); 374 break; 375 default: 376 break; 377 } 378 379 netcfg_rump_if(ifname, "1.1.1.20", "255.255.255.0"); 380 381 RL(s = rump_sys_socket(PF_INET, SOCK_RAW, 0)); 382 x = 1; 383 RL(rump_sys_setsockopt(s, IPPROTO_IP, IP_HDRINCL, &x, sizeof(x))); 384 385 memset(&dst, 0, sizeof(dst)); 386 dst.sin_len = sizeof(dst); 387 dst.sin_family = AF_INET; 388 dst.sin_addr.s_addr = inet_addr("1.1.1.10"); 389 390 /* construct packet */ 391 memset(data, 0, sizeof(data)); 392 ip = (struct ip *)data; 393 ip->ip_v = 4; 394 ip->ip_hl = sizeof(*ip) >> 2; 395 ip->ip_p = IPPROTO_ICMP; 396 ip->ip_ttl = IPDEFTTL; 397 ip->ip_dst = dst.sin_addr; 398 ip->ip_id = 1234; 399 400 icmp = (struct icmp *)(ip + 1); 401 icmp->icmp_type = ICMP_ECHO; 402 icmp->icmp_cksum = in_cksum(icmp, sizeof(*icmp)); 403 404 for (;;) { 405 int status; 406 407 /* resolve arp before sending raw stuff */ 408 netcfg_rump_pingtest("1.1.1.10", 1); 409 410 for (tot = 0; 411 tot < 65538 - sizeof(*ip); 412 tot += (frag - sizeof(*ip))) { 413 frag = MIN(65538 - tot, sizeof(data)); 414 ip->ip_off = tot >> 3; 415 assert((size_t)ip->ip_off << 3 == tot); 416 ip->ip_len = frag; 417 418 if (frag == sizeof(data)) { 419 ip->ip_off |= IP_MF; 420 } 421 422 error = rump_sys_sendto(s, data, frag, 0, 423 (struct sockaddr *)&dst, sizeof(dst)); 424 if (error == -1) { 425 if (errno == ENOBUFS) 426 continue; 427 atf_tc_fail_errno("sendto failed"); 428 } 429 if ((size_t)error != frag) 430 atf_tc_fail("sendto did not write all data"); 431 } 432 if (waitpid(-1, &status, WNOHANG) > 0) { 433 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) 434 break; 435 atf_tc_fail("child did not exit clean"); 436 } 437 438 usleep(10000); 439 } 440 } 441 442 ATF_TP_ADD_TCS(tp) 443 { 444 445 ATF_TP_ADD_TC(tp, simpleping); 446 ATF_TP_ADD_TC(tp, floodping); 447 ATF_TP_ADD_TC(tp, floodping2); 448 ATF_TP_ADD_TC(tp, pingsize); 449 ATF_TP_ADD_TC(tp, ping_of_death); 450 451 return atf_no_error(); 452 } 453