VirtualBox

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

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

slirp: another fix for windows hosts

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