mirror of
https://github.com/soypat/lneto.git
synced 2026-09-11 09:09:30 +00:00
More IPv6 support and race condition fixes (#123)
* changes in preparation of wireguard impl * suggestions by @MDr164
This commit is contained in:
@@ -35,8 +35,8 @@ go run ./examples/gen/gen-binary-bench # generate table
|
|||||||
|
|
||||||
| Program | Extra Protocols | Packet capture printing | amd64 Go | WASM Go | amd64 TinyGo | WASM TinyGo | Pico TinyGo |
|
| Program | Extra Protocols | Packet capture printing | amd64 Go | WASM Go | amd64 TinyGo | WASM TinyGo | Pico TinyGo |
|
||||||
|---|:---:|:---:|---|---|---|---|---|
|
|---|:---:|:---:|---|---|---|---|---|
|
||||||
| [Lneto MWE](./examples/min-working-example/) | DNS,NTP,DHCP | ✅ | 3.8MB | 4.3MB | 1.6MB | 1.2MB | 185kB |
|
| [Lneto MWE](./examples/min-working-example/) | DNS,NTP,DHCP | ✅ | 3.9MB | 4.4MB | 1.6MB | 1.2MB | 189kB |
|
||||||
| [Gvisor MWE w/ go-net](./examples/_import_examples/gvisor-mwe/) | None | ❌ | 6.6MB | 7.4MB | DNC | DNC | DNC |
|
| [Gvisor MWE w/ go-net](./examples/_import_examples/gvisor-mwe/) | None | ❌ | 6.6MB | 7.5MB | DNC | DNC | DNC |
|
||||||
## `xcurl` example
|
## `xcurl` example
|
||||||
You may try lneto out on linux with the [xcurl example](./examples/xcurl/) which gets an HTTP page by doing all the low-level networking part using absolutely no standard library.
|
You may try lneto out on linux with the [xcurl example](./examples/xcurl/) which gets an HTTP page by doing all the low-level networking part using absolutely no standard library.
|
||||||
|
|
||||||
|
|||||||
+10
@@ -103,6 +103,16 @@ func (conn *Conn) State() State {
|
|||||||
return conn.h.State()
|
return conn.h.State()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AwaitingSynSend reports whether the connection has been opened actively but has
|
||||||
|
// not yet emitted its SYN. It locks the connection so it is safe to poll from a
|
||||||
|
// goroutine other than the one driving the stack (unlike reaching through
|
||||||
|
// [Conn.InternalHandler]).
|
||||||
|
func (conn *Conn) AwaitingSynSend() bool {
|
||||||
|
conn.mu.Lock()
|
||||||
|
defer conn.mu.Unlock()
|
||||||
|
return conn.h.AwaitingSynSend()
|
||||||
|
}
|
||||||
|
|
||||||
// BufferedInput returns the number of bytes in the socket's receive(input) buffer
|
// BufferedInput returns the number of bytes in the socket's receive(input) buffer
|
||||||
// and available to read via a [Conn.Read] call.
|
// and available to read via a [Conn.Read] call.
|
||||||
func (conn *Conn) BufferedInput() int {
|
func (conn *Conn) BufferedInput() int {
|
||||||
|
|||||||
+59
-15
@@ -364,18 +364,18 @@ func (s *StackAsync) Addr4() [4]byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *StackAsync) SetAddr6(addr [16]byte) error {
|
func (s *StackAsync) SetAddr6(addr [16]byte) error {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
if s.ipv6enabled {
|
if s.ipv6enabled {
|
||||||
s.mu.Lock()
|
|
||||||
defer s.mu.Unlock()
|
|
||||||
return s.stack6.SetAddr6(addr)
|
return s.stack6.SetAddr6(addr)
|
||||||
}
|
}
|
||||||
return lneto.ErrUnsupported
|
return lneto.ErrUnsupported
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StackAsync) Addr6() [16]byte {
|
func (s *StackAsync) Addr6() [16]byte {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
if s.ipv6enabled {
|
if s.ipv6enabled {
|
||||||
s.mu.Lock()
|
|
||||||
defer s.mu.Unlock()
|
|
||||||
return s.stack6.Addr6()
|
return s.stack6.Addr6()
|
||||||
}
|
}
|
||||||
return [16]byte{}
|
return [16]byte{}
|
||||||
@@ -412,6 +412,13 @@ func (s *StackAsync) GatewayHardwareAddr() [6]byte {
|
|||||||
return s.link.Gateway6()
|
return s.link.Gateway6()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StackAsync) IsIPv6Enabled() bool {
|
||||||
|
s.mu.Lock()
|
||||||
|
enabled := s.ipv6enabled
|
||||||
|
s.mu.Unlock()
|
||||||
|
return enabled
|
||||||
|
}
|
||||||
|
|
||||||
// EnableICMP registers an ICMP handler to the stack when enabled is true.
|
// EnableICMP registers an ICMP handler to the stack when enabled is true.
|
||||||
// If enabled=false the currently registered ICMP handler is unregistered and state reset.
|
// If enabled=false the currently registered ICMP handler is unregistered and state reset.
|
||||||
func (s *StackAsync) EnableICMP(enabled bool) (err error) {
|
func (s *StackAsync) EnableICMP(enabled bool) (err error) {
|
||||||
@@ -437,25 +444,32 @@ func (s *StackAsync) EnableICMP(enabled bool) (err error) {
|
|||||||
func (s *StackAsync) DialUDP(conn *udp.Conn, localPort uint16, addrp netip.AddrPort) (err error) {
|
func (s *StackAsync) DialUDP(conn *udp.Conn, localPort uint16, addrp netip.AddrPort) (err error) {
|
||||||
addr := addrp.Addr()
|
addr := addrp.Addr()
|
||||||
if addr.Is4() {
|
if addr.Is4() {
|
||||||
err = s.DialUDP4(conn, localPort, addrp.Addr().As4(), addrp.Port())
|
return s.DialUDP4(conn, localPort, addrp.Addr().As4(), addrp.Port())
|
||||||
} else if s.ipv6enabled && addr.Is6() {
|
} else if s.ipv6enabled && addr.Is6() {
|
||||||
err = s.stack6.DialUDP6(conn, localPort, addr.As16(), addrp.Port())
|
// stack6 is guarded by s.mu (the single stack lock), just like the IPv4
|
||||||
} else {
|
// path locks inside DialUDP4. Hold it here so the port-handler mutation is
|
||||||
err = lneto.ErrInvalidAddr
|
// serialized against the Ingress/Egress demux.
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
return s.stack6.DialUDP6(conn, localPort, addr.As16(), addrp.Port())
|
||||||
}
|
}
|
||||||
return err
|
return lneto.ErrInvalidAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StackAsync) DialTCP(conn *tcp.Conn, localPort uint16, addrp netip.AddrPort) (err error) {
|
func (s *StackAsync) DialTCP(conn *tcp.Conn, localPort uint16, addrp netip.AddrPort) (err error) {
|
||||||
addr := addrp.Addr()
|
addr := addrp.Addr()
|
||||||
if addr.Is4() {
|
if addr.Is4() {
|
||||||
err = s.DialTCP4(conn, localPort, addrp.Addr().As4(), addrp.Port())
|
return s.DialTCP4(conn, localPort, addrp.Addr().As4(), addrp.Port())
|
||||||
} else if s.ipv6enabled && addr.Is6() {
|
} else if s.ipv6enabled && addr.Is6() {
|
||||||
err = s.stack6.DialTCP6(conn, localPort, addr.As16(), addrp.Port(), tcp.Value(s.Prand32()))
|
// stack6 is guarded by s.mu (the single stack lock), just like the IPv4
|
||||||
} else {
|
// path locks inside DialTCP4. Hold it here so the port-handler mutation is
|
||||||
err = lneto.ErrInvalidAddr
|
// serialized against the Ingress/Egress demux. Use the unlocked prand32
|
||||||
|
// since we already hold s.mu (Prand32 would deadlock).
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
return s.stack6.DialTCP6(conn, localPort, addr.As16(), addrp.Port(), tcp.Value(s.prand32()))
|
||||||
}
|
}
|
||||||
return err
|
return lneto.ErrInvalidAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StackAsync) DialUDP4(conn *udp.Conn, localPort uint16, raddr [4]byte, rport uint16) (err error) {
|
func (s *StackAsync) DialUDP4(conn *udp.Conn, localPort uint16, raddr [4]byte, rport uint16) (err error) {
|
||||||
@@ -540,14 +554,44 @@ func (s *StackAsync) RegisterListenerUDP(pktconn *udp.PacketConn) (err error) {
|
|||||||
return s.udps.RegisterMACFiltered(pktconn, nil)
|
return s.udps.RegisterMACFiltered(pktconn, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StackAsync) RegisterListenerTCP6(listener *tcp.Listener) (err error) {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
if !s.ipv6enabled {
|
||||||
|
return lneto.ErrUnsupported
|
||||||
|
}
|
||||||
|
return s.stack6.RegisterListenerTCP6(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StackAsync) RegisterListenerUDP6(pktconn *udp.PacketConn) (err error) {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
if !s.ipv6enabled {
|
||||||
|
return lneto.ErrUnsupported
|
||||||
|
}
|
||||||
|
return s.stack6.RegisterListenerUDP6(pktconn)
|
||||||
|
}
|
||||||
|
|
||||||
var errNoDNSServer = errors.New("no DNS server- did DHCP complete? You can set a predetermined DNS server in Stack configuration")
|
var errNoDNSServer = errors.New("no DNS server- did DHCP complete? You can set a predetermined DNS server in Stack configuration")
|
||||||
|
|
||||||
|
var errDNSv6Transport = errors.New("DNS query over IPv6 transport not supported; configure an IPv4 DNS server")
|
||||||
|
|
||||||
func (s *StackAsync) StartLookupIP(host string) error {
|
func (s *StackAsync) StartLookupIP(host string) error {
|
||||||
|
return s.StartLookupIPType(host, dns.TypeA)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartLookupIPType begins resolving host for the given record type (e.g. dns.TypeA
|
||||||
|
// or dns.TypeAAAA). The DNS query is always carried over IPv4 to the configured DNS
|
||||||
|
// server; resolving over an IPv6 DNS transport is not yet supported.
|
||||||
|
func (s *StackAsync) StartLookupIPType(host string, qtype dns.Type) error {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
if !s.dnssv.IsValid() {
|
if !s.dnssv.IsValid() {
|
||||||
return errNoDNSServer
|
return errNoDNSServer
|
||||||
}
|
}
|
||||||
|
if !s.dnssv.Is4() {
|
||||||
|
return errDNSv6Transport
|
||||||
|
}
|
||||||
name, err := dns.NewName(host)
|
name, err := dns.NewName(host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -561,7 +605,7 @@ func (s *StackAsync) StartLookupIP(host string) error {
|
|||||||
Questions: []dns.Question{
|
Questions: []dns.Question{
|
||||||
{
|
{
|
||||||
Name: name,
|
Name: name,
|
||||||
Type: dns.TypeA,
|
Type: qtype,
|
||||||
Class: dns.ClassINET,
|
Class: dns.ClassINET,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/soypat/lneto"
|
"github.com/soypat/lneto"
|
||||||
"github.com/soypat/lneto/dhcp/dhcpv4"
|
"github.com/soypat/lneto/dhcp/dhcpv4"
|
||||||
|
"github.com/soypat/lneto/dns"
|
||||||
"github.com/soypat/lneto/tcp"
|
"github.com/soypat/lneto/tcp"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -147,7 +148,13 @@ func (s StackBlocking) DoResolveHardwareAddress6(addr netip.Addr, timeout time.D
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s StackBlocking) DoLookupIP(host string, timeout time.Duration) (addrs []netip.Addr, err error) {
|
func (s StackBlocking) DoLookupIP(host string, timeout time.Duration) (addrs []netip.Addr, err error) {
|
||||||
err = s.async.StartLookupIP(host)
|
return s.DoLookupIPType(host, timeout, dns.TypeA)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DoLookupIPType resolves host for the given record type (dns.TypeA or dns.TypeAAAA),
|
||||||
|
// blocking until a response arrives or the timeout elapses.
|
||||||
|
func (s StackBlocking) DoLookupIPType(host string, timeout time.Duration, qtype dns.Type) (addrs []netip.Addr, err error) {
|
||||||
|
err = s.async.StartLookupIPType(host, qtype)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -180,7 +187,7 @@ func (s StackBlocking) DoDialTCP(conn *tcp.Conn, localPort uint16, addrp netip.A
|
|||||||
state := conn.State()
|
state := conn.State()
|
||||||
if state == tcp.StateEstablished {
|
if state == tcp.StateEstablished {
|
||||||
return nil
|
return nil
|
||||||
} else if state == tcp.StateSynSent || state == tcp.StateSynRcvd || conn.InternalHandler().AwaitingSynSend() {
|
} else if state == tcp.StateSynSent || state == tcp.StateSynRcvd || conn.AwaitingSynSend() {
|
||||||
if err = s.checkDeadline(deadline); err != nil {
|
if err = s.checkDeadline(deadline); err != nil {
|
||||||
conn.Abort()
|
conn.Abort()
|
||||||
return err
|
return err
|
||||||
|
|||||||
+36
-17
@@ -47,6 +47,10 @@ type StackGo struct {
|
|||||||
func (s StackGo) Socket(ctx context.Context, network string, family, sotype int, laddr, raddr net.Addr) (c any, err error) {
|
func (s StackGo) Socket(ctx context.Context, network string, family, sotype int, laddr, raddr net.Addr) (c any, err error) {
|
||||||
switch family {
|
switch family {
|
||||||
case syscall.AF_INET:
|
case syscall.AF_INET:
|
||||||
|
case syscall.AF_INET6:
|
||||||
|
if !s.blk.async.IsIPv6Enabled() {
|
||||||
|
return nil, errors.ErrUnsupported
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return nil, lneto.ErrUnsupported
|
return nil, lneto.ErrUnsupported
|
||||||
}
|
}
|
||||||
@@ -67,31 +71,38 @@ func (s StackGo) Socket(ctx context.Context, network string, family, sotype int,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s StackGo) SocketNetip(ctx context.Context, network string, family, sotype int, laddr, raddr netip.AddrPort) (c any, err error) {
|
func (s StackGo) SocketNetip(ctx context.Context, network string, family, sotype int, laddr, raddr netip.AddrPort) (c any, err error) {
|
||||||
|
var isV6 bool
|
||||||
switch family {
|
switch family {
|
||||||
case syscall.AF_INET:
|
case syscall.AF_INET:
|
||||||
|
case syscall.AF_INET6:
|
||||||
|
if !s.blk.async.IsIPv6Enabled() {
|
||||||
|
return nil, errors.ErrUnsupported
|
||||||
|
}
|
||||||
|
isV6 = true
|
||||||
default:
|
default:
|
||||||
return nil, lneto.ErrUnsupported
|
return nil, lneto.ErrUnsupported
|
||||||
}
|
}
|
||||||
|
// A dial targets a specified (non-unspecified) remote; a listen does not.
|
||||||
|
isDial := raddr.IsValid() && !raddr.Addr().IsUnspecified()
|
||||||
if laddr.Port() == 0 {
|
if laddr.Port() == 0 {
|
||||||
if raddr.IsValid() && raddr.Addr() != netip.IPv4Unspecified() {
|
// Auto-assign an ephemeral port for both outbound dials and for listeners
|
||||||
// Outbound (dial) connection: auto-assign ephemeral port.
|
// that did not request a fixed port.
|
||||||
laddr = netip.AddrPortFrom(laddr.Addr(), uint16(49152+s.blk.async.Prand32()%16384))
|
laddr = netip.AddrPortFrom(laddr.Addr(), uint16(49152+s.blk.async.Prand32()%16384))
|
||||||
|
}
|
||||||
|
if laddr.Addr().IsUnspecified() {
|
||||||
|
// Fill in the stack's configured address for the requested family.
|
||||||
|
if isV6 {
|
||||||
|
laddr = netip.AddrPortFrom(netip.AddrFrom16(s.blk.async.Addr6()), laddr.Port())
|
||||||
} else {
|
} else {
|
||||||
return nil, lneto.ErrZeroSource
|
laddr = netip.AddrPortFrom(netip.AddrFrom4(s.blk.async.ip4.Addr4()), laddr.Port())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if laddr.Addr() == netip.IPv4Unspecified() {
|
|
||||||
// Specify address.
|
|
||||||
laddr = netip.AddrPortFrom(netip.AddrFrom4(s.blk.async.ip4.Addr4()), laddr.Port())
|
|
||||||
} else if laddr.Addr().Is6() {
|
|
||||||
return nil, lneto.ErrUnsupported
|
|
||||||
}
|
|
||||||
switch network {
|
switch network {
|
||||||
case "udp", "udp4":
|
case "udp", "udp4", "udp6":
|
||||||
if sotype != sockDGRAM {
|
if sotype != sockDGRAM {
|
||||||
return nil, lneto.ErrUnsupported
|
return nil, lneto.ErrUnsupported
|
||||||
}
|
}
|
||||||
if !raddr.IsValid() || raddr.Addr() == netip.IPv4Unspecified() {
|
if !isDial {
|
||||||
// LISTEN UDP: no fixed remote → PacketConn.
|
// LISTEN UDP: no fixed remote → PacketConn.
|
||||||
var pc udppktconn
|
var pc udppktconn
|
||||||
err = pc.c.Configure(udp.PacketConnConfig{
|
err = pc.c.Configure(udp.PacketConnConfig{
|
||||||
@@ -109,7 +120,11 @@ func (s StackGo) SocketNetip(ctx context.Context, network string, family, sotype
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
pc.laddr = net.UDPAddr{IP: laddr.Addr().AsSlice(), Port: int(laddr.Port())}
|
pc.laddr = net.UDPAddr{IP: laddr.Addr().AsSlice(), Port: int(laddr.Port())}
|
||||||
err = s.blk.async.RegisterListenerUDP(&pc.c)
|
if isV6 {
|
||||||
|
err = s.blk.async.RegisterListenerUDP6(&pc.c)
|
||||||
|
} else {
|
||||||
|
err = s.blk.async.RegisterListenerUDP(&pc.c)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -138,12 +153,12 @@ func (s StackGo) SocketNetip(ctx context.Context, network string, family, sotype
|
|||||||
raddr: udpaddr(raddr),
|
raddr: udpaddr(raddr),
|
||||||
}
|
}
|
||||||
return uc, nil
|
return uc, nil
|
||||||
case "tcp", "tcp4":
|
case "tcp", "tcp4", "tcp6":
|
||||||
if sotype != sockSTREAM {
|
if sotype != sockSTREAM {
|
||||||
return nil, lneto.ErrUnsupported
|
return nil, lneto.ErrUnsupported
|
||||||
}
|
}
|
||||||
|
|
||||||
if raddr.IsValid() && raddr.Addr() != netip.IPv4Unspecified() {
|
if isDial {
|
||||||
var conn tcp.Conn
|
var conn tcp.Conn
|
||||||
// DIAL TCP: active connection a.k.a TCP Client branch.
|
// DIAL TCP: active connection a.k.a TCP Client branch.
|
||||||
err = conn.Configure(tcp.ConnConfig{
|
err = conn.Configure(tcp.ConnConfig{
|
||||||
@@ -171,7 +186,7 @@ func (s StackGo) SocketNetip(ctx context.Context, network string, family, sotype
|
|||||||
localAddr: net.TCPAddrFromAddrPort(laddr),
|
localAddr: net.TCPAddrFromAddrPort(laddr),
|
||||||
}
|
}
|
||||||
return tc, nil
|
return tc, nil
|
||||||
} else if state == tcp.StateSynSent || state == tcp.StateSynRcvd || conn.InternalHandler().AwaitingSynSend() {
|
} else if state == tcp.StateSynSent || state == tcp.StateSynRcvd || conn.AwaitingSynSend() {
|
||||||
if err = ctx.Err(); err != nil {
|
if err = ctx.Err(); err != nil {
|
||||||
conn.Abort()
|
conn.Abort()
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -195,7 +210,11 @@ func (s StackGo) SocketNetip(ctx context.Context, network string, family, sotype
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = s.blk.async.RegisterListenerTCP(&l.l)
|
if isV6 {
|
||||||
|
err = s.blk.async.RegisterListenerTCP6(&l.l)
|
||||||
|
} else {
|
||||||
|
err = s.blk.async.RegisterListenerTCP(&l.l)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ type Stack6 interface {
|
|||||||
Register6(node lneto.StackNode) error
|
Register6(node lneto.StackNode) error
|
||||||
DialUDP6(conn *udp.Conn, localPort uint16, raddr [16]byte, rport uint16) error
|
DialUDP6(conn *udp.Conn, localPort uint16, raddr [16]byte, rport uint16) error
|
||||||
DialTCP6(conn *tcp.Conn, localPort uint16, raddr [16]byte, rport uint16, iss tcp.Value) error
|
DialTCP6(conn *tcp.Conn, localPort uint16, raddr [16]byte, rport uint16, iss tcp.Value) error
|
||||||
|
RegisterListenerTCP6(listener *tcp.Listener) error
|
||||||
|
RegisterListenerUDP6(pktconn *udp.PacketConn) error
|
||||||
IngressIPv6(ipframe []byte) error
|
IngressIPv6(ipframe []byte) error
|
||||||
EgressIPv6(ipframe []byte) (int, error)
|
EgressIPv6(ipframe []byte) (int, error)
|
||||||
IPv6Stack() lneto.StackNode
|
IPv6Stack() lneto.StackNode
|
||||||
@@ -119,6 +121,18 @@ func (s *stack6) EnableICMP6(enabled bool) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RegisterListenerTCP6 registers a passive TCP listener on the IPv6 stack so it
|
||||||
|
// receives inbound IPv6 segments. Mirrors StackAsync.RegisterListenerTCP for IPv4.
|
||||||
|
func (s *stack6) RegisterListenerTCP6(listener *tcp.Listener) error {
|
||||||
|
return s.tcps6.RegisterMACFiltered(listener, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterListenerUDP6 registers a UDP packet connection on the IPv6 stack so it
|
||||||
|
// receives inbound IPv6 datagrams. Mirrors StackAsync.RegisterListenerUDP for IPv4.
|
||||||
|
func (s *stack6) RegisterListenerUDP6(pktconn *udp.PacketConn) error {
|
||||||
|
return s.udps6.RegisterMACFiltered(pktconn, nil)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *stack6) IngressIPv6(ipFrame []byte) error {
|
func (s *stack6) IngressIPv6(ipFrame []byte) error {
|
||||||
return s.ip6.Demux(ipFrame, 0)
|
return s.ip6.Demux(ipFrame, 0)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user