1 /* $NetBSD: t_ping.c,v 1.8 2010/08/26 17:24:14 pooka 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.8 2010/08/26 17:24:14 pooka 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 48 #include <netinet/in.h> 49 #include <netinet/ip_var.h> 50 51 #include <rump/rump.h> 52 #include <rump/rump_syscalls.h> 53 54 #include "../../h_macros.h" 55 #include "../config/netconfig.c" 56 57 ATF_TC(simpleping); 58 ATF_TC_HEAD(simpleping, tc) 59 { 60 61 atf_tc_set_md_var(tc, "descr", "check that kernel responds to ping"); 62 atf_tc_set_md_var(tc, "use.fs", "true"); 63 atf_tc_set_md_var(tc, "timeout", "2"); 64 } 65 66 ATF_TC_BODY(simpleping, tc) 67 { 68 char ifname[IFNAMSIZ]; 69 pid_t cpid; 70 bool win, win2; 71 72 cpid = fork(); 73 rump_init(); 74 netcfg_rump_makeshmif("but-can-i-buy-your-ether-bus", ifname); 75 76 switch (cpid) { 77 case -1: 78 atf_tc_fail_errno("fork failed"); 79 case 0: 80 netcfg_rump_if(ifname, "1.1.1.10", "255.255.255.0"); 81 pause(); 82 break; 83 default: 84 break; 85 } 86 87 usleep(500000); 88 89 netcfg_rump_if(ifname, "1.1.1.20", "255.255.255.0"); 90 91 /* 92 * The beauty of shmif is that we don't have races here. 93 */ 94 win = netcfg_rump_pingtest("1.1.1.10", 500); 95 win2 = netcfg_rump_pingtest("1.1.1.30", 500); 96 97 kill(cpid, SIGKILL); 98 99 if (!win) 100 atf_tc_fail("ping failed"); 101 if (win2) 102 atf_tc_fail("non-existent host responded"); 103 } 104 105 ATF_TC(floodping); 106 ATF_TC_HEAD(floodping, tc) 107 { 108 109 atf_tc_set_md_var(tc, "descr", "see how kernel responds to floodping"); 110 atf_tc_set_md_var(tc, "use.fs", "true"); 111 } 112 113 /* why the hell isn't this available in userspace??? */ 114 static uint16_t 115 in_cksum(void *data, size_t len) 116 { 117 uint16_t *buf = data; 118 unsigned sum; 119 120 for (sum = 0; len > 1; len -= 2) 121 sum += *buf++; 122 if (len) 123 sum += *(uint8_t *)buf; 124 125 sum = (sum >> 16) + (sum & 0xffff); 126 sum += (sum >> 16); 127 128 return ~sum; 129 } 130 131 static int 132 doping(const char *target, int loops, int pktsize) 133 { 134 char sndbuf[IP_MAXPACKET - sizeof(struct ip)]; 135 char recvbuf[IP_MAXPACKET]; 136 struct sockaddr_in dst, pingee; 137 struct icmp *icmp; 138 socklen_t slen; 139 ssize_t n; 140 int loop, i, succ; 141 int x, xnon, s; 142 143 RL(s = rump_sys_socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)); 144 RL(x = rump_sys_fcntl(s, F_GETFL, 0)); 145 xnon = x | O_NONBLOCK; 146 147 memset(&dst, 0, sizeof(dst)); 148 dst.sin_len = sizeof(dst); 149 dst.sin_family = AF_INET; 150 dst.sin_addr.s_addr = inet_addr(target); 151 152 icmp = (struct icmp *)sndbuf; 153 memset(icmp, 0, sizeof(*icmp)); 154 icmp->icmp_type = ICMP_ECHO; 155 icmp->icmp_id = htons(37); 156 157 if (pktsize < sizeof(*icmp)) 158 pktsize = sizeof(*icmp); 159 if (pktsize > sizeof(sndbuf)) 160 pktsize = sizeof(sndbuf); 161 162 RL(rump_sys_setsockopt(s, SOL_SOCKET, SO_SNDBUF, 163 &pktsize, sizeof(pktsize))); 164 RL(rump_sys_setsockopt(s, SOL_SOCKET, SO_RCVBUF, 165 &pktsize, sizeof(pktsize))); 166 167 slen = sizeof(pingee); 168 succ = 0; 169 for (loop = 0; loop < loops; loop++) { 170 RL(rump_sys_fcntl(s, F_SETFL, x)); 171 icmp->icmp_seq = htons(loop); 172 icmp->icmp_cksum = 0; 173 icmp->icmp_cksum = in_cksum(icmp, pktsize); 174 RL(rump_sys_sendto(s, icmp, pktsize, 0, 175 (struct sockaddr *)&dst, sizeof(dst))); 176 177 RL(rump_sys_fcntl(s, F_SETFL, xnon)); 178 while ((n = rump_sys_recvfrom(s, recvbuf, sizeof(recvbuf), 0, 179 (struct sockaddr *)&pingee, &slen)) > 0) { 180 succ++; 181 } 182 if (n == -1 && errno == EAGAIN) 183 continue; 184 atf_tc_fail_errno("recv failed"); 185 } 186 187 rump_sys_close(s); 188 return succ; 189 } 190 191 #define LOOPS 10000 192 193 ATF_TC_BODY(floodping, tc) 194 { 195 char ifname[IFNAMSIZ]; 196 pid_t cpid; 197 int succ; 198 199 cpid = fork(); 200 rump_init(); 201 netcfg_rump_makeshmif("thank-you-driver-for-getting-me-here", ifname); 202 203 switch (cpid) { 204 case -1: 205 atf_tc_fail_errno("fork failed"); 206 case 0: 207 netcfg_rump_if(ifname, "1.1.1.10", "255.255.255.0"); 208 pause(); 209 break; 210 default: 211 break; 212 } 213 214 netcfg_rump_if(ifname, "1.1.1.20", "255.255.255.0"); 215 216 succ = doping("1.1.1.10", LOOPS, 56); 217 printf("got %d/%d\n", succ, LOOPS); 218 219 kill(cpid, SIGKILL); 220 } 221 222 ATF_TC(floodping2); 223 ATF_TC_HEAD(floodping2, tc) 224 { 225 226 atf_tc_set_md_var(tc, "descr", "two hosts floodpinging each other"); 227 atf_tc_set_md_var(tc, "use.fs", "true"); 228 } 229 230 ATF_TC_BODY(floodping2, tc) 231 { 232 char ifname[IFNAMSIZ]; 233 pid_t cpid; 234 int succ; 235 236 cpid = fork(); 237 rump_init(); 238 netcfg_rump_makeshmif("floodping2", ifname); 239 240 switch (cpid) { 241 case -1: 242 atf_tc_fail_errno("fork failed"); 243 case 0: 244 netcfg_rump_if(ifname, "1.1.1.10", "255.255.255.0"); 245 succ = doping("1.1.1.20", LOOPS, 56); 246 break; 247 default: 248 netcfg_rump_if(ifname, "1.1.1.20", "255.255.255.0"); 249 succ = doping("1.1.1.10", LOOPS, 56); 250 break; 251 } 252 253 printf("got %d/%d\n", succ, LOOPS); 254 } 255 256 ATF_TC(pingsize); 257 ATF_TC_HEAD(pingsize, tc) 258 { 259 260 atf_tc_set_md_var(tc, "descr", "ping with packets min <= size <= max"); 261 atf_tc_set_md_var(tc, "use.fs", "true"); 262 } 263 264 ATF_TC_BODY(pingsize, tc) 265 { 266 char ifname[IFNAMSIZ]; 267 pid_t cpid; 268 int succ, i; 269 270 cpid = fork(); 271 rump_init(); 272 netcfg_rump_makeshmif("jippikaiee", ifname); 273 274 switch (cpid) { 275 case -1: 276 atf_tc_fail_errno("fork failed"); 277 case 0: 278 netcfg_rump_if(ifname, "1.1.1.10", "255.255.255.0"); 279 pause(); 280 break; 281 default: 282 break; 283 } 284 285 netcfg_rump_if(ifname, "1.1.1.20", "255.255.255.0"); 286 287 succ = 0; 288 289 /* small sizes */ 290 for (i = 0 ; i < IP_MAXPACKET - 60000; i++) 291 succ += doping("1.1.1.10", 1, i); 292 293 /* medium sizes */ 294 for (i = IP_MAXPACKET - 60000; i < IP_MAXPACKET - 100; i += 1000) 295 succ += doping("1.1.1.10", 1, i); 296 297 /* big sizes */ 298 for (i = IP_MAXPACKET - 100; i < IP_MAXPACKET; i += 10) 299 succ += doping("1.1.1.10", 1, i); 300 301 printf("got %d/%d\n", succ, IP_MAXPACKET); 302 kill(cpid, SIGKILL); 303 } 304 305 ATF_TC(ping_of_death); 306 ATF_TC_HEAD(ping_of_death, tc) 307 { 308 309 atf_tc_set_md_var(tc, "descr", "send a \"ping of death\""); 310 atf_tc_set_md_var(tc, "use.fs", "true"); 311 atf_tc_set_md_var(tc, "timeout", "2"); 312 } 313 314 ATF_TC_BODY(ping_of_death, tc) 315 { 316 char data[1500]; 317 struct sockaddr_in dst; 318 struct ip *ip; 319 struct icmp *icmp; 320 char ifname[IFNAMSIZ]; 321 pid_t cpid; 322 size_t tot, frag; 323 int s, x, loop; 324 325 cpid = fork(); 326 rump_init(); 327 netcfg_rump_makeshmif("jippikaiee", ifname); 328 329 switch (cpid) { 330 case -1: 331 atf_tc_fail_errno("fork failed"); 332 case 0: 333 /* wait until we receive a too long IP packet */ 334 for (loop = 0;; loop++) { 335 uint64_t ipstat[IP_NSTATS]; 336 size_t arglen; 337 int mib[4]; 338 339 if (loop == 1) 340 netcfg_rump_if(ifname, 341 "1.1.1.10", "255.255.255.0"); 342 343 mib[0] = CTL_NET; 344 mib[1] = PF_INET; 345 mib[2] = IPPROTO_IP; 346 mib[3] = IPCTL_STATS; 347 348 arglen = sizeof(ipstat); 349 RL(rump_sys___sysctl(mib, 4, &ipstat, &arglen, 350 NULL, 0)); 351 if (loop == 0 && ipstat[IP_STAT_TOOLONG] != 0) 352 _exit(1); 353 if (ipstat[IP_STAT_TOOLONG]) 354 break; 355 usleep(10000); 356 } 357 358 _exit(0); 359 break; 360 default: 361 break; 362 } 363 364 netcfg_rump_if(ifname, "1.1.1.20", "255.255.255.0"); 365 366 RL(s = rump_sys_socket(PF_INET, SOCK_RAW, 0)); 367 x = 1; 368 RL(rump_sys_setsockopt(s, IPPROTO_IP, IP_HDRINCL, &x, sizeof(x))); 369 370 memset(&dst, 0, sizeof(dst)); 371 dst.sin_len = sizeof(dst); 372 dst.sin_family = AF_INET; 373 dst.sin_addr.s_addr = inet_addr("1.1.1.10"); 374 375 /* construct packet */ 376 memset(data, 0, sizeof(data)); 377 ip = (struct ip *)data; 378 ip->ip_v = 4; 379 ip->ip_hl = sizeof(*ip) >> 2; 380 ip->ip_p = IPPROTO_ICMP; 381 ip->ip_ttl = IPDEFTTL; 382 ip->ip_dst = dst.sin_addr; 383 ip->ip_id = 1234; 384 385 icmp = (struct icmp *)(ip + 1); 386 icmp->icmp_type = ICMP_ECHO; 387 icmp->icmp_cksum = in_cksum(icmp, sizeof(*icmp)); 388 389 for (;;) { 390 int status; 391 392 /* resolve arp before sending raw stuff */ 393 netcfg_rump_pingtest("1.1.1.10", 1); 394 395 for (tot = 0; 396 tot < 65538 - sizeof(*ip); 397 tot += (frag - sizeof(*ip))) { 398 frag = MIN(65538 - tot, sizeof(data)); 399 ip->ip_off = tot >> 3; 400 assert(ip->ip_off << 3 == tot); 401 ip->ip_len = frag; 402 403 if (frag == sizeof(data)) { 404 ip->ip_off |= IP_MF; 405 } 406 407 RL(rump_sys_sendto(s, data, frag, 0, 408 (struct sockaddr *)&dst, sizeof(dst))); 409 } 410 if (waitpid(-1, &status, WNOHANG) > 0) { 411 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) 412 break; 413 atf_tc_fail("child did not exit clean"); 414 } 415 416 usleep(10000); 417 } 418 } 419 420 ATF_TP_ADD_TCS(tp) 421 { 422 423 ATF_TP_ADD_TC(tp, simpleping); 424 ATF_TP_ADD_TC(tp, floodping); 425 ATF_TP_ADD_TC(tp, floodping2); 426 ATF_TP_ADD_TC(tp, pingsize); 427 ATF_TP_ADD_TC(tp, ping_of_death); 428 429 return atf_no_error(); 430 } 431