tap program output much more readable

This commit is contained in:
soypat
2025-06-04 01:13:26 -03:00
parent 589c9d3013
commit bc14ed395d
4 changed files with 113 additions and 23 deletions
+9
View File
@@ -12,6 +12,7 @@ import (
"time"
"github.com/soypat/lneto/internal/ltesto"
"github.com/soypat/lneto/internet/pcap"
)
func main() {
@@ -39,6 +40,14 @@ func run() error {
return err
}
defer sv.Close()
var cap pcap.PacketBreakdown
sv.OnTransfer(func(channel int, pkt []byte) {
captime := time.Now()
frames, err := cap.CaptureEthernet(nil, pkt, 0)
if err == nil {
fmt.Println(channel, captime.Format("15:04:05.000"), frames)
}
})
hwaddr, err := sv.HardwareAddress6()
if err != nil {
return err
+19 -10
View File
@@ -5,7 +5,6 @@ import (
"encoding/json"
"errors"
"fmt"
"log/slog"
"net"
"net/http"
"net/netip"
@@ -153,6 +152,7 @@ type HTTPTapServer struct {
stack stack
tap *internal.Tap
buf []byte
onTx func(channel int, pkt []byte)
tapfailed bool
}
@@ -162,6 +162,10 @@ type tapInfo struct {
HardwareAddr string
}
func (sv *HTTPTapServer) OnTransfer(cb func(channel int, pkt []byte)) {
sv.onTx = cb
}
func NewHTTPTapServer(iface string, ip netip.Prefix, mtu, queueOut, queueIn int) (*HTTPTapServer, error) {
if mtu < minMTU {
return nil, errors.New("too small MTU")
@@ -176,15 +180,23 @@ func NewHTTPTapServer(iface string, ip netip.Prefix, mtu, queueOut, queueIn int)
in: make(chan []byte, queueIn),
}
sv := http.NewServeMux()
taps := &HTTPTapServer{
router: sv,
stack: s,
tap: tap,
buf: make([]byte, mtu),
}
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 {
if taps.onTx != nil {
taps.onTx(1, data)
}
select {
case s.out <- data:
slog.Info("http-send", slog.Int("plen", len(data)))
default:
http.Error(w, "outgoing packet queue full", http.StatusInternalServerError)
}
@@ -194,7 +206,6 @@ func NewHTTPTapServer(iface string, ip netip.Prefix, mtu, queueOut, queueIn int)
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.
}
@@ -211,13 +222,8 @@ func NewHTTPTapServer(iface string, ip netip.Prefix, mtu, queueOut, queueIn int)
}
json.NewEncoder(w).Encode(info)
})
taps := HTTPTapServer{
router: sv,
stack: s,
tap: tap,
buf: make([]byte, mtu),
}
return &taps, nil
return taps, nil
}
func (sv *HTTPTapServer) HardwareAddress6() (hwaddr [6]byte, err error) {
@@ -262,6 +268,9 @@ func (sv *HTTPTapServer) readTap() (int, error) {
sv.tapfailed = true
return n, err
} else if n > 0 {
if sv.onTx != nil {
sv.onTx(0, buf[:n])
}
err = sv.stack.recv(buf[:n])
if err != nil {
return n, err
+50 -13
View File
@@ -1,8 +1,10 @@
package pcap
//go:generate stringer -type=FieldClass -linecomment -output stringers.go .
import (
"encoding/binary"
"errors"
"fmt"
"math"
"github.com/soypat/lneto"
@@ -56,7 +58,7 @@ func (pc *PacketBreakdown) CaptureEthernet(dst []Frame, pkt []byte, bitOffset in
case ethernet.TypeIPv4:
dst, err = pc.CaptureIPv4(dst, pkt, end)
default:
dst = append(dst, remainingFrameInfo(nil, FieldClassPayload, end, octet*len(pkt)))
dst = append(dst, remainingFrameInfo(etype, FieldClassPayload, end, octet*len(pkt)))
}
return dst, err
}
@@ -131,11 +133,13 @@ func (pc *PacketBreakdown) CaptureIPv4(dst []Frame, pkt []byte, bitOffset int) (
}
finfo.Fields = append(finfo.Fields, baseIPv4Fields[:]...)
options := ifrm4.Options()
finfo.Fields = append(finfo.Fields, FrameField{
Class: FieldClassOptions,
FrameBitOffset: 20 * octet,
BitLength: octet * len(options),
})
if len(options) > 0 {
finfo.Fields = append(finfo.Fields, FrameField{
Class: FieldClassOptions,
FrameBitOffset: 20 * octet,
BitLength: octet * len(options),
})
}
proto := ifrm4.Protocol()
dst = append(dst, finfo)
end := bitOffset + octet*ifrm4.HeaderLength()
@@ -167,11 +171,13 @@ func (pc *PacketBreakdown) CaptureTCP(dst []Frame, pkt []byte, bitOffset int) ([
}
finfo.Fields = append(finfo.Fields, baseTCPFields[:]...)
options := tfrm.Options()
finfo.Fields = append(finfo.Fields, FrameField{
Class: FieldClassOptions,
FrameBitOffset: 20 * octet,
BitLength: octet * len(options),
})
if len(options) > 0 {
finfo.Fields = append(finfo.Fields, FrameField{
Class: FieldClassOptions,
FrameBitOffset: 20 * octet,
BitLength: octet * len(options),
})
}
dst = append(dst, finfo)
payload := tfrm.Payload()
if len(payload) > 0 {
@@ -274,7 +280,7 @@ func (frm Frame) FieldByClass(c FieldClass) (int, error) {
}
// FieldAsUint evaluates the field as a 64-bit integer.
func (frm *Frame) FieldAsUint(fieldIdx int, pkt []byte) (uint64, error) {
func (frm Frame) FieldAsUint(fieldIdx int, pkt []byte) (uint64, error) {
const badUint64 = math.MaxUint64
if fieldIdx < 0 || fieldIdx >= len(frm.Fields) {
return badUint64, errors.New("invalid field index")
@@ -293,7 +299,7 @@ func (frm *Frame) FieldAsUint(fieldIdx int, pkt []byte) (uint64, error) {
return v, nil
}
func (frm *Frame) AppendField(dst []byte, fieldIdx int, pkt []byte) ([]byte, error) {
func (frm Frame) AppendField(dst []byte, fieldIdx int, pkt []byte) ([]byte, error) {
if fieldIdx < 0 || fieldIdx >= len(frm.Fields) {
return dst, errors.New("invalid field index")
}
@@ -349,6 +355,37 @@ func (frm *Frame) AppendField(dst []byte, fieldIdx int, pkt []byte) ([]byte, err
return dst, nil
}
func (frm Frame) String() string {
iopt, err := frm.FieldByClass(FieldClassOptions)
hasOpts := ""
if err == nil {
hasOpts = fmt.Sprintf(" optlen=%d", (frm.Fields[iopt].BitLength+7)/8)
}
bitlen := frm.LenBits()
if bitlen%8 == 0 {
return fmt.Sprintf("%s len=%d%s", frm.Protocol, bitlen/8, hasOpts)
}
return fmt.Sprintf("%s bits=%d%s", frm.Protocol, bitlen, hasOpts)
}
func (frm Frame) LenBits() (totalBitlen int) {
for i := range frm.Fields {
totalBitlen = max(totalBitlen, frm.Fields[i].FrameBitOffset+frm.Fields[i].BitLength)
}
return totalBitlen
}
func (ff FrameField) String() string {
if ff.Class == FieldClassPayload {
return fmt.Sprintf("Payload len=%d", ff.BitLength/8)
}
if ff.Name != "" {
return fmt.Sprintf("%s (%s)", ff.Name, ff.Class.String())
}
return ff.Class.String()
}
type FieldClass uint8
const (
+35
View File
@@ -0,0 +1,35 @@
// Code generated by "stringer -type=FieldClass -linecomment -output stringers.go ."; DO NOT EDIT.
package pcap
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[fieldClassUndefined-0]
_ = x[FieldClassSrc-1]
_ = x[FieldClassDst-2]
_ = x[FieldClassProto-3]
_ = x[FieldClassVersion-4]
_ = x[FieldClassType-5]
_ = x[FieldClassSize-6]
_ = x[FieldClassFlags-7]
_ = x[FieldClassID-8]
_ = x[FieldClassChecksum-9]
_ = x[FieldClassOptions-10]
_ = x[FieldClassPayload-11]
_ = x[FieldClassText-12]
}
const _FieldClass_name = "undefinedsourcedestinationprotocolversiontypefield sizeflagsidentificationchecksumoptionspayloadtext"
var _FieldClass_index = [...]uint8{0, 9, 15, 26, 34, 41, 45, 55, 60, 74, 82, 89, 96, 100}
func (i FieldClass) String() string {
if i >= FieldClass(len(_FieldClass_index)-1) {
return "FieldClass(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _FieldClass_name[_FieldClass_index[i]:_FieldClass_index[i+1]]
}