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