/*++ Copyright (c) 1995 Microsoft Corporation Module Name: net\routing\ipx\adptif\adptif.c Abstract: Router/sap agent interface to ipx stack Author: Vadim Eydelman Revision History: --*/ #include "ipxdefs.h" #ifdef UNICODE #define _UNICODE #endif // [pmay] Make adptif pnp aware #include "pnp.h" // [pmay] The global trace id. DWORD g_dwTraceId = 0; DWORD g_dwBufferId = 0; WCHAR ISN_IPX_NAME[] = L"\\Device\\NwlnkIpx"; // Ipx stack driver name ULONG InternalNetworkNumber=0; // Internal network parameters UCHAR INTERNAL_NODE_ADDRESS[6]={0,0,0,0,0,1}; IO_STATUS_BLOCK IoctlStatus; // IO status buffer for config change notifications HANDLE IpxDriverHandle; // Driver handle for config change notifications LONG AdapterChangeApcPending = 0; /* DWORD (APIENTRY * RouterQueueWorkItemProc) (WORKERFUNCTION, PVOID, BOOL)=NULL; // Thread management API routine when running // under router #define InRouter() (RouterQueueWorkItemProc!=NULL) */ LIST_ENTRY PortListHead; // List of configuraiton ports PCONFIG_PORT IpxWanPort; // Special port for IPX WAN CRITICAL_SECTION ConfigInfoLock; // Protects access to port and message queues ULONG NumAdapters; // Total number of available adapters (used // to estimate the size of the buffer passed to // the driver in config change notification calls) DWORD OpenAdapterConfigPort ( void ); NTSTATUS CloseAdapterConfigPort ( PVOID pvConfigBuffer ); VOID APIENTRY PostAdapterConfigRequest ( PVOID context ); NTSTATUS ProcessAdapterConfigInfo ( PVOID pvConfigBuffer ); DWORD InitializeMessageQueueForClient ( PCONFIG_PORT config ); VOID AdapterChangeAPC ( PVOID context, PIO_STATUS_BLOCK IoStatus, ULONG Reserved ); VOID IpxSendCompletion ( IN PVOID Context, IN PIO_STATUS_BLOCK IoStatus, IN ULONG Reserved ); VOID IpxRecvCompletion ( IN PVOID Context, IN PIO_STATUS_BLOCK IoStatus, IN ULONG Reserved ); VOID FwCleanup ( void ); // [pmay] Synchronize the forwarder with nic id renumberings // in the stack. DWORD FwRenumberNics (DWORD dwOpCode, USHORT usThreshold); #if DBG && defined(WATCHER_DIALOG) #include "watcher.c" #endif /*++ ******************************************************************* D l l M a i n Routine Description: Dll initialization and cleanup Arguments: hinstDLL, handle of DLL module fdwReason, reason for calling function lpvReserved reserved Return Value: TRUE intialized OK FALSE failed Remarks: Return value is only valid when called with DLL_PROCESS_ATTACH reason This DLL makes use of CRT.DLL, so this entry point should be called from CRT.DLL entry point ******************************************************************* --*/ BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved ) { BOOL res = FALSE; TCHAR ProcessFileName[MAX_PATH]; DWORD cnt; switch (fdwReason) { case DLL_PROCESS_ATTACH: // We are being attached to a new process // Initialize the system that maps virtual nic ids to physical ones NicMapInitialize (); InitializeCriticalSection (&ConfigInfoLock); InitializeListHead (&PortListHead); IpxWanPort = NULL; // Register with the trace utility g_dwTraceId = TraceRegisterExA("IpxAdptif", 0); #if DBG && defined(WATCHER_DIALOG) InitializeWatcher (hinstDLL); #endif res = TRUE; break; case DLL_PROCESS_DETACH: // The process is exiting #if DBG && defined(WATCHER_DIALOG) CleanupWatcher (); #endif DeleteCriticalSection (&ConfigInfoLock); FwCleanup (); TraceDeregisterExA(g_dwTraceId , 4); // Cleanup the system that maps virtual nic ids to physical ones NicMapCleanup (); default: // Not interested in all other cases res = TRUE; break; } return res; } // Debug functions char * DbgStatusToString(DWORD dwStatus) { switch (dwStatus) { case NIC_CREATED: return "Created"; case NIC_DELETED: return "Deleted"; case NIC_CONNECTED: return "Connected"; case NIC_DISCONNECTED: return "Disconnected"; case NIC_LINE_DOWN: return "Line down"; case NIC_LINE_UP: return "Line up"; case NIC_CONFIGURED: return "Configured"; } return "Unknown"; } // Debugging functions char * DbgTypeToString(DWORD dwType) { switch (dwType) { case NdisMedium802_3: return "802.3"; case NdisMedium802_5: return "802.5"; case NdisMediumFddi: return "Fddi"; case NdisMediumWan: return "Wan"; case NdisMediumLocalTalk: return "LocalTalk"; case NdisMediumDix: return "Dix"; case NdisMediumArcnetRaw: return "Raw Arcnet"; case NdisMediumArcnet878_2: return "878.2"; case NdisMediumAtm: return "Atm"; case NdisMediumWirelessWan: return "Wireless Wan"; case NdisMediumIrda: return "Irda"; case NdisMediumBpc: return "Bpc"; case NdisMediumCoWan: return "Co Wan"; case NdisMediumMax: return "Maxium value allowed"; } return "Unknown"; } // Returns the op-code (for nic id renumbering) associated with this // message DWORD GetNicOpCode(PIPX_NIC_INFO pNic) { DWORD dwOp = (DWORD)(pNic->Status & 0xf0); pNic->Status &= 0x0f; return dwOp; } // Inserts the op-code (for nic id renumbering) associated with this // message DWORD PutNicOpCode(PIPX_NIC_INFO pNic, DWORD dwOp) { pNic->Status |= dwOp; return dwOp; } // Outputs a list of nics to the tracing service DWORD DbgDisplayNics(PIPX_NIC_INFO NicPtr, DWORD dwNicCount) { DWORD i; for (i = 0; i < dwNicCount; i++) { PUCHAR ln = NicPtr[i].Details.Node; USHORT NicId = NicPtr[i].Details.NicId; BOOLEAN Status = NicPtr[i].Status; GetNicOpCode(&NicPtr[i]); TracePrintf(g_dwTraceId, "[R=%d V=%x: %s]: Net=%x IfNum=%d Local=%x%x%x%x%x%x Type= %s", NicId, NicMapGetVirtualNicId(NicId), DbgStatusToString(NicPtr[i].Status), NicPtr[i].Details.NetworkNumber, NicPtr[i].InterfaceIndex, ln[0], ln[1], ln[2], ln[3], ln[4], ln[5], DbgTypeToString(NicPtr[i].NdisMediumType) ); NicPtr[i].Status = Status; } TracePrintf(g_dwTraceId, "\n"); return NO_ERROR; } int DVNID (int x) { USHORT tmp; tmp = (USHORT)NicMapGetVirtualNicId((USHORT)x); return (tmp < 50) ? tmp : -1; } int DRNID (int x) { USHORT tmp; tmp = NicMapGetPhysicalNicId((USHORT)x); return (tmp < 50) ? tmp : -1; } // Outputs the virtual to physical adapter map DWORD DbgDisplayMap() { USHORT i; /* for (i = 0; i < 6; i++) { PUCHAR m = GlobalNicIdMap.nidMacAddrs[i]; if (m) { TracePrintf(g_dwTraceId, "Real %d \tis Virtual %x \t(%x->%x) \twith Mac %x%x%x%x%x%x", i, NicMapGetVirtualNicId(i), i, NicMapGetPhysicalNicId(i), m[0], m[1], m[2], m[3], m[4], m[5]); } else { TracePrintf(g_dwTraceId, "Real %d \tis Virtual %x \t(%x->%x)", i, NicMapGetVirtualNicId(i), i, NicMapGetPhysicalNicId(i)); } } */ TracePrintf(g_dwTraceId, "R: %3d %3d %3d %3d %3d %3d %3d %3d %3d %3d", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); TracePrintf(g_dwTraceId, "V: %3d %3d %3d %3d %3d %3d %3d %3d %3d %3d", DVNID(1), DVNID(2), DVNID(3), DVNID(4), DVNID(5), DVNID(6), DVNID(7), DVNID(8), DVNID(9), DVNID(10)); TracePrintf(g_dwTraceId, "V: %3d %3d %3d %3d %3d %3d %3d %3d %3d %3d", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); TracePrintf(g_dwTraceId, "R: %3d %3d %3d %3d %3d %3d %3d %3d %3d %3d\n", DRNID(1), DRNID(2), DRNID(3), DRNID(4), DRNID(5), DRNID(6), DRNID(7), DRNID(8), DRNID(9), DRNID(10)); return NO_ERROR; } /*++ C r e a t e S o c k e t P o r t Routine Description: Creates port to communicate over IPX socket Arguments: Socket - IPX socket number to use (network byte order) Return Value: Handle to communication port that provides async interface to IPX stack. Returns INVALID_HANDLE_VALUE if port can not be opened --*/ HANDLE WINAPI CreateSocketPort( IN USHORT Socket ) { NTSTATUS status; HANDLE AddressHandle; IO_STATUS_BLOCK IoStatusBlock; UNICODE_STRING FileString; OBJECT_ATTRIBUTES ObjectAttributes; CHAR spec[IPX_ENDPOINT_SPEC_BUFFER_SIZE]; #define ea ((PFILE_FULL_EA_INFORMATION)&spec) #define TrAddress ((PTRANSPORT_ADDRESS)&ea->EaName[ROUTER_INTERFACE_LENGTH+1]) #define TaAddress ((PTA_ADDRESS)&TrAddress->Address[0]) #define IpxAddress ((PTDI_ADDRESS_IPX)&TaAddress->Address[0]) RtlInitUnicodeString (&FileString, ISN_IPX_NAME); InitializeObjectAttributes( &ObjectAttributes, &FileString, OBJ_CASE_INSENSITIVE, NULL, NULL); ea->NextEntryOffset = 0; ea->Flags = 0; ea->EaNameLength = ROUTER_INTERFACE_LENGTH; RtlCopyMemory (ea->EaName, ROUTER_INTERFACE, ROUTER_INTERFACE_LENGTH + 1); ea->EaValueLength = sizeof(TRANSPORT_ADDRESS) - 1 + sizeof(TDI_ADDRESS_IPX); TrAddress->TAAddressCount = 1; TaAddress->AddressType = TDI_ADDRESS_TYPE_IPX; TaAddress->AddressLength = sizeof(TDI_ADDRESS_IPX); IpxAddress->Socket = Socket; status = NtCreateFile( &AddressHandle, GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, // returned status information 0, // block size (unused). 0, // file attributes. FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_CREATE, // create disposition. 0, // create options. ea, sizeof (spec) ); if (NT_SUCCESS (status)) { SetLastError (NO_ERROR); return AddressHandle; } else { #if DBG DbgPrint ("NtCreateFile (router if) failed with status %08x\n", status); #endif RtlNtStatusToDosError (status); // Sets last error in Teb } return INVALID_HANDLE_VALUE; #undef TrAddress #undef TaAddress #undef IpxAddress } /*++ D e l e t e S o c k e t P o r t Routine Description: Cancel all the outstandng requests and dispose of all the resources allocated for communication port Arguments: Handle - Handle to communication port to be disposed of Return Value: NO_ERROR - success Windows error code - operation failed --*/ DWORD WINAPI DeleteSocketPort ( HANDLE Handle ) { return RtlNtStatusToDosError (NtClose (Handle)); } /*++ I p x S e n d C o m p l e t i o n Routine Description: Io APC. Calls client provided completion routine Arguments: Context - Pointer to client completion routine IoStatus - status of completed io operation (clients overlapped structure is used as the buffer) Reserved - ??? Return Value: None --*/ VOID IpxSendCompletion ( IN PVOID Context, IN PIO_STATUS_BLOCK IoStatus, IN ULONG Reserved ) { if (NT_SUCCESS (IoStatus->Status)) (*(LPOVERLAPPED_COMPLETION_ROUTINE)Context) (NO_ERROR, // Adjust byte trasferred parameter to // include header supplied in the // packet ((DWORD)IoStatus->Information+=sizeof (IPX_HEADER)), (LPOVERLAPPED)IoStatus); else (*(LPOVERLAPPED_COMPLETION_ROUTINE)Context) ( RtlNtStatusToDosError (IoStatus->Status), // Adjust byte trasferred parameter to // include header supplied in the // packet if something was sent (IoStatus->Information > 0) ? ((DWORD)IoStatus->Information += sizeof (IPX_HEADER)) : ((DWORD)IoStatus->Information = 0), (LPOVERLAPPED)IoStatus); } /*++ I p x R e c v C o m p l e t i on Routine Description: Io APC. Calls client provided completion routine Arguments: Context - Pointer to client completion routine IoStatus - status of completed io operation (clients overlapped structure is used as the buffer) Reserved - ??? Return Value: None --*/ VOID IpxRecvCompletion ( IN PVOID Context, IN PIO_STATUS_BLOCK IoStatus, IN ULONG Reserved ) { if (NT_SUCCESS (IoStatus->Status)) (*(LPOVERLAPPED_COMPLETION_ROUTINE)Context) (NO_ERROR, // Substract size of options header // reported by the driver in the beggining // of the buffer ((DWORD)IoStatus->Information -= FIELD_OFFSET (IPX_DATAGRAM_OPTIONS2,Data)), (LPOVERLAPPED)IoStatus); else (*(LPOVERLAPPED_COMPLETION_ROUTINE)Context) ( RtlNtStatusToDosError (IoStatus->Status), // Substract size of options header // reported by the driver in the beggining // of the buffer if dirver was able // to actually receive something (not // just options in the buffer ((DWORD)IoStatus->Information > FIELD_OFFSET (IPX_DATAGRAM_OPTIONS2,Data)) ? ((DWORD)IoStatus->Information -= FIELD_OFFSET (IPX_DATAGRAM_OPTIONS2,Data)) : ((DWORD)IoStatus->Information = 0), (LPOVERLAPPED)IoStatus); } /*++ I p x G e t O v e r l a p p e d R e s u l t Routine Description: GetOverlappedResult wrapper: gives adptif.dll a chance to adjust returned parameters (currently number of bytes transferred). Arguments: Same as in GetOverlappedResult (see SDK doc) Return Value: Same as in GetOverlappedResult (see SDK doc) --*/ BOOL IpxGetOverlappedResult ( HANDLE Handle, LPOVERLAPPED lpOverlapped, LPDWORD lpNumberOfBytesTransferred, BOOL bWait ) { BOOL res = GetOverlappedResult (Handle, lpOverlapped, lpNumberOfBytesTransferred, bWait); if (res) { if (NT_SUCCESS (lpOverlapped->Internal)) { if (lpOverlapped->Offset==MIPX_SEND_DATAGRAM) *lpNumberOfBytesTransferred += sizeof (IPX_HEADER); else if (lpOverlapped->Offset==MIPX_RCV_DATAGRAM) *lpNumberOfBytesTransferred -= FIELD_OFFSET (IPX_DATAGRAM_OPTIONS2,Data); // else - neither, for packets generated with // PostQueuedCompletionStatus } else { if (lpOverlapped->Offset==MIPX_SEND_DATAGRAM) { if (*lpNumberOfBytesTransferred>0) *lpNumberOfBytesTransferred += sizeof (IPX_HEADER); } else if (lpOverlapped->Offset==MIPX_RCV_DATAGRAM) { if (*lpNumberOfBytesTransferred>FIELD_OFFSET (IPX_DATAGRAM_OPTIONS2,Data)) *lpNumberOfBytesTransferred -= FIELD_OFFSET (IPX_DATAGRAM_OPTIONS2,Data); else *lpNumberOfBytesTransferred = 0; } // else - neither, for packets generated with // PostQueuedCompletionStatus } } return res; } /*++ I p x G e t Q u e u e d C o m p l e t i o n S t a t u s Routine Description: GetQueuedCompletionStatus wrapper: gives adptif.dll a chance to adjust returned parameters (currently number of bytes transferred) Arguments: Same as in GetQueuedCompletionStatus (see SDK doc) Return Value: Same as in GetQueuedCompletionStatus (see SDK doc) --*/ BOOL IpxGetQueuedCompletionStatus( HANDLE CompletionPort, LPDWORD lpNumberOfBytesTransferred, PULONG_PTR lpCompletionKey, LPOVERLAPPED *lpOverlapped, DWORD dwMilliseconds ) { BOOL res = GetQueuedCompletionStatus (CompletionPort, lpNumberOfBytesTransferred, lpCompletionKey, lpOverlapped, dwMilliseconds); if (res) { if (NT_SUCCESS ((*lpOverlapped)->Internal)) { if ((*lpOverlapped)->Offset==MIPX_SEND_DATAGRAM) { *lpNumberOfBytesTransferred += sizeof (IPX_HEADER); (*lpOverlapped)->InternalHigh = *lpNumberOfBytesTransferred; } else if ((*lpOverlapped)->Offset==MIPX_RCV_DATAGRAM) { *lpNumberOfBytesTransferred -= FIELD_OFFSET (IPX_DATAGRAM_OPTIONS2,Data); (*lpOverlapped)->InternalHigh = *lpNumberOfBytesTransferred; } // else - neither, for packets generated with // PostQueuedCompletionStatus } else { if ((*lpOverlapped)->Offset==MIPX_SEND_DATAGRAM) { if (*lpNumberOfBytesTransferred>0) { *lpNumberOfBytesTransferred += sizeof (IPX_HEADER); (*lpOverlapped)->InternalHigh = *lpNumberOfBytesTransferred; } } else if ((*lpOverlapped)->Offset==MIPX_RCV_DATAGRAM) { if (*lpNumberOfBytesTransferred>FIELD_OFFSET (IPX_DATAGRAM_OPTIONS2,Data)) { *lpNumberOfBytesTransferred -= FIELD_OFFSET (IPX_DATAGRAM_OPTIONS2,Data); (*lpOverlapped)->InternalHigh = *lpNumberOfBytesTransferred; } else { *lpNumberOfBytesTransferred = 0; (*lpOverlapped)->InternalHigh = *lpNumberOfBytesTransferred; } } // else - neither, for packets generated with // PostQueuedCompletionStatus } } return res; } /*++ I p x A d j u s t I o C o m p l e t i o n P a r a m s Routine Description: Adjust io completion parameters for io performed by IpxSendPacket or IpxReceivePacket and completed through the mechanisms other than routines provided above Arguments: lpOverlapped - overlapped structure passed to Ipx(Send/Recv)Packet routines lpNumberOfBytesTransferred - adjusted number of bytes transferred in io error - win32 error code Return Value: None --*/ VOID IpxAdjustIoCompletionParams ( IN OUT LPOVERLAPPED lpOverlapped, OUT LPDWORD lpNumberOfBytesTransferred, OUT LPDWORD error ) { if (NT_SUCCESS (lpOverlapped->Internal)) { if (lpOverlapped->Offset==MIPX_SEND_DATAGRAM) { lpOverlapped->InternalHigh += sizeof (IPX_HEADER); *lpNumberOfBytesTransferred = (DWORD)lpOverlapped->InternalHigh; } else if (lpOverlapped->Offset==MIPX_RCV_DATAGRAM) { lpOverlapped->InternalHigh -= FIELD_OFFSET (IPX_DATAGRAM_OPTIONS2,Data); *lpNumberOfBytesTransferred = (DWORD)lpOverlapped->InternalHigh; } // else - neither, for packets generated with // PostQueuedCompletionStatus *error = NO_ERROR; } else { if (lpOverlapped->Offset==MIPX_SEND_DATAGRAM) { if (lpOverlapped->InternalHigh>0) { lpOverlapped->InternalHigh += sizeof (IPX_HEADER); *lpNumberOfBytesTransferred = (DWORD)lpOverlapped->InternalHigh; } } else if (lpOverlapped->Offset==MIPX_RCV_DATAGRAM) { if (lpOverlapped->InternalHigh>FIELD_OFFSET (IPX_DATAGRAM_OPTIONS2,Data)) { lpOverlapped->InternalHigh -= FIELD_OFFSET (IPX_DATAGRAM_OPTIONS2,Data); *lpNumberOfBytesTransferred = (DWORD)lpOverlapped->InternalHigh; } else { lpOverlapped->InternalHigh = 0; *lpNumberOfBytesTransferred = 0; } } // else - neither, for packets generated with // PostQueuedCompletionStatus *error = RtlNtStatusToDosError ((DWORD)lpOverlapped->Internal); } } /*++ I p x P o s t Q u e u e d C o m p l e t i o n S t a t u s Routine Description: PostQueuedCompletionStatus wrapper: gives adptif.dll a chance to setup lpOverlapped so it can be correctly processed by the IpxGetQueueCompletionStatus and IpxGetOverlappedResult Arguments: Same as in PostQueuedCompletionStatus (see SDK doc) Return Value: Same as in PostQueuedCompletionStatus (see SDK doc) --*/ BOOL IpxPostQueuedCompletionStatus( HANDLE CompletionPort, DWORD dwNumberOfBytesTransferred, DWORD dwCompletionKey, LPOVERLAPPED lpOverlapped ) { lpOverlapped->Offset = 0; return PostQueuedCompletionStatus (CompletionPort, dwNumberOfBytesTransferred, dwCompletionKey, lpOverlapped); } /*++ I p x S e n d P a c k e t Routine Description: Enqueue request to receive IPX packet and return immediately. Event will be signalled or comletion routine will be called when done Arguments: Handle - Handle to adapter & socket to use AdapterIdx - adapter on which to send IpxPacket - ipx packet complete with header IpxPacketLength - length of the packet pReserved - buffer to supply info to IPX stack lpOverlapped - structure to be used for async IO: Internal - reserved InternalHigh - reserved Offset - not used OffsetHigh - not used hEvent - event to be signalled when IO completes or NULL if CompletionRoutine is to be called CompletionRoutine - to be called when IO operation is completes Return Value: NO_ERROR - if lpOverlapped->hEvent!=NULL, then recv has successfully completed (do not need to wait on event), otherwise, recv operation has started and completion routine will be called when done ERROR_IO_PENDING - only returned if lpOverlapped->hEvent!=NULL and recv could not be completed immediately, event will be signalled when operation is done: call GetOverlapedResult to retrieve result of the operation other (windows error code) - operation could not be started (completion routine won't be called) --*/ DWORD WINAPI IpxSendPacket ( IN HANDLE Handle, IN ULONG AdapterIdx, IN PUCHAR IpxPacket, IN ULONG IpxPacketLength, IN PADDRESS_RESERVED lpReserved, LPOVERLAPPED lpOverlapped, LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine ) { #define hdr ((PIPX_HEADER)IpxPacket) #define opt ((PIPX_DATAGRAM_OPTIONS2)lpReserved) NTSTATUS status; // Send the data to the correct physical index AdapterIdx = (ULONG)NicMapGetPhysicalNicId((USHORT)AdapterIdx); // Put IPX header parameters into datagram options: // Packet type opt->DgrmOptions.PacketType = hdr->pkttype; // Source opt->DgrmOptions.LocalTarget.NicId = (USHORT)AdapterIdx; IPX_NODENUM_CPY (&opt->DgrmOptions.LocalTarget.MacAddress, hdr->dst.node); // Destination IPX_NODENUM_CPY (&opt->RemoteAddress.NodeAddress, hdr->dst.node); IPX_NETNUM_CPY (&opt->RemoteAddress.NetworkAddress, hdr->dst.net); opt->RemoteAddress.Socket = hdr->dst.socket; lpOverlapped->Offset = MIPX_SEND_DATAGRAM; status = NtDeviceIoControlFile( Handle, lpOverlapped->hEvent, ((lpOverlapped->hEvent!=NULL) || (CompletionRoutine==NULL)) ? NULL : IpxSendCompletion, CompletionRoutine ? (LPVOID)CompletionRoutine : (LPVOID)lpOverlapped, (PIO_STATUS_BLOCK)lpOverlapped, MIPX_SEND_DATAGRAM, lpReserved, sizeof (IPX_DATAGRAM_OPTIONS2), &hdr[1], IpxPacketLength-sizeof (IPX_HEADER) ); if (NT_SUCCESS (status)) { SetLastError (NO_ERROR); return NO_ERROR; } #if DBG DbgPrint ("Ioctl MIPX_SEND_DATAGRAM failed with status %08x\n", status); #endif return RtlNtStatusToDosError (status); #undef hdr #undef opt } /*++ I p x R e c v P a c k e t Routine Description: Enqueue request to receive IPX packet and return immediately. Event will be signalled or comletion routine will be called when done Arguments: Handle - Handle to adapter & socket to use AdapterIdx - adapter on which to packet was received (set upon completion) IpxPacket - buffer for ipx packet (complete with header) IpxPacketLength - length of the buffer pReserved - buffer to get info from IPX stack lpOverlapped - structure to be used for async IO: Internal - Reserved InternalHigh - Reserved Offset - not used OffsetHigh - not used hEvent - event to be signalled when IO completes or NULL if CompletionRoutine is to be called CompletionRoutine - to be called when IO operation is completes Return Value: NO_ERROR - if lpOverlapped->hEvent!=NULL, then send has successfully completed (do not need to wait on event), otherwise, send operation has started and completion routine will be called when done ERROR_IO_PENDING - only returned if lpOverlapped->hEvent!=NULL and send could not be completed immediately, event will be signalled when operation is done: call GetOverlapedResult to retrieve result of the operation other (windows error code) - operation could not be started (completion routine won't be called) --*/ DWORD WINAPI IpxRecvPacket( IN HANDLE Handle, OUT PUCHAR IpxPacket, IN ULONG IpxPacketLength, IN PADDRESS_RESERVED lpReserved, LPOVERLAPPED lpOverlapped, LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine ) { NTSTATUS status; // A temporary hack (due to the available ipx interface): ASSERTMSG ("Packet buffer does not follow reserved area ", IpxPacket==(PUCHAR)(&lpReserved[1])); lpOverlapped->Offset = MIPX_RCV_DATAGRAM; status = NtDeviceIoControlFile( Handle, lpOverlapped->hEvent, ((lpOverlapped->hEvent!=NULL) || (CompletionRoutine==NULL)) ? NULL : IpxRecvCompletion, CompletionRoutine ? (LPVOID)CompletionRoutine : (LPVOID)lpOverlapped, (PIO_STATUS_BLOCK)lpOverlapped, MIPX_RCV_DATAGRAM, NULL, 0, lpReserved, FIELD_OFFSET (IPX_DATAGRAM_OPTIONS2,Data) + IpxPacketLength ); if (NT_SUCCESS (status)) { SetLastError (NO_ERROR); return NO_ERROR; } #if DBG DbgPrint ("Ioctl MIPX_RCV_DATAGRAM failed with status %08x\n", status); #endif return RtlNtStatusToDosError (status); } /*++ I p x C r e a t e A d a p t e r C o n f i g u r a t i o n P o r t Routine Description: Register client that wants to be updated of any changes in adapter state Arguments: NotificationEvent - event to be signaled when adapter state changes AdptGlobalParameters - parameters that common to all adapters Return Value: Handle to configuration port thru which changes in adapter state are reported. Returns INVALID_HANDLE_VALUE if port could not be created --*/ HANDLE WINAPI IpxCreateAdapterConfigurationPort(IN HANDLE NotificationEvent, OUT PADAPTERS_GLOBAL_PARAMETERS AdptGlobalParameters) { PCONFIG_PORT port; INT i; DWORD error=NO_ERROR; TracePrintf( g_dwTraceId, "IpxCreateAdapterConfigurationPort: entered."); // Allocate port data structure port = (PCONFIG_PORT) RtlAllocateHeap (RtlProcessHeap (), 0, sizeof (CONFIG_PORT)); if (port == NULL) { TracePrintf( g_dwTraceId, "IpxCreateAdapterConfigurationPort: unable to allocate port."); SetLastError (ERROR_NOT_ENOUGH_MEMORY); return INVALID_HANDLE_VALUE; } // Initialize port data structure port->event = NotificationEvent; InitializeListHead (&port->msgqueue); // Make sure we own the list EnterCriticalSection (&ConfigInfoLock); // Open channel to IPX stack if not already opened // if (IpxDriverHandle == NULL) { TracePrintf( g_dwTraceId, "IpxCreateAdapterConfigurationPort: calling OpenAdapterConfigPort."); error = OpenAdapterConfigPort(); } else { error = NO_ERROR; } if (error==NO_ERROR) { // Add messages about existing adapters to the beginning of the queue // (to be seen only by the new client) error = InitializeMessageQueueForClient(port); if (error==NO_ERROR) { InsertTailList (&PortListHead, &port->link); AdptGlobalParameters->AdaptersCount = NumAdapters; } else { TracePrintf( g_dwTraceId, "IpxCreateAdapterConfigurationPort: InitMessQForClient fail."); } } else { TracePrintf( g_dwTraceId, "IpxCreateAdapterConfigurationPort: OpenAdapterConfigPort failed."); } // Release our lock on the configuration information LeaveCriticalSection (&ConfigInfoLock); if (error==NO_ERROR) return (HANDLE)port; else SetLastError (error); RtlFreeHeap (RtlProcessHeap (), 0, port); return INVALID_HANDLE_VALUE; } /*++ I p x W a n C r e a t e A d a p t e r C o n f i g u r a t i o n P o r t Routine Description: Same as above, but creates port that only reports ADAPTER_UP events on WAN adapters that require IPXWAN negotiation. IpxGetQueuedAdapterConfigurationStatus on this port should be followed by IpxWanSetAdapterConfiguration obtained during the negotiation process, and ADAPTER_UP event will then be reported to other clients (including forwarder dirver) */ HANDLE WINAPI IpxWanCreateAdapterConfigurationPort( IN HANDLE NotificationEvent, OUT PADAPTERS_GLOBAL_PARAMETERS AdptGlobalParameters ) { INT i; DWORD error=NO_ERROR; PCONFIG_PORT port; // Allocate port data structure port = (PCONFIG_PORT)RtlAllocateHeap (RtlProcessHeap (), 0, sizeof (CONFIG_PORT)); if (port!=NULL) { // Initialize port data structure port->event = NotificationEvent; InitializeListHead (&port->msgqueue); EnterCriticalSection (&ConfigInfoLock); if (IpxWanPort==NULL) { // Open channel to IPX stack if not already opened if (IpxDriverHandle==NULL) { error = OpenAdapterConfigPort (); } else error = NO_ERROR; if (error==NO_ERROR) { IpxWanPort = port; AdptGlobalParameters->AdaptersCount = NumAdapters; } } else error = ERROR_ALREADY_EXISTS; LeaveCriticalSection (&ConfigInfoLock); if (error==NO_ERROR) return (HANDLE)port; else SetLastError (error); RtlFreeHeap (RtlProcessHeap (), 0, port); } else SetLastError (ERROR_NOT_ENOUGH_MEMORY); return INVALID_HANDLE_VALUE; } /*++ I p x D e l e t e A d a p t e r C o n f i g u r a t i o n P o r t Routine Description: Unregister client Arguments: Handle - configuration port handle Return Value: NO_ERROR ERROR_INVALID_PARAMETER ERROR_GEN_FAILURE --*/ DWORD WINAPI IpxDeleteAdapterConfigurationPort ( IN HANDLE Handle ) { PCONFIG_PORT port = (PCONFIG_PORT)Handle; // Make sure we owe the list EnterCriticalSection (&ConfigInfoLock); if (port==IpxWanPort) { IpxWanPort = NULL; } else { RemoveEntryList (&port->link); } #if DBG && defined(WATCHER_DIALOG) // Adapter port is maintained by the watcher dialog #else if (IsListEmpty (&PortListHead) && (IpxWanPort==NULL)) { CloseAdapterConfigPort (NULL); } #endif LeaveCriticalSection (&ConfigInfoLock); // Delete messages that client have not dequeued while (!IsListEmpty (&port->msgqueue)) { PLIST_ENTRY cur = RemoveHeadList (&port->msgqueue); RtlFreeHeap (RtlProcessHeap (), 0, CONTAINING_RECORD (cur, ADAPTER_MSG, link)); } // Free the port itself RtlFreeHeap (RtlProcessHeap (), 0, port); return NO_ERROR; } /*++ G e t Q u e u e d A d a p t e r C o n f i g u r a t i o n S t a t u s Routine Description: Get info from the list of adapter info chages queued to the configuration info port Arguments: Handle - configuration port handle AdapterIndex - number of adapter being reported AdapterConfigurationStatus - new adapter status AdapterParameters - adapter parameters Return Value: NO_ERROR - new information is reported ERROR_NO_MORE_ITEMS - there is nothing to report Windows error code - operation failed --*/ DWORD WINAPI IpxGetQueuedAdapterConfigurationStatus(IN HANDLE Handle, OUT PULONG AdapterIndex, OUT PULONG AdapterConfigurationStatus, PADAPTER_INFO AdapterInfo) { PCONFIG_PORT port = (PCONFIG_PORT)Handle; DWORD error; PWCHAR pszName; // Make sure nothing changes while we are reading the info EnterCriticalSection (&ConfigInfoLock); // If there is something to report if (!IsListEmpty (&port->msgqueue)) { PADAPTER_MSG msg = CONTAINING_RECORD (port->msgqueue.Flink, ADAPTER_MSG, link); RemoveEntryList (&msg->link); LeaveCriticalSection (&ConfigInfoLock); // By now, the correct virtual nic id has been set *AdapterIndex = (ULONG)msg->info.Details.NicId; // Map driver reported nic states to adapter states switch (msg->info.Status) { case NIC_CREATED: case NIC_CONFIGURED: *AdapterConfigurationStatus = ADAPTER_CREATED; break; case NIC_DELETED: *AdapterConfigurationStatus = ADAPTER_DELETED; break; case NIC_LINE_UP: *AdapterConfigurationStatus = ADAPTER_UP; break; case NIC_LINE_DOWN: *AdapterConfigurationStatus = ADAPTER_DOWN; break; default: ASSERTMSG ("Unknown nic status ", FALSE); } // Copy adapter parameters to client's buffer AdapterInfo->InterfaceIndex = msg->info.InterfaceIndex; IPX_NETNUM_CPY (&AdapterInfo->Network, &msg->info.Details.NetworkNumber); IPX_NODENUM_CPY (&AdapterInfo->LocalNode, &msg->info.Details.Node); IPX_NODENUM_CPY (&AdapterInfo->RemoteNode, &msg->info.RemoteNodeAddress); AdapterInfo->LinkSpeed = msg->info.LinkSpeed; AdapterInfo->PacketType = msg->info.PacketType; AdapterInfo->MaxPacketSize = msg->info.MaxPacketSize; AdapterInfo->NdisMedium = msg->info.NdisMediumType; AdapterInfo->ConnectionId = msg->info.ConnectionId; // Copy in the adapter name pszName = wcsstr(msg->info.Details.AdapterName, L"{"); if (!pszName) pszName = (PWCHAR)msg->info.Details.AdapterName; wcsncpy(AdapterInfo->pszAdpName, pszName, MAX_ADAPTER_NAME_LEN); EnterCriticalSection (&ConfigInfoLock); if (IsListEmpty (&port->msgqueue)) { // Last message -> reset event (in case // client uses manual reset event) BOOL res = ResetEvent (port->event); ASSERTMSG ("Can't reset port event ", res); } // Decrement reference count on processed message and dispose of it // when ref count gets to 0 RtlFreeHeap(RtlProcessHeap (), 0, msg); error = NO_ERROR; // There is a message in the buffer } else if (NT_SUCCESS (IoctlStatus.Status)) { error = ERROR_NO_MORE_ITEMS; // No more messages, request is pending } else { // Last request completed with error, report it to client, // Client will have to reopen the port to force posting of new request error = RtlNtStatusToDosError (IoctlStatus.Status); #if DBG DbgPrint ("Reporting result of failed Ioctl to client: status:%0lx -> error:%ld\n", IoctlStatus.Status, error); #endif } LeaveCriticalSection (&ConfigInfoLock); SetLastError (error); return error; } // // Function: IpxGetAdapterConfig // // Queries the stack for the internal network number along with the current total // number of adapters. Function blocks until the query completes. // DWORD IpxGetAdapterConfig(OUT LPDWORD lpdwInternalNetNum, OUT LPDWORD lpdwAdapterCount) { DWORD dwErr = NO_ERROR, dwNet, dwCount; // Synchronize EnterCriticalSection (&ConfigInfoLock); // Open channel to IPX stack if not already opened if (IpxDriverHandle==NULL) dwErr = OpenAdapterConfigPort(); // Set the values that were read dwNet = InternalNetworkNumber; dwCount = NumAdapters; // Release our lock on the configuration information LeaveCriticalSection (&ConfigInfoLock); if (dwErr != NO_ERROR) return dwErr; *lpdwInternalNetNum = dwNet; *lpdwAdapterCount = dwCount; return NO_ERROR; } // // Function: IpxGetAdapterConfig // // Queries the stack for the list of all adapters currently bound to a network. // This function blocks until the query completes. // DWORD IpxGetAdapterList(OUT PIPX_ADAPTER_BINDING_INFO pAdapters, IN DWORD dwMaxAdapters, OUT LPDWORD lpdwAdaptersRead) { NTSTATUS status; PNWLINK_ACTION action; PIPX_NICS request; IO_STATUS_BLOCK IoStatus; PIPX_NIC_INFO info=NULL; DWORD dwActionBufSize, dwRead; *lpdwAdaptersRead = 0; // Calculate the size of the buffer that we'll use // to retrieve adapter information from the IPX Stack dwActionBufSize = FIELD_OFFSET (NWLINK_ACTION, Data) + FIELD_OFFSET (IPX_NICS, Data) + sizeof (IPX_NIC_INFO) * (dwMaxAdapters>0 ? dwMaxAdapters : 1); // Prepare the data to send to the IPX Stack to retrieve the // information about each adapter action = (PNWLINK_ACTION) RtlAllocateHeap(RtlProcessHeap (), 0, dwActionBufSize); if (action!=NULL) { // Initialize the action buffer with the appropriate identifiers action->Header.TransportId = ISN_ACTION_TRANSPORT_ID; action->OptionType = NWLINK_OPTION_CONTROL; action->Option = MIPX_GETNEWNICINFO; // The BufferLength includes the length of everything after it, // which is sizeof(ULONG) for Option plus whatever Data is present. action->BufferLength = sizeof (action->Option) + FIELD_OFFSET(IPX_NICS,Data) + sizeof (IPX_NIC_INFO) * (dwMaxAdapters>0 ? dwMaxAdapters : 1); // Setting this flag makes the stack return information about // all known adapters request = (PIPX_NICS)action->Data; request->NoOfNics = 0; request->TotalNoOfNics = 0; request->fAllNicsDesired = TRUE; // Send the Ioctl status = NtDeviceIoControlFile(IpxDriverHandle,NULL,NULL,NULL,&IoStatus, IOCTL_TDI_ACTION,NULL,0,action,dwActionBufSize); // Wait for it to complete if (status==STATUS_PENDING) { status = NtWaitForSingleObject (IpxDriverHandle, FALSE, NULL); if (NT_SUCCESS (status)) status = IoStatus.Status; } // Make sure it was a successful completion if (NT_SUCCESS (status)) { PADAPTER_MSG msg; PIPX_NIC_INFO NicPtr = (PIPX_NIC_INFO)request->Data; UINT i, j=0; dwRead = request->TotalNoOfNics; // Loop through the adapters for (i=0; (iDetails.NetworkNumber != 0) { pAdapters[j].AdapterIndex = (ULONG)NicMapGetVirtualNicId((USHORT)NicPtr->Details.NetworkNumber); PUTULONG2LONG(pAdapters[j].Network, NicPtr->Details.NetworkNumber); memcpy(pAdapters[j].LocalNode, NicPtr->Details.Node, 6); memcpy(pAdapters[j].RemoteNode, NicPtr->RemoteNodeAddress, 6); pAdapters[j].MaxPacketSize = NicPtr->MaxPacketSize; pAdapters[j].LinkSpeed = NicPtr->LinkSpeed; j++; } } *lpdwAdaptersRead = j; } // We're done with the action buffer we sent to the stack // now. It's safe to clean it up. RtlFreeHeap (RtlProcessHeap (), 0, action); } return NO_ERROR; } // Requests the list of adapters from the stack DWORD IpxSeedNicMap() { NTSTATUS status; PNWLINK_ACTION action; PIPX_NICS request; IO_STATUS_BLOCK IoStatus; PIPX_NIC_INFO info=NULL; DWORD dwActionBufSize, dwRead; TracePrintf(g_dwTraceId, "IpxSeedMap: entered."); // Calculate the size of the buffer that we'll use // to retrieve adapter information from the IPX Stack dwActionBufSize = FIELD_OFFSET (NWLINK_ACTION, Data) + FIELD_OFFSET (IPX_NICS, Data) + sizeof (IPX_NIC_INFO); // Prepare the data to send to the IPX Stack to retrieve the // information about each adapter action = (PNWLINK_ACTION) RtlAllocateHeap(RtlProcessHeap (), 0, dwActionBufSize); if (action!=NULL) { // Initialize the action buffer with the appropriate identifiers action->Header.TransportId = ISN_ACTION_TRANSPORT_ID; action->OptionType = NWLINK_OPTION_CONTROL; action->Option = MIPX_GETNEWNICINFO; // The BufferLength includes the length of everything after it, // which is sizeof(ULONG) for Option plus whatever Data is present. action->BufferLength = sizeof (action->Option) + FIELD_OFFSET(IPX_NICS,Data) + sizeof (IPX_NIC_INFO); // Setting this flag makes the stack return information about // all known adapters request = (PIPX_NICS)action->Data; request->NoOfNics = 0; request->TotalNoOfNics = 0; request->fAllNicsDesired = TRUE; // Send the Ioctl status = NtDeviceIoControlFile(IpxDriverHandle,NULL,NULL,NULL,&IoStatus, IOCTL_TDI_ACTION,NULL,0,action,dwActionBufSize); // Wait for it to complete if (status==STATUS_PENDING) { status = NtWaitForSingleObject (IpxDriverHandle, FALSE, NULL); if (NT_SUCCESS (status)) status = IoStatus.Status; } // Make sure it was a successful completion if (NT_SUCCESS (status)) { PADAPTER_MSG msg; PIPX_NIC_INFO NicPtr = (PIPX_NIC_INFO)request->Data; UINT i, j=0; NumAdapters = request->TotalNoOfNics; dwRead = request->NoOfNics; // Display the nics and their status as reported in this completion of the // MIPX_GETNEWNICINFO ioctl. TracePrintf(g_dwTraceId, "=========================="); TracePrintf(g_dwTraceId, "MIPX_GETNEWNICS Completed. (%d of %d adapters reported)", request->NoOfNics, request->TotalNoOfNics); TracePrintf(g_dwTraceId, "Internal Net Number: %x", InternalNetworkNumber); DbgDisplayNics(NicPtr, dwRead); // Loop through the adapters for (i=0; (iinfo.Details.NetworkNumber = dwNewNetNum; msg->info.Status = NIC_CONFIGURED; IPX_NODENUM_CPY (msg->info.Details.Node, INTERNAL_NODE_ADDRESS); // Signal event if this is the first message we process // and client queue is empty if (IsListEmpty (&pPort->msgqueue)) { BOOL res = SetEvent (pPort->event); ASSERTMSG ("Can't set client event ", res); } // Insert the message into the port's message queue. InsertTailList (&pPort->msgqueue, &msg->link); return NO_ERROR; } // // Function IpxDoesRouteExist // // Queries the stack to see if it has a route to the given network // // Arguments: // puNetwork The network-ordered network number to query for // pbRouteFound Set to true if network is found, false otherwise // // Returns: // NO_ERROR on success // Otherwise, an error that can be displayed with FormatMessage // DWORD IpxDoesRouteExist (IN PUCHAR puNetwork, OUT PBOOL pbRouteFound) { NTSTATUS status; PNWLINK_ACTION action; PISN_ACTION_GET_LOCAL_TARGET pTarget; IO_STATUS_BLOCK IoStatusBlock; PUCHAR puIoctlBuffer; DWORD dwBufferSize; // Verify parameters if (!puNetwork || !pbRouteFound) return ERROR_INVALID_PARAMETER; // Initialize *pbRouteFound = FALSE; dwBufferSize = sizeof(NWLINK_ACTION) + sizeof(ISN_ACTION_GET_LOCAL_TARGET); puIoctlBuffer = (PUCHAR) RtlAllocateHeap(RtlProcessHeap(), 0, dwBufferSize); if (!puIoctlBuffer) return ERROR_NOT_ENOUGH_MEMORY; ZeroMemory(puIoctlBuffer, dwBufferSize); // Initialize the buffer for the ioctl action = (PNWLINK_ACTION)puIoctlBuffer; action->Header.TransportId = ISN_ACTION_TRANSPORT_ID; action->OptionType = NWLINK_OPTION_CONTROL; action->Option = MIPX_LOCALTARGET; action->BufferLength = sizeof (action->Option) + sizeof(ISN_ACTION_GET_LOCAL_TARGET); pTarget = (PISN_ACTION_GET_LOCAL_TARGET) action->Data; pTarget->IpxAddress.NetworkAddress = *((ULONG*)puNetwork); // Use critical section to serialize usage of driver handle EnterCriticalSection (&ConfigInfoLock); // Ask the stack if the route exists status = NtDeviceIoControlFile(IpxDriverHandle, NULL, NULL, NULL, &IoStatusBlock, IOCTL_TDI_ACTION, NULL, 0, action, dwBufferSize); // Wait for an answer if (status == STATUS_PENDING) status = NtWaitForSingleObject (IpxDriverHandle, FALSE, NULL); LeaveCriticalSection (&ConfigInfoLock); // Find out if the route was found if (NT_SUCCESS(IoStatusBlock.Status)) *pbRouteFound = TRUE; else *pbRouteFound = FALSE; // Cleanup RtlFreeHeap (RtlProcessHeap (), 0, puIoctlBuffer); return RtlNtStatusToDosError (status); } /*++ G e t A d a p t e r N a m e W Routine Description: Returns UNICODE name of the adapter associated with given index Arguments: AdapterIndex - index of adapter AdapterNameSize - size of adapter name (in bytes), including terminal wchar NULL AdapterNameBuffer - buffer to receive adapter name Return Value: NO_ERROR - adapter name is in the buffer ERROR_INVALID_PARAMETER - adapter with given index does not exist ERROR_INSUFFICIENT_BUFFER - buffer in to small. Updates AdapterNameSize to the correct value. Other windows error code - operation failed --*/ DWORD WINAPI GetAdapterNameFromPhysNicW( IN ULONG AdapterIndex, IN OUT PULONG AdapterNameSize, OUT LPWSTR AdapterNameBuffer ) { NTSTATUS status; DWORD error; ULONG ln; PNWLINK_ACTION action; IO_STATUS_BLOCK IoStatusBlock; PISN_ACTION_GET_DETAILS details; CHAR IoctlBuffer[ sizeof (NWLINK_ACTION) +sizeof (ISN_ACTION_GET_DETAILS)]; action = (PNWLINK_ACTION)IoctlBuffer; action->Header.TransportId = ISN_ACTION_TRANSPORT_ID; action->OptionType = NWLINK_OPTION_CONTROL; action->BufferLength = sizeof (action->Option) +sizeof (ISN_ACTION_GET_DETAILS); action->Option = MIPX_CONFIG; details = (PISN_ACTION_GET_DETAILS)action->Data; details->NicId = (USHORT)AdapterIndex; // Use critical section to serialize usage of driver handle EnterCriticalSection (&ConfigInfoLock); status = NtDeviceIoControlFile( IpxDriverHandle, NULL, NULL, NULL, &IoStatusBlock, IOCTL_TDI_ACTION, NULL, 0, action, sizeof(NWLINK_ACTION) +sizeof (ISN_ACTION_GET_DETAILS)); if (status==STATUS_PENDING){ status = NtWaitForSingleObject (IpxDriverHandle, FALSE, NULL); if (NT_SUCCESS (status)) status = IoStatusBlock.Status; } LeaveCriticalSection (&ConfigInfoLock); if (NT_SUCCESS (status)) { // Compute required buffer size ln = (lstrlenW (details->AdapterName)+1)*sizeof(WCHAR); if (ln<=(*AdapterNameSize)) { // Size of provided buffer is ok, copy the result *AdapterNameSize = ln; lstrcpyW (AdapterNameBuffer,details->AdapterName); error = NO_ERROR; } else { // Caller buffer is to small *AdapterNameSize = ln; error = ERROR_INSUFFICIENT_BUFFER; } } else { error = RtlNtStatusToDosError (status); #if DBG DbgPrint ("TDI Ioctl MIPX_CONFIG failed with status %08x\n", status); #endif } return error; } DWORD WINAPI GetAdapterNameW(IN ULONG AdapterIndex, IN OUT PULONG AdapterNameSize, OUT LPWSTR AdapterNameBuffer) { return GetAdapterNameFromPhysNicW((ULONG)NicMapGetPhysicalNicId((USHORT)AdapterIndex), AdapterNameSize, AdapterNameBuffer); } DWORD WINAPI GetAdapterNameFromMacAddrW(IN PUCHAR puMacAddr, IN OUT PULONG AdapterNameSize, OUT LPWSTR AdapterNameBuffer) { // return GetAdapterNameFromPhysNicW((ULONG)GetPhysFromMac(puMacAddr), // AdapterNameSize, // AdapterNameBuffer); return NO_ERROR; } /*++ I p x W a n S e t A d a p t e r C o n f i g u r a t i o n Routine Description: Sets adapter configuration to be reported to both user and kernel mode clients (through the ADAPTER_UP/LINE_UP events) Arguments: AdapterIndex - number of adapter being set IpxWanInfo - IPXWAN negotiated parameters Return Value: NO_ERROR - adapter info set successfully Windows error code - operation failed --*/ DWORD IpxWanSetAdapterConfiguration ( IN ULONG AdapterIndex, IN PIPXWAN_INFO IpxWanInfo ) { NTSTATUS status; PNWLINK_ACTION action; IO_STATUS_BLOCK IoStatusBlock; PIPXWAN_CONFIG_DONE config; CHAR IoctlBuffer[ sizeof (NWLINK_ACTION) +sizeof (IPXWAN_CONFIG_DONE)]; action = (PNWLINK_ACTION)IoctlBuffer; action->Header.TransportId = ISN_ACTION_TRANSPORT_ID; action->OptionType = NWLINK_OPTION_CONTROL; action->BufferLength = sizeof (action->Option) +sizeof (IPXWAN_CONFIG_DONE); action->Option = MIPX_IPXWAN_CONFIG_DONE; config = (PIPXWAN_CONFIG_DONE)action->Data; config->NicId = NicMapGetPhysicalNicId((USHORT)AdapterIndex); IPX_NETNUM_CPY (&config->Network, &IpxWanInfo->Network); IPX_NODENUM_CPY (&config->LocalNode, &IpxWanInfo->LocalNode); IPX_NODENUM_CPY (&config->RemoteNode, &IpxWanInfo->RemoteNode); // Use critical section to serialize usage of driver handle EnterCriticalSection (&ConfigInfoLock); status = NtDeviceIoControlFile( IpxDriverHandle, NULL, NULL, NULL, &IoStatusBlock, IOCTL_TDI_ACTION, NULL, 0, action, sizeof(NWLINK_ACTION) +sizeof (IPXWAN_CONFIG_DONE)); if (status==STATUS_PENDING){ status = NtWaitForSingleObject (IpxDriverHandle, FALSE, NULL); if (NT_SUCCESS (status)) status = IoStatusBlock.Status; } LeaveCriticalSection (&ConfigInfoLock); #if DBG if (!NT_SUCCESS (status)) { DbgPrint ("TDI Ioctl MIPX_IPXWAN_CONFIG_DONE failed with status %08x\n", status); } #endif return RtlNtStatusToDosError (status); } /*++ I p x W a n Q u e r y I n a c t i v i t y T i m e r Routine Description: Returns value of inactivity timer associated with WAN line Arguments: ConnectionId - connection id that identifies WAN line (used only if *AdapterIndex==INVALID_NICID AdapterIndex - adapter index that identifies WAN line (preferred over connection id), if *AdapterIndex==INVALID_NICID the value of connection id is used to identify the WAN line and value of AdapterIndex is returned. InactivityCounter - value of inactivity counter. Return Value: NO_ERROR - inactivity timer reading is returned Windows error code - operation failed --*/ DWORD IpxWanQueryInactivityTimer ( IN ULONG ConnectionId, IN OUT PULONG AdapterIndex, OUT PULONG InactivityCounter ) { NTSTATUS status; PNWLINK_ACTION action; IO_STATUS_BLOCK IoStatusBlock; PIPX_QUERY_WAN_INACTIVITY query; CHAR IoctlBuffer[ sizeof (NWLINK_ACTION) +sizeof (IPX_QUERY_WAN_INACTIVITY)]; action = (PNWLINK_ACTION)IoctlBuffer; action->Header.TransportId = ISN_ACTION_TRANSPORT_ID; action->OptionType = NWLINK_OPTION_CONTROL; action->BufferLength = sizeof (action->Option) +sizeof (IPX_QUERY_WAN_INACTIVITY); action->Option = MIPX_QUERY_WAN_INACTIVITY; query = (PIPX_QUERY_WAN_INACTIVITY)action->Data; query->ConnectionId = ConnectionId; query->NicId = NicMapGetPhysicalNicId((USHORT)(*AdapterIndex)); // Use critical section to serialize usage of driver handle EnterCriticalSection (&ConfigInfoLock); status = NtDeviceIoControlFile( IpxDriverHandle, NULL, NULL, NULL, &IoStatusBlock, IOCTL_TDI_ACTION, NULL, 0, action, sizeof(NWLINK_ACTION) +sizeof (IPX_QUERY_WAN_INACTIVITY)); if (status==STATUS_PENDING){ status = NtWaitForSingleObject (IpxDriverHandle, FALSE, NULL); if (NT_SUCCESS (status)) status = IoStatusBlock.Status; } LeaveCriticalSection (&ConfigInfoLock); if (NT_SUCCESS (status)) { *AdapterIndex = query->NicId; *InactivityCounter = query->WanInactivityCounter; } #if DBG else { DbgPrint ("TDI Ioctl MIPX_QUERY_WAN_INACTIVITY failed with status %08x\n", status); } #endif return RtlNtStatusToDosError (status); } /*++ O p e n A d a p t e r C o n f i g P o r t Routine Description: Creates path to adapter configuration mechanism provided by the IPX stack and obtains "static" adapter information (number of adapters, internal net parameters) Arguments: None Return Value: NO_ERROR - port was open OK Windows error code - operation failed --*/ DWORD OpenAdapterConfigPort (void) { UNICODE_STRING FileString; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatus; NTSTATUS status; DWORD i; // Initialize the parameters needed to open the driver RtlInitUnicodeString (&FileString, ISN_IPX_NAME); InitializeObjectAttributes( &ObjectAttributes, &FileString, OBJ_CASE_INSENSITIVE, NULL, NULL); // Get a handle to the ipx driver status = NtOpenFile(&IpxDriverHandle, SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA, &ObjectAttributes, &IoStatus, FILE_SHARE_READ | FILE_SHARE_WRITE, 0); // If the driver handle wasn't opened, we're in an error state if (NT_SUCCESS (status)) { PISN_ACTION_GET_DETAILS details; PNWLINK_ACTION action; CHAR IoctlBuffer[sizeof (NWLINK_ACTION) +sizeof (ISN_ACTION_GET_DETAILS)]; // Prepare to send an ioctl to the stack to get the internal // net information along with the global adapter information action = (PNWLINK_ACTION)IoctlBuffer; action->Header.TransportId = ISN_ACTION_TRANSPORT_ID; action->OptionType = NWLINK_OPTION_CONTROL; action->BufferLength = sizeof(action->Option) + sizeof(ISN_ACTION_GET_DETAILS); action->Option = MIPX_CONFIG; details = (PISN_ACTION_GET_DETAILS)action->Data; // Nic id 0 will return internal net information and // total number of adapters details->NicId = 0; // Send the ioctl status = NtDeviceIoControlFile( IpxDriverHandle, NULL, NULL, NULL, &IoStatus, IOCTL_TDI_ACTION, NULL, 0, action, sizeof(NWLINK_ACTION) + sizeof(ISN_ACTION_GET_DETAILS)); // Wait for the ioctl to complete if (status==STATUS_PENDING) { status = NtWaitForSingleObject (IpxDriverHandle, FALSE, NULL); if (NT_SUCCESS (status)) status = IoStatus.Status; } // If the stack reports all the requested information without error, // update global variables with the information retrieved. if (NT_SUCCESS (status)) { NumAdapters = details->NicId; InternalNetworkNumber = details->NetworkNumber; // Seed the nic map by forcing the stack to at least report // one nic. (you'll always be guarenteed that one nic will // be available -- the IpxLoopbackAdadpter IpxSeedNicMap(); return NO_ERROR; } #if DBG // If this branch is reached, display the ioctl error code else DbgPrint ("TDI Ioctl MIPX_CONFIG failed with status %08x\n",status); #endif } #if DBG // If this branch is reached, display the couldn't open driver error else DbgPrint ("NtOpenFile failed with status %08x\n",status); #endif return RtlNtStatusToDosError (status); } /*++ I n i t i a l i z e M e s s a g e Q u e u e F o r C l i e n t Routine Description: Inserts messages that were already reported to existing clients in the beginning of the queue and points new client port (control block) to them. Thus new client can see adapters that were already reported to others before, while others are not disturbed Arguments: config - new client port (control block) Return Value: NO_ERROR - messages were inserted OK Windows error code - operation failed --*/ DWORD InitializeMessageQueueForClient (PCONFIG_PORT port) { NTSTATUS status = STATUS_SUCCESS; DWORD dwAdapterCount; PADAPTER_MSG msg; PIPX_NIC_INFO NicPtr; DWORD i, dwErr; USHORT usNicId; // Output some debug information TracePrintf(g_dwTraceId, "InitializeMessageQueueForClient: entered."); // Find out how many adapters we know about in our table. dwAdapterCount = NicMapGetMaxNicId(); // Loop through the adapters for (i = 0; i <= dwAdapterCount; i++) { NicPtr = NicMapGetNicInfo ((USHORT)i); if (!NicPtr) continue; #if DBG && defined(WATCHER_DIALOG) if (IsAdapterDisabled (NicPtr->NicId)) continue; #endif if (NicPtr->IpxwanConfigRequired == 1) continue; // Place the appropriate messages in the message queue of // the port of the client passed in. // switch (NicPtr->Status) { case NIC_CONFIGURED: case NIC_LINE_UP: // Insert the message in the client queue // usNicId = NicMapGetVirtualNicId(NicPtr->Details.NicId); if (usNicId == NIC_MAP_INVALID_NICID) { break; } NicPtr->Details.NicId = usNicId; msg = (PADAPTER_MSG) RtlAllocateHeap(RtlProcessHeap (), 0, sizeof(ADAPTER_MSG)); if (msg!=NULL) { RtlCopyMemory (&msg->info, NicPtr, sizeof (IPX_NIC_INFO)); InsertTailList (&port->msgqueue, &msg->link); status = STATUS_SUCCESS; } else { #if DBG DbgPrint ("Could not allocate memory for config" " message (gle:%08x).\n", GetLastError ()); #endif status = STATUS_NO_MEMORY; } break; case NIC_DELETED: case NIC_CREATED: case NIC_LINE_DOWN: break; default: ASSERTMSG ("Unknown nic state reported ", FALSE); } } DbgDisplayMap(); // Advertise the internal adapter dwErr = IpxPostIntNetNumMessage(port, InternalNetworkNumber); if (dwErr != NO_ERROR) { TracePrintf( g_dwTraceId, "Unable to report internal network number: %x Err: %x", InternalNetworkNumber, dwErr); } // Go ahead and signal the client to do its processing // if everything has been successful to this point and // if the client's message queue isn't empty. if (NT_SUCCESS (status)) { if (!IsListEmpty (&port->msgqueue)) { BOOL res = SetEvent (port->event); ASSERTMSG ("Can't set client's event ", res); } } return RtlNtStatusToDosError (status); } /*++ C l o s e A d a p t e r C o n f i g P o r t Routine Description: Closes path to the IPX stack adapter notification mechanism Arguments: None Return Value: STATUS_SUCCESS - port was closed OK NT error status - operation failed --*/ NTSTATUS CloseAdapterConfigPort (PVOID pvConfigBuffer) { NTSTATUS status; TracePrintf(g_dwTraceId, "CloseAdapterConfigPort: Entered"); // Only close it if it is open if (IpxDriverHandle!=NULL) { HANDLE localHandle = IpxDriverHandle; IpxDriverHandle = NULL; status = NtClose (localHandle); ASSERTMSG ("NtClose failed ", NT_SUCCESS (status)); } // Get rid of the buffer if (pvConfigBuffer != NULL) RtlFreeHeap (RtlProcessHeap(), 0, pvConfigBuffer); while (AdapterChangeApcPending>0) Sleep (100); return NO_ERROR; } /*++ I n s e r t M e s s a g e Routine Description: Inserts message into client port queue Arguments: port - client port to isert message into NicInfo - adapter info to be inserted as the message Return Value: STATUS_SUCCESS - message was inserted ok NT error status - operation failed --*/ NTSTATUS InsertMessage (PCONFIG_PORT port, PIPX_NIC_INFO NicInfo) { PADAPTER_MSG msg; // Allocate a new message msg = (PADAPTER_MSG)RtlAllocateHeap (RtlProcessHeap (), 0, sizeof (ADAPTER_MSG)); if (msg!=NULL) { // Copy in the Nic information RtlCopyMemory (&msg->info, NicInfo, sizeof (IPX_NIC_INFO)); // Signal event if this is the first message we process // and client queue is empty if (IsListEmpty (&port->msgqueue)) { BOOL res = SetEvent (port->event); ASSERTMSG ("Can't set client event ", res); } // Insert the message into the port's message queue. InsertTailList (&port->msgqueue, &msg->link); return STATUS_SUCCESS; } else { #if DBG DbgPrint ("Could not allocate memory for config" " message (gle:%08x).\n",GetLastError ()); #endif return STATUS_NO_MEMORY; } } /*++ P r o c e s s A d a p t e r C o n f i g I n f o Routine Description: Process adapter change information returned by the IPX stack and converts it to messages Arguments: None Return Value: None --*/ NTSTATUS ProcessAdapterConfigInfo ( IN PVOID pvConfigBuffer) { INT i, nMessages, nClients=0; PNWLINK_ACTION action = (PNWLINK_ACTION)pvConfigBuffer; PIPX_NICS request = (PIPX_NICS)action->Data; PIPX_NIC_INFO NicPtr = (PIPX_NIC_INFO)request->Data; NTSTATUS status = STATUS_SUCCESS; // Update number of adapters NumAdapters = request->TotalNoOfNics; nMessages = request->NoOfNics; // Display the nics and their status as reported in this completion of the // MIPX_GETNEWNICINFO ioctl. DbgDisplayNics(NicPtr, nMessages); // Loop through all of the adapters for (i=0; (iDetails.NicId); NicMapRenumber (dwOpCode, NicPtr->Details.NicId); NicMapAdd(NicPtr); TracePrintf( g_dwTraceId, "Added %d -- Increment map", NicPtr->Details.NicId); NicPtr->Details.NicId = NicMapGetVirtualNicId(NicPtr->Details.NicId); } else if (dwOpCode == NIC_OPCODE_DECREMENT_NICIDS) { USHORT usNicId = NicMapGetVirtualNicId(NicPtr->Details.NicId); FwRenumberNics (dwOpCode, NicPtr->Details.NicId); NicMapDel (NicPtr); NicMapRenumber (dwOpCode, NicPtr->Details.NicId); TracePrintf( g_dwTraceId, "Deleted %d -- Decrement map", NicPtr->Details.NicId); NicPtr->Details.NicId = usNicId; } else { if (NicPtr->Status != NIC_DELETED) { TracePrintf( g_dwTraceId, "Configured: %d -- Map reconfigure", NicPtr->Details.NicId); NicMapReconfigure(NicPtr); } else { TracePrintf( g_dwTraceId, "Deleted: %d -- No map renumber", NicPtr->Details.NicId); NicMapDel(NicPtr); } NicPtr->Details.NicId = NicMapGetVirtualNicId(NicPtr->Details.NicId); } // If the information about the current NIC is stating // that a NIC has been created with a network address of // zero, and it's not a wan link (unumbered wan links can // have net number = 0), then nothing needs to be done // about this adapter since we wont be able to send information // out over it anyway. if ((NicPtr->Status==NIC_CREATED) && (NicPtr->Details.NetworkNumber==0) && (NicPtr->NdisMediumType!=NdisMediumWan)) { continue; } #if DBG && defined(WATCHER_DIALOG) // Make sure that the adapter is enabled if (IsAdapterDisabled (NicPtr->NicId)) { continue; } #endif // Update the ipxwan configuration if neccesary // if (NicPtr->IpxwanConfigRequired==1) { if (IpxWanPort!=NULL) { status = InsertMessage (IpxWanPort, NicPtr); } } else { // If this is a notification that the nic was deleted, // tell the computers calling in that the nic was deleted. if ((IpxWanPort!=NULL) && (NicPtr->Status==NIC_DELETED)) { status = InsertMessage (IpxWanPort, NicPtr); } // Signal each client (as in rtrmgr, sap, rip) to // check the status of the current Nic. for (cur = PortListHead.Flink; (cur != &PortListHead) && (status == STATUS_SUCCESS); cur = cur->Flink) { status = InsertMessage ( CONTAINING_RECORD (cur, CONFIG_PORT, link), NicPtr); } } } DbgDisplayMap(); return status; } /*++ A d a p t e r C h a n g e A P C Routine Description: APC invoked when adapter change notification IRP is completed by the Ipx Stack It is only used when running in router context (alertable thread provided by rtutils is used) Arguments: Context - Not used IoStatus - status of completed io operation Reserved - ??? Return Value: None --*/ VOID AdapterChangeAPC ( PVOID context, PIO_STATUS_BLOCK IoStatus, ULONG Reserved) { DWORD dwErr, dwNetNum = 0; BOOL bNewInternal = FALSE; PVOID pvConfigBuffer = ((PUCHAR)context) + sizeof(DWORD); ASSERT (IoStatus==&IoctlStatus); // Display the id of the buffer reporting this information // TracePrintf( g_dwTraceId, "AdapterChangeAPC called for buffer %d", *((DWORD*)context)); // [pmay] Check to see if the internal network number has // changed. if (PnpGetCurrentInternalNetNum(&dwNetNum) == NO_ERROR) { if ((bNewInternal = (InternalNetworkNumber != dwNetNum)) == TRUE) { // Notify all clients to adptif (rtrmgr, sap, rip) that the // internalnetwork number has changed. if (PnpHandleInternalNetNumChange(dwNetNum) == NO_ERROR) { InternalNetworkNumber = dwNetNum; } } } // Output some debug information { PNWLINK_ACTION action = (PNWLINK_ACTION)pvConfigBuffer; PIPX_NICS request = (PIPX_NICS)action->Data; TracePrintf( g_dwTraceId, "=========================="); TracePrintf( g_dwTraceId, "MIPX_GETNEWNICS Completed. (%d of %d adapters reported)", request->NoOfNics, request->TotalNoOfNics); TracePrintf( g_dwTraceId, "Internal Net Number: %x (%s)", dwNetNum, (bNewInternal) ? "new" : "same"); } // Ignore request when port is closed // if (IpxDriverHandle!=NULL) { EnterCriticalSection (&ConfigInfoLock); // If the Irp completed successfully, process the received // information. if (NT_SUCCESS (IoctlStatus.Status)) { IoctlStatus.Status = ProcessAdapterConfigInfo (pvConfigBuffer); } // Re-send the IRP immediately so that the next time // an adapter change occurs, we'll be notified. if (NT_SUCCESS (IoctlStatus.Status)) { PostAdapterConfigRequest (NULL); } else { PLIST_ENTRY cur; // Signal clients dialing in, so that they can get // error information. // if ((IpxWanPort!=NULL) && IsListEmpty (&IpxWanPort->msgqueue)) { BOOL res = SetEvent (IpxWanPort->event); ASSERTMSG ("Can't set client event ", res); } // Loop through all of the clients to this dll (i.e. rip, // sap, router manager) for (cur=PortListHead.Flink; cur!=&PortListHead; cur = cur->Flink) { PCONFIG_PORT port = CONTAINING_RECORD (cur, CONFIG_PORT, link); // If the mes queue for client is empty at this point, then // it means ProcessAdapterConfigInfo() didn't detect any work // items for the client in question. We set the message // here so that the client knows that something happened. if (IsListEmpty (&port->msgqueue)) { BOOL res = SetEvent (port->event); ASSERTMSG ("Can't set client event ", res); } } } LeaveCriticalSection (&ConfigInfoLock); #if DBG && defined(WATCHER_DIALOG) InformWatcher (); // Let watcher update its info as well #endif } else { TracePrintf(g_dwTraceId, "Warning - IpxDriverHandle is NULL, not processing"); } // [pmay] // We're done with the new nic info buffer now. // if (context) { RtlFreeHeap (RtlProcessHeap(), 0, context); } InterlockedDecrement (&AdapterChangeApcPending); } /*++ P o s t A d a p t e r C o n f i g R e q u e s t Routine Description: Posts IRP to the driver to get adapter change notifications Arguments: Context - event to be used to signal completion of the IRP, NULL if APC is to be used for this purpose Return Value: None --*/ VOID APIENTRY PostAdapterConfigRequest ( IN PVOID context) { HANDLE WaitHandle = (HANDLE)context; PNWLINK_ACTION action; PIPX_NICS request; PVOID pvConfigBuffer = NULL; DWORD dwBufSize = 0, dwActionSize = 0, dwNicBufSize = 0; TracePrintf(g_dwTraceId, "PostAdapterConfigRequest: Entered\n"); EnterCriticalSection (&ConfigInfoLock); // Allocate request buffer, making sure that we have space for at // least one adapter. // dwNicBufSize = FIELD_OFFSET (IPX_NICS, Data) + (sizeof (IPX_NIC_INFO) * (NumAdapters>0 ? NumAdapters : 1)); dwActionSize = FIELD_OFFSET (NWLINK_ACTION, Data) + dwNicBufSize; dwBufSize = sizeof(DWORD) + dwActionSize; pvConfigBuffer = RtlAllocateHeap (RtlProcessHeap (), 0, dwBufSize); if (pvConfigBuffer == NULL) { #if DBG DbgPrint ( "Could not alloc mem for global req buffer (gle:%08x).\n", GetLastError ()); #endif IoctlStatus.Status=STATUS_NO_MEMORY; LeaveCriticalSection (&ConfigInfoLock); return; } // Set up global buffer parameters // *((DWORD*)pvConfigBuffer) = g_dwBufferId++; // Set up the actions parameters // action = (PNWLINK_ACTION)((PUCHAR)pvConfigBuffer + sizeof(DWORD)); action->Header.TransportId = ISN_ACTION_TRANSPORT_ID; action->OptionType = NWLINK_OPTION_CONTROL; action->BufferLength = sizeof (action->Option) + dwNicBufSize; action->Option = MIPX_GETNEWNICINFO; request = (PIPX_NICS)action->Data; request->NoOfNics = 0; request->TotalNoOfNics = 0; request->fAllNicsDesired = FALSE; IoctlStatus.Status = NtDeviceIoControlFile( IpxDriverHandle, WaitHandle, (WaitHandle==NULL) ? AdapterChangeAPC : NULL, (WaitHandle==NULL) ? pvConfigBuffer : NULL, &IoctlStatus, IOCTL_TDI_ACTION, NULL, 0, action, dwActionSize); if (NT_SUCCESS (IoctlStatus.Status)) { if (WaitHandle==NULL) { InterlockedIncrement (&AdapterChangeApcPending); } } else { #if DBG DbgPrint ( "Ioctl MIPX_GETNEWNICINFO failed with status %08x\n", IoctlStatus.Status); #endif } LeaveCriticalSection (&ConfigInfoLock); }