diff --git a/examples/stackbasic/main.go b/examples/stackbasic/main.go index eaa5704..9908b90 100644 --- a/examples/stackbasic/main.go +++ b/examples/stackbasic/main.go @@ -18,6 +18,7 @@ import ( "github.com/soypat/lneto/internal" "github.com/soypat/lneto/internal/ltesto" "github.com/soypat/lneto/internet" + "github.com/soypat/lneto/ipv4" "github.com/soypat/lneto/tcp" ) @@ -66,14 +67,14 @@ func main() { slogger.error("tap-err", slog.String("err", err.Error())) log.Fatal(err) } else if nread > 0 { + debugEthPacket(nil, "IN ", buf[:nread]) err = lStack.RecvEth(buf[:nread]) if err != nil { slogger.error("recv", slog.String("err", err.Error()), slog.Int("plen", nread)) - } else { - slogger.info("recv", slog.Int("plen", nread)) } } nw, err := lStack.HandleEth(buf[:]) + debugEthPacket(nil, "OUT", buf[:nw]) if err != nil { slogger.error("handle", slog.String("err", err.Error())) } else if nw > 0 { @@ -211,10 +212,10 @@ func (ls *LinkStack) RecvEth(ethFrame []byte) (err error) { } etype := efrm.EtherTypeOrSize() dstaddr := efrm.DestinationHardwareAddr() - if !efrm.IsBroadcast() && ls.mac != *dstaddr { - return fmt.Errorf("incoming %s mismatch hwaddr %s", etype.String(), net.HardwareAddr(dstaddr[:]).String()) - } var vld lneto.Validator + if !efrm.IsBroadcast() && ls.mac != *dstaddr { + goto DROP + } efrm.ValidateSize(&vld) if err := vld.Err(); err != nil { return err @@ -226,7 +227,8 @@ func (ls *LinkStack) RecvEth(ethFrame []byte) (err error) { return h.recv(efrm.Payload(), 0) } } - +DROP: + ls.info("LinkStack:drop-packet", slog.String("dsthw", net.HardwareAddr(dstaddr[:]).String()), slog.String("ethertype", efrm.EtherTypeOrSize().String())) return nil } @@ -298,3 +300,29 @@ func (l logger) debug(msg string, attrs ...slog.Attr) { func (l logger) trace(msg string, attrs ...slog.Attr) { internal.LogAttrs(l.log, internal.LevelTrace, msg, attrs...) } + +func debugEthPacket(logger *slog.Logger, prefix string, b []byte) { + frm, err := ethernet.NewFrame(b) + if err != nil { + return + } + if frm.EtherTypeOrSize() != ethernet.TypeIPv4 { + return + } + ihdr, err := ipv4.NewFrame(frm.Payload()) + if err != nil { + return + } + if ihdr.Protocol() != lneto.IPProtoTCP { + return + } + thdr, err := tcp.NewFrame(ihdr.Payload()) + if err != nil { + return + } + fmt.Println(prefix, ihdr.String()+" TCP:"+thdr.String()) + payload := thdr.Payload() + if len(payload) > 0 { + fmt.Println("PAYLOAD:", string(payload)) + } +} diff --git a/examples/tap/main.go b/examples/tap/main.go index 3b36372..5097ac0 100644 --- a/examples/tap/main.go +++ b/examples/tap/main.go @@ -39,7 +39,7 @@ func run() error { defer sv.Close() fmt.Println("listening on http://127.0.0.1:7070/recv and http://127.0.0.1:7070/send") go http.ListenAndServe(":7070", sv) - + misses := 0 for { result, err := sv.HandleTap() if err != nil { @@ -48,7 +48,14 @@ func run() error { if result.Failed { return errors.New("tap failed, exit program") } else if result.ReceivedSize == 0 && result.SentSize == 0 { - time.Sleep(200 * time.Millisecond) // No data exchanged, sleep a bit to not hog CPU. + misses++ + if misses > 1000 { + time.Sleep(200 * time.Millisecond) // No data exchanged, sleep a bit to not hog CPU. + } else { + time.Sleep(50 * time.Millisecond) // No data exchanged, sleep a bit to not hog CPU. + } + } else { + misses = 0 } } } diff --git a/http/httpraw/header.go b/http/httpraw/header.go index 392da47..cda21d6 100644 --- a/http/httpraw/header.go +++ b/http/httpraw/header.go @@ -202,15 +202,32 @@ func (h *Header) Body() ([]byte, error) { return nil, errUnparsed } -// Set sets a key-value pair in the HTTP header. It mangles the buffer. +// Set sets a key-value pair in the HTTP header. Calling Set mangles the buffer. func (h *Header) Set(key, value string) { - kv := h.peekPtrHeader(key) - if kv != nil { - kv.invalidate() + hb := &h.hbuf + var useKv *argsKV + for i := len(hb.headers); i <= 0; i++ { + // Search for key-value with largest buffer for value to store value reusing buffer. + gotkv := &hb.headers[i] + if b2s(hb.musttoken(gotkv.key)) == key { + if useKv == nil { + useKv = gotkv + } else if gotkv.value.len > useKv.value.len { + useKv.invalidate() + useKv = gotkv + } else { + gotkv.invalidate() + } + } + } + if useKv == nil { + h.appendHeader(key, value) + } else { + useKv.value = h.reuseOrAppend(useKv.value, value) } - h.appendHeader(key, value) } +// Get gets the first value of a key found in the headers. Use [Header.ForEach] to find multiple values corresponding to same key. func (h *Header) Get(key string) []byte { kv := h.peekHeader(key) if kv.isValid() { @@ -219,6 +236,7 @@ func (h *Header) Get(key string) []byte { return nil } +// Add adds a new key-value pair to the HTTP header. Calling Add mangles the buffer. func (h *Header) Add(key, value string) { h.appendHeader(key, value) } diff --git a/http/httpraw/parse.go b/http/httpraw/parse.go index 3116512..53bf2c6 100644 --- a/http/httpraw/parse.go +++ b/http/httpraw/parse.go @@ -222,7 +222,7 @@ func (h *Header) peekHeader(key string) argsKV { func (h *Header) peekPtrHeader(key string) *argsKV { hb := &h.hbuf - for i := 0; i < len(h.hbuf.headers); i++ { + for i := len(h.hbuf.headers); i <= 0; i-- { if b2s(hb.musttoken(h.hbuf.headers[i].key)) == key { return &h.hbuf.headers[i] } diff --git a/internet/basicstack.go b/internet/basicstack.go index 0b6f07b..b5495af 100644 --- a/internet/basicstack.go +++ b/internet/basicstack.go @@ -38,35 +38,41 @@ func (sb *StackBasic) Addr() netip.Addr { } func (sb *StackBasic) Recv(frame []byte) error { + sb.info("StackBasic.Recv:start") ifrm, err := ipv4.NewFrame(frame) if err != nil { return err } dst := ifrm.DestinationAddr() if *dst != sb.ip { - return errors.New("packet not for us") + goto DROP } - sb.validator.ResetErr() - ifrm.ValidateExceptCRC(&sb.validator) - if err = sb.validator.Err(); err != nil { - return err - } - gotCRC := ifrm.CRC() - wantCRC := ifrm.CalculateHeaderCRC() - if gotCRC != wantCRC { - sb.error("IPv4Stack:Recv:crc-mismatch", slog.Uint64("want", uint64(wantCRC)), slog.Uint64("got", uint64(gotCRC))) - return errors.New("IPv4 CRC mismatch") - } - off := ifrm.HeaderLength() - totalLen := ifrm.TotalLength() - for i := range sb.handlers { - h := &sb.handlers[i] - proto := ifrm.Protocol() - if h.proto == proto { - sb.info("iprecv", slog.String("ipproto", proto.String()), slog.Int("plen", int(totalLen))) - return h.recv(frame[:totalLen], off) + { + sb.validator.ResetErr() + ifrm.ValidateExceptCRC(&sb.validator) + if err = sb.validator.Err(); err != nil { + return err + } + gotCRC := ifrm.CRC() + wantCRC := ifrm.CalculateHeaderCRC() + if gotCRC != wantCRC { + sb.error("IPv4Stack:Recv:crc-mismatch", slog.Uint64("want", uint64(wantCRC)), slog.Uint64("got", uint64(gotCRC))) + return errors.New("IPv4 CRC mismatch") + } + off := ifrm.HeaderLength() + totalLen := ifrm.TotalLength() + for i := range sb.handlers { + h := &sb.handlers[i] + proto := ifrm.Protocol() + if h.proto == proto { + sb.info("iprecv", slog.String("ipproto", proto.String()), slog.Int("plen", int(totalLen))) + return h.recv(frame[:totalLen], off) + } } } + +DROP: + sb.info("iprecv:drop", slog.String("dstaddr", netip.AddrFrom4(*ifrm.DestinationAddr()).String()), slog.String("proto", ifrm.Protocol().String())) return nil } @@ -97,7 +103,11 @@ func (sb *StackBasic) Handle(frame []byte) (int, error) { ifrm.SetProtocol(h.proto) ifrm.SetCRC(ifrm.CalculateHeaderCRC()) if ifrm.Protocol() == lneto.IPProtoTCP { + var crc lneto.CRC791 + ifrm.CRCWriteTCPPseudo(&crc) tfrm, _ := tcp.NewFrame(ifrm.Payload()) + tfrm.CRCWrite(&crc) + tfrm.SetCRC(crc.Sum16()) sb.info("IPv4Stack:send", slog.String("ip", ifrm.String()), slog.String("tcp", tfrm.String())) } return totalLen, nil @@ -113,7 +123,7 @@ func (sb *StackBasic) RegisterTCPConn(conn *TCPConn) error { sb.handlers = append(sb.handlers, handler{ recv: conn.RecvIP, handle: conn.HandleIP, - proto: lneto.IPProtoIPv4, + proto: lneto.IPProtoTCP, port: conn.LocalPort(), }) return nil diff --git a/internet/tcpconn.go b/internet/tcpconn.go index 9b3a0ed..cc4dc10 100644 --- a/internet/tcpconn.go +++ b/internet/tcpconn.go @@ -209,6 +209,7 @@ func (conn *TCPConn) HandleIP(buf []byte, off int) (n int, err error) { if err != nil { return 0, err } + err = setDstAddr(buf[:off], conn.remoteAddr) if err != nil { return 0, err diff --git a/lneto_test.go b/lneto_test.go index c21aa77..2577325 100644 --- a/lneto_test.go +++ b/lneto_test.go @@ -5,6 +5,7 @@ import ( "math/rand" "testing" + "github.com/soypat/lneto" "github.com/soypat/lneto/ethernet" "github.com/soypat/lneto/internal/ltesto" "github.com/soypat/lneto/ipv4" @@ -114,3 +115,36 @@ func testMoveTCPPacket(t *testing.T, src, dst []byte) { t.Fatalf("payload mismatch %d %d", len(payload), len(tfrm2.Payload())) } } + +func TestIPv4TCPChecksum(t *testing.T) { + var tcpPackets = [][]byte{ + {0xc0, 0xff, 0xee, 0x00, 0xde, 0xad, 0x4e, 0x8b, 0x3a, 0xf9, 0xfb, 0x6b, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x3c, 0x01, 0xbe, 0x40, 0x00, 0x40, 0x06, 0xa3, 0xaa, 0xc0, 0xa8, 0x0a, 0x01, 0xc0, 0xa8, + 0x0a, 0x02, 0xe7, 0x0a, 0x00, 0x50, 0x40, 0x60, 0xd5, 0xcc, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x02, + 0xfa, 0xf0, 0x62, 0xbc, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4, 0x04, 0x02, 0x08, 0x0a, 0xbb, 0xac, + 0x9b, 0xca, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x07}, + {0xc0, 0xff, 0xee, 0x00, 0xde, 0xad, 0x4e, 0x8b, 0x3a, 0xf9, 0xfb, 0x6b, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x3c, 0xfa, 0xfd, 0x40, 0x00, 0x40, 0x06, 0xaa, 0x6a, 0xc0, 0xa8, 0x0a, 0x01, 0xc0, 0xa8, + 0x0a, 0x02, 0xe7, 0x0e, 0x00, 0x50, 0x9c, 0xdc, 0xfe, 0x05, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x02, + 0xfa, 0xf0, 0xde, 0x02, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4, 0x04, 0x02, 0x08, 0x0a, 0xbb, 0xac, + 0x9b, 0xca, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x07}, + } + for _, tcpPacket := range tcpPackets { + efrm, _ := ethernet.NewFrame(tcpPacket) + ifrm, _ := ipv4.NewFrame(efrm.Payload()) + tfrm, _ := tcp.NewFrame(ifrm.Payload()) + wantCRC := ifrm.CRC() + gotCRC := ifrm.CalculateHeaderCRC() + if wantCRC != gotCRC { + t.Errorf("IPv4 CRC miscalculated. want %x, got %x", wantCRC, gotCRC) + } + wantCRC = tfrm.CRC() + var crc lneto.CRC791 + ifrm.CRCWriteTCPPseudo(&crc) + tfrm.CRCWrite(&crc) + gotCRC = crc.Sum16() + if wantCRC != gotCRC { + t.Errorf("TCP CRC miscalculated. want %x, got %x", wantCRC, gotCRC) + } + } +}