diff --git a/examples/stack/main.go b/examples/stack/main.go index f33c327..305670f 100644 --- a/examples/stack/main.go +++ b/examples/stack/main.go @@ -1,13 +1,19 @@ package main import ( + "bytes" + "encoding/json" "errors" "fmt" "io" "log" "log/slog" "net" + "net/http" "net/netip" + "net/url" + "os" + "time" "github.com/soypat/lneto/arp" "github.com/soypat/lneto/ethernet" @@ -23,6 +29,7 @@ const ( iface = "192.168.10.1/24" stackIP = "192.168.10.2" stackPort = 80 + iss = 100 ) var stackHWAddr = [6]byte{0xc0, 0xff, 0xee, 0x00, 0xde, 0xad} @@ -40,21 +47,27 @@ func main() { log.Fatal(err) } - tap, err := internal.NewTap("tap0", iface) - if err != nil { - log.Fatal(err) - } - const port, iss = 80, 300 - err = handler.OpenListen(port, iss) - if err != nil { - log.Fatal(err) - } + logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{ + Level: slog.LevelDebug, + })) + handler.SetLoggers(logger, logger) + err = handler.OpenListen(addrPort.Port(), iss) + if err != nil { + log.Fatal(err) + } + tap := NewHTTPTap("http://127.0.0.1:7070") + // tap, err := internal.NewTap("tap0", iface) + if err != nil { + log.Fatal(err) + } defer tap.Close() + fmt.Println("hosting server at ", addrPort.String()) var buf [mtu]byte for { n, err := tap.Read(buf[:]) if err != nil { + slogger.error("tap-err", slog.String("err", err.Error())) log.Fatal(err) } else if n > 0 { err = lStack.RecvEth(buf[:n]) @@ -63,6 +76,8 @@ func main() { } else { slogger.info("recv", slog.Int("plen", n)) } + } else if n == 0 { + time.Sleep(250 * time.Millisecond) } n, err = lStack.HandleEth(buf[:]) if err != nil { @@ -519,3 +534,56 @@ func addHandler(handlers []handler, h Handler, remoteAddr []byte, lport uint16) hh.lport = lport return handlers } + +func NewHTTPTap(baseURL string) *HTTPTap { + var h HTTPTap + h.sendurl = baseURL + "/send" + h.recvurl = baseURL + "/recv" + _, err := url.Parse(h.sendurl) + if err != nil { + panic(err) + } + var data [2048]byte + var n int = -1 + for n != 0 { + n, _ = h.Read(data[:]) // Empty remote data. + } + return &h +} + +type HTTPTap struct { + c http.Client + recvurl string + sendurl string +} + +func (h *HTTPTap) Read(b []byte) (int, error) { + resp, err := h.c.Get(h.recvurl) + if err != nil { + return 0, err + } else if resp.StatusCode != 200 { + return 0, errors.New(resp.Status + " for " + h.recvurl) + } + var data []byte + err = json.NewDecoder(resp.Body).Decode(&data) + if err != nil { + return 0, err + } else if len(b) < len(data) { + return 0, fmt.Errorf("got too large packet %d for buffer %d", len(data), len(b)) + } + copy(b, data) + return len(data), nil +} + +func (h *HTTPTap) Write(b []byte) (int, error) { + data, _ := json.Marshal(b) + resp, err := h.c.Post(h.sendurl, "application/json", bytes.NewReader(data)) + if err != nil { + return 0, err + } else if resp.StatusCode != 200 { + return 0, errors.New(resp.Status + " for " + h.sendurl) + } + return len(b), nil +} + +func (h *HTTPTap) Close() error { return nil } diff --git a/examples/tap/main.go b/examples/tap/main.go index e5a7c68..2be079a 100644 --- a/examples/tap/main.go +++ b/examples/tap/main.go @@ -1,11 +1,13 @@ package main import ( + "encoding/json" + "errors" "fmt" "log" + "log/slog" + "net/http" "net/netip" - "os" - "time" "github.com/soypat/lneto/internal" ) @@ -20,23 +22,103 @@ func main() { } func run() error { - ip := netip.MustParsePrefix("192.168.10.1/24") - tap, err := internal.NewTap("tap0", ip) + var ( + flagNet = "192.168.10.1/24" + flagiface = "tap0" + flagMTU = 1500 + ) + slogger := slog.Default() + ip, err := netip.ParsePrefix(flagNet) + if err != nil { + return err + } + tap, err := internal.NewTap(flagiface, ip) if err != nil { return err } defer tap.Close() - var buf [2048]byte - pkt := 0 + s := stack{ + out: make(chan []byte, 256), + in: make(chan []byte, 2048), + } + sv := http.NewServeMux() + sv.HandleFunc("/send", func(w http.ResponseWriter, r *http.Request) { + var data []byte + err := json.NewDecoder(r.Body).Decode(&data) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + } else { + select { + case s.out <- data: + slog.Info("http-send", slog.Int("plen", len(data))) + default: + http.Error(w, "outgoing packet queue full", http.StatusInternalServerError) + } + } + }) + sv.HandleFunc("/recv", func(w http.ResponseWriter, r *http.Request) { + select { + case data := <-s.in: + json.NewEncoder(w).Encode(data) + slog.Info("http-recv", slog.Int("plen", len(data))) + default: + json.NewEncoder(w).Encode("") // send empty string. + } + }) + fmt.Println("listening on http://127.0.0.1:7070/recv and http://127.0.0.1:7070/send") + go http.ListenAndServe(":7070", sv) + + buf := make([]byte, flagMTU) for { n, err := tap.Read(buf[:]) if err != nil { - return err - } else if n == 0 { - time.Sleep(250 * time.Millisecond) - continue + log.Fatal(err) + } else if n > 0 { + err = s.recv(buf[:n]) + if err != nil { + slogger.Error("recv", slog.String("err", err.Error()), slog.Int("plen", n)) + } else { + slogger.Info("recv", slog.Int("plen", n)) + } + } + n, err = s.handle(buf[:]) + if err != nil { + slogger.Error("handle", slog.String("err", err.Error())) + } else if n > 0 { + _, err = tap.Write(buf[:n]) + if err != nil { + log.Fatal(err) + } else { + slogger.Info("write", slog.Int("plen", n)) + } } - pkt++ - fmt.Fprintf(os.Stdout, "rx%d (%d): %q\n\n", pkt, n, buf[:n]) } } + +type stack struct { + out chan []byte + in chan []byte +} + +func (s *stack) recv(b []byte) (err error) { + bcopy := append([]byte{}, b...) +RETRY: + select { + case s.in <- bcopy: + default: + err = errors.New("receive queue packet full, dropping packet") + <-s.in + goto RETRY + } + return err +} + +func (s *stack) handle(b []byte) (n int, _ error) { + select { + case incoming := <-s.out: + n = copy(b, incoming) + default: + // pass if no data available. + } + return n, nil +} diff --git a/tcp/handler.go b/tcp/handler.go index fb294c1..0e8f271 100644 --- a/tcp/handler.go +++ b/tcp/handler.go @@ -33,6 +33,11 @@ type Handler struct { closing bool } +func (h *Handler) SetLoggers(handler, scb *slog.Logger) { + h.logger.log = handler + h.scb.logger.log = scb +} + func (h *Handler) State() State { return h.scb.State() } func (h *Handler) SetBuffers(txbuf, rxbuf []byte, packets int) error {