VirtualBox

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

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

NAT: Linux build fix; added some perfcounters

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