初版功能完成
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user