Fix windows bridge forwarding to connected subnets

This commit is contained in:
世界
2026-08-30 17:41:42 +08:00
parent edb7e1da15
commit 7004fc6fd4
2 changed files with 184 additions and 44 deletions
+53 -23
View File
@@ -73,8 +73,9 @@ type filterInst struct {
//
// Zero value = "reject all" (match nothing), suitable for send-only handles.
type Filter struct {
insts []filterInst
flags uint64 // filter flags for STARTUP ioctl
insts []filterInst
anyInsts []filterInst // trailing OR block: any match accepts
flags uint64 // filter flags for STARTUP ioctl
}
// reject returns a filter that matches no packet. The empty insts slice
@@ -116,28 +117,41 @@ 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")
func inboundTo(destinations []netip.Addr) (*Filter, error) {
if len(destinations) == 0 {
return nil, E.New("windivert: filter: no destination address")
}
isV6 := destinations[0].Is6()
for _, destination := range destinations {
if !destination.IsValid() {
return nil, E.New("windivert: filter: invalid address")
}
if destination.Is6() != isV6 {
return nil, E.New("windivert: filter: mixed IPv4/IPv6")
}
}
f := &Filter{
flags: filterFlagInbound,
}
f.add(fieldInbound, testEQ, argUint32(1))
if destination.Is4() {
if !isV6 {
f.flags |= filterFlagIP
f.add(fieldIP, testEQ, argUint32(1))
f.add(fieldIPDstAddr, testEQ, argIPv4(destination))
for _, destination := range destinations {
f.addAny(fieldIPDstAddr, testEQ, argIPv4(destination))
}
} else {
f.flags |= filterFlagIPv6
f.add(fieldIPv6, testEQ, argUint32(1))
f.add(fieldIPv6DstAddr, testEQ, argIPv6(destination))
for _, destination := range destinations {
f.addAny(fieldIPv6DstAddr, testEQ, argIPv6(destination))
}
}
return f, nil
}
func InboundTCPPortRange(destination netip.Addr, portLow, portHigh uint16) (*Filter, error) {
f, err := inboundTo(destination)
func InboundTCPPortRange(destinations []netip.Addr, portLow, portHigh uint16) (*Filter, error) {
f, err := inboundTo(destinations)
if err != nil {
return nil, err
}
@@ -147,8 +161,8 @@ func InboundTCPPortRange(destination netip.Addr, portLow, portHigh uint16) (*Fil
return f, nil
}
func InboundUDPPortRange(destination netip.Addr, portLow, portHigh uint16) (*Filter, error) {
f, err := inboundTo(destination)
func InboundUDPPortRange(destinations []netip.Addr, portLow, portHigh uint16) (*Filter, error) {
f, err := inboundTo(destinations)
if err != nil {
return nil, err
}
@@ -158,12 +172,12 @@ func InboundUDPPortRange(destination netip.Addr, portLow, portHigh uint16) (*Fil
return f, nil
}
func InboundICMPEchoReply(destination netip.Addr) (*Filter, error) {
f, err := inboundTo(destination)
func InboundICMPEchoReply(destinations []netip.Addr) (*Filter, error) {
f, err := inboundTo(destinations)
if err != nil {
return nil, err
}
if destination.Is4() {
if destinations[0].Is4() {
f.add(fieldICMP, testEQ, argUint32(1))
f.add(fieldICMPType, testEQ, argUint32(0))
} else {
@@ -173,12 +187,12 @@ func InboundICMPEchoReply(destination netip.Addr) (*Filter, error) {
return f, nil
}
func InboundICMPError(destination netip.Addr) (*Filter, error) {
f, err := inboundTo(destination)
func InboundICMPError(destinations []netip.Addr) (*Filter, error) {
f, err := inboundTo(destinations)
if err != nil {
return nil, err
}
if destination.Is4() {
if destinations[0].Is4() {
f.add(fieldICMP, testEQ, argUint32(1))
f.add(fieldICMPType, testGEQ, argUint32(3))
f.add(fieldICMPType, testLEQ, argUint32(12))
@@ -194,6 +208,10 @@ func (f *Filter) add(field uint16, test uint8, arg [4]uint32) {
f.insts = append(f.insts, filterInst{field: field, test: test, arg: arg})
}
func (f *Filter) addAny(field uint16, test uint8, arg [4]uint32) {
f.anyInsts = append(f.anyInsts, filterInst{field: field, test: test, arg: arg})
}
func argUint32(v uint32) [4]uint32 { return [4]uint32{v, 0, 0, 0} }
// argIPv4 encodes an IPv4 address for IP_SRCADDR/IP_DSTADDR. The driver
@@ -222,9 +240,12 @@ func argIPv6(addr netip.Addr) [4]uint32 {
}
// encode serializes the Filter to the on-wire WINDIVERT_FILTER[] format
// plus the filter_flags for STARTUP ioctl.
// plus the filter_flags for STARTUP ioctl. insts chain as AND (failure
// rejects); anyInsts follow as an OR block (success accepts, failure falls
// through to the next alternative).
func (f *Filter) encode() ([]byte, uint64, error) {
if len(f.insts) == 0 {
total := len(f.insts) + len(f.anyInsts)
if total == 0 {
// "Reject all" — one instruction, ZERO == 0 is always true, but we
// invert by setting both success and failure to REJECT.
return encodeInst(filterInst{
@@ -234,12 +255,12 @@ func (f *Filter) encode() ([]byte, uint64, error) {
failure: resultReject,
}), 0, nil
}
if len(f.insts) > filterMaxInsts-1 {
if total > filterMaxInsts-1 {
return nil, 0, E.New("windivert: filter too long")
}
buf := make([]byte, 0, filterInstBytes*len(f.insts))
buf := make([]byte, 0, filterInstBytes*total)
for i, inst := range f.insts {
if i == len(f.insts)-1 {
if i == total-1 {
inst.success = resultAccept
} else {
inst.success = uint16(i + 1)
@@ -247,6 +268,15 @@ func (f *Filter) encode() ([]byte, uint64, error) {
inst.failure = resultReject
buf = append(buf, encodeInst(inst)...)
}
for i, inst := range f.anyInsts {
inst.success = resultAccept
if len(f.insts)+i == total-1 {
inst.failure = resultReject
} else {
inst.failure = uint16(len(f.insts) + i + 1)
}
buf = append(buf, encodeInst(inst)...)
}
return buf, f.flags, nil
}