mirror of
https://github.com/soypat/lneto.git
synced 2026-08-23 16:09:07 +00:00
add ipv6 to xnet.StackAsync (#107)
* add ipv6 to xnet.StackAsync * dns improvements * improve DNS workings of StackAsync * add tentative ICMPv6 * work on prefixes and fix some small bugs, plan UDP/TCP6 * fix bugs in StackAsync and ipv4.Prefix.Contains * update arpsubtable * completely remove legacy internet.StackIP for StackIPv4/v6 * ipv4/ipv6 tcp/udp * add TCP6/UDP6 dialing APIs * add xnet.Stack6 interface * more ipv6 integration into StackAsync; various tweaks to lneto and documentation+TODOs * add stack6 tests * replace netip.Prefix with ipv4.Prefix where it makes sense
This commit is contained in:
+205
-145
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/soypat/lneto/ethernet"
|
||||
"github.com/soypat/lneto/internal"
|
||||
"github.com/soypat/lneto/internet"
|
||||
"github.com/soypat/lneto/ipv4"
|
||||
"github.com/soypat/lneto/ipv4/icmpv4"
|
||||
"github.com/soypat/lneto/ntp"
|
||||
"github.com/soypat/lneto/tcp"
|
||||
@@ -23,6 +24,7 @@ import (
|
||||
|
||||
const (
|
||||
minTCPBuffer = 256
|
||||
icmpEchoSize = 64
|
||||
)
|
||||
|
||||
type StackAsync struct {
|
||||
@@ -30,9 +32,13 @@ type StackAsync struct {
|
||||
hostname string
|
||||
clientID string
|
||||
link internet.StackEthernet
|
||||
ip internet.StackIP
|
||||
arp arp.Handler
|
||||
icmp icmpv4.Client
|
||||
ip4 internet.StackIPv4
|
||||
|
||||
// ip6 internet.StackIPv6
|
||||
arp arp.Handler
|
||||
icmp icmpv4.Client
|
||||
// icmp6 icmpv6.Client
|
||||
icmp6buf []byte
|
||||
udps internet.StackPortsMACFiltered
|
||||
tcps internet.StackPortsMACFiltered
|
||||
|
||||
@@ -58,38 +64,51 @@ type StackAsync struct {
|
||||
|
||||
prng uint32
|
||||
|
||||
addrBuf [6]byte // Temporary buffer for As4()/HardwareAddr6() results to avoid heap escapes.
|
||||
addrBuf [6]byte // Temporary buffer for As4()/HardwareAddr6() results to avoid heap escapes.
|
||||
addrbufnip [4]netip.Addr
|
||||
|
||||
totalsent uint64
|
||||
totalrecv uint64
|
||||
stats Statistics
|
||||
|
||||
ipv6enabled bool
|
||||
stack6 Stack6
|
||||
}
|
||||
|
||||
type StackConfig struct {
|
||||
// StaticAddress6 [16]byte
|
||||
StaticAddress4 [4]byte
|
||||
HardwareAddress [6]byte
|
||||
StaticAddress4 [4]byte
|
||||
StaticAddress6 [16]byte
|
||||
|
||||
IPv6Stack Stack6
|
||||
|
||||
DNSServer netip.Addr
|
||||
NTPServer netip.Addr
|
||||
RandSeed int64
|
||||
Hostname string
|
||||
|
||||
// MaxActiveTCPPorts and MaxActiveUDPPorts are a memory guardrail to limit
|
||||
// number of simultaneous open TCP/UDP ports. The memory impact at the stack level
|
||||
// of a port corresponds to ~64 bytes excluding the registered StackNode i.e: [tcp.Conn] or [udp.Conn].
|
||||
MaxActiveTCPPorts, MaxActiveUDPPorts uint16
|
||||
// Hostname is used for DHCP hostname and ICMP ID.
|
||||
Hostname string
|
||||
|
||||
EthernetTxCRC32Update func(crc uint32, b []byte) uint32
|
||||
|
||||
HardwareAddress [6]byte
|
||||
MTU uint16
|
||||
// Accept multicast ethernet and IP packets. Needed for MDNS.
|
||||
AcceptMulticast bool
|
||||
// ICMPQueueLimit sets maximum number of input/output packets queued for processing.
|
||||
// If set to zero ICMP cannot be enabled on the stack.
|
||||
ICMPQueueLimit int
|
||||
// PassivePeers limits how many subnet peers the stack passively learns MAC addresses for.
|
||||
// Passively learned entries skip ARP round-trips on the first DialTCP/DialUDP to that peer.
|
||||
PassivePeers int
|
||||
|
||||
// MaxActiveTCPPorts and MaxActiveUDPPorts are a memory guardrail to limit
|
||||
// number of simultaneous open TCP/UDP ports. The memory impact at the stack level
|
||||
// of a port corresponds to ~64 bytes excluding the registered StackNode i.e: [tcp.Conn] or [udp.Conn].
|
||||
MaxActiveTCPPorts, MaxActiveUDPPorts uint16
|
||||
// MTU sets the maximum transmission unit, which is the maximum size of the Ethernet payload
|
||||
// not including ethernet header, ethernet CRC. It is determined by the NIC hardware and the route the packets take over the network.
|
||||
// By far the most common value for MTU is 1500 as specified by IEEE 802.3.
|
||||
MTU uint16
|
||||
// Accept multicast ethernet and IP packets. Needed for MDNS.
|
||||
AcceptMulticast bool
|
||||
}
|
||||
|
||||
func (cfg *StackConfig) id() uint16 {
|
||||
return uint16(cfg.Hostname[len(cfg.Hostname)-1] - '0')
|
||||
}
|
||||
|
||||
func (s *StackAsync) Hostname() string {
|
||||
@@ -100,7 +119,7 @@ func (s *StackAsync) Hostname() string {
|
||||
func (s *StackAsync) IngressEthernet(ethernetFrame []byte) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.totalrecv += uint64(len(ethernetFrame))
|
||||
s.stats.TotalReceived += uint64(len(ethernetFrame))
|
||||
err := s.link.Demux(ethernetFrame, 0)
|
||||
if err == nil {
|
||||
s.arpt.learnFromIngressEthernet(ethernetFrame)
|
||||
@@ -114,16 +133,28 @@ func (s *StackAsync) EgressEthernet(dstEthernetFrame []byte) (int, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
n, err := s.link.Encapsulate(dstEthernetFrame, -1, 0)
|
||||
s.totalsent += uint64(n)
|
||||
s.stats.TotalSent += uint64(n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
// IngressIP processes an incoming IP frame through the stack and omits ethernet header processing.
|
||||
func (s *StackAsync) IngressIP(ipFrame []byte) error {
|
||||
if len(ipFrame) < 1 {
|
||||
return lneto.ErrTruncatedFrame
|
||||
}
|
||||
version := ipFrame[0] >> 4
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.totalrecv += uint64(len(ipFrame))
|
||||
return s.ip.Demux(ipFrame, 0)
|
||||
s.stats.TotalReceived += uint64(len(ipFrame))
|
||||
switch version {
|
||||
case 4:
|
||||
return s.ip4.Demux(ipFrame, 0)
|
||||
case 6:
|
||||
if s.ipv6enabled {
|
||||
return s.stack6.IngressIPv6(ipFrame)
|
||||
}
|
||||
}
|
||||
return lneto.ErrPacketDrop
|
||||
}
|
||||
|
||||
// EgressIP writes the next IP frame to send into dstIPFrame from the stack. The length of dstIPFrame should be at least MTU.
|
||||
@@ -133,8 +164,11 @@ func (s *StackAsync) EgressIP(dstIPFrame []byte) (int, error) {
|
||||
if len(dstIPFrame) < s.link.MTU() {
|
||||
return 0, lneto.ErrShortBuffer
|
||||
}
|
||||
n, err := s.ip.Encapsulate(dstIPFrame, 0, 0)
|
||||
s.totalsent += uint64(n)
|
||||
n, err := s.ip4.Encapsulate(dstIPFrame, 0, 0)
|
||||
if s.ipv6enabled && n == 0 {
|
||||
n, err = s.stack6.EgressIPv6(dstIPFrame)
|
||||
}
|
||||
s.stats.TotalSent += uint64(n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
@@ -147,17 +181,33 @@ func (s *StackAsync) MTU() int {
|
||||
return s.link.MTU()
|
||||
}
|
||||
|
||||
func (s *StackAsync) Reset(cfg StackConfig) error {
|
||||
func (s *StackAsync) Reset(cfg StackConfig) (err error) {
|
||||
ipv6Enabled := cfg.IPv6Stack != nil
|
||||
if cfg.RandSeed == 0 || cfg.Hostname == "" || cfg.PassivePeers > 255 {
|
||||
return lneto.ErrInvalidConfig
|
||||
} else if !internal.IsZeroed(cfg.StaticAddress6) && !ipv6Enabled {
|
||||
return lneto.ErrBug // Forgot to EnableIPv6 after setting static IPv6 address.
|
||||
}
|
||||
mac := cfg.HardwareAddress
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.prng = uint32(cfg.RandSeed)
|
||||
s.hostname = cfg.Hostname
|
||||
|
||||
const linkNodes = 2 // ARP and IP nodes
|
||||
// Treat last character of hostname as number.
|
||||
id := cfg.id()
|
||||
linkNodes := 2 // ARP and IPv4 nodes
|
||||
s.ipv6enabled = ipv6Enabled
|
||||
s.stack6 = nil
|
||||
if s.ipv6enabled {
|
||||
linkNodes = 3 // IPv6
|
||||
s.Debug("ipv6 enabled")
|
||||
err = cfg.IPv6Stack.Reset6(&cfg)
|
||||
if err != nil {
|
||||
s.ipv6enabled = false
|
||||
return err
|
||||
}
|
||||
}
|
||||
s.stack6 = cfg.IPv6Stack
|
||||
ecfg := internet.StackEthernetConfig{
|
||||
MTU: int(cfg.MTU),
|
||||
MaxNodes: linkNodes,
|
||||
@@ -166,42 +216,37 @@ func (s *StackAsync) Reset(cfg StackConfig) error {
|
||||
AppendCRC32: cfg.EthernetTxCRC32Update != nil,
|
||||
CRC32Update: cfg.EthernetTxCRC32Update,
|
||||
}
|
||||
err := s.link.Configure(ecfg)
|
||||
err = s.link.Configure(ecfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.link.SetAcceptMulticast(cfg.AcceptMulticast)
|
||||
if cfg.PassivePeers == 0 {
|
||||
s.link.OnEncapsulate(nil)
|
||||
} else {
|
||||
s.link.OnEncapsulate(s.arpt.patchEgressMAC)
|
||||
}
|
||||
const ipNodes = 3 // 3 IP protocols possible: UDP, TCP, ICMP.
|
||||
err = s.ip.Reset(&s.defaultValidator, ipNodes, 0)
|
||||
err = s.ip4.Reset(&s.defaultValidator, ipNodes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.ip.SetAddr4(cfg.StaticAddress4)
|
||||
s.ip.SetAcceptMulticast4(cfg.AcceptMulticast)
|
||||
s.ip4.SetAddr4(cfg.StaticAddress4)
|
||||
s.setAcceptMulticast4(cfg.AcceptMulticast)
|
||||
|
||||
s.arpt.passivePeers = uint8(cfg.PassivePeers)
|
||||
err = s.resetARP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
udpConns := 3 + cfg.MaxActiveUDPPorts // DHCP, DNS, NTP + user-registered.
|
||||
err = s.udps.ResetUDP(udpConns)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.udps.ResetUDP(udpConns)
|
||||
|
||||
internal.SliceReuse(&s.userUDPs, int(cfg.MaxActiveUDPPorts))
|
||||
|
||||
// Enable TCP if connections present.
|
||||
if cfg.MaxActiveTCPPorts > 0 {
|
||||
err = s.tcps.ResetTCP(cfg.MaxActiveTCPPorts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.ip.Register4(&s.tcps)
|
||||
s.tcps.ResetTCP(cfg.MaxActiveTCPPorts)
|
||||
err = s.ip4.Register4(&s.tcps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -209,20 +254,21 @@ func (s *StackAsync) Reset(cfg StackConfig) error {
|
||||
|
||||
// Now setup stacks.
|
||||
// ARP registered in resetARP.
|
||||
err = s.link.Register(&s.ip) // IPv4 | IPv6
|
||||
err = s.link.RegisterEthernet(&s.ip4) // IPv4
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.ip.Register4(&s.udps)
|
||||
|
||||
err = s.ip4.Register4(&s.udps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cfg.ICMPQueueLimit > 0 {
|
||||
err = s.icmp.Configure(icmpv4.ClientConfig{
|
||||
ResponseQueueBuffer: make([]byte, cfg.ICMPQueueLimit*64),
|
||||
ResponseQueueBuffer: make([]byte, cfg.ICMPQueueLimit*icmpEchoSize),
|
||||
ResponseQueueLimit: cfg.ICMPQueueLimit,
|
||||
HashSeed: s.prand32(),
|
||||
ID: uint16(cfg.Hostname[len(cfg.Hostname)-1]) - '0', // Treat last character of hostname as number.
|
||||
ID: id,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -233,17 +279,23 @@ func (s *StackAsync) Reset(cfg StackConfig) error {
|
||||
if s.clientID == "" {
|
||||
s.clientID = "lneto-" + s.hostname
|
||||
}
|
||||
s.totalrecv = 0
|
||||
s.totalsent = 0
|
||||
s.stats = Statistics{}
|
||||
if cfg.DNSServer.IsValid() {
|
||||
s.dnssv = cfg.DNSServer
|
||||
}
|
||||
if s.ipv6enabled {
|
||||
s.Debug("registering IPv6 to ethernet")
|
||||
err = s.link.RegisterEthernet(s.stack6.IPv6Stack())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *StackAsync) resetARP() error {
|
||||
mac := s.link.HardwareAddr6()
|
||||
addr := s.ip.Addr4()
|
||||
addr := s.ip4.Addr4()
|
||||
proto := ethernet.TypeIPv4
|
||||
err := s.arp.Reset(arp.HandlerConfig{
|
||||
HardwareAddr: mac[:],
|
||||
@@ -258,7 +310,7 @@ func (s *StackAsync) resetARP() error {
|
||||
}
|
||||
s.arpt.reset(10, s.arpt.passivePeers)
|
||||
s.arp.SetOnResolveCallback(s.arpt.onResolve)
|
||||
err = s.link.Register(&s.arp)
|
||||
err = s.link.RegisterEthernet(&s.arp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -299,42 +351,42 @@ func (s *StackAsync) SetAddr4(addr [4]byte) error {
|
||||
}
|
||||
|
||||
func (s *StackAsync) setIPAddr4(addr [4]byte) error {
|
||||
s.ip.SetAddr4(addr)
|
||||
s.ip4.SetAddr4(addr)
|
||||
return s.arp.UpdateProtoAddr(addr[:])
|
||||
}
|
||||
|
||||
func (s *StackAsync) Addr4() [4]byte {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return s.ip.Addr4()
|
||||
return s.ip4.Addr4()
|
||||
}
|
||||
|
||||
func (s *StackAsync) SetSubnet(subnetMask netip.Prefix) {
|
||||
func (s *StackAsync) SetSubnet4(addr [4]byte, prefixBits uint8) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.arpt.subnet = subnetMask
|
||||
s.arpt.subnet4 = ipv4.PrefixFrom(addr, prefixBits)
|
||||
}
|
||||
|
||||
func (s *StackAsync) SetHardwareAddress(hw [6]byte) error {
|
||||
func (s *StackAsync) SetHardwareAddr(hw [6]byte) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.link.SetHardwareAddr6(hw)
|
||||
return s.resetARP()
|
||||
}
|
||||
|
||||
func (s *StackAsync) HardwareAddress() (hw [6]byte) {
|
||||
func (s *StackAsync) HardwareAddr() (hw [6]byte) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return s.link.HardwareAddr6()
|
||||
}
|
||||
|
||||
func (s *StackAsync) SetGateway6(gwhw [6]byte) {
|
||||
func (s *StackAsync) SetGatewayHardwareAddr(gwhw [6]byte) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.link.SetGateway6(gwhw)
|
||||
}
|
||||
|
||||
func (s *StackAsync) Gateway6() [6]byte {
|
||||
func (s *StackAsync) GatewayHardwareAddr() [6]byte {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return s.link.Gateway6()
|
||||
@@ -348,71 +400,56 @@ func (s *StackAsync) EnableICMP(enabled bool) (err error) {
|
||||
enabled = false // ensure aborted.
|
||||
}
|
||||
if enabled {
|
||||
if s.ip.IsRegistered4(lneto.IPProtoICMP) {
|
||||
return nil
|
||||
if !s.ip4.IsRegistered4(lneto.IPProtoICMP) {
|
||||
err = s.ip4.Register4(&s.icmp)
|
||||
}
|
||||
err = s.ip.Register4(&s.icmp)
|
||||
} else {
|
||||
s.icmp.Abort()
|
||||
}
|
||||
if s.ipv6enabled {
|
||||
if err2 := s.stack6.EnableICMP6(enabled); err2 != nil {
|
||||
err = err2
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *StackAsync) DialUDP(conn *udp.Conn, localPort uint16, addrp netip.AddrPort) (err error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
var mac []byte
|
||||
if s.arpt.subnet.Contains(addrp.Addr()) {
|
||||
mac = make([]byte, 6)
|
||||
ip := addrp.Addr().As4()
|
||||
hw, err := s.arp.CacheLookup(ip[:])
|
||||
if err == nil {
|
||||
// MAC already contained in results.
|
||||
copy(mac, hw)
|
||||
} else {
|
||||
// StartQuery starts an ARP query for addresses in this network.
|
||||
// On finishing query MAC is set and thus the StackPort will allow encapsulating
|
||||
// data on that connection.
|
||||
err = s.arpt.startQuery(mac, ip[:], &s.arp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
addr := addrp.Addr()
|
||||
if addr.Is4() {
|
||||
err = s.DialUDP4(conn, localPort, addrp.Addr().As4(), addrp.Port())
|
||||
} else if s.ipv6enabled && addr.Is6() {
|
||||
err = s.stack6.DialUDP6(conn, localPort, addr.As16(), addrp.Port())
|
||||
} else {
|
||||
err = lneto.ErrInvalidAddr
|
||||
}
|
||||
err = conn.Open(localPort, addrp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.udps.Register(conn, mac)
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *StackAsync) DialTCP(conn *tcp.Conn, localPort uint16, addrp netip.AddrPort) (err error) {
|
||||
addr := addrp.Addr()
|
||||
if addr.Is4() {
|
||||
err = s.DialTCP4(conn, localPort, addrp.Addr().As4(), addrp.Port())
|
||||
} else if s.ipv6enabled && addr.Is6() {
|
||||
err = s.stack6.DialTCP6(conn, localPort, addr.As16(), addrp.Port(), tcp.Value(s.Prand32()))
|
||||
} else {
|
||||
err = lneto.ErrInvalidAddr
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *StackAsync) DialUDP4(conn *udp.Conn, localPort uint16, raddr [4]byte, rport uint16) (err error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
var mac []byte
|
||||
if s.arpt.subnet.Contains(addrp.Addr()) {
|
||||
ip := addrp.Addr().As4()
|
||||
hw, err := s.arp.CacheLookup(ip[:])
|
||||
mac = make([]byte, 6)
|
||||
if err == nil {
|
||||
// Query exists, use pre-existing result.
|
||||
copy(mac, hw)
|
||||
} else {
|
||||
// StartQuery starts an ARP query for addresses in this network.
|
||||
// On finishing query MAC is set and thus the StackPort will allow encapsulating
|
||||
// data on that connection.
|
||||
err = s.arpt.startQuery(mac, ip[:], &s.arp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
err = conn.OpenActive(localPort, addrp, tcp.Value(s.prand32()))
|
||||
mac, err := s.arpt.hwDynamicResolve(raddr, &s.arp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.tcps.Register(conn, mac) // MAC is set later on by ARP response arriving to our network.
|
||||
err = conn.Open(localPort, netip.AddrPortFrom(netip.AddrFrom4(raddr), rport))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.udps.RegisterMACFiltered(conn, mac)
|
||||
if err != nil {
|
||||
conn.Abort()
|
||||
return err
|
||||
@@ -420,14 +457,33 @@ func (s *StackAsync) DialTCP(conn *tcp.Conn, localPort uint16, addrp netip.AddrP
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *StackAsync) ListenTCP(conn *tcp.Conn, localPort uint16) (err error) {
|
||||
func (s *StackAsync) DialTCP4(conn *tcp.Conn, localPort uint16, raddr [4]byte, rport uint16) (err error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
mac, err := s.arpt.hwDynamicResolve(raddr, &s.arp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = conn.OpenActive(localPort, netip.AddrPortFrom(netip.AddrFrom4(raddr), rport), tcp.Value(s.prand32()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.tcps.RegisterMACFiltered(conn, mac) // MAC is set later on by ARP response arriving to our network.
|
||||
if err != nil {
|
||||
conn.Abort()
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *StackAsync) ListenTCP4(conn *tcp.Conn, localPort uint16) (err error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
err = conn.OpenListen(localPort, tcp.Value(s.prand32()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.tcps.Register(conn, nil)
|
||||
err = s.tcps.RegisterMACFiltered(conn, nil)
|
||||
if err != nil {
|
||||
conn.Abort()
|
||||
return err
|
||||
@@ -436,19 +492,21 @@ func (s *StackAsync) ListenTCP(conn *tcp.Conn, localPort uint16) (err error) {
|
||||
}
|
||||
|
||||
func (s *StackAsync) RegisterListener(listener *tcp.Listener) (err error) {
|
||||
// TODO(pato): Possible to forward both IPv4 and IPv6 packets to the listener and have it selectively mux out correctly?
|
||||
// Can try changing listener to inspect carrierData on demux and get the IPversion to know which tcp.Conns match the IP version.
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
lport := listener.LocalPort()
|
||||
if lport == 0 {
|
||||
return lneto.ErrZeroSource
|
||||
}
|
||||
return s.tcps.Register(listener, nil)
|
||||
return s.tcps.RegisterMACFiltered(listener, nil)
|
||||
}
|
||||
|
||||
// RegisterUDP registers a StackNode on a UDP port with the given remote address and port.
|
||||
// RegisterUDP4 registers a StackNode on a UDP port with the given remote address and port.
|
||||
// The StackUDPPort wrapping is handled internally. The number of user-registered UDP ports
|
||||
// is limited by [StackConfig.MaxUDPConns].
|
||||
func (s *StackAsync) RegisterUDP(node lneto.StackNode, remoteAddr []byte, remotePort uint16) error {
|
||||
func (s *StackAsync) RegisterUDP4(node lneto.StackNode, remoteAddr []byte, remotePort uint16) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
idx := len(s.userUDPs)
|
||||
@@ -457,7 +515,7 @@ func (s *StackAsync) RegisterUDP(node lneto.StackNode, remoteAddr []byte, remote
|
||||
}
|
||||
s.userUDPs = s.userUDPs[:idx+1]
|
||||
s.userUDPs[idx].SetStackNode(node, remoteAddr, remotePort)
|
||||
return s.udps.Register(&s.userUDPs[idx], nil)
|
||||
return s.udps.RegisterMACFiltered(&s.userUDPs[idx], nil)
|
||||
}
|
||||
|
||||
var errNoDNSServer = errors.New("no DNS server- did DHCP complete? You can set a predetermined DNS server in Stack configuration")
|
||||
@@ -495,38 +553,27 @@ func (s *StackAsync) StartLookupIP(host string) error {
|
||||
}
|
||||
*(*[4]byte)(s.addrBuf[:4]) = s.dnssv.As4()
|
||||
s.dnsUDP.SetStackNode(&s.dns, s.addrBuf[:4], dns.ServerPort)
|
||||
err = s.udps.Register(&s.dnsUDP, nil)
|
||||
err = s.udps.RegisterMACFiltered(&s.dnsUDP, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
var errDNSNotDone = errors.New("DNS not done")
|
||||
var (
|
||||
errDNSNotDone = errors.New("DNS not done")
|
||||
errDNSNoAns = errors.New("no address in DNS answer")
|
||||
)
|
||||
|
||||
func (s *StackAsync) ResultLookupIP(host string) ([]netip.Addr, bool, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
done, err := s.dns.MessageCopyTo(&s.lookup)
|
||||
if err != nil {
|
||||
return nil, done, err
|
||||
} else if !done {
|
||||
return nil, done, errDNSNotDone
|
||||
_, ok := s.dns.ResponseFlags()
|
||||
if !ok {
|
||||
return nil, false, errDNSNotDone
|
||||
}
|
||||
|
||||
var addrs []netip.Addr
|
||||
ans := s.lookup.Answers
|
||||
for i := range ans {
|
||||
data := ans[i].RawData()
|
||||
if len(data) == 4 {
|
||||
addrs = append(addrs, netip.AddrFrom4([4]byte(data)))
|
||||
} else if len(data) == 16 {
|
||||
addrs = append(addrs, netip.AddrFrom16([16]byte(data)))
|
||||
} else {
|
||||
err = lneto.ErrInvalidAddr
|
||||
}
|
||||
n, err := s.dns.ResponseAnswerLookup(s.addrbufnip[:], host)
|
||||
if n == 0 && err == nil {
|
||||
err = errDNSNoAns
|
||||
}
|
||||
if err == nil && len(addrs) == 0 {
|
||||
err = errors.New("no address in DNS answer")
|
||||
}
|
||||
return addrs, done, err
|
||||
return s.addrbufnip[:n], true, err
|
||||
}
|
||||
|
||||
func (s *StackAsync) StartDHCPv4Request(request [4]byte) error {
|
||||
@@ -545,7 +592,7 @@ func (s *StackAsync) StartDHCPv4Request(request [4]byte) error {
|
||||
}
|
||||
|
||||
s.dhcpUDP.SetStackNode(&s.dhcp, nil, dhcpv4.DefaultServerPort)
|
||||
err = s.udps.Register(&s.dhcpUDP, nil)
|
||||
err = s.udps.RegisterMACFiltered(&s.dhcpUDP, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -559,7 +606,7 @@ func (s *StackAsync) StartNTP(addr netip.Addr) error {
|
||||
|
||||
*(*[4]byte)(s.addrBuf[:4]) = addr.As4()
|
||||
s.ntpUDP.SetStackNode(&s.ntp, s.addrBuf[:4], ntp.ServerPort)
|
||||
err := s.udps.Register(&s.ntpUDP, nil)
|
||||
err := s.udps.RegisterMACFiltered(&s.ntpUDP, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -611,6 +658,17 @@ func (s *StackAsync) DiscardResolveHardwareAddress6(ip netip.Addr) error {
|
||||
return s.arp.CacheRemove(addr[:])
|
||||
}
|
||||
|
||||
func (s *StackAsync) SetAcceptMulticast4(enabled bool) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.setAcceptMulticast4(enabled)
|
||||
}
|
||||
|
||||
func (s *StackAsync) setAcceptMulticast4(enabled bool) {
|
||||
s.link.SetAcceptMulticast(enabled)
|
||||
s.ip4.SetAcceptMulticast4(enabled)
|
||||
}
|
||||
|
||||
type DHCPResults struct {
|
||||
DNSServers []netip.Addr
|
||||
Router netip.Addr
|
||||
@@ -640,8 +698,9 @@ type Statistics struct {
|
||||
}
|
||||
|
||||
func (s *StackAsync) ReadStatistics(stats *Statistics) {
|
||||
stats.TotalReceived = s.totalrecv
|
||||
stats.TotalSent = s.totalsent
|
||||
s.mu.Lock()
|
||||
*stats = s.stats
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
// AssimilateDHCPResults sets the stack's following parameters:
|
||||
@@ -651,8 +710,8 @@ func (s *StackAsync) ReadStatistics(stats *Statistics) {
|
||||
func (stack *StackAsync) AssimilateDHCPResults(results *DHCPResults) error {
|
||||
stack.mu.Lock()
|
||||
defer stack.mu.Unlock()
|
||||
if results.Subnet.IsValid() {
|
||||
stack.arpt.subnet = results.Subnet
|
||||
if results.Subnet.IsValid() && results.Subnet.Addr().Is4() {
|
||||
stack.arpt.subnet4 = ipv4.PrefixFromNetip(results.Subnet)
|
||||
}
|
||||
if !internal.IsZeroed(results.AssignedAddr4) {
|
||||
err := stack.setIPAddr4(results.AssignedAddr4)
|
||||
@@ -682,9 +741,10 @@ func (s *StackAsync) populateDHCPResults() error {
|
||||
return errors.New("no DHCP assigned address")
|
||||
}
|
||||
router := netip.AddrFrom4(router4)
|
||||
subnet := s.dhcp.SubnetPrefix()
|
||||
s.dhcpResults = DHCPResults{
|
||||
Router: router,
|
||||
Subnet: s.dhcp.SubnetPrefix(),
|
||||
Subnet: subnet.NetipPrefix(),
|
||||
AssignedAddr4: assigned4,
|
||||
ServerAddr: addr4(s.dhcp.ServerAddr()),
|
||||
BroadcastAddr: addr4(s.dhcp.BroadcastAddr()),
|
||||
@@ -712,8 +772,8 @@ func addr4(addr [4]byte, ok bool) netip.Addr {
|
||||
func (s *StackAsync) Debug(msg string) {
|
||||
internal.LogAttrs(slog.Default(), slog.LevelDebug, "stackasync",
|
||||
slog.String("umsg", msg),
|
||||
slog.Uint64("sent", s.totalsent),
|
||||
slog.Uint64("recv", s.totalrecv),
|
||||
slog.Uint64("sent", s.stats.TotalSent),
|
||||
slog.Uint64("recv", s.stats.TotalReceived),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -725,7 +785,7 @@ func (s *StackAsync) DebugErr(msg, err string) {
|
||||
internal.LogAttrs(slog.Default(), slog.LevelError, "stackasync",
|
||||
slog.String("umsg", msg),
|
||||
slog.String("err", err),
|
||||
slog.Uint64("sent", s.totalsent),
|
||||
slog.Uint64("recv", s.totalrecv),
|
||||
slog.Uint64("sent", s.stats.TotalSent),
|
||||
slog.Uint64("recv", s.stats.TotalReceived),
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user