[Windows x64] Userspace Network Bridge 5Gb/s+

Share your advanced PureBasic knowledge/code with the community.
AndyMK
Enthusiast
Enthusiast
Posts: 540
Joined: Wed Jul 12, 2006 4:38 pm
Location: UK

[Windows x64] Userspace Network Bridge 5Gb/s+

Post by AndyMK »

This code creates a network bridge between 2 ethernet adapters (WiFi can be done but needs extra code) using the Windows Packet Filter library provided by NT KERNEL https://www.ntkernel.com/
You must download and install the signed network filter driver from https://www.ntkernel.com/downloads/Wind ... %20x64.msi
You must also put the dll/lib files in the same folder as the PB source code. I have compiled them for you download from https://1drv.ms/u/s!AufD7SJtv9Nuj5Ukx6v ... w?e=pIXRMy

I have posted this in the hope that someone can modify it to use the Fast I/O functions included in the lib. Fast I/O can improve performance a lot.
Api docs can be found here https://www.ntkernel.com/docs/windows-p ... mentation/
You will need at least 2 Ethernet Adapters. You must also set the correct adapter index in the source for each adapter.

I have tested this with a dual 10Gbit Intel Nic and I can pass 5.5Gb/sec in one direction limited by the routers CPU. The PC CPU usage is 80% per thread but that's with HT enabled on the CPU. Multithreading is possible but packets have to be queued in the correct order before being sent.

The Library is free for non commercial use.

Have fun :)

**** UPDATE ****
ENABLE THREADSAFE!!

Code: Select all

