diff --git a/core/sys_windows.go b/core/sys_windows.go index bbaad91f..81f3601f 100644 --- a/core/sys_windows.go +++ b/core/sys_windows.go @@ -29,10 +29,16 @@ func InitInterface(logger *slog.Logger, ifName string) error { } func ConfigureAlias(logger *slog.Logger, ifName string, addr netip.Addr) error { + if addr.Is6() { + return Exec(logger, "netsh", "interface", "ipv6", "add", "address", ifName, addr.String()) + } return Exec(logger, "netsh", "interface", "ip", "add", "address", ifName, addr.String()) } func RemoveAlias(logger *slog.Logger, ifName string, addr netip.Addr) error { + if addr.Is6() { + return Exec(logger, "netsh", "interface", "ipv6", "delete", "address", ifName, addr.String()) + } return Exec(logger, "netsh", "interface", "ip", "delete", "address", ifName, addr.String()) } diff --git a/polyamide/device/send.go b/polyamide/device/send.go index 1a50ee06..3f677e7c 100644 --- a/polyamide/device/send.go +++ b/polyamide/device/send.go @@ -249,6 +249,7 @@ func (device *Device) RoutineReadFromTUN() { } tce := device.GetTCElement() tce.Buffer = bufs[i] + tce.Packet = bufs[i][offset : offset+sizes[i]] tcBufs = append(tcBufs, tce) bufs[i] = device.GetMessageBuffer() diff --git a/polyamide/device/traffic_control.go b/polyamide/device/traffic_control.go index 04147042..6ecb48b9 100644 --- a/polyamide/device/traffic_control.go +++ b/polyamide/device/traffic_control.go @@ -109,8 +109,7 @@ func (device *Device) TCBatch(batch []*TCElement, tcs *TCState) { for i, elem := range batch { // process TC Filters act := TcPass - elem.ParsePacket() - if !elem.Validate() { + if !elem.ParsePacket() || !elem.Validate() { device.Log.Errorf("Found malformed packet, dropping packet") act = TcDrop } else { diff --git a/polyamide/device/traffic_manip.go b/polyamide/device/traffic_manip.go index 23061fc4..89d956eb 100644 --- a/polyamide/device/traffic_manip.go +++ b/polyamide/device/traffic_manip.go @@ -20,10 +20,25 @@ func (elem *TCElement) InitPacket(ver int, len uint16) { elem.SetLength(len) } -func (elem *TCElement) ParsePacket() { - elem.Packet = elem.Buffer[MessageTransportHeaderSize:] +func (elem *TCElement) ParsePacket() bool { + if elem == nil { + return false + } + if len(elem.Packet) == 0 { + if elem.Buffer == nil { + return false + } + elem.Packet = elem.Buffer[MessageTransportHeaderSize:] + } + if !elem.hasPacketHeader() { + return false + } l := elem.GetLength() - elem.Packet = elem.Buffer[MessageTransportHeaderSize : MessageTransportHeaderSize+l] + if int(l) > len(elem.Packet) { + return false + } + elem.Packet = elem.Packet[:l] + return true } func (elem *TCElement) Incoming() bool { @@ -137,6 +152,9 @@ func (elem *TCElement) Validate() bool { if elem == nil || len(elem.Packet) == 0 { return false } + if !elem.hasPacketHeader() { + return false + } ver := elem.GetIPVersion() if ver == 4 { if elem.GetLength() < ipv4.HeaderLen { @@ -154,6 +172,19 @@ func (elem *TCElement) Validate() bool { return true } +func (elem *TCElement) hasPacketHeader() bool { + if elem == nil || len(elem.Packet) < PolyHeaderSize { + return false + } + ver := elem.GetIPVersion() + if ver == 4 { + return len(elem.Packet) >= ipv4.HeaderLen + } else if ver == 6 { + return len(elem.Packet) >= ipv6.HeaderLen + } + return true +} + func (elem *TCElement) TTLBytes() []byte { if elem.GetIPVersion() == 4 { return elem.Packet[8:9] diff --git a/polyamide/device/traffic_manip_test.go b/polyamide/device/traffic_manip_test.go new file mode 100644 index 00000000..bf9e3ad6 --- /dev/null +++ b/polyamide/device/traffic_manip_test.go @@ -0,0 +1,41 @@ +package device + +import ( + "encoding/binary" + "testing" +) + +func TestParsePacketRejectsLengthBeyondReadBuffer(t *testing.T) { + var buf [MaxMessageSize]byte + packet := buf[MessageTransportHeaderSize : MessageTransportHeaderSize+2016] + packet[0] = 6 << 4 + binary.BigEndian.PutUint16(packet[IPv6offsetPayloadLength:IPv6offsetPayloadLength+2], 2010) + + elem := &TCElement{ + Buffer: &buf, + Packet: packet, + } + + if elem.ParsePacket() { + t.Fatal("expected oversized packet to be rejected") + } +} + +func TestParsePacketTrimsToAdvertisedLength(t *testing.T) { + var buf [MaxMessageSize]byte + packet := buf[MessageTransportHeaderSize : MessageTransportHeaderSize+128] + packet[0] = 6 << 4 + binary.BigEndian.PutUint16(packet[IPv6offsetPayloadLength:IPv6offsetPayloadLength+2], 8) + + elem := &TCElement{ + Buffer: &buf, + Packet: packet, + } + + if !elem.ParsePacket() { + t.Fatal("expected valid packet to parse") + } + if len(elem.Packet) != 48 { + t.Fatalf("expected packet length 48, got %d", len(elem.Packet)) + } +} diff --git a/polyamide/tun/tun_windows.go b/polyamide/tun/tun_windows.go index de65fb44..c92e3229 100644 --- a/polyamide/tun/tun_windows.go +++ b/polyamide/tun/tun_windows.go @@ -12,7 +12,7 @@ import ( "sync" "sync/atomic" "time" - _ "unsafe" + "unsafe" "golang.org/x/sys/windows" "golang.zx2c4.com/wintun" @@ -49,6 +49,9 @@ type NativeTun struct { var ( WintunTunnelType = "WireGuard" WintunStaticRequestedGUID *windows.GUID + modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll") + procGetIpInterfaceEntry = modiphlpapi.NewProc("GetIpInterfaceEntry") + procSetIpInterfaceEntry = modiphlpapi.NewProc("SetIpInterfaceEntry") ) //go:linkname procyield runtime.procyield @@ -75,6 +78,11 @@ func CreateTUNWithRequestedGUID(ifname string, requestedGUID *windows.GUID, mtu if mtu > 0 { forcedMTU = mtu } + if err := setInterfaceMTU(wt.LUID(), windows.AF_INET6, forcedMTU); err != nil { + wt.Close() + return nil, fmt.Errorf("Error setting IPv6 interface MTU: %w", err) + } + _ = setInterfaceMTU(wt.LUID(), windows.AF_INET, forcedMTU) tun := &NativeTun{ wt: wt, @@ -225,6 +233,24 @@ func (tun *NativeTun) RunningVersion() (version uint32, err error) { return wintun.RunningVersion() } +func setInterfaceMTU(luid uint64, family uint16, mtu int) error { + var row windows.MibIpInterfaceRow + row.Family = family + row.InterfaceLuid = luid + + r1, _, _ := procGetIpInterfaceEntry.Call(uintptr(unsafe.Pointer(&row))) + if r1 != 0 { + return windows.Errno(r1) + } + + row.NlMtu = uint32(mtu) + r1, _, _ = procSetIpInterfaceEntry.Call(uintptr(unsafe.Pointer(&row))) + if r1 != 0 { + return windows.Errno(r1) + } + return nil +} + func (rate *rateJuggler) update(packetLen uint64) { now := nanotime() total := rate.nextByteCount.Add(packetLen)