From bdbd38ab4406eb93aeee3e513dddb4bc408fc67c Mon Sep 17 00:00:00 2001 From: Pat Whittingslow Date: Sat, 9 May 2026 16:11:31 -0300 Subject: [PATCH] ipv6: StackIP and StackAsync.Addr refactor (#105) * apply StackIP changes and internet package test passing * fix tests and examples * remove old Reset method on StackIP * use encapsulate for ipv6 * add TCP over IPv6 tests --- examples/berkeley-listener/berkeley_server.go | 18 +- examples/httpserver/main.go | 8 +- examples/min-working-example/main-mwe.go | 2 +- examples/xcurl/main.go | 6 +- internet/definitions.go | 29 ++ internet/stack-ip.go | 249 +++--------------- internet/stack-ip4.go | 183 +++++++++++++ internet/stack-ip6.go | 144 ++++++++++ internet/stackbasic_test.go | 95 ++++++- internet/tcplistener_test.go | 24 +- x/xnet/stack-async.go | 71 +++-- x/xnet/stack-go.go | 2 +- x/xnet/xnet_arp_test.go | 8 +- x/xnet/xnet_bench_test.go | 10 +- x/xnet/xnet_concurrent_test.go | 10 +- x/xnet/xnet_dns_test.go | 2 +- x/xnet/xnet_fuzz_test.go | 30 +-- x/xnet/xnet_icmp_test.go | 11 +- x/xnet/xnet_listener_test.go | 14 +- x/xnet/xnet_mdns_test.go | 10 +- x/xnet/xnet_subnettable_test.go | 6 +- x/xnet/xnet_test.go | 34 +-- 22 files changed, 609 insertions(+), 357 deletions(-) create mode 100644 internet/stack-ip4.go create mode 100644 internet/stack-ip6.go diff --git a/examples/berkeley-listener/berkeley_server.go b/examples/berkeley-listener/berkeley_server.go index a6ef591..194f340 100644 --- a/examples/berkeley-listener/berkeley_server.go +++ b/examples/berkeley-listener/berkeley_server.go @@ -27,6 +27,7 @@ import ( "github.com/soypat/lneto/internal" "github.com/soypat/lneto/internal/ltesto" "github.com/soypat/lneto/internet/pcap" + "github.com/soypat/lneto/ipv4" "github.com/soypat/lneto/tcp" "github.com/soypat/lneto/x/xnet" ) @@ -134,7 +135,8 @@ func run() error { pfbuf = fmt.Appendf(pfbuf[:0], "%-3s %3d", context, len(pkt)) pfbuf = append(pfbuf, ' ', '[') pfbuf, err = pf.FormatFrames(pfbuf, frames, pkt) - pfbuf = bytes.ReplaceAll(pfbuf, stack.Addr().AppendTo(nil), []byte("us")) + addr := stack.Addr4() + pfbuf = bytes.ReplaceAll(pfbuf, ipv4.AppendFormatAddr(nil, addr), []byte("us")) pfbuf = bytes.ReplaceAll(pfbuf, ethernet.AppendAddr(nil, stack.HardwareAddress()), []byte("us")) pfbuf = append(pfbuf, ']', '\n') if err != nil { @@ -158,11 +160,11 @@ func run() error { } else if n != nwrite { log.Fatalf("mismatch written bytes %d!=%d", nwrite, n) } - if flagMockClient && mockStack.Addr().IsValid() { + if flagMockClient && mockStack.Addr4() != ([4]byte{}) { mockStack.IngressEthernet(buf[:nwrite]) } } - if flagMockClient && mockStack.Addr().IsValid() { + if flagMockClient && mockStack.Addr4() != ([4]byte{}) { n, _ := mockStack.EgressEthernet(buf[:]) if n > 0 { stack.IngressEthernet(buf[:n]) @@ -222,7 +224,7 @@ func run() error { if err = stack.AssimilateDHCPResults(results); err != nil { return fmt.Errorf("assimilating DHCP results: %w", err) } - slog.Info("dhcp-complete", slog.String("assignedIP", results.AssignedAddr.String()), slog.String("routerIP", results.Router.String())) + slog.Info("dhcp-complete", slog.String("assignedIP", string(ipv4.AppendFormatAddr(nil, results.AssignedAddr4))), slog.String("routerIP", results.Router.String())) // Resolve router HW and set gateway routerHw, err := rstack.DoResolveHardwareAddress6(results.Router, 2*time.Second, 2) @@ -246,7 +248,7 @@ func run() error { } defer ln.Close() - fmt.Printf("Listening (Berkeley) on %s:%d\n", stack.Addr().String(), flagPort) + fmt.Printf("Listening (Berkeley) on %s:%d\n", string(ipv4.AppendFormatAddr(nil, stack.Addr4())), flagPort) // Optionally run an in-memory mock client that dials the berkeley listener if flagMockClient { @@ -326,9 +328,9 @@ func tryPoll(iface ltesto.Interface, poll time.Duration) (dataMayBeReady bool, _ } func mockClient(stack *xnet.StackAsync, port uint16, subnet netip.Prefix) { - target := netip.AddrPortFrom(stack.Addr(), port) + target := netip.AddrPortFrom(netip.AddrFrom4(stack.Addr4()), port) err := mockStack.Reset(xnet.StackConfig{ - StaticAddress: subnet.Addr().Next(), + StaticAddress4: subnet.Addr().Next().As4(), MaxActiveTCPPorts: 1, HardwareAddress: stack.Gateway6(), Hostname: "the-other", @@ -368,7 +370,7 @@ func mockClient(stack *xnet.StackAsync, port uint16, subnet netip.Prefix) { hdr.SetMethod("GET") hdr.SetRequestURI("/") hdr.SetProtocol("HTTP/1.1") - hdr.Set("Host", stack.Addr().String()) + hdr.Set("Host", string(ipv4.AppendFormatAddr(nil, stack.Addr4()))) hdr.Set("User-Agent", "lneto-mock") hdr.Set("Connection", "close") req, err := hdr.AppendRequest(nil) diff --git a/examples/httpserver/main.go b/examples/httpserver/main.go index f889cc4..c5e0c40 100644 --- a/examples/httpserver/main.go +++ b/examples/httpserver/main.go @@ -26,6 +26,7 @@ import ( "github.com/soypat/lneto/internal" "github.com/soypat/lneto/internal/ltesto" "github.com/soypat/lneto/internet/pcap" + "github.com/soypat/lneto/ipv4" "github.com/soypat/lneto/tcp" "github.com/soypat/lneto/x/xnet" ) @@ -134,7 +135,8 @@ func run() (err error) { pfbuf = fmt.Appendf(pfbuf[:0], "%-3s %3d", context, len(pkt)) pfbuf = append(pfbuf, ' ', '[') pfbuf, err = pf.FormatFrames(pfbuf, frames, pkt) - pfbuf = bytes.ReplaceAll(pfbuf, stack.Addr().AppendTo(nil), []byte("us")) + + pfbuf = bytes.ReplaceAll(pfbuf, ipv4.AppendFormatAddr(nil, stack.Addr4()), []byte("us")) pfbuf = bytes.ReplaceAll(pfbuf, ethernet.AppendAddr(nil, stack.HardwareAddress()), []byte("us")) pfbuf = append(pfbuf, ']', '\n') if err != nil { @@ -206,7 +208,7 @@ func run() (err error) { if err != nil { return fmt.Errorf("assimilating DHCP results: %w", err) } - slog.Info("dhcp-complete", slog.String("assignedIP", results.AssignedAddr.String()), slog.String("routerIP", results.Router.String())) + slog.Info("dhcp-complete", slog.String("assignedIP", string(ipv4.AppendFormatAddr(nil, results.AssignedAddr4))), slog.String("routerIP", results.Router.String())) const ( arpTimeout = 2 * time.Second @@ -221,7 +223,7 @@ func run() (err error) { stack.SetGateway6(routerHw) svPort := uint16(flagPort) - fmt.Printf("Listening on %s:%d\n", stack.Addr().String(), svPort) + fmt.Printf("Listening on %s:%d\n", ipv4.AppendFormatAddr(nil, stack.Addr4()), svPort) // Serve connections in a loop. for { diff --git a/examples/min-working-example/main-mwe.go b/examples/min-working-example/main-mwe.go index 135f377..9229b7d 100644 --- a/examples/min-working-example/main-mwe.go +++ b/examples/min-working-example/main-mwe.go @@ -105,7 +105,7 @@ func run(ctx context.Context, stack *xnet.StackAsync) error { }, }) - laddr := net.TCPAddrFromAddrPort(netip.AddrPortFrom(results.AssignedAddr, 80)) + laddr := net.TCPAddrFromAddrPort(netip.AddrPortFrom(netip.AddrFrom4(results.AssignedAddr4), 80)) // raddr := net.TCPAddr{} // If active (client) connection then set raddr in which case a net.Conn type is returned. const sockstream = 0x1 c, err := berkstack.Socket(ctx, "tcp", syscall.AF_INET, sockstream, laddr, nil) diff --git a/examples/xcurl/main.go b/examples/xcurl/main.go index df0a87b..a7594d3 100644 --- a/examples/xcurl/main.go +++ b/examples/xcurl/main.go @@ -26,6 +26,7 @@ import ( "github.com/soypat/lneto/internal" "github.com/soypat/lneto/internal/ltesto" "github.com/soypat/lneto/internet/pcap" + "github.com/soypat/lneto/ipv4" "github.com/soypat/lneto/tcp" "github.com/soypat/lneto/x/xnet" ) @@ -156,7 +157,8 @@ func run() (err error) { pfbuf = fmt.Appendf(pfbuf[:0], "%-3s %3d", context, len(pkt)) pfbuf = append(pfbuf, ' ', '[') pfbuf, err = pf.FormatFrames(pfbuf, frames, pkt) - pfbuf = bytes.ReplaceAll(pfbuf, stack.Addr().AppendTo(nil), []byte("us")) + addr := stack.Addr4() + pfbuf = bytes.ReplaceAll(pfbuf, addr[:], []byte("us")) pfbuf = bytes.ReplaceAll(pfbuf, ethernet.AppendAddr(nil, stack.HardwareAddress()), []byte("us")) pfbuf = append(pfbuf, ']', '\n') if err != nil { @@ -231,7 +233,7 @@ func run() (err error) { if err != nil { return fmt.Errorf("assimilating DHCP results: %w", err) } - slog.Info("dhcp-complete", slog.String("assignedIP", results.AssignedAddr.String()), slog.String("routerIP", results.Router.String()), slog.Any("DNS", results.DNSServers), slog.Any("subnet", results.Subnet.String())) + slog.Info("dhcp-complete", slog.String("assignedIP", string(ipv4.AppendFormatAddr(nil, results.AssignedAddr4))), slog.String("routerIP", results.Router.String()), slog.Any("DNS", results.DNSServers), slog.Any("subnet", results.Subnet.String())) const ( arpTimeout = 2 * time.Second arpRetries = 2 diff --git a/internet/definitions.go b/internet/definitions.go index 585ba1d..ac71964 100644 --- a/internet/definitions.go +++ b/internet/definitions.go @@ -7,6 +7,7 @@ import ( "slices" "github.com/soypat/lneto" + "github.com/soypat/lneto/internal" ) // node is a concrete StackNode as stored in Stacks. Methods are devirtualized for performance benefits, especially on TinyGo. @@ -231,3 +232,31 @@ func incLim(v, max int) int { } return v } + +type logger struct { + log *slog.Logger +} + +func (l logger) error(msg string, attrs ...slog.Attr) { + internal.LogAttrs(l.log, slog.LevelError, msg, attrs...) +} +func (l logger) info(msg string, attrs ...slog.Attr) { + internal.LogAttrs(l.log, slog.LevelInfo, msg, attrs...) +} +func (l logger) warn(msg string, attrs ...slog.Attr) { + internal.LogAttrs(l.log, slog.LevelWarn, msg, attrs...) +} +func (l logger) debug(msg string, attrs ...slog.Attr) { + internal.LogAttrs(l.log, slog.LevelDebug, msg, attrs...) +} +func (l logger) trace(msg string, attrs ...slog.Attr) { + internal.LogAttrs(l.log, internal.LevelTrace, msg, attrs...) +} + +const enableAllocLog = internal.HeapAllocDebugging + +func debugLog(msg string) { + if enableAllocLog { + internal.LogAllocs(msg) + } +} diff --git a/internet/stack-ip.go b/internet/stack-ip.go index ea59350..18bf2a2 100644 --- a/internet/stack-ip.go +++ b/internet/stack-ip.go @@ -1,251 +1,68 @@ package internet import ( - "io" "log/slog" - "net/netip" "github.com/soypat/lneto" "github.com/soypat/lneto/ethernet" - "github.com/soypat/lneto/internal" - "github.com/soypat/lneto/ipv4" - "github.com/soypat/lneto/tcp" - "github.com/soypat/lneto/udp" ) var _ lneto.StackNode = (*StackIP)(nil) type StackIP struct { - connID uint64 - ipID uint16 - ip [4]byte - acceptMulticast bool - validator lneto.Validator - handlers handlers + connID uint64 + stackip4 + stackip6 } -func (sb *StackIP) Reset(addr netip.Addr, maxNodes int) error { - if maxNodes <= 0 { +func (stackip *StackIP) Reset(vld *lneto.Validator, maxNodes4, maxNodes6 int) error { + if maxNodes4 <= 0 && maxNodes6 <= 0 || vld == nil { return lneto.ErrInvalidConfig } - err := sb.SetAddr(addr) - if err != nil { - return err - } - sb.handlers.reset("StackIP", maxNodes) - *sb = StackIP{ - connID: sb.connID + 1, - validator: sb.validator, - handlers: sb.handlers, - ip: sb.ip, - acceptMulticast: sb.acceptMulticast, - } + stackip.connID++ + stackip.reset4(vld, maxNodes4) + stackip.reset6(vld, maxNodes6) return nil } -func (sb *StackIP) SetAddr(addr netip.Addr) error { - if !addr.IsValid() { - return lneto.ErrInvalidAddr - } else if !addr.Is4() { - return lneto.ErrUnsupported - } - sb.ip = addr.As4() - return nil +func (stackip *StackIP) ConnectionID() *uint64 { + return &stackip.connID } -func (sb *StackIP) ConnectionID() *uint64 { - return &sb.connID -} - -func (sb *StackIP) Protocol() uint64 { +func (stackip *StackIP) Protocol() uint64 { return uint64(ethernet.TypeIPv4) // Only support ipv4 for now. } -func (sb *StackIP) LocalPort() uint16 { return 0 } +func (stackip *StackIP) LocalPort() uint16 { return 0 } -func (sb *StackIP) Addr() netip.Addr { - return netip.AddrFrom4(sb.ip) +func (stackip *StackIP) SetLogger(logger *slog.Logger) { + stackip.stackip4.handlers.log = logger + stackip.stackip6.handlers.log = logger } -func (sb *StackIP) SetAcceptMulticast(accept bool) { - sb.acceptMulticast = accept -} - -func (sb *StackIP) SetLogger(logger *slog.Logger) { - sb.handlers.log = logger -} - -func (sb *StackIP) Demux(carrierData []byte, offset int) error { +func (stackip *StackIP) Demux(carrierData []byte, offset int) error { debugLog("ip:demux") - sb.handlers.info("StackIP.Demux:start") - frame := carrierData[offset:] // we don't care about carrier data in IP. - ifrm, err := ipv4.NewFrame(frame) - if err != nil { - return err + if len(carrierData) < 1 { + return lneto.ErrTruncatedFrame } - dst := ifrm.DestinationAddr() - if sb.ip != ([4]byte{}) && *dst != sb.ip { - if !sb.acceptMulticast || dst[0]&0xF0 != 0xE0 { - sb.handlers.debug("ip:not-for-us") - return lneto.ErrPacketDrop // Not meant for us. - } + 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 } - - sb.validator.ResetErr() - ifrm.ValidateExceptCRC(&sb.validator) - if err = sb.validator.ErrPop(); err != nil { - sb.handlers.error("ip:Demux.validate") - return err - } - - if ifrm.CalculateHeaderCRC() != 0 { - sb.handlers.error("ip:demux.crc") - return lneto.ErrBadCRC - } - off := ifrm.HeaderLength() - totalLen := ifrm.TotalLength() - proto := ifrm.Protocol() - node := sb.handlers.nodeByProto(uint16(proto)) - // nodeIdx := getNodeByProto(sb.handlers, uint16(proto)) - if node == nil { - // Drop packet. - sb.handlers.info("ip:demux.drop", internal.SlogAddr4("dstaddr", ifrm.DestinationAddr()), slog.String("proto", ifrm.Protocol().String())) - return lneto.ErrPacketDrop - } - // Incoming CRC Validation of common IP Protocols. - var crc lneto.CRC791 - switch proto { - case lneto.IPProtoTCP: - ifrm.CRCWriteTCPPseudo(&crc) - if crc.PayloadSum16(ifrm.Payload()) != 0 { - sb.handlers.error("ip:demux.tcpcrc") - return lneto.ErrBadCRC - } - case lneto.IPProtoUDP: - ufrm, err := udp.NewFrame(ifrm.Payload()) - if err != nil { - return err - } - ufrm.ValidateSize(&sb.validator) - if err = sb.validator.ErrPop(); err != nil { - sb.handlers.error("ip:demux.udpvalidatesize") - return err - } - frameLen := ufrm.Length() - ifrm.CRCWriteUDPPseudo(&crc, frameLen) - if crc.PayloadSum16(ufrm.RawData()[:frameLen]) != 0 { - sb.handlers.error("ip:demux.udpcrc") - return lneto.ErrBadCRC - } - } - sb.handlers.info("ipDemux", slog.String("ipproto", proto.String()), slog.Int("plen", int(totalLen))) - err = node.callbacks.Demux(frame[:totalLen], off) - if sb.handlers.tryHandleError(node, err) { - sb.handlers.info("ipclose", slog.String("proto", proto.String())) - err = nil - } - return err } -func (sb *StackIP) Encapsulate(carrierData []byte, offsetToIP, offsetToFrame int) (int, error) { - frame := carrierData[offsetToFrame:] - if len(frame) < ipv4.MinimumMTU { - return 0, io.ErrShortBuffer +func (stackip *StackIP) Encapsulate(carrierData []byte, offsetToIP, offsetToFrame int) (n int, err error) { + if offsetToFrame != offsetToIP { + return 0, lneto.ErrBug } - ifrm, _ := ipv4.NewFrame(frame) - const ihl = 5 - const headerlen = ihl * 4 - const dontFrag = 0x4000 - ifrm.SetVersionAndIHL(4, ihl) - ifrm.SetToS(0) - seed := sb.ipID + uint16(sb.connID) - id := internal.Prand16(seed) - ifrm.SetID(id) - ifrm.SetFlags(dontFrag) - ifrm.SetTTL(64) - *ifrm.SourceAddr() = sb.ip - sb.ipID = id - // Children (TCP/UDP) start at offset headerlen (20 bytes after IP header start). - // offsetToIP is 0 relative to this slice (frame), children's frame starts at headerlen. - node, n, err := sb.handlers.encapsulateAny(carrierData, offsetToFrame, offsetToFrame+headerlen) - if n == 0 { - return n, err - } - proto := lneto.IPProto(node.proto) - totalLen := n + headerlen - ifrm.SetTotalLength(uint16(totalLen)) - ifrm.SetProtocol(proto) - // Zero the CRC field so its value does not add to the final result. - ifrm.SetCRC(0) - crcValue := ifrm.CalculateHeaderCRC() - ifrm.SetCRC(crcValue) - // Calculate CRC for our newly generated packet. - var crc lneto.CRC791 - payload := ifrm.Payload() - switch proto { - case lneto.IPProtoTCP: - ifrm.CRCWriteTCPPseudo(&crc) - tfrm, _ := tcp.NewFrame(payload) - // Zero the CRC field so its value does not add to the final result. - tfrm.SetCRC(0) - crcValue = crc.PayloadSum16(payload) - tfrm.SetCRC(crcValue) - case lneto.IPProtoUDP: - ufrm, _ := udp.NewFrame(payload) - ifrm.CRCWriteUDPPseudo(&crc, uint16(n)) - ufrm.SetLength(uint16(n)) - // Zero the CRC field so its value does not add to the final result. - ufrm.SetCRC(0) - crcValue = lneto.NeverZeroSum(crc.PayloadSum16(payload)) - ufrm.SetCRC(crcValue) - } - return totalLen, err -} - -func (sb *StackIP) Register(h lneto.StackNode) error { - proto := h.Protocol() - if proto > 255 { - return lneto.ErrInvalidConfig - } - return sb.handlers.registerByPortProto(nodeFromStackNode(h, h.LocalPort(), proto, nil)) -} - -func (sb *StackIP) IsRegistered(proto lneto.IPProto) bool { - return sb.handlers.nodeByProto(uint16(proto)) != nil -} - -func (sb *StackIP) recvicmp(icmpData []byte) error { - var crc lneto.CRC791 - if crc.PayloadSum16(icmpData) != 0 { - return lneto.ErrBadCRC - } - return nil -} - -type logger struct { - log *slog.Logger -} - -func (l logger) error(msg string, attrs ...slog.Attr) { - internal.LogAttrs(l.log, slog.LevelError, msg, attrs...) -} -func (l logger) info(msg string, attrs ...slog.Attr) { - internal.LogAttrs(l.log, slog.LevelInfo, msg, attrs...) -} -func (l logger) warn(msg string, attrs ...slog.Attr) { - internal.LogAttrs(l.log, slog.LevelWarn, msg, attrs...) -} -func (l logger) debug(msg string, attrs ...slog.Attr) { - internal.LogAttrs(l.log, slog.LevelDebug, msg, attrs...) -} -func (l logger) trace(msg string, attrs ...slog.Attr) { - internal.LogAttrs(l.log, internal.LevelTrace, msg, attrs...) -} - -const enableAllocLog = internal.HeapAllocDebugging - -func debugLog(msg string) { - if enableAllocLog { - internal.LogAllocs(msg) + 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 } diff --git a/internet/stack-ip4.go b/internet/stack-ip4.go new file mode 100644 index 0000000..36c8900 --- /dev/null +++ b/internet/stack-ip4.go @@ -0,0 +1,183 @@ +package internet + +import ( + "io" + "log/slog" + + "github.com/soypat/lneto" + "github.com/soypat/lneto/internal" + "github.com/soypat/lneto/ipv4" + "github.com/soypat/lneto/tcp" + "github.com/soypat/lneto/udp" +) + +// stackip4 is NOT a StackNode implementation. +// It is meant to be embedded within StackNodes. +// var _ lneto.StackNode = (*stackip4)(nil) + +type stackip4 struct { + handlers handlers + vld *lneto.Validator + ipID uint16 + ip4 [4]byte + acceptMulticast bool +} + +func (si4 *stackip4) reset4(vld *lneto.Validator, maxNodes int) { + *si4 = stackip4{ + ip4: [4]byte{}, + ipID: 1, + acceptMulticast: false, + handlers: si4.handlers, + vld: vld, + } + si4.handlers.reset("stackip4", maxNodes) +} + +func (si4 *stackip4) Register4(h lneto.StackNode) error { + proto := h.Protocol() + if proto > 255 { + return lneto.ErrInvalidConfig + } + return si4.handlers.registerByPortProto(nodeFromStackNode(h, h.LocalPort(), proto, nil)) +} + +func (si4 *stackip4) IsRegistered4(proto lneto.IPProto) bool { + return si4.handlers.nodeByProto(uint16(proto)) != nil +} + +func (si4 *stackip4) SetAcceptMulticast4(accept bool) { + si4.acceptMulticast = accept +} +func (si4 *stackip4) Addr4() [4]byte { return si4.ip4 } +func (si4 *stackip4) SetAddr4(ip4 [4]byte) { + si4.ip4 = ip4 +} + +func (si4 *stackip4) demux4(carrierData []byte, offset int) error { + debugLog("ip4:demux") + si4.handlers.info("demux:start") + frame := carrierData[offset:] // we don't care about carrier data in IP. + ifrm, err := ipv4.NewFrame(frame) + if err != nil { + return err + } + dst := ifrm.DestinationAddr() + if si4.ip4 != ([4]byte{}) && *dst != si4.ip4 { + if !si4.acceptMulticast || dst[0]&0xF0 != 0xE0 { + si4.handlers.debug("ip:not-for-us") + return lneto.ErrPacketDrop // Not meant for us. + } + } + + si4.vld.ResetErr() + ifrm.ValidateExceptCRC(si4.vld) + if err = si4.vld.ErrPop(); err != nil { + si4.handlers.error("ip:Demux.validate") + return err + } + + if ifrm.CalculateHeaderCRC() != 0 { + si4.handlers.error("ip:demux.crc") + return lneto.ErrBadCRC + } + off := ifrm.HeaderLength() + + proto := ifrm.Protocol() + node := si4.handlers.nodeByProto(uint16(proto)) + // nodeIdx := getNodeByProto(sb.handlers, uint16(proto)) + if node == nil { + // Drop packet. + si4.handlers.info("ip:demux.drop", internal.SlogAddr4("dstaddr", ifrm.DestinationAddr()), slog.String("proto", ifrm.Protocol().String())) + return lneto.ErrPacketDrop + } + // Incoming CRC Validation of common IP Protocols. + var crc lneto.CRC791 + switch proto { + case lneto.IPProtoTCP: + ifrm.CRCWriteTCPPseudo(&crc) + if crc.PayloadSum16(ifrm.Payload()) != 0 { + si4.handlers.error("ip:demux.tcpcrc") + return lneto.ErrBadCRC + } + case lneto.IPProtoUDP: + ufrm, err := udp.NewFrame(ifrm.Payload()) + if err != nil { + return err + } + ufrm.ValidateSize(si4.vld) + if err = si4.vld.ErrPop(); err != nil { + si4.handlers.error("ip:demux.udpvalidatesize") + return err + } + frameLen := ufrm.Length() + ifrm.CRCWriteUDPPseudo(&crc, frameLen) + if crc.PayloadSum16(ufrm.RawData()[:frameLen]) != 0 { + si4.handlers.error("ip:demux.udpcrc") + return lneto.ErrBadCRC + } + } + totalLen := ifrm.TotalLength() + si4.handlers.info("ipDemux", slog.String("ipproto", proto.String()), slog.Int("tlen", int(totalLen))) + err = node.callbacks.Demux(frame[:totalLen], off) + if si4.handlers.tryHandleError(node, err) { + si4.handlers.info("ipclose", slog.String("proto", proto.String())) + err = nil + } + return err +} + +func (si4 *stackip4) encapsulate4(carrierData []byte, offsetToIP int) (int, error) { + frame := carrierData[offsetToIP:] + if len(frame) < ipv4.MinimumMTU { + return 0, io.ErrShortBuffer + } + ifrm, _ := ipv4.NewFrame(frame) + const ihl = 5 + const headerlen = ihl * 4 + const dontFrag = 0x4000 + ifrm.SetVersionAndIHL(4, ihl) + ifrm.SetToS(0) + seed := (si4.ipID + 1) ^ uint16(si4.ip4[0]) + id := internal.Prand16(seed) + ifrm.SetID(id) + ifrm.SetFlags(dontFrag) + ifrm.SetTTL(64) + *ifrm.SourceAddr() = si4.ip4 + si4.ipID = id + // Children (TCP/UDP) start at offset headerlen (20 bytes after IP header start). + // offsetToIP is 0 relative to this slice (frame), children's frame starts at headerlen. + node, n, err := si4.handlers.encapsulateAny(carrierData, offsetToIP, offsetToIP+headerlen) + if n == 0 { + return n, err + } + proto := lneto.IPProto(node.proto) + totalLen := n + headerlen + ifrm.SetTotalLength(uint16(totalLen)) + ifrm.SetProtocol(proto) + // Zero the CRC field so its value does not add to the final result. + ifrm.SetCRC(0) + crcValue := ifrm.CalculateHeaderCRC() + ifrm.SetCRC(crcValue) + // Calculate CRC for our newly generated packet. + var crc lneto.CRC791 + payload := ifrm.Payload() + switch proto { + case lneto.IPProtoTCP: + ifrm.CRCWriteTCPPseudo(&crc) + tfrm, _ := tcp.NewFrame(payload) + // Zero the CRC field so its value does not add to the final result. + tfrm.SetCRC(0) + crcValue = crc.PayloadSum16(payload) + tfrm.SetCRC(crcValue) + case lneto.IPProtoUDP: + ufrm, _ := udp.NewFrame(payload) + ifrm.CRCWriteUDPPseudo(&crc, uint16(n)) + ufrm.SetLength(uint16(n)) + // Zero the CRC field so its value does not add to the final result. + ufrm.SetCRC(0) + crcValue = lneto.NeverZeroSum(crc.PayloadSum16(payload)) + ufrm.SetCRC(crcValue) + } + return totalLen, err +} diff --git a/internet/stack-ip6.go b/internet/stack-ip6.go new file mode 100644 index 0000000..b219f69 --- /dev/null +++ b/internet/stack-ip6.go @@ -0,0 +1,144 @@ +package internet + +import ( + "log/slog" + + "github.com/soypat/lneto" + "github.com/soypat/lneto/ipv6" + "github.com/soypat/lneto/tcp" + "github.com/soypat/lneto/udp" +) + +// stackip6 is NOT a StackNode implementation. +// It is meant to be embedded within StackNodes. +// var _ lneto.StackNode = (*stackip6)(nil) + +type stackip6 struct { + handlers handlers + vld *lneto.Validator + ip6 [16]byte + acceptMulticast bool +} + +func (si6 *stackip6) Register6(h lneto.StackNode) error { + proto := h.Protocol() + if proto > 255 { + return lneto.ErrInvalidConfig + } + return si6.handlers.registerByPortProto(nodeFromStackNode(h, h.LocalPort(), proto, nil)) +} + +func (si6 *stackip6) IsRegistered6(proto lneto.IPProto) bool { + return si6.handlers.nodeByProto(uint16(proto)) != nil +} + +func (si6 *stackip6) SetAcceptMulticast6(accept bool) { si6.acceptMulticast = accept } +func (si6 *stackip6) Addr6() [16]byte { return si6.ip6 } +func (si6 *stackip6) SetAddr6(ip6 [16]byte) { si6.ip6 = ip6 } + +func (si6 *stackip6) reset6(vld *lneto.Validator, maxNodes int) { + *si6 = stackip6{ + handlers: si6.handlers, + vld: vld, + } + si6.handlers.reset("stackip6", maxNodes) +} + +func (si6 *stackip6) demux6(carrierData []byte, offset int) error { + debugLog("ip6:demux") + si6.handlers.info("StackIP6.Demux:start") + ifrm, err := ipv6.NewFrame(carrierData[offset:]) + if err != nil { + return err + } + dst := ifrm.DestinationAddr() + if si6.ip6 != ([16]byte{}) && *dst != si6.ip6 { + if !si6.acceptMulticast || dst[0] != 0xFF { + si6.handlers.debug("ip6:not-for-us") + return lneto.ErrPacketDrop + } + } + + si6.vld.ResetErr() + ifrm.ValidateSize(si6.vld) + if err = si6.vld.ErrPop(); err != nil { + si6.handlers.error("ip6:Demux.validate") + return err + } + + proto := ifrm.NextHeader() + node := si6.handlers.nodeByProto(uint16(proto)) + if node == nil { + si6.handlers.info("ip6:demux.drop", slog.String("proto", proto.String())) + return lneto.ErrPacketDrop + } + payload := ifrm.Payload() + var crc lneto.CRC791 + switch proto { + case lneto.IPProtoTCP: + ifrm.CRCWritePseudo(&crc) + if crc.PayloadSum16(payload) != 0 { + si6.handlers.error("ip6:demux.tcpcrc") + return lneto.ErrBadCRC + } + case lneto.IPProtoUDP: + ufrm, err := udp.NewFrame(payload) + if err != nil { + return err + } + ufrm.ValidateSize(si6.vld) + if err = si6.vld.ErrPop(); err != nil { + si6.handlers.error("ip6:demux.udpvalidatesize") + return err + } + ifrm.CRCWritePseudo(&crc) + if crc.PayloadSum16(payload) != 0 { + si6.handlers.error("ip6:demux.udpcrc") + return lneto.ErrBadCRC + } + } + const headerlen = 40 + plen := ifrm.PayloadLength() + si6.handlers.info("ip6Demux", slog.String("ipproto", proto.String()), slog.Int("plen", int(plen))) + err = node.callbacks.Demux(carrierData[offset:offset+headerlen+int(plen)], headerlen) + if si6.handlers.tryHandleError(node, err) { + si6.handlers.info("ip6close", slog.String("proto", proto.String())) + err = nil + } + return err +} + +func (si6 *stackip6) encapsulate6(carrierData []byte, offsetToIP int) (int, error) { + ifrm, err := ipv6.NewFrame(carrierData[offsetToIP:]) + if err != nil { + return 0, err + } + // Set default parameters which node is free to change. + ifrm.SetVersionTrafficAndFlow(6, 0, 0) + ifrm.SetHopLimit(64) + *ifrm.SourceAddr() = si6.ip6 + const headerlen = 40 + node, n, err := si6.handlers.encapsulateAny(carrierData, offsetToIP, offsetToIP+headerlen) + if n == 0 { + return n, err + } + proto := lneto.IPProto(node.proto) + ifrm.SetNextHeader(proto) + ifrm.SetPayloadLength(uint16(n)) + var crc lneto.CRC791 + payload := ifrm.Payload() + switch proto { + case lneto.IPProtoTCP: + ifrm.CRCWritePseudo(&crc) + tfrm, _ := tcp.NewFrame(payload) + tfrm.SetCRC(0) + tfrm.SetCRC(crc.PayloadSum16(payload)) + case lneto.IPProtoUDP: + ufrm, _ := udp.NewFrame(payload) + ufrm.SetLength(uint16(n)) + ifrm.CRCWritePseudo(&crc) + ufrm.SetCRC(0) + ufrm.SetCRC(lneto.NeverZeroSum(crc.PayloadSum16(payload))) + } + return headerlen + n, err +} diff --git a/internet/stackbasic_test.go b/internet/stackbasic_test.go index c86552c..a31f90e 100644 --- a/internet/stackbasic_test.go +++ b/internet/stackbasic_test.go @@ -5,6 +5,7 @@ import ( "net/netip" "testing" + "github.com/soypat/lneto" "github.com/soypat/lneto/tcp" ) @@ -44,7 +45,7 @@ func TestBasicStack2(t *testing.T) { func expectExchange(t *testing.T, from, to *StackIP, buf []byte) { t.Helper() - n, err := from.Encapsulate(buf, -1, 0) + n, err := from.Encapsulate(buf, 0, 0) if err != nil { t.Error("expectExchange:encapsulate:", err) } else if n == 0 { @@ -90,15 +91,97 @@ 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 connCl, connSv tcp.Conn + setupClientServer6(t, rng, &sbCl, &sbSv, &connCl, &connSv) + var buf [2048]byte + nextToSend := &sbCl + nextToRecv := &sbSv + exchangeAndExpectStates := func(clState, svState tcp.State) { + t.Helper() + expectExchange(t, nextToSend, nextToRecv, buf[:]) + gotCl := connCl.State() + gotSv := connSv.State() + if gotCl != clState { + t.Errorf("want client state %s, got %s", clState, gotCl) + } + if gotSv != svState { + t.Errorf("want server state %s, got %s", svState, gotSv) + } + nextToSend, nextToRecv = nextToRecv, nextToSend + } + exchangeAndExpectStates(tcp.StateSynSent, tcp.StateSynRcvd) + exchangeAndExpectStates(tcp.StateEstablished, tcp.StateSynRcvd) + exchangeAndExpectStates(tcp.StateEstablished, tcp.StateEstablished) +} + +func TestBasicStack6Established(t *testing.T) { + rng := rand.New(rand.NewSource(1)) + var sbCl, sbSv StackIP + 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) { + t.Helper() + _ = rng + const maxNodes = 1 + bufsize := 2048 + svip6 := netip.AddrFrom16([16]byte{0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}) // 2001:db8::1 + 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 { + t.Fatal(err) + } + if err := client.Reset(new(lneto.Validator), 0, maxNodes); err != nil { + t.Fatal(err) + } + server.SetAddr6(svip6.As16()) + client.SetAddr6(clip6.As16()) + err := connServer.Configure(tcp.ConnConfig{ + RxBuf: make([]byte, bufsize), + TxBuf: make([]byte, bufsize), + TxPacketQueueSize: 3, + }) + if err != nil { + t.Fatal(err) + } + err = connClient.Configure(tcp.ConnConfig{ + RxBuf: make([]byte, bufsize), + TxBuf: make([]byte, bufsize), + TxPacketQueueSize: 3, + }) + if err != nil { + t.Fatal(err) + } + if err = connServer.OpenListen(svip.Port(), 200); err != nil { + t.Fatal(err) + } + if err = connClient.OpenActive(clip.Port(), svip, 100); err != nil { + t.Fatal(err) + } + if err = server.Register6(connServer); err != nil { + t.Fatal(err) + } + if err = client.Register6(connClient); err != nil { + t.Fatal(err) + } +} + func setupClientServer(t *testing.T, rng *rand.Rand, client, server *StackIP, 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(svip.Addr(), maxNodes) - client.Reset(clip.Addr(), maxNodes) - + server.Reset(new(lneto.Validator), maxNodes, 0) + client.Reset(new(lneto.Validator), maxNodes, 0) + server.SetAddr4(svip.Addr().As4()) + client.SetAddr4(clip.Addr().As4()) err := connServer.Configure(tcp.ConnConfig{ RxBuf: make([]byte, bufsize), TxBuf: make([]byte, bufsize), @@ -127,11 +210,11 @@ func setupClientServer(t *testing.T, rng *rand.Rand, client, server *StackIP, co t.Fatal(err) } - err = server.Register(connServer) + err = server.Register4(connServer) if err != nil { t.Fatal(err) } - err = client.Register(connClient) + err = client.Register4(connClient) if err != nil { t.Fatal(err) } diff --git a/internet/tcplistener_test.go b/internet/tcplistener_test.go index fc202f3..0cfe57c 100644 --- a/internet/tcplistener_test.go +++ b/internet/tcplistener_test.go @@ -6,6 +6,7 @@ import ( "net/netip" "testing" + "github.com/soypat/lneto" "github.com/soypat/lneto/tcp" ) @@ -24,7 +25,7 @@ func TestListener_SingleConnection(t *testing.T) { if err := listener.Reset(serverPort, pool); err != nil { t.Fatal(err) } - if err := serverStack.Register(&listener); err != nil { + if err := serverStack.Register4(&listener); err != nil { t.Fatal(err) } @@ -77,7 +78,7 @@ func TestListener_AcceptAfterEstablished(t *testing.T) { if err := listener.Reset(serverPort, pool); err != nil { t.Fatal(err) } - if err := serverStack.Register(&listener); err != nil { + if err := serverStack.Register4(&listener); err != nil { t.Fatal(err) } @@ -105,7 +106,7 @@ func TestListener_AcceptAfterEstablished(t *testing.T) { // Setup second client and verify we can still accept. var client2Stack StackIP var client2Conn tcp.Conn - setupClient(t, &client2Stack, &client2Conn, serverStack.Addr(), serverPort, 1338) + setupClient(t, &client2Stack, &client2Conn, netip.AddrFrom4(serverStack.Addr4()), serverPort, 1338) // Complete full handshake for client2. expectExchange(t, &client2Stack, &serverStack, buf[:]) // SYN @@ -147,14 +148,14 @@ func TestListener_MultiConn(t *testing.T) { if err := listener.Reset(serverPort, pool); err != nil { t.Fatal(err) } - if err := serverStack.Register(&listener); err != nil { + if err := serverStack.Register4(&listener); err != nil { t.Fatal(err) } // Setup remaining clients. for i := 1; i < numClients; i++ { clientPort := uint16(1337 + i) - setupClient(t, &clientStacks[i], &clientConns[i], serverStack.Addr(), serverPort, clientPort) + setupClient(t, &clientStacks[i], &clientConns[i], netip.AddrFrom4(serverStack.Addr4()), serverPort, clientPort) } var buf [2048]byte @@ -324,7 +325,7 @@ func TestListener_RSTOnPoolExhaustion(t *testing.T) { if err := listener.Reset(serverPort, pool); err != nil { t.Fatal(err) } - if err := serverStack.Register(&listener); err != nil { + if err := serverStack.Register4(&listener); err != nil { t.Fatal(err) } @@ -340,10 +341,10 @@ func TestListener_RSTOnPoolExhaustion(t *testing.T) { // Setup client2 and send its SYN — pool is full, server should queue RST. const client2Port = uint16(1338) - setupClient(t, &client2Stack, &client2Conn, serverStack.Addr(), serverPort, client2Port) + setupClient(t, &client2Stack, &client2Conn, netip.AddrFrom4(serverStack.Addr4()), serverPort, client2Port) // Client2 sends SYN. - n, err := client2Stack.Encapsulate(buf[:], -1, 0) + n, err := client2Stack.Encapsulate(buf[:], 0, 0) if err != nil { t.Fatal("client2 encapsulate:", err) } else if n == 0 { @@ -356,7 +357,7 @@ func TestListener_RSTOnPoolExhaustion(t *testing.T) { } // Server encapsulates — should produce RST (no connection data pending). - n, err = serverStack.Encapsulate(buf[:], -1, 0) + n, err = serverStack.Encapsulate(buf[:], 0, 0) if err != nil { t.Fatal("server encapsulate RST:", err) } else if n == 0 { @@ -619,7 +620,8 @@ func setupClient(t *testing.T, client *StackIP, conn *tcp.Conn, serverAddr netip t.Helper() bufsize := 2048 clientIP := netip.AddrFrom4([4]byte{192, 168, 1, byte(clientPort % 256)}) - client.Reset(clientIP, 1) + client.Reset(new(lneto.Validator), 1, 0) + client.SetAddr4(clientIP.As4()) err := conn.Configure(tcp.ConnConfig{ RxBuf: make([]byte, bufsize), TxBuf: make([]byte, bufsize), @@ -633,7 +635,7 @@ func setupClient(t *testing.T, client *StackIP, conn *tcp.Conn, serverAddr netip if err != nil { t.Fatal(err) } - err = client.Register(conn) + err = client.Register4(conn) if err != nil { t.Fatal(err) } diff --git a/x/xnet/stack-async.go b/x/xnet/stack-async.go index f5f00ef..88e0f8e 100644 --- a/x/xnet/stack-async.go +++ b/x/xnet/stack-async.go @@ -36,6 +36,8 @@ type StackAsync struct { udps internet.StackPortsMACFiltered tcps internet.StackPortsMACFiltered + defaultValidator lneto.Validator + dhcpUDP internet.StackUDPPort dhcp dhcpv4.Client dhcpResults DHCPResults @@ -63,11 +65,13 @@ type StackAsync struct { } type StackConfig struct { - StaticAddress netip.Addr - DNSServer netip.Addr - NTPServer netip.Addr - RandSeed int64 - Hostname string + // StaticAddress6 [16]byte + StaticAddress4 [4]byte + + 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 @@ -148,16 +152,11 @@ func (s *StackAsync) Reset(cfg StackConfig) error { return lneto.ErrInvalidConfig } mac := cfg.HardwareAddress - addr := cfg.StaticAddress s.mu.Lock() defer s.mu.Unlock() s.prng = uint32(cfg.RandSeed) s.hostname = cfg.Hostname - if !addr.IsValid() { - addr = netip.AddrFrom4([4]byte{}) // If static not set DHCP will be performed and address will be zero. - } else if addr.Is6() { - return lneto.ErrUnsupported - } + const linkNodes = 2 // ARP and IP nodes ecfg := internet.StackEthernetConfig{ MTU: int(cfg.MTU), @@ -178,11 +177,12 @@ func (s *StackAsync) Reset(cfg StackConfig) error { s.link.OnEncapsulate(s.arpt.patchEgressMAC) } const ipNodes = 3 // 3 IP protocols possible: UDP, TCP, ICMP. - err = s.ip.Reset(addr, ipNodes) + err = s.ip.Reset(&s.defaultValidator, ipNodes, 0) if err != nil { return err } - s.ip.SetAcceptMulticast(cfg.AcceptMulticast) + s.ip.SetAddr4(cfg.StaticAddress4) + s.ip.SetAcceptMulticast4(cfg.AcceptMulticast) s.arpt.passivePeers = uint8(cfg.PassivePeers) err = s.resetARP() if err != nil { @@ -201,7 +201,7 @@ func (s *StackAsync) Reset(cfg StackConfig) error { if err != nil { return err } - err = s.ip.Register(&s.tcps) + err = s.ip.Register4(&s.tcps) if err != nil { return err } @@ -213,7 +213,7 @@ func (s *StackAsync) Reset(cfg StackConfig) error { if err != nil { return err } - err = s.ip.Register(&s.udps) + err = s.ip.Register4(&s.udps) if err != nil { return err } @@ -243,17 +243,11 @@ func (s *StackAsync) Reset(cfg StackConfig) error { func (s *StackAsync) resetARP() error { mac := s.link.HardwareAddr6() - addr := s.ip.Addr() - if !addr.IsValid() { - return lneto.ErrInvalidAddr - } + addr := s.ip.Addr4() proto := ethernet.TypeIPv4 - if addr.Is6() { - proto = ethernet.TypeIPv6 - } err := s.arp.Reset(arp.HandlerConfig{ HardwareAddr: mac[:], - ProtocolAddr: addr.AsSlice(), + ProtocolAddr: addr[:], MaxQueries: 5, MaxPending: 5, HardwareType: 1, @@ -298,26 +292,21 @@ func (s *StackAsync) prand32() uint32 { return seed } -func (s *StackAsync) SetIPAddr(addr netip.Addr) error { +func (s *StackAsync) SetAddr4(addr [4]byte) error { s.mu.Lock() defer s.mu.Unlock() - return s.setIPAddr(addr) + return s.setIPAddr4(addr) } -func (s *StackAsync) setIPAddr(addr netip.Addr) error { - err := s.ip.SetAddr(addr) - if err != nil { - return err - } - ip := addr.As4() - err = s.arp.UpdateProtoAddr(ip[:]) - return err +func (s *StackAsync) setIPAddr4(addr [4]byte) error { + s.ip.SetAddr4(addr) + return s.arp.UpdateProtoAddr(addr[:]) } -func (s *StackAsync) Addr() netip.Addr { +func (s *StackAsync) Addr4() [4]byte { s.mu.Lock() defer s.mu.Unlock() - return s.ip.Addr() + return s.ip.Addr4() } func (s *StackAsync) SetSubnet(subnetMask netip.Prefix) { @@ -359,10 +348,10 @@ func (s *StackAsync) EnableICMP(enabled bool) (err error) { enabled = false // ensure aborted. } if enabled { - if s.ip.IsRegistered(lneto.IPProtoICMP) { + if s.ip.IsRegistered4(lneto.IPProtoICMP) { return nil } - err = s.ip.Register(&s.icmp) + err = s.ip.Register4(&s.icmp) } else { s.icmp.Abort() } @@ -625,7 +614,7 @@ func (s *StackAsync) DiscardResolveHardwareAddress6(ip netip.Addr) error { type DHCPResults struct { DNSServers []netip.Addr Router netip.Addr - AssignedAddr netip.Addr + AssignedAddr4 [4]byte ServerAddr netip.Addr BroadcastAddr netip.Addr Gateway netip.Addr @@ -665,8 +654,8 @@ func (stack *StackAsync) AssimilateDHCPResults(results *DHCPResults) error { if results.Subnet.IsValid() { stack.arpt.subnet = results.Subnet } - if results.AssignedAddr.IsValid() { - err := stack.setIPAddr(results.AssignedAddr) + if !internal.IsZeroed(results.AssignedAddr4) { + err := stack.setIPAddr4(results.AssignedAddr4) if err != nil { return err } @@ -696,7 +685,7 @@ func (s *StackAsync) populateDHCPResults() error { s.dhcpResults = DHCPResults{ Router: router, Subnet: s.dhcp.SubnetPrefix(), - AssignedAddr: netip.AddrFrom4(assigned4), + AssignedAddr4: assigned4, ServerAddr: addr4(s.dhcp.ServerAddr()), BroadcastAddr: addr4(s.dhcp.BroadcastAddr()), Gateway: addr4(s.dhcp.GatewayAddr()), diff --git a/x/xnet/stack-go.go b/x/xnet/stack-go.go index fd43db4..5b567f2 100644 --- a/x/xnet/stack-go.go +++ b/x/xnet/stack-go.go @@ -76,7 +76,7 @@ func (s StackGo) SocketNetip(ctx context.Context, network string, family, sotype } if laddr.Addr() == netip.IPv4Unspecified() { // Specify address. - laddr = netip.AddrPortFrom(s.blk.async.ip.Addr(), laddr.Port()) + laddr = netip.AddrPortFrom(netip.AddrFrom4(s.blk.async.ip.Addr4()), laddr.Port()) } else if laddr.Addr().Is6() { return nil, lneto.ErrUnsupported } diff --git a/x/xnet/xnet_arp_test.go b/x/xnet/xnet_arp_test.go index 6f94f7b..d6a793a 100644 --- a/x/xnet/xnet_arp_test.go +++ b/x/xnet/xnet_arp_test.go @@ -16,13 +16,13 @@ func TestARPLocal(t *testing.T) { // Most common case: we have a router in between computers. s1.SetGateway6(routerHw) s2.SetGateway6(routerHw) - addr1 := netip.AddrPortFrom(s1.Addr(), 1024) // dialer, client. - addr2 := netip.AddrPortFrom(s2.Addr(), 80) // listener, server. + addr1 := netip.AddrPortFrom(netip.AddrFrom4(s1.Addr4()), 1024) // dialer, client. + addr2 := netip.AddrPortFrom(netip.AddrFrom4(s2.Addr4()), 80) // listener, server. err := s1.AssimilateDHCPResults(&DHCPResults{ Router: netip.AddrFrom4([4]byte{10, 0, 0, 255}), BroadcastAddr: netip.AddrFrom4([4]byte{255, 255, 255, 255}), - AssignedAddr: s1.Addr(), - Subnet: netip.PrefixFrom(s2.Addr(), 24), // Subnet containing s2 will force an ARP on s1. + AssignedAddr4: s1.Addr4(), + Subnet: netip.PrefixFrom(netip.AddrFrom4(s2.Addr4()), 24), // Subnet containing s2 will force an ARP on s1. TRenewal: 1000, TRebind: 1000, TLease: 1000, diff --git a/x/xnet/xnet_bench_test.go b/x/xnet/xnet_bench_test.go index f994ca3..99b9d0c 100644 --- a/x/xnet/xnet_bench_test.go +++ b/x/xnet/xnet_bench_test.go @@ -17,7 +17,7 @@ func BenchmarkARPExchange(b *testing.B) { err := c1.Reset(StackConfig{ Hostname: "C1", RandSeed: 1, - StaticAddress: netip.AddrFrom4([4]byte{192, 168, 1, 1}), + StaticAddress4: [4]byte{192, 168, 1, 1}, HardwareAddress: [6]byte{0xde, 0xad, 0xbe, 0xef, 0x00, 0x00}, MTU: MTU, }) @@ -27,7 +27,7 @@ func BenchmarkARPExchange(b *testing.B) { err = c2.Reset(StackConfig{ Hostname: "C2", RandSeed: 2, - StaticAddress: queryAddr, + StaticAddress4: queryAddr.As4(), HardwareAddress: [6]byte{0xc0, 0xff, 0xee, 0xc0, 0xff, 0xee}, MTU: MTU, }) @@ -85,7 +85,7 @@ func BenchmarkTCPHandshake(b *testing.B) { err := sv.Reset(StackConfig{ Hostname: "Server", RandSeed: 1, - StaticAddress: netip.AddrFrom4([4]byte{10, 0, 0, 1}), + StaticAddress4: [4]byte{10, 0, 0, 1}, MaxActiveTCPPorts: 1, HardwareAddress: [6]byte{0xbe, 0xef, 0, 0, 0, 1}, MTU: MTU, @@ -96,7 +96,7 @@ func BenchmarkTCPHandshake(b *testing.B) { err = client.Reset(StackConfig{ Hostname: "Client", RandSeed: 2, - StaticAddress: netip.AddrFrom4([4]byte{10, 0, 0, 2}), + StaticAddress4: [4]byte{10, 0, 0, 2}, MaxActiveTCPPorts: 1, HardwareAddress: [6]byte{0xbe, 0xef, 0, 0, 0, 2}, MTU: MTU, @@ -134,7 +134,7 @@ func BenchmarkTCPHandshake(b *testing.B) { if err != nil { b.Fatal(err) } - err = client.DialTCP(clconn, 1337, netip.AddrPortFrom(sv.Addr(), svPort)) + err = client.DialTCP(clconn, 1337, netip.AddrPortFrom(netip.AddrFrom4(sv.Addr4()), svPort)) if err != nil { b.Fatal(err) } diff --git a/x/xnet/xnet_concurrent_test.go b/x/xnet/xnet_concurrent_test.go index d875011..f27187c 100644 --- a/x/xnet/xnet_concurrent_test.go +++ b/x/xnet/xnet_concurrent_test.go @@ -37,7 +37,7 @@ func TestTCPListener_ConcurrentEcho(t *testing.T) { err := serverStack.Reset(StackConfig{ Hostname: "Server", RandSeed: seed, - StaticAddress: serverIP, + StaticAddress4: serverIP.As4(), MaxActiveTCPPorts: numClients, HardwareAddress: serverMAC, MTU: MTU, @@ -75,11 +75,11 @@ func TestTCPListener_ConcurrentEcho(t *testing.T) { for i := range clientStacks { clientMAC := [6]byte{0xaa, 0xbb, 0xcc, 0x00, 0x01, byte(i + 1)} - clientIP := netip.AddrFrom4([4]byte{10, 0, 0, byte(i + 10)}) + clientIP := [4]byte{10, 0, 0, byte(i + 10)} err := clientStacks[i].Reset(StackConfig{ Hostname: fmt.Sprintf("Client%d", i), RandSeed: int64(seed + i + 1), - StaticAddress: clientIP, + StaticAddress4: clientIP, MaxActiveTCPPorts: 1, HardwareAddress: clientMAC, MTU: MTU, @@ -185,10 +185,10 @@ func routePacketToClient(pkt []byte, clients []StackAsync) { if len(pkt) < 20+ethernet.MaxOverheadSize { // 20 min IP header return } - dstIP := netip.AddrFrom4([4]byte{pkt[30], pkt[31], pkt[32], pkt[33]}) + dstIP := [4]byte{pkt[30], pkt[31], pkt[32], pkt[33]} for i := range clients { - if clients[i].Addr() == dstIP { + if clients[i].Addr4() == dstIP { clients[i].IngressEthernet(pkt) return } diff --git a/x/xnet/xnet_dns_test.go b/x/xnet/xnet_dns_test.go index 06e56be..0665357 100644 --- a/x/xnet/xnet_dns_test.go +++ b/x/xnet/xnet_dns_test.go @@ -27,7 +27,7 @@ func TestDNS_QueryReceivesAnswer(t *testing.T) { err := client.Reset(StackConfig{ Hostname: "DNSClient", RandSeed: seed, - StaticAddress: clientAddr, + StaticAddress4: clientAddr.As4(), DNSServer: dnsServerAddr, HardwareAddress: clientMAC, MTU: uint16(MTU), diff --git a/x/xnet/xnet_fuzz_test.go b/x/xnet/xnet_fuzz_test.go index 8fb5699..a2e55fc 100644 --- a/x/xnet/xnet_fuzz_test.go +++ b/x/xnet/xnet_fuzz_test.go @@ -26,7 +26,7 @@ func FuzzStackPacketHTTP(f *testing.F) { if err != nil { f.Fatal(err) } - err = s2.DialTCP(c2, 1337, netip.AddrPortFrom(s1.Addr(), c1.LocalPort())) + err = s2.DialTCP(c2, 1337, netip.AddrPortFrom(netip.AddrFrom4(s1.Addr4()), c1.LocalPort())) if err != nil { f.Fatal(err) } @@ -92,7 +92,7 @@ func FuzzStackPacketHTTP(f *testing.F) { if err != nil { t.Fatal(err) } - err = s2.DialTCP(c2, 1337, netip.AddrPortFrom(s1.Addr(), c1.LocalPort())) + err = s2.DialTCP(c2, 1337, netip.AddrPortFrom(netip.AddrFrom4(s1.Addr4()), c1.LocalPort())) if err != nil { t.Fatal(err) } @@ -275,7 +275,7 @@ func testStackSeeded(t *testing.T, seed1, seed2 int64) { v1, v2 := byte(seed1), byte(seed2) cfg1 := StackConfig{ Hostname: "s1", - StaticAddress: netip.AddrFrom4([4]byte{1, 0, 0, v1}), + StaticAddress4: [4]byte{1, 0, 0, v1}, RandSeed: seed1, MaxActiveTCPPorts: 1, MaxActiveUDPPorts: 1, @@ -291,7 +291,7 @@ func testStackSeeded(t *testing.T, seed1, seed2 int64) { } cfg2 := StackConfig{ Hostname: "s2", - StaticAddress: netip.AddrFrom4([4]byte{1, 0, 0, v2}), + StaticAddress4: [4]byte{1, 0, 0, v2}, RandSeed: seed2, MaxActiveTCPPorts: 1, MaxActiveUDPPorts: 1, @@ -360,7 +360,7 @@ func testStackSeeded(t *testing.T, seed1, seed2 int64) { if verbose { fmt.Fprintln(fzoutput, "TCP dial") } - err = s1.DialTCP(&tcp1, port1, netip.AddrPortFrom(s2.Addr(), port2)) + err = s1.DialTCP(&tcp1, port1, netip.AddrPortFrom(netip.AddrFrom4(s2.Addr4()), port2)) if err != nil { t.Fatal(i, err) } @@ -387,7 +387,7 @@ func testStackSeeded(t *testing.T, seed1, seed2 int64) { if verbose { fmt.Fprintln(fzoutput, "UDP dial 1") } - err = s1.DialUDP(&udp1, port1, netip.AddrPortFrom(s2.Addr(), port2)) + err = s1.DialUDP(&udp1, port1, netip.AddrPortFrom(netip.AddrFrom4(s2.Addr4()), port2)) if err != nil { t.Fatal(i, err) } @@ -396,7 +396,7 @@ func testStackSeeded(t *testing.T, seed1, seed2 int64) { if verbose { fmt.Fprintln(fzoutput, "UDP dial 2") } - err = s2.DialUDP(&udp2, port2, netip.AddrPortFrom(s1.Addr(), port1)) + err = s2.DialUDP(&udp2, port2, netip.AddrPortFrom(netip.AddrFrom4(s1.Addr4()), port1)) if err != nil { t.Fatal(i, err) } @@ -447,13 +447,13 @@ func testStackSeeded(t *testing.T, seed1, seed2 int64) { switch icmpaction { case 0: s1.icmp.Reset() - _, err = s1.icmp.PingStart(s2.Addr().As4(), buf[:pingMinPayload], pingMinPayload+uint16(action.Rand)%pingMinPayload) + _, err = s1.icmp.PingStart(s2.Addr4(), buf[:pingMinPayload], pingMinPayload+uint16(action.Rand)%pingMinPayload) if err != nil { t.Fatal(i, err) } case 1: s2.icmp.Reset() - _, err = s2.icmp.PingStart(s1.Addr().As4(), buf[:pingMinPayload], pingMinPayload+uint16(action.Rand)%pingMinPayload) + _, err = s2.icmp.PingStart(s1.Addr4(), buf[:pingMinPayload], pingMinPayload+uint16(action.Rand)%pingMinPayload) if err != nil { t.Fatal(i, err) } @@ -466,17 +466,17 @@ func testStackSeeded(t *testing.T, seed1, seed2 int64) { } switch action { case 0: // s1 queries s2 address. - s1.StartResolveHardwareAddress6(s2.Addr()) + s1.StartResolveHardwareAddress6(netip.AddrFrom4(s2.Addr4())) case 1: // s2 queries s1 address. - s2.StartResolveHardwareAddress6(s1.Addr()) + s2.StartResolveHardwareAddress6(netip.AddrFrom4(s1.Addr4())) case 2: // s1 checks query result for s2. - s1.ResultResolveHardwareAddress6(s2.Addr()) + s1.ResultResolveHardwareAddress6(netip.AddrFrom4(s2.Addr4())) case 3: // s2 checks query result for s1. - s2.ResultResolveHardwareAddress6(s1.Addr()) + s2.ResultResolveHardwareAddress6(netip.AddrFrom4(s1.Addr4())) case 4: // s1 discards pending query. - s1.DiscardResolveHardwareAddress6(s2.Addr()) + s1.DiscardResolveHardwareAddress6(netip.AddrFrom4(s2.Addr4())) case 5: // s2 discards pending query. - s2.DiscardResolveHardwareAddress6(s1.Addr()) + s2.DiscardResolveHardwareAddress6(netip.AddrFrom4(s1.Addr4())) } } // Exchange data while checking stack does not enter runaway infinite frame send loop. diff --git a/x/xnet/xnet_icmp_test.go b/x/xnet/xnet_icmp_test.go index 8261cec..3cc0aad 100644 --- a/x/xnet/xnet_icmp_test.go +++ b/x/xnet/xnet_icmp_test.go @@ -1,7 +1,6 @@ package xnet import ( - "net/netip" "testing" ) @@ -37,7 +36,7 @@ func TestStackAsync_ICMPEcho(t *testing.T) { sender.SetGateway6(receiver.HardwareAddress()) receiver.SetGateway6(sender.HardwareAddress()) - key, err := sender.icmp.PingStart(receiver.Addr().As4(), tt.pattern, tt.size) + key, err := sender.icmp.PingStart(receiver.Addr4(), tt.pattern, tt.size) if err != nil { t.Fatal(err) } @@ -92,15 +91,15 @@ func newICMPStacks(t testing.TB, randSeed int64, mtu int) (*StackAsync, *StackAs // Use the seed to generate two adjacent IPs (10.0.0.x) and MACs. base := byte(randSeed & 0x7F) // keep in safe range 0..127 - addr1 := netip.AddrFrom4([4]byte{10, 0, 0, base}) - addr2 := netip.AddrFrom4([4]byte{10, 0, 0, base + 1}) + addr1 := [4]byte{10, 0, 0, base} + addr2 := [4]byte{10, 0, 0, base + 1} mac1 := [6]byte{0xbe, 0xef, 0, 0, 0, base} mac2 := [6]byte{0xbe, 0xef, 0, 0, 0, base + 1} if err := s1.Reset(StackConfig{ Hostname: "icmp-stack-1", RandSeed: randSeed, - StaticAddress: addr1, + StaticAddress4: addr1, HardwareAddress: mac1, MTU: uint16(mtu), ICMPQueueLimit: icmpQueue, @@ -111,7 +110,7 @@ func newICMPStacks(t testing.TB, randSeed int64, mtu int) (*StackAsync, *StackAs if err := s2.Reset(StackConfig{ Hostname: "icmp-stack-2", RandSeed: ^randSeed, - StaticAddress: addr2, + StaticAddress4: addr2, HardwareAddress: mac2, MTU: uint16(mtu), ICMPQueueLimit: icmpQueue, diff --git a/x/xnet/xnet_listener_test.go b/x/xnet/xnet_listener_test.go index 0129960..01398e0 100644 --- a/x/xnet/xnet_listener_test.go +++ b/x/xnet/xnet_listener_test.go @@ -21,7 +21,7 @@ func TestStackAsyncListener_SingleConnection(t *testing.T) { err := client.Reset(StackConfig{ Hostname: "Client", RandSeed: seed, - StaticAddress: netip.AddrFrom4([4]byte{10, 0, 0, 1}), + StaticAddress4: [4]byte{10, 0, 0, 1}, MaxActiveTCPPorts: 1, HardwareAddress: [6]byte{0xbe, 0xef, 0, 0, 0, 1}, MTU: MTU, @@ -32,7 +32,7 @@ func TestStackAsyncListener_SingleConnection(t *testing.T) { err = sv.Reset(StackConfig{ Hostname: "Server", RandSeed: ^seed, - StaticAddress: netip.AddrFrom4([4]byte{10, 0, 0, 2}), + StaticAddress4: [4]byte{10, 0, 0, 2}, MaxActiveTCPPorts: 1, // Note: We use listener, not direct TCP conn registration. HardwareAddress: [6]byte{0xbe, 0xef, 0, 0, 0, 2}, MTU: MTU, @@ -78,7 +78,7 @@ func TestStackAsyncListener_SingleConnection(t *testing.T) { } // Client dials server. - err = client.DialTCP(&clConn, clPort, netip.AddrPortFrom(sv.Addr(), svPort)) + err = client.DialTCP(&clConn, clPort, netip.AddrPortFrom(netip.AddrFrom4(sv.Addr4()), svPort)) if err != nil { t.Fatal(err) } @@ -133,7 +133,7 @@ func TestStackAsyncListener_MultiSequentialConn(t *testing.T) { err := sv.Reset(StackConfig{ Hostname: "Server", RandSeed: ^seed, - StaticAddress: netip.AddrFrom4([4]byte{10, 0, 0, 2}), + StaticAddress4: [4]byte{10, 0, 0, 2}, MaxActiveTCPPorts: 1, // Note: We use listener, not direct TCP conn registration. HardwareAddress: [6]byte{0xbe, 0xef, 0, 0, 0, 2}, MTU: MTU, @@ -173,7 +173,7 @@ func TestStackAsyncListener_MultiSequentialConn(t *testing.T) { err := client.Reset(StackConfig{ Hostname: "Client", RandSeed: seed, - StaticAddress: caddrp.Addr(), + StaticAddress4: caddrp.Addr().As4(), MaxActiveTCPPorts: 1, HardwareAddress: chw, MTU: MTU, @@ -193,7 +193,7 @@ func TestStackAsyncListener_MultiSequentialConn(t *testing.T) { t.Fatal(err) } // Client dials server. - err = client.DialTCP(&clConn, caddrp.Port(), netip.AddrPortFrom(sv.Addr(), svPort)) + err = client.DialTCP(&clConn, caddrp.Port(), netip.AddrPortFrom(netip.AddrFrom4(sv.Addr4()), svPort)) if err != nil { t.Fatal(err) } @@ -207,7 +207,7 @@ func TestStackAsyncListener_MultiSequentialConn(t *testing.T) { if err != nil { t.Fatal(err) } else if svconn.RemotePort() != clConn.LocalPort() || - [4]byte(svconn.RemoteAddr()) != client.Addr().As4() { + [4]byte(svconn.RemoteAddr()) != client.Addr4() { t.Fatal("race condition to listener acquisition") } // Verify both connections are established. diff --git a/x/xnet/xnet_mdns_test.go b/x/xnet/xnet_mdns_test.go index 87042b2..3dc0a5d 100644 --- a/x/xnet/xnet_mdns_test.go +++ b/x/xnet/xnet_mdns_test.go @@ -34,9 +34,9 @@ func TestMDNS_QueryResponse(t *testing.T) { Port: 80, } - responderAddr := netip.AddrFrom4([4]byte{192, 168, 1, 50}) + responderAddr := [4]byte{192, 168, 1, 50} responderMAC := [6]byte{0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x01} - querierAddr := netip.AddrFrom4([4]byte{192, 168, 1, 100}) + querierAddr := [4]byte{192, 168, 1, 100} querierMAC := [6]byte{0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x02} mcastAddr := []byte{224, 0, 0, 251} @@ -45,7 +45,7 @@ func TestMDNS_QueryResponse(t *testing.T) { err = responderStack.Reset(StackConfig{ Hostname: "responder", RandSeed: 1234, - StaticAddress: responderAddr, + StaticAddress4: responderAddr, HardwareAddress: responderMAC, MTU: MTU, MaxActiveUDPPorts: 1, @@ -75,7 +75,7 @@ func TestMDNS_QueryResponse(t *testing.T) { err = querierStack.Reset(StackConfig{ Hostname: "querier", RandSeed: 5678, - StaticAddress: querierAddr, + StaticAddress4: querierAddr, HardwareAddress: querierMAC, MTU: MTU, MaxActiveUDPPorts: 1, @@ -280,7 +280,7 @@ func newMDNSStack(t *testing.T, hostname string, seed int64, err := stack.Reset(StackConfig{ Hostname: hostname, RandSeed: seed, - StaticAddress: addr, + StaticAddress4: addr.As4(), HardwareAddress: mac, MTU: MTU, MaxActiveUDPPorts: 1, diff --git a/x/xnet/xnet_subnettable_test.go b/x/xnet/xnet_subnettable_test.go index a358b29..c13d8a2 100644 --- a/x/xnet/xnet_subnettable_test.go +++ b/x/xnet/xnet_subnettable_test.go @@ -53,7 +53,7 @@ func TestStackAsync_ListenerSynAckAddressedToClient(t *testing.T) { err := sv.Reset(StackConfig{ Hostname: "Server1", RandSeed: 1234, - StaticAddress: netip.AddrFrom4([4]byte{10, 0, 0, 2}), + StaticAddress4: [4]byte{10, 0, 0, 2}, MaxActiveTCPPorts: 1, HardwareAddress: serverMAC, MTU: mtu, @@ -89,7 +89,7 @@ func TestStackAsync_ListenerSynAckAddressedToClient(t *testing.T) { err = client.Reset(StackConfig{ Hostname: "Client1", RandSeed: 5678, - StaticAddress: netip.AddrFrom4([4]byte{10, 0, 0, 1}), + StaticAddress4: [4]byte{10, 0, 0, 1}, MaxActiveTCPPorts: 1, HardwareAddress: clientMAC, MTU: mtu, @@ -106,7 +106,7 @@ func TestStackAsync_ListenerSynAckAddressedToClient(t *testing.T) { }); err != nil { t.Fatal(err) } - if err = client.DialTCP(&clConn, 54321, netip.AddrPortFrom(sv.Addr(), svPort)); err != nil { + if err = client.DialTCP(&clConn, 54321, netip.AddrPortFrom(netip.AddrFrom4(sv.Addr4()), svPort)); err != nil { t.Fatal(err) } diff --git a/x/xnet/xnet_test.go b/x/xnet/xnet_test.go index 200de7a..baba791 100644 --- a/x/xnet/xnet_test.go +++ b/x/xnet/xnet_test.go @@ -178,7 +178,7 @@ func newTCPStacks(t testing.TB, randSeed int64, mtu int) (s1, s2 *StackAsync, c1 err := s1.Reset(StackConfig{ Hostname: "Stack1", RandSeed: randSeed, - StaticAddress: netip.AddrFrom4([4]byte{10, 0, 0, byte1}), + StaticAddress4: [4]byte{10, 0, 0, byte1}, MaxActiveTCPPorts: 1, HardwareAddress: [6]byte{0xbe, 0xef, 0, 0, 0, byte1}, MTU: uint16(mtu), @@ -192,7 +192,7 @@ func newTCPStacks(t testing.TB, randSeed int64, mtu int) (s1, s2 *StackAsync, c1 err = s2.Reset(StackConfig{ Hostname: "Stack2", RandSeed: ^randSeed, - StaticAddress: netip.AddrFrom4([4]byte{10, 0, 0, byte2}), + StaticAddress4: [4]byte{10, 0, 0, byte2}, MaxActiveTCPPorts: 1, HardwareAddress: [6]byte{0xbe, 0xef, 0, 0, 0, byte2}, MTU: uint16(mtu), @@ -257,7 +257,7 @@ func (tst *tester) TestTCPSetupAndEstablish(svStack, clStack *StackAsync, svConn if err != nil { t.Fatal(err) } - err = clStack.DialTCP(clConn, clPort, netip.AddrPortFrom(svStack.Addr(), svPort)) + err = clStack.DialTCP(clConn, clPort, netip.AddrPortFrom(netip.AddrFrom4(svStack.Addr4()), svPort)) if err != nil { t.Fatal(err) } @@ -464,12 +464,12 @@ func (tst *tester) TCPExchange(expect tcpExpectExchange, stack1, stack2 *StackAs if tst.getInt(protoIPv4, pcap.FieldClassVersion) != 4 { t.Errorf("did not get IP version=4, got=%d", tst.getInt(protoIPv4, pcap.FieldClassVersion)) } - srcAddr := src.Addr() - dstAddr := dst.Addr() - if !bytes.Equal(srcAddr.AsSlice(), tst.getData(protoIPv4, pcap.FieldClassSrc)) { + srcAddr := src.Addr4() + dstAddr := dst.Addr4() + if !bytes.Equal(srcAddr[:], tst.getData(protoIPv4, pcap.FieldClassSrc)) { t.Errorf("mismatched ip src addr %d", tst.getData(protoIPv4, pcap.FieldClassSrc)) } - if !bytes.Equal(dstAddr.AsSlice(), tst.getData(protoIPv4, pcap.FieldClassDst)) { + if !bytes.Equal(dstAddr[:], tst.getData(protoIPv4, pcap.FieldClassDst)) { t.Errorf("mismatched ip dst addr %d", tst.getData(protoIPv4, pcap.FieldClassDst)) } tfrm := tst.getTCPFrame() @@ -514,8 +514,8 @@ func (tst *tester) ARPExchangeOnly(querying, target *StackAsync) { qHw := querying.HardwareAddress() tgtHw := target.HardwareAddress() broadcast := ethernet.BroadcastAddr() - qIP := querying.Addr() - tgtIP := target.Addr() + qIP := querying.Addr4() + tgtIP := target.Addr4() // Validate Ethernet layer (request is broadcast) if !bytes.Equal(qHw[:], tst.getData(protoEthernet, pcap.FieldClassSrc)) { @@ -534,10 +534,10 @@ func (tst *tester) ARPExchangeOnly(querying, target *StackAsync) { if !bytes.Equal(qHw[:], tst.getFieldByClassLen(protoARP, pcap.FieldClassSrc, 6, 0)) { t.Errorf("request: mismatched ARP sender HW") } - if !bytes.Equal(qIP.AsSlice(), tst.getFieldByClassLen(protoARP, pcap.FieldClassSrc, 4, 0)) { + if !bytes.Equal(qIP[:], tst.getFieldByClassLen(protoARP, pcap.FieldClassSrc, 4, 0)) { t.Errorf("request: mismatched ARP sender proto") } - if !bytes.Equal(tgtIP.AsSlice(), tst.getFieldByClassLen(protoARP, pcap.FieldClassSrc, 4, 1)) { + if !bytes.Equal(tgtIP[:], tst.getFieldByClassLen(protoARP, pcap.FieldClassSrc, 4, 1)) { t.Errorf("request: mismatched ARP target proto") } @@ -579,13 +579,13 @@ func (tst *tester) ARPExchangeOnly(querying, target *StackAsync) { if !bytes.Equal(tgtHw[:], tst.getFieldByClassLen(protoARP, pcap.FieldClassSrc, 6, 0)) { t.Errorf("reply: mismatched ARP sender HW (should be target's MAC)") } - if !bytes.Equal(tgtIP.AsSlice(), tst.getFieldByClassLen(protoARP, pcap.FieldClassSrc, 4, 0)) { + if !bytes.Equal(tgtIP[:], tst.getFieldByClassLen(protoARP, pcap.FieldClassSrc, 4, 0)) { t.Errorf("reply: mismatched ARP sender proto (should be target's IP)") } if !bytes.Equal(qHw[:], tst.getFieldByClassLen(protoARP, pcap.FieldClassSrc, 6, 1)) { t.Errorf("reply: mismatched ARP target HW (should be querying's MAC)") } - if !bytes.Equal(qIP.AsSlice(), tst.getFieldByClassLen(protoARP, pcap.FieldClassSrc, 4, 1)) { + if !bytes.Equal(qIP[:], tst.getFieldByClassLen(protoARP, pcap.FieldClassSrc, 4, 1)) { t.Errorf("reply: mismatched ARP target proto (should be querying's IP)") } @@ -597,7 +597,7 @@ func (tst *tester) ARPExchangeOnly(querying, target *StackAsync) { setzero(buf[:n]) // === PHASE 3: Verify querying stack learned target's MAC === - resolvedHw, err := querying.ResultResolveHardwareAddress6(tgtIP) + resolvedHw, err := querying.ResultResolveHardwareAddress6(netip.AddrFrom4(tgtIP)) if err != nil { t.Errorf("ARP query result failed: %v", err) } else if resolvedHw != tgtHw { @@ -929,7 +929,7 @@ func TestTCPConn_BufferNotClearedOnPassiveClose(t *testing.T) { func TestStackAsync_ICMPEchoChecksum(t *testing.T) { const MTU = ethernet.MaxMTU const MaxFrameLength = MTU + ethernet.MaxOverheadSize // Ethernet header+FCS+VLAN. - stackAddr := netip.AddrFrom4([4]byte{192, 168, 1, 99}) + stackAddr := [4]byte{192, 168, 1, 99} stackMAC := [6]byte{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff} routerAddr := [4]byte{192, 168, 1, 1} routerMAC := [6]byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55} @@ -938,7 +938,7 @@ func TestStackAsync_ICMPEchoChecksum(t *testing.T) { err := stack.Reset(StackConfig{ Hostname: "ICMPTest", RandSeed: 42, - StaticAddress: stackAddr, + StaticAddress4: stackAddr, HardwareAddress: stackMAC, MTU: MTU, ICMPQueueLimit: 2, @@ -954,7 +954,7 @@ func TestStackAsync_ICMPEchoChecksum(t *testing.T) { SrcMAC: routerMAC, DstMAC: stackMAC, SrcIPv4: routerAddr, - DstIPv4: stackAddr.As4(), + DstIPv4: stackAddr, } icmpPayload := []byte("abcdefghijklmnopqrstuvwxyz012345") // 32 bytes, typical ping payload. const (