2 * netsniff-ng - the packet sniffing beast
3 * Copyright (C) 2012 Christoph Jaeger <christoph@netsniff-ng.org>
4 * Subject to the GPL, version 2.
8 #include <asm/byteorder.h>
9 #include <netinet/in.h>
13 #include "dissector_eth.h"
17 /* IGMPv0 (RFC-988) */
23 uint32_t group_address
;
27 /* igmp_v0_msg.type */
28 #define IGMP_V0_CREATE_GROUP_REQUEST 0x01
29 #define IGMP_V0_CREATE_GROUP_REPLY 0x02
30 #define IGMP_V0_JOIN_GROUP_REQUEST 0x03
31 #define IGMP_V0_JOIN_GROUP_REPLY 0x04
32 #define IGMP_V0_LEAVE_GROUP_REQUEST 0x05
33 #define IGMP_V0_LEAVE_GROUP_REPLY 0x06
34 #define IGMP_V0_CONFIRM_GROUP_REQUEST 0x07
35 #define IGMP_V0_CONFIRM_GROUP_REPLY 0x08
37 /* IGMPv1 (RFC-1054/RFC-1112, obsoletes RFC-988) */
40 uint8_t version__type
;
42 #if defined(__LITTLE_ENDIAN_BITFIELD)
45 #elif defined(__BIG_ENDIAN_BITFIELD)
49 # error "Please fix <asm/byteorder.h>"
53 uint8_t unused
; /* always zero */
55 uint32_t group_address
;
56 } __attribute__((packed
));
58 /* igmp_v1_msg.version__type (!) */
59 /* IGMP_V1_MEMBERSHIP_QUERY 0x11 */
60 #define IGMP_V1_MEMBERSHIP_REPORT 0x12
62 /* IGMPv2 (RFC-2236) */
65 uint8_t max_resp_time
;
67 uint32_t group_address
;
68 } __attribute__((packed
));
70 /* igmp_v2_msg.type */
71 /* IGMP_V2_MEMBERSHIP_QUERY 0x11 */
72 #define IGMP_V2_MEMBERSHIP_REPORT 0x16
73 #define IGMP_V2_LEAVE_GROUP 0x17
77 * The RGMP message format resembles the IGMPv2 message format. All RGMP
78 * messages are sent with TTL 1, to destination address 224.0.0.25.
80 #define RGMP_LEAVE_GROUP 0xFC
81 #define RGMP_JOIN_GROUP 0xFD
83 #define RGMP_HELLO 0xFF
85 /* IGMPv3 (RFC-3376) */
86 struct igmp_v3_group_record
{
88 uint8_t aux_data_len
; /* always zero */
89 uint16_t number_of_sources
;
90 uint32_t multicast_address
;
91 uint32_t source_addresses
[0];
92 /* auxiliary data (IGMPv3 does not define any) */
93 } __attribute__((packed
));
95 /* igmp_v3_group_record.record_type */
96 #define IGMP_V3_MODE_IS_INCLUDE 1
97 #define IGMP_V3_MODE_IS_EXCLUDE 2
98 #define IGMP_V3_CHANGE_TO_INCLUDE_MODE 3
99 #define IGMP_V3_CHANGE_TO_EXCLUDE_MODE 4
100 #define IGMP_V3_ALLOW_NEW_SOURCES 5
101 #define IGMP_V3_BLOCK_OLD_SOURCES 6
103 struct igmp_v3_membership_report
{
108 uint16_t number_of_group_records
;
109 struct igmp_v3_group_record group_records
[0];
110 } __attribute__((packed
));
112 struct igmp_v3_membership_query
{
114 uint8_t max_resp_code
;
116 uint32_t group_address
;
117 #if defined(__LITTLE_ENDIAN_BITFIELD)
121 #elif defined(__BIG_ENDIAN_BITFIELD)
126 # error "Please fix <asm/byteorder.h>"
129 uint16_t number_of_sources
;
130 uint32_t source_addresses
[0];
131 } __attribute__((packed
));
133 #define IGMP_MEMBERSHIP_QUERY 0x11 /* v1/v2/v3 */
134 #define IGMP_V3_MEMBERSHIP_REPORT 0x22
136 #define EXP(x) (((x) & 0x70) >> 4)
137 #define MANT(x) ((x) & 0x0F)
139 #define DECODE_MAX_RESP_CODE(x) ((x) < 128 ? (x) : (MANT(x) | 0x10) << (EXP(x) + 3))
140 #define DECODE_QQIC(x) ((x) < 128 ? (x) : (MANT(x) | 0x10) << (EXP(x) + 3))
142 static char *friendly_msg_type_name(uint8_t msg_type
)
145 case IGMP_V0_CREATE_GROUP_REQUEST
:
146 return "Create Group Request";
147 case IGMP_V0_CREATE_GROUP_REPLY
:
148 return "Create Group Reply";
149 case IGMP_V0_JOIN_GROUP_REQUEST
:
150 return "Join Group Request";
151 case IGMP_V0_JOIN_GROUP_REPLY
:
152 return "Join Group Reply";
153 case IGMP_V0_LEAVE_GROUP_REQUEST
:
154 return "Leave Group Request";
155 case IGMP_V0_LEAVE_GROUP_REPLY
:
156 return "Leave Group Reply";
157 case IGMP_V0_CONFIRM_GROUP_REQUEST
:
158 return "Confirm Group Request";
159 case IGMP_V0_CONFIRM_GROUP_REPLY
:
160 return "Confirm Group Reply";
161 case IGMP_MEMBERSHIP_QUERY
:
162 return "Membership Query";
163 case IGMP_V1_MEMBERSHIP_REPORT
:
164 case IGMP_V2_MEMBERSHIP_REPORT
:
165 case IGMP_V3_MEMBERSHIP_REPORT
:
166 return "Membership Report";
167 case IGMP_V2_LEAVE_GROUP
:
168 return "Leave Group";
173 case RGMP_JOIN_GROUP
:
175 case RGMP_LEAVE_GROUP
:
176 return "Leave Group";
182 #define PRINT_FRIENDLY_NAMED_MSG_TYPE(type) \
184 if (friendly_msg_type_name(type)) \
185 tprintf(" Type (0x%.2x, %s)", type, \
186 friendly_msg_type_name(type)); \
188 tprintf(" Type (0x%.2x)", type); \
191 static char *friendly_group_rec_type_name(uint8_t rec_type
)
194 case IGMP_V3_MODE_IS_INCLUDE
:
195 return "Mode Is Include";
196 case IGMP_V3_MODE_IS_EXCLUDE
:
197 return "Mode Is Exclude";
198 case IGMP_V3_CHANGE_TO_INCLUDE_MODE
:
199 return "Change To Include Mode";
200 case IGMP_V3_CHANGE_TO_EXCLUDE_MODE
:
201 return "Change To Exclude Mode";
202 case IGMP_V3_ALLOW_NEW_SOURCES
:
203 return "Allow New Sources";
204 case IGMP_V3_BLOCK_OLD_SOURCES
:
205 return "Block Old Sources";
211 static void dissect_igmp_v0(struct pkt_buff
*pkt
)
213 char addr
[INET_ADDRSTRLEN
];
216 static const char *reply_codes
[] = {
218 "Request Denied, No Resources",
219 "Request Denied, Invalid Code",
220 "Request Denied, Invalid Group Address",
221 "Request Denied, Invalid Access Key"
224 struct igmp_v0_msg
*msg
=
225 (struct igmp_v0_msg
*) pkt_pull(pkt
, sizeof(*msg
));
230 tprintf(" [ IGMPv0");
231 PRINT_FRIENDLY_NAMED_MSG_TYPE(msg
->type
);
234 case IGMP_V0_CREATE_GROUP_REQUEST
:
237 tprintf(", Code (%u, %s)", msg
->code
, "Public");
240 tprintf(", Code (%u, %s)", msg
->code
, "Private");
243 tprintf(", Code (%u)", msg
->code
);
246 case IGMP_V0_CREATE_GROUP_REPLY
:
247 case IGMP_V0_JOIN_GROUP_REPLY
:
248 case IGMP_V0_LEAVE_GROUP_REPLY
:
249 case IGMP_V0_CONFIRM_GROUP_REPLY
:
251 tprintf(", Code (%u, %s)", msg
->code
, reply_codes
[msg
->code
]);
253 tprintf(", Code (%u, Request Pending, Retry In %u Seconds)",
254 msg
->code
, msg
->code
);
257 tprintf(", Code (%u)", msg
->code
);
260 csum
= calc_csum(msg
, sizeof(*msg
) + pkt_len(pkt
), 0);
261 tprintf(", CSum (0x%.4x) is %s", ntohs(msg
->checksum
), csum
?
262 colorize_start_full(black
, red
) "bogus (!)" colorize_end() : "ok");
264 tprintf(" - %s should be %x%s", colorize_start_full(black
, red
),
265 csum_expected(msg
->checksum
, csum
), colorize_end());
266 tprintf(", Id (%u)", ntohs(msg
->identifier
));
267 inet_ntop(AF_INET
, &msg
->group_address
, addr
, sizeof(addr
));
268 tprintf(", Group Addr (%s)", addr
);
269 tprintf(", Access Key (0x%.16lx)", msg
->access_key
);
273 static void dissect_igmp_v1(struct pkt_buff
*pkt
)
275 char addr
[INET_ADDRSTRLEN
];
278 struct igmp_v1_msg
*msg
=
279 (struct igmp_v1_msg
*) pkt_pull(pkt
, sizeof(*msg
));
284 tprintf(" [ IGMPv1");
285 PRINT_FRIENDLY_NAMED_MSG_TYPE(msg
->version__type
);
286 csum
= calc_csum(msg
, sizeof(*msg
) + pkt_len(pkt
), 0);
287 tprintf(", CSum (0x%.4x) is %s", ntohs(msg
->checksum
), csum
?
288 colorize_start_full(black
, red
) "bogus (!)" colorize_end() : "ok");
290 tprintf(" - %s should be %x%s", colorize_start_full(black
, red
),
291 csum_expected(msg
->checksum
, csum
), colorize_end());
292 inet_ntop(AF_INET
, &msg
->group_address
, addr
, sizeof(addr
));
293 tprintf(", Group Addr (%s)", addr
);
297 static void dissect_igmp_v2(struct pkt_buff
*pkt
)
299 char addr
[INET_ADDRSTRLEN
];
302 struct igmp_v2_msg
*msg
=
303 (struct igmp_v2_msg
*) pkt_pull(pkt
, sizeof(*msg
));
311 case RGMP_JOIN_GROUP
:
312 case RGMP_LEAVE_GROUP
:
313 tprintf(" [ IGMPv2 (RGMP)");
316 tprintf(" [ IGMPv2");
320 PRINT_FRIENDLY_NAMED_MSG_TYPE(msg
->type
);
321 tprintf(", Max Resp Time (%u)", msg
->max_resp_time
);
322 csum
= calc_csum(msg
, sizeof(*msg
) + pkt_len(pkt
), 0);
323 tprintf(", CSum (0x%.4x) is %s", ntohs(msg
->checksum
), csum
?
324 colorize_start_full(black
, red
) "bogus (!)" colorize_end() : "ok");
326 tprintf(" - %s should be %x%s", colorize_start_full(black
, red
),
327 csum_expected(msg
->checksum
, csum
), colorize_end());
328 inet_ntop(AF_INET
, &msg
->group_address
, addr
, sizeof(addr
));
329 tprintf(", Group Addr (%s)", addr
);
333 static void dissect_igmp_v3_membership_query(struct pkt_buff
*pkt
)
335 char addr
[INET_ADDRSTRLEN
];
340 struct igmp_v3_membership_query
*msg
=
341 (struct igmp_v3_membership_query
*) pkt_pull(pkt
, sizeof(*msg
));
346 tprintf(" [ IGMPv3");
347 PRINT_FRIENDLY_NAMED_MSG_TYPE(msg
->type
);
348 tprintf(", Max Resp Code (0x%.2x => %u)", msg
->max_resp_code
,
349 DECODE_MAX_RESP_CODE(msg
->max_resp_code
));
350 csum
= calc_csum(msg
, sizeof(*msg
) + pkt_len(pkt
), 0);
351 tprintf(", CSum (0x%.4x) is %s", ntohs(msg
->checksum
), csum
?
352 colorize_start_full(black
, red
) "bogus (!)" colorize_end() : "ok");
354 tprintf(" - %s should be %x%s", colorize_start_full(black
, red
),
355 csum_expected(msg
->checksum
, csum
), colorize_end());
356 inet_ntop(AF_INET
, &msg
->group_address
, addr
, sizeof(addr
));
357 /* S Flag (Suppress Router-Side Processing) */
358 tprintf(", Suppress (%u)", msg
->s_flag
? 1 : 0);
359 /* QRV (Querier's Robustness Variable) */
360 tprintf(", QRV (%u)", msg
->qrv
);
361 /* QQIC (Querier's Query Interval Code) */
362 tprintf(", QQIC (0x%.2x => %u)", msg
->qqic
, DECODE_QQIC(msg
->qqic
));
363 tprintf(", Group Addr (%s)", addr
);
364 n
= ntohs(msg
->number_of_sources
);
365 tprintf(", Num Src (%zu)", n
);
368 src_addr
= (uint32_t *) pkt_pull(pkt
, sizeof(*src_addr
));
369 if (src_addr
!= NULL
) {
370 inet_ntop(AF_INET
, src_addr
, addr
, sizeof(addr
));
371 tprintf(", Src Addr (%s", addr
);
373 src_addr
= (uint32_t *)
374 pkt_pull(pkt
, sizeof(*src_addr
));
375 if (src_addr
== NULL
)
377 inet_ntop(AF_INET
, src_addr
, addr
, sizeof(addr
));
378 tprintf(", %s", addr
);
386 static void dissect_igmp_v3_membership_report(struct pkt_buff
*pkt
)
388 char addr
[INET_ADDRSTRLEN
];
393 struct igmp_v3_group_record
*rec
;
394 struct igmp_v3_membership_report
*msg
=
395 (struct igmp_v3_membership_report
*) pkt_pull(pkt
, sizeof(*msg
));
400 tprintf(" [ IGMPv3");
401 PRINT_FRIENDLY_NAMED_MSG_TYPE(msg
->type
);
402 csum
= calc_csum(msg
, sizeof(*msg
) + pkt_len(pkt
), 0);
403 tprintf(", CSum (0x%.4x) is %s", ntohs(msg
->checksum
), csum
?
404 colorize_start_full(black
, red
) "bogus (!)" colorize_end() : "ok");
406 tprintf(" - %s should be %x%s", colorize_start_full(black
, red
),
407 csum_expected(msg
->checksum
, csum
), colorize_end());
408 m
= ntohs(msg
->number_of_group_records
);
409 tprintf(", Num Group Rec (%zu)", m
);
413 rec
= (struct igmp_v3_group_record
*) pkt_pull(pkt
, sizeof(*rec
));
418 tprintf(" [ Group Record");
419 if (friendly_group_rec_type_name(rec
->record_type
))
420 tprintf(" Type (%u, %s)", rec
->record_type
,
421 friendly_group_rec_type_name(rec
->record_type
));
423 tprintf(" Type (%u)", rec
->record_type
);
424 n
= ntohs(rec
->number_of_sources
);
425 tprintf(", Num Src (%zu)", n
);
426 inet_ntop(AF_INET
, &rec
->multicast_address
, addr
, sizeof(addr
));
427 tprintf(", Multicast Addr (%s)", addr
);
430 src_addr
= (uint32_t *) pkt_pull(pkt
, sizeof(*src_addr
));
431 if (src_addr
!= NULL
) {
432 inet_ntop(AF_INET
, src_addr
, addr
, sizeof(addr
));
433 tprintf(", Src Addr (%s", addr
);
435 src_addr
= (uint32_t *)
436 pkt_pull(pkt
, sizeof(*src_addr
));
437 if (src_addr
== NULL
)
439 inet_ntop(AF_INET
, src_addr
, addr
, sizeof(addr
));
440 tprintf(", %s", addr
);
451 static void igmp(struct pkt_buff
*pkt
)
453 switch (*pkt_peek(pkt
)) {
454 case IGMP_V0_CREATE_GROUP_REQUEST
:
455 case IGMP_V0_CREATE_GROUP_REPLY
:
456 case IGMP_V0_JOIN_GROUP_REQUEST
:
457 case IGMP_V0_JOIN_GROUP_REPLY
:
458 case IGMP_V0_LEAVE_GROUP_REQUEST
:
459 case IGMP_V0_LEAVE_GROUP_REPLY
:
460 case IGMP_V0_CONFIRM_GROUP_REQUEST
:
461 case IGMP_V0_CONFIRM_GROUP_REPLY
:
462 if (pkt_len(pkt
) == sizeof(struct igmp_v0_msg
))
463 dissect_igmp_v0(pkt
);
465 case IGMP_MEMBERSHIP_QUERY
: /* v1/v2/v3 */
466 if (pkt_len(pkt
) >= sizeof(struct igmp_v3_membership_query
))
467 dissect_igmp_v3_membership_query(pkt
);
468 else if (pkt_len(pkt
) == sizeof(struct igmp_v2_msg
)
469 && *(pkt_peek(pkt
) + 1))
470 dissect_igmp_v2(pkt
);
471 else if (pkt_len(pkt
) == sizeof(struct igmp_v1_msg
))
472 dissect_igmp_v1(pkt
);
474 case IGMP_V1_MEMBERSHIP_REPORT
:
475 if (pkt_len(pkt
) == sizeof(struct igmp_v1_msg
))
476 dissect_igmp_v1(pkt
);
480 case RGMP_JOIN_GROUP
:
481 case RGMP_LEAVE_GROUP
:
482 case IGMP_V2_MEMBERSHIP_REPORT
:
483 case IGMP_V2_LEAVE_GROUP
:
484 if (pkt_len(pkt
) == sizeof(struct igmp_v2_msg
))
485 dissect_igmp_v2(pkt
);
487 case IGMP_V3_MEMBERSHIP_REPORT
:
488 if (pkt_len(pkt
) >= sizeof(struct igmp_v3_membership_report
))
489 dissect_igmp_v3_membership_report(pkt
);
494 static void igmp_less(struct pkt_buff
*pkt
)
498 switch (*pkt_peek(pkt
)) {
499 case IGMP_V0_CREATE_GROUP_REQUEST
:
500 case IGMP_V0_CREATE_GROUP_REPLY
:
501 case IGMP_V0_JOIN_GROUP_REQUEST
:
502 case IGMP_V0_JOIN_GROUP_REPLY
:
503 case IGMP_V0_LEAVE_GROUP_REQUEST
:
504 case IGMP_V0_LEAVE_GROUP_REPLY
:
505 case IGMP_V0_CONFIRM_GROUP_REQUEST
:
506 case IGMP_V0_CONFIRM_GROUP_REPLY
:
507 if (pkt_len(pkt
) == sizeof(struct igmp_v0_msg
))
510 case IGMP_MEMBERSHIP_QUERY
: /* v1/v2/v3 */
511 if (pkt_len(pkt
) >= sizeof(struct igmp_v3_membership_query
))
513 else if (pkt_len(pkt
) == sizeof(struct igmp_v2_msg
)
514 && *(pkt_peek(pkt
) + 1))
516 else if (pkt_len(pkt
) == sizeof(struct igmp_v1_msg
))
519 case IGMP_V1_MEMBERSHIP_REPORT
:
520 if (pkt_len(pkt
) == sizeof(struct igmp_v1_msg
))
525 case RGMP_JOIN_GROUP
:
526 case RGMP_LEAVE_GROUP
:
527 case IGMP_V2_MEMBERSHIP_REPORT
:
528 case IGMP_V2_LEAVE_GROUP
:
529 if (pkt_len(pkt
) == sizeof(struct igmp_v2_msg
))
532 case IGMP_V3_MEMBERSHIP_REPORT
:
533 if (pkt_len(pkt
) >= sizeof(struct igmp_v3_membership_report
))
538 if (version
< 0 || version
> 3)
541 switch (*pkt_peek(pkt
)) {
544 case RGMP_JOIN_GROUP
:
545 case RGMP_LEAVE_GROUP
:
546 tprintf(" IGMPv2 (RGMP)");
549 tprintf(" IGMPv%u", version
);
552 PRINT_FRIENDLY_NAMED_MSG_TYPE(*pkt_peek(pkt
));
555 struct protocol igmp_ops
= {
558 .print_less
= igmp_less
,