VirtualBox

source: vbox/trunk/src/libs/libslirp-4.9.0/test/pingtest.c

Last change on this file was 109078, checked in by vboxsync, 2 weeks ago

libs/libslirp-4.9.0: Make it build on Linux, bugref:10268

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

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