diff --git a/firewall/interception/nfq/nfq.go b/firewall/interception/nfq/nfq.go index b22e125a..910097ce 100644 --- a/firewall/interception/nfq/nfq.go +++ b/firewall/interception/nfq/nfq.go @@ -179,11 +179,13 @@ func (q *Queue) packetHandler(ctx context.Context) func(nfqueue.Attribute) int { verdictPending: abool.New(), } - if attrs.Payload != nil { - pkt.Payload = *attrs.Payload + if attrs.Payload == nil { + // 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) _ = pkt.Drop() return 0 diff --git a/firewall/interception/nfq/packet.go b/firewall/interception/nfq/packet.go index 872a6243..f8c7ed96 100644 --- a/firewall/interception/nfq/packet.go +++ b/firewall/interception/nfq/packet.go @@ -65,6 +65,11 @@ func (pkt *packet) ID() string { 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: // The legacy implementation of nfqueue (and the interception) module // always accept a packet but may mark it so that a subsequent rule in diff --git a/firewall/interception/windowskext/kext.go b/firewall/interception/windowskext/kext.go index aabd8fd1..76057342 100644 --- a/firewall/interception/windowskext/kext.go +++ b/firewall/interception/windowskext/kext.go @@ -230,6 +230,7 @@ func GetPayload(packetID uint32, packetSize uint32) ([]byte, error) { if packetSize < uint32(len(buf)) { return buf[:packetSize], nil } + return buf, nil } diff --git a/firewall/interception/windowskext/packet.go b/firewall/interception/windowskext/packet.go index cb43d598..35790a5c 100644 --- a/firewall/interception/windowskext/packet.go +++ b/firewall/interception/windowskext/packet.go @@ -24,7 +24,7 @@ type Packet struct { } // GetPayload returns the full raw packet. -func (pkt *Packet) GetPayload() ([]byte, error) { +func (pkt *Packet) LoadPacketData() error { pkt.lock.Lock() defer pkt.lock.Unlock() @@ -33,17 +33,21 @@ func (pkt *Packet) GetPayload() ([]byte, error) { payload, err := GetPayload(pkt.verdictRequest.id, pkt.verdictRequest.packetSize) if err != nil { - log.Tracer(pkt.Ctx()).Warningf("windowskext: failed to load payload %s", err) - log.Errorf("windowskext: failed to load payload %s", err) - return nil, packet.ErrFailedToLoadPayload + log.Tracer(pkt.Ctx()).Warningf("windowskext: failed to load payload: %s", err) + return packet.ErrFailedToLoadPayload + } + + err = packet.Parse(payload, &pkt.Base) + if err != nil { + log.Tracer(pkt.Ctx()).Warningf("windowskext: failed to parse payload: %s", err) + return packet.ErrFailedToLoadPayload } - pkt.Payload = payload } - if len(pkt.Payload) == 0 { - return nil, packet.ErrFailedToLoadPayload + if len(pkt.Raw()) == 0 { + return packet.ErrFailedToLoadPayload } - return pkt.Payload, nil + return nil } // Accept accepts the packet. diff --git a/network/packet/packet.go b/network/packet/packet.go index 315292ca..81d0f626 100644 --- a/network/packet/packet.go +++ b/network/packet/packet.go @@ -4,14 +4,18 @@ import ( "context" "fmt" "net" + + "github.com/google/gopacket" ) // Base is a base structure for satisfying the Packet interface. type Base struct { - ctx context.Context - info Info - connID string - Payload []byte + ctx context.Context + info Info + connID string + layers gopacket.Packet + layer3Data []byte + layer5Data []byte } // SetCtx sets the packet context. @@ -65,9 +69,24 @@ func (pkt *Base) HasPorts() bool { return false } -// GetPayload returns the packet payload. In some cases, this will fetch the payload from the os integration system. -func (pkt *Base) GetPayload() ([]byte, error) { - return pkt.Payload, ErrFailedToLoadPayload +// LoadPacketData loads packet data from the integration, if not yet done. +func (pkt *Base) LoadPacketData() error { + 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. @@ -214,9 +233,14 @@ type Packet interface { SetInbound() SetOutbound() HasPorts() bool - GetPayload() ([]byte, error) GetConnectionID() string + // PAYLOAD + LoadPacketData() error + Layers() gopacket.Packet + Raw() []byte + Payload() []byte + // MATCHING MatchesAddress(bool, IPProtocol, *net.IPNet, uint16) bool MatchesIP(bool, *net.IPNet) bool diff --git a/network/packet/parse.go b/network/packet/parse.go index d78b1321..c84a1964 100644 --- a/network/packet/parse.go +++ b/network/packet/parse.go @@ -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. -func Parse(packetData []byte, pktInfo *Info) error { +func Parse(packetData []byte, pktBase *Base) (err error) { if len(packetData) == 0 { return errors.New("empty packet") } + pktBase.layer3Data = packetData ipVersion := packetData[0] >> 4 var networkLayerType gopacket.LayerType @@ -137,11 +138,15 @@ func Parse(packetData []byte, pktInfo *Info) error { } for _, dec := range availableDecoders { - if err := dec(packet, pktInfo); err != nil { + if err := dec(packet, pktBase.Info()); err != nil { return err } } + pktBase.layers = packet + if packet.TransportLayer() != nil { + pktBase.layer5Data = packet.TransportLayer().LayerPayload() + } return nil }