mirror of
https://github.com/soypat/lneto.git
synced 2026-08-29 19: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:
@@ -128,7 +128,7 @@ func (ls *StackEthernet) LocalPort() uint16 { return 0 }
|
||||
|
||||
func (ls *StackEthernet) Protocol() uint64 { return 1 }
|
||||
|
||||
func (ls *StackEthernet) Register(h lneto.StackNode) error {
|
||||
func (ls *StackEthernet) RegisterEthernet(h lneto.StackNode) error {
|
||||
proto := h.Protocol()
|
||||
if proto > math.MaxUint16 || proto <= 1500 {
|
||||
return lneto.ErrInvalidConfig
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
package internet
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
|
||||
"github.com/soypat/lneto"
|
||||
"github.com/soypat/lneto/ethernet"
|
||||
)
|
||||
|
||||
var _ lneto.StackNode = (*StackIP)(nil)
|
||||
|
||||
type StackIP struct {
|
||||
connID uint64
|
||||
stackip4
|
||||
stackip6
|
||||
}
|
||||
|
||||
func (stackip *StackIP) Reset(vld *lneto.Validator, maxNodes4, maxNodes6 int) error {
|
||||
if maxNodes4 <= 0 && maxNodes6 <= 0 || vld == nil {
|
||||
return lneto.ErrInvalidConfig
|
||||
}
|
||||
stackip.connID++
|
||||
stackip.reset4(vld, maxNodes4)
|
||||
stackip.reset6(vld, maxNodes6)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (stackip *StackIP) ConnectionID() *uint64 {
|
||||
return &stackip.connID
|
||||
}
|
||||
|
||||
func (stackip *StackIP) Protocol() uint64 {
|
||||
return uint64(ethernet.TypeIPv4) // Only support ipv4 for now.
|
||||
}
|
||||
|
||||
func (stackip *StackIP) LocalPort() uint16 { return 0 }
|
||||
|
||||
func (stackip *StackIP) SetLogger(logger *slog.Logger) {
|
||||
stackip.stackip4.handlers.log = logger
|
||||
stackip.stackip6.handlers.log = logger
|
||||
}
|
||||
|
||||
func (stackip *StackIP) Demux(carrierData []byte, offset int) error {
|
||||
debugLog("ip:demux")
|
||||
if len(carrierData) < 1 {
|
||||
return lneto.ErrTruncatedFrame
|
||||
}
|
||||
version := carrierData[offset] >> 4
|
||||
switch version {
|
||||
case 4:
|
||||
return stackip.stackip4.demux4(carrierData, offset)
|
||||
case 6:
|
||||
return stackip.stackip6.demux6(carrierData, offset)
|
||||
default:
|
||||
return lneto.ErrUnsupported
|
||||
}
|
||||
}
|
||||
|
||||
func (stackip *StackIP) Encapsulate(carrierData []byte, offsetToIP, offsetToFrame int) (n int, err error) {
|
||||
if offsetToFrame != offsetToIP {
|
||||
return 0, lneto.ErrBug
|
||||
}
|
||||
n, err = stackip.stackip4.encapsulate4(carrierData, offsetToIP)
|
||||
if len(stackip.stackip6.handlers.nodes) > 0 && n == 0 {
|
||||
n, err = stackip.stackip6.encapsulate6(carrierData, offsetToIP)
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"log/slog"
|
||||
|
||||
"github.com/soypat/lneto"
|
||||
"github.com/soypat/lneto/ethernet"
|
||||
"github.com/soypat/lneto/internal"
|
||||
"github.com/soypat/lneto/ipv4"
|
||||
"github.com/soypat/lneto/tcp"
|
||||
@@ -15,6 +16,42 @@ import (
|
||||
// It is meant to be embedded within StackNodes.
|
||||
// var _ lneto.StackNode = (*stackip4)(nil)
|
||||
|
||||
type StackIPv4 struct {
|
||||
connID uint64
|
||||
stackip4
|
||||
}
|
||||
|
||||
func (stackip4 *StackIPv4) Reset(vld *lneto.Validator, maxNodes int) error {
|
||||
stackip4.reset4(vld, maxNodes)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (stackip *StackIPv4) ConnectionID() *uint64 {
|
||||
return &stackip.connID
|
||||
}
|
||||
|
||||
func (stackip *StackIPv4) Protocol() uint64 {
|
||||
return uint64(ethernet.TypeIPv4)
|
||||
}
|
||||
|
||||
func (stackip *StackIPv4) LocalPort() uint16 { return 0 }
|
||||
|
||||
func (stackip *StackIPv4) SetLogger(logger *slog.Logger) {
|
||||
stackip.stackip4.handlers.log = logger
|
||||
}
|
||||
|
||||
func (stackip *StackIPv4) Demux(carrierData []byte, offset int) error {
|
||||
debugLog("ip:demux")
|
||||
return stackip.stackip4.demux4(carrierData, offset)
|
||||
}
|
||||
|
||||
func (stackip *StackIPv4) Encapsulate(carrierData []byte, offsetToIP, offsetToFrame int) (n int, err error) {
|
||||
if offsetToFrame != offsetToIP {
|
||||
return 0, lneto.ErrBug
|
||||
}
|
||||
return stackip.stackip4.encapsulate4(carrierData, offsetToIP)
|
||||
}
|
||||
|
||||
type stackip4 struct {
|
||||
handlers handlers
|
||||
vld *lneto.Validator
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"log/slog"
|
||||
|
||||
"github.com/soypat/lneto"
|
||||
"github.com/soypat/lneto/ethernet"
|
||||
"github.com/soypat/lneto/ipv6"
|
||||
"github.com/soypat/lneto/tcp"
|
||||
"github.com/soypat/lneto/udp"
|
||||
@@ -12,6 +13,41 @@ import (
|
||||
// stackip6 is NOT a StackNode implementation.
|
||||
// It is meant to be embedded within StackNodes.
|
||||
// var _ lneto.StackNode = (*stackip6)(nil)
|
||||
type StackIPv6 struct {
|
||||
connID uint64
|
||||
stackip6
|
||||
}
|
||||
|
||||
func (stackip4 *StackIPv6) Reset(vld *lneto.Validator, maxNodes int) error {
|
||||
stackip4.reset6(vld, maxNodes)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (stackip *StackIPv6) ConnectionID() *uint64 {
|
||||
return &stackip.connID
|
||||
}
|
||||
|
||||
func (stackip *StackIPv6) Protocol() uint64 {
|
||||
return uint64(ethernet.TypeIPv6)
|
||||
}
|
||||
|
||||
func (stackip *StackIPv6) LocalPort() uint16 { return 0 }
|
||||
|
||||
func (stackip *StackIPv6) SetLogger(logger *slog.Logger) {
|
||||
stackip.stackip6.handlers.log = logger
|
||||
}
|
||||
|
||||
func (stackip *StackIPv6) Demux(carrierData []byte, offset int) error {
|
||||
debugLog("ip:demux")
|
||||
return stackip.stackip6.demux6(carrierData, offset)
|
||||
}
|
||||
|
||||
func (stackip *StackIPv6) Encapsulate(carrierData []byte, offsetToIP, offsetToFrame int) (n int, err error) {
|
||||
if offsetToFrame != offsetToIP {
|
||||
return 0, lneto.ErrBug
|
||||
}
|
||||
return stackip.stackip6.encapsulate6(carrierData, offsetToIP)
|
||||
}
|
||||
|
||||
type stackip6 struct {
|
||||
handlers handlers
|
||||
|
||||
+11
-9
@@ -33,8 +33,6 @@ func (ps *StackPorts) ResetTCP(maxNodes uint16) error {
|
||||
func (ps *StackPorts) Reset(protocol uint64, dstPortOffset, maxNodes uint16) error {
|
||||
if protocol > math.MaxUint16 {
|
||||
return lneto.ErrInvalidConfig
|
||||
} else if maxNodes <= 0 {
|
||||
return lneto.ErrInvalidConfig
|
||||
}
|
||||
ps.handlers.reset("StackPorts(proto="+strconv.Itoa(int(protocol))+")", int(maxNodes))
|
||||
*ps = StackPorts{
|
||||
@@ -105,25 +103,29 @@ type StackPortsMACFiltered struct {
|
||||
sp StackPorts
|
||||
}
|
||||
|
||||
func (mfsp *StackPortsMACFiltered) Register(h lneto.StackNode, addr []byte) error {
|
||||
func (mfsp *StackPortsMACFiltered) RegisterMACFiltered(h lneto.StackNode, macAddr []byte) error {
|
||||
// TODO(soypat): We can likely constrain memory and the slice lifetime if StackPortsMACFiltered owns it
|
||||
// or better yet, if the handlers node slice owns the memory. We need to think carefully of who has write access (the ARP and NDP handlers)
|
||||
// and make sure that they never write after the connection has been terminated. Idea:
|
||||
// RegisterMACFiltered(h lneto.StackNode, filterMAC bool) (macAddr *[6]byte, connIDthing *uint8, err error)
|
||||
port := h.LocalPort()
|
||||
proto := h.Protocol()
|
||||
if port <= 0 {
|
||||
return lneto.ErrZeroSource
|
||||
} else if proto != uint64(mfsp.sp.protocol) {
|
||||
return lneto.ErrInvalidConfig
|
||||
} else if addr != nil && len(addr) != 6 {
|
||||
} else if macAddr != nil && len(macAddr) != 6 {
|
||||
return lneto.ErrInvalidAddr
|
||||
}
|
||||
return mfsp.sp.handlers.registerByPortProto(nodeFromStackNode(h, port, proto, addr))
|
||||
return mfsp.sp.handlers.registerByPortProto(nodeFromStackNode(h, port, proto, macAddr))
|
||||
}
|
||||
|
||||
func (ps *StackPortsMACFiltered) ResetUDP(maxNodes uint16) error {
|
||||
return ps.sp.ResetUDP(maxNodes)
|
||||
func (ps *StackPortsMACFiltered) ResetUDP(maxNodes uint16) {
|
||||
ps.sp.ResetUDP(maxNodes) // Can't error.
|
||||
}
|
||||
|
||||
func (ps *StackPortsMACFiltered) ResetTCP(maxNodes uint16) error {
|
||||
return ps.sp.ResetTCP(maxNodes)
|
||||
func (ps *StackPortsMACFiltered) ResetTCP(maxNodes uint16) {
|
||||
ps.sp.ResetTCP(maxNodes) // Can't error.
|
||||
}
|
||||
|
||||
func (ps *StackPortsMACFiltered) Reset(protocol uint64, dstPortOffset, maxNodes uint16) error {
|
||||
|
||||
+13
-13
@@ -11,7 +11,7 @@ import (
|
||||
|
||||
func TestBasicStack(t *testing.T) {
|
||||
rng := rand.New(rand.NewSource(1))
|
||||
var sbCl, sbSv StackIP
|
||||
var sbCl, sbSv StackIPv4
|
||||
var connCl, connSv tcp.Conn
|
||||
setupClientServer(t, rng, &sbCl, &sbSv, &connCl, &connSv)
|
||||
var buf [2048]byte
|
||||
@@ -37,13 +37,13 @@ func TestBasicStack(t *testing.T) {
|
||||
|
||||
func TestBasicStack2(t *testing.T) {
|
||||
rng := rand.New(rand.NewSource(1))
|
||||
var sbCl, sbSv StackIP
|
||||
var sbCl, sbSv StackIPv4
|
||||
var connCl, connSv tcp.Conn
|
||||
setupClientServerEstablished(t, rng, &sbCl, &sbSv, &connCl, &connSv)
|
||||
|
||||
}
|
||||
|
||||
func expectExchange(t *testing.T, from, to *StackIP, buf []byte) {
|
||||
func expectExchange(t *testing.T, from, to lneto.StackNode, buf []byte) {
|
||||
t.Helper()
|
||||
n, err := from.Encapsulate(buf, 0, 0)
|
||||
if err != nil {
|
||||
@@ -58,13 +58,13 @@ func expectExchange(t *testing.T, from, to *StackIP, buf []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
func setupClientServerEstablished(t *testing.T, rng *rand.Rand, client, server *StackIP, connClient, connServer *tcp.Conn) {
|
||||
func setupClientServerEstablished(t *testing.T, rng *rand.Rand, client, server *StackIPv4, connClient, connServer *tcp.Conn) {
|
||||
t.Helper()
|
||||
setupClientServer(t, rng, client, server, connClient, connServer)
|
||||
testClientServerEstablish(t, client, server, connClient, connServer)
|
||||
}
|
||||
|
||||
func testClientServerEstablish(t *testing.T, client, server *StackIP, connClient, connServer *tcp.Conn) {
|
||||
func testClientServerEstablish(t *testing.T, client, server lneto.StackNode, connClient, connServer *tcp.Conn) {
|
||||
t.Helper()
|
||||
var buf [2048]byte
|
||||
nextToSend := client
|
||||
@@ -93,7 +93,7 @@ func testClientServerEstablish(t *testing.T, client, server *StackIP, connClient
|
||||
|
||||
func TestBasicStack6(t *testing.T) {
|
||||
rng := rand.New(rand.NewSource(1))
|
||||
var sbCl, sbSv StackIP
|
||||
var sbCl, sbSv StackIPv6
|
||||
var connCl, connSv tcp.Conn
|
||||
setupClientServer6(t, rng, &sbCl, &sbSv, &connCl, &connSv)
|
||||
var buf [2048]byte
|
||||
@@ -119,13 +119,13 @@ func TestBasicStack6(t *testing.T) {
|
||||
|
||||
func TestBasicStack6Established(t *testing.T) {
|
||||
rng := rand.New(rand.NewSource(1))
|
||||
var sbCl, sbSv StackIP
|
||||
var sbCl, sbSv StackIPv6
|
||||
var connCl, connSv tcp.Conn
|
||||
setupClientServer6(t, rng, &sbCl, &sbSv, &connCl, &connSv)
|
||||
testClientServerEstablish(t, &sbCl, &sbSv, &connCl, &connSv)
|
||||
}
|
||||
|
||||
func setupClientServer6(t *testing.T, rng *rand.Rand, client, server *StackIP, connClient, connServer *tcp.Conn) {
|
||||
func setupClientServer6(t *testing.T, rng *rand.Rand, client, server *StackIPv6, connClient, connServer *tcp.Conn) {
|
||||
t.Helper()
|
||||
_ = rng
|
||||
const maxNodes = 1
|
||||
@@ -134,10 +134,10 @@ func setupClientServer6(t *testing.T, rng *rand.Rand, client, server *StackIP, c
|
||||
clip6 := netip.AddrFrom16([16]byte{0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}) // 2001:db8::2
|
||||
svip := netip.AddrPortFrom(svip6, 80)
|
||||
clip := netip.AddrPortFrom(clip6, 1337)
|
||||
if err := server.Reset(new(lneto.Validator), 0, maxNodes); err != nil {
|
||||
if err := server.Reset(new(lneto.Validator), maxNodes); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := client.Reset(new(lneto.Validator), 0, maxNodes); err != nil {
|
||||
if err := client.Reset(new(lneto.Validator), maxNodes); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
server.SetAddr6(svip6.As16())
|
||||
@@ -172,14 +172,14 @@ func setupClientServer6(t *testing.T, rng *rand.Rand, client, server *StackIP, c
|
||||
}
|
||||
}
|
||||
|
||||
func setupClientServer(t *testing.T, rng *rand.Rand, client, server *StackIP, connClient, connServer *tcp.Conn) {
|
||||
func setupClientServer(t *testing.T, rng *rand.Rand, client, server *StackIPv4, connClient, connServer *tcp.Conn) {
|
||||
const maxNodes = 1
|
||||
bufsize := 2048
|
||||
// Ensure buffer sizes are OK with reused buffers.
|
||||
svip := netip.AddrPortFrom(netip.AddrFrom4([4]byte{192, 168, 1, 0}), 80)
|
||||
clip := netip.AddrPortFrom(netip.AddrFrom4([4]byte{192, 168, 1, 1}), 1337)
|
||||
server.Reset(new(lneto.Validator), maxNodes, 0)
|
||||
client.Reset(new(lneto.Validator), maxNodes, 0)
|
||||
server.Reset(new(lneto.Validator), maxNodes)
|
||||
client.Reset(new(lneto.Validator), maxNodes)
|
||||
server.SetAddr4(svip.Addr().As4())
|
||||
client.SetAddr4(clip.Addr().As4())
|
||||
err := connServer.Configure(tcp.ConnConfig{
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
func TestListener_SingleConnection(t *testing.T) {
|
||||
rng := rand.New(rand.NewSource(1))
|
||||
var clientStack, serverStack StackIP
|
||||
var clientStack, serverStack StackIPv4
|
||||
var clientConn, serverConn tcp.Conn
|
||||
var listener tcp.Listener
|
||||
|
||||
@@ -66,7 +66,7 @@ func TestListener_SingleConnection(t *testing.T) {
|
||||
|
||||
func TestListener_AcceptAfterEstablished(t *testing.T) {
|
||||
rng := rand.New(rand.NewSource(1))
|
||||
var client1Stack, serverStack StackIP
|
||||
var client1Stack, serverStack StackIPv4
|
||||
var client1Conn, serverConn tcp.Conn
|
||||
var listener tcp.Listener
|
||||
pool := newMockTCPPool(2, 3, 2048)
|
||||
@@ -104,7 +104,7 @@ func TestListener_AcceptAfterEstablished(t *testing.T) {
|
||||
}
|
||||
|
||||
// Setup second client and verify we can still accept.
|
||||
var client2Stack StackIP
|
||||
var client2Stack StackIPv4
|
||||
var client2Conn tcp.Conn
|
||||
setupClient(t, &client2Stack, &client2Conn, netip.AddrFrom4(serverStack.Addr4()), serverPort, 1338)
|
||||
|
||||
@@ -131,13 +131,13 @@ func TestListener_AcceptAfterEstablished(t *testing.T) {
|
||||
func TestListener_MultiConn(t *testing.T) {
|
||||
const numClients = 5
|
||||
rng := rand.New(rand.NewSource(1))
|
||||
var serverStack StackIP
|
||||
var serverStack StackIPv4
|
||||
var serverConn tcp.Conn
|
||||
var listener tcp.Listener
|
||||
pool := newMockTCPPool(numClients, 3, 2048)
|
||||
|
||||
// Create slices for clients.
|
||||
clientStacks := make([]StackIP, numClients)
|
||||
clientStacks := make([]StackIPv4, numClients)
|
||||
clientConns := make([]tcp.Conn, numClients)
|
||||
acceptedConns := make([]*tcp.Conn, numClients)
|
||||
|
||||
@@ -257,7 +257,7 @@ func TestListener_MultiConn(t *testing.T) {
|
||||
|
||||
// Close connections, alternating between client-initiated and server-initiated.
|
||||
for i := range numClients {
|
||||
var closer, responder *StackIP
|
||||
var closer, responder *StackIPv4
|
||||
var closerConn, responderConn *tcp.Conn
|
||||
var serverClosed bool
|
||||
whoCloses := "client"
|
||||
@@ -313,7 +313,7 @@ func TestListener_MultiConn(t *testing.T) {
|
||||
|
||||
func TestListener_RSTOnPoolExhaustion(t *testing.T) {
|
||||
rng := rand.New(rand.NewSource(1))
|
||||
var client1Stack, client2Stack, serverStack StackIP
|
||||
var client1Stack, client2Stack, serverStack StackIPv4
|
||||
var client1Conn, client2Conn, serverConn tcp.Conn
|
||||
var listener tcp.Listener
|
||||
|
||||
@@ -606,21 +606,11 @@ func TestStackPorts_ECN_SYN_RST(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// tryExchange attempts an exchange but doesn't fail if no data to send.
|
||||
func tryExchange(t *testing.T, from, to *StackIP, buf []byte) {
|
||||
t.Helper()
|
||||
n, err := from.Encapsulate(buf, -1, 0)
|
||||
if err != nil || n == 0 {
|
||||
return // No data to send.
|
||||
}
|
||||
_ = to.Demux(buf[:n], 0) // Ignore errors during close.
|
||||
}
|
||||
|
||||
func setupClient(t *testing.T, client *StackIP, conn *tcp.Conn, serverAddr netip.Addr, serverPort, clientPort uint16) {
|
||||
func setupClient(t *testing.T, client *StackIPv4, conn *tcp.Conn, serverAddr netip.Addr, serverPort, clientPort uint16) {
|
||||
t.Helper()
|
||||
bufsize := 2048
|
||||
clientIP := netip.AddrFrom4([4]byte{192, 168, 1, byte(clientPort % 256)})
|
||||
client.Reset(new(lneto.Validator), 1, 0)
|
||||
client.Reset(new(lneto.Validator), 1)
|
||||
client.SetAddr4(clientIP.As4())
|
||||
err := conn.Configure(tcp.ConnConfig{
|
||||
RxBuf: make([]byte, bufsize),
|
||||
|
||||
Reference in New Issue
Block a user