mirror of
https://github.com/soypat/lneto.git
synced 2026-08-15 12:23:44 +00:00
tap program output much more readable
This commit is contained in:
@@ -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
@@ -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
@@ -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 (
|
||||
|
||||
@@ -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]]
|
||||
}
|
||||
Reference in New Issue
Block a user