Home | History | Annotate | Line # | Download | only in icmp
t_ping.c revision 1.12
      1 /*	$NetBSD: t_ping.c,v 1.12 2011/01/05 14:08:12 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.12 2011/01/05 14:08:12 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))
    161 		pktsize = sizeof(sndbuf);
    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