You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by sr...@apache.org on 2022/07/14 16:10:39 UTC
[plc4x] branch develop updated: feat(plc4go/plc4xpcapanalyzer): analyzer should now be able to handle segmented messages (for c-bus)
This is an automated email from the ASF dual-hosted git repository.
sruehl pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/plc4x.git
The following commit(s) were added to refs/heads/develop by this push:
new 929558b9c feat(plc4go/plc4xpcapanalyzer): analyzer should now be able to handle segmented messages (for c-bus)
929558b9c is described below
commit 929558b9c84f68dec444a9cd89c7c5b5d9d8e706
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Thu Jul 14 18:10:32 2022 +0200
feat(plc4go/plc4xpcapanalyzer): analyzer should now be able to handle segmented messages (for c-bus)
+ Improvements on extractor
---
.../internal/analyzer/analyzer.go | 14 +++-
.../internal/cbusanalyzer/analyzer.go | 90 ++++++++++++++++++++--
.../plc4xpcapanalyzer/internal/common/common.go | 6 ++
.../internal/extractor/extractor.go | 30 +++++---
4 files changed, 119 insertions(+), 21 deletions(-)
diff --git a/plc4go/tools/plc4xpcapanalyzer/internal/analyzer/analyzer.go b/plc4go/tools/plc4xpcapanalyzer/internal/analyzer/analyzer.go
index 2d648e2c3..00d7863dc 100644
--- a/plc4go/tools/plc4xpcapanalyzer/internal/analyzer/analyzer.go
+++ b/plc4go/tools/plc4xpcapanalyzer/internal/analyzer/analyzer.go
@@ -54,6 +54,7 @@ func Analyze(pcapFile, protocolType string) {
serializePackage = bacnetanalyzer.SerializePackage
case "c-bus":
analyzer := cbusanalyzer.Analyzer{Client: net.ParseIP(config.AnalyzeConfigInstance.Client)}
+ analyzer.Init()
packageParse = analyzer.PackageParse
serializePackage = analyzer.SerializePackage
prettyPrint = analyzer.PrettyPrint
@@ -112,9 +113,16 @@ func Analyze(pcapFile, protocolType string) {
}
payload := applicationLayer.Payload()
if parsed, err := packageParse(packetInformation, payload); err != nil {
- parseFails++
- // TODO: write report to xml or something
- log.Warn().Stringer("packetInformation", packetInformation).Err(err).Msgf("No.[%d] Error parsing package", realPacketNumber)
+ switch err {
+ case common.ErrUnterminatedPackage:
+ log.Info().Stringer("packetInformation", packetInformation).Msgf("No.[%d] is unterminated", realPacketNumber)
+ case common.ErrEmptyPackage:
+ log.Info().Stringer("packetInformation", packetInformation).Msgf("No.[%d] is empty", realPacketNumber)
+ default:
+ parseFails++
+ // TODO: write report to xml or something
+ log.Error().Stringer("packetInformation", packetInformation).Err(err).Msgf("No.[%d] Error parsing package", realPacketNumber)
+ }
continue
} else {
log.Info().Stringer("packetInformation", packetInformation).Msgf("No.[%d] Parsed", realPacketNumber)
diff --git a/plc4go/tools/plc4xpcapanalyzer/internal/cbusanalyzer/analyzer.go b/plc4go/tools/plc4xpcapanalyzer/internal/cbusanalyzer/analyzer.go
index 2af94956a..f47adf517 100644
--- a/plc4go/tools/plc4xpcapanalyzer/internal/cbusanalyzer/analyzer.go
+++ b/plc4go/tools/plc4xpcapanalyzer/internal/cbusanalyzer/analyzer.go
@@ -31,23 +31,83 @@ import (
)
type Analyzer struct {
- Client net.IP
- requestContext model.RequestContext
- cBusOptions model.CBusOptions
- initialized bool
+ Client net.IP
+ requestContext model.RequestContext
+ cBusOptions model.CBusOptions
+ initialized bool
+ currentInboundPayloads map[string][]byte
+}
+
+func (a *Analyzer) Init() {
+ if a.initialized {
+ return
+ }
+ a.requestContext = model.NewRequestContext(false, false, false)
+ a.cBusOptions = model.NewCBusOptions(config.CBusConfigInstance.Connect, config.CBusConfigInstance.Smart, config.CBusConfigInstance.Idmon, config.CBusConfigInstance.Exstat, config.CBusConfigInstance.Monitor, config.CBusConfigInstance.Monall, config.CBusConfigInstance.Pun, config.CBusConfigInstance.Pcn, config.CBusConfigInstance.Srchk)
+ a.currentInboundPayloads = make(map[string][]byte)
+ a.initialized = true
}
func (a *Analyzer) PackageParse(packetInformation common.PacketInformation, payload []byte) (interface{}, error) {
if !a.initialized {
log.Warn().Msg("Not initialized... doing that now")
- a.requestContext = model.NewRequestContext(false, false, false)
- a.cBusOptions = model.NewCBusOptions(config.CBusConfigInstance.Connect, config.CBusConfigInstance.Smart, config.CBusConfigInstance.Idmon, config.CBusConfigInstance.Exstat, config.CBusConfigInstance.Monitor, config.CBusConfigInstance.Monall, config.CBusConfigInstance.Pun, config.CBusConfigInstance.Pcn, config.CBusConfigInstance.Srchk)
- a.initialized = true
+ a.Init()
}
log.Debug().Msgf("Parsing %s with requestContext\n%v\nBusOptions\n%s", packetInformation, a.requestContext, a.cBusOptions)
isResponse := packetInformation.DstIp.Equal(a.Client)
log.Debug().Stringer("packetInformation", packetInformation).Msgf("isResponse: %t", isResponse)
- parse, err := model.CBusMessageParse(utils.NewReadBufferByteBased(payload), isResponse, a.requestContext, a.cBusOptions, uint16(len(payload)))
+ payload = filterXOnXOff(payload)
+ if len(payload) == 0 {
+ return nil, common.ErrEmptyPackage
+ }
+ // Check if we have a termination in the middle
+ currentPayload := a.currentInboundPayloads[packetInformation.SrcIp.String()]
+ currentPayload = append(currentPayload, payload...)
+ shouldClearInboundPayload := true
+ // Check if we have a merged message
+ for i, b := range currentPayload {
+ if i == 0 {
+ // TODO: we ignore the first byte as this is typical for reset etc... so maybe this is good or bad we will see
+ continue
+ }
+ switch b {
+ case 0x0D:
+ if i+1 < len(currentPayload) && currentPayload[i+1] == 0x0A {
+ // If we know the next is a newline we jump to that index...
+ i++
+ }
+ // ... other than that the logic is the same
+ fallthrough
+ case 0x0A:
+ // We have a merged message if we are not at the end
+ if i < len(currentPayload)-1 {
+ log.Warn().Stringer("packetInformation", packetInformation).Msgf("we have a split at index %d", i)
+ // In this case we need to put the tail into our "buffer"
+ a.currentInboundPayloads[packetInformation.SrcIp.String()] = currentPayload[i+1:]
+ // and use the beginning as current payload
+ currentPayload = currentPayload[:i+1]
+ shouldClearInboundPayload = false
+ }
+ }
+ }
+ a.currentInboundPayloads[packetInformation.SrcIp.String()] = currentPayload
+ if lastElement := currentPayload[len(currentPayload)-1]; (!isResponse /*a request must end with cr*/ && lastElement != 0x0D /*cr*/) || (isResponse /*a response must end with lf*/ && lastElement != 0x0A /*lf*/) {
+ return nil, common.ErrUnterminatedPackage
+ } else {
+ log.Debug().Msgf("Last element %x", lastElement)
+ if shouldClearInboundPayload {
+ if currentSavedPayload := a.currentInboundPayloads[packetInformation.SrcIp.String()]; currentSavedPayload != nil {
+ // We remove our current payload from the beginning of the cache
+ for i, b := range currentPayload {
+ if currentSavedPayload[i] != b {
+ panic("programming error... at this point they should start with the identical bytes")
+ }
+ }
+ }
+ a.currentInboundPayloads[packetInformation.SrcIp.String()] = nil
+ }
+ }
+ parse, err := model.CBusMessageParse(utils.NewReadBufferByteBased(currentPayload), isResponse, a.requestContext, a.cBusOptions, uint16(len(currentPayload)))
if err != nil {
return nil, errors.Wrap(err, "Error parsing CBusCommand")
}
@@ -109,6 +169,20 @@ func (a *Analyzer) PackageParse(packetInformation common.PacketInformation, payl
return parse, nil
}
+func filterXOnXOff(payload []byte) []byte {
+ n := 0
+ for _, b := range payload {
+ switch b {
+ case 0x11: // Filter XON
+ case 0x13: // Filter XOFF
+ default:
+ payload[n] = b
+ n++
+ }
+ }
+ return payload[:n]
+}
+
func (a *Analyzer) SerializePackage(message interface{}) ([]byte, error) {
if message, ok := message.(model.CBusMessage); !ok {
log.Fatal().Msgf("Unsupported type %T supplied", message)
diff --git a/plc4go/tools/plc4xpcapanalyzer/internal/common/common.go b/plc4go/tools/plc4xpcapanalyzer/internal/common/common.go
index 941365c4b..d95ac04e1 100644
--- a/plc4go/tools/plc4xpcapanalyzer/internal/common/common.go
+++ b/plc4go/tools/plc4xpcapanalyzer/internal/common/common.go
@@ -21,6 +21,7 @@ package common
import (
"fmt"
+ "github.com/pkg/errors"
"net"
"time"
)
@@ -36,3 +37,8 @@ type PacketInformation struct {
func (p PacketInformation) String() string {
return fmt.Sprintf("%s (SrcIp:%v, DstIp:%v)", p.Description, p.SrcIp, p.DstIp)
}
+
+// ErrUnterminatedPackage is used when a transmission is incomplete (usually when package is split)
+var ErrUnterminatedPackage = errors.New("ErrUnterminatedPackage")
+
+var ErrEmptyPackage = errors.New("ErrEmptyPackage")
diff --git a/plc4go/tools/plc4xpcapanalyzer/internal/extractor/extractor.go b/plc4go/tools/plc4xpcapanalyzer/internal/extractor/extractor.go
index 058cff647..4495ea7cf 100644
--- a/plc4go/tools/plc4xpcapanalyzer/internal/extractor/extractor.go
+++ b/plc4go/tools/plc4xpcapanalyzer/internal/extractor/extractor.go
@@ -56,22 +56,30 @@ func Extract(pcapFile, protocolType string) {
clientRequestWriter := color.New(color.FgGreen)
clientRequestIndicatorWriter := color.New(color.FgHiGreen)
printPayload = func(packetInformation common.PacketInformation, payload []byte) {
+ payloadString := ""
suffix := ""
- if properTerminated := payload[len(payload)-1] == 0x0D || payload[len(payload)-1] == 0x0A; properTerminated {
- suffix = "\n"
+ extraInformation := ""
+ if config.ExtractConfigInstance.Verbosity > 2 {
+ extraInformation = fmt.Sprintf("(No.[%d])", packetInformation.PacketNumber)
+ }
+ if len(payload) > 0 {
+ if properTerminated := payload[len(payload)-1] == 0x0D || payload[len(payload)-1] == 0x0A; properTerminated {
+ suffix = "\n"
+ }
+ quotedPayload := fmt.Sprintf("%+q", payload)
+ unquotedPayload := quotedPayload[1 : len(quotedPayload)-1]
+ payloadString = unquotedPayload
}
- quotedPayload := fmt.Sprintf("%+q", payload)
- unquotedPayload := quotedPayload[1 : len(quotedPayload)-1]
if isResponse := packetInformation.DstIp.Equal(clientIp); isResponse {
if config.ExtractConfigInstance.ShowDirectionalIndicators {
- _, _ = serverResponseIndicatorWriter.Fprint(stderr, "(<--pci)")
+ _, _ = serverResponseIndicatorWriter.Fprintf(stderr, "%s(<--pci)", extraInformation)
}
- _, _ = serverResponseWriter.Fprintf(stdout, "%s%s", unquotedPayload, suffix)
+ _, _ = serverResponseWriter.Fprintf(stdout, "%s%s", payloadString, suffix)
} else {
if config.ExtractConfigInstance.ShowDirectionalIndicators {
- _, _ = clientRequestIndicatorWriter.Fprint(stderr, "(-->pci)")
+ _, _ = clientRequestIndicatorWriter.Fprintf(stderr, "%s(-->pci)", extraInformation)
}
- _, _ = clientRequestWriter.Fprintf(stdout, "%s%s", unquotedPayload, suffix)
+ _, _ = clientRequestWriter.Fprintf(stdout, "%s%s", payloadString, suffix)
}
}
}
@@ -122,12 +130,14 @@ func Extract(pcapFile, protocolType string) {
packetInformation.DstIp = networkLayer.DstIP
}
+ var payload []byte
applicationLayer := packet.ApplicationLayer()
if applicationLayer == nil {
log.Info().Stringer("packetInformation", packetInformation).Msgf("No.[%d] No application layer", realPacketNumber)
- continue
+ } else {
+ payload = applicationLayer.Payload()
}
- payload := applicationLayer.Payload()
+
log.Debug().Msgf("Got payload %x", payload)
if config.ExtractConfigInstance.Verbosity > 1 {
printPayload(packetInformation, payload)