VirtualBox

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

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

typo

  • Property svn:eol-style set to native
File size: 29.8 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
389 STAM_REL_PROFILE_START(&pData->StatFill, a);
390
391 nfds = *pnfds;
392 /*
393 * First, TCP sockets
394 */
395 do_slowtimo = 0;
396 if (link_up) {
397 /*
398 * *_slowtimo needs calling if there are IP fragments
399 * in the fragment queue, or there are TCP connections active
400 */
401 do_slowtimo = ((tcb.so_next != &tcb) ||
402 ((struct ipasfrag *)&ipq != u32_to_ptr(pData, ipq.next, struct ipasfrag *)));
403
404 STAM_REL_COUNTER_RESET(&pData->StatTCP);
405 STAM_REL_COUNTER_RESET(&pData->StatTCPHot);
406
407 for (so = tcb.so_next; so != &tcb; so = so_next) {
408 so_next = so->so_next;
409
410 STAM_REL_COUNTER_INC(&pData->StatTCP);
411
412 /*
413 * See if we need a tcp_fasttimo
414 */
415 if (time_fasttimo == 0 && so->so_tcpcb->t_flags & TF_DELACK)
416 time_fasttimo = curtime; /* Flag when we want a fasttimo */
417
418 /*
419 * NOFDREF can include still connecting to local-host,
420 * newly socreated() sockets etc. Don't want to select these.
421 */
422 if (so->so_state & SS_NOFDREF || so->s == -1)
423 continue;
424
425 /*
426 * Set for reading sockets which are accepting
427 */
428 if (so->so_state & SS_FACCEPTCONN) {
429 STAM_REL_COUNTER_INC(&pData->StatTCPHot);
430#if !defined(VBOX_WITH_SIMPLEFIED_SLIRP_SYNC) || !defined(RT_OS_WINDOWS)
431 FD_SET(so->s, readfds);
432 UPD_NFDS(so->s);
433#else
434 rc = WSAEventSelect(so->s, VBOX_SOCKET_EVENT, FD_READ|FD_WRITE|FD_ACCEPT|FD_CONNECT|FD_OOB);
435 if (rc == SOCKET_ERROR)
436 {
437socket_error:
438 error = WSAGetLastError();
439 LogRel(("WSAEventSelector error %d (so=%x, socket=%s, event=%x)\n", error, so, so->s, VBOX_SOCKET_EVENT));
440 }
441#endif
442 continue;
443 }
444
445 /*
446 * Set for writing sockets which are connecting
447 */
448 if (so->so_state & SS_ISFCONNECTING) {
449 STAM_REL_COUNTER_INC(&pData->StatTCPHot);
450#if !defined(VBOX_WITH_SIMPLEFIED_SLIRP_SYNC) || !defined(RT_OS_WINDOWS)
451 FD_SET(so->s, writefds);
452 UPD_NFDS(so->s);
453#else
454 rc = WSAEventSelect(so->s, VBOX_SOCKET_EVENT, FD_READ|FD_WRITE|FD_ACCEPT|FD_CONNECT|FD_OOB);
455 if (rc == SOCKET_ERROR)
456 goto socket_error;
457#endif
458 continue;
459 }
460
461 /*
462 * Set for writing if we are connected, can send more, and
463 * we have something to send
464 */
465 if (CONN_CANFSEND(so) && so->so_rcv.sb_cc) {
466 STAM_REL_COUNTER_INC(&pData->StatTCPHot);
467#if !defined(VBOX_WITH_SIMPLEFIED_SLIRP_SYNC) || !defined(RT_OS_WINDOWS)
468 FD_SET(so->s, writefds);
469 UPD_NFDS(so->s);
470#else
471 rc = WSAEventSelect(so->s, VBOX_SOCKET_EVENT, FD_READ|FD_WRITE|FD_ACCEPT|FD_CONNECT|FD_OOB);
472 if (rc == SOCKET_ERROR)
473 goto socket_error;
474 continue; /*XXX: we're using the widest mask for event*/
475#endif
476 }
477
478 /*
479 * Set for reading (and urgent data) if we are connected, can
480 * receive more, and we have room for it XXX /2 ?
481 */
482 if (CONN_CANFRCV(so) && (so->so_snd.sb_cc < (so->so_snd.sb_datalen/2))) {
483 STAM_REL_COUNTER_INC(&pData->StatTCPHot);
484#if !defined(VBOX_WITH_SIMPLEFIED_SLIRP_SYNC) || !defined(RT_OS_WINDOWS)
485 FD_SET(so->s, readfds);
486 FD_SET(so->s, xfds);
487 UPD_NFDS(so->s);
488#else
489 rc = WSAEventSelect(so->s, VBOX_SOCKET_EVENT, FD_OOB|FD_READ|FD_WRITE|FD_ACCEPT|FD_CONNECT);
490 if (rc == SOCKET_ERROR)
491 goto socket_error;
492 continue; /*XXX: we're using the widest mask for event*/
493#endif
494 }
495#if defined(VBOX_WITH_SIMPLEFIED_SLIRP_SYNC) && defined(RT_OS_WINDOWS)
496 rc = WSAEventSelect(so->s, NULL, 0);
497 if (rc == SOCKET_ERROR)
498 goto socket_error;
499#endif
500 }
501
502 /*
503 * UDP sockets
504 */
505 STAM_REL_COUNTER_RESET(&pData->StatUDP);
506 STAM_REL_COUNTER_RESET(&pData->StatUDPHot);
507
508 for (so = udb.so_next; so != &udb; so = so_next) {
509 so_next = so->so_next;
510
511 STAM_REL_COUNTER_INC(&pData->StatUDP);
512
513 /*
514 * See if it's timed out
515 */
516 if (so->so_expire) {
517 if (so->so_expire <= curtime) {
518 udp_detach(pData, so);
519 continue;
520 } else
521 do_slowtimo = 1; /* Let socket expire */
522 }
523
524 /*
525 * When UDP packets are received from over the
526 * link, they're sendto()'d straight away, so
527 * no need for setting for writing
528 * Limit the number of packets queued by this session
529 * to 4. Note that even though we try and limit this
530 * to 4 packets, the session could have more queued
531 * if the packets needed to be fragmented
532 * (XXX <= 4 ?)
533 */
534 if ((so->so_state & SS_ISFCONNECTED) && so->so_queued <= 4) {
535 STAM_REL_COUNTER_INC(&pData->StatUDPHot);
536#if !defined(VBOX_WITH_SIMPLEFIED_SLIRP_SYNC) || !defined(RT_OS_WINDOWS)
537 FD_SET(so->s, readfds);
538 UPD_NFDS(so->s);
539#else
540 rc = WSAEventSelect(so->s, VBOX_SOCKET_EVENT, FD_READ|FD_WRITE|FD_OOB|FD_ACCEPT);
541 if (rc == SOCKET_ERROR)
542 goto socket_error;
543 continue;
544#endif
545 }
546#if defined(VBOX_WITH_SIMPLEFIED_SLIRP_SYNC) && defined(RT_OS_WINDOWS)
547 else
548 {
549 rc = WSAEventSelect(so->s, NULL, 0);
550 if (rc != SOCKET_ERROR)
551 goto socket_error;
552 }
553#endif
554 }
555 }
556
557 /*
558 * Setup timeout to use minimum CPU usage, especially when idle
559 */
560
561 /*
562 * First, see the timeout needed by *timo
563 */
564 timeout.tv_sec = 0;
565 timeout.tv_usec = -1;
566 /*
567 * If a slowtimo is needed, set timeout to 500ms from the last
568 * slow timeout. If a fast timeout is needed, set timeout within
569 * 200ms of when it was requested.
570 */
571 if (do_slowtimo) {
572 /* XXX + 10000 because some select()'s aren't that accurate */
573 timeout.tv_usec = ((500 - (curtime - last_slowtimo)) * 1000) + 10000;
574 if (timeout.tv_usec < 0)
575 timeout.tv_usec = 0;
576 else if (timeout.tv_usec > 510000)
577 timeout.tv_usec = 510000;
578
579 /* Can only fasttimo if we also slowtimo */
580 if (time_fasttimo) {
581 tmp_time = (200 - (curtime - time_fasttimo)) * 1000;
582 if (tmp_time < 0)
583 tmp_time = 0;
584
585 /* Choose the smallest of the 2 */
586 if (tmp_time < timeout.tv_usec)
587 timeout.tv_usec = (u_int)tmp_time;
588 }
589 }
590#if !defined(VBOX_WITH_SIMPLEFIED_SLIRP_SYNC) || !defined(RT_OS_WINDOWS)
591 *pnfds = nfds;
592#else
593 *pnfds = VBOX_EVENT_COUNT;
594#endif
595
596 STAM_REL_PROFILE_STOP(&pData->StatFill, a);
597}
598
599void slirp_select_poll(PNATState pData, fd_set *readfds, fd_set *writefds, fd_set *xfds)
600{
601 struct socket *so, *so_next;
602 int ret;
603#if defined(VBOX_WITH_SIMPLEFIED_SLIRP_SYNC) && defined(RT_OS_WINDOWS)
604 WSANETWORKEVENTS NetworkEvents;
605 int rc;
606 int error;
607 int timer_update = (readfds == NULL && writefds == NULL && xfds == NULL);
608#endif
609 STAM_REL_PROFILE_START(&pData->StatPoll, a);
610
611 /* Update time */
612 updtime(pData);
613
614 /*
615 * See if anything has timed out
616 */
617 if (link_up) {
618 if (time_fasttimo && ((curtime - time_fasttimo) >= 2)) {
619 STAM_REL_PROFILE_START(&pData->StatFastTimer, a);
620 tcp_fasttimo(pData);
621 time_fasttimo = 0;
622 STAM_REL_PROFILE_STOP(&pData->StatFastTimer, a);
623 }
624 if (do_slowtimo && ((curtime - last_slowtimo) >= 499)) {
625 STAM_REL_PROFILE_START(&pData->StatSlowTimer, a);
626 ip_slowtimo(pData);
627 tcp_slowtimo(pData);
628 last_slowtimo = curtime;
629 STAM_REL_PROFILE_STOP(&pData->StatSlowTimer, a);
630 }
631 }
632#if defined(VBOX_WITH_SIMPLEFIED_SLIRP_SYNC) && defined(RT_OS_WINDOWS)
633 if (timer_update) return;
634#endif
635
636 /*
637 * Check sockets
638 */
639 if (link_up) {
640 /*
641 * Check TCP sockets
642 */
643 for (so = tcb.so_next; so != &tcb; so = so_next) {
644 so_next = so->so_next;
645
646 /*
647 * FD_ISSET is meaningless on these sockets
648 * (and they can crash the program)
649 */
650 if (so->so_state & SS_NOFDREF || so->s == -1)
651 continue;
652#if defined(VBOX_WITH_SIMPLEFIED_SLIRP_SYNC) && defined(RT_OS_WINDOWS)
653 rc = WSAEnumNetworkEvents(so->s, VBOX_SOCKET_EVENT, &NetworkEvents);
654 if (rc == SOCKET_ERROR)
655 {
656 error = WSAGetLastError();
657 LogRel(("WSAEnumNetworkEvents TCP error %d\n", error));
658 continue;
659 }
660#endif
661
662 /*
663 * Check for URG data
664 * This will soread as well, so no need to
665 * test for readfds below if this succeeds
666 */
667#if !defined(VBOX_WITH_SIMPLEFIED_SLIRP_SYNC) || !defined(RT_OS_WINDOWS)
668 if (FD_ISSET(so->s, xfds))
669#else
670 /* out-of-band data */
671 if ((NetworkEvents.lNetworkEvents & FD_OOB) && NetworkEvents.iErrorCode[FD_OOB_BIT] == 0)
672#endif
673 sorecvoob(pData, so);
674 /*
675 * Check sockets for reading
676 */
677#if !defined(VBOX_WITH_SIMPLEFIED_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 * Check for incoming connections
684 */
685 if (so->so_state & SS_FACCEPTCONN) {
686 tcp_connect(pData, so);
687 continue;
688 } /* else */
689 ret = soread(pData, so);
690
691 /* Output it if we read something */
692 if (ret > 0)
693 tcp_output(pData, sototcpcb(so));
694 }
695
696 /*
697 * Check sockets for writing
698 */
699#if !defined(VBOX_WITH_SIMPLEFIED_SLIRP_SYNC) || !defined(RT_OS_WINDOWS)
700 if (FD_ISSET(so->s, writefds)) {
701#else
702 if ((NetworkEvents.lNetworkEvents & FD_WRITE) && (NetworkEvents.iErrorCode[FD_WRITE_BIT] == 0)) {
703#endif
704 /*
705 * Check for non-blocking, still-connecting sockets
706 */
707 if (so->so_state & SS_ISFCONNECTING) {
708 /* Connected */
709 so->so_state &= ~SS_ISFCONNECTING;
710
711 /*
712 * This should be probably guarded by PROBE_CONN too. Anyway,
713 * we disable it on OS/2 because the below send call returns
714 * EFAULT which causes the opened TCP socket to close right
715 * after it has been opened and connected.
716 */
717#ifndef RT_OS_OS2
718 ret = send(so->s, (const char *)&ret, 0, 0);
719 if (ret < 0) {
720 /* XXXXX Must fix, zero bytes is a NOP */
721 if (errno == EAGAIN || errno == EWOULDBLOCK ||
722 errno == EINPROGRESS || errno == ENOTCONN) {
723 continue;
724 }
725
726 /* else failed */
727 so->so_state = SS_NOFDREF;
728 }
729 /* else so->so_state &= ~SS_ISFCONNECTING; */
730#endif
731
732 /*
733 * Continue tcp_input
734 */
735 tcp_input(pData, (struct mbuf *)NULL, sizeof(struct ip), so);
736 /* continue; */
737 } else
738 ret = sowrite(pData, so);
739 /*
740 * XXXXX If we wrote something (a lot), there
741 * could be a need for a window update.
742 * In the worst case, the remote will send
743 * a window probe to get things going again
744 */
745 }
746
747 /*
748 * Probe a still-connecting, non-blocking socket
749 * to check if it's still alive
750 */
751#ifdef PROBE_CONN
752 if (so->so_state & SS_ISFCONNECTING) {
753 ret = recv(so->s, (char *)&ret, 0,0);
754
755 if (ret < 0) {
756 /* XXX */
757 if (errno == EAGAIN || errno == EWOULDBLOCK ||
758 errno == EINPROGRESS || errno == ENOTCONN) {
759 continue; /* Still connecting, continue */
760 }
761
762 /* else failed */
763 so->so_state = SS_NOFDREF;
764
765 /* tcp_input will take care of it */
766 } else {
767 ret = send(so->s, &ret, 0,0);
768 if (ret < 0) {
769 /* XXX */
770 if (errno == EAGAIN || errno == EWOULDBLOCK ||
771 errno == EINPROGRESS || errno == ENOTCONN) {
772 continue;
773 }
774 /* else failed */
775 so->so_state = SS_NOFDREF;
776 } else
777 so->so_state &= ~SS_ISFCONNECTING;
778
779 }
780 tcp_input((struct mbuf *)NULL, sizeof(struct ip),so);
781 } /* SS_ISFCONNECTING */
782#endif
783 }
784
785 /*
786 * Now UDP sockets.
787 * Incoming packets are sent straight away, they're not buffered.
788 * Incoming UDP data isn't buffered either.
789 */
790 for (so = udb.so_next; so != &udb; so = so_next) {
791 so_next = so->so_next;
792
793#if defined(VBOX_WITH_SIMPLEFIED_SLIRP_SYNC) && defined(RT_OS_WINDOWS)
794 rc = WSAEnumNetworkEvents(so->s, VBOX_SOCKET_EVENT, &NetworkEvents);
795 if (rc == SOCKET_ERROR)
796 {
797 error = WSAGetLastError();
798 LogRel(("WSAEnumNetworkEvents TCP error %d\n", error));
799 continue;
800 }
801#endif
802#if !defined(VBOX_WITH_SIMPLEFIED_SLIRP_SYNC) || !defined(RT_OS_WINDOWS)
803 if (so->s != -1 && FD_ISSET(so->s, readfds)) {
804#else
805 if ((NetworkEvents.lNetworkEvents & FD_READ) && (NetworkEvents.iErrorCode[FD_READ_BIT] == 0)) {
806#endif
807 sorecvfrom(pData, so);
808 }
809 }
810 }
811
812 /*
813 * See if we can start outputting
814 */
815 if (if_queued && link_up)
816 if_start(pData);
817
818 STAM_REL_PROFILE_STOP(&pData->StatPoll, a);
819}
820
821#define ETH_ALEN 6
822#define ETH_HLEN 14
823
824#define ETH_P_IP 0x0800 /* Internet Protocol packet */
825#define ETH_P_ARP 0x0806 /* Address Resolution packet */
826
827#define ARPOP_REQUEST 1 /* ARP request */
828#define ARPOP_REPLY 2 /* ARP reply */
829
830struct ethhdr
831{
832 unsigned char h_dest[ETH_ALEN]; /* destination eth addr */
833 unsigned char h_source[ETH_ALEN]; /* source ether addr */
834 unsigned short h_proto; /* packet type ID field */
835};
836
837struct arphdr
838{
839 unsigned short ar_hrd; /* format of hardware address */
840 unsigned short ar_pro; /* format of protocol address */
841 unsigned char ar_hln; /* length of hardware address */
842 unsigned char ar_pln; /* length of protocol address */
843 unsigned short ar_op; /* ARP opcode (command) */
844
845 /*
846 * Ethernet looks like this : This bit is variable sized however...
847 */
848 unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */
849 unsigned char ar_sip[4]; /* sender IP address */
850 unsigned char ar_tha[ETH_ALEN]; /* target hardware address */
851 unsigned char ar_tip[4]; /* target IP address */
852};
853
854static
855void arp_input(PNATState pData, const uint8_t *pkt, int pkt_len)
856{
857 struct ethhdr *eh = (struct ethhdr *)pkt;
858 struct arphdr *ah = (struct arphdr *)(pkt + ETH_HLEN);
859 uint8_t arp_reply[ETH_HLEN + sizeof(struct arphdr)];
860 struct ethhdr *reh = (struct ethhdr *)arp_reply;
861 struct arphdr *rah = (struct arphdr *)(arp_reply + ETH_HLEN);
862 int ar_op;
863 struct ex_list *ex_ptr;
864 uint32_t htip = ntohl(*(uint32_t*)ah->ar_tip);
865
866 ar_op = ntohs(ah->ar_op);
867 switch(ar_op) {
868 case ARPOP_REQUEST:
869 if ((htip & pData->netmask) == ntohl(special_addr.s_addr)) {
870 if ( (htip & ~pData->netmask) == CTL_DNS
871 || (htip & ~pData->netmask) == CTL_ALIAS)
872 goto arp_ok;
873 for (ex_ptr = exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) {
874 if ((htip & ~pData->netmask) == ex_ptr->ex_addr)
875 goto arp_ok;
876 }
877 return;
878 arp_ok:
879 /* XXX: make an ARP request to have the client address */
880 memcpy(client_ethaddr, eh->h_source, ETH_ALEN);
881
882 /* ARP request for alias/dns mac address */
883 memcpy(reh->h_dest, pkt + ETH_ALEN, ETH_ALEN);
884 memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 1);
885 reh->h_source[5] = ah->ar_tip[3];
886 reh->h_proto = htons(ETH_P_ARP);
887
888 rah->ar_hrd = htons(1);
889 rah->ar_pro = htons(ETH_P_IP);
890 rah->ar_hln = ETH_ALEN;
891 rah->ar_pln = 4;
892 rah->ar_op = htons(ARPOP_REPLY);
893 memcpy(rah->ar_sha, reh->h_source, ETH_ALEN);
894 memcpy(rah->ar_sip, ah->ar_tip, 4);
895 memcpy(rah->ar_tha, ah->ar_sha, ETH_ALEN);
896 memcpy(rah->ar_tip, ah->ar_sip, 4);
897 slirp_output(pData->pvUser, arp_reply, sizeof(arp_reply));
898 }
899 break;
900 default:
901 break;
902 }
903}
904
905void slirp_input(PNATState pData, const uint8_t *pkt, int pkt_len)
906{
907 struct mbuf *m;
908 int proto;
909
910 if (pkt_len < ETH_HLEN)
911 return;
912
913 proto = ntohs(*(uint16_t *)(pkt + 12));
914 switch(proto) {
915 case ETH_P_ARP:
916 arp_input(pData, pkt, pkt_len);
917 break;
918 case ETH_P_IP:
919 /* Update time. Important if the network is very quiet, as otherwise
920 * the first outgoing connection gets an incorrect timestamp. */
921 updtime(pData);
922
923 m = m_get(pData);
924 if (!m)
925 return;
926 /* Note: we add to align the IP header */
927 if (M_FREEROOM(m) < pkt_len + 2) {
928 m_inc(m, pkt_len + 2);
929 }
930 m->m_len = pkt_len + 2;
931 memcpy(m->m_data + 2, pkt, pkt_len);
932
933 m->m_data += 2 + ETH_HLEN;
934 m->m_len -= 2 + ETH_HLEN;
935
936 ip_input(pData, m);
937 break;
938 default:
939 break;
940 }
941}
942
943/* output the IP packet to the ethernet device */
944void if_encap(PNATState pData, const uint8_t *ip_data, int ip_data_len)
945{
946 uint8_t buf[1600];
947 struct ethhdr *eh = (struct ethhdr *)buf;
948
949 if (ip_data_len + ETH_HLEN > sizeof(buf))
950 return;
951
952 memcpy(eh->h_dest, client_ethaddr, ETH_ALEN);
953 memcpy(eh->h_source, special_ethaddr, ETH_ALEN - 1);
954 /* XXX: not correct */
955 eh->h_source[5] = CTL_ALIAS;
956 eh->h_proto = htons(ETH_P_IP);
957 memcpy(buf + sizeof(struct ethhdr), ip_data, ip_data_len);
958 slirp_output(pData->pvUser, buf, ip_data_len + ETH_HLEN);
959}
960
961int slirp_redir(PNATState pData, int is_udp, int host_port,
962 struct in_addr guest_addr, int guest_port)
963{
964 if (is_udp) {
965 if (!udp_listen(pData, htons(host_port), guest_addr.s_addr,
966 htons(guest_port), 0))
967 return -1;
968 } else {
969 if (!solisten(pData, htons(host_port), guest_addr.s_addr,
970 htons(guest_port), 0))
971 return -1;
972 }
973 return 0;
974}
975
976int slirp_add_exec(PNATState pData, int do_pty, const char *args, int addr_low_byte,
977 int guest_port)
978{
979 return add_exec(&exec_list, do_pty, (char *)args,
980 addr_low_byte, htons(guest_port));
981}
982
983void slirp_set_ethaddr(PNATState pData, const uint8_t *ethaddr)
984{
985 memcpy(client_ethaddr, ethaddr, ETH_ALEN);
986}
987
988#if defined(VBOX_WITH_SIMPLEFIED_SLIRP_SYNC) && defined(RT_OS_WINDOWS)
989HANDLE *slirp_get_events(PNATState pData)
990{
991 return pData->phEvents;
992}
993void slirp_register_external_event(PNATState pData, HANDLE hEvent, int index)
994{
995 pData->phEvents[index] = hEvent;
996}
997#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