Merge pull request #58 from electronjoe/a1cf780cccc4819eb360cda1e0e94e17935cb8c7
[netsniff-ng-old.git] / proto_igmp.c
blob30fb52780627016d0a05f189521e5f5fc59239a8
1 /*
2 * netsniff-ng - the packet sniffing beast
3 * Copyright (C) 2012 Christoph Jaeger <christoph@netsniff-ng.org>
4 * Subject to the GPL, version 2.
5 */
7 #include <arpa/inet.h>
8 #include <asm/byteorder.h>
9 #include <netinet/in.h>
11 #include "proto.h"
12 #include "protos.h"
13 #include "csum.h"
14 #include "dissector_eth.h"
15 #include "built_in.h"
16 #include "pkt_buff.h"
18 /* IGMPv0 (RFC-988) */
19 struct igmp_v0_msg {
20 uint8_t type;
21 uint8_t code;
22 uint16_t checksum;
23 uint32_t identifier;
24 uint32_t group_address;
25 uint64_t access_key;
26 } __packed;
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) */
39 struct igmp_v1_msg {
40 union {
41 uint8_t version__type;
42 struct {
43 #if defined(__LITTLE_ENDIAN_BITFIELD)
44 uint8_t type :4,
45 version :4;
46 #elif defined(__BIG_ENDIAN_BITFIELD)
47 uint8_t version :4,
48 type :4;
49 #else
50 # error "Please fix <asm/byteorder.h>"
51 #endif
54 uint8_t unused; /* always zero */
55 uint16_t checksum;
56 uint32_t group_address;
57 } __attribute__((packed));
59 /* igmp_v1_msg.version__type (!) */
60 /* IGMP_V1_MEMBERSHIP_QUERY 0x11 */
61 #define IGMP_V1_MEMBERSHIP_REPORT 0x12
63 /* IGMPv2 (RFC-2236) */
64 struct igmp_v2_msg {
65 uint8_t type;
66 uint8_t max_resp_time;
67 uint16_t checksum;
68 uint32_t group_address;
69 } __attribute__((packed));
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
77 * RGMP (RFC-3488)
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
83 #define RGMP_BYE 0xFE
84 #define RGMP_HELLO 0xFF
86 /* IGMPv3 (RFC-3376) */
87 struct igmp_v3_group_record {
88 uint8_t record_type;
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) */
94 } __attribute__((packed));
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 {
105 uint8_t type;
106 uint8_t reserved1;
107 uint16_t checksum;
108 uint16_t reserved2;
109 uint16_t number_of_group_records;
110 struct igmp_v3_group_record group_records[0];
111 } __attribute__((packed));
113 struct igmp_v3_membership_query {
114 uint8_t type;
115 uint8_t max_resp_code;
116 uint16_t checksum;
117 uint32_t group_address;
118 #if defined(__LITTLE_ENDIAN_BITFIELD)
119 uint8_t qrv :3,
120 s_flag :1,
122 #elif defined(__BIG_ENDIAN_BITFIELD)
123 uint8_t :4,
124 s_flag :1,
125 qrv :3;
126 #else
127 # error "Please fix <asm/byteorder.h>"
128 #endif
129 uint8_t qqic;
130 uint16_t number_of_sources;
131 uint32_t source_addresses[0];
132 } __attribute__((packed));
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)
145 switch (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";
170 case RGMP_HELLO:
171 return "Hello";
172 case RGMP_BYE:
173 return "Bye";
174 case RGMP_JOIN_GROUP:
175 return "Join Group";
176 case RGMP_LEAVE_GROUP:
177 return "Leave Group";
178 default:
179 return NULL;
183 #define PRINT_FRIENDLY_NAMED_MSG_TYPE(type) \
184 do { \
185 if (friendly_msg_type_name(type)) \
186 tprintf(" Type (0x%.2x, %s)", type, \
187 friendly_msg_type_name(type)); \
188 else \
189 tprintf(" Type (0x%.2x)", type); \
190 } while (0)
192 static char *friendly_group_rec_type_name(uint8_t rec_type)
194 switch (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";
207 default:
208 return NULL;
212 static void dissect_igmp_v0(struct pkt_buff *pkt)
214 char addr[INET_ADDRSTRLEN];
215 uint16_t csum;
217 static const char *reply_codes[] = {
218 "Request Granted",
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));
228 if (msg == NULL)
229 return;
231 tprintf(" [ IGMPv0");
232 PRINT_FRIENDLY_NAMED_MSG_TYPE(msg->type);
234 switch (msg->type) {
235 case IGMP_V0_CREATE_GROUP_REQUEST:
236 switch (msg->code) {
237 case 0:
238 tprintf(", Code (%u, %s)", msg->code, "Public");
239 break;
240 case 1:
241 tprintf(", Code (%u, %s)", msg->code, "Private");
242 break;
243 default:
244 tprintf(", Code (%u)", msg->code);
246 break;
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:
251 if (msg->code < 5)
252 tprintf(", Code (%u, %s)", msg->code, reply_codes[msg->code]);
253 else
254 tprintf(", Code (%u, Request Pending, Retry In %u Seconds)",
255 msg->code, msg->code);
256 break;
257 default:
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");
264 if (csum)
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%.16lx)", msg->access_key);
271 tprintf(" ]\n");
274 static void dissect_igmp_v1(struct pkt_buff *pkt)
276 char addr[INET_ADDRSTRLEN];
277 uint16_t csum;
279 struct igmp_v1_msg *msg =
280 (struct igmp_v1_msg *) pkt_pull(pkt, sizeof(*msg));
282 if (msg == NULL)
283 return;
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");
290 if (csum)
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);
295 tprintf(" ]\n");
298 static void dissect_igmp_v2(struct pkt_buff *pkt)
300 char addr[INET_ADDRSTRLEN];
301 uint16_t csum;
303 struct igmp_v2_msg *msg =
304 (struct igmp_v2_msg *) pkt_pull(pkt, sizeof(*msg));
306 if (msg == NULL)
307 return;
309 switch (msg->type) {
310 case RGMP_HELLO:
311 case RGMP_BYE:
312 case RGMP_JOIN_GROUP:
313 case RGMP_LEAVE_GROUP:
314 tprintf(" [ IGMPv2 (RGMP)");
315 break;
316 default:
317 tprintf(" [ IGMPv2");
318 break;
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");
326 if (csum)
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);
331 tprintf(" ]\n");
334 static void dissect_igmp_v3_membership_query(struct pkt_buff *pkt)
336 char addr[INET_ADDRSTRLEN];
337 size_t n;
338 uint16_t csum;
339 uint32_t *src_addr;
341 struct igmp_v3_membership_query *msg =
342 (struct igmp_v3_membership_query *) pkt_pull(pkt, sizeof(*msg));
344 if (msg == NULL)
345 return;
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");
354 if (csum)
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);
368 if (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);
373 while (n--) {
374 src_addr = (uint32_t *)
375 pkt_pull(pkt, sizeof(*src_addr));
376 if (src_addr != NULL)
377 break;
378 inet_ntop(AF_INET, src_addr, addr, sizeof(addr));
379 tprintf(", %s", addr);
381 tprintf(")");
384 tprintf(" ]\n");
387 static void dissect_igmp_v3_membership_report(struct pkt_buff *pkt)
389 char addr[INET_ADDRSTRLEN];
390 size_t m, n;
391 uint16_t csum;
392 uint32_t *src_addr;
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));
398 if (msg == NULL)
399 return;
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");
406 if (csum)
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);
411 tprintf(" ]\n");
413 while (m--) {
414 rec = (struct igmp_v3_group_record *) pkt_pull(pkt, sizeof(*rec));
416 if (rec == NULL)
417 break;
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));
423 else
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);
430 if (n--) {
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);
435 while (n--) {
436 src_addr = (uint32_t *)
437 pkt_pull(pkt, sizeof(*src_addr));
438 if (src_addr != NULL)
439 break;
440 inet_ntop(AF_INET, src_addr, addr, sizeof(addr));
441 tprintf(", %s", addr);
443 tprintf(")");
447 tprintf(" ]\n");
449 tprintf("\n");
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);
465 break;
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);
474 break;
475 case IGMP_V1_MEMBERSHIP_REPORT:
476 if (pkt_len(pkt) == sizeof(struct igmp_v1_msg))
477 dissect_igmp_v1(pkt);
478 break;
479 case RGMP_HELLO:
480 case RGMP_BYE:
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);
487 break;
488 case IGMP_V3_MEMBERSHIP_REPORT:
489 if (pkt_len(pkt) >= sizeof(struct igmp_v3_membership_report))
490 dissect_igmp_v3_membership_report(pkt);
491 break;
495 static void igmp_less(struct pkt_buff *pkt)
497 int version = -1;
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))
509 version = 0;
510 break;
511 case IGMP_MEMBERSHIP_QUERY: /* v1/v2/v3 */
512 if (pkt_len(pkt) >= sizeof(struct igmp_v3_membership_query))
513 version = 3;
514 else if (pkt_len(pkt) == sizeof(struct igmp_v2_msg)
515 && *(pkt_peek(pkt) + 1))
516 version = 2;
517 else if (pkt_len(pkt) == sizeof(struct igmp_v1_msg))
518 version = 1;
519 break;
520 case IGMP_V1_MEMBERSHIP_REPORT:
521 if (pkt_len(pkt) == sizeof(struct igmp_v1_msg))
522 version = 1;
523 break;
524 case RGMP_HELLO:
525 case RGMP_BYE:
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))
531 version = 2;
532 break;
533 case IGMP_V3_MEMBERSHIP_REPORT:
534 if (pkt_len(pkt) >= sizeof(struct igmp_v3_membership_report))
535 version = 3;
536 break;
539 if (version < 0 || version > 3)
540 return;
542 switch (*pkt_peek(pkt)) {
543 case RGMP_HELLO:
544 case RGMP_BYE:
545 case RGMP_JOIN_GROUP:
546 case RGMP_LEAVE_GROUP:
547 tprintf(" IGMPv2 (RGMP)");
548 break;
549 default:
550 tprintf(" IGMPv%u", version);
551 break;
553 PRINT_FRIENDLY_NAMED_MSG_TYPE(*pkt_peek(pkt));
556 struct protocol igmp_ops = {
557 .key = 0x02,
558 .print_full = igmp,
559 .print_less = igmp_less,