Improve packet parsing

This commit is contained in:
Daniel 2021-03-29 13:39:36 +02:00
parent 3abaca1d90
commit 5d61b7b682
6 changed files with 62 additions and 21 deletions

View file

@ -179,11 +179,13 @@ func (q *Queue) packetHandler(ctx context.Context) func(nfqueue.Attribute) int {
verdictPending: abool.New(), verdictPending: abool.New(),
} }
if attrs.Payload != nil { if attrs.Payload == nil {
pkt.Payload = *attrs.Payload // There is not payload.
log.Warningf("nfqueue: packet #%s has no payload", pkt.pktID)
return 0
} }
if err := pmpacket.Parse(pkt.Payload, pkt.Info()); err != nil { if err := pmpacket.Parse(*attrs.Payload, &pkt.Base); err != nil {
log.Warningf("nfqueue: failed to parse payload: %s", err) log.Warningf("nfqueue: failed to parse payload: %s", err)
_ = pkt.Drop() _ = pkt.Drop()
return 0 return 0

View file

@ -65,6 +65,11 @@ func (pkt *packet) ID() string {
return fmt.Sprintf("pkt:%d qid:%d", pkt.pktID, pkt.queue.id) return fmt.Sprintf("pkt:%d qid:%d", pkt.pktID, pkt.queue.id)
} }
// LoadPacketData does nothing on Linux, as data is always fully parsed.
func (pkt *packet) LoadPacketData() error {
return nil
}
// TODO(ppacher): revisit the following behavior: // TODO(ppacher): revisit the following behavior:
// The legacy implementation of nfqueue (and the interception) module // The legacy implementation of nfqueue (and the interception) module
// always accept a packet but may mark it so that a subsequent rule in // always accept a packet but may mark it so that a subsequent rule in

View file

@ -230,6 +230,7 @@ func GetPayload(packetID uint32, packetSize uint32) ([]byte, error) {
if packetSize < uint32(len(buf)) { if packetSize < uint32(len(buf)) {
return buf[:packetSize], nil return buf[:packetSize], nil
} }
return buf, nil return buf, nil
} }

View file

@ -24,7 +24,7 @@ type Packet struct {
} }
// GetPayload returns the full raw packet. // GetPayload returns the full raw packet.
func (pkt *Packet) GetPayload() ([]byte, error) { func (pkt *Packet) LoadPacketData() error {
pkt.lock.Lock() pkt.lock.Lock()
defer pkt.lock.Unlock() defer pkt.lock.Unlock()
@ -33,17 +33,21 @@ func (pkt *Packet) GetPayload() ([]byte, error) {
payload, err := GetPayload(pkt.verdictRequest.id, pkt.verdictRequest.packetSize) payload, err := GetPayload(pkt.verdictRequest.id, pkt.verdictRequest.packetSize)
if err != nil { if err != nil {
log.Tracer(pkt.Ctx()).Warningf("windowskext: failed to load payload %s", err) log.Tracer(pkt.Ctx()).Warningf("windowskext: failed to load payload: %s", err)
log.Errorf("windowskext: failed to load payload %s", err) return packet.ErrFailedToLoadPayload
return nil, packet.ErrFailedToLoadPayload
}
pkt.Payload = payload
} }
if len(pkt.Payload) == 0 { err = packet.Parse(payload, &pkt.Base)
return nil, packet.ErrFailedToLoadPayload if err != nil {
log.Tracer(pkt.Ctx()).Warningf("windowskext: failed to parse payload: %s", err)
return packet.ErrFailedToLoadPayload
} }
return pkt.Payload, nil }
if len(pkt.Raw()) == 0 {
return packet.ErrFailedToLoadPayload
}
return nil
} }
// Accept accepts the packet. // Accept accepts the packet.

View file

@ -4,6 +4,8 @@ import (
"context" "context"
"fmt" "fmt"
"net" "net"
"github.com/google/gopacket"
) )
// Base is a base structure for satisfying the Packet interface. // Base is a base structure for satisfying the Packet interface.
@ -11,7 +13,9 @@ type Base struct {
ctx context.Context ctx context.Context
info Info info Info
connID string connID string
Payload []byte layers gopacket.Packet
layer3Data []byte
layer5Data []byte
} }
// SetCtx sets the packet context. // SetCtx sets the packet context.
@ -65,9 +69,24 @@ func (pkt *Base) HasPorts() bool {
return false return false
} }
// GetPayload returns the packet payload. In some cases, this will fetch the payload from the os integration system. // LoadPacketData loads packet data from the integration, if not yet done.
func (pkt *Base) GetPayload() ([]byte, error) { func (pkt *Base) LoadPacketData() error {
return pkt.Payload, ErrFailedToLoadPayload return ErrFailedToLoadPayload
}
// Layers returns the parsed layer data.
func (pkt *Base) Layers() gopacket.Packet {
return pkt.layers
}
// Raw returns the raw Layer 3 Network Data.
func (pkt *Base) Raw() []byte {
return pkt.layer3Data
}
// Payload returns the raw Layer 5 Network Data.
func (pkt *Base) Payload() []byte {
return pkt.layer5Data
} }
// GetConnectionID returns the link ID for this packet. // GetConnectionID returns the link ID for this packet.
@ -214,9 +233,14 @@ type Packet interface {
SetInbound() SetInbound()
SetOutbound() SetOutbound()
HasPorts() bool HasPorts() bool
GetPayload() ([]byte, error)
GetConnectionID() string GetConnectionID() string
// PAYLOAD
LoadPacketData() error
Layers() gopacket.Packet
Raw() []byte
Payload() []byte
// MATCHING // MATCHING
MatchesAddress(bool, IPProtocol, *net.IPNet, uint16) bool MatchesAddress(bool, IPProtocol, *net.IPNet, uint16) bool
MatchesIP(bool, *net.IPNet) bool MatchesIP(bool, *net.IPNet) bool

View file

@ -102,10 +102,11 @@ func checkError(packet gopacket.Packet, _ *Info) error {
} }
// Parse parses an IP packet and saves the information in the given packet object. // Parse parses an IP packet and saves the information in the given packet object.
func Parse(packetData []byte, pktInfo *Info) error { func Parse(packetData []byte, pktBase *Base) (err error) {
if len(packetData) == 0 { if len(packetData) == 0 {
return errors.New("empty packet") return errors.New("empty packet")
} }
pktBase.layer3Data = packetData
ipVersion := packetData[0] >> 4 ipVersion := packetData[0] >> 4
var networkLayerType gopacket.LayerType var networkLayerType gopacket.LayerType
@ -137,11 +138,15 @@ func Parse(packetData []byte, pktInfo *Info) error {
} }
for _, dec := range availableDecoders { for _, dec := range availableDecoders {
if err := dec(packet, pktInfo); err != nil { if err := dec(packet, pktBase.Info()); err != nil {
return err return err
} }
} }
pktBase.layers = packet
if packet.TransportLayer() != nil {
pktBase.layer5Data = packet.TransportLayer().LayerPayload()
}
return nil return nil
} }