2 * netsniff-ng - the packet sniffing beast
3 * Copyright (C) 2012 Christoph Jaeger <christoph@netsniff-ng.org>
4 * Subject to the GPL, version 2.
9 #include <asm/byteorder.h>
10 #include <netinet/in.h>
18 /* IGMPv0 (RFC-988) */
24 uint32_t group_address
;
28 /* igmp_v0_msg.type */
29 #define IGMP_V0_CREATE_GROUP_REQUEST 0x01
30 #define IGMP_V0_CREATE_GROUP_REPLY 0x02
31 #define IGMP_V0_JOIN_GROUP_REQUEST 0x03
32 #define IGMP_V0_JOIN_GROUP_REPLY 0x04
33 #define IGMP_V0_LEAVE_GROUP_REQUEST 0x05
34 #define IGMP_V0_LEAVE_GROUP_REPLY 0x06
35 #define IGMP_V0_CONFIRM_GROUP_REQUEST 0x07
36 #define IGMP_V0_CONFIRM_GROUP_REPLY 0x08
38 /* IGMPv1 (RFC-1054/RFC-1112, obsoletes RFC-988) */
41 uint8_t version__type
;
43 #if defined(__LITTLE_ENDIAN_BITFIELD)
46 #elif defined(__BIG_ENDIAN_BITFIELD)
50 # error "Please fix <asm/byteorder.h>"
54 uint8_t unused
; /* always zero */
56 uint32_t group_address
;
59 /* igmp_v1_msg.version__type (!) */
60 /* IGMP_V1_MEMBERSHIP_QUERY 0x11 */
61 #define IGMP_V1_MEMBERSHIP_REPORT 0x12
63 /* IGMPv2 (RFC-2236) */
66 uint8_t max_resp_time
;
68 uint32_t group_address
;
71 /* igmp_v2_msg.type */
72 /* IGMP_V2_MEMBERSHIP_QUERY 0x11 */
73 #define IGMP_V2_MEMBERSHIP_REPORT 0x16
74 #define IGMP_V2_LEAVE_GROUP 0x17
78 * The RGMP message format resembles the IGMPv2 message format. All RGMP
79 * messages are sent with TTL 1, to destination address 224.0.0.25.
81 #define RGMP_LEAVE_GROUP 0xFC
82 #define RGMP_JOIN_GROUP 0xFD
84 #define RGMP_HELLO 0xFF
86 /* IGMPv3 (RFC-3376) */
87 struct igmp_v3_group_record
{
89 uint8_t aux_data_len
; /* always zero */
90 uint16_t number_of_sources
;
91 uint32_t multicast_address
;
92 uint32_t source_addresses
[0];
93 /* auxiliary data (IGMPv3 does not define any) */
96 /* igmp_v3_group_record.record_type */
97 #define IGMP_V3_MODE_IS_INCLUDE 1
98 #define IGMP_V3_MODE_IS_EXCLUDE 2
99 #define IGMP_V3_CHANGE_TO_INCLUDE_MODE 3
100 #define IGMP_V3_CHANGE_TO_EXCLUDE_MODE 4
101 #define IGMP_V3_ALLOW_NEW_SOURCES 5
102 #define IGMP_V3_BLOCK_OLD_SOURCES 6
104 struct igmp_v3_membership_report
{
109 uint16_t number_of_group_records
;
110 struct igmp_v3_group_record group_records
[0];
113 struct igmp_v3_membership_query
{
115 uint8_t max_resp_code
;
117 uint32_t group_address
;
118 #if defined(__LITTLE_ENDIAN_BITFIELD)
122 #elif defined(__BIG_ENDIAN_BITFIELD)
127 # error "Please fix <asm/byteorder.h>"
130 uint16_t number_of_sources
;
131 uint32_t source_addresses
[0];
134 #define IGMP_MEMBERSHIP_QUERY 0x11 /* v1/v2/v3 */
135 #define IGMP_V3_MEMBERSHIP_REPORT 0x22
137 #define EXP(x) (((x) & 0x70) >> 4)
138 #define MANT(x) ((x) & 0x0F)
140 #define DECODE_MAX_RESP_CODE(x) ((x) < 128 ? (x) : (MANT(x) | 0x10) << (EXP(x) + 3))
141 #define DECODE_QQIC(x) ((x) < 128 ? (x) : (MANT(x) | 0x10) << (EXP(x) + 3))
143 static char *friendly_msg_type_name(uint8_t msg_type
)
146 case IGMP_V0_CREATE_GROUP_REQUEST
:
147 return "Create Group Request";
148 case IGMP_V0_CREATE_GROUP_REPLY
:
149 return "Create Group Reply";
150 case IGMP_V0_JOIN_GROUP_REQUEST
:
151 return "Join Group Request";
152 case IGMP_V0_JOIN_GROUP_REPLY
:
153 return "Join Group Reply";
154 case IGMP_V0_LEAVE_GROUP_REQUEST
:
155 return "Leave Group Request";
156 case IGMP_V0_LEAVE_GROUP_REPLY
:
157 return "Leave Group Reply";
158 case IGMP_V0_CONFIRM_GROUP_REQUEST
:
159 return "Confirm Group Request";
160 case IGMP_V0_CONFIRM_GROUP_REPLY
:
161 return "Confirm Group Reply";
162 case IGMP_MEMBERSHIP_QUERY
:
163 return "Membership Query";
164 case IGMP_V1_MEMBERSHIP_REPORT
:
165 case IGMP_V2_MEMBERSHIP_REPORT
:
166 case IGMP_V3_MEMBERSHIP_REPORT
:
167 return "Membership Report";
168 case IGMP_V2_LEAVE_GROUP
:
169 return "Leave Group";
174 case RGMP_JOIN_GROUP
:
176 case RGMP_LEAVE_GROUP
:
177 return "Leave Group";
183 #define PRINT_FRIENDLY_NAMED_MSG_TYPE(type) \
185 if (friendly_msg_type_name(type)) \
186 tprintf(" Type (0x%.2x, %s)", type, \
187 friendly_msg_type_name(type)); \
189 tprintf(" Type (0x%.2x)", type); \
192 static char *friendly_group_rec_type_name(uint8_t rec_type
)
195 case IGMP_V3_MODE_IS_INCLUDE
:
196 return "Mode Is Include";
197 case IGMP_V3_MODE_IS_EXCLUDE
:
198 return "Mode Is Exclude";
199 case IGMP_V3_CHANGE_TO_INCLUDE_MODE
:
200 return "Change To Include Mode";
201 case IGMP_V3_CHANGE_TO_EXCLUDE_MODE
:
202 return "Change To Exclude Mode";
203 case IGMP_V3_ALLOW_NEW_SOURCES
:
204 return "Allow New Sources";
205 case IGMP_V3_BLOCK_OLD_SOURCES
:
206 return "Block Old Sources";
212 static void dissect_igmp_v0(struct pkt_buff
*pkt
)
214 char addr
[INET_ADDRSTRLEN
];
217 static const char *reply_codes
[] = {
219 "Request Denied, No Resources",
220 "Request Denied, Invalid Code",
221 "Request Denied, Invalid Group Address",
222 "Request Denied, Invalid Access Key"
225 struct igmp_v0_msg
*msg
=
226 (struct igmp_v0_msg
*) pkt_pull(pkt
, sizeof(*msg
));
231 tprintf(" [ IGMPv0");
232 PRINT_FRIENDLY_NAMED_MSG_TYPE(msg
->type
);
235 case IGMP_V0_CREATE_GROUP_REQUEST
:
238 tprintf(", Code (%u, %s)", msg
->code
, "Public");
241 tprintf(", Code (%u, %s)", msg
->code
, "Private");
244 tprintf(", Code (%u)", msg
->code
);
247 case IGMP_V0_CREATE_GROUP_REPLY
:
248 case IGMP_V0_JOIN_GROUP_REPLY
:
249 case IGMP_V0_LEAVE_GROUP_REPLY
:
250 case IGMP_V0_CONFIRM_GROUP_REPLY
:
252 tprintf(", Code (%u, %s)", msg
->code
, reply_codes
[msg
->code
]);
254 tprintf(", Code (%u, Request Pending, Retry In %u Seconds)",
255 msg
->code
, msg
->code
);
258 tprintf(", Code (%u)", msg
->code
);
261 csum
= calc_csum(msg
, sizeof(*msg
) + pkt_len(pkt
), 0);
262 tprintf(", CSum (0x%.4x) is %s", ntohs(msg
->checksum
), csum
?
263 colorize_start_full(black
, red
) "bogus (!)" colorize_end() : "ok");
265 tprintf(" - %s should be %x%s", colorize_start_full(black
, red
),
266 csum_expected(msg
->checksum
, csum
), colorize_end());
267 tprintf(", Id (%u)", ntohs(msg
->identifier
));
268 inet_ntop(AF_INET
, &msg
->group_address
, addr
, sizeof(addr
));
269 tprintf(", Group Addr (%s)", addr
);
270 tprintf(", Access Key (0x%.16"PRIx64
")", msg
->access_key
);
274 static void dissect_igmp_v1(struct pkt_buff
*pkt
)
276 char addr
[INET_ADDRSTRLEN
];
279 struct igmp_v1_msg
*msg
=
280 (struct igmp_v1_msg
*) pkt_pull(pkt
, sizeof(*msg
));
285 tprintf(" [ IGMPv1");
286 PRINT_FRIENDLY_NAMED_MSG_TYPE(msg
->version__type
);
287 csum
= calc_csum(msg
, sizeof(*msg
) + pkt_len(pkt
), 0);
288 tprintf(", CSum (0x%.4x) is %s", ntohs(msg
->checksum
), csum
?
289 colorize_start_full(black
, red
) "bogus (!)" colorize_end() : "ok");
291 tprintf(" - %s should be %x%s", colorize_start_full(black
, red
),
292 csum_expected(msg
->checksum
, csum
), colorize_end());
293 inet_ntop(AF_INET
, &msg
->group_address
, addr
, sizeof(addr
));
294 tprintf(", Group Addr (%s)", addr
);
298 static void dissect_igmp_v2(struct pkt_buff
*pkt
)
300 char addr
[INET_ADDRSTRLEN
];
303 struct igmp_v2_msg
*msg
=
304 (struct igmp_v2_msg
*) pkt_pull(pkt
, sizeof(*msg
));
312 case RGMP_JOIN_GROUP
:
313 case RGMP_LEAVE_GROUP
:
314 tprintf(" [ IGMPv2 (RGMP)");
317 tprintf(" [ IGMPv2");
321 PRINT_FRIENDLY_NAMED_MSG_TYPE(msg
->type
);
322 tprintf(", Max Resp Time (%u)", msg
->max_resp_time
);
323 csum
= calc_csum(msg
, sizeof(*msg
) + pkt_len(pkt
), 0);
324 tprintf(", CSum (0x%.4x) is %s", ntohs(msg
->checksum
), csum
?
325 colorize_start_full(black
, red
) "bogus (!)" colorize_end() : "ok");
327 tprintf(" - %s should be %x%s", colorize_start_full(black
, red
),
328 csum_expected(msg
->checksum
, csum
), colorize_end());
329 inet_ntop(AF_INET
, &msg
->group_address
, addr
, sizeof(addr
));
330 tprintf(", Group Addr (%s)", addr
);
334 static void dissect_igmp_v3_membership_query(struct pkt_buff
*pkt
)
336 char addr
[INET_ADDRSTRLEN
];
341 struct igmp_v3_membership_query
*msg
=
342 (struct igmp_v3_membership_query
*) pkt_pull(pkt
, sizeof(*msg
));
347 tprintf(" [ IGMPv3");
348 PRINT_FRIENDLY_NAMED_MSG_TYPE(msg
->type
);
349 tprintf(", Max Resp Code (0x%.2x => %u)", msg
->max_resp_code
,
350 DECODE_MAX_RESP_CODE(msg
->max_resp_code
));
351 csum
= calc_csum(msg
, sizeof(*msg
) + pkt_len(pkt
), 0);
352 tprintf(", CSum (0x%.4x) is %s", ntohs(msg
->checksum
), csum
?
353 colorize_start_full(black
, red
) "bogus (!)" colorize_end() : "ok");
355 tprintf(" - %s should be %x%s", colorize_start_full(black
, red
),
356 csum_expected(msg
->checksum
, csum
), colorize_end());
357 inet_ntop(AF_INET
, &msg
->group_address
, addr
, sizeof(addr
));
358 /* S Flag (Suppress Router-Side Processing) */
359 tprintf(", Suppress (%u)", msg
->s_flag
? 1 : 0);
360 /* QRV (Querier's Robustness Variable) */
361 tprintf(", QRV (%u)", msg
->qrv
);
362 /* QQIC (Querier's Query Interval Code) */
363 tprintf(", QQIC (0x%.2x => %u)", msg
->qqic
, DECODE_QQIC(msg
->qqic
));
364 tprintf(", Group Addr (%s)", addr
);
365 n
= ntohs(msg
->number_of_sources
);
366 tprintf(", Num Src (%zu)", n
);
369 src_addr
= (uint32_t *) pkt_pull(pkt
, sizeof(*src_addr
));
370 if (src_addr
!= NULL
) {
371 inet_ntop(AF_INET
, src_addr
, addr
, sizeof(addr
));
372 tprintf(", Src Addr (%s", addr
);
374 src_addr
= (uint32_t *)
375 pkt_pull(pkt
, sizeof(*src_addr
));
376 if (src_addr
== NULL
)
378 inet_ntop(AF_INET
, src_addr
, addr
, sizeof(addr
));
379 tprintf(", %s", addr
);
387 static void dissect_igmp_v3_membership_report(struct pkt_buff
*pkt
)
389 char addr
[INET_ADDRSTRLEN
];
394 struct igmp_v3_group_record
*rec
;
395 struct igmp_v3_membership_report
*msg
=
396 (struct igmp_v3_membership_report
*) pkt_pull(pkt
, sizeof(*msg
));
401 tprintf(" [ IGMPv3");
402 PRINT_FRIENDLY_NAMED_MSG_TYPE(msg
->type
);
403 csum
= calc_csum(msg
, sizeof(*msg
) + pkt_len(pkt
), 0);
404 tprintf(", CSum (0x%.4x) is %s", ntohs(msg
->checksum
), csum
?
405 colorize_start_full(black
, red
) "bogus (!)" colorize_end() : "ok");
407 tprintf(" - %s should be %x%s", colorize_start_full(black
, red
),
408 csum_expected(msg
->checksum
, csum
), colorize_end());
409 m
= ntohs(msg
->number_of_group_records
);
410 tprintf(", Num Group Rec (%zu)", m
);
414 rec
= (struct igmp_v3_group_record
*) pkt_pull(pkt
, sizeof(*rec
));
419 tprintf(" [ Group Record");
420 if (friendly_group_rec_type_name(rec
->record_type
))
421 tprintf(" Type (%u, %s)", rec
->record_type
,
422 friendly_group_rec_type_name(rec
->record_type
));
424 tprintf(" Type (%u)", rec
->record_type
);
425 n
= ntohs(rec
->number_of_sources
);
426 tprintf(", Num Src (%zu)", n
);
427 inet_ntop(AF_INET
, &rec
->multicast_address
, addr
, sizeof(addr
));
428 tprintf(", Multicast Addr (%s)", addr
);
431 src_addr
= (uint32_t *) pkt_pull(pkt
, sizeof(*src_addr
));
432 if (src_addr
!= NULL
) {
433 inet_ntop(AF_INET
, src_addr
, addr
, sizeof(addr
));
434 tprintf(", Src Addr (%s", addr
);
436 src_addr
= (uint32_t *)
437 pkt_pull(pkt
, sizeof(*src_addr
));
438 if (src_addr
== NULL
)
440 inet_ntop(AF_INET
, src_addr
, addr
, sizeof(addr
));
441 tprintf(", %s", addr
);
452 static void igmp(struct pkt_buff
*pkt
)
454 switch (*pkt_peek(pkt
)) {
455 case IGMP_V0_CREATE_GROUP_REQUEST
:
456 case IGMP_V0_CREATE_GROUP_REPLY
:
457 case IGMP_V0_JOIN_GROUP_REQUEST
:
458 case IGMP_V0_JOIN_GROUP_REPLY
:
459 case IGMP_V0_LEAVE_GROUP_REQUEST
:
460 case IGMP_V0_LEAVE_GROUP_REPLY
:
461 case IGMP_V0_CONFIRM_GROUP_REQUEST
:
462 case IGMP_V0_CONFIRM_GROUP_REPLY
:
463 if (pkt_len(pkt
) == sizeof(struct igmp_v0_msg
))
464 dissect_igmp_v0(pkt
);
466 case IGMP_MEMBERSHIP_QUERY
: /* v1/v2/v3 */
467 if (pkt_len(pkt
) >= sizeof(struct igmp_v3_membership_query
))
468 dissect_igmp_v3_membership_query(pkt
);
469 else if (pkt_len(pkt
) == sizeof(struct igmp_v2_msg
)
470 && *(pkt_peek(pkt
) + 1))
471 dissect_igmp_v2(pkt
);
472 else if (pkt_len(pkt
) == sizeof(struct igmp_v1_msg
))
473 dissect_igmp_v1(pkt
);
475 case IGMP_V1_MEMBERSHIP_REPORT
:
476 if (pkt_len(pkt
) == sizeof(struct igmp_v1_msg
))
477 dissect_igmp_v1(pkt
);
481 case RGMP_JOIN_GROUP
:
482 case RGMP_LEAVE_GROUP
:
483 case IGMP_V2_MEMBERSHIP_REPORT
:
484 case IGMP_V2_LEAVE_GROUP
:
485 if (pkt_len(pkt
) == sizeof(struct igmp_v2_msg
))
486 dissect_igmp_v2(pkt
);
488 case IGMP_V3_MEMBERSHIP_REPORT
:
489 if (pkt_len(pkt
) >= sizeof(struct igmp_v3_membership_report
))
490 dissect_igmp_v3_membership_report(pkt
);
495 static void igmp_less(struct pkt_buff
*pkt
)
499 switch (*pkt_peek(pkt
)) {
500 case IGMP_V0_CREATE_GROUP_REQUEST
:
501 case IGMP_V0_CREATE_GROUP_REPLY
:
502 case IGMP_V0_JOIN_GROUP_REQUEST
:
503 case IGMP_V0_JOIN_GROUP_REPLY
:
504 case IGMP_V0_LEAVE_GROUP_REQUEST
:
505 case IGMP_V0_LEAVE_GROUP_REPLY
:
506 case IGMP_V0_CONFIRM_GROUP_REQUEST
:
507 case IGMP_V0_CONFIRM_GROUP_REPLY
:
508 if (pkt_len(pkt
) == sizeof(struct igmp_v0_msg
))
511 case IGMP_MEMBERSHIP_QUERY
: /* v1/v2/v3 */
512 if (pkt_len(pkt
) >= sizeof(struct igmp_v3_membership_query
))
514 else if (pkt_len(pkt
) == sizeof(struct igmp_v2_msg
)
515 && *(pkt_peek(pkt
) + 1))
517 else if (pkt_len(pkt
) == sizeof(struct igmp_v1_msg
))
520 case IGMP_V1_MEMBERSHIP_REPORT
:
521 if (pkt_len(pkt
) == sizeof(struct igmp_v1_msg
))
526 case RGMP_JOIN_GROUP
:
527 case RGMP_LEAVE_GROUP
:
528 case IGMP_V2_MEMBERSHIP_REPORT
:
529 case IGMP_V2_LEAVE_GROUP
:
530 if (pkt_len(pkt
) == sizeof(struct igmp_v2_msg
))
533 case IGMP_V3_MEMBERSHIP_REPORT
:
534 if (pkt_len(pkt
) >= sizeof(struct igmp_v3_membership_report
))
539 if (version
< 0 || version
> 3)
542 switch (*pkt_peek(pkt
)) {
545 case RGMP_JOIN_GROUP
:
546 case RGMP_LEAVE_GROUP
:
547 tprintf(" IGMPv2 (RGMP)");
550 tprintf(" IGMPv%u", version
);
553 PRINT_FRIENDLY_NAMED_MSG_TYPE(*pkt_peek(pkt
));
556 struct protocol igmp_ops
= {
559 .print_less
= igmp_less
,