t_ping.c revision 1.13 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