VirtualBox

source: vbox/trunk/src/libs/libslirp-4.8.0/test/pingtest.c@ 106842

Last change on this file since 106842 was 105533, checked in by vboxsync, 6 months ago

libs/libslirp: merged our changes into libslirp 4.8.0. enabled updated version. bugref:10268

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.7 KB
Line 
1/* SPDX-License-Identifier: BSD-3-Clause */
2/*
3 * Copyright (c) 2021-2022 Samuel Thibault
4 */
5
6/*
7 * This simple test configures slirp and tries to ping it
8 *
9 * Note: to make this example actually be able to use the outside world, you
10 * need to either
11 * - run as root
12 * - set /proc/sys/net/ipv4/ping_group_range to allow sending ICMP echo requests
13 * - run a UDP echo server on the target
14 */
15
16#include <stdio.h>
17#include <stdlib.h>
18#include <time.h>
19#include <assert.h>
20
21#include "libslirp.h"
22
23//#define _WIN32
24#ifdef _WIN32
25//#include <sys/select.h>
26#include <winsock2.h>
27static int slirp_inet_aton(const char *cp, struct in_addr *ia)
28{
29 uint32_t addr = inet_addr(cp);
30 if (addr == 0xffffffff) {
31 return 0;
32 }
33 ia->s_addr = addr;
34 return 1;
35}
36#define inet_aton slirp_inet_aton
37#else
38#include <sys/socket.h>
39#include <arpa/inet.h>
40#include <poll.h>
41#endif
42
43/* Dumb simulation tick: 100ms */
44#define TICK 100
45
46static Slirp *slirp;
47static bool done;
48static int64_t mytime;
49
50/* Print a frame for debugging */
51static void print_frame(const uint8_t *data, size_t len) {
52 int i;
53
54 printf("\ngot packet size %zd:\n", len);
55 for (i = 0; i < len; i++) {
56 if (i && i % 16 == 0)
57 printf("\n");
58 printf("%s%02x", i % 16 ? " " : "", data[i]);
59 }
60 if (len % 16 != 0)
61 printf("\n");
62 printf("\n");
63}
64
65/* Classical 16bit checksum */
66static void checksum(uint8_t *data, size_t size, uint8_t *cksum) {
67 uint32_t sum = 0;
68 int i;
69
70 cksum[0] = 0;
71 cksum[1] = 0;
72
73 for (i = 0; i+1 < size; i += 2)
74 sum += (((uint16_t) data[i]) << 8) + data[i+1];
75 if (i < size) /* Odd number of bytes */
76 sum += ((uint16_t) data[i]) << 8;
77
78 sum = (sum & 0xffff) + (sum >> 16);
79 sum = (sum & 0xffff) + (sum >> 16);
80 sum = ~sum;
81
82 cksum[0] = sum >> 8;
83 cksum[1] = sum;
84}
85
86/* This is called when receiving a packet from the virtual network, for the
87 * guest */
88static slirp_ssize_t send_packet(const void *buf, size_t len, void *opaque) {
89 const uint8_t *data = buf;
90
91 assert(len >= 14);
92
93 if (data[12] == 0x86 &&
94 data[13] == 0xdd) {
95 /* Ignore IPv6 */
96 return len;
97 }
98
99 print_frame(data, len);
100
101 if (data[12] == 0x08 &&
102 data[13] == 0x06) {
103 /* ARP */
104 /* We expect receiving an ARP request for our address */
105
106 /* Ethernet address type */
107 assert(data[14] == 0x00);
108 assert(data[15] == 0x01);
109
110 /* IPv4 address type */
111 assert(data[16] == 0x08);
112 assert(data[17] == 0x00);
113
114 /* Ethernet addresses are 6 bytes long */
115 assert(data[18] == 0x06);
116
117 /* IPv4 addresses are 4 bytes long */
118 assert(data[19] == 0x04);
119
120 /* Opcode: ARP request */
121 assert(data[20] == 0x00);
122 assert(data[21] == 0x01);
123
124 /* Ok, reply! */
125 uint8_t myframe[] = {
126 /*** Ethernet ***/
127 /* dst */
128 0x52, 0x55, 0x0a, 0x00, 0x02, 0x02,
129 /* src */
130 0x52, 0x55, 0x0a, 0x00, 0x02, 0x0e,
131 /* Type: ARP */
132 0x08, 0x06,
133
134 /* ether, IPv4, */
135 0x00, 0x01, 0x08, 0x00,
136 /* elen, IPlen */
137 0x06, 0x04,
138 /* ARP reply */
139 0x00, 0x02,
140
141 /* Our ethernet address */
142 0x52, 0x55, 0x0a, 0x00, 0x02, 0x0e,
143 /* Our IP address */
144 0x0a, 0x00, 0x02, 0x0e,
145
146 /* Host ethernet address */
147 0x52, 0x55, 0x0a, 0x00, 0x02, 0x02,
148 /* Host IP address */
149 0x0a, 0x00, 0x02, 0x02,
150 };
151
152 slirp_input(slirp, myframe, sizeof(myframe));
153 }
154
155 if (data[12] == 0x08 &&
156 data[13] == 0x00) {
157 /* IPv4 */
158 assert(len >= 14 + 20);
159
160 /* We expect receiving the ICMP echo reply for our echo request */
161
162 /* IPv + hlen */
163 assert(data[14] == 0x45);
164
165 /* proto: ICMP */
166 assert(data[23] == 0x01);
167
168 /* ICMP */
169 assert(len >= 14 + 20 + 8 + 4);
170
171 /* ICMP type: reply */
172 assert(data[34] == 0x00);
173
174 /* Check the data */
175 assert(data[42] == 0xde);
176 assert(data[43] == 0xad);
177 assert(data[44] == 0xbe);
178 assert(data[45] == 0xef);
179
180 /* Got the answer! */
181 printf("got it!\n");
182 done = 1;
183 }
184
185 return len;
186}
187
188static void guest_error(const char *msg, void *opaque) {
189 printf("guest error %s\n", msg);
190}
191
192
193/*
194 * Dumb timer implementation
195 */
196static int64_t clock_get_ns(void *opaque) {
197 return mytime;
198}
199
200struct timer {
201 SlirpTimerId id;
202 void *cb_opaque;
203 int64_t expire;
204 struct timer *next;
205};
206
207static struct timer *timer_queue;
208
209static void *timer_new_opaque(SlirpTimerId id, void *cb_opaque, void *opaque) {
210 struct timer *new_timer = malloc(sizeof(*new_timer));
211 new_timer->id = id;
212 new_timer->cb_opaque = cb_opaque;
213 new_timer->next = NULL;
214 return new_timer;
215}
216
217static void timer_free(void *_timer, void *opaque) {
218 struct timer *timer = _timer;
219 struct timer **t;
220
221 for (t = &timer_queue; *t != NULL; *t = (*t)->next) {
222 if (*t == timer) {
223 /* Not expired yet, drop it */
224 *t = timer->next;
225 break;
226 }
227 }
228
229 free(timer);
230}
231
232static void timer_mod(void *_timer, int64_t expire_time, void *opaque) {
233 struct timer *timer = _timer;
234 struct timer **t;
235
236 timer->expire = expire_time * 1000 * 1000;
237
238 for (t = &timer_queue; *t != NULL; *t = (*t)->next) {
239 if (expire_time < (*t)->expire)
240 break;
241 }
242
243 timer->next = *t;
244 *t = timer;
245}
246
247static void timer_check(Slirp *slirp) {
248 while (timer_queue && timer_queue->expire <= mytime)
249 {
250 struct timer *t = timer_queue;
251 printf("handling %p at time %lu\n",
252 t, (unsigned long) timer_queue->expire);
253 timer_queue = t->next;
254 slirp_handle_timer(slirp, t->id, t->cb_opaque);
255 }
256}
257
258static uint32_t timer_timeout(void) {
259 if (timer_queue)
260 {
261 uint32_t timeout = (timer_queue->expire - mytime) / (1000 * 1000);
262 if (timeout < TICK)
263 return timeout;
264 }
265
266 return TICK;
267}
268
269
270/*
271 * Dumb polling implementation
272 */
273static int npoll;
274static void register_poll_fd(int fd, void *opaque) {
275 /* We might want to prepare for polling on fd */
276 npoll++;
277}
278
279static void unregister_poll_fd(int fd, void *opaque) {
280 /* We might want to clear polling on fd */
281 npoll--;
282}
283
284static void notify(void *opaque) {
285 /* No need for this in single-thread case */
286}
287
288#ifdef _WIN32
289/* select() variant */
290static fd_set readfds, writefds, exceptfds;
291static int maxfd;
292static int add_poll_cb(int fd, int events, void *opaque)
293{
294 if (events & SLIRP_POLL_IN)
295 FD_SET(fd, &readfds);
296 if (events & SLIRP_POLL_OUT)
297 FD_SET(fd, &writefds);
298 if (events & SLIRP_POLL_PRI)
299 FD_SET(fd, &exceptfds);
300 if (maxfd < fd)
301 maxfd = fd;
302 return fd;
303}
304
305static int get_revents_cb(int idx, void *opaque)
306{
307 int event = 0;
308 if (FD_ISSET(idx, &readfds))
309 event |= SLIRP_POLL_IN;
310 if (FD_ISSET(idx, &writefds))
311 event |= SLIRP_POLL_OUT;
312 if (FD_ISSET(idx, &exceptfds))
313 event |= SLIRP_POLL_PRI;
314 return event;
315}
316
317static void dopoll(uint32_t timeout) {
318 int err;
319 FD_ZERO(&readfds);
320 FD_ZERO(&writefds);
321 FD_ZERO(&exceptfds);
322 maxfd = 0;
323
324 slirp_pollfds_fill(slirp, &timeout, add_poll_cb, NULL);
325 printf("we will use timeout %u\n", (unsigned) timeout);
326
327 struct timeval tv = {
328 .tv_sec = timeout / 1000,
329 .tv_usec = (timeout % 1000) * 1000,
330 };
331 err = select(maxfd+1, &readfds, &writefds, &exceptfds, &tv);
332
333 slirp_pollfds_poll(slirp, err < 0, get_revents_cb, NULL);
334}
335#else
336/* poll() variant */
337static struct pollfd *fds;
338static int cur_poll;
339static int add_poll_cb(int fd, int events, void *opaque)
340{
341 short poll_events = 0;
342
343 assert(cur_poll < npoll);
344 fds[cur_poll].fd = fd;
345
346 if (events & SLIRP_POLL_IN)
347 poll_events |= POLLIN;
348 if (events & SLIRP_POLL_OUT)
349 poll_events |= POLLOUT;
350 if (events & SLIRP_POLL_PRI)
351 poll_events |= POLLPRI;
352 fds[cur_poll].events = poll_events;
353
354 return cur_poll++;
355}
356
357static int get_revents_cb(int idx, void *opaque)
358{
359 return fds[idx].revents;
360}
361
362static void dopoll(uint32_t timeout) {
363 int err;
364 fds = malloc(sizeof(*fds) * npoll);
365 cur_poll = 0;
366
367 slirp_pollfds_fill(slirp, &timeout, add_poll_cb, NULL);
368 printf("we will use timeout %u\n", (unsigned) timeout);
369
370 err = poll(fds, cur_poll, timeout);
371
372 slirp_pollfds_poll(slirp, err < 0, get_revents_cb, NULL);
373
374 free(fds);
375}
376#endif
377
378
379static struct SlirpCb callbacks = {
380 .send_packet = send_packet,
381 .guest_error = guest_error,
382 .clock_get_ns = clock_get_ns,
383 .timer_new_opaque = timer_new_opaque,
384 .timer_free = timer_free,
385 .timer_mod = timer_mod,
386 .register_poll_fd = register_poll_fd,
387 .unregister_poll_fd = unregister_poll_fd,
388 .notify = notify,
389};
390
391
392int main(int argc, char *argv[]) {
393 SlirpConfig config = {
394 .version = 4,
395 .restricted = false,
396 .in_enabled = true,
397 .vnetwork.s_addr = htonl(0x0a000200),
398 .vnetmask.s_addr = htonl(0xffffff00),
399 .vhost.s_addr = htonl(0x0a000202),
400 .vdhcp_start.s_addr = htonl(0x0a00020f),
401 .vnameserver.s_addr = htonl(0x0a000203),
402 .disable_host_loopback = false,
403 .enable_emu = false,
404 .disable_dns = false,
405 };
406 uint32_t timeout = 0;
407
408 printf("Slirp version %s\n", slirp_version_string());
409
410#if !defined(_WIN32)
411 inet_pton(AF_INET6, "fec0::", &config.vprefix_addr6);
412 config.vprefix_len = 64;
413 config.vhost6 = config.vprefix_addr6;
414 config.vhost6.s6_addr[15] = 2;
415 config.vnameserver6 = config.vprefix_addr6;
416 config.vnameserver6.s6_addr[15] = 2;
417 config.in6_enabled = true,
418#endif
419
420 slirp = slirp_new(&config, &callbacks, NULL);
421
422 /* Send echo request */
423 uint8_t myframe[] = {
424 /*** Ethernet ***/
425 /* dst */
426 0x52, 0x55, 0x0a, 0x00, 0x02, 0x02,
427 /* src */
428 0x52, 0x55, 0x0a, 0x00, 0x02, 0x0e,
429 /* Type: IPv4 */
430 0x08, 0x00,
431
432 /*** IPv4 ***/
433 /* vhl,tos, len */
434 0x45, 0x00, 0x00, 0x20,
435 /* id, off (DF) */
436 0x68, 0xd7, 0x40, 0x00,
437 /* ttl,pro, cksum */
438 0x40, 0x01, 0x00, 0x00,
439 /* src */
440 0x0a, 0x00, 0x02, 0x0e,
441 /* dst */
442 0x00, 0x00, 0x00, 0x00,
443
444 /*** ICMPv4 ***/
445 /* type, code, cksum */
446 0x08, 0x00, 0x00, 0x00,
447 /* id, seq */
448 0x01, 0xec, 0x00, 0x01,
449 /* data */
450 0xde, 0xad, 0xbe, 0xef,
451 };
452
453 struct in_addr in_addr = { .s_addr = htonl(0x0a000202) };
454 if (argc > 1) {
455 if (inet_aton(argv[1], &in_addr) == 0) {
456 printf("usage: %s [destination IPv4 address]\n", argv[0]);
457 exit(EXIT_FAILURE);
458 }
459 }
460 uint32_t addr = ntohl(in_addr.s_addr);
461 myframe[30] = addr >> 24;
462 myframe[31] = addr >> 16;
463 myframe[32] = addr >> 8;
464 myframe[33] = addr >> 0;
465
466 /* IPv4 header checksum */
467 checksum(&myframe[14], 20, &myframe[24]);
468 /* ICMP header checksum */
469 checksum(&myframe[34], 12, &myframe[36]);
470
471 slirp_input(slirp, myframe, sizeof(myframe));
472
473 /* Wait for echo reply */
474 while (!done) {
475 printf("time %lu\n", (unsigned long) mytime);
476
477 timer_check(slirp);
478 /* Here we make the virtual time wait like the real time, but we could
479 * make it wait differently */
480 timeout = timer_timeout();
481 printf("we wish timeout %u\n", (unsigned) timeout);
482
483 dopoll(timeout);
484
485 /* Fake that the tick elapsed */
486 mytime += TICK * 1000 * 1000;
487 }
488
489 slirp_cleanup(slirp);
490}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette