diff --git a/etc/schema.json b/etc/schema.json index 4cff77cefb..fab5361eea 100644 --- a/etc/schema.json +++ b/etc/schema.json @@ -7109,6 +7109,10 @@ "type": "integer", "description": "Number of packets with too many VLAN layers for ERSPAN" }, + "unsupported_encapsulation": { + "type": "integer", + "description": "Number of packets with unsupported encapsulation for ERSPAN" + }, "unsupported_version": { "type": "integer", "description": "Number of packets with unsupported version for ERSPAN" diff --git a/src/decode-erspan.c b/src/decode-erspan.c index 3d4a68e4c1..88411914c8 100644 --- a/src/decode-erspan.c +++ b/src/decode-erspan.c @@ -47,6 +47,14 @@ typedef struct ErspanHdr_ { uint32_t padding; } __attribute__((__packed__)) ErspanHdr; +typedef struct Erspan3Hdr_ { + uint16_t ver_vlan; + uint16_t flags_spanid; + uint32_t timestamp; + uint16_t sgt; + uint16_t flags; +} __attribute__((__packed__)) Erspan3Hdr; + /** * \brief Functions to decode ERSPAN Type I and II packets */ @@ -117,6 +125,69 @@ int DecodeERSPAN(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t return DecodeEthernet(tv, dtv, p, pkt + sizeof(ErspanHdr), len - sizeof(ErspanHdr)); } +/** + * \brief ERSPAN Type III + */ +int DecodeERSPANTypeIII( + ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *pkt, uint32_t len) +{ + DEBUG_VALIDATE_BUG_ON(pkt == NULL); + + StatsCounterIncr(&tv->stats, dtv->counter_erspan); + + if (len < sizeof(Erspan3Hdr)) { + ENGINE_SET_EVENT(p, ERSPAN_HEADER_TOO_SMALL); + return TM_ECODE_FAILED; + } + if (!PacketIncreaseCheckLayers(p)) { + return TM_ECODE_FAILED; + } + + const Erspan3Hdr *ehdr = (const Erspan3Hdr *)pkt; + uint16_t version = SCNtohs(ehdr->ver_vlan) >> 12; + uint16_t vlan_id = SCNtohs(ehdr->ver_vlan) & 0x0fff; + uint16_t flags = SCNtohs(ehdr->flags); + + pkt += sizeof(Erspan3Hdr); + len -= sizeof(Erspan3Hdr); + + if (version != 2) { + ENGINE_SET_EVENT(p, ERSPAN_UNSUPPORTED_VERSION); + return TM_ECODE_FAILED; + } + + /* If O flag is set, then the optional platform specific header is present. + * We need to skip past it. + */ +#define PLATFORM_SPECIFIC_HDR_SIZE 8 + if (flags & 0x0001) { + if (len < PLATFORM_SPECIFIC_HDR_SIZE) { + ENGINE_SET_EVENT(p, ERSPAN_HEADER_TOO_SMALL); + return TM_ECODE_FAILED; + } + + pkt += PLATFORM_SPECIFIC_HDR_SIZE; + len -= PLATFORM_SPECIFIC_HDR_SIZE; + } + + if (vlan_id > 0) { + if (p->vlan_idx > VLAN_MAX_LAYER_IDX) { + ENGINE_SET_EVENT(p, ERSPAN_TOO_MANY_VLAN_LAYERS); + return TM_ECODE_FAILED; + } + p->vlan_id[p->vlan_idx] = vlan_id; + p->vlan_idx++; + } + + switch ((flags & 0x7c00) >> 10) { + case 0x00: /* ethernet */ + return DecodeEthernet(tv, dtv, p, pkt, len); + default: + ENGINE_SET_EVENT(p, ERSPAN_UNSUPPORTED_ENCAPSULATION); + return TM_ECODE_FAILED; + } +} + /** * @} */ diff --git a/src/decode-ethernet.h b/src/decode-ethernet.h index 992f7530bf..7b98c3f110 100644 --- a/src/decode-ethernet.h +++ b/src/decode-ethernet.h @@ -46,6 +46,7 @@ #define ETHERNET_TYPE_LOOP 0x9000 #define ETHERNET_TYPE_8021QINQ 0x9100 #define ETHERNET_TYPE_ERSPAN 0x88BE +#define ETHERNET_TYPE_ERSPAN_III 0x22EB /* ERSPAN III */ #define ETHERNET_TYPE_DCE 0x8903 /* Data center ethernet, * Cisco Fabric Path */ #define ETHERNET_TYPE_ETAG 0x893F /* 802.1BR */ diff --git a/src/decode-events.c b/src/decode-events.c index 3f37713dd3..c6cda0c93b 100644 --- a/src/decode-events.c +++ b/src/decode-events.c @@ -603,6 +603,10 @@ const struct DecodeEvents_ DEvents[] = { "decoder.erspan.too_many_vlan_layers", ERSPAN_TOO_MANY_VLAN_LAYERS, }, + { + "decoder.erspan.unsupported_encapsulation", + ERSPAN_UNSUPPORTED_ENCAPSULATION, + }, /* Cisco Fabric Path/DCE events. */ { diff --git a/src/decode-events.h b/src/decode-events.h index 45602235d5..15f2483edd 100644 --- a/src/decode-events.h +++ b/src/decode-events.h @@ -221,6 +221,7 @@ enum { ERSPAN_HEADER_TOO_SMALL, ERSPAN_UNSUPPORTED_VERSION, ERSPAN_TOO_MANY_VLAN_LAYERS, + ERSPAN_UNSUPPORTED_ENCAPSULATION, /* Cisco Fabric Path/DCE events. */ DCE_PKT_TOO_SMALL, diff --git a/src/decode-gre.c b/src/decode-gre.c index 7045cdf20f..b34edf81f0 100644 --- a/src/decode-gre.c +++ b/src/decode-gre.c @@ -262,6 +262,15 @@ int DecodeGRE(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *p } break; } + case ETHERNET_TYPE_ERSPAN_III: { + Packet *tp = PacketTunnelPktSetup( + tv, dtv, p, pkt + header_len, len - header_len, DECODE_TUNNEL_ERSPANIII); + if (tp != NULL) { + PKT_SET_SRC(tp, PKT_SRC_DECODER_GRE); + PacketEnqueueNoLock(&tv->decode_pq, tp); + } + break; + } case ETHERNET_TYPE_BRIDGE: { diff --git a/src/decode.c b/src/decode.c index 9d7b2df244..32d7c4b2ab 100644 --- a/src/decode.c +++ b/src/decode.c @@ -198,6 +198,8 @@ static int DecodeTunnel(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const return DecodeVLAN(tv, dtv, p, pkt, len); case DECODE_TUNNEL_ETHERNET: return DecodeEthernet(tv, dtv, p, pkt, len); + case DECODE_TUNNEL_ERSPANIII: + return DecodeERSPANTypeIII(tv, dtv, p, pkt, len); case DECODE_TUNNEL_ERSPANII: return DecodeERSPAN(tv, dtv, p, pkt, len); case DECODE_TUNNEL_ERSPANI: diff --git a/src/decode.h b/src/decode.h index fb7b1466ef..b9878ced7b 100644 --- a/src/decode.h +++ b/src/decode.h @@ -1142,6 +1142,7 @@ static inline void PacketTunnelSetVerdicted(Packet *p) enum DecodeTunnelProto { DECODE_TUNNEL_ETHERNET, + DECODE_TUNNEL_ERSPANIII, DECODE_TUNNEL_ERSPANII, DECODE_TUNNEL_ERSPANI, DECODE_TUNNEL_VXLAN, @@ -1206,6 +1207,7 @@ int DecodeIEEE8021ah(ThreadVars *, DecodeThreadVars *, Packet *, const uint8_t * int DecodeGeneve(ThreadVars *, DecodeThreadVars *, Packet *, const uint8_t *, uint32_t); int DecodeVXLAN(ThreadVars *, DecodeThreadVars *, Packet *, const uint8_t *, uint32_t); int DecodeMPLS(ThreadVars *, DecodeThreadVars *, Packet *, const uint8_t *, uint32_t); +int DecodeERSPANTypeIII(ThreadVars *, DecodeThreadVars *, Packet *, const uint8_t *, uint32_t); int DecodeERSPAN(ThreadVars *, DecodeThreadVars *, Packet *, const uint8_t *, uint32_t); int DecodeERSPANTypeI(ThreadVars *, DecodeThreadVars *, Packet *, const uint8_t *, uint32_t); int DecodeCHDLC(ThreadVars *, DecodeThreadVars *, Packet *, const uint8_t *, uint32_t);