diff options
Diffstat (limited to 'src/VBox/Devices/Network/DrvNAT.cpp')
-rw-r--r-- | src/VBox/Devices/Network/DrvNAT.cpp | 274 |
1 files changed, 247 insertions, 27 deletions
diff --git a/src/VBox/Devices/Network/DrvNAT.cpp b/src/VBox/Devices/Network/DrvNAT.cpp index 15a1ca3c..c7e86fb3 100644 --- a/src/VBox/Devices/Network/DrvNAT.cpp +++ b/src/VBox/Devices/Network/DrvNAT.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2011 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -23,7 +23,12 @@ #define __STDC_LIMIT_MACROS #define __STDC_CONSTANT_MACROS #include "slirp/libslirp.h" +extern "C" { +#include "slirp/slirp_dns.h" +} #include "slirp/ctl.h" + +#include <VBox/vmm/dbgf.h> #include <VBox/vmm/pdmdrv.h> #include <VBox/vmm/pdmnetifs.h> #include <VBox/vmm/pdmnetinline.h> @@ -51,6 +56,10 @@ #endif #include <iprt/semaphore.h> #include <iprt/req.h> +#ifdef RT_OS_DARWIN +# include <SystemConfiguration/SystemConfiguration.h> +# include <CoreFoundation/CoreFoundation.h> +#endif #define COUNTERS_INIT #include "counters.h" @@ -60,6 +69,8 @@ * Defined Constants And Macros * *******************************************************************************/ +#define DRVNAT_MAXFRAMESIZE (16 * 1024) + /** * @todo: This is a bad hack to prevent freezing the guest during high network * activity. Windows host only. This needs to be fixed properly. @@ -194,6 +205,11 @@ typedef struct DRVNAT /** Transmit lock taken by BeginXmit and released by EndXmit. */ RTCRITSECT XmitLock; + +#ifdef RT_OS_DARWIN + /* Handle of the DNS watcher runloop source. */ + CFRunLoopSourceRef hRunLoopSrcDnsWatcher; +#endif } DRVNAT; AssertCompileMemberAlignment(DRVNAT, StatNATRecvWakeups, 8); /** Pointer to the NAT driver instance data. */ @@ -204,6 +220,10 @@ typedef DRVNAT *PDRVNAT; * Internal Functions * *******************************************************************************/ static void drvNATNotifyNATThread(PDRVNAT pThis, const char *pszWho); +DECLINLINE(void) drvNATHostNetworkConfigurationChangeEventStrategySelector( + PDRVNAT pThis, + bool fHostNetworkConfigurationEventListener); +static DECLCALLBACK(int) drvNATReinitializeHostNameResolving(PDRVNAT pThis); static DECLCALLBACK(int) drvNATRecv(PPDMDRVINS pDrvIns, PPDMTHREAD pThread) @@ -465,6 +485,16 @@ static DECLCALLBACK(int) drvNATNetworkUp_AllocBuf(PPDMINETWORKUP pInterface, siz return VERR_NO_MEMORY; if (!pGso) { + /* + * Drop the frame if it is too big. + */ + if (cbMin >= DRVNAT_MAXFRAMESIZE) + { + Log(("drvNATNetowrkUp_AllocBuf: drops over-sized frame (%u bytes), returns VERR_INVALID_PARAMETER\n", + cbMin)); + return VERR_INVALID_PARAMETER; + } + pSgBuf->pvUser = NULL; pSgBuf->pvAllocator = slirp_ext_m_get(pThis->pNATState, cbMin, &pSgBuf->aSegs[0].pvSeg, &pSgBuf->aSegs[0].cbSeg); @@ -476,6 +506,16 @@ static DECLCALLBACK(int) drvNATNetworkUp_AllocBuf(PPDMINETWORKUP pInterface, siz } else { + /* + * Drop the frame if its segment is too big. + */ + if (pGso->cbHdrsTotal + pGso->cbMaxSeg >= DRVNAT_MAXFRAMESIZE) + { + Log(("drvNATNetowrkUp_AllocBuf: drops over-sized frame (%u bytes), returns VERR_INVALID_PARAMETER\n", + pGso->cbHdrsTotal + pGso->cbMaxSeg)); + return VERR_INVALID_PARAMETER; + } + pSgBuf->pvUser = RTMemDup(pGso, sizeof(*pGso)); pSgBuf->pvAllocator = NULL; pSgBuf->aSegs[0].cbSeg = RT_ALIGN_Z(cbMin, 16); @@ -917,6 +957,34 @@ void slirp_output(void *pvUser, struct mbuf *m, const uint8_t *pu8Buf, int cb) } +#ifdef RT_OS_DARWIN +/** + * Callback for the SystemConfiguration framework to notify us whenever the DNS + * server changes. + * + * @returns nothing. + * @param hDynStor The DynamicStore handle. + * @param hChangedKey Array of changed keys we watch for. + * @param pvUser Opaque user data (NAT driver instance). + */ +static DECLCALLBACK(void) drvNatDnsChanged(SCDynamicStoreRef hDynStor, CFArrayRef hChangedKeys, void *pvUser) +{ + PDRVNAT pThis = (PDRVNAT)pvUser; + + LogRel(("NAT: DNS servers changed, triggering reconnect\n")); + + CFDictionaryRef hDnsDict = (CFDictionaryRef)SCDynamicStoreCopyValue(hDynStor, CFSTR("State:/Network/Global/DNS")); + if (hDnsDict) + { + CFArrayRef hArrAddresses = (CFArrayRef)CFDictionaryGetValue(hDnsDict, kSCPropNetDNSServerAddresses); + if (hArrAddresses) + drvNATHostNetworkConfigurationChangeEventStrategySelector(pThis, /* RT_OS_DARWIN */ true); + + CFRelease(hDnsDict); + } +} +#endif + /** * @interface_method_impl{PDMIBASE,pfnQueryInterface} */ @@ -973,6 +1041,92 @@ static DECLCALLBACK(void) drvNATPowerOn(PPDMDRVINS pDrvIns) /** + * @interface_method_impl{PDMDEVREG,pfnResume} + */ +static DECLCALLBACK(void) drvNATResume(PPDMDRVINS pDrvIns) +{ + PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT); + VMRESUMEREASON enmReason = PDMDrvHlpVMGetResumeReason(pDrvIns); + + switch (enmReason) + { + case VMRESUMEREASON_HOST_RESUME: +#if RT_OS_DARWIN + drvNATHostNetworkConfigurationChangeEventStrategySelector(pThis, false); +#else + drvNATHostNetworkConfigurationChangeEventStrategySelector(pThis, true); +#endif + return; + default: /* Ignore every other resume reason. */ + /* do nothing */ + return; + } +} + + +static DECLCALLBACK(int) drvNATReinitializeHostNameResolving(PDRVNAT pThis) +{ + slirpReleaseDnsSettings(pThis->pNATState); + slirpInitializeDnsSettings(pThis->pNATState); + return VINF_SUCCESS; +} + +/** + * This function at this stage could be called from two places, but both from non-NAT thread, + * - drvNATResume (EMT?) + * - drvNatDnsChanged (darwin, GUI or main) "listener" + * When Main's interface IHost will support host network configuration change event on every host, + * we won't call it from drvNATResume, but from listener of Main event in the similar way it done + * for port-forwarding, and it wan't be on GUI/main thread, but on EMT thread only. + * + * Thread here is important, because we need to change DNS server list and domain name (+ perhaps, + * search string) at runtime (VBOX_NAT_ENFORCE_INTERNAL_DNS_UPDATE), we can do it safely on NAT thread, + * so with changing other variables (place where we handle update) the main mechanism of update + * _won't_ be changed, the only thing will change is drop of fHostNetworkConfigurationEventListener parameter. + */ +DECLINLINE(void) drvNATHostNetworkConfigurationChangeEventStrategySelector(PDRVNAT pThis, + bool fHostNetworkConfigurationEventListener) +{ + int strategy = slirp_host_network_configuration_change_strategy_selector(pThis->pNATState); + switch (strategy) + { + + case VBOX_NAT_HNCE_DNSPROXY: + { + /** + * It's unsafe to to do it directly on non-NAT thread + * so we schedule the worker and kick the NAT thread. + */ + RTREQQUEUE hQueue = pThis->hSlirpReqQueue; + + int rc = RTReqQueueCallEx(hQueue, NULL /*ppReq*/, 0 /*cMillies*/, + RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT, + (PFNRT)drvNATReinitializeHostNameResolving, 1, pThis); + if (RT_SUCCESS(rc)) + drvNATNotifyNATThread(pThis, "drvNATNetworkUp_SendBuf"); + + + return; + + } + case VBOX_NAT_HNCE_EXSPOSED_NAME_RESOLUTION_INFO: + /* + * Host resumed from a suspend and the network might have changed. + * Disconnect the guest from the network temporarily to let it pick up the changes. + */ + + if (fHostNetworkConfigurationEventListener) + pThis->pIAboveConfig->pfnSetLinkState(pThis->pIAboveConfig, + PDMNETWORKLINKSTATE_DOWN_RESUME); + return; + case VBOX_NAT_HNCE_HOSTRESOLVER: + default: + return; + } +} + + +/** * Info handler. */ static DECLCALLBACK(void) drvNATInfo(PPDMDRVINS pDrvIns, PCDBGFINFOHLP pHlp, const char *pszArgs) @@ -1029,7 +1183,7 @@ static int drvNATConstructDNSMappings(unsigned iInstance, PDRVNAT pThis, PCFGMNO * @returns VBox status code. * @param pCfg The configuration handle. */ -static int drvNATConstructRedir(unsigned iInstance, PDRVNAT pThis, PCFGMNODE pCfg, RTIPV4ADDR Network) +static int drvNATConstructRedir(unsigned iInstance, PDRVNAT pThis, PCFGMNODE pCfg, PRTNETADDRIPV4 pNetwork) { RTMAC Mac; RT_ZERO(Mac); /* can't get MAC here */ @@ -1088,8 +1242,7 @@ static int drvNATConstructRedir(unsigned iInstance, PDRVNAT pThis, PCFGMNODE pCf /* guest address */ struct in_addr GuestIP; - /* @todo (vvl) use CTL_* */ - GETIP_DEF(rc, pThis, pNode, GuestIP, htonl(Network | CTL_GUEST)); + GETIP_DEF(rc, pThis, pNode, GuestIP, RT_H2N_U32(pNetwork->u | CTL_GUEST)); /* Store the guest IP for re-establishing the port-forwarding rules. Note that GuestIP * is not documented. Without */ @@ -1156,6 +1309,16 @@ static DECLCALLBACK(void) drvNATDestruct(PPDMDRVINS pDrvIns) if (RTCritSectIsInitialized(&pThis->XmitLock)) RTCritSectDelete(&pThis->XmitLock); + +#ifdef RT_OS_DARWIN + /* Cleanup the DNS watcher. */ + CFRunLoopRef hRunLoopMain = CFRunLoopGetMain(); + CFRetain(hRunLoopMain); + CFRunLoopRemoveSource(hRunLoopMain, pThis->hRunLoopSrcDnsWatcher, kCFRunLoopCommonModes); + CFRelease(hRunLoopMain); + CFRelease(pThis->hRunLoopSrcDnsWatcher); + pThis->hRunLoopSrcDnsWatcher = NULL; +#endif } @@ -1171,24 +1334,6 @@ static DECLCALLBACK(int) drvNATConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uin PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns); /* - * Validate the config. - */ - if (!CFGMR3AreValuesValid(pCfg, - "PassDomain\0TFTPPrefix\0BootFile\0Network" - "\0NextServer\0DNSProxy\0BindIP\0UseHostResolver\0" - "SlirpMTU\0AliasMode\0" - "SockRcv\0SockSnd\0TcpRcv\0TcpSnd\0" - "ICMPCacheLimit\0" - "SoMaxConnection\0" -#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER - "HostResolverMappings\0" -#endif - )) - return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES, - N_("Unknown NAT configuration option, only supports PassDomain," - " TFTPPrefix, BootFile and Network")); - - /* * Init the static parts. */ pThis->pDrvIns = pDrvIns; @@ -1200,6 +1345,9 @@ static DECLCALLBACK(int) drvNATConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uin pThis->hUrgRecvReqQueue = NIL_RTREQQUEUE; pThis->EventRecv = NIL_RTSEMEVENT; pThis->EventUrgRecv = NIL_RTSEMEVENT; +#ifdef RT_OS_DARWIN + pThis->hRunLoopSrcDnsWatcher = NULL; +#endif /* IBase */ pDrvIns->IBase.pfnQueryInterface = drvNATQueryInterface; @@ -1217,6 +1365,24 @@ static DECLCALLBACK(int) drvNATConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uin pThis->INetworkNATCfg.pfnRedirectRuleCommand = drvNATNetworkNatConfig_RedirectRuleCommand; /* + * Validate the config. + */ + if (!CFGMR3AreValuesValid(pCfg, + "PassDomain\0TFTPPrefix\0BootFile\0Network" + "\0NextServer\0DNSProxy\0BindIP\0UseHostResolver\0" + "SlirpMTU\0AliasMode\0" + "SockRcv\0SockSnd\0TcpRcv\0TcpSnd\0" + "ICMPCacheLimit\0" + "SoMaxConnection\0" +#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER + "HostResolverMappings\0" +#endif + )) + return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES, + N_("Unknown NAT configuration option, only supports PassDomain," + " TFTPPrefix, BootFile and Network")); + + /* * Get the configuration settings. */ int rc; @@ -1266,8 +1432,8 @@ static DECLCALLBACK(int) drvNATConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uin "missing network"), pDrvIns->iInstance, szNetwork); - RTIPV4ADDR Network; - RTIPV4ADDR Netmask; + RTNETADDRIPV4 Network, Netmask; + rc = RTCidrStrToIPv4(szNetwork, &Network, &Netmask); if (RT_FAILURE(rc)) return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NAT#%d: Configuration error: " @@ -1277,7 +1443,7 @@ static DECLCALLBACK(int) drvNATConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uin /* * Initialize slirp. */ - rc = slirp_init(&pThis->pNATState, RT_H2N_U32(Network), Netmask, + rc = slirp_init(&pThis->pNATState, RT_H2N_U32(Network.u), Netmask.u, fPassDomain, !!fUseHostResolver, i32AliasMode, iIcmpCacheLimit, pThis); if (RT_SUCCESS(rc)) @@ -1326,7 +1492,7 @@ static DECLCALLBACK(int) drvNATConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uin AssertRC(rc); } #endif - rc = drvNATConstructRedir(pDrvIns->iInstance, pThis, pCfg, Network); + rc = drvNATConstructRedir(pDrvIns->iInstance, pThis, pCfg, &Network); if (RT_SUCCESS(rc)) { /* @@ -1390,6 +1556,60 @@ static DECLCALLBACK(int) drvNATConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uin pThis->enmLinkState = pThis->enmLinkStateWant = PDMNETWORKLINKSTATE_UP; +#ifdef RT_OS_DARWIN + /* Set up a watcher which notifies us everytime the DNS server changes. */ + int rc2 = VINF_SUCCESS; + SCDynamicStoreContext SCDynStorCtx; + + SCDynStorCtx.version = 0; + SCDynStorCtx.info = pThis; + SCDynStorCtx.retain = NULL; + SCDynStorCtx.release = NULL; + SCDynStorCtx.copyDescription = NULL; + + SCDynamicStoreRef hDynStor = SCDynamicStoreCreate(NULL, CFSTR("org.virtualbox.drvnat"), drvNatDnsChanged, &SCDynStorCtx); + if (hDynStor) + { + CFRunLoopSourceRef hRunLoopSrc = SCDynamicStoreCreateRunLoopSource(NULL, hDynStor, 0); + if (hRunLoopSrc) + { + CFStringRef aWatchKeys[] = + { + CFSTR("State:/Network/Global/DNS") + }; + CFArrayRef hArray = CFArrayCreate(NULL, (const void **)aWatchKeys, 1, &kCFTypeArrayCallBacks); + + if (hArray) + { + if (SCDynamicStoreSetNotificationKeys(hDynStor, hArray, NULL)) + { + CFRunLoopRef hRunLoopMain = CFRunLoopGetMain(); + CFRetain(hRunLoopMain); + CFRunLoopAddSource(hRunLoopMain, hRunLoopSrc, kCFRunLoopCommonModes); + CFRelease(hRunLoopMain); + pThis->hRunLoopSrcDnsWatcher = hRunLoopSrc; + } + else + rc2 = VERR_NO_MEMORY; + + CFRelease(hArray); + } + else + rc2 = VERR_NO_MEMORY; + + if (RT_FAILURE(rc2)) /* Keep the runloop source referenced for destruction. */ + CFRelease(hRunLoopSrc); + } + CFRelease(hDynStor); + } + else + rc2 = VERR_NO_MEMORY; + + if (RT_FAILURE(rc2)) + LogRel(("NAT#%d: Failed to install DNS change notifier. The guest might loose DNS access when switching networks on the host\n", + pDrvIns->iInstance)); +#endif + /* might return VINF_NAT_DNS */ return rc; } @@ -1446,7 +1666,7 @@ const PDMDRVREG g_DrvNAT = /* pfnSuspend */ NULL, /* pfnResume */ - NULL, + drvNATResume, /* pfnAttach */ NULL, /* pfnDetach */ |