diff --git a/core/device/tun_windows.go b/core/device/tun_windows.go new file mode 100644 index 0000000..b1e9888 --- /dev/null +++ b/core/device/tun_windows.go @@ -0,0 +1,95 @@ +package device + +import ( + "NetHive/pkgs/win" + "net/netip" + + "github.com/pkg/errors" + "golang.zx2c4.com/wintun" +) + +const ( + WintunTunnelType = "NetHive" +) + +type tun struct { + adapter *wintun.Adapter + session *wintun.Session + name string + mtu int +} + +func (t *tun) Read(bytes []byte) (int, error) { + //TODO implement me + panic("implement me") +} + +func (t *tun) Write(bytes []byte) (int, error) { + //TODO implement me + panic("implement me") +} + +func (t *tun) Close() error { + return t.adapter.Close() +} + +func (t *tun) MTU() (int, error) { + return t.mtu, nil +} + +func (t *tun) Name() (string, error) { + return t.name, nil +} + +func (t *tun) AddAddress(addr netip.Prefix) error { + luid := t.adapter.LUID() + itf := win.NetItf(luid) + + return errors.Wrap(itf.AddIPAddress(addr), "Error AddAddress:") +} + +func (t *tun) FlushAddress() error { + //TODO implement me + panic("implement me") +} + +func (t *tun) Up() error { + //TODO implement me + panic("implement me") +} + +func (t *tun) Down() error { + //TODO implement me + panic("implement me") +} + +func (t *tun) State() bool { + //TODO implement me + panic("implement me") +} + +func CreateTUN(name string, mtu int) (Device, error) { + adapter, err := wintun.CreateAdapter(name, WintunTunnelType, nil) + if err != nil { + return nil, errors.Wrapf(err, "error creating interface: ") + } + + session, err := adapter.StartSession(0x800000) + if err != nil { + adapter.Close() + return nil, errors.Wrapf(err, "error start session: ") + } + + if mtu <= 0 { + mtu = 1420 + } + + t := &tun{ + adapter: adapter, + session: &session, + name: name, + mtu: mtu, + } + + return t, nil +} diff --git a/go.mod b/go.mod index 854c5c1..4804b10 100644 --- a/go.mod +++ b/go.mod @@ -120,6 +120,7 @@ require ( golang.org/x/sync v0.3.0 // indirect golang.org/x/text v0.11.0 // indirect golang.org/x/tools v0.11.0 // indirect + golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect google.golang.org/protobuf v1.30.0 // indirect lukechampine.com/blake3 v1.2.1 // indirect ) diff --git a/go.sum b/go.sum index 6d09c5c..883572c 100644 --- a/go.sum +++ b/go.sum @@ -563,6 +563,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= +golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= diff --git a/pkgs/win/interface.go b/pkgs/win/interface.go new file mode 100644 index 0000000..6e7d33b --- /dev/null +++ b/pkgs/win/interface.go @@ -0,0 +1,26 @@ +package win + +import ( + "net/netip" +) + +// NetItf represents the network interface UUID of windows. +type NetItf uint64 + +func (i NetItf) AddIPAddress(address netip.Prefix) error { + row := &MibUnicastIPAddressRow{} + row.Init() + if err := row.Address.SetAddrPort(netip.AddrPortFrom(address.Addr(), 0)); err != nil { + return err + } + row.InterfaceLUID = uint64(i) + row.ValidLifetime = 0xffffffff + row.PreferredLifetime = 0xffffffff + row.OnLinkPrefixLength = uint8(address.Bits()) + row.DadState = NldsPreferred + return nil +} + +func (i *NetItf) FlushAddress() { + +} diff --git a/pkgs/win/iphlpapi.go b/pkgs/win/iphlpapi.go new file mode 100644 index 0000000..a99aff9 --- /dev/null +++ b/pkgs/win/iphlpapi.go @@ -0,0 +1,18 @@ +package win + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var ( + modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll") + + procInitializeUnicastIpAddressEntry = modiphlpapi.NewProc("InitializeUnicastIpAddressEntry") +) + +func initializeUnicastIPAddressEntry(row *MibUnicastIPAddressRow) { + syscall.SyscallN(procInitializeUnicastIpAddressEntry.Addr(), 1, uintptr(unsafe.Pointer(row)), 0, 0) +} diff --git a/pkgs/win/netioapi.go b/pkgs/win/netioapi.go new file mode 100644 index 0000000..5fd49cc --- /dev/null +++ b/pkgs/win/netioapi.go @@ -0,0 +1,22 @@ +package win + +// MibUnicastIPAddressRow structure stores information about a unicast IP address. +// https://learn.microsoft.com/en-us/windows/win32/api/netioapi/ns-netioapi-mib_unicastipaddress_row +type MibUnicastIPAddressRow struct { + Address SockAddrInet + InterfaceLUID uint64 + InterfaceIndex uint32 + PrefixOrigin uint32 + SuffixOrigin uint32 + ValidLifetime uint32 + PreferredLifetime uint32 + OnLinkPrefixLength uint8 + SkipAsSource bool + DadState NLDadState + ScopeID uint32 + CreationTimeStamp int64 +} + +func (m *MibUnicastIPAddressRow) Init() { + initializeUnicastIPAddressEntry(m) +} diff --git a/pkgs/win/nldef.go b/pkgs/win/nldef.go new file mode 100644 index 0000000..3ce58e7 --- /dev/null +++ b/pkgs/win/nldef.go @@ -0,0 +1,11 @@ +package win + +type NLDadState uint32 + +const ( + NldsInvalid NLDadState = iota + NldsTentative + NldsDuplicate + NldsDeprecated + NldsPreferred +) diff --git a/pkgs/win/sockaddr.go b/pkgs/win/sockaddr.go new file mode 100644 index 0000000..c3ec6c0 --- /dev/null +++ b/pkgs/win/sockaddr.go @@ -0,0 +1,43 @@ +package win + +import ( + "net/netip" + "strconv" + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +type SockAddrInet struct { + Family uint16 + data [26]byte +} + +func (s *SockAddrInet) SetAddrPort(ap netip.AddrPort) error { + if ap.Addr().Is4() { + addr4 := (*syscall.RawSockaddrInet4)(unsafe.Pointer(s)) + addr4.Family = syscall.AF_INET + addr4.Addr = ap.Addr().As4() + addr4.Port = be2se(ap.Port()) + for i := 0; i < 8; i++ { + addr4.Zero[i] = 0 + } + return nil + } else if ap.Addr().Is6() { + addr6 := (*windows.RawSockaddrInet6)(unsafe.Pointer(s)) + addr6.Family = syscall.AF_INET6 + addr6.Addr = ap.Addr().As16() + addr6.Port = be2se(ap.Port()) + addr6.Flowinfo = 0 + scopeId := uint32(0) + if z := ap.Addr().Zone(); z != "" { + if s, err := strconv.ParseUint(z, 10, 32); err == nil { + scopeId = uint32(s) + } + } + addr6.Scope_id = scopeId + return nil + } + return windows.ERROR_INVALID_PARAMETER +} diff --git a/pkgs/win/util.go b/pkgs/win/util.go new file mode 100644 index 0000000..cb39c8c --- /dev/null +++ b/pkgs/win/util.go @@ -0,0 +1,9 @@ +package win + +func be2se(i uint16) uint16 { + return (i>>8)&0xFF | (i&0xFF)<<8 +} + +func se2be(i uint16) uint16 { + return (i>>8)&0xFF | (i&0xFF)<<8 +}