/*++ Copyright (c) 1995 Microsoft Corporation Module Name: ipinip\adapter.c Abstract: The file contains the section interface of the IP in IP tunnel driver to the TCP/IP stack that is involved with Binding Notification and Querying/Setting information for interfaces The code is a cleaned up version of wanarp\ipif.c which in turn was derived from HenrySa's ip\arp.c Revision History: AmritanR --*/ #define __FILE_SIG__ ADAPTER_SIG #include "inc.h" VOID IpIpOpenAdapter( IN PVOID pvContext ) /*++ Routine Description Called by IP when the adapter from it IPAddInterface() call Locks Arguments pvContext Pointer to the TUNNEL structure Return Value None --*/ { TraceEnter(TUNN, "IpIpOpenAdapter"); // // Nothing to be done here, really // TraceLeave(TUNN, "IpIpOpenAdapter"); } VOID IpIpCloseAdapter( IN PVOID pvContext ) /*++ Routine Description Called by IP when it wants to close an adapter. Currently this is done from CloseNets() and IPDelInterface(). Locks Arguments pvContext Pointer to the TUNNEL Return Value None --*/ { TraceEnter(TUNN, "IpIpCloseAdapter"); TraceLeave(TUNN, "IpIpCloseAdapter"); } UINT IpIpAddAddress( IN PVOID pvContext, IN UINT uiType, IN DWORD dwAddress, IN DWORD dwMask, IN PVOID pvUnused ) /*++ Routine Description This routine is called by the upper layer to add an address as a local address, or specify the broadcast address for this Interface Locks Arguments Return Value NO_ERROR --*/ { TraceEnter(TUNN, "IpIpAddAddress"); TraceLeave(TUNN, "IpIpAddAddress"); return (UINT)TRUE; } UINT IpIpDeleteAddress( IN PVOID pvContext, IN UINT uiType, IN DWORD dwAddress, IN DWORD dwMask ) /*++ Routine Description Called to delete a local or proxy address. Locks Arguments Return Value NO_ERROR --*/ { TraceEnter(TUNN, "IpIpDeleteAddress"); TraceLeave(TUNN, "IpIpDeleteAddress"); return TRUE; } INT IpIpQueryInfo( IN PVOID pvIfContext, IN TDIObjectID *pTdiObjId, IN PNDIS_BUFFER pnbBuffer, IN PUINT puiSize, IN PVOID pvContext ) /*++ Routine Description Routine is called by IP to query the MIB-II information related to the TUNNEL interface Locks We acquire the TUNNEL lock. We dont need to refcount the tunnel as explained in ipinip.h Arguments pvIfContext The context we returned to IP, a pointer to the TUNNEL Return Value --*/ { PTUNNEL pTunnel; ULONG ulOffset; ULONG ulBufferSize; UINT BytesCopied = 0; BYTE rgbyInfoBuff[sizeof(IFEntry)]; DWORD dwEntity; DWORD dwInstance; IFEntry *pIFE; NTSTATUS nStatus; pTunnel = (PTUNNEL)pvIfContext; dwEntity = pTdiObjId->toi_entity.tei_entity; dwInstance = pTdiObjId->toi_entity.tei_instance; // // We support only Interface MIBs - no address xlation - pretty much like // a loopback i/f (per Henry circa 1994) // if((dwEntity isnot IF_ENTITY) or (dwInstance isnot pTunnel->dwIfInstance)) { return TDI_INVALID_REQUEST; } if(pTdiObjId->toi_type isnot INFO_TYPE_PROVIDER) { Trace(TUNN, INFO, ("IpIpQueryInfo: toi_type is wrong 0x%x\n", pTdiObjId->toi_type)); return TDI_INVALID_PARAMETER; } // // a safe initialization. // ulBufferSize = *puiSize; *puiSize = 0; ulOffset = 0; if(pTdiObjId->toi_class is INFO_CLASS_GENERIC) { if(pTdiObjId->toi_id isnot ENTITY_TYPE_ID) { Trace(TUNN, INFO, ("IpIpQueryInfo: toi_id is wrong 0x%x\n", pTdiObjId->toi_id)); return TDI_INVALID_PARAMETER; } // // He's trying to see what type we are. // if(ulBufferSize < sizeof(DWORD)) { Trace(TUNN, ERROR, ("IpIpQueryInfo: Buffer size %d too small\n", ulBufferSize)); return TDI_BUFFER_TOO_SMALL; } *(PDWORD)&rgbyInfoBuff[0] = (dwEntity is AT_ENTITY) ? AT_ARP : IF_MIB; #if NDISBUFFERISMDL nStatus = TdiCopyBufferToMdl(rgbyInfoBuff, 0, sizeof(DWORD), (PMDL)pnbBuffer, 0, &ulOffset); #else #error "Fix this" #endif *puiSize = ulOffset; return nStatus; } if(pTdiObjId->toi_class isnot INFO_CLASS_PROTOCOL) { Trace(TUNN, INFO, ("IpIpQueryInfo: toi_class is wrong 0x%x\n", pTdiObjId->toi_class)); return TDI_INVALID_PARAMETER; } // // The usermust be asking for Interface level information. // See if we support what is being asked for // if(pTdiObjId->toi_id isnot IF_MIB_STATS_ID) { Trace(TUNN, INFO, ("IpIpQueryInfo: toi_id 0x%x is not MIB_STATS\n", pTdiObjId->toi_id)); return TDI_INVALID_PARAMETER; } // // He's asking for statistics. Make sure his buffer is at least big // enough to hold the fixed part. // if(ulBufferSize < IFE_FIXED_SIZE) { Trace(TUNN, ERROR, ("IpIpQueryInfo: Buffer size %d smaller than IFE %d\n", ulBufferSize, IFE_FIXED_SIZE)); return TDI_BUFFER_TOO_SMALL; } pIFE = (IFEntry *)rgbyInfoBuff; RtlZeroMemory(pIFE, sizeof(IFEntry)); // // He's got enough to hold the fixed part. Build the IFEntry structure, // and copy it to his buffer. // pIFE->if_index = pTunnel->dwIfIndex; pIFE->if_type = IF_TYPE_TUNNEL; pIFE->if_physaddrlen = ARP_802_ADDR_LENGTH; RtlCopyMemory(pIFE->if_physaddr, pTunnel->rgbyHardwareAddr, ARP_802_ADDR_LENGTH); pIFE->if_mtu = pTunnel->ulMtu; pIFE->if_speed = DEFAULT_SPEED; pIFE->if_adminstatus = GetAdminState(pTunnel); pIFE->if_operstatus = pTunnel->dwOperState; pIFE->if_lastchange = pTunnel->dwLastChange; pIFE->if_inoctets = pTunnel->ulInOctets; pIFE->if_inucastpkts = pTunnel->ulInUniPkts; pIFE->if_innucastpkts = pTunnel->ulInNonUniPkts; pIFE->if_indiscards = pTunnel->ulInDiscards; pIFE->if_inerrors = pTunnel->ulInErrors; pIFE->if_inunknownprotos = pTunnel->ulInUnknownProto; pIFE->if_outoctets = pTunnel->ulOutOctets; pIFE->if_outucastpkts = pTunnel->ulOutUniPkts; pIFE->if_outnucastpkts = pTunnel->ulOutNonUniPkts; pIFE->if_outdiscards = pTunnel->ulOutDiscards; pIFE->if_outerrors = pTunnel->ulOutErrors; pIFE->if_outqlen = pTunnel->ulOutQLen; pIFE->if_descrlen = strlen(VENDOR_DESCRIPTION_STRING); #if NDISBUFFERISMDL nStatus = TdiCopyBufferToMdl(pIFE, 0, IFE_FIXED_SIZE, (PMDL)pnbBuffer, 0, &ulOffset); #else #error "Fix this" #endif // // See if he has room for the descriptor string. // if(ulBufferSize < (IFE_FIXED_SIZE + strlen(VENDOR_DESCRIPTION_STRING))) { Trace(TUNN, ERROR, ("IpIpQueryInfo: Buffer size %d too small for VENDOR string\n", ulBufferSize)); // // Not enough room to copy the desc. string. // *puiSize = IFE_FIXED_SIZE; return TDI_BUFFER_OVERFLOW; } #if NDISBUFFERISMDL nStatus = TdiCopyBufferToMdl(VENDOR_DESCRIPTION_STRING, 0, strlen(VENDOR_DESCRIPTION_STRING), (PMDL)pnbBuffer, ulOffset, &ulOffset); #else #error "Fix this" #endif *puiSize = IFE_FIXED_SIZE + strlen(VENDOR_DESCRIPTION_STRING); return TDI_SUCCESS; } INT IpIpSetRequest( PVOID pvContext, NDIS_OID Oid, UINT Type ) { return NDIS_STATUS_SUCCESS; } INT IpIpSetInfo( IN PVOID pvContext, IN TDIObjectID *pTdiObjId, IN PVOID pvBuffer, IN UINT uiSize ) { PTUNNEL pTunnel; INT iStatus; IFEntry *pIFE; DWORD dwEntity; DWORD dwInstance; KIRQL kiIrql; pIFE = (IFEntry *)pvBuffer; pTunnel = (PTUNNEL)pvContext; dwEntity = pTdiObjId->toi_entity.tei_entity; dwInstance = pTdiObjId->toi_entity.tei_instance; // // Might be able to handle this. // if((dwEntity isnot IF_ENTITY) or (dwInstance isnot pTunnel->dwIfInstance)) { return TDI_INVALID_REQUEST; } // // It's for the I/F level, see if it's for the statistics. // if (pTdiObjId->toi_class isnot INFO_CLASS_PROTOCOL) { Trace(TUNN, INFO, ("IpIpSetInfo: toi_class is wrong 0x%x\n", pTdiObjId->toi_class)); return TDI_INVALID_PARAMETER; } if (pTdiObjId->toi_id isnot IF_MIB_STATS_ID) { Trace(TUNN, INFO, ("IpIpSetInfo: toi_id 0x%x is not MIB_STATS\n", pTdiObjId->toi_id)); return TDI_INVALID_PARAMETER; } // // It's for the stats. Make sure it's a valid size. // if(uiSize < IFE_FIXED_SIZE) { Trace(TUNN, ERROR, ("IpIpSetInfo: Buffer size %d too small\n", uiSize)); return TDI_BUFFER_TOO_SMALL; } // // Good size // RtAcquireSpinLock(&(pTunnel->rlLock), &kiIrql); switch(pIFE->if_adminstatus) { case IF_STATUS_UP: { iStatus = TDI_SUCCESS; if(GetAdminState(pTunnel) is IF_STATUS_UP) { // // Nothing to do // break; } pTunnel->dwAdminState = (pTunnel->dwAdminState & 0xFFFF0000) | IF_STATUS_UP; if(pTunnel->dwAdminState & TS_ADDRESS_PRESENT) { // // This will set the oper. status // UpdateMtuAndReachability(pTunnel); } break; } case IF_STATUS_DOWN: { iStatus = TDI_SUCCESS; if(GetAdminState(pTunnel) is IF_STATUS_DOWN) { // // Nothing to do // break; } pTunnel->dwAdminState = (pTunnel->dwAdminState & 0xFFFF0000) | IF_STATUS_DOWN; pTunnel->dwOperState = IF_OPER_STATUS_NON_OPERATIONAL; break; } case IF_STATUS_TESTING: { // // Not supported, just return SUCCESS // iStatus = TDI_SUCCESS; break; } default: { iStatus = TDI_INVALID_PARAMETER; break; } } RtReleaseSpinLock(&(pTunnel->rlLock), kiIrql); return iStatus; } INT IpIpGetEntityList( IN PVOID pvContext, IN TDIEntityID *pTdiEntityList, IN PUINT puiCount ) { PTUNNEL pTunnel; UINT uiEntityCount; UINT uiMyIFBase; UINT i; TDIEntityID *pTdiIFEntity; KIRQL kiIrql; pTunnel = (PTUNNEL)pvContext; // // Walk down the list, looking for existing IF entities, and // adjust our base instance accordingly. // uiMyIFBase = 0; pTdiIFEntity = NULL; for(i = 0; i < *puiCount; i++, pTdiEntityList++) { if(pTdiEntityList->tei_entity is IF_ENTITY) { // // if we are already on the list remember our entity item // o/w find an instance # for us. // if((pTdiEntityList->tei_instance is pTunnel->dwIfInstance) and (pTdiEntityList->tei_instance isnot INVALID_ENTITY_INSTANCE)) { // // Matched our instance // pTdiIFEntity = pTdiEntityList; break; } else { // // Take the max of the two // uiMyIFBase = uiMyIFBase > (pTdiEntityList->tei_instance + 1)? uiMyIFBase : (pTdiEntityList->tei_instance + 1); } } } RtAcquireSpinLock(&(pTunnel->rlLock), &kiIrql); if(pTdiIFEntity is NULL ) { // // we are not on the list. // make sure we have the room for it. // if (*puiCount >= MAX_TDI_ENTITIES) { return FALSE; } pTunnel->dwIfInstance = uiMyIFBase; // // Now fill it in. // pTdiEntityList->tei_entity = IF_ENTITY; pTdiEntityList->tei_instance = uiMyIFBase; (*puiCount)++; } else { if(pTunnel->dwAdminState & TS_DELETING) { pTunnel->dwIfInstance = INVALID_ENTITY_INSTANCE; pTdiEntityList->tei_instance = INVALID_ENTITY_INSTANCE; } } RtReleaseSpinLock(&(pTunnel->rlLock), kiIrql); return TRUE; } INT IpIpBindAdapter( IN PNDIS_STATUS pnsRetStatus, IN NDIS_HANDLE nhBindContext, IN PNDIS_STRING pnsAdapterName, IN PVOID pvSS1, IN PVOID pvSS2 ) /*++ Routine Description Called by IP to bind an adapter. Locks The routine acquires the global adapter list lock, so it is not PAGEABLE. Arguments Return Value --*/ { #if 0 DWORD fFlags; PTUNNEL pNewTunnel; // Newly created adapter block. UNICODE_STRING usTempUnicodeString; NTSTATUS nStatus; KIRQL irql; #if DBG ANSI_STRING asTempAnsiString; #endif TraceEnter(TUNN, "IpIpBindAdapter"); // // All our adapter names must be upper case // // // Increase length so we have space to null terminate // pnsAdapterName->Length += sizeof(WCHAR); // // Allocate memory for the string, instead of passing TRUE to RtlUpcase // because that allocates from the heap // usTempUnicodeString.Buffer = RtAllocate(NonPagedPool, pnsAdapterName->Length, STRING_TAG); if(usTempUnicodeString.Buffer is NULL) { Trace(TUNN, ERROR, ("IpIpBindAdapter: Unable to allocate %d bytes\n", pnsAdapterName->Length)); return STATUS_INSUFFICIENT_RESOURCES; } usTempUnicodeString.MaximumLength = pnsAdapterName->Length; #if DBG asTempAnsiString.Buffer = RtAllocate(NonPagedPool, (pnsAdapterName->Length + 1)/2, STRING_TAG); if(asTempAnsiString.Buffer is NULL) { Trace(TUNN, ERROR, ("IpIpBindAdapter: Unable to allocate %d bytes\n", (pnsAdapterName->Length + 1)/2)); RtFree(usTempUnicodeString.Buffer); usTempUnicodeString.MaximumLength = 0; return STATUS_INSUFFICIENT_RESOURCES; } asTempAnsiString.MaximumLength = (pnsAdapterName->Length + 1)/2; #endif RtlUpcaseUnicodeString(&usTempUnicodeString, pnsAdapterName, FALSE); pnsAdapterName->Length -= sizeof(WCHAR); // // Null terminate the temp string // usTempUnicodeString.Buffer[usTempUnicodeString.MaximumLength/sizeof(WCHAR) - 1] = UNICODE_NULL; #if DBG // // This must be run at PASSIVE // RtlUnicodeStringToAnsiString(&asTempAnsiString, &usTempUnicodeString, FALSE); asTempAnsiString.Length -= sizeof(CHAR); #endif usTempUnicodeString.Length -= sizeof(WCHAR); Trace(TUNN, INFO, ("IpIpBindAdapter: IP called to bind to adapter %S\n", usTempUnicodeString.Buffer)); // // Lock the TUNNEL list - since we may be adding // EnterWriter(&g_rwlTunnelLock, &irql); // // Since we dont NdisOpenAdapter on the bindings we may // get duplicates. Check if this has already been indicated // if(IsBindingPresent(&usTempUnicodeString)) { ExitWriter(&g_rwlTunnelLock, irql); Trace(TUNN, WARN, ("IpIpBindAdapter: Adapter %S already present\n", usTempUnicodeString.Buffer)); *pnsRetStatus = NDIS_STATUS_SUCCESS; RtFree(usTempUnicodeString.Buffer); usTempUnicodeString.MaximumLength = 0; #if DBG RtFree(asTempAnsiString.Buffer); asTempAnsiString.MaximumLength = 0; #endif TraceLeave(TUNN, "IpIpBindAdapter"); return (int) TRUE; } pNewTunnel = NULL; #if DBG nStatus = CreateTunnel(&usTempUnicodeString, &pNewTunnel, &asTempAnsiString); #else nStatus = CreateTunnel(&usTempUnicodeString, &pNewTunnel); #endif ExitWriter(&g_rwlTunnelLock, irql); #if DBG RtFree(asTempAnsiString.Buffer); asTempAnsiString.MaximumLength = 0; #endif if(nStatus isnot STATUS_SUCCESS) { Trace(TUNN, ERROR, ("IpIpBindAdapter: CreateTunnel failed with error %x for %w\n", nStatus, usTempUnicodeString.Buffer)); *pnsRetStatus = NDIS_STATUS_FAILURE; RtFree(usTempUnicodeString.Buffer); usTempUnicodeString.MaximumLength = 0; TraceLeave(TUNN, "IpIpBindAdapter"); return (int) TRUE; } InterlockedIncrement(&g_ulNumTunnels); // // The tunnel has been created with a ref count of 2 // RtAssert(pNewTunnel); // // At this point the TUNNEL is ref counted , but not locked // We add it to IP (and keep a ref count because IP has a pointer to // the structure) // if(AddInterfaceToIP(pNewTunnel, &usTempUnicodeString, pvSS1, pvSS2) isnot STATUS_SUCCESS) { Trace(TUNN, ERROR, ("IpIpBindAdapter: Add interface to IP failed for adapter %S\n", usTempUnicodeString.Buffer)); // // Remove the TUNNEL from the list // EnterWriter(&g_rwlTunnelLock, &irql); RemoveEntryList(&(pNewTunnel->leTunnelLink)); ExitWriter(&g_rwlTunnelLock, irql); // // Since one ref count was kept because the list had a pointer, // deref it once // DereferenceTunnel(pNewTunnel); *pnsRetStatus = NDIS_STATUS_FAILURE; } else { // // We are done with the // *pnsRetStatus = NDIS_STATUS_SUCCESS; // // The tunnel was added to IP, so increment the ref count // ReferenceTunnel(pNewTunnel); Trace(TUNN, TRACE, ("IpIpBindAdapter: Successfully bound to adapter %ws\n", usTempUnicodeString.Buffer)); } // // This bit of code is done with the TUNNEL. // If everything succeeded, the the current refcount is 3 // One is because it is on the TUNNEL list, the second because it // is with IP and ofcourse the third because of this code // So we deref it here and the refcount is 2, as it should be // // If the add to IP failed, then the refcount is 1 (because we would // never incremented it for adding it to IP, and RemoveTunnel() would // have decremented the refcount by 1 // So derefing it here will free the memory // DereferenceTunnel(pNewTunnel); RtFree(usTempUnicodeString.Buffer); usTempUnicodeString.MaximumLength = 0; TraceLeave(TUNN, "IpIpBindAdapter"); return (int) TRUE; } NTSTATUS AddInterfaceToIP( IN PTUNNEL pTunnel, IN PNDIS_STRING pnsName, IN PVOID pvSystemSpecific1, IN PVOID pvSystemSpecific2 ) /*++ Routine Description Adds an interface to IP. We add one interface for every adapter Code is pageable hence must be called at PASSIVE Locks The TUNNEL must be ref counted BUT NOT LOCKED Arguments Return Value --*/ { LLIPBindInfo BindInfo; IP_STATUS IPStatus; NDIS_STRING IPConfigName = NDIS_STRING_CONST("IPConfig"); NDIS_STRING nsRemoteAddrName = NDIS_STRING_CONST("RemoteAddress"); NDIS_STRING nsLocalAddrName = NDIS_STRING_CONST("LocalAddress"); NDIS_STATUS nsStatus; NDIS_HANDLE nhConfigHandle; PNDIS_CONFIGURATION_PARAMETER pParam; PAGED_CODE(); TraceEnter(TUNN, "AddInterfaceToIP"); // // Open the key for this "adapter" // NdisOpenProtocolConfiguration(&nsStatus, &nhConfigHandle, (PNDIS_STRING)pvSystemSpecific1); if(nsStatus isnot NDIS_STATUS_SUCCESS) { Trace(TUNN, ERROR, ("AddInterfaceToIP: Unable to Open Protocol Configuration %x\n", nsStatus)); TraceLeave(TUNN, "AddInterfaceToIP"); return nsStatus; } // // Read in the IPConfig string. If this is not present, // fail this call. // NdisReadConfiguration(&nsStatus, &pParam, nhConfigHandle, &IPConfigName, NdisParameterMultiString); if((nsStatus isnot NDIS_STATUS_SUCCESS) or (pParam->ParameterType isnot NdisParameterMultiString)) { Trace(TUNN, ERROR, ("AddInterfaceToIP: Unable to Read Configuration. Status %x \n", nsStatus)); NdisCloseConfiguration(nhConfigHandle); TraceLeave(TUNN, "AddInterfaceToIP"); return STATUS_UNSUCCESSFUL; } // // We add one interface per adapter for IP // BindInfo.lip_context = pTunnel; BindInfo.lip_mss = pTunnel->ulMtu; BindInfo.lip_speed = DEFAULT_SPEED; // // Our "ARP" header is an IPHeader // BindInfo.lip_txspace = sizeof(IPHeader); BindInfo.lip_transmit = IpIpSend; BindInfo.lip_transfer = IpIpTransferData; BindInfo.lip_returnPkt = IpIpReturnPacket; BindInfo.lip_close = IpIpCloseAdapter; BindInfo.lip_addaddr = IpIpAddAddress; BindInfo.lip_deladdr = IpIpDeleteAddress; BindInfo.lip_invalidate = IpIpInvalidateRce; BindInfo.lip_open = IpIpOpenAdapter; BindInfo.lip_qinfo = IpIpQueryInfo; BindInfo.lip_setinfo = IpIpSetInfo; BindInfo.lip_getelist = IpIpGetEntityList; BindInfo.lip_flags = LIP_COPY_FLAG | LIP_NOIPADDR_FLAG | LIP_P2P_FLAG; BindInfo.lip_addrlen = ARP_802_ADDR_LENGTH; BindInfo.lip_addr = pTunnel->rgbyHardwareAddr; BindInfo.lip_dowakeupptrn = NULL; BindInfo.lip_pnpcomplete = NULL; BindInfo.lip_OffloadFlags = 0; BindInfo.lip_arpflushate = NULL; BindInfo.lip_arpflushallate = NULL; BindInfo.lip_setndisrequest = NULL; IPStatus = g_pfnIpAddInterface(pnsName, &(pParam->ParameterData.StringData), NULL, pTunnel, IpIpDynamicRegister, &BindInfo, 0); NdisCloseConfiguration(nhConfigHandle); if(IPStatus isnot IP_SUCCESS) { // // NB: freeing of resources not done. // Trace(TUNN, ERROR, ("AddInterfaceToIP: IPAddInterface failed for %w\n", pTunnel->usBindName.Buffer)); TraceLeave(TUNN, "AddInterfaceToIP"); return STATUS_UNSUCCESSFUL; } Trace(TUNN, TRACE, ("IPAddInterface succeeded for adapter %w\n", pTunnel->usBindName.Buffer)); TraceLeave(TUNN, "AddInterfaceToIP"); #endif // 0 return STATUS_SUCCESS; } NTSTATUS IpIpCreateAdapter( IN PIPINIP_CREATE_TUNNEL pCreateInfo, IN USHORT usKeyLength, OUT PDWORD pdwIfIndex ) /*++ Routine Description Our dynamic interface creation routine. Looks a bit like a bindadapter call Locks The routine acquires the global adapter list lock, so it is not PAGEABLE. Arguments pCreateInfo Info from the ioctl usKeyLength Length in bytes of the pwszKeyName (without the NULL) Return Value STATUS_OBJECT_NAME_EXISTS --*/ { DWORD fFlags; PTUNNEL pNewTunnel; UNICODE_STRING usTempUnicodeString; NTSTATUS nStatus; KIRQL irql; USHORT usOldLength; PWCHAR pwszBuffer; #if DBG ANSI_STRING asTempAnsiString; #endif TraceEnter(TUNN, "IpIpCreateAdapter"); nStatus = RtlStringFromGUID(&(pCreateInfo->Guid), &usTempUnicodeString); if(nStatus isnot STATUS_SUCCESS) { Trace(TUNN, ERROR, ("IpIpCreateAdapter: Unable to create GUID\n")); TraceLeave(TUNN, "IpIpCreateAdapter"); return nStatus; } // // Now create a non-paged buffer to store the GUID string. This is because // the Rtl routines allocate memory from heap // usOldLength = usTempUnicodeString.Length; RtAssert((usOldLength % sizeof(WCHAR)) == 0); pwszBuffer = RtAllocate(NonPagedPool, usOldLength + sizeof(WCHAR), STRING_TAG); if(pwszBuffer is NULL) { Trace(TUNN, ERROR, ("IpIpCreateAdapter: Unable to allocate %d bytes\n", usOldLength + sizeof(WCHAR))); RtlFreeUnicodeString(&usTempUnicodeString); TraceLeave(TUNN, "IpIpCreateAdapter"); return STATUS_INSUFFICIENT_RESOURCES; } RtlCopyMemory(pwszBuffer, usTempUnicodeString.Buffer, usOldLength); // // Zero out the last bit // pwszBuffer[usOldLength/sizeof(WCHAR)] = UNICODE_NULL; // // Now free the old string, make usTempUnicodeString point to this // non paged buffer // RtlFreeUnicodeString(&usTempUnicodeString); usTempUnicodeString.Buffer = pwszBuffer; usTempUnicodeString.MaximumLength = usOldLength + sizeof(WCHAR); usTempUnicodeString.Length = usOldLength; // // Increase the length of the unicode string so that // the ansi string is null terminated // usTempUnicodeString.Length += sizeof(WCHAR); #if DBG // // This must be run at PASSIVE // asTempAnsiString.Buffer = RtAllocate(NonPagedPool, usTempUnicodeString.MaximumLength, STRING_TAG); if(asTempAnsiString.Buffer is NULL) { Trace(TUNN, ERROR, ("IpIpCreateAdapter: Unable to allocate %d bytes\n", usTempUnicodeString.MaximumLength)); RtFree(usTempUnicodeString.Buffer); usTempUnicodeString.Buffer = NULL; usTempUnicodeString.MaximumLength = 0; usTempUnicodeString.Length = 0; TraceLeave(TUNN, "IpIpCreateAdapter"); return STATUS_INSUFFICIENT_RESOURCES; } RtlUnicodeStringToAnsiString(&asTempAnsiString, &usTempUnicodeString, FALSE); asTempAnsiString.Length -= sizeof(CHAR); #endif usTempUnicodeString.Length -= sizeof(WCHAR); Trace(TUNN, INFO, ("IpIpCreateAdapter: IP called to bind to adapter %S\n", usTempUnicodeString.Buffer)); // // Lock the TUNNEL list - since we may be adding // EnterWriter(&g_rwlTunnelLock, &irql); // // Make sure this is not a duplicate // if(IsBindingPresent(&usTempUnicodeString)) { ExitWriter(&g_rwlTunnelLock, irql); Trace(TUNN, WARN, ("IpIpCreateAdapter: Adapter %S already present\n", usTempUnicodeString.Buffer)); #if DBG RtFree(asTempAnsiString.Buffer); asTempAnsiString.Buffer = NULL; asTempAnsiString.MaximumLength = 0; asTempAnsiString.Length = 0; #endif RtFree(usTempUnicodeString.Buffer); usTempUnicodeString.Buffer = NULL; usTempUnicodeString.MaximumLength = 0; usTempUnicodeString.Length = 0; TraceLeave(TUNN, "IpIpCreateAdapter"); return STATUS_OBJECT_NAME_EXISTS; } pNewTunnel = NULL; #if DBG nStatus = CreateTunnel(&usTempUnicodeString, &pNewTunnel, &asTempAnsiString); #else nStatus = CreateTunnel(&usTempUnicodeString, &pNewTunnel); #endif ExitWriter(&g_rwlTunnelLock, irql); #if DBG RtFree(asTempAnsiString.Buffer); asTempAnsiString.Buffer = NULL; asTempAnsiString.MaximumLength = 0; asTempAnsiString.Length = 0; #endif if(nStatus isnot STATUS_SUCCESS) { Trace(TUNN, ERROR, ("IpIpCreateAdapter: CreateTunnel failed with error %x for %w\n", nStatus, usTempUnicodeString.Buffer)); RtFree(usTempUnicodeString.Buffer); usTempUnicodeString.Buffer = NULL; usTempUnicodeString.MaximumLength = 0; usTempUnicodeString.Length = 0; TraceLeave(TUNN, "IpIpCreateAdapter"); return nStatus; } InterlockedIncrement(&g_ulNumTunnels); // // The tunnel has been created with a ref count of 2 // RtAssert(pNewTunnel); // // At this point the TUNNEL is ref counted , but not locked // We add it to IP (and keep a ref count because IP has a pointer to // the structure) // nStatus = AddInterfaceToIP2(pNewTunnel, &usTempUnicodeString); if(nStatus isnot STATUS_SUCCESS) { Trace(TUNN, ERROR, ("IpIpCreateAdapter: Add interface to IP failed for adapter %S\n", usTempUnicodeString.Buffer)); // // Remove the TUNNEL from the list // EnterWriter(&g_rwlTunnelLock, &irql); RemoveEntryList(&(pNewTunnel->leTunnelLink)); ExitWriter(&g_rwlTunnelLock, irql); // // Since one ref count was kept because the list had a pointer, // deref it once // DereferenceTunnel(pNewTunnel); } else { // // The tunnel was added to IP, so increment the ref count // ReferenceTunnel(pNewTunnel); Trace(TUNN, TRACE, ("IpIpCreateAdapter: Successfully bound to adapter %ws\n", usTempUnicodeString.Buffer)); } *pdwIfIndex = pNewTunnel->dwIfIndex; // // This bit of code is done with the TUNNEL. // If everything succeeded, the the current refcount is 3 // One is because it is on the TUNNEL list, the second because it // is with IP and ofcourse the third because of this code // So we deref it here and the refcount is 2, as it should be // // If the add to IP failed, then the refcount is 1 (because we would // never incremented it for adding it to IP, and RemoveTunnel() would // have decremented the refcount by 1 // So derefing it here will free the memory // DereferenceTunnel(pNewTunnel); RtFree(usTempUnicodeString.Buffer); usTempUnicodeString.Buffer = NULL; usTempUnicodeString.MaximumLength = 0; usTempUnicodeString.Length = 0; TraceLeave(TUNN, "IpIpCreateAdapter"); return nStatus; } NTSTATUS AddInterfaceToIP2( IN PTUNNEL pTunnel, IN PNDIS_STRING pnsName ) /*++ Routine Description Adds an interface to IP. We add one interface for every adapter Code is pageable hence must be called at PASSIVE Locks The TUNNEL must be ref counted BUT NOT LOCKED Arguments Return Value --*/ { LLIPBindInfo BindInfo; IP_STATUS IPStatus; NDIS_STRING nsIPConfigKey, nsIfName; NDIS_STATUS nsStatus; ULONG ulKeyLen, ulPrefixLen; PWCHAR pwszKeyBuffer, pwszNameBuffer; PAGED_CODE(); TraceEnter(TUNN, "AddInterfaceToIP2"); // // Fake the key for the adapter // ulPrefixLen = wcslen(TCPIP_INTERFACES_KEY); ulKeyLen = pnsName->Length + ((ulPrefixLen + 1) * sizeof(WCHAR)); pwszKeyBuffer = RtAllocate(NonPagedPool, ulKeyLen, TUNNEL_TAG); if(pwszKeyBuffer is NULL) { Trace(TUNN, ERROR, ("AddInterfaceToIP2: Couldnt allocate %d bytes of paged pool\n", ulKeyLen)); return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(pwszKeyBuffer, ulKeyLen); nsIPConfigKey.MaximumLength = (USHORT)ulKeyLen; nsIPConfigKey.Length = (USHORT)ulKeyLen - sizeof(WCHAR); nsIPConfigKey.Buffer = pwszKeyBuffer; // // Copy over the prefix // RtlCopyMemory(pwszKeyBuffer, TCPIP_INTERFACES_KEY, ulPrefixLen * sizeof(WCHAR)); // // Cat the name // RtlCopyMemory(&(pwszKeyBuffer[ulPrefixLen]), pnsName->Buffer, pnsName->Length); // // TCPIP expects the name of the interface to be of the type \Device\ // The name in pnsName is only , so create a new string // ulPrefixLen = wcslen(TCPIP_IF_PREFIX); ulKeyLen = pnsName->Length + ((ulPrefixLen + 1) * sizeof(WCHAR)); pwszNameBuffer = RtAllocate(NonPagedPool, ulKeyLen, TUNNEL_TAG); if(pwszNameBuffer is NULL) { Trace(TUNN, ERROR, ("AddInterfaceToIP2: Couldnt allocate %d bytes for name\n", ulKeyLen)); return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(pwszNameBuffer, ulKeyLen); nsIfName.MaximumLength = (USHORT)ulKeyLen; nsIfName.Length = (USHORT)ulKeyLen - sizeof(WCHAR); nsIfName.Buffer = pwszNameBuffer; // // Start with \Device\ // RtlCopyMemory(pwszNameBuffer, TCPIP_IF_PREFIX, ulPrefixLen * sizeof(WCHAR)); // // Cat the name // RtlCopyMemory(&(pwszNameBuffer[ulPrefixLen]), pnsName->Buffer, pnsName->Length); RtlZeroMemory(&BindInfo, sizeof(LLIPBindInfo)); // // We add one interface per adapter for IP // BindInfo.lip_context = pTunnel; BindInfo.lip_mss = pTunnel->ulMtu; BindInfo.lip_speed = DEFAULT_SPEED; // // Our "ARP" header is an IPHeader // BindInfo.lip_txspace = sizeof(IPHeader); BindInfo.lip_transmit = IpIpSend; BindInfo.lip_transfer = IpIpTransferData; BindInfo.lip_returnPkt = IpIpReturnPacket; BindInfo.lip_close = IpIpCloseAdapter; BindInfo.lip_addaddr = IpIpAddAddress; BindInfo.lip_deladdr = IpIpDeleteAddress; BindInfo.lip_invalidate = IpIpInvalidateRce; BindInfo.lip_open = IpIpOpenAdapter; BindInfo.lip_qinfo = IpIpQueryInfo; BindInfo.lip_setinfo = IpIpSetInfo; BindInfo.lip_getelist = IpIpGetEntityList; BindInfo.lip_flags = LIP_NOIPADDR_FLAG | LIP_P2P_FLAG | LIP_COPY_FLAG; BindInfo.lip_addrlen = ARP_802_ADDR_LENGTH; BindInfo.lip_addr = pTunnel->rgbyHardwareAddr; BindInfo.lip_dowakeupptrn = NULL; BindInfo.lip_pnpcomplete = NULL; BindInfo.lip_setndisrequest = IpIpSetRequest; IPStatus = g_pfnIpAddInterface(&nsIfName, NULL, &nsIPConfigKey, NULL, pTunnel, IpIpDynamicRegister, &BindInfo, 0, IF_TYPE_TUNNEL, IF_ACCESS_POINTTOPOINT, IF_CONNECTION_DEDICATED); RtFree(pwszKeyBuffer); RtFree(pwszNameBuffer); if(IPStatus isnot IP_SUCCESS) { Trace(TUNN, ERROR, ("AddInterfaceToIP2: IPAddInterface failed for %w\n", pTunnel->usBindName.Buffer)); TraceLeave(TUNN, "AddInterfaceToIP2"); return STATUS_UNSUCCESSFUL; } Trace(TUNN, TRACE, ("IPAddInterface succeeded for adapter %w\n", pTunnel->usBindName.Buffer)); TraceLeave(TUNN, "AddInterfaceToIP2"); return STATUS_SUCCESS; } INT IpIpDynamicRegister( IN PNDIS_STRING InterfaceName, IN PVOID pvIpInterfaceContext, IN struct _IP_HANDLERS * IpHandlers, IN struct LLIPBindInfo * ARPBindInfo, IN UINT uiInterfaceNumber ) { PTUNNEL pTunnel; KIRQL irql; TraceEnter(TUNN, "DynamicRegisterIp"); pTunnel = (PTUNNEL)(ARPBindInfo->lip_context); RtAcquireSpinLock(&(pTunnel->rlLock), &irql); #if DBG Trace(TUNN, INFO, ("IP called out to dynamically register %s\n", pTunnel->asDebugBindName.Buffer)); #endif pTunnel->pvIpContext = pvIpInterfaceContext; pTunnel->dwIfIndex = uiInterfaceNumber; if(g_pfnIpRcv is NULL) { g_pfnIpRcv = IpHandlers->IpRcvHandler; g_pfnIpRcvComplete = IpHandlers->IpRcvCompleteHandler; g_pfnIpSendComplete = IpHandlers->IpTxCompleteHandler; g_pfnIpTDComplete = IpHandlers->IpTransferCompleteHandler; g_pfnIpStatus = IpHandlers->IpStatusHandler; g_pfnIpRcvPkt = IpHandlers->IpRcvPktHandler; g_pfnIpPnp = IpHandlers->IpPnPHandler; } RtReleaseSpinLock(&(pTunnel->rlLock), irql); TraceLeave(TUNN, "DynamicRegisterIp"); return TRUE; } BOOLEAN IsBindingPresent( IN PUNICODE_STRING pusBindName ) /*++ Routine Description Code to catch duplicate bind notifications Locks Must be called with the g_rwlTunnelLock held as READER Arguments Return Value TRUE if an adapter with a matching name was found FALSE otherwise --*/ { BOOLEAN bFound; PTUNNEL pTunnel; PLIST_ENTRY pleNode; bFound = FALSE; for(pleNode = g_leTunnelList.Flink; pleNode != &g_leTunnelList; pleNode = pleNode->Flink) { pTunnel = CONTAINING_RECORD(pleNode, TUNNEL, leTunnelLink); if(CompareUnicodeStrings(&(pTunnel->usBindName), pusBindName)) { bFound = TRUE; break; } } return bFound; } #if DBG NTSTATUS CreateTunnel( IN PNDIS_STRING pnsBindName, OUT TUNNEL **ppNewTunnel, IN PANSI_STRING pasAnsiName ) #else NTSTATUS CreateTunnel( IN PNDIS_STRING pnsBindName, OUT TUNNEL **ppNewTunnel ) #endif /*++ Routine Description Creates and initializes an TUNNEL when we get a bind notification The tunnel is added to the tunnel list Locks Must be called with the g_rwlTunnelLock held as WRITER Arguments pnsBindName Name of binding ppNewTunnel Pointer to location to return a pointer to created tunnel pasAnsiBindName Only in DBG versions. Binding name, as ANSI string Return Value STATUS_SUCCESS STATUS_NO_MEMORY --*/ { DWORD dwSize; PBYTE pbyBuffer; PTUNNEL pTunnel; PTDI_ADDRESS_IP pTdiIp; TraceEnter(TUNN, "CreateTunnel"); *ppNewTunnel = NULL; // // The size that one needs is the size of the adapter + the length of the // name. Add 4 to help with alignment // dwSize = ALIGN_UP(sizeof(TUNNEL),ULONG) + ALIGN_UP(pnsBindName->Length + sizeof(WCHAR), ULONG); #if DBG // // For debug code we also store the adapter name in ANSI // dwSize += ALIGN_UP((pnsBindName->Length/sizeof(WCHAR)) + sizeof(CHAR), ULONG); #endif pTunnel = RtAllocate(NonPagedPool, dwSize, TUNNEL_TAG); if(pTunnel is NULL) { Trace(TUNN, ERROR, ("CreateTunnel: Failed to allocate memory\n")); TraceLeave(TUNN, "CreateTunnel"); return STATUS_NO_MEMORY; } // // Clear all the fields out // RtlZeroMemory(pTunnel, dwSize); // // The Unicode name buffer starts at the end of the adapter structure. // pbyBuffer = (PBYTE)pTunnel + sizeof(TUNNEL); // // We DWORD align it for better compare/copy // pbyBuffer = ALIGN_UP_POINTER(pbyBuffer, ULONG); pTunnel->usBindName.Length = pnsBindName->Length; pTunnel->usBindName.MaximumLength = pnsBindName->Length; pTunnel->usBindName.Buffer = (PWCHAR)(pbyBuffer); RtlCopyMemory(pTunnel->usBindName.Buffer, pnsBindName->Buffer, pnsBindName->Length); #if DBG // // The debug string comes after the UNICODE adapter name buffer // pbyBuffer = pbyBuffer + pnsBindName->Length + sizeof(WCHAR); pbyBuffer = ALIGN_UP_POINTER(pbyBuffer, ULONG); pTunnel->asDebugBindName.Buffer = pbyBuffer; pTunnel->asDebugBindName.MaximumLength = pasAnsiName->MaximumLength; pTunnel->asDebugBindName.Length = pasAnsiName->Length; RtlCopyMemory(pTunnel->asDebugBindName.Buffer, pasAnsiName->Buffer, pasAnsiName->Length); #endif // // Must be set to INVALID so that GetEntityList can work // pTunnel->dwATInstance = INVALID_ENTITY_INSTANCE; pTunnel->dwIfInstance = INVALID_ENTITY_INSTANCE; // // Set the admin state to UP, but mark the interface unmapped // pTunnel->dwAdminState = IF_ADMIN_STATUS_UP; pTunnel->dwOperState = IF_OPER_STATUS_NON_OPERATIONAL; // // This hardware index is needed to generate the Unique ID that // DHCP uses. // NOTE - we dont have an index so all hardware addrs will be the same // BuildHardwareAddrFromIndex(pTunnel->rgbyHardwareAddr, pTunnel->dwIfIndex); // // Initialize the lock for the tunnel // RtInitializeSpinLock(&(pTunnel->rlLock)); InitRefCount(pTunnel); pTunnel->ulMtu = DEFAULT_MTU; // // Initialize the TDI related stuff // pTunnel->tiaIpAddr.TAAddressCount = 1; pTunnel->tiaIpAddr.Address[0].AddressLength = TDI_ADDRESS_LENGTH_IP; pTunnel->tiaIpAddr.Address[0].AddressType = TDI_ADDRESS_TYPE_IP; pTunnel->tciConnInfo.UserDataLength = 0; pTunnel->tciConnInfo.UserData = NULL; pTunnel->tciConnInfo.OptionsLength = 0; pTunnel->tciConnInfo.Options = NULL; pTunnel->tciConnInfo.RemoteAddress = &(pTunnel->tiaIpAddr); pTunnel->tciConnInfo.RemoteAddressLength = sizeof(pTunnel->tiaIpAddr); InitBufferPool(&(pTunnel->HdrBufferPool), HEADER_BUFFER_SIZE, 0, 5, 0, TRUE, HEADER_TAG); InitPacketPool(&(pTunnel->PacketPool), PACKET_RSVD_LENGTH, 0, 20, 0, PACKET_TAG); // // Initialize the packet queue // InitializeListHead(&(pTunnel->lePacketQueueHead)); InsertHeadList(&g_leTunnelList, &(pTunnel->leTunnelLink)); *ppNewTunnel = pTunnel; TraceLeave(TUNN, "CreateTunnel"); return STATUS_SUCCESS; } VOID DeleteTunnel( IN PTUNNEL pTunnel ) /*++ Routine Description Locks Arguments Return Value NO_ERROR --*/ { TraceEnter(TUNN, "DeleteTunnel"); Trace(TUNN, INFO, ("DeleteTunnel: Deleting tunnel 0x%x. Index %d\n", pTunnel, pTunnel->dwIfIndex)); if(FreeBufferPool(&(pTunnel->HdrBufferPool)) is FALSE) { Trace(TUNN, ERROR, ("DeleteTunnel: Couldnt free buffer pool %x for tunnel %x\n", pTunnel, pTunnel->HdrBufferPool)); RtAssert(FALSE); } RtFree(pTunnel); TraceLeave(TUNN, "DeleteTunnel"); } PTUNNEL FindTunnel( IN PULARGE_INTEGER puliTunnelId ) /*++ Routine Description Routine to lookup tunnels given a TunnelId (which is a 64 bit integer created by concatenating the RemoteEnpoint Address and LocalEndpoint Address) The tunnel returned is refcounted and locked Locks The g_rwlTunnelLock must be taken as READER Arguments uliTunnelId a 64 bit integer created by concatenating the RemoteEndpoint Address and LocalEndpoint Address Return Value Address of the TUNNEL if found NULL otherwise --*/ { PLIST_ENTRY pleNode; PTUNNEL pTunnel; for(pleNode = g_leTunnelList.Flink; pleNode isnot &g_leTunnelList; pleNode = pleNode->Flink) { pTunnel = CONTAINING_RECORD(pleNode, TUNNEL, leTunnelLink); if(pTunnel->uliTunnelId.QuadPart is puliTunnelId->QuadPart) { RtAcquireSpinLockAtDpcLevel(&(pTunnel->rlLock)); ReferenceTunnel(pTunnel); return pTunnel; } } return NULL; } PTUNNEL FindTunnelGivenIndex( IN DWORD dwIfIndex ) /*++ Routine Description Routine to lookup tunnels given an IfIndex. This is a slow routine The tunnel returned is refcounted - BUT NOT LOCKED Locks The g_rwlTunnelLock must be taken as READER Arguments dwIfIndex Interface Index of the tunnel Return Value Address of the TUNNEL if found NULL otherwise --*/ { PLIST_ENTRY pleNode; PTUNNEL pTunnel; for(pleNode = g_leTunnelList.Flink; pleNode isnot &g_leTunnelList; pleNode = pleNode->Flink) { pTunnel = CONTAINING_RECORD(pleNode, TUNNEL, leTunnelLink); if(pTunnel->dwIfIndex is dwIfIndex) { RtAcquireSpinLockAtDpcLevel(&(pTunnel->rlLock)); ReferenceTunnel(pTunnel); return pTunnel; } } return NULL; } VOID RemoveAllTunnels( VOID ) /*++ Routine Description Removes all the tunnels in the system. We remove the tunnel from the list and delete the corresponding interface from IP, dereferencing the tunnel twice. If the tunnels is not being used any more, this should delete the tunnel Locks This is called from the Unload handler so does not need any locks Arguments None Return Value None --*/ { PLIST_ENTRY pleNode; PTUNNEL pTunnel; while(!IsListEmpty(&g_leTunnelList)) { pleNode = RemoveHeadList(&g_leTunnelList); pTunnel = CONTAINING_RECORD(pleNode, TUNNEL, leTunnelLink); // // Deref the tunnel once for deleting it from the list // DereferenceTunnel(pTunnel); // // Delete the interface from IP // g_pfnIpDeleteInterface(pTunnel->pvIpContext, TRUE); // // Dereference the tunnel for deleting it from IP // DereferenceTunnel(pTunnel); } g_ulNumTunnels = 0; }