diff --git a/arp/handler.go b/arp/handler.go index 076a335..46753e0 100644 --- a/arp/handler.go +++ b/arp/handler.go @@ -150,7 +150,7 @@ func (c *Handler) Recv(b []byte) error { var vld lneto.Validator afrm.ValidateSize(&vld) if vld.HasError() { - return vld.Err() + return vld.ErrPop() } htype, hlen := afrm.Hardware() if htype != c.htype || int(hlen) != len(c.ourHWAddr) { diff --git a/arp/handler_test.go b/arp/handler_test.go index 009e293..d97ef6c 100644 --- a/arp/handler_test.go +++ b/arp/handler_test.go @@ -114,8 +114,8 @@ func validateARP(t *testing.T, buf []byte) { var vld lneto.Validator afrm.ValidateSize(&vld) if vld.HasError() { - t.Errorf("invalid arp: %s", vld.Err()) - } else if err := vld.Err(); err != nil { + t.Errorf("invalid arp: %s", vld.ErrPop()) + } else if err := vld.ErrPop(); err != nil { panic("unreachable: " + err.Error()) } } diff --git a/examples/stack/main.go b/examples/stack/main.go index 44dfd1b..547f7ac 100644 --- a/examples/stack/main.go +++ b/examples/stack/main.go @@ -1,6 +1,8 @@ package main import ( + "crypto/rand" + "encoding/binary" "fmt" "log" "log/slog" @@ -49,7 +51,7 @@ func main() { if err != nil { log.Fatal(err) } - handler, err := stack.OpenPassiveTCP(addrPort.Port(), iss) + listener, err := stack.OpenTCPListener(addrPort.Port()) if err != nil { log.Fatal(err) } @@ -62,6 +64,7 @@ func main() { const standbyDuration = 5 * time.Second lastHit := time.Now().Add(-standbyDuration) var cap pcap.PacketBreakdown + var conn *tcp.Conn for { nread, err := tap.Read(buf[:]) if err != nil { @@ -82,7 +85,25 @@ func main() { lg.Error("recv", slog.String("err", err.Error()), slog.Int("plen", nread)) } } - doHTTP(handler, &hdr) + if conn == nil && listener.NumberOfReadyToAccept() > 0 { + conn, err = listener.TryAccept() + if err != nil { + lg.Error("tryaccept", slog.String("err", err.Error())) + } + lg.Info("ACCEPT!") + } + if conn != nil { + done, err := doHTTP(conn, &hdr) + if done { + lg.Info("close forever") + conn.Close() + conn = nil + } + if err != nil { + lg.Error("doHTTP", slog.String("err", err.Error())) + } + } + nw, err := stack.ethernet.Encapsulate(buf[:], 0) if err != nil { lg.Error("handle", slog.String("err", err.Error())) @@ -115,23 +136,21 @@ func main() { } } -func doHTTP(conn *tcp.Conn, hdr *httpraw.Header) error { +func doHTTP(conn *tcp.Conn, hdr *httpraw.Header) (done bool, err error) { const asRequest = false if conn.State() != tcp.StateEstablished || conn.BufferedInput() == 0 { - return nil // No data yet. + return false, nil // No data yet. } fmt.Println("state is established; check request and send response") - _, err := hdr.ReadFromLimited(conn, hdr.BufferFree()) + _, err = hdr.ReadFromLimited(conn, hdr.BufferFree()) if err != nil { - return err + return false, err } needMore, err := hdr.TryParse(asRequest) - if err != nil { - if !needMore { - fmt.Println("IT's SO GOVER") - conn.Close() - } - return err + if needMore { + return false, nil + } else if err != nil { + return true, err } // HTTP parsed succesfully! fmt.Println("GOT HTTP:\n", hdr.String()) @@ -141,18 +160,18 @@ func doHTTP(conn *tcp.Conn, hdr *httpraw.Header) error { data := `{"ok":true}` response, err := hdr.AppendResponse(nil) if err != nil { - return err + return true, err } response = append(response, data...) _, err = conn.Write(response) if err != nil { - return err + return true, err } err = conn.Close() if err != nil { - return err + return true, err } - return nil + return true, nil } type Stack struct { @@ -198,6 +217,7 @@ func (stack *Stack) Reset(ourMAC, gwMAC [6]byte, ip netip.Addr, mtu int) (err er if err != nil { return err } + return nil } @@ -209,6 +229,19 @@ func (stack *Stack) Send(b []byte) (int, error) { return stack.ethernet.Encapsulate(b, 0) } +func (stack *Stack) OpenTCPListener(port uint16) (*internet.NodeTCPListener, error) { + var listener internet.NodeTCPListener + err := listener.Reset(port, naiveTCPPool{}) + if err != nil { + return nil, err + } + err = stack.tcpports.Register(&listener) + if err != nil { + return nil, err + } + return &listener, nil +} + func (stack *Stack) OpenPassiveTCP(port uint16, iss tcp.Value) (*tcp.Conn, error) { mtu := stack.ethernet.MTU() conn := new(tcp.Conn) @@ -250,3 +283,25 @@ func getTCPFlags(frames []pcap.Frame, pkt []byte) (flags tcp.Flags) { } return 0 } + +type naiveTCPPool struct { +} + +func (naiveTCPPool) GetTCP() (*tcp.Conn, tcp.Value) { + var buf [4]byte + rand.Read(buf[:]) + randVal := binary.LittleEndian.Uint32(buf[:]) + var conn tcp.Conn + err := conn.Configure(&tcp.ConnConfig{ + RxBuf: make([]byte, 1024), + TxBuf: make([]byte, 1024), + TxPacketQueueSize: 3, + Logger: slog.Default(), + }) + if err != nil { + panic(err) + } + return &conn, tcp.Value(randVal) +} + +func (naiveTCPPool) PutTCP(*tcp.Conn) {} diff --git a/examples/stackbasic/main.go b/examples/stackbasic/main.go index 7b9380a..bd3eed9 100644 --- a/examples/stackbasic/main.go +++ b/examples/stackbasic/main.go @@ -268,7 +268,7 @@ func (ls *LinkStack) RecvEth(ethFrame []byte) (err error) { goto DROP } efrm.ValidateSize(&vld) - if err := vld.Err(); err != nil { + if err := vld.ErrPop(); err != nil { return err } diff --git a/internal/ltesto/ltesto.go b/internal/ltesto/ltesto.go index 2facfef..1e2ce1d 100644 --- a/internal/ltesto/ltesto.go +++ b/internal/ltesto/ltesto.go @@ -143,15 +143,15 @@ func (gen *PacketGen) AppendRandomIPv4TCPPacket(dst []byte, rng *rand.Rand, seg } var vld lneto.Validator efrm.ValidateSize(&vld) - if err = vld.Err(); err != nil { + if err = vld.ErrPop(); err != nil { panic(err) } ifrm.ValidateExceptCRC(&vld) - if err = vld.Err(); err != nil { + if err = vld.ErrPop(); err != nil { panic(err) } tfrm.ValidateSize(&vld) - if err = vld.Err(); err != nil { + if err = vld.ErrPop(); err != nil { panic(err) } return dst diff --git a/internet/node-arp.go b/internet/node-arp.go index 1a3a184..6efd061 100644 --- a/internet/node-arp.go +++ b/internet/node-arp.go @@ -31,7 +31,7 @@ func (narp *NodeARP) Demux(EtherFrame []byte, arpOff int) error { } afrm.ValidateSize(&narp.vld) if narp.vld.HasError() { - slog.Error("invalid-ARP", slog.String("err", narp.vld.Err().Error())) + slog.Error("invalid-ARP", slog.String("err", narp.vld.ErrPop().Error())) return nil } return narp.handler.Recv(EtherFrame[arpOff:]) diff --git a/internet/node-tcplistener.go b/internet/node-tcplistener.go index 58e85f9..b8608f1 100644 --- a/internet/node-tcplistener.go +++ b/internet/node-tcplistener.go @@ -65,9 +65,36 @@ func (listener *NodeTCPListener) Reset(port uint16, pool tcpPool) error { return nil } +func (listener *NodeTCPListener) NumberOfReadyToAccept() (nready int) { + if listener.isClosed() { + return 0 + } + for _, conn := range listener.ready { + if conn == nil { + continue + } + nready++ + } + return nready +} + +func (listener *NodeTCPListener) TryAccept() (*tcp.Conn, error) { + if listener.isClosed() { + return nil, net.ErrClosed + } + for i, conn := range listener.ready { + if conn == nil { + continue + } + listener.accepted = append(listener.accepted, conn) + listener.ready[i] = nil // discard from ready. + return conn, nil + } + return nil, errors.New("no conns available") +} + func (listener *NodeTCPListener) AcceptRaw() (*tcp.Conn, error) { connid := listener.connID - for { if listener.isClosed() || connid != listener.connID { return nil, net.ErrClosed diff --git a/internet/pcap/capture.go b/internet/pcap/capture.go index 8eab9be..df179a3 100644 --- a/internet/pcap/capture.go +++ b/internet/pcap/capture.go @@ -34,7 +34,7 @@ func (pc *PacketBreakdown) CaptureEthernet(dst []Frame, pkt []byte, bitOffset in } efrm.ValidateSize(pc.validator()) if pc.validator().HasError() { - return dst, pc.validator().Err() + return dst, pc.validator().ErrPop() } finfo := Frame{ @@ -61,6 +61,8 @@ func (pc *PacketBreakdown) CaptureEthernet(dst []Frame, pkt []byte, bitOffset in dst, err = pc.CaptureARP(dst, pkt, end) case ethernet.TypeIPv4: dst, err = pc.CaptureIPv4(dst, pkt, end) + case ethernet.TypeIPv6: + dst, err = pc.CaptureIPv6(dst, pkt, end) default: dst = append(dst, remainingFrameInfo(etype, FieldClassPayload, end, octet*len(pkt))) } @@ -77,7 +79,7 @@ func (pc *PacketBreakdown) CaptureARP(dst []Frame, pkt []byte, bitOffset int) ([ } afrm.ValidateSize(pc.validator()) if pc.validator().HasError() { - return dst, pc.validator().Err() + return dst, pc.validator().ErrPop() } finfo := Frame{ @@ -129,7 +131,7 @@ func (pc *PacketBreakdown) CaptureIPv6(dst []Frame, pkt []byte, bitOffset int) ( } ifrm6.ValidateSize(pc.validator()) if pc.validator().HasError() { - return dst, pc.validator().Err() + return dst, pc.validator().ErrPop() } finfo := Frame{ Protocol: ethernet.TypeIPv6, @@ -138,7 +140,7 @@ func (pc *PacketBreakdown) CaptureIPv6(dst []Frame, pkt []byte, bitOffset int) ( finfo.Fields = append(finfo.Fields, baseIPv6Fields[:]...) dst = append(dst, finfo) proto := ifrm6.NextHeader() - end := 40 * octet + end := bitOffset + 40*octet return pc.captureIPProto(proto, dst, pkt, end) } @@ -152,7 +154,7 @@ func (pc *PacketBreakdown) CaptureIPv4(dst []Frame, pkt []byte, bitOffset int) ( } ifrm4.ValidateSize(pc.validator()) if pc.validator().HasError() { - return dst, pc.validator().Err() + return dst, pc.validator().ErrPop() } finfo := Frame{ Protocol: ethernet.TypeIPv4, @@ -195,7 +197,7 @@ func (pc *PacketBreakdown) CaptureTCP(dst []Frame, pkt []byte, bitOffset int) ([ } tfrm.ValidateSize(pc.validator()) if pc.validator().HasError() { - return dst, pc.validator().Err() + return dst, pc.validator().ErrPop() } end := bitOffset + octet*tfrm.HeaderLength() finfo := Frame{ @@ -232,7 +234,7 @@ func (pc *PacketBreakdown) CaptureUDP(dst []Frame, pkt []byte, bitOffset int) ([ } ufrm.ValidateSize(pc.validator()) if pc.validator().HasError() { - return dst, pc.validator().Err() + return dst, pc.validator().ErrPop() } finfo := Frame{ Protocol: lneto.IPProtoUDP, diff --git a/internet/stack-ethernet.go b/internet/stack-ethernet.go index 8e42205..51802e3 100644 --- a/internet/stack-ethernet.go +++ b/internet/stack-ethernet.go @@ -77,7 +77,7 @@ func (ls *StackEthernet) Demux(carrierData []byte, frameOffset int) (err error) } efrm.ValidateSize(&vld) if vld.HasError() { - return vld.Err() + return vld.ErrPop() } for i := range ls.handlers { diff --git a/internet/stack-ip.go b/internet/stack-ip.go index 46e16c0..4d5d420 100644 --- a/internet/stack-ip.go +++ b/internet/stack-ip.go @@ -78,7 +78,7 @@ func (sb *StackIP) Demux(carrierData []byte, offset int) error { { sb.validator.ResetErr() ifrm.ValidateExceptCRC(&sb.validator) - if err = sb.validator.Err(); err != nil { + if err = sb.validator.ErrPop(); err != nil { return err } gotCRC := ifrm.CRC() diff --git a/ipv4/frame_test.go b/ipv4/frame_test.go index 88f1d25..d13503a 100644 --- a/ipv4/frame_test.go +++ b/ipv4/frame_test.go @@ -45,8 +45,8 @@ func TestFrame(t *testing.T) { wantDst := *dst ifrm.ValidateExceptCRC(v) ifrm.ValidateSize(v) - if v.Err() != nil { - t.Error(v.Err()) + if v.ErrPop() != nil { + t.Error(v.ErrPop()) } // OPTION+PAYLOAD VALIDATION: diff --git a/lneto_test.go b/lneto_test.go index a25a0ae..c0a9e8f 100644 --- a/lneto_test.go +++ b/lneto_test.go @@ -137,7 +137,7 @@ func TestIPv4TCPChecksum(t *testing.T) { ifrm.ValidateSize(&vld) tfrm, _ := tcp.NewFrame(ifrm.Payload()) tfrm.ValidateExceptCRC(&vld) - if err := vld.Err(); err != nil { + if err := vld.ErrPop(); err != nil { t.Fatal(err) } wantCRC := ifrm.CRC() diff --git a/tcp/handler.go b/tcp/handler.go index 806bf7b..01bcb5e 100644 --- a/tcp/handler.go +++ b/tcp/handler.go @@ -144,7 +144,7 @@ func (h *Handler) Recv(incomingPacket []byte) error { return err } tfrm.ValidateExceptCRC(&h.validator) - err = h.validator.Err() + err = h.validator.ErrPop() if err != nil { return err } diff --git a/tcp/tcp_test.go b/tcp/tcp_test.go index 9c59d91..c0937c3 100644 --- a/tcp/tcp_test.go +++ b/tcp/tcp_test.go @@ -596,8 +596,8 @@ func parseSegment(t *testing.T, b []byte) (tcp.Segment, []byte) { t.Fatalf("not IPv4") } efrm.ValidateSize(&vld) - if err := vld.Err(); err != nil { - t.Fatal(vld.Err()) + if err := vld.ErrPop(); err != nil { + t.Fatal(vld.ErrPop()) } ifrm, err := ipv4.NewFrame(efrm.Payload()) if err != nil { @@ -611,8 +611,8 @@ func parseSegment(t *testing.T, b []byte) (tcp.Segment, []byte) { t.Fatal("invalid IP version", v) } ifrm.ValidateSize(&vld) - if err := vld.Err(); err != nil { - t.Fatal(vld.Err()) + if err := vld.ErrPop(); err != nil { + t.Fatal(vld.ErrPop()) } ipl := ifrm.Payload() @@ -621,7 +621,7 @@ func parseSegment(t *testing.T, b []byte) (tcp.Segment, []byte) { t.Fatal(err) } tfrm.ValidateSize(&vld) - if err := vld.Err(); err != nil { + if err := vld.ErrPop(); err != nil { t.Fatal(err) } _ = tfrm.String() diff --git a/tcp/txqueue.go b/tcp/txqueue.go index 1bfe450..9463773 100644 --- a/tcp/txqueue.go +++ b/tcp/txqueue.go @@ -50,6 +50,7 @@ type ringidx struct { // Reset resets the RingTx's internal state to use buf as the main ring buffer and creates or reuses // the packet ring buffer. func (rtx *ringTx) Reset(buf []byte, maxqueuedPackets int, seq Value) error { + buf = buf[:len(buf):len(buf)] // safely omit capacity section. if maxqueuedPackets <= 0 { return errors.New("queued packets <=0") } else if len(buf) < minBufferSize || len(buf) < maxqueuedPackets { diff --git a/validation.go b/validation.go index 5b5412d..11fca69 100644 --- a/validation.go +++ b/validation.go @@ -39,13 +39,16 @@ func (v *Validator) HasError() bool { return len(v.accum) != 0 } -func (v *Validator) Err() error { +// ErrPop returns the error(s) accumulated in the validator and clears them. +func (v *Validator) ErrPop() (err error) { if len(v.accum) == 1 { - return v.accum[0] - } else if len(v.accum) == 0 { - return nil + err = v.accum[0] + v.ResetErr() + } else if len(v.accum) > 0 { + err = errors.Join(v.accum...) + v.ResetErr() } - return errors.Join(v.accum...) + return err } func (v *Validator) gotErr(err error) {