Home › Forums › Discussions › Support › I have a problem rewriting NDIS in Go
- This topic has 5 replies, 2 voices, and was last updated 1 month, 3 weeks ago by devman.
-
AuthorPosts
-
October 24, 2024 at 10:32 am #13903
Hello,
I was trying to rewrite the NDIS API from C++ code to Go and I faced a problem which I tried to fix but because I don’t get any proper error or result I can’t tell what’s exactly wrong.
This is part of the code that is working and showing the result correctly, at least to my udnerstanding:
`
import (
“fmt”
“strings”
“unsafe”“golang.org/x/sys/windows”
)type NdisApi struct {
hFileHandle windows.Handle
}func NewNdisApi() (*NdisApi, error) {
devicePath, err := windows.UTF16PtrFromString(“\\\\.\\NDISRD”)
if err != nil {
return nil, err
}handle, err := windows.CreateFile(
devicePath, // Device path
windows.GENERIC_READ|windows.GENERIC_WRITE,
0,
nil,
windows.OPEN_EXISTING,
windows.FILE_ATTRIBUTE_NORMAL,
0)
if err != nil {
return nil, err
}
return &NdisApi{hFileHandle: handle}, nil
}func (api *NdisApi) DeviceIoControl(dwService uint32, BuffIn unsafe.Pointer, SizeIn uint32, BuffOut unsafe.Pointer, SizeOut uint32, SizeRet *uint32, povlp *windows.Overlapped) error {
var returnedBytes uint32
if SizeRet == nil {
SizeRet = &returnedBytes
}return windows.DeviceIoControl(
api.hFileHandle,
dwService,
(*byte)(BuffIn),
SizeIn,
(*byte)(BuffOut),
SizeOut,
SizeRet,
povlp)
}// GetVersion Retrieves the NDISAPI driver version.
func (api *NdisApi) GetVersion() (*uint64, error) {
var nDriverAPIVersion uint64err := api.DeviceIoControl(
IOCTL_NDISRD_GET_VERSION,
unsafe.Pointer(&nDriverAPIVersion),
uint32(unsafe.Sizeof(nDriverAPIVersion)),
unsafe.Pointer(&nDriverAPIVersion),
uint32(unsafe.Sizeof(nDriverAPIVersion)),
nil,
nil,
)if err != nil {
return nil, err
}return &nDriverAPIVersion, err
}func (api *NdisApi) GetTcpipBoundAdaptersInfo() (*TCPAdapterList, error) {
var tcpAdapterList TCPAdapterListerr := api.DeviceIoControl(
IOCTL_NDISRD_GET_TCPIP_INTERFACES,
unsafe.Pointer(&tcpAdapterList),
uint32(unsafe.Sizeof(tcpAdapterList)),
unsafe.Pointer(&tcpAdapterList),
uint32(unsafe.Sizeof(tcpAdapterList)),
nil,
nil,
)if err != nil {
return nil, err
}return &tcpAdapterList, nil
}
`
Also, I created a test to see if the results are correct:
`
package ndis_test
import (
“fmt”
“testing”“github.com/lagzap/ndis-go/ndis”
“github.com/stretchr/testify/assert”
)func TestNdisApi_GetVersion(t *testing.T) {
api, err := ndis.NewNdisApi()
if err != nil {
t.Fatalf(“Failed to create NdisApi: %v”, err)
}version, err := api.GetVersion()
if err != nil {
t.Fatalf(“Failed to get version: %v”, err)
}fmt.Printf(“NDIS Driver Version: %v\n”, version)
assert.NotZero(t, version, “Version should not be zero”)
assert.NoError(t, err, “There should be no error when getting version”)
}func TestNdisApi_GetTcpipBoundAdaptersInfo(t *testing.T) {
api, err := ndis.NewNdisApi()
if err != nil {
t.Fatalf(“Failed to create NdisApi: %v”, err)
}adaptersInfo, err := api.GetTcpipBoundAdaptersInfo()
if err != nil {
t.Fatalf(“Failed to get list: %v”, err)
}for i := range adaptersInfo.AdapterCount {
nameBytes := adaptersInfo.AdapterNameList[i]
fmt.Printf(“NDIS List: %v\n”, string(nameBytes[:]))
}assert.NotZero(t, adaptersInfo.AdapterCount, “List should not be zero”)
assert.NoError(t, err, “There should be no error when getting version”)
}`
And this is the result:
=== RUN TestNdisApi_GetVersion
NDIS Driver Version: 0xc00009c928
— PASS: TestNdisApi_GetVersion (0.00s)
=== RUN TestNdisApi_GetTcpipBoundAdaptersInfo
NDIS List: \DEVICE\{ED78279B-9E2E-4C66-AEE8-793EFB217C27}
NDIS List: \DEVICE\{FF9AC243-7969-4F09-BD77-AF6C2C6A65B2}
NDIS List: \DEVICE\{40FF7482-C93F-4892-9A57-FDB19D966B6E}
NDIS List: \DEVICE\{12C7B741-5C7B-4147-B9A7-A5A2E71D5E9F}
NDIS List: \DEVICE\{A88A6FB4-497C-4464-BBA7-F48BC135DD07}
NDIS List: \DEVICE\{9353CCF4-21B8-44BA-8E2B-CA3F156FD2B1}
NDIS List: \DEVICE\{B404DFD0-745B-4C91-B5C7-BA78647826B8}
NDIS List: \DEVICE\{423C1CD9-E2E9-4821-A3A4-36E3EBBA7FA2}
NDIS List: \DEVICE\{0E2EB66C-DF42-4786-A1BF-23621E04B1A1}
NDIS List: \DEVICE\{601E27D7-7B79-498D-B121-CF3942AA124A}
NDIS List: \DEVICE\{8CF058FF-AF7F-418F-8F08-60D745F68CD5}
NDIS List: \DEVICE\{0C10FCBB-4201-4B6D-848F-6675E672F9BC}
NDIS List: \DEVICE\{1D8A9E21-80A0-404B-A563-3940C023C60F}
NDIS List: \DEVICE\{8BE3670D-A47D-4F23-91C4-85547DE5EDAD}
— PASS: TestNdisApi_GetTcpipBoundAdaptersInfo (0.00s)
PASS
ok command-line-arguments 0.015s
`
So, if i’m not mistaken this is communicating with the driver correctly because it’s giving me the result I want from these two. So, I went up further and tried to implement simple_packet_filter.go and network_adapter.go to able to do some real stuff
And this is the rewritten code of InitializeNetworkInterfaces from simple_packet_filter.go:
`
func (f *SimplePacketFilter) initializeNetworkInterfaces() error {
adapters, err := f.api.GetTcpipBoundAdaptersInfo()
if err != nil {
return err
}for i := range adapters.AdapterCount {
name := string(adapters.AdapterNameList[i][:])
adapterHandle := adapters.AdapterHandle[i]
currentAddress := adapters.CurrentAddress[i]
medium := adapters.AdapterMediumList[i]
mtu := adapters.MTU[i]friendlyName := ConvertWindows2000AdapterName(name)
networkAdapter := NewNetworkAdapter(f.api, adapterHandle, currentAddress, name, friendlyName, medium, mtu)
f.networkInterfaces = append(f.networkInterfaces, networkAdapter)
}return nil
}
`
and part of network_adapter.go
`
func NewNetworkAdapter(api *NdisApi, adapterHandle windows.Handle, macAddr [6]byte, internalName, friendlyName string, medium uint32, mtu uint16) *NetworkAdapter {
adapter := &NetworkAdapter{
API: api,
HardwareAddr: macAddr,
InternalName: internalName,
FriendlyName: friendlyName,
Medium: medium,
MTU: mtu,
CurrentMode: AdapterMode{
AdapterHandle: adapterHandle,
Flags: 0,
},
}
event, err := windows.CreateEvent(nil, 1, 0, “”)
if err != nil {
fmt.Println(err.Error())
return nil
}
adapter.packetEvent = event
`
And of course SetPacketEvent():
`
func (na *NetworkAdapter) SetPacketEvent() error {
return na.API.SetPacketEvent(na.CurrentMode.AdapterHandle, na.packetEvent)
}
`
and finally SetPacketEvent on ndis.go
`
type AdapterEvent struct {
AdapterHandle windows.Handle
Event windows.Handle
}
func (api *NdisApi) SetPacketEvent(adapter windows.Handle, win32Event windows.Handle) error {
var ring0Event windows.Handleif IsWindowsNTPlatform() {
// Windows NT platform
ring0Event = win32Event
} else {
// Windows 9x/ME platform
…
}adapterEvent := AdapterEvent{
AdapterHandle: adapter,
Event: ring0Event,
}err := api.DeviceIoControl(
IOCTL_NDISRD_SET_EVENT,
unsafe.Pointer(&adapterEvent),
uint32(unsafe.Sizeof(adapterEvent)),
nil,
0,
nil, // Bytes Returned
nil,
)fmt.Printf(“DeviceIoControl failed: %v\n”, err)
return err
}
`
and I get this error only:
DeviceIoControl failed: The parameter is incorrect.I’m not sure if I’m creating windows event incorrectly or what’s wrong, I even tried this instead of windows.CreateEvent():
https://github.com/docker/dockercraft/blob/master/vendor/github.com/docker/docker/pkg/system/events_windows.go#L22I’ll be happy if you help me solve this so I can develop this
October 24, 2024 at 3:21 pm #13904I would recommend paying attention to the following points:
1. Ensure that all structures used for driver communication are properly packed.
2. If you are using a 64-bit driver, make sure to build your code for 64-bit as well.
3. The adapter handle must correspond to the selected network interface.
4. The event handle should be a native Windows handle.
5. I suggest building and using the ndisapi.dll C interface in Go instead of reimplementing all the DeviceIoControl calls directly in Go.
October 24, 2024 at 11:29 pm #13907I don’t know how I didn’t think of using C interface, thank you, I think I’m going to do that.
October 26, 2024 at 7:19 pm #13908I tried to use the C Interface and I think I have same problem and I think it’s related to Handle.
I think I’m using wrong datatype for Handle, let’s say I’m getting AdapterMode and on I should have a AdapterHandle that I got from TCPAdapterList and for now I used uintptr datatype to store handle, I’m not sure if it’s actualy uintptr or something else. (Because windows.Handle or syscall.Handle actual datatype on go is uintptr).I can get AdapterList and store it into a struct which is exactly like C/C++ version but when I want to for example SetEvent or any API that wants AdapterHandle I get error: “The parameter is incorrect.”
AdapterList (works without error):
type TCP_AdapterList struct {
AdapterCount uint32
AdapterNameList [ADAPTER_LIST_SIZE][ADAPTER_NAME_SIZE]byte
AdapterHandle [ADAPTER_LIST_SIZE]uintptr
AdapterMediumList [ADAPTER_LIST_SIZE]uint32
CurrentAddress [ADAPTER_LIST_SIZE][ETHER_ADDR_LENGTH]byte
MTU [ADAPTER_LIST_SIZE]uint16
}
func (a *NdisApi) GetTcpipBoundAdaptersInfo() (*TCP_AdapterList, error) {
var adapterList TCP_AdapterList
_, _, err := a.c.GetTcpipBoundAdaptersInfo.Call(a.handle, uintptr(unsafe.Pointer(&adapterList)))if !errors.Is(err, windows.ERROR_SUCCESS) {
return nil, err
}return &adapterList, nil
}GetAdapterMode (error: “The parameter is incorrect.”):
type AdapterMode struct {
AdapterHandle uintptr
Flags uint32
}
func (a *NdisApi) GetAdapterMode(currentMode *AdapterMode) (error) {
r1, r2, err := a.c.GetAdapterMode.Call(a.handle, uintptr(unsafe.Pointer(currentMode)))
fmt.Println(r1, r2, err, a.handle)if !errors.Is(err, windows.ERROR_SUCCESS) {
return err
}return nil
}I actually don’t know whether if I’m correct or wrong, I’ll appreciate it if you help me solve this.
How can I make sure I’m using windows native event handle or if I got the Handle correctly, because I think thats what my problem is about?Note that, I’m using 64-bit.
- This reply was modified 1 month, 3 weeks ago by devman.
October 26, 2024 at 7:52 pm #13910Could it be size of Handle difference?
I mean if uintptr is unsigned int and if I’m on 64bit, I should use uint64 instead of uintptr?October 26, 2024 at 8:39 pm #13911I think I managed to fix it but not with C interface, I used the initial code that I wrote purely in go, I can now ReadPackets but instead of using windows.Handle or uintptr I created an 8 bytes array (type HANDLE [8]byte) for network adapter handle and used it and it’s working
I’m not sure what did I do wrong maybe go did some type-casting or something…
-
AuthorPosts
- You must be logged in to reply to this topic.