From 6066c4d6e7c0f2e207c8e5204e5198fd2cde4117 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Sun, 3 Dec 2023 19:37:31 +0100 Subject: [PATCH] decode/tunnel: improve tunnel handling Give each packet explicit tunnel type `ttype`: none, root, child. Assigning happens when a (tunnel) packet is set up and is thread safe. --- src/decode.c | 35 +++++++++++++------------ src/decode.h | 58 +++++++++++++++++++++++++++++++---------- src/defrag.c | 10 ++----- src/detect-mark.c | 2 +- src/log-pcap.c | 10 +++---- src/output-json-alert.c | 2 +- src/packet.c | 1 + src/respond-reject.c | 2 +- src/source-af-packet.c | 4 +-- src/source-ipfw.c | 7 +++-- src/source-nfq.c | 4 +-- src/source-pfring.c | 2 +- src/source-windivert.c | 2 +- src/stream-tcp-list.c | 2 +- src/tmqh-packetpool.c | 20 +++++++------- src/util-profiling.c | 4 +-- 16 files changed, 95 insertions(+), 70 deletions(-) diff --git a/src/decode.c b/src/decode.c index d302c7654675..70ef282b9f1a 100644 --- a/src/decode.c +++ b/src/decode.c @@ -335,13 +335,15 @@ Packet *PacketTunnelPktSetup(ThreadVars *tv, DecodeThreadVars *dtv, Packet *pare p->livedev = parent->livedev; /* set the root ptr to the lowest layer */ - if (parent->root != NULL) + if (parent->root != NULL) { p->root = parent->root; - else + BUG_ON(parent->ttype != PacketTunnelChild); + } else { p->root = parent; - + parent->ttype = PacketTunnelRoot; + } /* tell new packet it's part of a tunnel */ - SET_TUNNEL_PKT(p); + p->ttype = PacketTunnelChild; ret = DecodeTunnel(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), proto); @@ -351,18 +353,15 @@ Packet *PacketTunnelPktSetup(ThreadVars *tv, DecodeThreadVars *dtv, Packet *pare { /* Not a (valid) tunnel packet */ SCLogDebug("tunnel packet is invalid"); - p->root = NULL; - UNSET_TUNNEL_PKT(p); TmqhOutputPacketpool(tv, p); SCReturnPtr(NULL, "Packet"); } - - /* tell parent packet it's part of a tunnel */ - SET_TUNNEL_PKT(parent); - - /* increment tunnel packet refcnt in the root packet */ + /* Update tunnel settings in parent */ + if (parent->root == NULL) { + parent->ttype = PacketTunnelRoot; + } TUNNEL_INCR_PKT_TPR(p); /* disable payload (not packet) inspection on the parent, as the payload @@ -397,10 +396,15 @@ Packet *PacketDefragPktSetup(Packet *parent, const uint8_t *pkt, uint32_t len, u } /* set the root ptr to the lowest layer */ - if (parent->root != NULL) + if (parent->root != NULL) { p->root = parent->root; - else + BUG_ON(parent->ttype != PacketTunnelChild); + } else { p->root = parent; + // we set parent->ttype later + } + /* tell new packet it's part of a tunnel */ + p->ttype = PacketTunnelChild; /* copy packet and set length, proto */ if (pkt && len) { @@ -410,8 +414,6 @@ Packet *PacketDefragPktSetup(Packet *parent, const uint8_t *pkt, uint32_t len, u p->ts = parent->ts; p->datalink = DLT_RAW; p->tenant_id = parent->tenant_id; - /* tell new packet it's part of a tunnel */ - SET_TUNNEL_PKT(p); memcpy(&p->vlan_id[0], &parent->vlan_id[0], sizeof(p->vlan_id)); p->vlan_idx = parent->vlan_idx; p->livedev = parent->livedev; @@ -426,7 +428,8 @@ Packet *PacketDefragPktSetup(Packet *parent, const uint8_t *pkt, uint32_t len, u void PacketDefragPktSetupParent(Packet *parent) { /* tell parent packet it's part of a tunnel */ - SET_TUNNEL_PKT(parent); + if (parent->ttype == PacketTunnelNone) + parent->ttype = PacketTunnelRoot; /* increment tunnel packet refcnt in the root packet */ TUNNEL_INCR_PKT_TPR(parent); diff --git a/src/decode.h b/src/decode.h index fc5b9a074443..34ae84aa8b55 100644 --- a/src/decode.h +++ b/src/decode.h @@ -407,6 +407,12 @@ enum PacketDropReason { PKT_DROP_REASON_MAX, }; +enum PacketTunnelType { + PacketTunnelNone, + PacketTunnelRoot, + PacketTunnelChild, +}; + /* forward declaration since Packet struct definition requires this */ struct PacketQueue_; @@ -472,6 +478,9 @@ typedef struct Packet_ * hash size still */ uint32_t flow_hash; + /* tunnel type: none, root or child */ + enum PacketTunnelType ttype; + SCTime_t ts; union { @@ -618,7 +627,7 @@ typedef struct Packet_ /* enum PacketDropReason::PKT_DROP_REASON_* as uint8_t for compactness */ uint8_t drop_reason; - /* has tunnel been verdicted? */ + /** has verdict on this tunneled packet been issued? */ bool tunnel_verdicted; /* tunnel/encapsulation handling */ @@ -649,8 +658,8 @@ typedef struct Packet_ /** lock to protect access to: * - tunnel_rtv_cnt * - tunnel_tpr_cnt - * - nfq_v.mark - * - flags + * - tunnel_verdicted + * - nfq_v.mark (if p->ttype != PacketTunnelNone) */ SCSpinlock tunnel_lock; } persistent; @@ -799,13 +808,14 @@ static inline void TUNNEL_INCR_PKT_TPR(Packet *p) #define TUNNEL_PKT_RTV(p) ((p)->root ? (p)->root->tunnel_rtv_cnt : (p)->tunnel_rtv_cnt) #define TUNNEL_PKT_TPR(p) ((p)->root ? (p)->root->tunnel_tpr_cnt : (p)->tunnel_tpr_cnt) -#define IS_TUNNEL_PKT(p) (((p)->flags & PKT_TUNNEL)) -#define SET_TUNNEL_PKT(p) ((p)->flags |= PKT_TUNNEL) -#define UNSET_TUNNEL_PKT(p) ((p)->flags &= ~PKT_TUNNEL) -#define IS_TUNNEL_ROOT_PKT(p) (IS_TUNNEL_PKT(p) && (p)->root == NULL) - -#define IS_TUNNEL_PKT_VERDICTED(p) (p)->tunnel_verdicted -#define SET_TUNNEL_PKT_VERDICTED(p) (p)->tunnel_verdicted = true +static inline bool PacketTunnelIsVerdicted(const Packet *p) +{ + return p->tunnel_verdicted; +} +static inline void PacketTunnelSetVerdicted(Packet *p) +{ + p->tunnel_verdicted = true; +} enum DecodeTunnelProto { DECODE_TUNNEL_ETHERNET, @@ -1017,8 +1027,7 @@ void DecodeUnregisterCounters(void); depth reached. */ #define PKT_STREAM_NOPCAPLOG BIT_U32(12) -#define PKT_TUNNEL BIT_U32(13) -// vacancy +// vacancy 2x /** Packet checksum is not computed (TX packet for example) */ #define PKT_IGNORE_CHECKSUM BIT_U32(15) @@ -1094,6 +1103,26 @@ static inline void DecodeSetNoPacketInspectionFlag(Packet *p) p->flags |= PKT_NOPACKET_INSPECTION; } +static inline bool PacketIsTunnelRoot(const Packet *p) +{ + return (p->ttype == PacketTunnelRoot); +} + +static inline bool PacketIsTunnelChild(const Packet *p) +{ + return (p->ttype == PacketTunnelChild); +} + +static inline bool PacketIsTunnel(const Packet *p) +{ + return (p->ttype != PacketTunnelNone); +} + +static inline bool PacketIsNotTunnel(const Packet *p) +{ + return (p->ttype == PacketTunnelNone); +} + /** \brief return true if *this* packet needs to trigger a verdict. * * If we have the root packet, and we have none outstanding, @@ -1113,10 +1142,11 @@ static inline bool VerdictTunnelPacket(Packet *p) SCLogDebug("tunnel: outstanding %u", outstanding); /* if there are packets outstanding, we won't verdict this one */ - if (IS_TUNNEL_ROOT_PKT(p) && !IS_TUNNEL_PKT_VERDICTED(p) && !outstanding) { + if (PacketIsTunnelRoot(p) && !PacketTunnelIsVerdicted(p) && !outstanding) { // verdict SCLogDebug("root %p: verdict", p); - } else if (!IS_TUNNEL_ROOT_PKT(p) && outstanding == 1 && p->root && IS_TUNNEL_PKT_VERDICTED(p->root)) { + } else if (PacketIsTunnelChild(p) && outstanding == 1 && p->root && + PacketTunnelIsVerdicted(p->root)) { // verdict SCLogDebug("tunnel %p: verdict", p); } else { diff --git a/src/defrag.c b/src/defrag.c index 36d6b8420712..9918747e7d6d 100644 --- a/src/defrag.c +++ b/src/defrag.c @@ -873,10 +873,7 @@ DefragInsertFrag(ThreadVars *tv, DecodeThreadVars *dtv, DefragTracker *tracker, r = Defrag4Reassemble(tv, tracker, p); if (r != NULL && tv != NULL && dtv != NULL) { StatsIncr(tv, dtv->counter_defrag_ipv4_reassembled); - if (DecodeIPV4(tv, dtv, r, (void *)r->ip4h, - IPV4_GET_IPLEN(r)) != TM_ECODE_OK) { - - UNSET_TUNNEL_PKT(r); + if (DecodeIPV4(tv, dtv, r, (void *)r->ip4h, IPV4_GET_IPLEN(r)) != TM_ECODE_OK) { r->root = NULL; TmqhOutputPacketpool(tv, r); r = NULL; @@ -890,10 +887,7 @@ DefragInsertFrag(ThreadVars *tv, DecodeThreadVars *dtv, DefragTracker *tracker, if (r != NULL && tv != NULL && dtv != NULL) { StatsIncr(tv, dtv->counter_defrag_ipv6_reassembled); if (DecodeIPV6(tv, dtv, r, (uint8_t *)r->ip6h, - IPV6_GET_PLEN(r) + IPV6_HEADER_LEN) - != TM_ECODE_OK) { - - UNSET_TUNNEL_PKT(r); + IPV6_GET_PLEN(r) + IPV6_HEADER_LEN) != TM_ECODE_OK) { r->root = NULL; TmqhOutputPacketpool(tv, r); r = NULL; diff --git a/src/detect-mark.c b/src/detect-mark.c index 90ed7750a4e5..9c5b9f46ea8e 100644 --- a/src/detect-mark.c +++ b/src/detect-mark.c @@ -228,7 +228,7 @@ static int DetectMarkPacket(DetectEngineThreadCtx *det_ctx, Packet *p, #ifdef NFQ const DetectMarkData *nf_data = (const DetectMarkData *)ctx; if (nf_data->mask) { - if (!(IS_TUNNEL_PKT(p))) { + if (PacketIsNotTunnel(p)) { /* coverity[missing_lock] */ p->nfq_v.mark = (nf_data->mark & nf_data->mask) | (p->nfq_v.mark & ~(nf_data->mask)); diff --git a/src/log-pcap.c b/src/log-pcap.c index 43b93b44defb..fdd0280fc853 100644 --- a/src/log-pcap.c +++ b/src/log-pcap.c @@ -245,7 +245,7 @@ static bool PcapLogCondition(ThreadVars *tv, void *thread_data, const Packet *p) return false; } - if (IS_TUNNEL_PKT(p) && !IS_TUNNEL_ROOT_PKT(p)) { + if (p->ttype == PacketTunnelChild) { return false; } return true; @@ -379,7 +379,7 @@ static int PcapLogOpenHandles(PcapLogData *pl, const Packet *p) PCAPLOG_PROFILE_START; int datalink = p->datalink; - if (IS_TUNNEL_PKT(p) && !IS_TUNNEL_ROOT_PKT(p)) { + if (p->ttype == PacketTunnelChild) { Packet *real_p = p->root; datalink = real_p->datalink; } @@ -588,7 +588,7 @@ static int PcapLog (ThreadVars *t, void *thread_data, const Packet *p) pl->pkt_cnt++; pl->h->ts.tv_sec = SCTIME_SECS(p->ts); pl->h->ts.tv_usec = SCTIME_USECS(p->ts); - if (IS_TUNNEL_PKT(p) && !IS_TUNNEL_ROOT_PKT(p)) { + if (p->ttype == PacketTunnelChild) { rp = p->root; pl->h->caplen = GET_PKT_LEN(rp); pl->h->len = GET_PKT_LEN(rp); @@ -666,7 +666,7 @@ static int PcapLog (ThreadVars *t, void *thread_data, const Packet *p) /* PcapLogDumpSegment has written over the PcapLogData variables so need to update */ pl->h->ts.tv_sec = SCTIME_SECS(p->ts); pl->h->ts.tv_usec = SCTIME_USECS(p->ts); - if (IS_TUNNEL_PKT(p) && !IS_TUNNEL_ROOT_PKT(p)) { + if (p->ttype == PacketTunnelChild) { rp = p->root; pl->h->caplen = GET_PKT_LEN(rp); pl->h->len = GET_PKT_LEN(rp); @@ -679,7 +679,7 @@ static int PcapLog (ThreadVars *t, void *thread_data, const Packet *p) } } - if (IS_TUNNEL_PKT(p) && !IS_TUNNEL_ROOT_PKT(p)) { + if (p->ttype == PacketTunnelChild) { rp = p->root; #ifdef HAVE_LIBLZ4 ret = PcapWrite(pl, comp, GET_PKT_DATA(rp), len); diff --git a/src/output-json-alert.c b/src/output-json-alert.c index 5f511b962d29..1622a323cd3f 100644 --- a/src/output-json-alert.c +++ b/src/output-json-alert.c @@ -562,7 +562,7 @@ static int AlertJson(ThreadVars *tv, JsonAlertLogThread *aft, const Packet *p) /* alert */ AlertJsonHeader(json_output_ctx, p, pa, jb, json_output_ctx->flags, &addr, xff_buffer); - if (IS_TUNNEL_PKT(p)) { + if (PacketIsTunnel(p)) { AlertJsonTunnel(p, jb); } diff --git a/src/packet.c b/src/packet.c index c798a0d11ea0..30ef4f11b31a 100644 --- a/src/packet.c +++ b/src/packet.c @@ -103,6 +103,7 @@ void PacketReinit(Packet *p) p->vlan_id[0] = 0; p->vlan_id[1] = 0; p->vlan_idx = 0; + p->ttype = PacketTunnelNone; SCTIME_INIT(p->ts); p->datalink = 0; p->drop_reason = 0; diff --git a/src/respond-reject.c b/src/respond-reject.c index b53fad9d3f31..e395880ef52f 100644 --- a/src/respond-reject.c +++ b/src/respond-reject.c @@ -69,7 +69,7 @@ static TmEcode RespondRejectFunc(ThreadVars *tv, Packet *p, void *data) return TM_ECODE_OK; } - if (IS_TUNNEL_PKT(p)) { + if (PacketIsTunnel(p)) { return TM_ECODE_OK; } diff --git a/src/source-af-packet.c b/src/source-af-packet.c index 3c783a149029..cf4cff192545 100644 --- a/src/source-af-packet.c +++ b/src/source-af-packet.c @@ -2194,7 +2194,7 @@ static int AFPBypassCallback(Packet *p) /* Bypassing tunneled packets is currently not supported * because we can't discard the inner packet only due to * primitive parsing in eBPF */ - if (IS_TUNNEL_PKT(p)) { + if (PacketIsTunnel(p)) { return 0; } if (PKT_IS_IPV4(p)) { @@ -2349,7 +2349,7 @@ static int AFPXDPBypassCallback(Packet *p) /* Bypassing tunneled packets is currently not supported * because we can't discard the inner packet only due to * primitive parsing in eBPF */ - if (IS_TUNNEL_PKT(p)) { + if (PacketIsTunnel(p)) { return 0; } if (PKT_IS_IPV4(p)) { diff --git a/src/source-ipfw.c b/src/source-ipfw.c index 4bdb7724598a..ecae507c41f3 100644 --- a/src/source-ipfw.c +++ b/src/source-ipfw.c @@ -614,9 +614,8 @@ TmEcode VerdictIPFW(ThreadVars *tv, Packet *p, void *data) } /* This came from NFQ. - * if this is a tunnel packet we check if we are ready to verdict - * already. */ - if (IS_TUNNEL_PKT(p)) { + * If this is a tunnel packet we check if we are ready to verdict already. */ + if (PacketIsTunnel(p)) { bool verdict = VerdictTunnelPacket(p); /* don't verdict if we are not ready */ @@ -628,7 +627,7 @@ TmEcode VerdictIPFW(ThreadVars *tv, Packet *p, void *data) /* no tunnel, verdict normally */ SCLogDebug("Setting verdict on non-tunnel"); retval = IPFWSetVerdict(tv, ptv, p); - } /* IS_TUNNEL_PKT end */ + } SCReturnInt(retval); } diff --git a/src/source-nfq.c b/src/source-nfq.c index 9c9fdd256c7d..a64f02632bc7 100644 --- a/src/source-nfq.c +++ b/src/source-nfq.c @@ -496,7 +496,7 @@ static void NFQReleasePacket(Packet *p) */ static int NFQBypassCallback(Packet *p) { - if (IS_TUNNEL_PKT(p)) { + if (PacketIsTunnel(p)) { /* real tunnels may have multiple flows inside them, so bypass can't * work for those. Rebuilt packets from IP fragments are fine. */ if (p->flags & PKT_REBUILT_FRAGMENT) { @@ -1186,7 +1186,7 @@ TmEcode VerdictNFQ(ThreadVars *tv, Packet *p, void *data) { /* if this is a tunnel packet we check if we are ready to verdict * already. */ - if (IS_TUNNEL_PKT(p)) { + if (p->ttype != PacketTunnelNone) { SCLogDebug("tunnel pkt: %p/%p %s", p, p->root, p->root ? "upper layer" : "root"); bool verdict = VerdictTunnelPacket(p); /* don't verdict if we are not ready */ diff --git a/src/source-pfring.c b/src/source-pfring.c index 3ec02327f92d..f39574b75327 100644 --- a/src/source-pfring.c +++ b/src/source-pfring.c @@ -310,7 +310,7 @@ static int PfringBypassCallback(Packet *p) } /* Bypassing tunneled packets is currently not supported */ - if (IS_TUNNEL_PKT(p)) { + if (PacketIsTunnel(p)) { return 0; } diff --git a/src/source-windivert.c b/src/source-windivert.c index 347d2e7a0f2d..3d37b1aaf6e5 100644 --- a/src/source-windivert.c +++ b/src/source-windivert.c @@ -769,7 +769,7 @@ static TmEcode WinDivertVerdictHelper(ThreadVars *tv, Packet *p) /* we can't verdict tunnel packets without ensuring all encapsulated * packets are verdicted */ - if (IS_TUNNEL_PKT(p)) { + if (PacketIsTunnel(p)) { bool finalVerdict = VerdictTunnelPacket(p); if (!finalVerdict) { SCReturnInt(TM_ECODE_OK); diff --git a/src/stream-tcp-list.c b/src/stream-tcp-list.c index 2b8a4d079cef..0966b9577431 100644 --- a/src/stream-tcp-list.c +++ b/src/stream-tcp-list.c @@ -618,7 +618,7 @@ static void StreamTcpSegmentAddPacketData( return; } - if (IS_TUNNEL_PKT(p) && !IS_TUNNEL_ROOT_PKT(p)) { + if (PacketIsTunnelChild(p)) { Packet *rp = p->root; StreamTcpSegmentAddPacketDataDo(seg, rp, p); } else { diff --git a/src/tmqh-packetpool.c b/src/tmqh-packetpool.c index f71274b4502c..fb2f211012fc 100644 --- a/src/tmqh-packetpool.c +++ b/src/tmqh-packetpool.c @@ -337,7 +337,7 @@ void TmqhOutputPacketpool(ThreadVars *t, Packet *p) SCEnter(); SCLogDebug("Packet %p, p->root %p, alloced %s", p, p->root, BOOL2STR(p->pool == NULL)); - if (IS_TUNNEL_PKT(p)) { + if (PacketIsTunnel(p)) { SCLogDebug("Packet %p is a tunnel packet: %s", p,p->root ? "upper layer" : "tunnel root"); @@ -345,9 +345,9 @@ void TmqhOutputPacketpool(ThreadVars *t, Packet *p) SCSpinlock *lock = p->root ? &p->root->persistent.tunnel_lock : &p->persistent.tunnel_lock; SCSpinLock(lock); - if (IS_TUNNEL_ROOT_PKT(p)) { + if (PacketIsTunnelRoot(p)) { SCLogDebug("IS_TUNNEL_ROOT_PKT == TRUE"); - CaptureStatsUpdate(t, p); + CaptureStatsUpdate(t, p); // TODO move out of lock const uint16_t outstanding = TUNNEL_PKT_TPR(p) - TUNNEL_PKT_RTV(p); SCLogDebug("root pkt: outstanding %u", outstanding); @@ -366,7 +366,7 @@ void TmqhOutputPacketpool(ThreadVars *t, Packet *p) * packets, return this to the pool. It's still referenced * by the tunnel packets, and we will return it * when we handle them */ - SET_TUNNEL_PKT_VERDICTED(p); + PacketTunnelSetVerdicted(p); PACKET_PROFILING_END(p); SCSpinUnlock(lock); @@ -381,9 +381,7 @@ void TmqhOutputPacketpool(ThreadVars *t, Packet *p) /* all tunnel packets are processed except us. Root already * processed. So return tunnel pkt and root packet to the * pool. */ - if (outstanding == 0 && - p->root && IS_TUNNEL_PKT_VERDICTED(p->root)) - { + if (outstanding == 0 && p->root && PacketTunnelIsVerdicted(p->root)) { SCLogDebug("root verdicted == true && no outstanding"); /* handle freeing the root as well*/ @@ -398,8 +396,8 @@ void TmqhOutputPacketpool(ThreadVars *t, Packet *p) * so get rid of the tunnel pkt only */ SCLogDebug("NOT IS_TUNNEL_PKT_VERDICTED (%s) || " - "outstanding > 0 (%u)", - (p->root && IS_TUNNEL_PKT_VERDICTED(p->root)) ? "true" : "false", + "outstanding > 0 (%u)", + (p->root && PacketTunnelIsVerdicted(p->root)) ? "true" : "false", outstanding); /* fall through */ @@ -414,8 +412,8 @@ void TmqhOutputPacketpool(ThreadVars *t, Packet *p) } SCLogDebug("[packet %p][%s] %s", p, - IS_TUNNEL_PKT(p) ? IS_TUNNEL_ROOT_PKT(p) ? "tunnel::root" : "tunnel::leaf" - : "no tunnel", + PacketIsTunnel(p) ? PacketIsTunnelRoot(p) ? "tunnel::root" : "tunnel::leaf" + : "no tunnel", (p->action & ACTION_DROP) ? "DROP" : "no drop"); /* we're done with the tunnel root now as well */ diff --git a/src/util-profiling.c b/src/util-profiling.c index 5d4bcc8905ce..a4f5bbef0ae7 100644 --- a/src/util-profiling.c +++ b/src/util-profiling.c @@ -1125,7 +1125,7 @@ void SCProfilingAddPacket(Packet *p) pd->tot += delta; pd->cnt ++; - if (IS_TUNNEL_PKT(p)) { + if (PacketIsTunnel(p)) { pd = &packet_profile_data4[256]; if (pd->min == 0 || delta < pd->min) { @@ -1161,7 +1161,7 @@ void SCProfilingAddPacket(Packet *p) pd->tot += delta; pd->cnt ++; - if (IS_TUNNEL_PKT(p)) { + if (PacketIsTunnel(p)) { pd = &packet_profile_data6[256]; if (pd->min == 0 || delta < pd->min) {