VirtualBox

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

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

Adapted versions of ip_reass, ip_freef and ip_slowtimo from FreeBSD (compilable on Linux, need some debugging)

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