Files
RemLink/internal/subnet/validator.go
T
qsc20001102 142e5dc7d6
ci / Go checks (ubuntu-latest) (push) Has been cancelled
ci / Go checks (windows-latest) (push) Has been cancelled
初版功能完成
2026-08-29 13:12:17 +08:00

83 lines
2.8 KiB
Go

package subnet
import (
"errors"
"fmt"
"net/netip"
"remlink/internal/protocol"
)
var (
ErrUnknownSession = errors.New("unknown or inactive Session")
ErrOuterSourceMismatch = errors.New("Session outer source does not match peer Overlay IP")
ErrInnerIPv4Invalid = errors.New("Session payload is not one complete IPv4 packet")
ErrInnerSourceMismatch = errors.New("Session inner source is not allowed")
ErrInnerTargetMismatch = errors.New("Session inner destination is not allowed")
)
// ValidateDatagram verifies framing, Active Session identity, outer source,
// IPv4 total length, and direction-specific inner addresses.
func ValidateDatagram(data []byte, outerSource netip.Addr, registry *Registry) (uint64, []byte, error) {
if registry == nil {
return 0, nil, ErrUnknownSession
}
header, packet, err := protocol.DecodeIPv4Session(data)
if err != nil {
return 0, nil, err
}
binding, found := registry.Lookup(header.SessionID)
if !found || !binding.Active {
return 0, nil, ErrUnknownSession
}
if outerSource.Unmap() != binding.PeerOverlayIP.Unmap() {
return 0, nil, ErrOuterSourceMismatch
}
source, destination, err := ipv4Addresses(packet)
if err != nil {
return 0, nil, err
}
switch binding.Direction {
case EngineerToSite:
if source != binding.EngineerOverlayIP {
return 0, nil, fmt.Errorf("%w: got %s want %s", ErrInnerSourceMismatch, source, binding.EngineerOverlayIP)
}
if !containedBy(destination, binding.RemoteCIDRs) {
return 0, nil, fmt.Errorf("%w: %s is outside Remote CIDRs", ErrInnerTargetMismatch, destination)
}
case SiteToEngineer:
if !containedBy(source, binding.RemoteCIDRs) {
return 0, nil, fmt.Errorf("%w: %s is outside Remote CIDRs", ErrInnerSourceMismatch, source)
}
if destination != binding.EngineerOverlayIP {
return 0, nil, fmt.Errorf("%w: got %s want %s", ErrInnerTargetMismatch, destination, binding.EngineerOverlayIP)
}
default:
return 0, nil, ErrUnknownSession
}
return header.SessionID, packet, nil
}
func ipv4Addresses(packet []byte) (source, destination netip.Addr, err error) {
if len(packet) < 20 || packet[0]>>4 != 4 {
return netip.Addr{}, netip.Addr{}, ErrInnerIPv4Invalid
}
headerLength := int(packet[0]&0x0F) * 4
totalLength := int(packet[2])<<8 | int(packet[3])
if headerLength < 20 || headerLength > len(packet) || totalLength != len(packet) || totalLength < headerLength {
return netip.Addr{}, netip.Addr{}, ErrInnerIPv4Invalid
}
source = netip.AddrFrom4([4]byte{packet[12], packet[13], packet[14], packet[15]})
destination = netip.AddrFrom4([4]byte{packet[16], packet[17], packet[18], packet[19]})
return source, destination, nil
}
func containedBy(address netip.Addr, prefixes []netip.Prefix) bool {
for _, prefix := range prefixes {
if prefix.Contains(address) {
return true
}
}
return false
}