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