VirtualBox

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

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

NAT: Slirp don't use ether address of guest anymore
instead it calculates ethernet address of destination
with lookup operation. Currently it's very simple looks
over send addresses via dhcp or assume destination in outer
network and gets Slirp's ethernet address.

  • Property svn:eol-style set to native
File size: 56.3 KB
Line 
1#include "slirp.h"
2#ifdef RT_OS_OS2
3# include <paths.h>
4#endif
5#ifdef VBOX_WITH_SLIRP_DNS_PROXY
6#include "dnsproxy/dnsproxy.h"
7#endif
8
9#include <VBox/err.h>
10#include <VBox/pdmdrv.h>
11#include <iprt/assert.h>
12#ifndef RT_OS_WINDOWS
13# include <sys/ioctl.h>
14# include <poll.h>
15#else
16# include <Winnls.h>
17# define _WINSOCK2API_
18# include <IPHlpApi.h>
19#endif
20
21#if !defined(RT_OS_WINDOWS)
22
23# define DO_ENGAGE_EVENT1(so, fdset, label) \
24 do { \
25 if( so->so_poll_index != -1 \
26 && so->s == polls[so->so_poll_index].fd) { \
27 polls[so->so_poll_index].events |= N_(fdset ## _poll); \
28 break; /* out of this loop */ \
29 } \
30 AssertRelease(poll_index < (nfds)); \
31 AssertRelease(poll_index >= 0 && poll_index < (nfds)); \
32 polls[poll_index].fd = (so)->s; \
33 (so)->so_poll_index = poll_index; \
34 polls[poll_index].events = N_(fdset ## _poll); \
35 polls[poll_index].revents = 0; \
36 poll_index++; \
37 } while(0)
38
39
40# define DO_ENGAGE_EVENT2(so, fdset1, fdset2, label) \
41 do { \
42 if( so->so_poll_index != -1 \
43 && so->s == polls[so->so_poll_index].fd) { \
44 polls[so->so_poll_index].events |= \
45 N_(fdset1 ## _poll) | N_(fdset1 ## _poll); \
46 break; /* out of this loop */ \
47 } \
48 AssertRelease(poll_index < (nfds)); \
49 polls[poll_index].fd = (so)->s; \
50 (so)->so_poll_index = poll_index; \
51 polls[poll_index].events = \
52 N_(fdset1 ## _poll) | N_(fdset1 ## _poll); \
53 poll_index++; \
54 } while(0)
55
56# define DO_POLL_EVENTS(rc, error, so, events, label) do {} while (0)
57
58# define DO_CHECK_FD_SET(so, events, fdset) ( ((so)->so_poll_index != -1) \
59 && ((so)->so_poll_index <= ndfs) \
60 && ((so)->s == polls[so->so_poll_index].fd) \
61 && (polls[(so)->so_poll_index].revents & N_(fdset ## _poll)))
62# define DO_UNIX_CHECK_FD_SET(so, events, fdset ) DO_CHECK_FD_SET((so), (events), fdset) /*specific for Unix API */
63# define DO_WIN_CHECK_FD_SET(so, events, fdset ) 0 /* specific for Windows Winsock API */
64
65# ifndef RT_OS_WINDOWS
66
67# ifndef RT_OS_LINUX
68# define readfds_poll (POLLRDNORM)
69# define writefds_poll (POLLWRNORM)
70# define xfds_poll (POLLRDBAND|POLLWRBAND|POLLPRI)
71# else
72# define readfds_poll (POLLIN)
73# define writefds_poll (POLLOUT)
74# define xfds_poll (POLLPRI)
75# endif
76# define rderr_poll (POLLERR)
77# define rdhup_poll (POLLHUP)
78# define nval_poll (POLLNVAL)
79
80# define ICMP_ENGAGE_EVENT(so, fdset) \
81 do { \
82 if (pData->icmp_socket.s != -1) \
83 DO_ENGAGE_EVENT1((so), fdset, ICMP); \
84 } while (0)
85# else /* !RT_OS_WINDOWS */
86# define DO_WIN_CHECK_FD_SET(so, events, fdset ) DO_CHECK_FD_SET((so), (events), fdset)
87# define ICMP_ENGAGE_EVENT(so, fdset) do {} while(0)
88#endif /* RT_OS_WINDOWS */
89
90#else /* defined(RT_OS_WINDOWS) */
91
92/*
93 * On Windows, we will be notified by IcmpSendEcho2() when the response arrives.
94 * So no call to WSAEventSelect necessary.
95 */
96# define ICMP_ENGAGE_EVENT(so, fdset) do {} while(0)
97
98# define DO_ENGAGE_EVENT1(so, fdset1, label) \
99 do { \
100 rc = WSAEventSelect((so)->s, VBOX_SOCKET_EVENT, FD_ALL_EVENTS); \
101 if (rc == SOCKET_ERROR) \
102 { \
103 /* This should not happen */ \
104 error = WSAGetLastError(); \
105 LogRel(("WSAEventSelect (" #label ") error %d (so=%x, socket=%s, event=%x)\n", \
106 error, (so), (so)->s, VBOX_SOCKET_EVENT)); \
107 } \
108 } while(0); \
109 CONTINUE(label)
110
111# define DO_ENGAGE_EVENT2(so, fdset1, fdset2, label) \
112 DO_ENGAGE_EVENT1((so), (fdset1), label)
113
114# define DO_POLL_EVENTS(rc, error, so, events, label) \
115 (rc) = WSAEnumNetworkEvents((so)->s, VBOX_SOCKET_EVENT, (events)); \
116 if ((rc) == SOCKET_ERROR) \
117 { \
118 (error) = WSAGetLastError(); \
119 LogRel(("WSAEnumNetworkEvents " #label " error %d\n", (error))); \
120 CONTINUE(label); \
121 }
122
123# define acceptds_win FD_ACCEPT
124# define acceptds_win_bit FD_ACCEPT_BIT
125
126# define readfds_win FD_READ
127# define readfds_win_bit FD_READ_BIT
128
129# define writefds_win FD_WRITE
130# define writefds_win_bit FD_WRITE_BIT
131
132# define xfds_win FD_OOB
133# define xfds_win_bit FD_OOB_BIT
134
135# define DO_CHECK_FD_SET(so, events, fdset) \
136 (((events).lNetworkEvents & fdset ## _win) && ((events).iErrorCode[fdset ## _win_bit] == 0))
137
138# define DO_WIN_CHECK_FD_SET(so, events, fdset ) DO_CHECK_FD_SET((so), (events), fdset)
139# define DO_UNIX_CHECK_FD_SET(so, events, fdset ) 1 /*specific for Unix API */
140
141#endif /* defined(RT_OS_WINDOWS) */
142
143#define TCP_ENGAGE_EVENT1(so, fdset) \
144 DO_ENGAGE_EVENT1((so), fdset, tcp)
145
146#define TCP_ENGAGE_EVENT2(so, fdset1, fdset2) \
147 DO_ENGAGE_EVENT2((so), fdset1, fdset2, tcp)
148
149#define UDP_ENGAGE_EVENT(so, fdset) \
150 DO_ENGAGE_EVENT1((so), fdset, udp)
151
152#define POLL_TCP_EVENTS(rc, error, so, events) \
153 DO_POLL_EVENTS((rc), (error), (so), (events), tcp)
154
155#define POLL_UDP_EVENTS(rc, error, so, events) \
156 DO_POLL_EVENTS((rc), (error), (so), (events), udp)
157
158#define CHECK_FD_SET(so, events, set) \
159 (DO_CHECK_FD_SET((so), (events), set))
160
161#define WIN_CHECK_FD_SET(so, events, set) \
162 (DO_WIN_CHECK_FD_SET((so), (events), set))
163#define UNIX_CHECK_FD_SET(so, events, set) \
164 (DO_UNIX_CHECK_FD_SET(so, events, set))
165
166/*
167 * Loging macros
168 */
169#if VBOX_WITH_DEBUG_NAT_SOCKETS
170# if defined(RT_OS_WINDOWS)
171# define DO_LOG_NAT_SOCK(so, proto, winevent, r_fdset, w_fdset, x_fdset) \
172 do { \
173 LogRel((" " #proto " %R[natsock] %R[natwinnetevents]\n", (so), (winevent))); \
174 } while (0)
175# else /* RT_OS_WINDOWS */
176# define DO_LOG_NAT_SOCK(so, proto, winevent, r_fdset, w_fdset, x_fdset) \
177 do { \
178 LogRel((" " #proto " %R[natsock] %s %s %s er: %s, %s, %s\n", (so), \
179 CHECK_FD_SET(so, ign ,r_fdset) ? "READ":"", \
180 CHECK_FD_SET(so, ign, w_fdset) ? "WRITE":"", \
181 CHECK_FD_SET(so, ign, x_fdset) ? "OOB":"", \
182 CHECK_FD_SET(so, ign, rderr) ? "RDERR":"", \
183 CHECK_FD_SET(so, ign, rdhup) ? "RDHUP":"", \
184 CHECK_FD_SET(so, ign, nval) ? "RDNVAL":"")); \
185 } while (0)
186# endif /* !RT_OS_WINDOWS */
187#else /* VBOX_WITH_DEBUG_NAT_SOCKETS */
188# define DO_LOG_NAT_SOCK(so, proto, winevent, r_fdset, w_fdset, x_fdset) do {} while (0)
189#endif /* !VBOX_WITH_DEBUG_NAT_SOCKETS */
190
191#define LOG_NAT_SOCK(so, proto, winevent, r_fdset, w_fdset, x_fdset) DO_LOG_NAT_SOCK((so), proto, (winevent), r_fdset, w_fdset, x_fdset)
192
193static const uint8_t special_ethaddr[6] =
194{
195 0x52, 0x54, 0x00, 0x12, 0x35, 0x00
196};
197
198static const uint8_t broadcast_ethaddr[6] =
199{
200 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
201};
202
203const uint8_t zerro_ethaddr[6] =
204{
205 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
206};
207
208#ifdef RT_OS_WINDOWS
209# ifndef VBOX_WITH_MULTI_DNS
210static int get_dns_addr_domain(PNATState pData, bool fVerbose,
211 struct in_addr *pdns_addr,
212 const char **ppszDomain)
213{
214 int rc = 0;
215 FIXED_INFO *FixedInfo = NULL;
216 ULONG BufLen;
217 DWORD ret;
218 IP_ADDR_STRING *pIPAddr;
219 struct in_addr tmp_addr;
220
221 FixedInfo = (FIXED_INFO *)GlobalAlloc(GPTR, sizeof(FIXED_INFO));
222 BufLen = sizeof(FIXED_INFO);
223
224 /** @todo: this API returns all DNS servers, no matter whether the
225 * corresponding network adapter is disabled or not. Maybe replace
226 * this by GetAdapterAddresses(), which is XP/Vista only though. */
227 if (ERROR_BUFFER_OVERFLOW == GetNetworkParams(FixedInfo, &BufLen))
228 {
229 if (FixedInfo)
230 {
231 GlobalFree(FixedInfo);
232 FixedInfo = NULL;
233 }
234 FixedInfo = GlobalAlloc(GPTR, BufLen);
235 }
236
237 if ((ret = GetNetworkParams(FixedInfo, &BufLen)) != ERROR_SUCCESS)
238 {
239 Log(("GetNetworkParams failed. ret = %08x\n", (u_int)ret ));
240 if (FixedInfo)
241 {
242 GlobalFree(FixedInfo);
243 FixedInfo = NULL;
244 }
245 rc = -1;
246 goto get_dns_prefix;
247 }
248
249 pIPAddr = &(FixedInfo->DnsServerList);
250 inet_aton(pIPAddr->IpAddress.String, &tmp_addr);
251 Log(("nat: DNS Servers:\n"));
252 if (fVerbose || pdns_addr->s_addr != tmp_addr.s_addr)
253 LogRel(("NAT: DNS address: %s\n", pIPAddr->IpAddress.String));
254 *pdns_addr = tmp_addr;
255
256 pIPAddr = FixedInfo -> DnsServerList.Next;
257 while (pIPAddr)
258 {
259 if (fVerbose)
260 LogRel(("NAT: ignored DNS address: %s\n", pIPAddr ->IpAddress.String));
261 pIPAddr = pIPAddr ->Next;
262 }
263 if (FixedInfo)
264 {
265 GlobalFree(FixedInfo);
266 FixedInfo = NULL;
267 }
268
269get_dns_prefix:
270 if (ppszDomain)
271 {
272 OSVERSIONINFO ver;
273 char szDnsDomain[256];
274 DWORD dwSize = sizeof(szDnsDomain);
275
276 *ppszDomain = NULL;
277 GetVersionEx(&ver);
278 if (ver.dwMajorVersion >= 5)
279 {
280 /* GetComputerNameEx exists in Windows versions starting with 2000. */
281 if (GetComputerNameEx(ComputerNameDnsDomain, szDnsDomain, &dwSize))
282 {
283 if (szDnsDomain[0])
284 {
285 /* Just non-empty strings are valid. */
286 *ppszDomain = RTStrDup(szDnsDomain);
287 if (pData->fPassDomain)
288 {
289 if (fVerbose)
290 LogRel(("NAT: passing domain name %s\n", szDnsDomain));
291 }
292 else
293 Log(("nat: ignoring domain %s\n", szDnsDomain));
294 }
295 }
296 else
297 Log(("nat: GetComputerNameEx failed (%d)\n", GetLastError()));
298 }
299 }
300 return rc;
301}
302# else /* !VBOX_WITH_MULTI_DNS */
303static int get_dns_addr_domain(PNATState pData, bool fVerbose,
304 struct in_addr *pdns_addr,
305 const char **ppszDomain)
306{
307 /* Get amount of memory required for operation */
308 ULONG flags = GAA_FLAG_INCLUDE_PREFIX; /*GAA_FLAG_INCLUDE_ALL_INTERFACES;*/ /* all interfaces registered in NDIS */
309 PIP_ADAPTER_ADDRESSES addresses = NULL;
310 PIP_ADAPTER_ADDRESSES addr = NULL;
311 PIP_ADAPTER_DNS_SERVER_ADDRESS dns = NULL;
312 ULONG size = 0;
313 int wlen = 0;
314 char *suffix;
315 struct dns_entry *da = NULL;
316 struct dns_domain_entry *dd = NULL;
317 ULONG ret = ERROR_SUCCESS;
318
319 /* @todo add SKIPing flags to get only required information */
320
321 ret = pData->pfGetAdaptersAddresses(AF_INET, 0, NULL /* reserved */, addresses, &size);
322 if (ret != ERROR_BUFFER_OVERFLOW)
323 {
324 LogRel(("NAT: error %lu occured on capacity detection operation\n", ret));
325 return -1;
326 }
327
328 if (size == 0)
329 {
330 LogRel(("NAT: Win socket API returns non capacity\n"));
331 return -1;
332 }
333
334 addresses = RTMemAllocZ(size);
335 if (addresses == NULL)
336 {
337 LogRel(("NAT: No memory available \n"));
338 return -1;
339 }
340
341 ret = pData->pfGetAdaptersAddresses(AF_INET, 0, NULL /* reserved */, addresses, &size);
342 if (ret != ERROR_SUCCESS)
343 {
344 LogRel(("NAT: error %lu occured on fetching adapters info\n", ret));
345 RTMemFree(addresses);
346 return -1;
347 }
348 addr = addresses;
349 while(addr != NULL)
350 {
351 int found;
352 if (addr->OperStatus != IfOperStatusUp)
353 goto next;
354 dns = addr->FirstDnsServerAddress;
355 while (dns != NULL)
356 {
357 struct sockaddr *saddr = dns->Address.lpSockaddr;
358 if (saddr->sa_family != AF_INET)
359 goto next_dns;
360 /* add dns server to list */
361 da = RTMemAllocZ(sizeof(struct dns_entry));
362 if (da == NULL)
363 {
364 LogRel(("NAT: Can't allocate buffer for DNS entry\n"));
365 RTMemFree(addresses);
366 return VERR_NO_MEMORY;
367 }
368 LogRel(("NAT: adding %R[IP4] to DNS server list\n", &((struct sockaddr_in *)saddr)->sin_addr));
369 if ((((struct sockaddr_in *)saddr)->sin_addr.s_addr & htonl(IN_CLASSA_NET)) == ntohl(INADDR_LOOPBACK & IN_CLASSA_NET)) {
370 da->de_addr.s_addr = htonl(ntohl(special_addr.s_addr) | CTL_ALIAS);
371 }
372 else
373 {
374 da->de_addr.s_addr = ((struct sockaddr_in *)saddr)->sin_addr.s_addr;
375 }
376 LIST_INSERT_HEAD(&pData->dns_list_head, da, de_list);
377
378 if (addr->DnsSuffix == NULL)
379 goto next_dns;
380
381 /*uniq*/
382 RTUtf16ToUtf8(addr->DnsSuffix, &suffix);
383
384 if (!suffix || strlen(suffix) == 0) {
385 RTStrFree(suffix);
386 goto next_dns;
387 }
388
389 found = 0;
390 LIST_FOREACH(dd, &pData->dns_domain_list_head, dd_list)
391 {
392 if ( dd->dd_pszDomain != NULL
393 && strcmp(dd->dd_pszDomain, suffix) == 0)
394 {
395 found = 1;
396 RTStrFree(suffix);
397 break;
398 }
399 }
400 if (found == 0)
401 {
402 dd = RTMemAllocZ(sizeof(struct dns_domain_entry));
403 if (dd == NULL)
404 {
405 LogRel(("NAT: not enough memory\n"));
406 RTStrFree(suffix);
407 RTMemFree(addresses);
408 return VERR_NO_MEMORY;
409 }
410 dd->dd_pszDomain = suffix;
411 LogRel(("NAT: adding domain name %s to search list\n", dd->dd_pszDomain));
412 LIST_INSERT_HEAD(&pData->dns_domain_list_head, dd, dd_list);
413 }
414 next_dns:
415 dns = dns->Next;
416 }
417 next:
418 addr = addr->Next;
419 }
420 RTMemFree(addresses);
421 return 0;
422}
423# endif /* VBOX_WITH_MULTI_DNS */
424
425#else /* !RT_OS_WINDOWS */
426
427static int get_dns_addr_domain(PNATState pData, bool fVerbose,
428 struct in_addr *pdns_addr,
429 const char **ppszDomain)
430{
431 char buff[512];
432 char buff2[256];
433 FILE *f = NULL;
434 int found = 0;
435 struct in_addr tmp_addr;
436
437#ifdef RT_OS_OS2
438 /* Try various locations. */
439 char *etc = getenv("ETC");
440 if (etc)
441 {
442 snprintf(buff, sizeof(buff), "%s/RESOLV2", etc);
443 f = fopen(buff, "rt");
444 }
445 if (!f)
446 {
447 snprintf(buff, sizeof(buff), "%s/RESOLV2", _PATH_ETC);
448 f = fopen(buff, "rt");
449 }
450 if (!f)
451 {
452 snprintf(buff, sizeof(buff), "%s/resolv.conf", _PATH_ETC);
453 f = fopen(buff, "rt");
454 }
455#else
456#ifndef DEBUG_vvl
457 f = fopen("/etc/resolv.conf", "r");
458#else
459 char *home = getenv("HOME");
460 snprintf(buff, sizeof(buff), "%s/resolv.conf", home);
461 f = fopen(buff, "r");
462 if (f != NULL)
463 {
464 Log(("NAT: DNS we're using %s\n", buff));
465 }
466 else
467 {
468 f = fopen("/etc/resolv.conf", "r");
469 Log(("NAT: DNS we're using %s\n", buff));
470 }
471#endif
472#endif
473 if (!f)
474 return -1;
475
476 if (ppszDomain)
477 *ppszDomain = NULL;
478 Log(("nat: DNS Servers:\n"));
479 while (fgets(buff, 512, f) != NULL)
480 {
481#ifdef VBOX_WITH_MULTI_DNS
482 struct dns_entry *da = NULL;
483#endif
484 if (sscanf(buff, "nameserver%*[ \t]%256s", buff2) == 1)
485 {
486 if (!inet_aton(buff2, &tmp_addr))
487 continue;
488#ifndef VBOX_WITH_MULTI_DNS
489 /* If it's the first one, set it to dns_addr */
490 if (!found)
491 {
492 if (fVerbose || pdns_addr->s_addr != tmp_addr.s_addr)
493 LogRel(("NAT: DNS address: %s\n", buff2));
494 *pdns_addr = tmp_addr;
495 }
496 else
497 {
498 if (fVerbose)
499 LogRel(("NAT: ignored DNS address: %s\n", buff2));
500 }
501#else
502 /*localhost mask */
503 da = RTMemAllocZ(sizeof (struct dns_entry));
504 if (da == NULL)
505 {
506 LogRel(("can't alloc memory for DNS entry\n"));
507 return -1;
508 }
509 /*check */
510 da->de_addr.s_addr = tmp_addr.s_addr;
511 if ((da->de_addr.s_addr & htonl(IN_CLASSA_NET)) == ntohl(INADDR_LOOPBACK & IN_CLASSA_NET)) {
512 da->de_addr.s_addr = htonl(ntohl(special_addr.s_addr) | CTL_ALIAS);
513 }
514 LIST_INSERT_HEAD(&pData->dns_list_head, da, de_list);
515#endif
516 found++;
517 }
518#ifndef VBOX_WITH_MULTI_DNS
519 if ( ppszDomain
520 && (!strncmp(buff, "domain", 6) || !strncmp(buff, "search", 6)))
521 {
522 /* Domain name/search list present. Pick first entry */
523 if (*ppszDomain == NULL)
524 {
525 char *tok;
526 char *saveptr;
527 tok = strtok_r(&buff[6], " \t\n", &saveptr);
528 if (tok)
529 {
530 *ppszDomain = RTStrDup(tok);
531 if (pData->fPassDomain)
532 {
533 if (fVerbose)
534 LogRel(("NAT: passing domain name %s\n", tok));
535 }
536 else
537 Log(("nat: ignoring domain %s\n", tok));
538 }
539 }
540 }
541#else
542 if ((!strncmp(buff, "domain", 6) || !strncmp(buff, "search", 6)))
543 {
544 char *tok;
545 char *saveptr;
546 struct dns_domain_entry *dd = NULL;
547 int found = 0;
548 tok = strtok_r(&buff[6], " \t\n", &saveptr);
549 LIST_FOREACH(dd, &pData->dns_domain_list_head, dd_list)
550 {
551 if( tok != NULL
552 && strcmp(tok, dd->dd_pszDomain) == 0)
553 {
554 found = 1;
555 break;
556 }
557 }
558 if (tok != NULL && found == 0) {
559 dd = RTMemAllocZ(sizeof(struct dns_domain_entry));
560 if (dd == NULL)
561 {
562 LogRel(("NAT: not enought memory to add domain list\n"));
563 return VERR_NO_MEMORY;
564 }
565 dd->dd_pszDomain = RTStrDup(tok);
566 LogRel(("NAT: adding domain name %s to search list\n", dd->dd_pszDomain));
567 LIST_INSERT_HEAD(&pData->dns_domain_list_head, dd, dd_list);
568 }
569 }
570#endif
571 }
572 fclose(f);
573 if (!found)
574 return -1;
575 return 0;
576}
577
578#endif
579#ifdef VBOX_WITH_MULTI_DNS
580static int slirp_init_dns_list(PNATState pData)
581{
582 LIST_INIT(&pData->dns_list_head);
583 LIST_INIT(&pData->dns_domain_list_head);
584 return get_dns_addr_domain(pData, true, NULL, NULL);
585}
586
587static void slirp_release_dns_list(PNATState pData)
588{
589 struct dns_entry *de = NULL;
590 struct dns_domain_entry *dd = NULL;
591 while(!LIST_EMPTY(&pData->dns_domain_list_head)) {
592 dd = LIST_FIRST(&pData->dns_domain_list_head);
593 LIST_REMOVE(dd, dd_list);
594 if (dd->dd_pszDomain != NULL)
595 RTStrFree(dd->dd_pszDomain);
596 RTMemFree(dd);
597 }
598 while(!LIST_EMPTY(&pData->dns_domain_list_head)) {
599 dd = LIST_FIRST(&pData->dns_domain_list_head);
600 LIST_REMOVE(dd, dd_list);
601 if (dd->dd_pszDomain != NULL)
602 RTStrFree(dd->dd_pszDomain);
603 RTMemFree(dd);
604 }
605}
606#endif
607
608int get_dns_addr(PNATState pData, struct in_addr *pdns_addr)
609{
610 return get_dns_addr_domain(pData, false, pdns_addr, NULL);
611}
612
613#ifndef VBOX_WITH_NAT_SERVICE
614int slirp_init(PNATState *ppData, const char *pszNetAddr, uint32_t u32Netmask,
615 bool fPassDomain, void *pvUser)
616#else
617int slirp_init(PNATState *ppData, uint32_t u32NetAddr, uint32_t u32Netmask,
618 bool fPassDomain, void *pvUser)
619#endif
620{
621 int fNATfailed = 0;
622 int rc;
623 PNATState pData = RTMemAllocZ(sizeof(NATState));
624 *ppData = pData;
625 if (!pData)
626 return VERR_NO_MEMORY;
627 if (u32Netmask & 0x1f)
628 /* CTL is x.x.x.15, bootp passes up to 16 IPs (15..31) */
629 return VERR_INVALID_PARAMETER;
630 pData->fPassDomain = fPassDomain;
631 pData->pvUser = pvUser;
632 pData->netmask = u32Netmask;
633
634#ifdef RT_OS_WINDOWS
635 {
636 WSADATA Data;
637 WSAStartup(MAKEWORD(2, 0), &Data);
638 }
639 pData->phEvents[VBOX_SOCKET_EVENT_INDEX] = CreateEvent(NULL, FALSE, FALSE, NULL);
640#endif
641#ifdef VBOX_WITH_SLIRP_MT
642 QSOCKET_LOCK_CREATE(tcb);
643 QSOCKET_LOCK_CREATE(udb);
644 rc = RTReqCreateQueue(&pData->pReqQueue);
645 AssertReleaseRC(rc);
646#endif
647
648 link_up = 1;
649
650 debug_init();
651 if_init(pData);
652 ip_init(pData);
653 icmp_init(pData);
654
655 /* Initialise mbufs *after* setting the MTU */
656 m_init(pData);
657
658#ifndef VBOX_WITH_NAT_SERVICE
659 inet_aton(pszNetAddr, &special_addr);
660#else
661 special_addr.s_addr = u32NetAddr;
662#endif
663#ifdef VBOX_WITHOUT_SLIRP_CLIENT_ETHER
664 pData->slirp_ethaddr = &special_ethaddr;
665#endif
666 alias_addr.s_addr = special_addr.s_addr | htonl(CTL_ALIAS);
667 /* @todo: add ability to configure this staff */
668
669 /* set default addresses */
670 inet_aton("127.0.0.1", &loopback_addr);
671#ifndef VBOX_WITH_MULTI_DNS
672 inet_aton("127.0.0.1", &dns_addr);
673
674 if (get_dns_addr_domain(pData, true, &dns_addr, &pData->pszDomain) < 0)
675#else
676 if (slirp_init_dns_list(pData) < 0)
677#endif
678 fNATfailed = 1;
679#ifdef VBOX_WITH_SLIRP_DNS_PROXY
680 dnsproxy_init(pData);
681#endif
682
683 getouraddr(pData);
684 return fNATfailed ? VINF_NAT_DNS : VINF_SUCCESS;
685}
686
687/**
688 * Statistics counters.
689 */
690void slirp_register_timers(PNATState pData, PPDMDRVINS pDrvIns)
691{
692#ifdef VBOX_WITH_STATISTICS
693 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatFill, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS,
694 STAMUNIT_TICKS_PER_CALL, "Profiling slirp fills", "/Drivers/NAT%d/Fill", pDrvIns->iInstance);
695 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatPoll, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS,
696 STAMUNIT_TICKS_PER_CALL, "Profiling slirp polls", "/Drivers/NAT%d/Poll", pDrvIns->iInstance);
697 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatFastTimer, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS,
698 STAMUNIT_TICKS_PER_CALL, "Profiling slirp fast timer", "/Drivers/NAT%d/TimerFast", pDrvIns->iInstance);
699 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatSlowTimer, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS,
700 STAMUNIT_TICKS_PER_CALL, "Profiling slirp slow timer", "/Drivers/NAT%d/TimerSlow", pDrvIns->iInstance);
701 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatTCP, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS,
702 STAMUNIT_COUNT, "TCP sockets", "/Drivers/NAT%d/SockTCP", pDrvIns->iInstance);
703 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatTCPHot, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS,
704 STAMUNIT_COUNT, "TCP sockets active", "/Drivers/NAT%d/SockTCPHot", pDrvIns->iInstance);
705 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatUDP, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS,
706 STAMUNIT_COUNT, "UDP sockets", "/Drivers/NAT%d/SockUDP", pDrvIns->iInstance);
707 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatUDPHot, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS,
708 STAMUNIT_COUNT, "UDP sockets active", "/Drivers/NAT%d/SockUDPHot", pDrvIns->iInstance);
709#endif /* VBOX_WITH_STATISTICS */
710}
711
712/**
713 * Marks the link as up, making it possible to establish new connections.
714 */
715void slirp_link_up(PNATState pData)
716{
717 link_up = 1;
718}
719
720/**
721 * Marks the link as down and cleans up the current connections.
722 */
723void slirp_link_down(PNATState pData)
724{
725 struct socket *so;
726
727 while ((so = tcb.so_next) != &tcb)
728 {
729 if (so->so_state & SS_NOFDREF || so->s == -1)
730 sofree(pData, so);
731 else
732 tcp_drop(pData, sototcpcb(so), 0);
733 }
734
735 while ((so = udb.so_next) != &udb)
736 udp_detach(pData, so);
737
738 link_up = 0;
739}
740
741/**
742 * Terminates the slirp component.
743 */
744void slirp_term(PNATState pData)
745{
746#ifndef VBOX_WITH_MULTI_DNS
747 if (pData->pszDomain)
748 RTStrFree((char *)(void *)pData->pszDomain);
749#endif
750
751#ifdef RT_OS_WINDOWS
752 pData->pfIcmpCloseHandle(pData->icmp_socket.sh);
753 FreeLibrary(pData->hmIcmpLibrary);
754 RTMemFree(pData->pvIcmpBuffer);
755# else
756 closesocket(pData->icmp_socket.s);
757#endif
758
759 slirp_link_down(pData);
760#ifdef VBOX_WITH_MULTI_DNS
761 slirp_release_dns_list(pData);
762#endif
763#ifdef RT_OS_WINDOWS
764 WSACleanup();
765#endif
766#ifdef LOG_ENABLED
767 Log(("\n"
768 "NAT statistics\n"
769 "--------------\n"
770 "\n"));
771 ipstats(pData);
772 tcpstats(pData);
773 udpstats(pData);
774 icmpstats(pData);
775 mbufstats(pData);
776 sockstats(pData);
777 Log(("\n"
778 "\n"
779 "\n"));
780#endif
781 RTMemFree(pData);
782}
783
784
785#define CONN_CANFSEND(so) (((so)->so_state & (SS_FCANTSENDMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED)
786#define CONN_CANFRCV(so) (((so)->so_state & (SS_FCANTRCVMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED)
787#define UPD_NFDS(x) do { if (nfds < (x)) nfds = (x); } while (0)
788
789/*
790 * curtime kept to an accuracy of 1ms
791 */
792static void updtime(PNATState pData)
793{
794#ifdef RT_OS_WINDOWS
795 struct _timeb tb;
796
797 _ftime(&tb);
798 curtime = (u_int)tb.time * (u_int)1000;
799 curtime += (u_int)tb.millitm;
800#else
801 gettimeofday(&tt, 0);
802
803 curtime = (u_int)tt.tv_sec * (u_int)1000;
804 curtime += (u_int)tt.tv_usec / (u_int)1000;
805
806 if ((tt.tv_usec % 1000) >= 500)
807 curtime++;
808#endif
809}
810
811#ifdef RT_OS_WINDOWS
812void slirp_select_fill(PNATState pData, int *pnfds)
813#else /* RT_OS_WINDOWS */
814void slirp_select_fill(PNATState pData, int *pnfds, struct pollfd *polls)
815#endif /* !RT_OS_WINDOWS */
816{
817 struct socket *so, *so_next;
818 int nfds;
819#if defined(RT_OS_WINDOWS)
820 int rc;
821 int error;
822#else
823 int poll_index = 0;
824#endif
825 int i;
826
827 STAM_PROFILE_START(&pData->StatFill, a);
828
829 nfds = *pnfds;
830
831 /*
832 * First, TCP sockets
833 */
834 do_slowtimo = 0;
835 if (link_up)
836 {
837 /*
838 * *_slowtimo needs calling if there are IP fragments
839 * in the fragment queue, or there are TCP connections active
840 */
841 /* XXX:
842 * triggering of fragment expiration should be the same but use new macroses
843 */
844 do_slowtimo = (tcb.so_next != &tcb);
845 if (!do_slowtimo)
846 {
847 for (i = 0; i < IPREASS_NHASH; i++)
848 {
849 if (!TAILQ_EMPTY(&ipq[i]))
850 {
851 do_slowtimo = 1;
852 break;
853 }
854 }
855 }
856 ICMP_ENGAGE_EVENT(&pData->icmp_socket, readfds);
857
858 STAM_COUNTER_RESET(&pData->StatTCP);
859 STAM_COUNTER_RESET(&pData->StatTCPHot);
860
861 QSOCKET_FOREACH(so, so_next, tcp)
862 /* { */
863#if !defined(RT_OS_WINDOWS)
864 so->so_poll_index = -1;
865#endif
866 STAM_COUNTER_INC(&pData->StatTCP);
867
868 /*
869 * See if we need a tcp_fasttimo
870 */
871 if ( time_fasttimo == 0
872 && so->so_tcpcb != NULL
873 && so->so_tcpcb->t_flags & TF_DELACK)
874 time_fasttimo = curtime; /* Flag when we want a fasttimo */
875
876 /*
877 * NOFDREF can include still connecting to local-host,
878 * newly socreated() sockets etc. Don't want to select these.
879 */
880 if (so->so_state & SS_NOFDREF || so->s == -1)
881 CONTINUE(tcp);
882
883 /*
884 * Set for reading sockets which are accepting
885 */
886 if (so->so_state & SS_FACCEPTCONN)
887 {
888 STAM_COUNTER_INC(&pData->StatTCPHot);
889 TCP_ENGAGE_EVENT1(so, readfds);
890 CONTINUE(tcp);
891 }
892
893 /*
894 * Set for writing sockets which are connecting
895 */
896 if (so->so_state & SS_ISFCONNECTING)
897 {
898 Log2(("connecting %R[natsock] engaged\n",so));
899 STAM_COUNTER_INC(&pData->StatTCPHot);
900 TCP_ENGAGE_EVENT1(so, writefds);
901 }
902
903 /*
904 * Set for writing if we are connected, can send more, and
905 * we have something to send
906 */
907 if (CONN_CANFSEND(so) && so->so_rcv.sb_cc)
908 {
909 STAM_COUNTER_INC(&pData->StatTCPHot);
910 TCP_ENGAGE_EVENT1(so, writefds);
911 }
912
913 /*
914 * Set for reading (and urgent data) if we are connected, can
915 * receive more, and we have room for it XXX /2 ?
916 */
917 if (CONN_CANFRCV(so) && (so->so_snd.sb_cc < (so->so_snd.sb_datalen/2)))
918 {
919 STAM_COUNTER_INC(&pData->StatTCPHot);
920 TCP_ENGAGE_EVENT2(so, readfds, xfds);
921 }
922 LOOP_LABEL(tcp, so, so_next);
923 }
924
925 /*
926 * UDP sockets
927 */
928 STAM_COUNTER_RESET(&pData->StatUDP);
929 STAM_COUNTER_RESET(&pData->StatUDPHot);
930
931 QSOCKET_FOREACH(so, so_next, udp)
932 /* { */
933
934 STAM_COUNTER_INC(&pData->StatUDP);
935#if !defined(RT_OS_WINDOWS)
936 so->so_poll_index = -1;
937#endif
938
939 /*
940 * See if it's timed out
941 */
942 if (so->so_expire)
943 {
944 if (so->so_expire <= curtime)
945 {
946#ifdef VBOX_WITH_SLIRP_DNS_PROXY
947 Log2(("NAT: %R[natsock] expired\n", so));
948 if (so->so_timeout != NULL)
949 {
950 so->so_timeout(pData, so, so->so_timeout_arg);
951 }
952#endif
953#ifdef VBOX_WITH_SLIRP_MT
954 /* we need so_next for continue our cycle*/
955 so_next = so->so_next;
956#endif
957 UDP_DETACH(pData, so, so_next);
958 CONTINUE_NO_UNLOCK(udp);
959 }
960 else
961 do_slowtimo = 1; /* Let socket expire */
962 }
963
964 /*
965 * When UDP packets are received from over the link, they're
966 * sendto()'d straight away, so no need for setting for writing
967 * Limit the number of packets queued by this session to 4.
968 * Note that even though we try and limit this to 4 packets,
969 * the session could have more queued if the packets needed
970 * to be fragmented.
971 *
972 * (XXX <= 4 ?)
973 */
974 if ((so->so_state & SS_ISFCONNECTED) && so->so_queued <= 4)
975 {
976 STAM_COUNTER_INC(&pData->StatUDPHot);
977 UDP_ENGAGE_EVENT(so, readfds);
978 }
979 LOOP_LABEL(udp, so, so_next);
980 }
981
982 }
983
984#if defined(RT_OS_WINDOWS)
985 *pnfds = VBOX_EVENT_COUNT;
986#else /* RT_OS_WINDOWS */
987 AssertRelease(poll_index <= *pnfds);
988 *pnfds = poll_index;
989#endif /* !RT_OS_WINDOWS */
990
991 STAM_PROFILE_STOP(&pData->StatFill, a);
992}
993
994#if defined(RT_OS_WINDOWS)
995void slirp_select_poll(PNATState pData, int fTimeout, int fIcmp)
996#else /* RT_OS_WINDOWS */
997void slirp_select_poll(PNATState pData, struct pollfd *polls, int ndfs)
998#endif /* !RT_OS_WINDOWS */
999{
1000 struct socket *so, *so_next;
1001 int ret;
1002#if defined(RT_OS_WINDOWS)
1003 WSANETWORKEVENTS NetworkEvents;
1004 int rc;
1005 int error;
1006#else
1007 int poll_index = 0;
1008#endif
1009
1010 STAM_PROFILE_START(&pData->StatPoll, a);
1011
1012 /* Update time */
1013 updtime(pData);
1014
1015 /*
1016 * See if anything has timed out
1017 */
1018 if (link_up)
1019 {
1020 if (time_fasttimo && ((curtime - time_fasttimo) >= 2))
1021 {
1022 STAM_PROFILE_START(&pData->StatFastTimer, a);
1023 tcp_fasttimo(pData);
1024 time_fasttimo = 0;
1025 STAM_PROFILE_STOP(&pData->StatFastTimer, a);
1026 }
1027 if (do_slowtimo && ((curtime - last_slowtimo) >= 499))
1028 {
1029 STAM_PROFILE_START(&pData->StatSlowTimer, a);
1030 ip_slowtimo(pData);
1031 tcp_slowtimo(pData);
1032 last_slowtimo = curtime;
1033 STAM_PROFILE_STOP(&pData->StatSlowTimer, a);
1034 }
1035 }
1036#if defined(RT_OS_WINDOWS)
1037 if (fTimeout)
1038 return; /* only timer update */
1039#endif
1040
1041 /*
1042 * Check sockets
1043 */
1044 if (link_up)
1045 {
1046#if defined(RT_OS_WINDOWS)
1047 /*XXX: before renaming please make see define
1048 * fIcmp in slirp_state.h
1049 */
1050 if (fIcmp)
1051 sorecvfrom(pData, &pData->icmp_socket);
1052#else
1053 if ( (pData->icmp_socket.s != -1)
1054 && CHECK_FD_SET(&pData->icmp_socket, ignored, readfds))
1055 sorecvfrom(pData, &pData->icmp_socket);
1056#endif
1057 /*
1058 * Check TCP sockets
1059 */
1060 QSOCKET_FOREACH(so, so_next, tcp)
1061 /* { */
1062
1063#ifdef VBOX_WITH_SLIRP_MT
1064 if ( so->so_state & SS_NOFDREF
1065 && so->so_deleted == 1)
1066 {
1067 struct socket *son, *sop = NULL;
1068 QSOCKET_LOCK(tcb);
1069 if (so->so_next != NULL)
1070 {
1071 if (so->so_next != &tcb)
1072 SOCKET_LOCK(so->so_next);
1073 son = so->so_next;
1074 }
1075 if ( so->so_prev != &tcb
1076 && so->so_prev != NULL)
1077 {
1078 SOCKET_LOCK(so->so_prev);
1079 sop = so->so_prev;
1080 }
1081 QSOCKET_UNLOCK(tcb);
1082 remque(pData, so);
1083 NSOCK_DEC();
1084 SOCKET_UNLOCK(so);
1085 SOCKET_LOCK_DESTROY(so);
1086 RTMemFree(so);
1087 so_next = son;
1088 if (sop != NULL)
1089 SOCKET_UNLOCK(sop);
1090 CONTINUE_NO_UNLOCK(tcp);
1091 }
1092#endif
1093 /*
1094 * FD_ISSET is meaningless on these sockets
1095 * (and they can crash the program)
1096 */
1097 if (so->so_state & SS_NOFDREF || so->s == -1)
1098 CONTINUE(tcp);
1099
1100 POLL_TCP_EVENTS(rc, error, so, &NetworkEvents);
1101
1102 LOG_NAT_SOCK(so, TCP, &NetworkEvents, readfds, writefds, xfds);
1103
1104
1105 /*
1106 * Check for URG data
1107 * This will soread as well, so no need to
1108 * test for readfds below if this succeeds
1109 */
1110
1111 /* out-of-band data */
1112 if (CHECK_FD_SET(so, NetworkEvents, xfds))
1113 {
1114 sorecvoob(pData, so);
1115 }
1116
1117 /*
1118 * Check sockets for reading
1119 */
1120 else if ( CHECK_FD_SET(so, NetworkEvents, readfds)
1121 || WIN_CHECK_FD_SET(so, NetworkEvents, acceptds))
1122 {
1123 /*
1124 * Check for incoming connections
1125 */
1126 if (so->so_state & SS_FACCEPTCONN)
1127 {
1128 TCP_CONNECT(pData, so);
1129#if defined(RT_OS_WINDOWS)
1130 if (!(NetworkEvents.lNetworkEvents & FD_CLOSE))
1131#endif
1132 CONTINUE(tcp);
1133 }
1134
1135 ret = soread(pData, so);
1136 /* Output it if we read something */
1137 if (ret > 0)
1138 TCP_OUTPUT(pData, sototcpcb(so));
1139 }
1140
1141#if defined(RT_OS_WINDOWS)
1142 /*
1143 * Check for FD_CLOSE events.
1144 * in some cases once FD_CLOSE engaged on socket it could be flashed latter (for some reasons)
1145 */
1146 if ( (NetworkEvents.lNetworkEvents & FD_CLOSE)
1147 || (so->so_close == 1))
1148 {
1149 so->so_close = 1; /* mark it */
1150 /*
1151 * drain the socket
1152 */
1153 for (;;)
1154 {
1155 ret = soread(pData, so);
1156 if (ret > 0)
1157 TCP_OUTPUT(pData, sototcpcb(so));
1158 else
1159 break;
1160 }
1161 CONTINUE(tcp);
1162 }
1163#endif
1164
1165 /*
1166 * Check sockets for writing
1167 */
1168 if (CHECK_FD_SET(so, NetworkEvents, writefds))
1169 {
1170 /*
1171 * Check for non-blocking, still-connecting sockets
1172 */
1173 if (so->so_state & SS_ISFCONNECTING)
1174 {
1175 Log2(("connecting %R[natsock] catched\n", so));
1176 /* Connected */
1177 so->so_state &= ~SS_ISFCONNECTING;
1178
1179 /*
1180 * This should be probably guarded by PROBE_CONN too. Anyway,
1181 * we disable it on OS/2 because the below send call returns
1182 * EFAULT which causes the opened TCP socket to close right
1183 * after it has been opened and connected.
1184 */
1185#ifndef RT_OS_OS2
1186 ret = send(so->s, (const char *)&ret, 0, 0);
1187 if (ret < 0)
1188 {
1189 /* XXXXX Must fix, zero bytes is a NOP */
1190 if ( errno == EAGAIN
1191 || errno == EWOULDBLOCK
1192 || errno == EINPROGRESS
1193 || errno == ENOTCONN)
1194 CONTINUE(tcp);
1195
1196 /* else failed */
1197 so->so_state = SS_NOFDREF;
1198 }
1199 /* else so->so_state &= ~SS_ISFCONNECTING; */
1200#endif
1201
1202 /*
1203 * Continue tcp_input
1204 */
1205 TCP_INPUT(pData, (struct mbuf *)NULL, sizeof(struct ip), so);
1206 /* continue; */
1207 }
1208 else
1209 SOWRITE(ret, pData, so);
1210 /*
1211 * XXX If we wrote something (a lot), there could be the need
1212 * for a window update. In the worst case, the remote will send
1213 * a window probe to get things going again.
1214 */
1215 }
1216
1217 /*
1218 * Probe a still-connecting, non-blocking socket
1219 * to check if it's still alive
1220 */
1221#ifdef PROBE_CONN
1222 if (so->so_state & SS_ISFCONNECTING)
1223 {
1224 ret = recv(so->s, (char *)&ret, 0, 0);
1225
1226 if (ret < 0)
1227 {
1228 /* XXX */
1229 if ( errno == EAGAIN
1230 || errno == EWOULDBLOCK
1231 || errno == EINPROGRESS
1232 || errno == ENOTCONN)
1233 {
1234 CONTINUE(tcp); /* Still connecting, continue */
1235 }
1236
1237 /* else failed */
1238 so->so_state = SS_NOFDREF;
1239
1240 /* tcp_input will take care of it */
1241 }
1242 else
1243 {
1244 ret = send(so->s, &ret, 0, 0);
1245 if (ret < 0)
1246 {
1247 /* XXX */
1248 if ( errno == EAGAIN
1249 || errno == EWOULDBLOCK
1250 || errno == EINPROGRESS
1251 || errno == ENOTCONN)
1252 {
1253 CONTINUE(tcp);
1254 }
1255 /* else failed */
1256 so->so_state = SS_NOFDREF;
1257 }
1258 else
1259 so->so_state &= ~SS_ISFCONNECTING;
1260
1261 }
1262 TCP_INPUT((struct mbuf *)NULL, sizeof(struct ip),so);
1263 } /* SS_ISFCONNECTING */
1264#endif
1265#ifndef RT_OS_WINDOWS
1266 if ( UNIX_CHECK_FD_SET(so, NetworkEvents, rdhup)
1267 || UNIX_CHECK_FD_SET(so, NetworkEvents, rderr))
1268 {
1269 int err;
1270 int inq, outq;
1271 int status;
1272 socklen_t optlen = sizeof(int);
1273 inq = outq = 0;
1274 status = getsockopt(so->s, SOL_SOCKET, SO_ERROR, &err, &optlen);
1275 if (status != 0)
1276 Log(("NAT: can't get error status from %R[natsock]\n", so));
1277#ifndef RT_OS_SOLARIS
1278 status = ioctl(so->s, FIONREAD, &inq); /* tcp(7) recommends SIOCINQ which is Linux specific */
1279 if (status != 0 || status != EINVAL)
1280 {
1281 /* EINVAL returned if socket in listen state tcp(7)*/
1282 Log(("NAT: can't get depth of IN queue status from %R[natsock]\n", so));
1283 }
1284 status = ioctl(so->s, TIOCOUTQ, &outq); /* SIOCOUTQ see previous comment */
1285 if (status != 0)
1286 Log(("NAT: can't get depth of OUT queue from %R[natsock]\n", so));
1287#else
1288 /*
1289 * Solaris has bit different ioctl commands and its handlings
1290 * hint: streamio(7) I_NREAD
1291 */
1292#endif
1293 if ( so->so_state & SS_ISFCONNECTING
1294 || UNIX_CHECK_FD_SET(so, NetworkEvents, readfds))
1295 {
1296 /**
1297 * Check if we need here take care about gracefull connection
1298 * @todo try with proxy server
1299 */
1300 if (UNIX_CHECK_FD_SET(so, NetworkEvents, readfds))
1301 {
1302 /*
1303 * Never meet inq != 0 or outq != 0, anyway let it stay for a while
1304 * in case it happens we'll able to detect it.
1305 * Give TCP/IP stack wait or expire the socket.
1306 */
1307 Log(("NAT: %R[natsock] err(%d:%s) s(in:%d,out:%d)happens on read I/O, "
1308 "other side close connection \n", so, err, strerror(err), inq, outq));
1309 CONTINUE(tcp);
1310 }
1311 goto tcp_input_close;
1312 }
1313 if ( !UNIX_CHECK_FD_SET(so, NetworkEvents, readfds)
1314 && !UNIX_CHECK_FD_SET(so, NetworkEvents, writefds)
1315 && !UNIX_CHECK_FD_SET(so, NetworkEvents, xfds))
1316 {
1317 Log(("NAT: system expires the socket %R[natsock] err(%d:%s) s(in:%d,out:%d) happens on non-I/O. ",
1318 so, err, strerror(err), inq, outq));
1319 goto tcp_input_close;
1320 }
1321 Log(("NAT: %R[natsock] we've met(%d:%s) s(in:%d, out:%d) unhandled combination hup (%d) "
1322 "rederr(%d) on (r:%d, w:%d, x:%d)\n",
1323 so, err, strerror(err),
1324 inq, outq,
1325 UNIX_CHECK_FD_SET(so, ign, rdhup),
1326 UNIX_CHECK_FD_SET(so, ign, rderr),
1327 UNIX_CHECK_FD_SET(so, ign, readfds),
1328 UNIX_CHECK_FD_SET(so, ign, writefds),
1329 UNIX_CHECK_FD_SET(so, ign, xfds)));
1330 /*
1331 * Give OS's TCP/IP stack a chance to resolve an issue or expire the socket.
1332 */
1333 CONTINUE(tcp);
1334tcp_input_close:
1335 so->so_state = SS_NOFDREF; /*cause connection valid tcp connection termination and socket closing */
1336 TCP_INPUT(pData, (struct mbuf *)NULL, sizeof(struct ip), so);
1337 CONTINUE(tcp);
1338 }
1339#endif
1340 LOOP_LABEL(tcp, so, so_next);
1341 }
1342
1343 /*
1344 * Now UDP sockets.
1345 * Incoming packets are sent straight away, they're not buffered.
1346 * Incoming UDP data isn't buffered either.
1347 */
1348 QSOCKET_FOREACH(so, so_next, udp)
1349 /* { */
1350#ifdef VBOX_WITH_SLIRP_MT
1351 if ( so->so_state & SS_NOFDREF
1352 && so->so_deleted == 1)
1353 {
1354 struct socket *son, *sop = NULL;
1355 QSOCKET_LOCK(udb);
1356 if (so->so_next != NULL)
1357 {
1358 if (so->so_next != &udb)
1359 SOCKET_LOCK(so->so_next);
1360 son = so->so_next;
1361 }
1362 if ( so->so_prev != &udb
1363 && so->so_prev != NULL)
1364 {
1365 SOCKET_LOCK(so->so_prev);
1366 sop = so->so_prev;
1367 }
1368 QSOCKET_UNLOCK(udb);
1369 remque(pData, so);
1370 NSOCK_DEC();
1371 SOCKET_UNLOCK(so);
1372 SOCKET_LOCK_DESTROY(so);
1373 RTMemFree(so);
1374 so_next = son;
1375 if (sop != NULL)
1376 SOCKET_UNLOCK(sop);
1377 CONTINUE_NO_UNLOCK(udp);
1378 }
1379#endif
1380 POLL_UDP_EVENTS(rc, error, so, &NetworkEvents);
1381
1382 LOG_NAT_SOCK(so, UDP, &NetworkEvents, readfds, writefds, xfds);
1383
1384 if (so->s != -1 && CHECK_FD_SET(so, NetworkEvents, readfds))
1385 {
1386 SORECVFROM(pData, so);
1387 }
1388 LOOP_LABEL(udp, so, so_next);
1389 }
1390
1391 }
1392
1393#ifndef VBOX_WITH_SLIRP_MT
1394 /*
1395 * See if we can start outputting
1396 */
1397 if (if_queued && link_up)
1398 if_start(pData);
1399#endif
1400
1401 STAM_PROFILE_STOP(&pData->StatPoll, a);
1402}
1403
1404#ifndef VBOX_WITHOUT_SLIRP_CLIENT_ETHER
1405#define ETH_ALEN 6
1406#define ETH_HLEN 14
1407
1408#define ARPOP_REQUEST 1 /* ARP request */
1409#define ARPOP_REPLY 2 /* ARP reply */
1410
1411struct ethhdr
1412{
1413 unsigned char h_dest[ETH_ALEN]; /* destination eth addr */
1414 unsigned char h_source[ETH_ALEN]; /* source ether addr */
1415 unsigned short h_proto; /* packet type ID field */
1416};
1417AssertCompileSize(struct ethhdr, 14);
1418#endif
1419
1420struct arphdr
1421{
1422 unsigned short ar_hrd; /* format of hardware address */
1423 unsigned short ar_pro; /* format of protocol address */
1424 unsigned char ar_hln; /* length of hardware address */
1425 unsigned char ar_pln; /* length of protocol address */
1426 unsigned short ar_op; /* ARP opcode (command) */
1427
1428 /*
1429 * Ethernet looks like this : This bit is variable sized however...
1430 */
1431 unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */
1432 unsigned char ar_sip[4]; /* sender IP address */
1433 unsigned char ar_tha[ETH_ALEN]; /* target hardware address */
1434 unsigned char ar_tip[4]; /* target IP address */
1435};
1436AssertCompileSize(struct arphdr, 28);
1437
1438static void arp_input(PNATState pData, struct mbuf *m)
1439{
1440 struct ethhdr *eh;
1441 struct ethhdr *reh;
1442 struct arphdr *ah;
1443 struct arphdr *rah;
1444 int ar_op;
1445 struct ex_list *ex_ptr;
1446 uint32_t htip;
1447 uint32_t tip;
1448 struct mbuf *mr;
1449 eh = mtod(m, struct ethhdr *);
1450 ah = (struct arphdr *)&eh[1];
1451 htip = ntohl(*(uint32_t*)ah->ar_tip);
1452 tip = *(uint32_t*)ah->ar_tip;
1453
1454 mr = m_get(pData);
1455#ifdef VBOX_WITHOUT_SLIRP_CLIENT_ETHER
1456 reh = mtod(mr, struct ethhdr *);
1457 memcpy(reh->h_source, eh->h_source, ETH_ALEN); /* XXX: if_encap will swap src and dst*/
1458 Log4(("NAT: arp:%R[ether]->%R[ether]\n",
1459 reh->h_source, reh->h_dest));
1460 Log4(("NAT: arp: %R[IP4]\n", &tip));
1461#endif
1462 mr->m_data += if_maxlinkhdr;
1463 mr->m_len = sizeof(struct arphdr);
1464 rah = mtod(mr, struct arphdr *);
1465
1466 ar_op = ntohs(ah->ar_op);
1467 switch(ar_op)
1468 {
1469 case ARPOP_REQUEST:
1470#ifdef VBOX_WITH_NAT_SERVICE
1471 if (tip == special_addr.s_addr) goto arp_ok;
1472#endif
1473 if ((htip & pData->netmask) == ntohl(special_addr.s_addr))
1474 {
1475 if ( CTL_CHECK(htip, CTL_DNS)
1476 || CTL_CHECK(htip, CTL_ALIAS)
1477 || CTL_CHECK(htip, CTL_TFTP))
1478 goto arp_ok;
1479 for (ex_ptr = exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next)
1480 {
1481 if ((htip & ~pData->netmask) == ex_ptr->ex_addr)
1482 {
1483 goto arp_ok;
1484 }
1485 }
1486 return;
1487 arp_ok:
1488 rah->ar_hrd = htons(1);
1489 rah->ar_pro = htons(ETH_P_IP);
1490 rah->ar_hln = ETH_ALEN;
1491 rah->ar_pln = 4;
1492 rah->ar_op = htons(ARPOP_REPLY);
1493 memcpy(rah->ar_sha, special_ethaddr, ETH_ALEN);
1494
1495 switch (htip & ~pData->netmask)
1496 {
1497 case CTL_DNS:
1498 case CTL_ALIAS:
1499 rah->ar_sha[5] = (uint8_t)(htip & ~pData->netmask);
1500 break;
1501 default:;
1502 }
1503
1504 memcpy(rah->ar_sip, ah->ar_tip, 4);
1505 memcpy(rah->ar_tha, ah->ar_sha, ETH_ALEN);
1506 memcpy(rah->ar_tip, ah->ar_sip, 4);
1507 if_encap(pData, ETH_P_ARP, mr);
1508 m_free(pData, m);
1509 }
1510 break;
1511 default:
1512 break;
1513 }
1514}
1515
1516void slirp_input(PNATState pData, const uint8_t *pkt, int pkt_len)
1517{
1518 struct mbuf *m;
1519 int proto;
1520 static bool fWarnedIpv6;
1521 struct ethhdr *eh = (struct ethhdr*)pkt;
1522
1523 if (pkt_len < ETH_HLEN)
1524 {
1525 LogRel(("NAT: packet having size %d has been ingnored\n", pkt_len));
1526 return;
1527 }
1528 Log4(("NAT: in:%R[ether]->%R[ether]\n", eh->h_source, eh->h_dest));
1529#ifdef VBOX_WITHOUT_SLIRP_CLIENT_ETHER
1530 if (memcmp(eh->h_source, special_ethaddr, ETH_ALEN) == 0)
1531 {
1532 /* @todo vasily: add ether logging routine in debug.c */
1533 Log(("NAT: packet was addressed to other MAC\n"));
1534 RTMemFree((void *)pkt);
1535 return;
1536 }
1537#endif
1538
1539 m = m_get(pData);
1540 if (!m)
1541 {
1542 LogRel(("NAT: can't allocate new mbuf\n"));
1543 return;
1544 }
1545
1546 /* Note: we add to align the IP header */
1547
1548 if (M_FREEROOM(m) < pkt_len)
1549 m_inc(m, pkt_len);
1550
1551 m->m_len = pkt_len ;
1552 memcpy(m->m_data, pkt, pkt_len);
1553
1554 proto = ntohs(*(uint16_t *)(pkt + 12));
1555 switch(proto)
1556 {
1557 case ETH_P_ARP:
1558 arp_input(pData, m);
1559 break;
1560 case ETH_P_IP:
1561 /* Update time. Important if the network is very quiet, as otherwise
1562 * the first outgoing connection gets an incorrect timestamp. */
1563 updtime(pData);
1564 m->m_data += ETH_HLEN;
1565 m->m_len -= ETH_HLEN;
1566 ip_input(pData, m);
1567 break;
1568 case ETH_P_IPV6:
1569 m_free(pData, m);
1570 if (!fWarnedIpv6)
1571 {
1572 LogRel(("NAT: IPv6 not supported\n"));
1573 fWarnedIpv6 = true;
1574 }
1575 break;
1576 default:
1577 LogRel(("NAT: Unsupported protocol %x\n", proto));
1578 m_free(pData, m);
1579 break;
1580 }
1581 RTMemFree((void *)pkt);
1582}
1583
1584/* output the IP packet to the ethernet device */
1585void if_encap(PNATState pData, uint16_t eth_proto, struct mbuf *m)
1586{
1587 struct ethhdr *eh;
1588 uint8_t *buf = RTMemAlloc(1600);
1589
1590 m->m_data -= if_maxlinkhdr;
1591 m->m_len += ETH_HLEN;
1592 eh = mtod(m, struct ethhdr *);
1593
1594 if(MBUF_HEAD(m) != m->m_data)
1595 {
1596 LogRel(("NAT: ethernet detects corruption of the packet"));
1597 AssertMsgFailed(("!!Ethernet frame corrupted!!"));
1598 }
1599
1600#ifndef VBOX_WITHOUT_SLIRP_CLIENT_ETHER
1601 memcpy(eh->h_dest, client_ethaddr, ETH_ALEN);
1602 memcpy(eh->h_source, special_ethaddr, ETH_ALEN - 1);
1603 /* XXX: not correct */
1604 eh->h_source[5] = CTL_ALIAS;
1605#else
1606 if (memcmp(eh->h_source, special_ethaddr, ETH_ALEN) != 0)
1607 {
1608 memcpy(eh->h_dest, eh->h_source, ETH_ALEN);
1609 memcpy(eh->h_source, special_ethaddr, ETH_ALEN);
1610 Assert(memcmp(eh->h_dest, special_ethaddr, ETH_ALEN) != 0);
1611 Assert(memcmp(eh->h_dest, zerro_ethaddr, ETH_ALEN) != 0);
1612 }
1613#endif
1614 eh->h_proto = htons(eth_proto);
1615#if 0
1616 slirp_output(pData->pvUser, m, mtod(m, uint8_t *), m->m_len);
1617#else
1618 memcpy(buf, mtod(m, uint8_t *), m->m_len);
1619 slirp_output(pData->pvUser, NULL, buf, m->m_len);
1620 m_free(pData, m);
1621#endif
1622}
1623
1624int slirp_redir(PNATState pData, int is_udp, int host_port,
1625 struct in_addr guest_addr, int guest_port)
1626{
1627 struct socket *so;
1628 Log2(("NAT: set redirect %s hp:%d gp:%d\n", (is_udp?"UDP":"TCP"), host_port, guest_port));
1629 if (is_udp)
1630 {
1631 so = udp_listen(pData, htons(host_port), guest_addr.s_addr,
1632 htons(guest_port), 0);
1633 }
1634 else
1635 {
1636 so = solisten(pData, htons(host_port), guest_addr.s_addr,
1637 htons(guest_port), 0);
1638 }
1639 Log2(("NAT: redirecting socket %R[natsock]\n", so));
1640 return (so != NULL ? 0 : -1);
1641}
1642
1643int slirp_add_exec(PNATState pData, int do_pty, const char *args, int addr_low_byte,
1644 int guest_port)
1645{
1646 return add_exec(&exec_list, do_pty, (char *)args,
1647 addr_low_byte, htons(guest_port));
1648}
1649
1650void slirp_set_ethaddr(PNATState pData, const uint8_t *ethaddr)
1651{
1652#ifndef VBOX_WITHOUT_SLIRP_CLIENT_ETHER
1653 memcpy(client_ethaddr, ethaddr, ETH_ALEN);
1654#endif
1655}
1656
1657#if defined(RT_OS_WINDOWS)
1658HANDLE *slirp_get_events(PNATState pData)
1659{
1660 return pData->phEvents;
1661}
1662void slirp_register_external_event(PNATState pData, HANDLE hEvent, int index)
1663{
1664 pData->phEvents[index] = hEvent;
1665}
1666#endif
1667
1668unsigned int slirp_get_timeout_ms(PNATState pData)
1669{
1670 if (link_up)
1671 {
1672 if (time_fasttimo)
1673 return 2;
1674 if (do_slowtimo)
1675 return 500; /* see PR_SLOWHZ */
1676 }
1677 return 0;
1678}
1679
1680#ifndef RT_OS_WINDOWS
1681int slirp_get_nsock(PNATState pData)
1682{
1683 return pData->nsock;
1684}
1685#endif
1686
1687/*
1688 * this function called from NAT thread
1689 */
1690void slirp_post_sent(PNATState pData, void *pvArg)
1691{
1692 struct socket *so = 0;
1693 struct tcpcb *tp = 0;
1694 struct mbuf *m = (struct mbuf *)pvArg;
1695 m_free(pData, m);
1696}
1697#ifdef VBOX_WITH_SLIRP_MT
1698void slirp_process_queue(PNATState pData)
1699{
1700 RTReqProcess(pData->pReqQueue, RT_INDEFINITE_WAIT);
1701}
1702void *slirp_get_queue(PNATState pData)
1703{
1704 return pData->pReqQueue;
1705}
1706#endif
1707
1708uint16_t slirp_get_service(int proto, uint16_t dport, uint16_t sport)
1709{
1710 uint16_t hdport, hsport, service;
1711 hdport = ntohs(dport);
1712 hsport = ntohs(sport);
1713 Log2(("proto: %d, dport: %d sport: %d\n", proto, hdport, hsport));
1714 service = 0;
1715#if 1
1716 /* Always return 0 here */
1717 switch (hdport)
1718 {
1719 case 500:
1720 if (hsport != 500) /* vpnc by default try operate in src:500/dst:500 mode*/
1721 /* Not sure why this make Cisco VPN client's connection more stable,
1722 * at least on some servers
1723 */
1724 service = sport;
1725 break;
1726 }
1727#endif
1728 Log2(("service : %d\n", service));
1729 return htons(service);
1730}
1731
1732void slirp_set_dhcp_TFTP_prefix(PNATState pData, const char *tftpPrefix)
1733{
1734 Log2(("tftp_prefix:%s\n", tftpPrefix));
1735 tftp_prefix = tftpPrefix;
1736}
1737
1738void slirp_set_dhcp_TFTP_bootfile(PNATState pData, const char *bootFile)
1739{
1740 Log2(("bootFile:%s\n", bootFile));
1741 bootp_filename = bootFile;
1742}
1743
1744void slirp_set_dhcp_next_server(PNATState pData, const char *next_server)
1745{
1746 Log2(("next_server:%s\n", next_server));
1747 if (next_server == NULL)
1748 pData->tftp_server.s_addr = htonl(ntohl(special_addr.s_addr) | CTL_TFTP);
1749 else
1750 inet_aton(next_server, &pData->tftp_server);
1751}
1752#ifdef VBOX_WITH_SLIRP_DNS_PROXY
1753void slirp_set_dhcp_dns_proxy(PNATState pData, bool fDNSProxy)
1754{
1755 Log2(("NAT: DNS proxy switched %s\n", (fDNSProxy ? "on" : "off")));
1756 pData->use_dns_proxy = fDNSProxy;
1757}
1758#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