1 /* $NetBSD: t_ping.c,v 1.17 2017/01/13 21:30:42 christos 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.17 2017/01/13 21:30:42 christos 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 RL(rump_sys_sendto(s, icmp, pktsize, 0, 186 (struct sockaddr *)&dst, sizeof(dst))); 187 188 RL(rump_sys_fcntl(s, F_SETFL, xnon)); 189 while ((n = rump_sys_recvfrom(s, recvbuf, sizeof(recvbuf), 0, 190 (struct sockaddr *)&pingee, &slen)) > 0) { 191 succ++; 192 } 193 if (n == -1 && errno == EAGAIN) 194 continue; 195 atf_tc_fail_errno("recv failed"); 196 } 197 198 rump_sys_close(s); 199 return succ; 200 } 201 202 #define LOOPS 10000 203 204 ATF_TC_BODY(floodping, tc) 205 { 206 char ifname[IFNAMSIZ]; 207 pid_t cpid; 208 int succ; 209 210 cpid = fork(); 211 rump_init(); 212 netcfg_rump_makeshmif("thank-you-driver-for-getting-me-here", ifname); 213 214 switch (cpid) { 215 case -1: 216 atf_tc_fail_errno("fork failed"); 217 case 0: 218 netcfg_rump_if(ifname, "1.1.1.10", "255.255.255.0"); 219 pause(); 220 break; 221 default: 222 break; 223 } 224 225 netcfg_rump_if(ifname, "1.1.1.20", "255.255.255.0"); 226 227 succ = doping("1.1.1.10", LOOPS, 56); 228 printf("got %d/%d\n", succ, LOOPS); 229 230 kill(cpid, SIGKILL); 231 } 232 233 ATF_TC(floodping2); 234 ATF_TC_HEAD(floodping2, tc) 235 { 236 237 atf_tc_set_md_var(tc, "descr", "two hosts floodpinging each other"); 238 } 239 240 ATF_TC_BODY(floodping2, tc) 241 { 242 char ifname[IFNAMSIZ]; 243 pid_t cpid; 244 int succ; 245 246 cpid = fork(); 247 rump_init(); 248 netcfg_rump_makeshmif("floodping2", ifname); 249 250 switch (cpid) { 251 case -1: 252 atf_tc_fail_errno("fork failed"); 253 case 0: 254 netcfg_rump_if(ifname, "1.1.1.10", "255.255.255.0"); 255 succ = doping("1.1.1.20", LOOPS, 56); 256 break; 257 default: 258 netcfg_rump_if(ifname, "1.1.1.20", "255.255.255.0"); 259 succ = doping("1.1.1.10", LOOPS, 56); 260 break; 261 } 262 263 printf("got %d/%d\n", succ, LOOPS); 264 } 265 266 ATF_TC(pingsize); 267 ATF_TC_HEAD(pingsize, tc) 268 { 269 270 atf_tc_set_md_var(tc, "descr", "ping with packets min <= size <= max"); 271 } 272 273 ATF_TC_BODY(pingsize, tc) 274 { 275 char ifname[IFNAMSIZ]; 276 pid_t cpid; 277 int succ, i; 278 279 cpid = fork(); 280 rump_init(); 281 netcfg_rump_makeshmif("jippikaiee", ifname); 282 283 switch (cpid) { 284 case -1: 285 atf_tc_fail_errno("fork failed"); 286 case 0: 287 netcfg_rump_if(ifname, "1.1.1.10", "255.255.255.0"); 288 pause(); 289 break; 290 default: 291 break; 292 } 293 294 netcfg_rump_if(ifname, "1.1.1.20", "255.255.255.0"); 295 296 succ = 0; 297 298 /* small sizes */ 299 for (i = 0 ; i < IP_MAXPACKET - 60000; i++) 300 succ += doping("1.1.1.10", 1, i); 301 302 /* medium sizes */ 303 for (i = IP_MAXPACKET - 60000; i < IP_MAXPACKET - 100; i += 1000) 304 succ += doping("1.1.1.10", 1, i); 305 306 /* big sizes */ 307 for (i = IP_MAXPACKET - 100; i < IP_MAXPACKET; i += 10) 308 succ += doping("1.1.1.10", 1, i); 309 310 printf("got %d/%d\n", succ, IP_MAXPACKET); 311 kill(cpid, SIGKILL); 312 } 313 314 ATF_TC(ping_of_death); 315 ATF_TC_HEAD(ping_of_death, tc) 316 { 317 318 atf_tc_set_md_var(tc, "descr", "send a \"ping of death\""); 319 atf_tc_set_md_var(tc, "timeout", "20"); 320 } 321 322 ATF_TC_BODY(ping_of_death, tc) 323 { 324 char data[1500]; 325 struct sockaddr_in dst; 326 struct ip *ip; 327 struct icmp *icmp; 328 char ifname[IFNAMSIZ]; 329 pid_t cpid; 330 size_t tot, frag; 331 int s, x, loop; 332 333 cpid = fork(); 334 rump_init(); 335 netcfg_rump_makeshmif("jippikaiee", ifname); 336 337 switch (cpid) { 338 case -1: 339 atf_tc_fail_errno("fork failed"); 340 case 0: 341 /* wait until we receive a too long IP packet */ 342 for (loop = 0;; loop++) { 343 uint64_t ipstat[IP_NSTATS]; 344 size_t arglen; 345 int mib[4]; 346 347 if (loop == 1) 348 netcfg_rump_if(ifname, 349 "1.1.1.10", "255.255.255.0"); 350 351 mib[0] = CTL_NET; 352 mib[1] = PF_INET; 353 mib[2] = IPPROTO_IP; 354 mib[3] = IPCTL_STATS; 355 356 arglen = sizeof(ipstat); 357 RL(rump_sys___sysctl(mib, 4, &ipstat, &arglen, 358 NULL, 0)); 359 if (loop == 0 && ipstat[IP_STAT_TOOLONG] != 0) 360 _exit(1); 361 if (ipstat[IP_STAT_TOOLONG]) 362 break; 363 usleep(10000); 364 } 365 366 _exit(0); 367 break; 368 default: 369 break; 370 } 371 372 netcfg_rump_if(ifname, "1.1.1.20", "255.255.255.0"); 373 374 RL(s = rump_sys_socket(PF_INET, SOCK_RAW, 0)); 375 x = 1; 376 RL(rump_sys_setsockopt(s, IPPROTO_IP, IP_HDRINCL, &x, sizeof(x))); 377 378 memset(&dst, 0, sizeof(dst)); 379 dst.sin_len = sizeof(dst); 380 dst.sin_family = AF_INET; 381 dst.sin_addr.s_addr = inet_addr("1.1.1.10"); 382 383 /* construct packet */ 384 memset(data, 0, sizeof(data)); 385 ip = (struct ip *)data; 386 ip->ip_v = 4; 387 ip->ip_hl = sizeof(*ip) >> 2; 388 ip->ip_p = IPPROTO_ICMP; 389 ip->ip_ttl = IPDEFTTL; 390 ip->ip_dst = dst.sin_addr; 391 ip->ip_id = 1234; 392 393 icmp = (struct icmp *)(ip + 1); 394 icmp->icmp_type = ICMP_ECHO; 395 icmp->icmp_cksum = in_cksum(icmp, sizeof(*icmp)); 396 397 for (;;) { 398 int status; 399 400 /* resolve arp before sending raw stuff */ 401 netcfg_rump_pingtest("1.1.1.10", 1); 402 403 for (tot = 0; 404 tot < 65538 - sizeof(*ip); 405 tot += (frag - sizeof(*ip))) { 406 frag = MIN(65538 - tot, sizeof(data)); 407 ip->ip_off = tot >> 3; 408 assert((size_t)ip->ip_off << 3 == tot); 409 ip->ip_len = frag; 410 411 if (frag == sizeof(data)) { 412 ip->ip_off |= IP_MF; 413 } 414 415 RL(rump_sys_sendto(s, data, frag, 0, 416 (struct sockaddr *)&dst, sizeof(dst))); 417 } 418 if (waitpid(-1, &status, WNOHANG) > 0) { 419 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) 420 break; 421 atf_tc_fail("child did not exit clean"); 422 } 423 424 usleep(10000); 425 } 426 } 427 428 ATF_TP_ADD_TCS(tp) 429 { 430 431 ATF_TP_ADD_TC(tp, simpleping); 432 ATF_TP_ADD_TC(tp, floodping); 433 ATF_TP_ADD_TC(tp, floodping2); 434 ATF_TP_ADD_TC(tp, pingsize); 435 ATF_TP_ADD_TC(tp, ping_of_death); 436 437 return atf_no_error(); 438 } 439