Add windows bridge
This commit is contained in:
@@ -21,27 +21,39 @@ const (
|
||||
filterMaxInsts = 256
|
||||
|
||||
fieldZero = 0
|
||||
fieldInbound = 1
|
||||
fieldOutbound = 2
|
||||
fieldIP = 5
|
||||
fieldIPv6 = 6
|
||||
fieldICMP = 7
|
||||
fieldTCP = 8
|
||||
fieldUDP = 9
|
||||
fieldICMPv6 = 10
|
||||
fieldIPSrcAddr = 21
|
||||
fieldIPDstAddr = 22
|
||||
fieldIPv6SrcAddr = 28
|
||||
fieldIPv6DstAddr = 29
|
||||
fieldICMPType = 30
|
||||
fieldICMPv6Type = 34
|
||||
fieldTCPSrcPort = 38
|
||||
fieldTCPDstPort = 39
|
||||
fieldUDPSrcPort = 53
|
||||
fieldUDPDstPort = 54
|
||||
|
||||
testEQ = 0
|
||||
testEQ = 0
|
||||
testLEQ = 3
|
||||
testGEQ = 5
|
||||
|
||||
resultAccept uint16 = 0x7FFE
|
||||
resultReject uint16 = 0x7FFF
|
||||
)
|
||||
|
||||
// Filter flags passed to IOCTL_WINDIVERT_STARTUP alongside the compiled
|
||||
// filter. These tell the driver what *kinds* of packets the filter might
|
||||
// match, used as a kernel-side fast-reject.
|
||||
// filter. The driver installs WFP callouts only for the directions and
|
||||
// address families named here (windivert_install_callouts), so a filter
|
||||
// missing its direction flag never sees a packet.
|
||||
const (
|
||||
filterFlagInbound uint64 = 0x0010
|
||||
filterFlagOutbound uint64 = 0x0020
|
||||
filterFlagIP uint64 = 0x0040
|
||||
filterFlagIPv6 uint64 = 0x0080
|
||||
@@ -104,6 +116,80 @@ func OutboundTCP(src, dst netip.AddrPort) (*Filter, error) {
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func inboundTo(destination netip.Addr) (*Filter, error) {
|
||||
if !destination.IsValid() {
|
||||
return nil, E.New("windivert: filter: invalid address")
|
||||
}
|
||||
f := &Filter{
|
||||
flags: filterFlagInbound,
|
||||
}
|
||||
f.add(fieldInbound, testEQ, argUint32(1))
|
||||
if destination.Is4() {
|
||||
f.flags |= filterFlagIP
|
||||
f.add(fieldIP, testEQ, argUint32(1))
|
||||
f.add(fieldIPDstAddr, testEQ, argIPv4(destination))
|
||||
} else {
|
||||
f.flags |= filterFlagIPv6
|
||||
f.add(fieldIPv6, testEQ, argUint32(1))
|
||||
f.add(fieldIPv6DstAddr, testEQ, argIPv6(destination))
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func InboundTCPPortRange(destination netip.Addr, portLow, portHigh uint16) (*Filter, error) {
|
||||
f, err := inboundTo(destination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.add(fieldTCP, testEQ, argUint32(1))
|
||||
f.add(fieldTCPDstPort, testGEQ, argUint32(uint32(portLow)))
|
||||
f.add(fieldTCPDstPort, testLEQ, argUint32(uint32(portHigh)))
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func InboundUDPPortRange(destination netip.Addr, portLow, portHigh uint16) (*Filter, error) {
|
||||
f, err := inboundTo(destination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.add(fieldUDP, testEQ, argUint32(1))
|
||||
f.add(fieldUDPDstPort, testGEQ, argUint32(uint32(portLow)))
|
||||
f.add(fieldUDPDstPort, testLEQ, argUint32(uint32(portHigh)))
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func InboundICMPEchoReply(destination netip.Addr) (*Filter, error) {
|
||||
f, err := inboundTo(destination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if destination.Is4() {
|
||||
f.add(fieldICMP, testEQ, argUint32(1))
|
||||
f.add(fieldICMPType, testEQ, argUint32(0))
|
||||
} else {
|
||||
f.add(fieldICMPv6, testEQ, argUint32(1))
|
||||
f.add(fieldICMPv6Type, testEQ, argUint32(129))
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func InboundICMPError(destination netip.Addr) (*Filter, error) {
|
||||
f, err := inboundTo(destination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if destination.Is4() {
|
||||
f.add(fieldICMP, testEQ, argUint32(1))
|
||||
f.add(fieldICMPType, testGEQ, argUint32(3))
|
||||
f.add(fieldICMPType, testLEQ, argUint32(12))
|
||||
} else {
|
||||
f.add(fieldICMPv6, testEQ, argUint32(1))
|
||||
f.add(fieldICMPv6Type, testGEQ, argUint32(1))
|
||||
f.add(fieldICMPv6Type, testLEQ, argUint32(4))
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (f *Filter) add(field uint16, test uint8, arg [4]uint32) {
|
||||
f.insts = append(f.insts, filterInst{field: field, test: test, arg: arg})
|
||||
}
|
||||
|
||||
@@ -26,11 +26,14 @@ import (
|
||||
// because Go's escape analysis does not see the pointer through the
|
||||
// unsafe.Pointer → uintptr → bytes conversion.
|
||||
type Handle struct {
|
||||
device windows.Handle
|
||||
event windows.Handle
|
||||
closing sync.Once
|
||||
closeErr error
|
||||
addr Address
|
||||
device windows.Handle
|
||||
event windows.Handle
|
||||
closing sync.Once
|
||||
closeErr error
|
||||
addr Address
|
||||
recvAddrs []Address
|
||||
recvAddrsLen uint32
|
||||
sendAddrs []Address
|
||||
}
|
||||
|
||||
// Filter may be nil for "reject all", suitable for send-only handles.
|
||||
@@ -169,10 +172,60 @@ func (h *Handle) Recv(buf []byte) (int, Address, error) {
|
||||
return int(n), h.addr, nil
|
||||
}
|
||||
|
||||
// BatchMax is WINDIVERT_BATCH_MAX: the driver caps both directions at 255
|
||||
// packets per ioctl.
|
||||
const BatchMax = 255
|
||||
|
||||
const addressSize = uint32(unsafe.Sizeof(Address{}))
|
||||
|
||||
// RecvBatch receives up to BatchMax packets in one ioctl. The driver packs
|
||||
// packets back-to-back into buf with no padding and copies exactly each
|
||||
// packet's IP total length, so boundaries are recovered by walking the IP
|
||||
// length fields. It returns as soon as at least one packet is available;
|
||||
// it never waits to fill the batch. The returned Address slice is owned by
|
||||
// the Handle and is overwritten by the next RecvBatch.
|
||||
func (h *Handle) RecvBatch(buf []byte) (int, []Address, error) {
|
||||
if len(buf) < MTUMax {
|
||||
return 0, nil, E.New("windivert: recv batch: buffer smaller than MTUMax")
|
||||
}
|
||||
if h.recvAddrs == nil {
|
||||
h.recvAddrs = make([]Address, BatchMax)
|
||||
}
|
||||
h.recvAddrsLen = uint32(len(h.recvAddrs)) * addressSize
|
||||
in := buildIoctlRecvBatch(&h.recvAddrs[0], &h.recvAddrsLen)
|
||||
n, err := doIoctl(h.device, ioctlRecv, in[:], buf, h.event)
|
||||
runtime.KeepAlive(h)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return int(n), h.recvAddrs[:h.recvAddrsLen/addressSize], nil
|
||||
}
|
||||
|
||||
// SendBatch injects the packets packed back-to-back in buf, one Address per
|
||||
// packet. The driver recovers packet boundaries from the IP total-length
|
||||
// fields and rejects the whole batch if they do not add up to len(buf).
|
||||
func (h *Handle) SendBatch(buf []byte, addrs []Address) (int, error) {
|
||||
if len(addrs) == 0 || len(addrs) > BatchMax {
|
||||
return 0, E.New("windivert: send batch: invalid packet count ", len(addrs))
|
||||
}
|
||||
if len(buf) == 0 {
|
||||
return 0, E.New("windivert: send batch: empty buffer")
|
||||
}
|
||||
if h.sendAddrs == nil {
|
||||
h.sendAddrs = make([]Address, BatchMax)
|
||||
}
|
||||
copy(h.sendAddrs, addrs)
|
||||
in := buildIoctlSend(&h.sendAddrs[0], uint32(len(addrs))*addressSize)
|
||||
n, err := doIoctl(h.device, ioctlSend, in[:], buf, h.event)
|
||||
runtime.KeepAlive(h)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(n), nil
|
||||
}
|
||||
|
||||
// The address's Outbound flag controls whether the packet is sent toward
|
||||
// the wire (outbound=true) or delivered up the stack (outbound=false).
|
||||
// IfIdx and SubIfIdx can stay zero — the driver uses the routing table
|
||||
// when IfIdx=0.
|
||||
func (h *Handle) Send(packet []byte, addr *Address) (int, error) {
|
||||
if len(packet) == 0 {
|
||||
return 0, E.New("windivert: send: empty packet")
|
||||
@@ -181,7 +234,7 @@ func (h *Handle) Send(packet []byte, addr *Address) (int, error) {
|
||||
return 0, E.New("windivert: send: nil address")
|
||||
}
|
||||
h.addr = *addr
|
||||
in := buildIoctlSend(&h.addr)
|
||||
in := buildIoctlSend(&h.addr, addressSize)
|
||||
n, err := doIoctl(h.device, ioctlSend, in[:], packet, h.event)
|
||||
runtime.KeepAlive(h)
|
||||
if err != nil {
|
||||
@@ -316,9 +369,20 @@ func buildIoctlRecv(addr *Address) [ioctlSize]byte {
|
||||
return buf
|
||||
}
|
||||
|
||||
func buildIoctlSend(addr *Address) [ioctlSize]byte {
|
||||
// buildIoctlRecvBatch additionally passes addr_len_ptr, a pointer to the
|
||||
// Address array capacity in bytes; the driver overwrites it with the bytes
|
||||
// actually written (packet count × 80). Caller must keep both pointees
|
||||
// alive via runtime.KeepAlive.
|
||||
func buildIoctlRecvBatch(addrs *Address, addrsLen *uint32) [ioctlSize]byte {
|
||||
var buf [ioctlSize]byte
|
||||
binary.LittleEndian.PutUint64(buf[0:8], uint64(uintptr(unsafe.Pointer(addr))))
|
||||
binary.LittleEndian.PutUint64(buf[8:16], uint64(unsafe.Sizeof(Address{})))
|
||||
binary.LittleEndian.PutUint64(buf[0:8], uint64(uintptr(unsafe.Pointer(addrs))))
|
||||
binary.LittleEndian.PutUint64(buf[8:16], uint64(uintptr(unsafe.Pointer(addrsLen))))
|
||||
return buf
|
||||
}
|
||||
|
||||
func buildIoctlSend(addrs *Address, addrsLen uint32) [ioctlSize]byte {
|
||||
var buf [ioctlSize]byte
|
||||
binary.LittleEndian.PutUint64(buf[0:8], uint64(uintptr(unsafe.Pointer(addrs))))
|
||||
binary.LittleEndian.PutUint64(buf[8:16], uint64(addrsLen))
|
||||
return buf
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ func TestBuildIoctlRecvEmbedsAddressPointer(t *testing.T) {
|
||||
func TestBuildIoctlSendEmbedsAddressPointerAndSize(t *testing.T) {
|
||||
t.Parallel()
|
||||
addr := &Address{}
|
||||
buf := buildIoctlSend(addr)
|
||||
buf := buildIoctlSend(addr, addressSize)
|
||||
require.Equal(t, uint64(uintptr(unsafe.Pointer(addr))),
|
||||
binary.LittleEndian.Uint64(buf[0:8]))
|
||||
require.Equal(t, uint64(unsafe.Sizeof(Address{})),
|
||||
|
||||
@@ -55,9 +55,11 @@ var _ [80]byte = [unsafe.Sizeof(Address{})]byte{}
|
||||
|
||||
// Bit positions inside the Address's packed flags word.
|
||||
const (
|
||||
addrBitOutbound = 17
|
||||
addrBitIPv6 = 20
|
||||
addrBitIPChecksum = 21
|
||||
addrBitTCPChecksum = 22
|
||||
addrBitUDPChecksum = 23
|
||||
)
|
||||
|
||||
func getFlagBit(bits uint32, pos uint) bool { return bits&(1<<pos) != 0 }
|
||||
@@ -69,6 +71,18 @@ func setFlagBit(bits uint32, pos uint, v bool) uint32 {
|
||||
}
|
||||
|
||||
func (a *Address) IPv6() bool { return getFlagBit(a.bits, addrBitIPv6) }
|
||||
|
||||
// SetIPv6 declares the address family of a packet built for injection. The
|
||||
// driver reads it to select the IPv6 network layer; a received address
|
||||
// already carries it, but a from-scratch injection address must set it.
|
||||
func (a *Address) SetIPv6(v bool) {
|
||||
a.bits = setFlagBit(a.bits, addrBitIPv6, v)
|
||||
}
|
||||
|
||||
func (a *Address) SetOutbound(v bool) {
|
||||
a.bits = setFlagBit(a.bits, addrBitOutbound, v)
|
||||
}
|
||||
|
||||
func (a *Address) SetIPChecksum(v bool) {
|
||||
a.bits = setFlagBit(a.bits, addrBitIPChecksum, v)
|
||||
}
|
||||
@@ -76,3 +90,7 @@ func (a *Address) SetIPChecksum(v bool) {
|
||||
func (a *Address) SetTCPChecksum(v bool) {
|
||||
a.bits = setFlagBit(a.bits, addrBitTCPChecksum, v)
|
||||
}
|
||||
|
||||
func (a *Address) SetUDPChecksum(v bool) {
|
||||
a.bits = setFlagBit(a.bits, addrBitUDPChecksum, v)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user