EnableExplicit
SetPriorityClass_(GetCurrentProcess_(),#HIGH_PRIORITY_CLASS)

#NDIS_PACKET_TYPE_PROMISCUOUS     =	$00000020
#MSTCP_FLAG_RECV_TUNNEL   = $00000002;	// Receive packets instead MSTCP
#MSTCP_FLAG_FILTER_DIRECT	= $00000010;	// In promiscuous mode TCP/IP stack receives all
#MSTCP_FLAG_LOOPBACK_BLOCK	= $00000040;  // Silently drop loopback packets, this flag

#PACKET_FLAG_ON_SEND    = $00000001
#PACKET_FLAG_ON_RECEIVE	= $00000002

Structure _ADAPTER_MODE Align 1
  *hAdapterHandle
  dwFlags.l
EndStructure

Structure LIST_ENTRY Align 1
  *Flink
  *Blink
EndStructure

Structure _INTERMEDIATE_BUFFER Align 1
  m_qLink.LIST_ENTRY
  m_dwDeviceFlags.l
  m_Length.l
  m_Flags.l; // NDIS_PACKET flags
  m_8021q.l; // 802.1q info
  m_FilterID.l
  m_Reserved.l[4]
  m_IBuffer.a[1514]
EndStructure

Structure _ETH_M_REQUEST Align 1
  *hAdapterHandle
  dwPacketsNumber.l
  dwPacketsSuccess.l
  *EthPacket._INTERMEDIATE_BUFFER[256]
EndStructure

Structure eal Align 1
  mac_char.b[6]
EndStructure

Structure names Align 1
  name.b[256]
EndStructure

Structure TCP_AdapterList Align 1
  m_nAdapterCount.l; // Number of adapters
  m_szAdapterNameList.names[32]; // Array of adapter names
  *m_nAdapterHandle[32]        ; // Array of adapter handles, this are key handles for any adapter relative operation
  m_nAdapterMediumList.l[32]   ; // List of adapter mediums
  m_czCurrentAddress.eal[32]   ; // current (configured) ethernet address
  m_usMTU.u                    ; // current adapter MTU
EndStructure

Structure params
  *filter
  *hEvent
  *pkt._ETH_M_REQUEST
  *spkt._ETH_M_REQUEST
  *TCP_AdapterList.TCP_AdapterList
EndStructure

Import "ndisapi.lib"
  OpenFilterDriver(driver_name.s)
  IsDriverLoaded(hOpen.l)
  GetTcpipBoundAdaptersInfo(hOpen.l, *pAdapters)
  ConvertWindows2000AdapterName(szAdapterName.p-Ascii, *szUserFriendlyName, dwUserFriendlyNameLength.l)
  SetAdapterMode(hOpen.l, *pMode)
  ReadPackets(hOpen.l, *pPacket)
  SetPacketEvent(hOpen.l, *hAdapter, *hEvent)
  GetHwPacketFilter(hOpen.l, *hAdapter, *pFilter)
  SetHwPacketFilter(hOpen.l, *hAdapter, filter.l)
  SendPacketsToAdapter(hOpen.l, *pPacket)
  SendPacketToMstcp(hOpen.l, *pPacket)
  SetHwPacketFilterEvent(hOpen.l, *hAdapter, *hEvent)
  FlushAdapterPacketQueue(hOpen.l, *hAdapter)
EndImport

Procedure wan(*val.params)
  Protected packets, i, length
  Debug "ADAPTER1 Thread Started"
  
  Repeat
    WaitForSingleObject_(*val\hEvent, #INFINITE)
    
    While ReadPackets(*val\filter, *val\pkt)
      packets = *val\pkt\dwPacketsSuccess
      For i = 0 To packets - 1
        If *val\pkt\EthPacket[i]\m_dwDeviceFlags = #PACKET_FLAG_ON_RECEIVE
          length = *val\pkt\EthPacket[i]\m_Length
          CopyMemory(@*val\pkt\EthPacket[i]\m_IBuffer, @*val\spkt\EthPacket[i]\m_IBuffer, *val\pkt\EthPacket[i]\m_Length)
          *val\spkt\EthPacket[i]\m_Length = *val\pkt\EthPacket[i]\m_Length
        EndIf
      Next
      *val\spkt\dwPacketsNumber = i
      SendPacketsToAdapter(*val\filter, *val\spkt)
      *val\pkt\dwPacketsSuccess = 0
      i = 0
    Wend
    
    ResetEvent_(*val\hEvent)
  ForEver
  
EndProcedure

Procedure lan(*val.params)
  Protected packets, i, length
  Debug "ADAPTER2 Thread Started"
  
  Repeat
    WaitForSingleObject_(*val\hEvent, #INFINITE)
    
    While ReadPackets(*val\filter, *val\pkt)
      packets = *val\pkt\dwPacketsSuccess
      For i = 0 To packets - 1
        If *val\pkt\EthPacket[i]\m_dwDeviceFlags = #PACKET_FLAG_ON_RECEIVE
          length = *val\pkt\EthPacket[i]\m_Length
          CopyMemory(@*val\pkt\EthPacket[i]\m_IBuffer, @*val\spkt\EthPacket[i]\m_IBuffer, *val\pkt\EthPacket[i]\m_Length)
          *val\spkt\EthPacket[i]\m_Length = *val\pkt\EthPacket[i]\m_Length
        EndIf
      Next
      *val\spkt\dwPacketsNumber = i
      SendPacketsToAdapter(*val\filter, *val\spkt)
      *val\pkt\dwPacketsSuccess = 0
      i = 0
    Wend
    
    ResetEvent_(*val\hEvent)
  ForEver
  
EndProcedure

Define *TCP_AdapterList.TCP_AdapterList = AllocateStructure(TCP_AdapterList)
Define *wan_mode._ADAPTER_MODE = AllocateStructure(_ADAPTER_MODE)
Define *lan_mode._ADAPTER_MODE = AllocateStructure(_ADAPTER_MODE)
Define *pkt._ETH_M_REQUEST = AllocateStructure(_ETH_M_REQUEST)
Define *spkt._ETH_M_REQUEST = AllocateStructure(_ETH_M_REQUEST)
Define *lpkt._ETH_M_REQUEST = AllocateStructure(_ETH_M_REQUEST)
Define *lspkt._ETH_M_REQUEST = AllocateStructure(_ETH_M_REQUEST)
Define *wan_param.params = AllocateStructure(params)
Define *lan_param.params = AllocateStructure(params)
Define *buff = AllocateMemory(256)
Define i, filter, recv_adap, send_adap, wan_recv_internal.s, lan_recv_internal.s, wan_hEvent, lan_hEvent, k, recv_mac.s, lan_recv_mac.s

For i = 0 To 255
  *pkt\EthPacket[i] = AllocateStructure(_INTERMEDIATE_BUFFER)
  *spkt\EthPacket[i] = AllocateStructure(_INTERMEDIATE_BUFFER)
  *lpkt\EthPacket[i] = AllocateStructure(_INTERMEDIATE_BUFFER)
  *lspkt\EthPacket[i] = AllocateStructure(_INTERMEDIATE_BUFFER)
Next 

filter = OpenFilterDriver("NDISRD")

If IsDriverLoaded(filter)
  recv_adap = 4 ;<-- Adapter 1
  send_adap = 8 ;<-- Adapter 2
  
  If GetTcpipBoundAdaptersInfo(filter, *TCP_AdapterList)
    Debug "Adapter Count: " + *TCP_AdapterList\m_nAdapterCount
    Debug #CR$
    wan_recv_internal = PeekS(*TCP_AdapterList\m_szAdapterNameList[recv_adap], -1, #PB_Ascii)
    Debug "Adapter Internal Name: " + wan_recv_internal
    ConvertWindows2000AdapterName(wan_recv_internal, *buff, 256)
    Debug "Adapter Friendly Name: " + PeekS(*buff, -1, #PB_Ascii)
    Debug "Adapter Handle: " + *TCP_AdapterList\m_nAdapterHandle[recv_adap]
    Debug "Adapter Medium: " + *TCP_AdapterList\m_nAdapterMediumList[recv_adap]
    For k = 0 To 5
      recv_mac.s + Hex(*TCP_AdapterList\m_czCurrentAddress[recv_adap]\mac_char[k], #PB_Byte) + ":"
    Next
    recv_mac = Left(recv_mac, Len(recv_mac) - 1)
    Debug "Adapter MAC Address: " + recv_mac
    Debug "Adapter MTU: " + *TCP_AdapterList\m_usMTU
    Debug #CR$
    
    lan_recv_internal.s = PeekS(*TCP_AdapterList\m_szAdapterNameList[send_adap], -1, #PB_Ascii)
    Debug "Adapter Internal Name: " + lan_recv_internal
    ConvertWindows2000AdapterName(lan_recv_internal, *buff, 256)
    Debug "Adapter Friendly Name: " + PeekS(*buff, -1, #PB_Ascii)
    Debug "Adapter Handle: " + *TCP_AdapterList\m_nAdapterHandle[send_adap]
    Debug "Adapter Medium: " + *TCP_AdapterList\m_nAdapterMediumList[send_adap]
    For k = 0 To 5
      lan_recv_mac.s + Hex(*TCP_AdapterList\m_czCurrentAddress[send_adap]\mac_char[k], #PB_Byte) + ":"
    Next
    lan_recv_mac = Left(lan_recv_mac, Len(lan_recv_mac) - 1)
    Debug "Adapter MAC Address: " + lan_recv_mac
    Debug "Adapter MTU: " + *TCP_AdapterList\m_usMTU
    Debug #CR$
    
    If SetHwPacketFilter(filter, *TCP_AdapterList\m_nAdapterHandle[recv_adap], #NDIS_PACKET_TYPE_PROMISCUOUS)
      Debug "ADAPTER1 Set Hardware Pascket Filter SUCCESS"
      *wan_mode\hAdapterHandle = *TCP_AdapterList\m_nAdapterHandle[recv_adap]
      *wan_mode\dwFlags = #MSTCP_FLAG_RECV_TUNNEL | #MSTCP_FLAG_FILTER_DIRECT | #MSTCP_FLAG_LOOPBACK_BLOCK
      If SetAdapterMode(filter, *wan_mode)
        Debug "ADAPTER1 Set Adapter Mode SUCCESS"
        *pkt\hAdapterHandle = *TCP_AdapterList\m_nAdapterHandle[recv_adap]
        *pkt\dwPacketsNumber = 512
        *spkt\hAdapterHandle = *TCP_AdapterList\m_nAdapterHandle[send_adap]
        wan_hEvent = CreateEvent_(#Null, #True, #False, #Null)
        If wan_hEvent
          If SetPacketEvent(filter, *TCP_AdapterList\m_nAdapterHandle[recv_adap], wan_hEvent)
            Debug "ADAPTER1 Created Adapter Event SUCCESS"
            *wan_param\filter = filter
            *wan_param\hEvent = wan_hEvent
            *wan_param\TCP_AdapterList = *TCP_AdapterList
            *wan_param\pkt = *pkt
            *wan_param\spkt = *spkt
            CreateThread(@wan(), *wan_param)
          EndIf
        EndIf
      EndIf
    EndIf
    
    If SetHwPacketFilter(filter, *TCP_AdapterList\m_nAdapterHandle[send_adap], #NDIS_PACKET_TYPE_PROMISCUOUS)
      Debug "ADAPTER2 Set Hardware Pascket Filter SUCCESS"
      *lan_mode\hAdapterHandle = *TCP_AdapterList\m_nAdapterHandle[send_adap]
      *lan_mode\dwFlags = #MSTCP_FLAG_RECV_TUNNEL | #MSTCP_FLAG_FILTER_DIRECT | #MSTCP_FLAG_LOOPBACK_BLOCK
      If SetAdapterMode(filter, *lan_mode)
        Debug "ADAPTER2 Set Adapter Mode SUCCESS"
        *lpkt\hAdapterHandle = *TCP_AdapterList\m_nAdapterHandle[send_adap]
        *lpkt\dwPacketsNumber = 512
        *lspkt\hAdapterHandle = *TCP_AdapterList\m_nAdapterHandle[recv_adap]
        lan_hEvent = CreateEvent_(#Null, #True, #False, #Null)
        If lan_hEvent
          If SetPacketEvent(filter, *TCP_AdapterList\m_nAdapterHandle[send_adap], lan_hEvent)
            Debug "ADAPTER2 Created Adapter Event SUCCESS"
            *lan_param\filter = filter
            *lan_param\hEvent = lan_hEvent
            *lan_param\TCP_AdapterList = *TCP_AdapterList
            *lan_param\pkt = *lpkt
            *lan_param\spkt = *lspkt
            CreateThread(@lan(), *lan_param)
          EndIf
        EndIf
      EndIf
    EndIf
    
    Repeat
      Delay(1000)
    ForEver
    
  EndIf
EndIf