1 /* $NetBSD: t_ping.c,v 1.14 2011/06/26 13:15:22 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.14 2011/06/26 13:15:22 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", "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 } 111 112 /* why the hell isn't this available in userspace??? */ 113 static uint16_t 114 in_cksum(void *data, size_t len) 115 { 116 uint16_t *buf = data; 117 unsigned sum; 118 119 for (sum = 0; len > 1; len -= 2) 120 sum += *buf++; 121 if (len) 122 sum += *(uint8_t *)buf; 123 124 sum = (sum >> 16) + (sum & 0xffff); 125 sum += (sum >> 16); 126 127 return ~sum; 128 } 129 130 static int 131 doping(const char *target, int loops, u_int pktsize) 132 { 133 union { 134 char buf[IP_MAXPACKET - sizeof(struct ip)]; 135 struct icmp i; /* ensure proper alignment */ 136 } sndbuf; 137 char recvbuf[IP_MAXPACKET]; 138 struct sockaddr_in dst, pingee; 139 struct icmp *icmp; 140 socklen_t slen; 141 ssize_t n; 142 int loop, succ; 143 int x, xnon, s; 144 145 RL(s = rump_sys_socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)); 146 RL(x = rump_sys_fcntl(s, F_GETFL, 0)); 147 xnon = x | O_NONBLOCK; 148 149 memset(&dst, 0, sizeof(dst)); 150 dst.sin_len = sizeof(dst); 151 dst.sin_family = AF_INET; 152 dst.sin_addr.s_addr = inet_addr(target); 153 154 icmp = (struct icmp *)&sndbuf; 155 memset(icmp, 0, sizeof(*icmp)); 156 icmp->icmp_type = ICMP_ECHO; 157 icmp->icmp_id = htons(37); 158 159 if (pktsize < sizeof(*icmp)) 160 pktsize = sizeof(*icmp); 161 if (pktsize > sizeof(sndbuf.buf)) 162 pktsize = sizeof(sndbuf.buf); 163 164 RL(rump_sys_setsockopt(s, SOL_SOCKET, SO_SNDBUF, 165 &pktsize, sizeof(pktsize))); 166 RL(rump_sys_setsockopt(s, SOL_SOCKET, SO_RCVBUF, 167 &pktsize, sizeof(pktsize))); 168 169 slen = sizeof(pingee); 170 succ = 0; 171 for (loop = 0; loop < loops; loop++) { 172 RL(rump_sys_fcntl(s, F_SETFL, x)); 173 icmp->icmp_seq = htons(loop); 174 icmp->icmp_cksum = 0; 175 icmp->icmp_cksum = in_cksum(icmp, pktsize); 176 RL(rump_sys_sendto(s, icmp, pktsize, 0, 177 (struct sockaddr *)&dst, sizeof(dst))); 178 179 RL(rump_sys_fcntl(s, F_SETFL, xnon)); 180 while ((n = rump_sys_recvfrom(s, recvbuf, sizeof(recvbuf), 0, 181 (struct sockaddr *)&pingee, &slen)) > 0) { 182 succ++; 183 } 184 if (n == -1 && errno == EAGAIN) 185 continue; 186 atf_tc_fail_errno("recv failed"); 187 } 188 189 rump_sys_close(s); 190 return succ; 191 } 192 193 #define LOOPS 10000 194 195 ATF_TC_BODY(floodping, tc) 196 { 197 char ifname[IFNAMSIZ]; 198 pid_t cpid; 199 int succ; 200 201 cpid = fork(); 202 rump_init(); 203 netcfg_rump_makeshmif("thank-you-driver-for-getting-me-here", ifname); 204 205 switch (cpid) { 206 case -1: 207 atf_tc_fail_errno("fork failed"); 208 case 0: 209 netcfg_rump_if(ifname, "1.1.1.10", "255.255.255.0"); 210 pause(); 211 break; 212 default: 213 break; 214 } 215 216 netcfg_rump_if(ifname, "1.1.1.20", "255.255.255.0"); 217 218 succ = doping("1.1.1.10", LOOPS, 56); 219 printf("got %d/%d\n", succ, LOOPS); 220 221 kill(cpid, SIGKILL); 222 } 223 224 ATF_TC(floodping2); 225 ATF_TC_HEAD(floodping2, tc) 226 { 227 228 atf_tc_set_md_var(tc, "descr", "two hosts floodpinging each other"); 229 } 230 231 ATF_TC_BODY(floodping2, tc) 232 { 233 char ifname[IFNAMSIZ]; 234 pid_t cpid; 235 int succ; 236 237 cpid = fork(); 238 rump_init(); 239 netcfg_rump_makeshmif("floodping2", ifname); 240 241 switch (cpid) { 242 case -1: 243 atf_tc_fail_errno("fork failed"); 244 case 0: 245 netcfg_rump_if(ifname, "1.1.1.10", "255.255.255.0"); 246 succ = doping("1.1.1.20", LOOPS, 56); 247 break; 248 default: 249 netcfg_rump_if(ifname, "1.1.1.20", "255.255.255.0"); 250 succ = doping("1.1.1.10", LOOPS, 56); 251 break; 252 } 253 254 printf("got %d/%d\n", succ, LOOPS); 255 } 256 257 ATF_TC(pingsize); 258 ATF_TC_HEAD(pingsize, tc) 259 { 260 261 atf_tc_set_md_var(tc, "descr", "ping with packets min <= size <= max"); 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, "timeout", "2"); 311 } 312 313 ATF_TC_BODY(ping_of_death, tc) 314 { 315 char data[1500]; 316 struct sockaddr_in dst; 317 struct ip *ip; 318 struct icmp *icmp; 319 char ifname[IFNAMSIZ]; 320 pid_t cpid; 321 size_t tot, frag; 322 int s, x, loop; 323 324 cpid = fork(); 325 rump_init(); 326 netcfg_rump_makeshmif("jippikaiee", ifname); 327 328 switch (cpid) { 329 case -1: 330 atf_tc_fail_errno("fork failed"); 331 case 0: 332 /* wait until we receive a too long IP packet */ 333 for (loop = 0;; loop++) { 334 uint64_t ipstat[IP_NSTATS]; 335 size_t arglen; 336 int mib[4]; 337 338 if (loop == 1) 339 netcfg_rump_if(ifname, 340 "1.1.1.10", "255.255.255.0"); 341 342 mib[0] = CTL_NET; 343 mib[1] = PF_INET; 344 mib[2] = IPPROTO_IP; 345 mib[3] = IPCTL_STATS; 346 347 arglen = sizeof(ipstat); 348 RL(rump_sys___sysctl(mib, 4, &ipstat, &arglen, 349 NULL, 0)); 350 if (loop == 0 && ipstat[IP_STAT_TOOLONG] != 0) 351 _exit(1); 352 if (ipstat[IP_STAT_TOOLONG]) 353 break; 354 usleep(10000); 355 } 356 357 _exit(0); 358 break; 359 default: 360 break; 361 } 362 363 netcfg_rump_if(ifname, "1.1.1.20", "255.255.255.0"); 364 365 RL(s = rump_sys_socket(PF_INET, SOCK_RAW, 0)); 366 x = 1; 367 RL(rump_sys_setsockopt(s, IPPROTO_IP, IP_HDRINCL, &x, sizeof(x))); 368 369 memset(&dst, 0, sizeof(dst)); 370 dst.sin_len = sizeof(dst); 371 dst.sin_family = AF_INET; 372 dst.sin_addr.s_addr = inet_addr("1.1.1.10"); 373 374 /* construct packet */ 375 memset(data, 0, sizeof(data)); 376 ip = (struct ip *)data; 377 ip->ip_v = 4; 378 ip->ip_hl = sizeof(*ip) >> 2; 379 ip->ip_p = IPPROTO_ICMP; 380 ip->ip_ttl = IPDEFTTL; 381 ip->ip_dst = dst.sin_addr; 382 ip->ip_id = 1234; 383 384 icmp = (struct icmp *)(ip + 1); 385 icmp->icmp_type = ICMP_ECHO; 386 icmp->icmp_cksum = in_cksum(icmp, sizeof(*icmp)); 387 388 for (;;) { 389 int status; 390 391 /* resolve arp before sending raw stuff */ 392 netcfg_rump_pingtest("1.1.1.10", 1); 393 394 for (tot = 0; 395 tot < 65538 - sizeof(*ip); 396 tot += (frag - sizeof(*ip))) { 397 frag = MIN(65538 - tot, sizeof(data)); 398 ip->ip_off = tot >> 3; 399 assert((size_t)ip->ip_off << 3 == tot); 400 ip->ip_len = frag; 401 402 if (frag == sizeof(data)) { 403 ip->ip_off |= IP_MF; 404 } 405 406 RL(rump_sys_sendto(s, data, frag, 0, 407 (struct sockaddr *)&dst, sizeof(dst))); 408 } 409 if (waitpid(-1, &status, WNOHANG) > 0) { 410 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) 411 break; 412 atf_tc_fail("child did not exit clean"); 413 } 414 415 usleep(10000); 416 } 417 } 418 419 ATF_TP_ADD_TCS(tp) 420 { 421 422 ATF_TP_ADD_TC(tp, simpleping); 423 ATF_TP_ADD_TC(tp, floodping); 424 ATF_TP_ADD_TC(tp, floodping2); 425 ATF_TP_ADD_TC(tp, pingsize); 426 ATF_TP_ADD_TC(tp, ping_of_death); 427 428 return atf_no_error(); 429 } 430