VirtualBox

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

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

removing reducular event assigning and conunter ticking
with model introduced in 39250 the counter return WSA_MAXIMUM_WAITING_EVENTS

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette