ring: define default fanout policy
[netsniff-ng.git] / src / proto_igmp.h
blobe750a3f7aa2ff3237190f15bb5fc8812cd6cd6fb
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 #ifndef PROTO_IGMP_H
8 #define PROTO_IGMP_H
10 #include <arpa/inet.h>
11 #include <asm/byteorder.h>
12 #include <netinet/in.h>
14 #include "csum.h"
15 #include "dissector_eth.h"
16 #include "proto_struct.h"
17 #include "built_in.h"
19 /* IGMPv0 (RFC-988) */
20 struct igmp_v0_msg {
21 uint8_t type;
22 uint8_t code;
23 uint16_t checksum;
24 uint32_t identifier;
25 uint32_t group_address;
26 uint64_t access_key;
27 } __packed;
29 /* igmp_v0_msg.type */
30 #define IGMP_V0_CREATE_GROUP_REQUEST 0x01
31 #define IGMP_V0_CREATE_GROUP_REPLY 0x02
32 #define IGMP_V0_JOIN_GROUP_REQUEST 0x03
33 #define IGMP_V0_JOIN_GROUP_REPLY 0x04
34 #define IGMP_V0_LEAVE_GROUP_REQUEST 0x05
35 #define IGMP_V0_LEAVE_GROUP_REPLY 0x06
36 #define IGMP_V0_CONFIRM_GROUP_REQUEST 0x07
37 #define IGMP_V0_CONFIRM_GROUP_REPLY 0x08
39 /* IGMPv1 (RFC-1054/RFC-1112, obsoletes RFC-988) */
40 struct igmp_v1_msg {
41 union {
42 uint8_t version__type;
43 struct {
44 #if defined(__LITTLE_ENDIAN_BITFIELD)
45 uint8_t type :4,
46 version :4;
47 #elif defined(__BIG_ENDIAN_BITFIELD)
48 uint8_t version :4,
49 type :4;
50 #else
51 # error "Please fix <asm/byteorder.h>"
52 #endif
55 uint8_t unused; /* always zero */
56 uint16_t checksum;
57 uint32_t group_address;
58 } __attribute__((packed));
60 /* igmp_v1_msg.version__type (!) */
61 /* IGMP_V1_MEMBERSHIP_QUERY 0x11 */
62 #define IGMP_V1_MEMBERSHIP_REPORT 0x12
64 /* IGMPv2 (RFC-2236) */
65 struct igmp_v2_msg {
66 uint8_t type;
67 uint8_t max_resp_time;
68 uint16_t checksum;
69 uint32_t group_address;
70 } __attribute__((packed));
72 /* igmp_v2_msg.type */
73 /* IGMP_V2_MEMBERSHIP_QUERY 0x11 */
74 #define IGMP_V2_MEMBERSHIP_REPORT 0x16
75 #define IGMP_V2_LEAVE_GROUP 0x17
78 * RGMP (RFC-3488)
79 * The RGMP message format resembles the IGMPv2 message format. All RGMP
80 * messages are sent with TTL 1, to destination address 224.0.0.25.
82 #define RGMP_LEAVE_GROUP 0xFC
83 #define RGMP_JOIN_GROUP 0xFD
84 #define RGMP_BYE 0xFE
85 #define RGMP_HELLO 0xFF
87 /* IGMPv3 (RFC-3376) */
88 struct igmp_v3_group_record {
89 uint8_t record_type;
90 uint8_t aux_data_len; /* always zero */
91 uint16_t number_of_sources;
92 uint32_t multicast_address;
93 uint32_t source_addresses[0];
94 /* auxiliary data (IGMPv3 does not define any) */
95 } __attribute__((packed));
97 /* igmp_v3_group_record.record_type */
98 #define IGMP_V3_MODE_IS_INCLUDE 1
99 #define IGMP_V3_MODE_IS_EXCLUDE 2
100 #define IGMP_V3_CHANGE_TO_INCLUDE_MODE 3
101 #define IGMP_V3_CHANGE_TO_EXCLUDE_MODE 4
102 #define IGMP_V3_ALLOW_NEW_SOURCES 5
103 #define IGMP_V3_BLOCK_OLD_SOURCES 6
105 struct igmp_v3_membership_report {
106 uint8_t type;
107 uint8_t reserved1;
108 uint16_t checksum;
109 uint16_t reserved2;
110 uint16_t number_of_group_records;
111 struct igmp_v3_group_record group_records[0];
112 } __attribute__((packed));
114 struct igmp_v3_membership_query {
115 uint8_t type;
116 uint8_t max_resp_code;
117 uint16_t checksum;
118 uint32_t group_address;
119 #if defined(__LITTLE_ENDIAN_BITFIELD)
120 uint8_t qrv :3,
121 s_flag :1,
123 #elif defined(__BIG_ENDIAN_BITFIELD)
124 uint8_t :4,
125 s_flag :1,
126 qrv :3;
127 #else
128 # error "Please fix <asm/byteorder.h>"
129 #endif
130 uint8_t qqic;
131 uint16_t number_of_sources;
132 uint32_t source_addresses[0];
133 } __attribute__((packed));
135 #define IGMP_MEMBERSHIP_QUERY 0x11 /* v1/v2/v3 */
136 #define IGMP_V3_MEMBERSHIP_REPORT 0x22
138 #define EXP(x) (((x) & 0x70) >> 4)
139 #define MANT(x) ((x) & 0x0F)
141 #define DECODE_MAX_RESP_CODE(x) ((x) < 128 ? (x) : (MANT(x) | 0x10) << (EXP(x) + 3))
142 #define DECODE_QQIC(x) ((x) < 128 ? (x) : (MANT(x) | 0x10) << (EXP(x) + 3))
144 static char *friendly_msg_type_name(uint8_t msg_type)
146 switch (msg_type) {
147 case IGMP_V0_CREATE_GROUP_REQUEST:
148 return "Create Group Request";
149 case IGMP_V0_CREATE_GROUP_REPLY:
150 return "Create Group Reply";
151 case IGMP_V0_JOIN_GROUP_REQUEST:
152 return "Join Group Request";
153 case IGMP_V0_JOIN_GROUP_REPLY:
154 return "Join Group Reply";
155 case IGMP_V0_LEAVE_GROUP_REQUEST:
156 return "Leave Group Request";
157 case IGMP_V0_LEAVE_GROUP_REPLY:
158 return "Leave Group Reply";
159 case IGMP_V0_CONFIRM_GROUP_REQUEST:
160 return "Confirm Group Request";
161 case IGMP_V0_CONFIRM_GROUP_REPLY:
162 return "Confirm Group Reply";
163 case IGMP_MEMBERSHIP_QUERY:
164 return "Membership Query";
165 case IGMP_V1_MEMBERSHIP_REPORT:
166 case IGMP_V2_MEMBERSHIP_REPORT:
167 case IGMP_V3_MEMBERSHIP_REPORT:
168 return "Membership Report";
169 case IGMP_V2_LEAVE_GROUP:
170 return "Leave Group";
171 case RGMP_HELLO:
172 return "Hello";
173 case RGMP_BYE:
174 return "Bye";
175 case RGMP_JOIN_GROUP:
176 return "Join Group";
177 case RGMP_LEAVE_GROUP:
178 return "Leave Group";
179 default:
180 return NULL;
184 #define PRINT_FRIENDLY_NAMED_MSG_TYPE(type) \
185 do { \
186 if (friendly_msg_type_name(type)) \
187 tprintf(" Type (0x%.2x, %s)", type, \
188 friendly_msg_type_name(type)); \
189 else \
190 tprintf(" Type (0x%.2x)", type); \
191 } while (0)
193 static char *friendly_group_rec_type_name(uint8_t rec_type)
195 switch (rec_type) {
196 case IGMP_V3_MODE_IS_INCLUDE:
197 return "Mode Is Include";
198 case IGMP_V3_MODE_IS_EXCLUDE:
199 return "Mode Is Exclude";
200 case IGMP_V3_CHANGE_TO_INCLUDE_MODE:
201 return "Change To Include Mode";
202 case IGMP_V3_CHANGE_TO_EXCLUDE_MODE:
203 return "Change To Exclude Mode";
204 case IGMP_V3_ALLOW_NEW_SOURCES:
205 return "Allow New Sources";
206 case IGMP_V3_BLOCK_OLD_SOURCES:
207 return "Block Old Sources";
208 default:
209 return NULL;
213 static inline void dissect_igmp_v0(struct pkt_buff *pkt)
215 char addr[INET_ADDRSTRLEN];
216 uint16_t csum;
218 static const char *reply_codes[] = {
219 "Request Granted",
220 "Request Denied, No Resources",
221 "Request Denied, Invalid Code",
222 "Request Denied, Invalid Group Address",
223 "Request Denied, Invalid Access Key"
226 struct igmp_v0_msg *msg =
227 (struct igmp_v0_msg *) pkt_pull(pkt, sizeof(*msg));
229 if (msg == NULL)
230 return;
232 tprintf(" [ IGMPv0");
233 PRINT_FRIENDLY_NAMED_MSG_TYPE(msg->type);
235 switch (msg->type) {
236 case IGMP_V0_CREATE_GROUP_REQUEST:
237 switch (msg->code) {
238 case 0:
239 tprintf(", Code (%u, %s)", msg->code, "Public");
240 break;
241 case 1:
242 tprintf(", Code (%u, %s)", msg->code, "Private");
243 break;
244 default:
245 tprintf(", Code (%u)", msg->code);
247 break;
248 case IGMP_V0_CREATE_GROUP_REPLY:
249 case IGMP_V0_JOIN_GROUP_REPLY:
250 case IGMP_V0_LEAVE_GROUP_REPLY:
251 case IGMP_V0_CONFIRM_GROUP_REPLY:
252 if (msg->code < 5)
253 tprintf(", Code (%u, %s)", msg->code, reply_codes[msg->code]);
254 else
255 tprintf(", Code (%u, Request Pending, Retry In %u Seconds)",
256 msg->code, msg->code);
257 break;
258 default:
259 tprintf(", Code (%u)", msg->code);
262 csum = calc_csum(msg, sizeof(*msg) + pkt_len(pkt), 0);
263 tprintf(", CSum (0x%.4x) is %s", ntohs(msg->checksum), csum ?
264 colorize_start_full(black, red) "bogus (!)" colorize_end() : "ok");
265 if (csum)
266 tprintf(" - %s should be %x%s", colorize_start_full(black, red),
267 csum_expected(msg->checksum, csum), colorize_end());
268 tprintf(", Id (%u)", ntohs(msg->identifier));
269 inet_ntop(AF_INET, &msg->group_address, addr, sizeof(addr));
270 tprintf(", Group Addr (%s)", addr);
271 tprintf(", Access Key (0x%.16x)", msg->access_key);
272 tprintf(" ]\n\n");
275 static inline void dissect_igmp_v1(struct pkt_buff *pkt)
277 char addr[INET_ADDRSTRLEN];
278 uint16_t csum;
280 struct igmp_v1_msg *msg =
281 (struct igmp_v1_msg *) pkt_pull(pkt, sizeof(*msg));
283 if (msg == NULL)
284 return;
286 tprintf(" [ IGMPv1");
287 PRINT_FRIENDLY_NAMED_MSG_TYPE(msg->version__type);
288 csum = calc_csum(msg, sizeof(*msg) + pkt_len(pkt), 0);
289 tprintf(", CSum (0x%.4x) is %s", ntohs(msg->checksum), csum ?
290 colorize_start_full(black, red) "bogus (!)" colorize_end() : "ok");
291 if (csum)
292 tprintf(" - %s should be %x%s", colorize_start_full(black, red),
293 csum_expected(msg->checksum, csum), colorize_end());
294 inet_ntop(AF_INET, &msg->group_address, addr, sizeof(addr));
295 tprintf(", Group Addr (%s)", addr);
296 tprintf(" ]\n\n");
299 static inline void dissect_igmp_v2(struct pkt_buff *pkt)
301 char addr[INET_ADDRSTRLEN];
302 uint16_t csum;
304 struct igmp_v2_msg *msg =
305 (struct igmp_v2_msg *) pkt_pull(pkt, sizeof(*msg));
307 if (msg == NULL)
308 return;
310 switch (msg->type) {
311 case RGMP_HELLO:
312 case RGMP_BYE:
313 case RGMP_JOIN_GROUP:
314 case RGMP_LEAVE_GROUP:
315 tprintf(" [ IGMPv2 (RGMP)");
316 break;
317 default:
318 tprintf(" [ IGMPv2");
319 break;
322 PRINT_FRIENDLY_NAMED_MSG_TYPE(msg->type);
323 tprintf(", Max Resp Time (%u)", msg->max_resp_time);
324 csum = calc_csum(msg, sizeof(*msg) + pkt_len(pkt), 0);
325 tprintf(", CSum (0x%.4x) is %s", ntohs(msg->checksum), csum ?
326 colorize_start_full(black, red) "bogus (!)" colorize_end() : "ok");
327 if (csum)
328 tprintf(" - %s should be %x%s", colorize_start_full(black, red),
329 csum_expected(msg->checksum, csum), colorize_end());
330 inet_ntop(AF_INET, &msg->group_address, addr, sizeof(addr));
331 tprintf(", Group Addr (%s)", addr);
332 tprintf(" ]\n\n");
335 static inline void dissect_igmp_v3_membership_query(struct pkt_buff *pkt)
337 char addr[INET_ADDRSTRLEN];
338 size_t n;
339 uint16_t csum;
340 uint32_t *src_addr;
342 struct igmp_v3_membership_query *msg =
343 (struct igmp_v3_membership_query *) pkt_pull(pkt, sizeof(*msg));
345 if (msg == NULL)
346 return;
348 tprintf(" [ IGMPv3");
349 PRINT_FRIENDLY_NAMED_MSG_TYPE(msg->type);
350 tprintf(", Max Resp Code (0x%.2x => %u)", msg->max_resp_code,
351 DECODE_MAX_RESP_CODE(msg->max_resp_code));
352 csum = calc_csum(msg, sizeof(*msg) + pkt_len(pkt), 0);
353 tprintf(", CSum (0x%.4x) is %s", ntohs(msg->checksum), csum ?
354 colorize_start_full(black, red) "bogus (!)" colorize_end() : "ok");
355 if (csum)
356 tprintf(" - %s should be %x%s", colorize_start_full(black, red),
357 csum_expected(msg->checksum, csum), colorize_end());
358 inet_ntop(AF_INET, &msg->group_address, addr, sizeof(addr));
359 /* S Flag (Suppress Router-Side Processing) */
360 tprintf(", Suppress (%u)", msg->s_flag ? 1 : 0);
361 /* QRV (Querier's Robustness Variable) */
362 tprintf(", QRV (%u)", msg->qrv);
363 /* QQIC (Querier's Query Interval Code) */
364 tprintf(", QQIC (0x%.2x => %u)", msg->qqic, DECODE_QQIC(msg->qqic));
365 tprintf(", Group Addr (%s)", addr);
366 n = ntohs(msg->number_of_sources);
367 tprintf(", Num Src (%u)", n);
369 if (n--) {
370 src_addr = (uint32_t *) pkt_pull(pkt, sizeof(*src_addr));
371 if (src_addr != NULL) {
372 inet_ntop(AF_INET, src_addr, addr, sizeof(addr));
373 tprintf(", Src Addr (%s", addr);
374 while (n--) {
375 src_addr = (uint32_t *)
376 pkt_pull(pkt, sizeof(*src_addr));
377 if (src_addr != NULL)
378 break;
379 inet_ntop(AF_INET, src_addr, addr, sizeof(addr));
380 tprintf(", %s", addr);
382 tprintf(")");
386 tprintf(" ]\n\n");
389 static inline void dissect_igmp_v3_membership_report(struct pkt_buff *pkt)
391 char addr[INET_ADDRSTRLEN];
392 size_t m, n;
393 uint16_t csum;
394 uint32_t *src_addr;
396 struct igmp_v3_group_record *rec;
397 struct igmp_v3_membership_report *msg =
398 (struct igmp_v3_membership_report *) pkt_pull(pkt, sizeof(*msg));
400 if (msg == NULL)
401 return;
403 tprintf(" [ IGMPv3");
404 PRINT_FRIENDLY_NAMED_MSG_TYPE(msg->type);
405 csum = calc_csum(msg, sizeof(*msg) + pkt_len(pkt), 0);
406 tprintf(", CSum (0x%.4x) is %s", ntohs(msg->checksum), csum ?
407 colorize_start_full(black, red) "bogus (!)" colorize_end() : "ok");
408 if (csum)
409 tprintf(" - %s should be %x%s", colorize_start_full(black, red),
410 csum_expected(msg->checksum, csum), colorize_end());
411 m = ntohs(msg->number_of_group_records);
412 tprintf(", Num Group Rec (%u)", m);
413 tprintf(" ]\n");
415 while (m--) {
416 rec = (struct igmp_v3_group_record *) pkt_pull(pkt, sizeof(*rec));
418 if (rec == NULL)
419 break;
421 tprintf(" [ Group Record");
422 if (friendly_group_rec_type_name(rec->record_type))
423 tprintf(" Type (%u, %s)", rec->record_type,
424 friendly_group_rec_type_name(rec->record_type));
425 else
426 tprintf(" Type (%u)", rec->record_type);
427 n = ntohs(rec->number_of_sources);
428 tprintf(", Num Src (%u)", n);
429 inet_ntop(AF_INET, &rec->multicast_address, addr, sizeof(addr));
430 tprintf(", Multicast Addr (%s)", addr);
432 if (n--) {
433 src_addr = (uint32_t *) pkt_pull(pkt, sizeof(*src_addr));
434 if (src_addr != NULL) {
435 inet_ntop(AF_INET, src_addr, addr, sizeof(addr));
436 tprintf(", Src Addr (%s", addr);
437 while (n--) {
438 src_addr = (uint32_t *)
439 pkt_pull(pkt, sizeof(*src_addr));
440 if (src_addr != NULL)
441 break;
442 inet_ntop(AF_INET, src_addr, addr, sizeof(addr));
443 tprintf(", %s", addr);
445 tprintf(")");
449 tprintf(" ]\n");
451 tprintf("\n");
454 static inline void igmp(struct pkt_buff *pkt)
456 switch (*pkt_peek(pkt)) {
457 case IGMP_V0_CREATE_GROUP_REQUEST:
458 case IGMP_V0_CREATE_GROUP_REPLY:
459 case IGMP_V0_JOIN_GROUP_REQUEST:
460 case IGMP_V0_JOIN_GROUP_REPLY:
461 case IGMP_V0_LEAVE_GROUP_REQUEST:
462 case IGMP_V0_LEAVE_GROUP_REPLY:
463 case IGMP_V0_CONFIRM_GROUP_REQUEST:
464 case IGMP_V0_CONFIRM_GROUP_REPLY:
465 if (pkt_len(pkt) == sizeof(struct igmp_v0_msg))
466 dissect_igmp_v0(pkt);
467 break;
468 case IGMP_MEMBERSHIP_QUERY: /* v1/v2/v3 */
469 if (pkt_len(pkt) >= sizeof(struct igmp_v3_membership_query))
470 dissect_igmp_v3_membership_query(pkt);
471 else if (pkt_len(pkt) == sizeof(struct igmp_v2_msg)
472 && *(pkt_peek(pkt) + 1))
473 dissect_igmp_v2(pkt);
474 else if (pkt_len(pkt) == sizeof(struct igmp_v1_msg))
475 dissect_igmp_v1(pkt);
476 break;
477 case IGMP_V1_MEMBERSHIP_REPORT:
478 if (pkt_len(pkt) == sizeof(struct igmp_v1_msg))
479 dissect_igmp_v1(pkt);
480 break;
481 case RGMP_HELLO:
482 case RGMP_BYE:
483 case RGMP_JOIN_GROUP:
484 case RGMP_LEAVE_GROUP:
485 case IGMP_V2_MEMBERSHIP_REPORT:
486 case IGMP_V2_LEAVE_GROUP:
487 if (pkt_len(pkt) == sizeof(struct igmp_v2_msg))
488 dissect_igmp_v2(pkt);
489 break;
490 case IGMP_V3_MEMBERSHIP_REPORT:
491 if (pkt_len(pkt) >= sizeof(struct igmp_v3_membership_report))
492 dissect_igmp_v3_membership_report(pkt);
493 break;
497 static inline void igmp_less(struct pkt_buff *pkt)
499 int version = -1;
501 switch (*pkt_peek(pkt)) {
502 case IGMP_V0_CREATE_GROUP_REQUEST:
503 case IGMP_V0_CREATE_GROUP_REPLY:
504 case IGMP_V0_JOIN_GROUP_REQUEST:
505 case IGMP_V0_JOIN_GROUP_REPLY:
506 case IGMP_V0_LEAVE_GROUP_REQUEST:
507 case IGMP_V0_LEAVE_GROUP_REPLY:
508 case IGMP_V0_CONFIRM_GROUP_REQUEST:
509 case IGMP_V0_CONFIRM_GROUP_REPLY:
510 if (pkt_len(pkt) == sizeof(struct igmp_v0_msg))
511 version = 0;
512 break;
513 case IGMP_MEMBERSHIP_QUERY: /* v1/v2/v3 */
514 if (pkt_len(pkt) >= sizeof(struct igmp_v3_membership_query))
515 version = 3;
516 else if (pkt_len(pkt) == sizeof(struct igmp_v2_msg)
517 && *(pkt_peek(pkt) + 1))
518 version = 2;
519 else if (pkt_len(pkt) == sizeof(struct igmp_v1_msg))
520 version = 1;
521 break;
522 case IGMP_V1_MEMBERSHIP_REPORT:
523 if (pkt_len(pkt) == sizeof(struct igmp_v1_msg))
524 version = 1;
525 break;
526 case RGMP_HELLO:
527 case RGMP_BYE:
528 case RGMP_JOIN_GROUP:
529 case RGMP_LEAVE_GROUP:
530 case IGMP_V2_MEMBERSHIP_REPORT:
531 case IGMP_V2_LEAVE_GROUP:
532 if (pkt_len(pkt) == sizeof(struct igmp_v2_msg))
533 version = 2;
534 break;
535 case IGMP_V3_MEMBERSHIP_REPORT:
536 if (pkt_len(pkt) >= sizeof(struct igmp_v3_membership_report))
537 version = 3;
538 break;
541 if (version < 0 || version > 3)
542 return;
544 switch (*pkt_peek(pkt)) {
545 case RGMP_HELLO:
546 case RGMP_BYE:
547 case RGMP_JOIN_GROUP:
548 case RGMP_LEAVE_GROUP:
549 tprintf(" IGMPv2 (RGMP)");
550 break;
551 default:
552 tprintf(" IGMPv%u", version);
553 break;
555 PRINT_FRIENDLY_NAMED_MSG_TYPE(*pkt_peek(pkt));
558 struct protocol igmp_ops = {
559 .key = 0x02,
560 .print_full = igmp,
561 .print_less = igmp_less,
564 #endif /* PROTO_IGMP_H */