diff --git a/etc/schema.json b/etc/schema.json index 124a41867a..1415128e60 100644 --- a/etc/schema.json +++ b/etc/schema.json @@ -5106,6 +5106,20 @@ }, "optional": true }, + "rgmp": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "integer", + "suricata": { + "keywords": [ + "igmp.type" + ] + } + } + } + }, "rpc": { "type": "object", "additionalProperties": false, diff --git a/src/decode-igmp.c b/src/decode-igmp.c index 796bf7d26c..d342a90835 100644 --- a/src/decode-igmp.c +++ b/src/decode-igmp.c @@ -34,6 +34,9 @@ * Divided by 4 bytes per address. */ #define IGMP_V3_MAX_N_SOURCES (65535 - 24 - 12) / 4 +/* RGMP requires a specific dest address 224.0.0.25. */ +#define RGMP_DEST_ADDRESS 0xe0000019 + typedef struct IGMPv3MemberQueryHdr_ { uint8_t type; uint8_t max_resp_time; @@ -77,6 +80,22 @@ int DecodeIGMP(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t * p->proto = IPPROTO_IGMP; uint8_t version; + /* see if we're RGMP (RFC 3488): + * "All RGMP messages are sent with TTL 1, to destination address 224.0.0.25." + */ + bool rgmp = false; + if (PacketIsIPv4(p)) { + const IPV4Hdr *ip4h = PacketGetIPv4(p); + const uint8_t pttl = IPV4_GET_RAW_IPTTL(ip4h); + /* if packet is of the correct length (header size 8) to the correct address + * and has a ttl of 1, we consider it RGMP. */ + if (len == 8 && RGMP_DEST_ADDRESS == SCNtohl(p->dst.address.address_un_data32[0]) && + pttl == 1) { + SCLogDebug("RGMP (RFC 3488)"); + rgmp = true; + } + } + /* For IGMPv3 Membership Query, we need to handle additional fields */ if (igmp->type == IGMP_TYPE_MEMBERSHIP_QUERY) { if (len >= 12) { @@ -172,6 +191,7 @@ int DecodeIGMP(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t * p->payload_len = (uint16_t)(len - sizeof(IGMPHdr)); } p->l4.vars.igmp.version = version; + p->l4.vars.igmp.rgmp = rgmp; return TM_ECODE_OK; } diff --git a/src/decode-igmp.h b/src/decode-igmp.h index 68fc0322ae..0a7c4ac11e 100644 --- a/src/decode-igmp.h +++ b/src/decode-igmp.h @@ -32,6 +32,7 @@ typedef struct IGMPHdr_ { typedef struct IGMPVars_ { uint16_t hlen; /**< length of the header */ uint8_t version; + bool rgmp; /**< is this really a RFC 3488 RGMP packet */ } IGMPVars; #endif /* SURICATA_DECODE_IGMP_H */ diff --git a/src/output-json.c b/src/output-json.c index fc110435af..2d70b59ce4 100644 --- a/src/output-json.c +++ b/src/output-json.c @@ -926,10 +926,17 @@ SCJsonBuilder *CreateEveHeader(const Packet *p, enum SCOutputJsonLogDirection di break; case IPPROTO_IGMP: if (PacketIsIGMP(p)) { - SCJbOpenObject(js, "igmp"); - SCJbSetUint(js, "type", PacketGetIGMP(p)->type); - SCJbSetUint(js, "version", p->l4.vars.igmp.version); - SCJbClose(js); + SCLogDebug("rgmp %s", BOOL2STR(p->l4.vars.igmp.rgmp)); + if (!p->l4.vars.igmp.rgmp) { + SCJbOpenObject(js, "igmp"); + SCJbSetUint(js, "type", PacketGetIGMP(p)->type); + SCJbSetUint(js, "version", p->l4.vars.igmp.version); + SCJbClose(js); + } else { + SCJbOpenObject(js, "rgmp"); + SCJbSetUint(js, "type", PacketGetIGMP(p)->type); + SCJbClose(js); + } } break; }