suricata
decode-igmp.c
Go to the documentation of this file.
1 /* Copyright (C) 2026 Open Information Security Foundation
2  *
3  * You can copy, redistribute or modify this Program under the terms of
4  * the GNU General Public License version 2 as published by the Free
5  * Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * version 2 along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15  * 02110-1301, USA.
16  */
17 
18 /**
19  * \file
20  */
21 
22 #include "suricata-common.h"
23 #include "decode.h"
24 #include "util-debug.h"
25 
26 #define IGMP_TYPE_MEMBERSHIP_QUERY 0x11
27 #define IGMP_TYPE_MEMBERSHIP_REPORT_V1 0x12
28 #define IGMP_TYPE_MEMBERSHIP_REPORT_V2 0x16
29 #define IGMP_TYPE_LEAVE_GROUP_V2 0x17
30 #define IGMP_TYPE_MEMBERSHIP_REPORT_V3 0x22
31 
32 /* Data size: IP segment size (64k), minus IP header + RA option (24),
33  * minus IGMPv3 header (12).
34  * Divided by 4 bytes per address. */
35 #define IGMP_V3_MAX_N_SOURCES (65535 - 24 - 12) / 4
36 
37 /* RGMP requires a specific dest address 224.0.0.25. */
38 #define RGMP_DEST_ADDRESS 0xe0000019
39 
40 typedef struct IGMPv3MemberQueryHdr_ {
41  uint8_t type;
42  uint8_t max_resp_time;
43  uint16_t checksum;
44  uint32_t group_address;
45  uint8_t s_qrv;
46  uint8_t qqic;
47  uint16_t n_sources;
48  /* Followed by source addresses */
50 
52  uint8_t type;
53  uint8_t aux_data_len; /**< in units of 32-bit words */
54  uint16_t n_sources;
55  uint32_t mcast_addr;
56  // source address
57  // aux data
59 
60 typedef struct IGMPv3MemberReportHdr_ {
61  uint8_t type;
62  uint8_t res;
63  uint16_t checksum;
64  uint16_t flags;
65  uint16_t n_group_recs;
66  /* Followed by group records */
67 
69 
70 int DecodeIGMP(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *pkt, uint32_t len)
71 {
73 
74  if (len < sizeof(IGMPHdr)) {
76  return TM_ECODE_FAILED;
77  }
78 
79  const IGMPHdr *igmp = PacketSetIGMP(p, pkt);
80  p->proto = IPPROTO_IGMP;
81  uint8_t version;
82 
83  /* see if we're RGMP (RFC 3488):
84  * "All RGMP messages are sent with TTL 1, to destination address 224.0.0.25."
85  */
86  bool rgmp = false;
87  if (PacketIsIPv4(p)) {
88  const IPV4Hdr *ip4h = PacketGetIPv4(p);
89  const uint8_t pttl = IPV4_GET_RAW_IPTTL(ip4h);
90  /* if packet is of the correct length (header size 8) to the correct address
91  * and has a ttl of 1, we consider it RGMP. */
92  if (len == 8 && RGMP_DEST_ADDRESS == SCNtohl(p->dst.address.address_un_data32[0]) &&
93  pttl == 1) {
94  SCLogDebug("RGMP (RFC 3488)");
95  rgmp = true;
96  }
97  }
98 
99  /* For IGMPv3 Membership Query, we need to handle additional fields */
100  if (igmp->type == IGMP_TYPE_MEMBERSHIP_QUERY) {
101  if (len >= 12) {
102  const IGMPv3MemberQueryHdr *igmpv3 = (const IGMPv3MemberQueryHdr *)pkt;
103  const uint32_t n_sources = SCNtohs(igmpv3->n_sources);
104  if (n_sources > IGMP_V3_MAX_N_SOURCES) {
106  return TM_ECODE_FAILED;
107  }
108 
109  const uint32_t header_len =
110  sizeof(IGMPv3MemberQueryHdr) + (n_sources * sizeof(uint32_t));
111  if (len < header_len) {
112  SCLogDebug("len %u < header_len %u", len, header_len);
114  return TM_ECODE_FAILED;
115  }
116  if (header_len >= UINT16_MAX) {
118  return TM_ECODE_FAILED;
119  }
120  p->l4.vars.igmp.hlen = (uint16_t)header_len;
121  p->payload = (uint8_t *)pkt + header_len;
122  p->payload_len = (uint16_t)(len - header_len);
123  version = 3;
124  } else {
125  if (igmp->max_resp_time == 0) {
126  version = 1;
127  } else {
128  version = 2;
129  }
130  p->l4.vars.igmp.hlen = (uint16_t)sizeof(IGMPHdr);
131  p->payload = (uint8_t *)pkt + sizeof(IGMPHdr);
132  p->payload_len = (uint16_t)(len - sizeof(IGMPHdr));
133  }
134 
135  } else if (igmp->type == IGMP_TYPE_MEMBERSHIP_REPORT_V3) {
136  const IGMPv3MemberReportHdr *igmpv3 = (const IGMPv3MemberReportHdr *)pkt;
137  const uint32_t n_group_recs = SCNtohs(igmpv3->n_group_recs);
138  uint32_t header_len = sizeof(IGMPv3MemberReportHdr);
139 
140  /* parse group records */
141  for (uint32_t i = 0; i < n_group_recs; i++) {
142  if (len - header_len < sizeof(IGMPv3MemberReportGroupRecord)) {
143  SCLogDebug("len %u - header_len %u < IGMPv3MemberReportGroupRecord %u", len,
144  header_len, (uint32_t)sizeof(IGMPv3MemberReportGroupRecord));
146  return TM_ECODE_FAILED;
147  }
148 
149  const IGMPv3MemberReportGroupRecord *grec =
150  (const IGMPv3MemberReportGroupRecord *)(pkt + header_len);
151  header_len += sizeof(IGMPv3MemberReportGroupRecord);
152 
153  if (len - header_len < (uint32_t)(grec->aux_data_len * sizeof(uint32_t))) {
154  SCLogDebug("len %u - header_len %u aux_data_len %u", len, header_len,
155  (uint32_t)grec->aux_data_len);
157  return TM_ECODE_FAILED;
158  }
159  header_len += (uint32_t)(grec->aux_data_len * sizeof(uint32_t));
160 
161  uint32_t sources_len = (uint32_t)SCNtohs(grec->n_sources) * (uint32_t)sizeof(uint32_t);
162  if (len - header_len < sources_len) {
163  SCLogDebug("len %u - header_len %u sources_len %u", len, header_len, sources_len);
165  return TM_ECODE_FAILED;
166  }
167  header_len += sources_len;
168  }
169  if (header_len >= UINT16_MAX) {
171  return TM_ECODE_FAILED;
172  }
173  p->l4.vars.igmp.hlen = (uint16_t)header_len;
174  p->payload = (uint8_t *)pkt + header_len;
175  p->payload_len = (uint16_t)(len - header_len);
176  version = 3;
177 
178  } else {
179  if (igmp->type == IGMP_TYPE_MEMBERSHIP_REPORT_V1) {
180  version = 1;
181 
182  } else if (igmp->type == IGMP_TYPE_MEMBERSHIP_REPORT_V2 ||
183  igmp->type == IGMP_TYPE_LEAVE_GROUP_V2) {
184  version = 2;
185  } else {
186  version = 255;
187  }
188 
189  p->l4.vars.igmp.hlen = (uint16_t)sizeof(IGMPHdr);
190  p->payload = (uint8_t *)pkt + sizeof(IGMPHdr);
191  p->payload_len = (uint16_t)(len - sizeof(IGMPHdr));
192  }
193  p->l4.vars.igmp.version = version;
194  p->l4.vars.igmp.rgmp = rgmp;
195 
196  return TM_ECODE_OK;
197 }
Packet_::proto
uint8_t proto
Definition: decode.h:527
len
uint8_t len
Definition: app-layer-dnp3.h:2
IGMPv3MemberQueryHdr_
Definition: decode-igmp.c:40
IGMP_PKT_TOO_SMALL
@ IGMP_PKT_TOO_SMALL
Definition: decode-events.h:233
IGMP_TYPE_LEAVE_GROUP_V2
#define IGMP_TYPE_LEAVE_GROUP_V2
Definition: decode-igmp.c:29
IGMPv3MemberReportHdr_::type
uint8_t type
Definition: decode-igmp.c:61
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:282
Packet_::payload
uint8_t * payload
Definition: decode.h:609
IGMPv3MemberReportGroupRecord_::mcast_addr
uint32_t mcast_addr
Definition: decode-igmp.c:55
IGMPv3MemberQueryHdr_::checksum
uint16_t checksum
Definition: decode-igmp.c:43
IGMP_TYPE_MEMBERSHIP_QUERY
#define IGMP_TYPE_MEMBERSHIP_QUERY
Definition: decode-igmp.c:26
Address_::address_un_data32
uint32_t address_un_data32[4]
Definition: decode.h:116
TM_ECODE_FAILED
@ TM_ECODE_FAILED
Definition: tm-threads-common.h:82
IGMPv3MemberReportHdr
struct IGMPv3MemberReportHdr_ IGMPv3MemberReportHdr
Packet_::payload_len
uint16_t payload_len
Definition: decode.h:610
IGMPHdr_::type
uint8_t type
Definition: decode-igmp.h:26
TM_ECODE_OK
@ TM_ECODE_OK
Definition: tm-threads-common.h:81
IGMPHdr_::max_resp_time
uint8_t max_resp_time
Definition: decode-igmp.h:27
Address_::address
union Address_::@29 address
IGMPv3MemberReportGroupRecord_
Definition: decode-igmp.c:51
DecodeIGMP
int DecodeIGMP(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *pkt, uint32_t len)
Definition: decode-igmp.c:70
IGMPv3MemberReportHdr_
Definition: decode-igmp.c:60
decode.h
IGMPv3MemberReportGroupRecord_::n_sources
uint16_t n_sources
Definition: decode-igmp.c:54
util-debug.h
IGMPv3MemberQueryHdr_::type
uint8_t type
Definition: decode-igmp.c:41
RGMP_DEST_ADDRESS
#define RGMP_DEST_ADDRESS
Definition: decode-igmp.c:38
ThreadVars_
Per thread variable structure.
Definition: threadvars.h:58
IGMP_TYPE_MEMBERSHIP_REPORT_V1
#define IGMP_TYPE_MEMBERSHIP_REPORT_V1
Definition: decode-igmp.c:27
StatsCounterIncr
void StatsCounterIncr(StatsThreadContext *stats, StatsCounterId id)
Increments the local counter.
Definition: counters.c:164
IGMPv3MemberQueryHdr_::qqic
uint8_t qqic
Definition: decode-igmp.c:46
IGMPv3MemberReportGroupRecord
struct IGMPv3MemberReportGroupRecord_ IGMPv3MemberReportGroupRecord
IGMPVars_::rgmp
bool rgmp
Definition: decode-igmp.h:35
IGMPv3MemberQueryHdr_::n_sources
uint16_t n_sources
Definition: decode-igmp.c:47
Packet_
Definition: decode.h:505
IPV4_GET_RAW_IPTTL
#define IPV4_GET_RAW_IPTTL(ip4h)
Definition: decode-ipv4.h:102
Packet_::l4
struct PacketL4 l4
Definition: decode.h:605
IGMP_TYPE_MEMBERSHIP_REPORT_V3
#define IGMP_TYPE_MEMBERSHIP_REPORT_V3
Definition: decode-igmp.c:30
IGMPv3MemberQueryHdr
struct IGMPv3MemberQueryHdr_ IGMPv3MemberQueryHdr
dtv
DecodeThreadVars * dtv
Definition: fuzz_decodepcapfile.c:34
IPV4Hdr_
Definition: decode-ipv4.h:72
IGMPv3MemberReportHdr_::flags
uint16_t flags
Definition: decode-igmp.c:64
IGMP_MALFORMED
@ IGMP_MALFORMED
Definition: decode-events.h:235
IGMPv3MemberReportHdr_::res
uint8_t res
Definition: decode-igmp.c:62
SCNtohs
#define SCNtohs(x)
Definition: suricata-common.h:431
suricata-common.h
DecodeThreadVars_::counter_igmp
StatsCounterId counter_igmp
Definition: decode.h:1008
IGMP_TYPE_MEMBERSHIP_REPORT_V2
#define IGMP_TYPE_MEMBERSHIP_REPORT_V2
Definition: decode-igmp.c:28
IGMPv3MemberQueryHdr_::max_resp_time
uint8_t max_resp_time
Definition: decode-igmp.c:42
IGMPHdr_
Definition: decode-igmp.h:25
IGMPv3MemberReportGroupRecord_::aux_data_len
uint8_t aux_data_len
Definition: decode-igmp.c:53
version
uint8_t version
Definition: decode-gre.h:1
IGMPv3MemberReportHdr_::checksum
uint16_t checksum
Definition: decode-igmp.c:63
tv
ThreadVars * tv
Definition: fuzz_decodepcapfile.c:33
SCNtohl
#define SCNtohl(x)
Definition: suricata-common.h:430
DecodeThreadVars_
Structure to hold thread specific data for all decode modules.
Definition: decode.h:982
IGMPv3MemberQueryHdr_::s_qrv
uint8_t s_qrv
Definition: decode-igmp.c:45
IGMP_V3_PKT_TOO_SMALL
@ IGMP_V3_PKT_TOO_SMALL
Definition: decode-events.h:234
IGMPv3MemberReportHdr_::n_group_recs
uint16_t n_group_recs
Definition: decode-igmp.c:65
PacketL4::L4Vars::igmp
IGMPVars igmp
Definition: decode.h:484
IGMP_V3_MAX_N_SOURCES
#define IGMP_V3_MAX_N_SOURCES
Definition: decode-igmp.c:35
Packet_::dst
Address dst
Definition: decode.h:510
ENGINE_SET_INVALID_EVENT
#define ENGINE_SET_INVALID_EVENT(p, e)
Definition: decode.h:1221
IGMPVars_::version
uint8_t version
Definition: decode-igmp.h:34
IGMPVars_::hlen
uint16_t hlen
Definition: decode-igmp.h:33
ThreadVars_::stats
StatsThreadContext stats
Definition: threadvars.h:121
PacketL4::vars
union PacketL4::L4Vars vars
IGMPv3MemberQueryHdr_::group_address
uint32_t group_address
Definition: decode-igmp.c:44
IGMPv3MemberReportGroupRecord_::type
uint8_t type
Definition: decode-igmp.c:52