implement sophisticated TAP interface to be able to debug stack on linux

This commit is contained in:
soypat
2025-02-20 18:51:31 -03:00
parent f86bb6f01a
commit c9d1c4f239
3 changed files with 176 additions and 21 deletions
+77 -9
View File
@@ -1,13 +1,19 @@
package main package main
import ( import (
"bytes"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"io" "io"
"log" "log"
"log/slog" "log/slog"
"net" "net"
"net/http"
"net/netip" "net/netip"
"net/url"
"os"
"time"
"github.com/soypat/lneto/arp" "github.com/soypat/lneto/arp"
"github.com/soypat/lneto/ethernet" "github.com/soypat/lneto/ethernet"
@@ -23,6 +29,7 @@ const (
iface = "192.168.10.1/24" iface = "192.168.10.1/24"
stackIP = "192.168.10.2" stackIP = "192.168.10.2"
stackPort = 80 stackPort = 80
iss = 100
) )
var stackHWAddr = [6]byte{0xc0, 0xff, 0xee, 0x00, 0xde, 0xad} var stackHWAddr = [6]byte{0xc0, 0xff, 0xee, 0x00, 0xde, 0xad}
@@ -40,21 +47,27 @@ func main() {
log.Fatal(err) log.Fatal(err)
} }
tap, err := internal.NewTap("tap0", iface) logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
if err != nil { Level: slog.LevelDebug,
log.Fatal(err) }))
} handler.SetLoggers(logger, logger)
const port, iss = 80, 300
err = handler.OpenListen(port, iss)
if err != nil {
log.Fatal(err)
}
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() defer tap.Close()
fmt.Println("hosting server at ", addrPort.String())
var buf [mtu]byte var buf [mtu]byte
for { for {
n, err := tap.Read(buf[:]) n, err := tap.Read(buf[:])
if err != nil { if err != nil {
slogger.error("tap-err", slog.String("err", err.Error()))
log.Fatal(err) log.Fatal(err)
} else if n > 0 { } else if n > 0 {
err = lStack.RecvEth(buf[:n]) err = lStack.RecvEth(buf[:n])
@@ -63,6 +76,8 @@ func main() {
} else { } else {
slogger.info("recv", slog.Int("plen", n)) slogger.info("recv", slog.Int("plen", n))
} }
} else if n == 0 {
time.Sleep(250 * time.Millisecond)
} }
n, err = lStack.HandleEth(buf[:]) n, err = lStack.HandleEth(buf[:])
if err != nil { if err != nil {
@@ -519,3 +534,56 @@ func addHandler(handlers []handler, h Handler, remoteAddr []byte, lport uint16)
hh.lport = lport hh.lport = lport
return handlers 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 }
+94 -12
View File
@@ -1,11 +1,13 @@
package main package main
import ( import (
"encoding/json"
"errors"
"fmt" "fmt"
"log" "log"
"log/slog"
"net/http"
"net/netip" "net/netip"
"os"
"time"
"github.com/soypat/lneto/internal" "github.com/soypat/lneto/internal"
) )
@@ -20,23 +22,103 @@ func main() {
} }
func run() error { func run() error {
ip := netip.MustParsePrefix("192.168.10.1/24") var (
tap, err := internal.NewTap("tap0", ip) 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 { if err != nil {
return err return err
} }
defer tap.Close() defer tap.Close()
var buf [2048]byte s := stack{
pkt := 0 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 { for {
n, err := tap.Read(buf[:]) n, err := tap.Read(buf[:])
if err != nil { if err != nil {
return err log.Fatal(err)
} else if n == 0 { } else if n > 0 {
time.Sleep(250 * time.Millisecond) err = s.recv(buf[:n])
continue 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
}
+5
View File
@@ -33,6 +33,11 @@ type Handler struct {
closing bool 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) State() State { return h.scb.State() }
func (h *Handler) SetBuffers(txbuf, rxbuf []byte, packets int) error { func (h *Handler) SetBuffers(txbuf, rxbuf []byte, packets int) error {