diff --git a/doc/userguide/rules/header-keywords.rst b/doc/userguide/rules/header-keywords.rst index 090fbdb157..c7ed88a0c4 100644 --- a/doc/userguide/rules/header-keywords.rst +++ b/doc/userguide/rules/header-keywords.rst @@ -1,5 +1,19 @@ .. role:: example-rule-emphasis +Ethernet Keywords +----------------- + +ether.hdr +^^^^^^^^^ + +Sticky buffer to match on the whole Ethernet header. + +Example rule: + +.. container:: example-rule + + alert ether any any -> any any (msg:"ARP packet"; :example-rule-emphasis:`ether.hdr; content:"|08 06|"; offset:12; depth:2;` sid:1234; rev:5;) + IP Keywords ----------- diff --git a/src/Makefile.am b/src/Makefile.am index 1db430d72f..9baf63ebcc 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -160,6 +160,7 @@ noinst_HEADERS = \ detect-engine-uint.h \ detect-engine.h \ detect-entropy.h \ + detect-etherhdr.h \ detect-fast-pattern.h \ detect-file-data.h \ detect-file-hash-common.h \ @@ -741,6 +742,7 @@ libsuricata_c_a_SOURCES = \ detect-engine-uint.c \ detect-engine.c \ detect-entropy.c \ + detect-etherhdr.c \ detect-fast-pattern.c \ detect-file-data.c \ detect-file-hash-common.c \ @@ -1169,6 +1171,7 @@ EXTRA_DIST = \ ${PRIVATE_INCLUDES} \ tests/stream-tcp-inline.c \ tests/stream-tcp-list.c \ + tests/detect-etherhdr.c \ tests/detect-ipv4hdr.c \ tests/detect-ipv6hdr.c \ tests/detect-tcphdr.c \ diff --git a/src/detect-engine-register.c b/src/detect-engine-register.c index 236d1d74f9..536715c7f5 100644 --- a/src/detect-engine-register.c +++ b/src/detect-engine-register.c @@ -191,6 +191,7 @@ #include "detect-tcphdr.h" #include "detect-tcpmss.h" #include "detect-udphdr.h" +#include "detect-etherhdr.h" #include "detect-icmpv6hdr.h" #include "detect-icmpv6-mtu.h" #include "detect-ipv4hdr.h" @@ -699,6 +700,7 @@ void SigTableSetup(void) DetectICMPv6hdrRegister(); DetectICMPv6mtuRegister(); DetectIPAddrBufferRegister(); + DetectEtherhdrRegister(); DetectIpv4hdrRegister(); DetectIpv6hdrRegister(); DetectKrb5CNameRegister(); diff --git a/src/detect-engine-register.h b/src/detect-engine-register.h index 716e17d653..68caf0141f 100644 --- a/src/detect-engine-register.h +++ b/src/detect-engine-register.h @@ -266,6 +266,7 @@ enum DetectKeywordId { DETECT_SIP_URI, DETECT_TEMPLATE, DETECT_TEMPLATE2, + DETECT_ETHERHDR, DETECT_IPV4HDR, DETECT_IPV6HDR, DETECT_ICMPV6HDR, diff --git a/src/detect-etherhdr.c b/src/detect-etherhdr.c new file mode 100644 index 0000000000..42ea68193f --- /dev/null +++ b/src/detect-etherhdr.c @@ -0,0 +1,123 @@ +/* Copyright (C) 2026 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + */ + +#include "suricata-common.h" + +#include "detect.h" +#include "detect-parse.h" +#include "detect-engine.h" +#include "detect-engine-buffer.h" +#include "detect-engine-mpm.h" +#include "detect-engine-prefilter.h" +#include "detect-engine-content-inspection.h" +#include "detect-fast-pattern.h" +#include "detect-etherhdr.h" + +/* prototypes */ +static int DetectEtherhdrSetup(DetectEngineCtx *, Signature *, const char *); +#ifdef UNITTESTS +void DetectEtherhdrRegisterTests(void); +#endif + +static int g_etherhdr_buffer_id = 0; + +static InspectionBuffer *GetData(DetectEngineThreadCtx *det_ctx, + const DetectEngineTransforms *transforms, Packet *p, const int list_id); + +/** + * \brief Registration function for ether.hdr: keyword + */ +void DetectEtherhdrRegister(void) +{ + sigmatch_table[DETECT_ETHERHDR].name = "ether.hdr"; + sigmatch_table[DETECT_ETHERHDR].desc = "sticky buffer to match on the Ethernet header"; + sigmatch_table[DETECT_ETHERHDR].url = "/rules/header-keywords.html#etherhdr"; + sigmatch_table[DETECT_ETHERHDR].Setup = DetectEtherhdrSetup; + sigmatch_table[DETECT_ETHERHDR].flags |= SIGMATCH_NOOPT | SIGMATCH_INFO_STICKY_BUFFER; +#ifdef UNITTESTS + sigmatch_table[DETECT_ETHERHDR].RegisterTests = DetectEtherhdrRegisterTests; +#endif + + g_etherhdr_buffer_id = DetectBufferTypeRegister("ether.hdr"); + BUG_ON(g_etherhdr_buffer_id < 0); + + DetectBufferTypeSupportsPacket("ether.hdr"); + + DetectPktMpmRegister("ether.hdr", 2, PrefilterGenericMpmPktRegister, GetData); + + DetectPktInspectEngineRegister("ether.hdr", GetData, DetectEngineInspectPktBufferGeneric); +} + +/** + * \brief setup ether.hdr sticky buffer + * + * \param de_ctx pointer to the Detection Engine Context + * \param s pointer to the Current Signature + * \param _unused unused + * + * \retval 0 on Success + * \retval -1 on Failure + */ +static int DetectEtherhdrSetup(DetectEngineCtx *de_ctx, Signature *s, const char *_unused) +{ + s->init_data->proto.flags |= DETECT_PROTO_ETHERNET; + s->flags |= SIG_FLAG_REQUIRE_PACKET; + + if (SCDetectBufferSetActiveList(de_ctx, s, g_etherhdr_buffer_id) < 0) + return -1; + + return 0; +} + +static InspectionBuffer *GetData(DetectEngineThreadCtx *det_ctx, + const DetectEngineTransforms *transforms, Packet *p, const int list_id) +{ + SCEnter(); + + InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id); + if (buffer->inspect == NULL) { + if (!PacketIsEthernet(p)) { + // DETECT_PROTO_ETHERNET does not prefilter + return NULL; + } + const EthernetHdr *ethh = PacketGetEthernet(p); + if (((uint8_t *)ethh + (ptrdiff_t)ETHERNET_HEADER_LEN) > + ((uint8_t *)GET_PKT_DATA(p) + (ptrdiff_t)GET_PKT_LEN(p))) { + SCLogDebug("data out of range: %p > %p", + ((uint8_t *)ethh + (ptrdiff_t)ETHERNET_HEADER_LEN), + ((uint8_t *)GET_PKT_DATA(p) + (ptrdiff_t)GET_PKT_LEN(p))); + return NULL; + } + + const uint32_t data_len = (uint32_t)ETHERNET_HEADER_LEN; + const uint8_t *data = (const uint8_t *)ethh; + SCLogDebug("inspect data %p / %u", data, data_len); + + InspectionBufferSetupAndApplyTransforms( + det_ctx, list_id, buffer, data, data_len, transforms); + } + + return buffer; +} + +#ifdef UNITTESTS +#include "tests/detect-etherhdr.c" +#endif diff --git a/src/detect-etherhdr.h b/src/detect-etherhdr.h new file mode 100644 index 0000000000..b1d4b36726 --- /dev/null +++ b/src/detect-etherhdr.h @@ -0,0 +1,27 @@ +/* Copyright (C) 2026 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + */ + +#ifndef SURICATA_DETECT_ETHERHDR_H +#define SURICATA_DETECT_ETHERHDR_H + +void DetectEtherhdrRegister(void); + +#endif /* SURICATA_DETECT_ETHERHDR_H */ diff --git a/src/tests/detect-etherhdr.c b/src/tests/detect-etherhdr.c new file mode 100644 index 0000000000..0bf43cf625 --- /dev/null +++ b/src/tests/detect-etherhdr.c @@ -0,0 +1,47 @@ +/* Copyright (C) 2026 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include "../suricata-common.h" + +#include "../detect.h" +#include "../detect-parse.h" +#include "../detect-engine-prefilter-common.h" + +#include "../detect-etherhdr.h" + +#include "../util-unittest.h" + +static int DetectEtherhdrParseTest01(void) +{ + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + + Signature *sig = DetectEngineAppendSig( + de_ctx, "alert ether any any -> any any (ether.hdr; content:\"A\"; sid:1; rev:1;)"); + FAIL_IF_NULL(sig); + + DetectEngineCtxFree(de_ctx); + PASS; +} + +/** + * \brief this function registers unit tests for ether.hdr + */ +void DetectEtherhdrRegisterTests(void) +{ + UtRegisterTest("DetectEtherhdrParseTest01", DetectEtherhdrParseTest01); +}