new: implemented can.dup and can.dump.inject to read a candump log file

This commit is contained in:
Simone Margaritelli 2024-08-26 15:12:09 +02:00
commit 7702207ee9
4 changed files with 108 additions and 14 deletions

1
.gitignore vendored
View file

@ -15,3 +15,4 @@ stage/
/snap
.idea
/cover.out
/can-data

View file

@ -2,6 +2,7 @@ package can
import (
"errors"
"fmt"
"net"
"github.com/bettercap/bettercap/v2/session"
@ -13,6 +14,8 @@ type CANModule struct {
session.SessionModule
deviceName string
dumpName string
dumpInject bool
transport string
filter string
filterExpr *bexpr.Evaluator
@ -30,6 +33,8 @@ func NewCanModule(s *session.Session) *CANModule {
filterExpr: nil,
transport: "can",
deviceName: "can0",
dumpName: "",
dumpInject: false,
}
mod.AddParam(session.NewStringParameter("can.device",
@ -37,6 +42,15 @@ func NewCanModule(s *session.Session) *CANModule {
"",
"CAN-bus device."))
mod.AddParam(session.NewStringParameter("can.dump",
mod.dumpName,
"",
"Load CAN traffic from this candump log file."))
mod.AddParam(session.NewBoolParameter("can.dump.inject",
fmt.Sprintf("%v", mod.dumpInject),
"Write CAN traffic read form the candump log file to the selected can.device."))
mod.AddParam(session.NewStringParameter("can.transport",
mod.transport,
"",
@ -53,18 +67,6 @@ func NewCanModule(s *session.Session) *CANModule {
return mod.Start()
}))
mod.AddHandler(session.NewModuleHandler("can.load_dbc PATH", "",
"Start CAN-bus discovery.",
func(args []string) error {
return mod.Start()
}))
mod.AddHandler(session.NewModuleHandler("can.dbc.load NAME", "can.dbc.load (.+)",
"Load a DBC file from the list of available ones or from disk.",
func(args []string) error {
return mod.dbcLoad(args[0])
}))
mod.AddHandler(session.NewModuleHandler("can.recon off", "",
"Stop CAN-bus discovery.",
func(args []string) error {
@ -84,6 +86,12 @@ func NewCanModule(s *session.Session) *CANModule {
return mod.Show()
}))
mod.AddHandler(session.NewModuleHandler("can.dbc.load NAME", "can.dbc.load (.+)",
"Load a DBC file from the list of available ones or from disk.",
func(args []string) error {
return mod.dbcLoad(args[0])
}))
mod.AddHandler(session.NewModuleHandler("can.inject FRAME_EXPRESSION", `(?i)^can\.inject\s+([a-fA-F0-9#R]+)$`,
"Parse FRAME_EXPRESSION as 'id#data' and inject it as a CAN frame.",
func(args []string) error {

View file

@ -0,0 +1,76 @@
package can
import (
"bufio"
"context"
"os"
"regexp"
"github.com/evilsocket/islazy/str"
"go.einride.tech/can"
)
// (1700623093.260875) can0 7E0#0322128C00000000
var dumpLineParser = regexp.MustCompile(`(?m)^\(([\d\.]+)\)\s+([^\s]+)\s+(.+)`)
type dumpEntry struct {
Time string
Device string
Frame string
}
func (mod *CANModule) startDumpReader() error {
mod.Info("loading CAN dump from %s ...", mod.dumpName)
file, err := os.Open(mod.dumpName)
if err != nil {
return err
}
defer file.Close()
entries := make([]dumpEntry, 0)
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := str.Trim(scanner.Text())
if line != "" {
if m := dumpLineParser.FindStringSubmatch(line); len(m) != 4 {
mod.Warning("unexpected line: '%s' -> %d matches", line, len(m))
} else {
entries = append(entries, dumpEntry{
Time: m[1],
Device: m[2],
Frame: m[3],
})
}
}
}
if err = scanner.Err(); err != nil {
return err
}
mod.Info("loaded %d entries from candump log", len(entries))
go func() {
mod.Info("candump reader started ...")
for _, entry := range entries {
frame := can.Frame{}
if err := frame.UnmarshalString(entry.Frame); err != nil {
mod.Error("could not unmarshal CAN frame: %v", err)
continue
}
if mod.dumpInject {
if err := mod.send.TransmitFrame(context.Background(), frame); err != nil {
mod.Error("could not send CAN frame: %v", err)
}
} else {
mod.onFrame(frame)
}
}
}()
return nil
}

View file

@ -25,6 +25,10 @@ func (mod *CANModule) Configure() error {
return session.ErrAlreadyStarted(mod.Name())
} else if err, mod.deviceName = mod.StringParam("can.device"); err != nil {
return err
} else if err, mod.dumpName = mod.StringParam("can.dump"); err != nil {
return err
} else if err, mod.dumpInject = mod.BoolParam("can.dump.inject"); err != nil {
return err
} else if err, mod.transport = mod.StringParam("can.transport"); err != nil {
return err
} else if mod.transport != "can" && mod.transport != "udp" {
@ -43,10 +47,15 @@ func (mod *CANModule) Configure() error {
if mod.conn, err = socketcan.Dial(mod.transport, mod.deviceName); err != nil {
return err
}
mod.recv = socketcan.NewReceiver(mod.conn)
mod.send = socketcan.NewTransmitter(mod.conn)
if mod.dumpName != "" {
if err = mod.startDumpReader(); err != nil {
return err
}
}
return nil
}