VirtualBox

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

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

this is AssertCompile in the header

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