decode/flow/esp: Add ESP decoder & flow

- Adds an ESP (Encapsulating Security Payload) header decoder
- Tracks ESP flows via the SPI field
pull/5743/head
Emmanuel Thompson 6 years ago committed by Victor Julien
parent 9adeae07b1
commit f12daa710f

@ -81,6 +81,7 @@ decode-ppp.c decode-ppp.h \
decode-pppoe.c decode-pppoe.h \
decode-raw.c decode-raw.h \
decode-sctp.c decode-sctp.h \
decode-esp.c decode-esp.h \
decode-sll.c decode-sll.h \
decode-tcp.c decode-tcp.h \
decode-teredo.c decode-teredo.h \

@ -0,0 +1,198 @@
/* Copyright (C) 2020 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.
*/
/**
* \ingroup decode
*
* @{
*/
/**
* \file
*
* Decode Encapsulating Security Payload (ESP)
*/
#include "suricata-common.h"
#include "decode-esp.h"
#include "flow.h"
static int DecodeESPPacket(ThreadVars *tv, Packet *p, const uint8_t *pkt, uint16_t len)
{
if (unlikely(len < ESP_HEADER_LEN)) {
ENGINE_SET_INVALID_EVENT(p, ESP_PKT_TOO_SMALL);
return -1;
}
p->esph = (ESPHdr *)pkt;
p->payload = (uint8_t *)pkt + sizeof(ESPHdr);
p->payload_len = len - sizeof(ESPHdr);
p->proto = IPPROTO_ESP;
return 0;
}
/**
* \brief Function to decode IPSEC-ESP packets
* \param tv thread vars
* \param dtv decoder thread vars
* \param p packet
* \param pkt raw packet data
* \param len length in bytes of pkt array
* \retval TM_ECODE_OK or TM_ECODE_FAILED on serious error
*/
int DecodeESP(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *pkt, uint16_t len)
{
StatsIncr(tv, dtv->counter_esp);
if (unlikely(DecodeESPPacket(tv, p, pkt, len) < 0)) {
CLEAR_ESP_PACKET(p);
return TM_ECODE_FAILED;
}
SCLogDebug("ESP spi: %" PRIu32 " sequence: %" PRIu32, ESP_GET_SPI(p), ESP_GET_SEQUENCE(p));
FlowSetupPacket(p);
return TM_ECODE_OK;
}
#ifdef UNITTESTS
#include "util-unittest.h"
/** \test Successful decoding */
static int DecodeESPTest01(void)
{
uint8_t raw_esp[] = { 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x08 };
Packet *p = PacketGetFromAlloc();
FAIL_IF_NULL(p);
ThreadVars tv;
DecodeThreadVars dtv;
memset(&tv, 0, sizeof(ThreadVars));
memset(&dtv, 0, sizeof(DecodeThreadVars));
int ret = DecodeESP(&tv, &dtv, p, raw_esp, sizeof(raw_esp));
FAIL_IF(ret != TM_ECODE_OK);
FAIL_IF(p->proto != IPPROTO_ESP);
FAIL_IF(p->payload_len != sizeof(raw_esp) - ESP_HEADER_LEN);
FAIL_IF(ESP_GET_SPI(p) != 0x7b);
FAIL_IF(ESP_GET_SEQUENCE(p) != 0x08);
SCFree(p);
PASS;
}
/** \test Successful decoding, with payload data */
static int DecodeESPTest02(void)
{
uint8_t raw_esp[] = { 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x08, 0xFF, 0xFF };
Packet *p = PacketGetFromAlloc();
FAIL_IF_NULL(p);
ThreadVars tv;
DecodeThreadVars dtv;
memset(&tv, 0, sizeof(ThreadVars));
memset(&dtv, 0, sizeof(DecodeThreadVars));
int ret = DecodeESP(&tv, &dtv, p, raw_esp, sizeof(raw_esp));
FAIL_IF(ret != TM_ECODE_OK);
FAIL_IF(p->proto != IPPROTO_ESP);
FAIL_IF(p->payload_len != sizeof(raw_esp) - ESP_HEADER_LEN);
FAIL_IF(memcmp(p->payload, raw_esp + ESP_HEADER_LEN, p->payload_len) != 0);
FAIL_IF(ESP_GET_SPI(p) != 0x7b);
FAIL_IF(ESP_GET_SEQUENCE(p) != 0x08);
SCFree(p);
PASS;
}
/** \test Failure decoding, not enough data */
static int DecodeESPTest03(void)
{
uint8_t raw_esp[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
Packet *p = PacketGetFromAlloc();
FAIL_IF_NULL(p);
ThreadVars tv;
DecodeThreadVars dtv;
memset(&tv, 0, sizeof(ThreadVars));
memset(&dtv, 0, sizeof(DecodeThreadVars));
int ret = DecodeESP(&tv, &dtv, p, raw_esp, sizeof(raw_esp));
FAIL_IF(ret != TM_ECODE_FAILED);
// expect ESP_PKT_TOO_SMALL
FAIL_IF_NOT(ENGINE_ISSET_EVENT(p, ESP_PKT_TOO_SMALL));
SCFree(p);
PASS;
}
/** \test Failure decoding, no data */
static int DecodeESPTest04(void)
{
uint8_t raw_esp[] = {};
Packet *p = PacketGetFromAlloc();
FAIL_IF_NULL(p);
ThreadVars tv;
DecodeThreadVars dtv;
memset(&tv, 0, sizeof(ThreadVars));
memset(&dtv, 0, sizeof(DecodeThreadVars));
int ret = DecodeESP(&tv, &dtv, p, raw_esp, sizeof(raw_esp));
FAIL_IF(ret != TM_ECODE_FAILED);
// expect ESP_PKT_TOO_SMALL
FAIL_IF_NOT(ENGINE_ISSET_EVENT(p, ESP_PKT_TOO_SMALL));
SCFree(p);
PASS;
}
#endif /* UNITTESTS */
void DecodeESPRegisterTests(void)
{
#ifdef UNITTESTS
UtRegisterTest("DecodeESPTest01", DecodeESPTest01);
UtRegisterTest("DecodeESPTest02", DecodeESPTest02);
UtRegisterTest("DecodeESPTest03", DecodeESPTest03);
UtRegisterTest("DecodeESPTest04", DecodeESPTest04);
#endif /* UNITTESTS */
}
/**
* @}
*/

@ -0,0 +1,51 @@
/* Copyright (C) 2020 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 __DECODE_ESP_H__
#define __DECODE_ESP_H__
/** \brief size of the ESP header */
#define ESP_HEADER_LEN 8
#define ESP_GET_RAW_SPI(esph) SCNtohl((esph)->spi)
#define ESP_GET_RAW_SEQUENCE(esph) SCNtohl((esph)->sequence)
/** \brief Get the spi field off a packet */
#define ESP_GET_SPI(p) ESP_GET_RAW_SPI(p->esph)
/** \brief Get the sequence field off a packet */
#define ESP_GET_SEQUENCE(p) ESP_GET_RAW_SEQUENCE(p->esph)
/** \brief ESP Header */
typedef struct ESPHdr_ {
uint32_t spi; /** < ESP Security Parameters Index */
uint32_t sequence; /** < ESP sequence number */
} __attribute__((__packed__)) ESPHdr;
#define CLEAR_ESP_PACKET(p) \
{ \
(p)->esph = NULL; \
} \
while (0)
void DecodeESPRegisterTests(void);
#endif /* __DECODE_ESP_H__ */

@ -425,6 +425,12 @@ const struct DecodeEvents_ DEvents[] = {
SCTP_PKT_TOO_SMALL,
},
/* ESP EVENTS */
{
"decoder.esp.pkt_too_small",
ESP_PKT_TOO_SMALL,
},
/* Fragmentation reasembly events. */
{
"decoder.ipv4.frag_pkt_too_large",

@ -157,6 +157,9 @@ enum {
/* SCTP EVENTS */
SCTP_PKT_TOO_SMALL, /**< sctp packet smaller than minimum size */
/* ESP EVENTS */
ESP_PKT_TOO_SMALL, /**< esp packet smaller than minimum size */
/* Fragmentation reasembly events. */
IPV4_FRAG_PKT_TOO_LARGE,
IPV6_FRAG_PKT_TOO_LARGE,

@ -575,6 +575,11 @@ int DecodeIPV4(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p,
DecodeSCTP(tv, dtv, p, pkt + IPV4_GET_HLEN(p),
IPV4_GET_IPLEN(p) - IPV4_GET_HLEN(p));
break;
case IPPROTO_ESP:
DecodeESP(tv, dtv, p, pkt + IPV4_GET_HLEN(p), IPV4_GET_IPLEN(p) - IPV4_GET_HLEN(p));
break;
case IPPROTO_IPV6:
{
/* spawn off tunnel packet */

@ -151,7 +151,6 @@ DecodeIPV6ExtHdrs(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p,
char exthdr_fh_done = 0;
int hh = 0;
int rh = 0;
int eh = 0;
int ah = 0;
while(1)
@ -462,24 +461,9 @@ DecodeIPV6ExtHdrs(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p,
case IPPROTO_ESP:
{
IPV6_SET_L4PROTO(p,nh);
hdrextlen = sizeof(IPV6EspHdr);
if (hdrextlen > plen) {
ENGINE_SET_INVALID_EVENT(p, IPV6_TRUNC_EXTHDR);
SCReturn;
}
if (eh) {
ENGINE_SET_EVENT(p, IPV6_EXTHDR_DUPL_EH);
DecodeESP(tv, dtv, p, pkt, plen);
SCReturn;
}
eh = 1;
nh = IPPROTO_NONE;
pkt += hdrextlen;
plen -= hdrextlen;
break;
}
case IPPROTO_AH:
{
IPV6_SET_L4PROTO(p,nh);

@ -500,6 +500,7 @@ void DecodeRegisterPerfCounters(DecodeThreadVars *dtv, ThreadVars *tv)
dtv->counter_tcp = StatsRegisterCounter("decoder.tcp", tv);
dtv->counter_udp = StatsRegisterCounter("decoder.udp", tv);
dtv->counter_sctp = StatsRegisterCounter("decoder.sctp", tv);
dtv->counter_esp = StatsRegisterCounter("decoder.esp", tv);
dtv->counter_icmpv4 = StatsRegisterCounter("decoder.icmpv4", tv);
dtv->counter_icmpv6 = StatsRegisterCounter("decoder.icmpv6", tv);
dtv->counter_ppp = StatsRegisterCounter("decoder.ppp", tv);

@ -90,6 +90,7 @@ enum PktSrcEnum {
#include "decode-tcp.h"
#include "decode-udp.h"
#include "decode-sctp.h"
#include "decode-esp.h"
#include "decode-raw.h"
#include "decode-null.h"
#include "decode-vlan.h"
@ -215,7 +216,6 @@ typedef struct Address_ {
} while (0)
#define GET_IPV4_SRC_ADDR_U32(p) ((p)->src.addr_data32[0])
#define GET_IPV4_DST_ADDR_U32(p) ((p)->dst.addr_data32[0])
#define GET_IPV4_SRC_ADDR_PTR(p) ((p)->src.addr_data32)
@ -535,6 +535,8 @@ typedef struct Packet_
SCTPHdr *sctph;
ESPHdr *esph;
ICMPV4Hdr *icmpv4h;
ICMPV6Hdr *icmpv6h;
@ -656,6 +658,7 @@ typedef struct DecodeThreadVars_
uint16_t counter_raw;
uint16_t counter_null;
uint16_t counter_sctp;
uint16_t counter_esp;
uint16_t counter_ppp;
uint16_t counter_geneve;
uint16_t counter_gre;
@ -797,6 +800,9 @@ void CaptureStatsSetup(ThreadVars *tv, CaptureStats *s);
if ((p)->sctph != NULL) { \
CLEAR_SCTP_PACKET((p)); \
} \
if ((p)->esph != NULL) { \
CLEAR_ESP_PACKET((p)); \
} \
if ((p)->icmpv4h != NULL) { \
CLEAR_ICMPV4_PACKET((p)); \
} \
@ -956,6 +962,7 @@ int DecodeICMPV6(ThreadVars *, DecodeThreadVars *, Packet *, const uint8_t *, ui
int DecodeTCP(ThreadVars *, DecodeThreadVars *, Packet *, const uint8_t *, uint16_t);
int DecodeUDP(ThreadVars *, DecodeThreadVars *, Packet *, const uint8_t *, uint16_t);
int DecodeSCTP(ThreadVars *, DecodeThreadVars *, Packet *, const uint8_t *, uint16_t);
int DecodeESP(ThreadVars *, DecodeThreadVars *, Packet *, const uint8_t *, uint16_t);
int DecodeGRE(ThreadVars *, DecodeThreadVars *, Packet *, const uint8_t *, uint32_t);
int DecodeVLAN(ThreadVars *, DecodeThreadVars *, Packet *, const uint8_t *, uint32_t);
int DecodeIEEE8021ah(ThreadVars *, DecodeThreadVars *, Packet *, const uint8_t *, uint32_t);

@ -407,6 +407,27 @@ static inline int FlowCompareICMPv4(Flow *f, const Packet *p)
return 0;
}
/**
* \brief See if a IP-ESP packet belongs to a flow by comparing the SPI
*
* \param f flow
* \param p ESP packet
*
* \retval 1 match
* \retval 0 no match
*/
static inline int FlowCompareESP(Flow *f, const Packet *p)
{
const uint32_t *f_src = f->src.address.address_un_data32;
const uint32_t *f_dst = f->dst.address.address_un_data32;
const uint32_t *p_src = p->src.address.address_un_data32;
const uint32_t *p_dst = p->dst.address.address_un_data32;
return CmpAddrs(f_src, p_src) && CmpAddrs(f_dst, p_dst) && f->proto == p->proto &&
f->recursion_level == p->recursion_level && CmpVlanIds(f->vlan_id, p->vlan_id) &&
f->esp.spi == ESP_GET_SPI(p);
}
void FlowSetupPacket(Packet *p)
{
p->flags |= PKT_WANTS_FLOW;
@ -417,6 +438,8 @@ static inline int FlowCompare(Flow *f, const Packet *p)
{
if (p->proto == IPPROTO_ICMP) {
return FlowCompareICMPv4(f, p);
} else if (p->proto == IPPROTO_ESP) {
return FlowCompareESP(f, p);
} else {
return CmpFlowPacket(f, p);
}

@ -187,6 +187,8 @@ void FlowInit(Flow *f, const Packet *p)
} else if (p->sctph != NULL) { /* XXX MACRO */
SET_SCTP_SRC_PORT(p,&f->sp);
SET_SCTP_DST_PORT(p,&f->dp);
} else if (p->esph != NULL) {
f->esp.spi = ESP_GET_SPI(p);
} /* XXX handle default */
#ifdef DEBUG
else {

@ -354,6 +354,10 @@ typedef struct Flow_
uint8_t type; /**< icmp type */
uint8_t code; /**< icmp code */
} icmp_s;
struct {
uint32_t spi; /**< esp spi */
} esp;
};
union {
Port dp; /**< tcp/udp destination port */

@ -171,6 +171,9 @@ static JsonBuilder *CreateEveHeaderFromFlow(const Flow *f)
jb_set_uint(jb, "response_icmp_code", f->icmp_d.code);
}
break;
case IPPROTO_ESP:
jb_set_uint(jb, "spi", f->esp.spi);
break;
}
return jb;
}

@ -179,6 +179,9 @@ static JsonBuilder *CreateEveHeaderFromNetFlow(const Flow *f, int dir)
jb_set_uint(js, "icmp_code", code);
break;
}
case IPPROTO_ESP:
jb_set_uint(js, "spi", f->esp.spi);
break;
}
return js;
}

@ -153,6 +153,7 @@ static void RegisterUnittests(void)
DecodeTCPRegisterTests();
DecodeUDPV4RegisterTests();
DecodeGRERegisterTests();
DecodeESPRegisterTests();
DecodeMPLSRegisterTests();
DecodeNSHRegisterTests();
AppLayerProtoDetectUnittestsRegister();
@ -292,4 +293,3 @@ void RunUnittests(int list_unittests, const char *regex_arg)
FatalError(SC_ERR_FATAL, "Unittests are not build-in");
#endif /* UNITTESTS */
}

Loading…
Cancel
Save