VirtualBox

source: vbox/trunk/src/VBox/Devices/Network/slirp/slirp.c@ 14964

Last change on this file since 14964 was 14964, checked in by vboxsync, 16 years ago

slirp: code cosmetics for better readability (no semantics change)

  • Property svn:eol-style set to native
File size: 32.4 KB
Line 
1#include "slirp.h"
2#ifdef RT_OS_OS2
3# include <paths.h>
4#endif
5
6/* disable these counters for the final release */
7/* #define VBOX_WITHOUT_RELEASE_STATISTICS */
8
9#include <VBox/err.h>
10#include <VBox/pdmdrv.h>
11#include <iprt/assert.h>
12
13static const uint8_t special_ethaddr[6] = {
14 0x52, 0x54, 0x00, 0x12, 0x35, 0x00
15};
16
17#ifdef RT_OS_WINDOWS
18
19static int get_dns_addr_domain(PNATState pData, bool fVerbose,
20 struct in_addr *pdns_addr,
21 const char **ppszDomain)
22{
23 int rc = 0;
24 FIXED_INFO *FixedInfo = NULL;
25 ULONG BufLen;
26 DWORD ret;
27 IP_ADDR_STRING *pIPAddr;
28 struct in_addr tmp_addr;
29
30 FixedInfo = (FIXED_INFO *)GlobalAlloc(GPTR, sizeof(FIXED_INFO));
31 BufLen = sizeof(FIXED_INFO);
32
33 /** @todo: this API returns all DNS servers, no matter whether the
34 * corresponding network adapter is disabled or not. Maybe replace
35 * this by GetAdapterAddresses(), which is XP/Vista only though. */
36 if (ERROR_BUFFER_OVERFLOW == GetNetworkParams(FixedInfo, &BufLen))
37 {
38 if (FixedInfo)
39 {
40 GlobalFree(FixedInfo);
41 FixedInfo = NULL;
42 }
43 FixedInfo = GlobalAlloc(GPTR, BufLen);
44 }
45
46 if ((ret = GetNetworkParams(FixedInfo, &BufLen)) != ERROR_SUCCESS)
47 {
48 Log(("GetNetworkParams failed. ret = %08x\n", (u_int)ret ));
49 if (FixedInfo)
50 {
51 GlobalFree(FixedInfo);
52 FixedInfo = NULL;
53 }
54 rc = -1;
55 goto get_dns_prefix;
56 }
57
58 pIPAddr = &(FixedInfo->DnsServerList);
59 inet_aton(pIPAddr->IpAddress.String, &tmp_addr);
60 Log(("nat: DNS Servers:\n"));
61 if (fVerbose || pdns_addr->s_addr != tmp_addr.s_addr)
62 LogRel(("NAT: DNS address: %s\n", pIPAddr->IpAddress.String));
63 *pdns_addr = tmp_addr;
64
65 pIPAddr = FixedInfo -> DnsServerList.Next;
66 while (pIPAddr)
67 {
68 if (fVerbose)
69 LogRel(("NAT: ignored DNS address: %s\n", pIPAddr ->IpAddress.String));
70 pIPAddr = pIPAddr ->Next;
71 }
72 if (FixedInfo)
73 {
74 GlobalFree(FixedInfo);
75 FixedInfo = NULL;
76 }
77
78get_dns_prefix:
79 if (ppszDomain)
80 {
81 OSVERSIONINFO ver;
82 char szDnsDomain[256];
83 DWORD dwSize = sizeof(szDnsDomain);
84
85 *ppszDomain = NULL;
86 GetVersionEx(&ver);
87 if (ver.dwMajorVersion >= 5)
88 {
89 /* GetComputerNameEx exists in Windows versions starting with 2000. */
90 if (GetComputerNameEx(ComputerNameDnsDomain, szDnsDomain, &dwSize))
91 {
92 if (szDnsDomain[0])
93 {
94 /* Just non-empty strings are valid. */
95 *ppszDomain = RTStrDup(szDnsDomain);
96 if (pData->fPassDomain)
97 {
98 if (fVerbose)
99 LogRel(("NAT: passing domain name %s\n", szDnsDomain));
100 }
101 else
102 Log(("nat: ignoring domain %s\n", szDnsDomain));
103 }
104 }
105 else
106 Log(("nat: GetComputerNameEx failed (%d)\n", GetLastError()));
107 }
108 }
109 return rc;
110}
111
112#else
113
114static int get_dns_addr_domain(PNATState pData, bool fVerbose,
115 struct in_addr *pdns_addr,
116 const char **ppszDomain)
117{
118 char buff[512];
119 char buff2[256];
120 FILE *f;
121 int found = 0;
122 struct in_addr tmp_addr;
123
124#ifdef RT_OS_OS2
125 /* Try various locations. */
126 char *etc = getenv("ETC");
127 f = NULL;
128 if (etc)
129 {
130 snprintf(buff, sizeof(buff), "%s/RESOLV2", etc);
131 f = fopen(buff, "rt");
132 }
133 if (!f)
134 {
135 snprintf(buff, sizeof(buff), "%s/RESOLV2", _PATH_ETC);
136 f = fopen(buff, "rt");
137 }
138 if (!f)
139 {
140 snprintf(buff, sizeof(buff), "%s/resolv.conf", _PATH_ETC);
141 f = fopen(buff, "rt");
142 }
143#else
144 f = fopen("/etc/resolv.conf", "r");
145#endif
146 if (!f)
147 return -1;
148
149 if (ppszDomain)
150 *ppszDomain = NULL;
151 Log(("nat: DNS Servers:\n"));
152 while (fgets(buff, 512, f) != NULL)
153 {
154 if (sscanf(buff, "nameserver%*[ \t]%256s", buff2) == 1)
155 {
156 if (!inet_aton(buff2, &tmp_addr))
157 continue;
158 if (tmp_addr.s_addr == loopback_addr.s_addr)
159 tmp_addr = our_addr;
160 /* If it's the first one, set it to dns_addr */
161 if (!found)
162 {
163 if (fVerbose || pdns_addr->s_addr != tmp_addr.s_addr)
164 LogRel(("NAT: DNS address: %s\n", buff2));
165 *pdns_addr = tmp_addr;
166 }
167 else
168 {
169 if (fVerbose)
170 LogRel(("NAT: ignored DNS address: %s\n", buff2));
171 }
172 found++;
173 }
174 if ( ppszDomain
175 && (!strncmp(buff, "domain", 6) || !strncmp(buff, "search", 6)))
176 {
177 /* Domain name/search list present. Pick first entry */
178 if (*ppszDomain == NULL)
179 {
180 char *tok;
181 char *saveptr;
182 tok = strtok_r(&buff[6], " \t\n", &saveptr);
183 if (tok)
184 {
185 *ppszDomain = RTStrDup(tok);
186 if (pData->fPassDomain)
187 {
188 if (fVerbose)
189 LogRel(("NAT: passing domain name %s\n", tok));
190 }
191 else
192 Log(("nat: ignoring domain %s\n", tok));
193 }
194 }
195 }
196 }
197 fclose(f);
198 if (!found)
199 return -1;
200 return 0;
201}
202
203#endif
204
205int get_dns_addr(PNATState pData, struct in_addr *pdns_addr)
206{
207 return get_dns_addr_domain(pData, false, pdns_addr, NULL);
208}
209
210int slirp_init(PNATState *ppData, const char *pszNetAddr, uint32_t u32Netmask,
211 bool fPassDomain, const char *pszTFTPPrefix,
212 const char *pszBootFile, void *pvUser)
213{
214 int fNATfailed = 0;
215 PNATState pData = malloc(sizeof(NATState));
216 *ppData = pData;
217 if (!pData)
218 return VERR_NO_MEMORY;
219 if (u32Netmask & 0x1f)
220 /* CTL is x.x.x.15, bootp passes up to 16 IPs (15..31) */
221 return VERR_INVALID_PARAMETER;
222 memset(pData, '\0', sizeof(NATState));
223 pData->fPassDomain = fPassDomain;
224 pData->pvUser = pvUser;
225#if ARCH_BITS == 64 && !defined(VBOX_WITH_BSD_REASS)
226 pData->cpvHashUsed = 1;
227#endif
228 tftp_prefix = pszTFTPPrefix;
229 bootp_filename = pszBootFile;
230 pData->netmask = u32Netmask;
231
232#ifdef RT_OS_WINDOWS
233 {
234 WSADATA Data;
235 WSAStartup(MAKEWORD(2,0), &Data);
236 }
237# if defined(VBOX_WITH_SIMPLIFIED_SLIRP_SYNC)
238 pData->phEvents[VBOX_SOCKET_EVENT_INDEX] = CreateEvent(NULL, FALSE, FALSE, NULL);
239# endif
240#endif
241
242 Assert(sizeof(struct ip) == 20);
243 link_up = 1;
244
245 debug_init();
246 if_init(pData);
247 ip_init(pData);
248
249 /* Initialise mbufs *after* setting the MTU */
250 m_init(pData);
251
252 /* set default addresses */
253 inet_aton("127.0.0.1", &loopback_addr);
254 inet_aton("127.0.0.1", &dns_addr);
255
256 if (get_dns_addr_domain(pData, true, &dns_addr, &pData->pszDomain) < 0)
257 fNATfailed = 1;
258
259 inet_aton(pszNetAddr, &special_addr);
260 alias_addr.s_addr = special_addr.s_addr | htonl(CTL_ALIAS);
261 getouraddr(pData);
262 return fNATfailed ? VINF_NAT_DNS : VINF_SUCCESS;
263}
264
265/**
266 * Statistics counters.
267 */
268void slirp_register_timers(PNATState pData, PPDMDRVINS pDrvIns)
269{
270#ifndef VBOX_WITHOUT_RELEASE_STATISTICS
271 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatFill, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS,
272 STAMUNIT_TICKS_PER_CALL, "Profiling slirp fills", "/Drivers/NAT%d/Fill", pDrvIns->iInstance);
273 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatPoll, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS,
274 STAMUNIT_TICKS_PER_CALL, "Profiling slirp polls", "/Drivers/NAT%d/Poll", pDrvIns->iInstance);
275 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatFastTimer, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS,
276 STAMUNIT_TICKS_PER_CALL, "Profiling slirp fast timer", "/Drivers/NAT%d/TimerFast", pDrvIns->iInstance);
277 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatSlowTimer, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS,
278 STAMUNIT_TICKS_PER_CALL, "Profiling slirp slow timer", "/Drivers/NAT%d/TimerSlow", pDrvIns->iInstance);
279 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatTCP, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS,
280 STAMUNIT_COUNT, "TCP sockets", "/Drivers/NAT%d/SockTCP", pDrvIns->iInstance);
281 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatTCPHot, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS,
282 STAMUNIT_COUNT, "TCP sockets active", "/Drivers/NAT%d/SockTCPHot", pDrvIns->iInstance);
283 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatUDP, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS,
284 STAMUNIT_COUNT, "UDP sockets", "/Drivers/NAT%d/SockUDP", pDrvIns->iInstance);
285 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatUDPHot, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS,
286 STAMUNIT_COUNT, "UDP sockets active", "/Drivers/NAT%d/SockUDPHot", pDrvIns->iInstance);
287#endif /* VBOX_WITHOUT_RELEASE_STATISTICS */
288}
289
290/**
291 * Marks the link as up, making it possible to establish new connections.
292 */
293void slirp_link_up(PNATState pData)
294{
295 link_up = 1;
296}
297
298/**
299 * Marks the link as down and cleans up the current connections.
300 */
301void slirp_link_down(PNATState pData)
302{
303 struct socket *so;
304
305 while ((so = tcb.so_next) != &tcb)
306 {
307 if (so->so_state & SS_NOFDREF || so->s == -1)
308 sofree(pData, so);
309 else
310 tcp_drop(pData, sototcpcb(so), 0);
311 }
312
313 while ((so = udb.so_next) != &udb)
314 udp_detach(pData, so);
315
316 link_up = 0;
317}
318
319/**
320 * Terminates the slirp component.
321 */
322void slirp_term(PNATState pData)
323{
324 if (pData->pszDomain)
325 RTStrFree((char *)(void *)pData->pszDomain);
326
327#if ARCH_BITS == 64 && !defined(VBOX_WITH_BSD_REASS)
328 LogRel(("NAT: cpvHashUsed=%RU32 cpvHashCollisions=%RU32 cpvHashInserts=%RU64 cpvHashDone=%RU64\n",
329 pData->cpvHashUsed, pData->cpvHashCollisions, pData->cpvHashInserts, pData->cpvHashDone));
330#endif
331
332 slirp_link_down(pData);
333#ifdef RT_OS_WINDOWS
334 WSACleanup();
335#endif
336#ifdef LOG_ENABLED
337 Log(("\n"
338 "NAT statistics\n"
339 "--------------\n"
340 "\n"));
341 ipstats(pData);
342 tcpstats(pData);
343 udpstats(pData);
344 icmpstats(pData);
345 mbufstats(pData);
346 sockstats(pData);
347 Log(("\n"
348 "\n"
349 "\n"));
350#endif
351 free(pData);
352}
353
354
355#define CONN_CANFSEND(so) (((so)->so_state & (SS_FCANTSENDMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED)
356#define CONN_CANFRCV(so) (((so)->so_state & (SS_FCANTRCVMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED)
357#define UPD_NFDS(x) if (nfds < (x)) nfds = (x)
358
359/*
360 * curtime kept to an accuracy of 1ms
361 */
362#ifdef RT_OS_WINDOWS
363static void updtime(PNATState pData)
364{
365 struct _timeb tb;
366
367 _ftime(&tb);
368 curtime = (u_int)tb.time * (u_int)1000;
369 curtime += (u_int)tb.millitm;
370}
371#else
372static void updtime(PNATState pData)
373{
374 gettimeofday(&tt, 0);
375
376 curtime = (u_int)tt.tv_sec * (u_int)1000;
377 curtime += (u_int)tt.tv_usec / (u_int)1000;
378
379 if ((tt.tv_usec % 1000) >= 500)
380 curtime++;
381}
382#endif
383
384void slirp_select_fill(PNATState pData, int *pnfds,
385 fd_set *readfds, fd_set *writefds, fd_set *xfds)
386{
387 struct socket *so, *so_next;
388 int nfds;
389#if defined(VBOX_WITH_SIMPLIFIED_SLIRP_SYNC) && defined(RT_OS_WINDOWS)
390 int rc;
391 int error;
392#endif
393#ifdef VBOX_WITH_BSD_REASS
394 int i;
395#endif /* VBOX_WITH_BSD_REASS */
396
397 STAM_REL_PROFILE_START(&pData->StatFill, a);
398
399 nfds = *pnfds;
400
401 /*
402 * First, TCP sockets
403 */
404 do_slowtimo = 0;
405 if (link_up)
406 {
407 /*
408 * *_slowtimo needs calling if there are IP fragments
409 * in the fragment queue, or there are TCP connections active
410 */
411#ifndef VBOX_WITH_BSD_REASS
412 do_slowtimo = ((tcb.so_next != &tcb)
413 || ((struct ipasfrag *)&ipq != u32_to_ptr(pData, ipq.next, struct ipasfrag *)));
414#else /* !VBOX_WITH_BSD_REASS */
415 /* XXX:
416 * triggering of fragment expiration should be the same but use new macroses
417 */
418 do_slowtimo = (tcb.so_next != &tcb);
419 if (!do_slowtimo)
420 {
421 for (i = 0; i < IPREASS_NHASH; i++)
422 {
423 if (!TAILQ_EMPTY(&ipq[i]))
424 {
425 do_slowtimo = 1;
426 break;
427 }
428 }
429 }
430#endif /* VBOX_WITH_BSD_REASS */
431
432 STAM_REL_COUNTER_RESET(&pData->StatTCP);
433 STAM_REL_COUNTER_RESET(&pData->StatTCPHot);
434
435 for (so = tcb.so_next; so != &tcb; so = so_next)
436 {
437 so_next = so->so_next;
438
439 STAM_REL_COUNTER_INC(&pData->StatTCP);
440
441 /*
442 * See if we need a tcp_fasttimo
443 */
444 if (time_fasttimo == 0 && so->so_tcpcb->t_flags & TF_DELACK)
445 time_fasttimo = curtime; /* Flag when we want a fasttimo */
446
447 /*
448 * NOFDREF can include still connecting to local-host,
449 * newly socreated() sockets etc. Don't want to select these.
450 */
451 if (so->so_state & SS_NOFDREF || so->s == -1)
452 continue;
453
454 /*
455 * Set for reading sockets which are accepting
456 */
457 if (so->so_state & SS_FACCEPTCONN)
458 {
459 STAM_REL_COUNTER_INC(&pData->StatTCPHot);
460#if !defined(VBOX_WITH_SIMPLIFIED_SLIRP_SYNC) || !defined(RT_OS_WINDOWS)
461 FD_SET(so->s, readfds);
462 UPD_NFDS(so->s);
463#else
464engage_event:
465 rc = WSAEventSelect(so->s, VBOX_SOCKET_EVENT, FD_ALL_EVENTS);
466 if (rc == SOCKET_ERROR)
467 {
468 /* This should not happen */
469 error = WSAGetLastError();
470 LogRel(("WSAEventSelector (TCP) error %d (so=%x, socket=%s, event=%x)\n",
471 error, so, so->s, VBOX_SOCKET_EVENT));
472 }
473#endif
474 continue;
475 }
476
477 /*
478 * Set for writing sockets which are connecting
479 */
480 if (so->so_state & SS_ISFCONNECTING)
481 {
482 STAM_REL_COUNTER_INC(&pData->StatTCPHot);
483#if !defined(VBOX_WITH_SIMPLIFIED_SLIRP_SYNC) || !defined(RT_OS_WINDOWS)
484 FD_SET(so->s, writefds);
485 UPD_NFDS(so->s);
486 continue;
487#else
488 goto engage_event;
489#endif
490 }
491
492 /*
493 * Set for writing if we are connected, can send more, and
494 * we have something to send
495 */
496 if (CONN_CANFSEND(so) && so->so_rcv.sb_cc)
497 {
498 STAM_REL_COUNTER_INC(&pData->StatTCPHot);
499#if !defined(VBOX_WITH_SIMPLIFIED_SLIRP_SYNC) || !defined(RT_OS_WINDOWS)
500 FD_SET(so->s, writefds);
501 UPD_NFDS(so->s);
502#else
503 goto engage_event;
504#endif
505 }
506
507 /*
508 * Set for reading (and urgent data) if we are connected, can
509 * receive more, and we have room for it XXX /2 ?
510 */
511 if (CONN_CANFRCV(so) && (so->so_snd.sb_cc < (so->so_snd.sb_datalen/2)))
512 {
513 STAM_REL_COUNTER_INC(&pData->StatTCPHot);
514#if !defined(VBOX_WITH_SIMPLIFIED_SLIRP_SYNC) || !defined(RT_OS_WINDOWS)
515 FD_SET(so->s, readfds);
516 FD_SET(so->s, xfds);
517 UPD_NFDS(so->s);
518#else
519 goto engage_event;
520#endif
521 }
522 }
523
524 /*
525 * UDP sockets
526 */
527 STAM_REL_COUNTER_RESET(&pData->StatUDP);
528 STAM_REL_COUNTER_RESET(&pData->StatUDPHot);
529
530 for (so = udb.so_next; so != &udb; so = so_next)
531 {
532 so_next = so->so_next;
533
534 STAM_REL_COUNTER_INC(&pData->StatUDP);
535
536 /*
537 * See if it's timed out
538 */
539 if (so->so_expire)
540 {
541 if (so->so_expire <= curtime)
542 {
543 udp_detach(pData, so);
544 continue;
545 }
546 else
547 do_slowtimo = 1; /* Let socket expire */
548 }
549
550 /*
551 * When UDP packets are received from over the link, they're
552 * sendto()'d straight away, so no need for setting for writing
553 * Limit the number of packets queued by this session to 4.
554 * Note that even though we try and limit this to 4 packets,
555 * the session could have more queued if the packets needed
556 * to be fragmented.
557 *
558 * (XXX <= 4 ?)
559 */
560 if ((so->so_state & SS_ISFCONNECTED) && so->so_queued <= 4)
561 {
562 STAM_REL_COUNTER_INC(&pData->StatUDPHot);
563#if !defined(VBOX_WITH_SIMPLIFIED_SLIRP_SYNC) || !defined(RT_OS_WINDOWS)
564 FD_SET(so->s, readfds);
565 UPD_NFDS(so->s);
566#else
567 rc = WSAEventSelect(so->s, VBOX_SOCKET_EVENT, FD_ALL_EVENTS);
568 if (rc == SOCKET_ERROR)
569 {
570 /* This should not happen */
571 error = WSAGetLastError();
572 LogRel(("WSAEventSelector (UDP) error %d (so=%x, socket=%s, event=%x)\n",
573 error, so, so->s, VBOX_SOCKET_EVENT));
574 }
575#endif
576 }
577 }
578 }
579
580#if !defined(VBOX_WITH_SIMPLIFIED_SLIRP_SYNC) || !defined(RT_OS_WINDOWS)
581 *pnfds = nfds;
582#else
583 *pnfds = VBOX_EVENT_COUNT;
584#endif
585
586 STAM_REL_PROFILE_STOP(&pData->StatFill, a);
587}
588
589void slirp_select_poll(PNATState pData, fd_set *readfds, fd_set *writefds, fd_set *xfds)
590{
591 struct socket *so, *so_next;
592 int ret;
593#if defined(VBOX_WITH_SIMPLIFIED_SLIRP_SYNC) && defined(RT_OS_WINDOWS)
594 WSANETWORKEVENTS NetworkEvents;
595 int rc;
596 int error;
597#endif
598
599 STAM_REL_PROFILE_START(&pData->StatPoll, a);
600
601 /* Update time */
602 updtime(pData);
603
604 /*
605 * See if anything has timed out
606 */
607 if (link_up)
608 {
609 if (time_fasttimo && ((curtime - time_fasttimo) >= 2))
610 {
611 STAM_REL_PROFILE_START(&pData->StatFastTimer, a);
612 tcp_fasttimo(pData);
613 time_fasttimo = 0;
614 STAM_REL_PROFILE_STOP(&pData->StatFastTimer, a);
615 }
616 if (do_slowtimo && ((curtime - last_slowtimo) >= 499))
617 {
618 STAM_REL_PROFILE_START(&pData->StatSlowTimer, a);
619 ip_slowtimo(pData);
620 tcp_slowtimo(pData);
621 last_slowtimo = curtime;
622 STAM_REL_PROFILE_STOP(&pData->StatSlowTimer, a);
623 }
624 }
625#if defined(VBOX_WITH_SIMPLIFIED_SLIRP_SYNC) && defined(RT_OS_WINDOWS)
626 if (!readfds && !writefds && !xfds)
627 return; /* only timer update */
628#endif
629
630 /*
631 * Check sockets
632 */
633 if (link_up)
634 {
635 /*
636 * Check TCP sockets
637 */
638 for (so = tcb.so_next; so != &tcb; so = so_next)
639 {
640 so_next = so->so_next;
641
642 /*
643 * FD_ISSET is meaningless on these sockets
644 * (and they can crash the program)
645 */
646 if (so->so_state & SS_NOFDREF || so->s == -1)
647 continue;
648
649#if defined(VBOX_WITH_SIMPLIFIED_SLIRP_SYNC) && defined(RT_OS_WINDOWS)
650 rc = WSAEnumNetworkEvents(so->s, VBOX_SOCKET_EVENT, &NetworkEvents);
651 if (rc == SOCKET_ERROR)
652 {
653 error = WSAGetLastError();
654 LogRel(("WSAEnumNetworkEvents TCP error %d\n", error));
655 continue;
656 }
657#endif
658
659 /*
660 * Check for URG data
661 * This will soread as well, so no need to
662 * test for readfds below if this succeeds
663 */
664#if !defined(VBOX_WITH_SIMPLIFIED_SLIRP_SYNC) || !defined(RT_OS_WINDOWS)
665 if (FD_ISSET(so->s, xfds))
666#else
667 /* out-of-band data */
668 if ((NetworkEvents.lNetworkEvents & FD_OOB) && NetworkEvents.iErrorCode[FD_OOB_BIT] == 0)
669#endif
670 {
671 sorecvoob(pData, so);
672 }
673
674 /*
675 * Check sockets for reading
676 */
677#if !defined(VBOX_WITH_SIMPLIFIED_SLIRP_SYNC) || !defined(RT_OS_WINDOWS)
678 else if (FD_ISSET(so->s, readfds))
679#else
680 else if ((NetworkEvents.lNetworkEvents & FD_READ) && (NetworkEvents.iErrorCode[FD_READ_BIT] == 0))
681#endif
682 {
683 /*
684 * Check for incoming connections
685 */
686 if (so->so_state & SS_FACCEPTCONN)
687 {
688 tcp_connect(pData, so);
689#if defined(VBOX_WITH_SIMPLIFIED_SLIRP_SYNC) && defined(RT_OS_WINDOWS)
690 if (!NetworkEvents.lNetworkEvents & FD_CLOSE)
691#endif
692 continue;
693 }
694
695 ret = soread(pData, so);
696 /* Output it if we read something */
697 if (ret > 0)
698 tcp_output(pData, sototcpcb(so));
699 }
700
701#if defined(VBOX_WITH_SIMPLIFIED_SLIRP_SYNC) && defined(RT_OS_WINDOWS)
702 /*
703 * Check for FD_CLOSE events.
704 */
705 if (NetworkEvents.lNetworkEvents & FD_CLOSE)
706 {
707 /*
708 * drain the socket
709 */
710 for (;;)
711 {
712 ret = soread(pData, so);
713 if (ret > 0)
714 tcp_output(pData, sototcpcb(so));
715 else
716 break;
717 }
718 }
719#endif
720
721 /*
722 * Check sockets for writing
723 */
724#if !defined(VBOX_WITH_SIMPLIFIED_SLIRP_SYNC) || !defined(RT_OS_WINDOWS)
725 if (FD_ISSET(so->s, writefds))
726#else
727 if ((NetworkEvents.lNetworkEvents & FD_WRITE) && (NetworkEvents.iErrorCode[FD_WRITE_BIT] == 0))
728#endif
729 {
730 /*
731 * Check for non-blocking, still-connecting sockets
732 */
733 if (so->so_state & SS_ISFCONNECTING)
734 {
735 /* Connected */
736 so->so_state &= ~SS_ISFCONNECTING;
737
738 /*
739 * This should be probably guarded by PROBE_CONN too. Anyway,
740 * we disable it on OS/2 because the below send call returns
741 * EFAULT which causes the opened TCP socket to close right
742 * after it has been opened and connected.
743 */
744#ifndef RT_OS_OS2
745 ret = send(so->s, (const char *)&ret, 0, 0);
746 if (ret < 0)
747 {
748 /* XXXXX Must fix, zero bytes is a NOP */
749 if ( errno == EAGAIN
750 || errno == EWOULDBLOCK
751 || errno == EINPROGRESS
752 || errno == ENOTCONN)
753 continue;
754
755 /* else failed */
756 so->so_state = SS_NOFDREF;
757 }
758 /* else so->so_state &= ~SS_ISFCONNECTING; */
759#endif
760
761 /*
762 * Continue tcp_input
763 */
764 tcp_input(pData, (struct mbuf *)NULL, sizeof(struct ip), so);
765 /* continue; */
766 }
767 else
768 ret = sowrite(pData, so);
769 /*
770 * XXX If we wrote something (a lot), there could be the need
771 * for a window update. In the worst case, the remote will send
772 * a window probe to get things going again.
773 */
774 }
775
776 /*
777 * Probe a still-connecting, non-blocking socket
778 * to check if it's still alive
779 */
780#ifdef PROBE_CONN
781 if (so->so_state & SS_ISFCONNECTING)
782 {
783 ret = recv(so->s, (char *)&ret, 0, 0);
784
785 if (ret < 0)
786 {
787 /* XXX */
788 if ( errno == EAGAIN
789 || errno == EWOULDBLOCK
790 || errno == EINPROGRESS
791 || errno == ENOTCONN)
792 {
793 continue; /* Still connecting, continue */
794 }
795
796 /* else failed */
797 so->so_state = SS_NOFDREF;
798
799 /* tcp_input will take care of it */
800 }
801 else
802 {
803 ret = send(so->s, &ret, 0, 0);
804 if (ret < 0)
805 {
806 /* XXX */
807 if ( errno == EAGAIN
808 || errno == EWOULDBLOCK
809 || errno == EINPROGRESS
810 || errno == ENOTCONN)
811 {
812 continue;
813 }
814 /* else failed */
815 so->so_state = SS_NOFDREF;
816 }
817 else
818 so->so_state &= ~SS_ISFCONNECTING;
819
820 }
821 tcp_input((struct mbuf *)NULL, sizeof(struct ip),so);
822 } /* SS_ISFCONNECTING */
823#endif
824 }
825
826 /*
827 * Now UDP sockets.
828 * Incoming packets are sent straight away, they're not buffered.
829 * Incoming UDP data isn't buffered either.
830 */
831 for (so = udb.so_next; so != &udb; so = so_next)
832 {
833 so_next = so->so_next;
834
835#if defined(VBOX_WITH_SIMPLIFIED_SLIRP_SYNC) && defined(RT_OS_WINDOWS)
836 rc = WSAEnumNetworkEvents(so->s, VBOX_SOCKET_EVENT, &NetworkEvents);
837 if (rc == SOCKET_ERROR)
838 {
839 error = WSAGetLastError();
840 LogRel(("WSAEnumNetworkEvents TCP error %d\n", error));
841 continue;
842 }
843#endif
844#if !defined(VBOX_WITH_SIMPLIFIED_SLIRP_SYNC) || !defined(RT_OS_WINDOWS)
845 if (so->s != -1 && FD_ISSET(so->s, readfds))
846#else
847 if ((NetworkEvents.lNetworkEvents & FD_READ) && (NetworkEvents.iErrorCode[FD_READ_BIT] == 0))
848#endif
849 {
850 sorecvfrom(pData, so);
851 }
852 }
853 }
854
855 /*
856 * See if we can start outputting
857 */
858 if (if_queued && link_up)
859 if_start(pData);
860
861 STAM_REL_PROFILE_STOP(&pData->StatPoll, a);
862}
863
864#define ETH_ALEN 6
865#define ETH_HLEN 14
866
867#define ETH_P_IP 0x0800 /* Internet Protocol packet */
868#define ETH_P_ARP 0x0806 /* Address Resolution packet */
869
870#define ARPOP_REQUEST 1 /* ARP request */
871#define ARPOP_REPLY 2 /* ARP reply */
872
873struct ethhdr
874{
875 unsigned char h_dest[ETH_ALEN]; /* destination eth addr */
876 unsigned char h_source[ETH_ALEN]; /* source ether addr */
877 unsigned short h_proto; /* packet type ID field */
878};
879
880struct arphdr
881{
882 unsigned short ar_hrd; /* format of hardware address */
883 unsigned short ar_pro; /* format of protocol address */
884 unsigned char ar_hln; /* length of hardware address */
885 unsigned char ar_pln; /* length of protocol address */
886 unsigned short ar_op; /* ARP opcode (command) */
887
888 /*
889 * Ethernet looks like this : This bit is variable sized however...
890 */
891 unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */
892 unsigned char ar_sip[4]; /* sender IP address */
893 unsigned char ar_tha[ETH_ALEN]; /* target hardware address */
894 unsigned char ar_tip[4]; /* target IP address */
895};
896
897static
898void arp_input(PNATState pData, const uint8_t *pkt, int pkt_len)
899{
900 struct ethhdr *eh = (struct ethhdr *)pkt;
901 struct arphdr *ah = (struct arphdr *)(pkt + ETH_HLEN);
902 uint8_t arp_reply[ETH_HLEN + sizeof(struct arphdr)];
903 struct ethhdr *reh = (struct ethhdr *)arp_reply;
904 struct arphdr *rah = (struct arphdr *)(arp_reply + ETH_HLEN);
905 int ar_op;
906 struct ex_list *ex_ptr;
907 uint32_t htip = ntohl(*(uint32_t*)ah->ar_tip);
908
909 ar_op = ntohs(ah->ar_op);
910 switch(ar_op)
911 {
912 case ARPOP_REQUEST:
913 if ((htip & pData->netmask) == ntohl(special_addr.s_addr))
914 {
915 if ( (htip & ~pData->netmask) == CTL_DNS
916 || (htip & ~pData->netmask) == CTL_ALIAS)
917 goto arp_ok;
918 for (ex_ptr = exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next)
919 {
920 if ((htip & ~pData->netmask) == ex_ptr->ex_addr)
921 goto arp_ok;
922 }
923 return;
924 arp_ok:
925 /* XXX: make an ARP request to have the client address */
926 memcpy(client_ethaddr, eh->h_source, ETH_ALEN);
927
928 /* ARP request for alias/dns mac address */
929 memcpy(reh->h_dest, pkt + ETH_ALEN, ETH_ALEN);
930 memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 1);
931 reh->h_source[5] = ah->ar_tip[3];
932 reh->h_proto = htons(ETH_P_ARP);
933
934 rah->ar_hrd = htons(1);
935 rah->ar_pro = htons(ETH_P_IP);
936 rah->ar_hln = ETH_ALEN;
937 rah->ar_pln = 4;
938 rah->ar_op = htons(ARPOP_REPLY);
939 memcpy(rah->ar_sha, reh->h_source, ETH_ALEN);
940 memcpy(rah->ar_sip, ah->ar_tip, 4);
941 memcpy(rah->ar_tha, ah->ar_sha, ETH_ALEN);
942 memcpy(rah->ar_tip, ah->ar_sip, 4);
943 slirp_output(pData->pvUser, arp_reply, sizeof(arp_reply));
944 }
945 break;
946 default:
947 break;
948 }
949}
950
951void slirp_input(PNATState pData, const uint8_t *pkt, int pkt_len)
952{
953 struct mbuf *m;
954 int proto;
955
956 if (pkt_len < ETH_HLEN)
957 return;
958
959 proto = ntohs(*(uint16_t *)(pkt + 12));
960 switch(proto)
961 {
962 case ETH_P_ARP:
963 arp_input(pData, pkt, pkt_len);
964 break;
965 case ETH_P_IP:
966 /* Update time. Important if the network is very quiet, as otherwise
967 * the first outgoing connection gets an incorrect timestamp. */
968 updtime(pData);
969
970 m = m_get(pData);
971 if (!m)
972 return;
973 /* Note: we add to align the IP header */
974 if (M_FREEROOM(m) < pkt_len + 2)
975 {
976 m_inc(m, pkt_len + 2);
977 }
978 m->m_len = pkt_len + 2;
979 memcpy(m->m_data + 2, pkt, pkt_len);
980
981 m->m_data += 2 + ETH_HLEN;
982 m->m_len -= 2 + ETH_HLEN;
983
984 ip_input(pData, m);
985 break;
986 default:
987 break;
988 }
989}
990
991/* output the IP packet to the ethernet device */
992void if_encap(PNATState pData, const uint8_t *ip_data, int ip_data_len)
993{
994 uint8_t buf[1600];
995 struct ethhdr *eh = (struct ethhdr *)buf;
996
997 if (ip_data_len + ETH_HLEN > sizeof(buf))
998 return;
999
1000 memcpy(eh->h_dest, client_ethaddr, ETH_ALEN);
1001 memcpy(eh->h_source, special_ethaddr, ETH_ALEN - 1);
1002 /* XXX: not correct */
1003 eh->h_source[5] = CTL_ALIAS;
1004 eh->h_proto = htons(ETH_P_IP);
1005 memcpy(buf + sizeof(struct ethhdr), ip_data, ip_data_len);
1006 slirp_output(pData->pvUser, buf, ip_data_len + ETH_HLEN);
1007}
1008
1009int slirp_redir(PNATState pData, int is_udp, int host_port,
1010 struct in_addr guest_addr, int guest_port)
1011{
1012 if (is_udp)
1013 {
1014 if (!udp_listen(pData, htons(host_port), guest_addr.s_addr,
1015 htons(guest_port), 0))
1016 return -1;
1017 }
1018 else
1019 {
1020 if (!solisten(pData, htons(host_port), guest_addr.s_addr,
1021 htons(guest_port), 0))
1022 return -1;
1023 }
1024 return 0;
1025}
1026
1027int slirp_add_exec(PNATState pData, int do_pty, const char *args, int addr_low_byte,
1028 int guest_port)
1029{
1030 return add_exec(&exec_list, do_pty, (char *)args,
1031 addr_low_byte, htons(guest_port));
1032}
1033
1034void slirp_set_ethaddr(PNATState pData, const uint8_t *ethaddr)
1035{
1036 memcpy(client_ethaddr, ethaddr, ETH_ALEN);
1037}
1038
1039#if defined(VBOX_WITH_SIMPLIFIED_SLIRP_SYNC) && defined(RT_OS_WINDOWS)
1040HANDLE *slirp_get_events(PNATState pData)
1041{
1042 return pData->phEvents;
1043}
1044void slirp_register_external_event(PNATState pData, HANDLE hEvent, int index)
1045{
1046 pData->phEvents[index] = hEvent;
1047}
1048#endif
1049
1050unsigned int slirp_get_timeout_ms(PNATState pData)
1051{
1052 if (link_up)
1053 {
1054 if (time_fasttimo)
1055 return 2;
1056 if (do_slowtimo)
1057 return 500; /* see PR_SLOWHZ */
1058 }
1059 return 0;
1060}
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