trafgen: remove timer-based trigger model
[netsniff-ng.git] / proto_igmp.c
blobb31f5a887c2b41ef462c523e1078ca85be7b6359
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 "csum.h"
13 #include "dissector_eth.h"
14 #include "built_in.h"
15 #include "pkt_buff.h"
17 /* IGMPv0 (RFC-988) */
18 struct igmp_v0_msg {
19 uint8_t type;
20 uint8_t code;
21 uint16_t checksum;
22 uint32_t identifier;
23 uint32_t group_address;
24 uint64_t access_key;
25 } __packed;
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) */
38 struct igmp_v1_msg {
39 union {
40 uint8_t version__type;
41 struct {
42 #if defined(__LITTLE_ENDIAN_BITFIELD)
43 uint8_t type :4,
44 version :4;
45 #elif defined(__BIG_ENDIAN_BITFIELD)
46 uint8_t version :4,
47 type :4;
48 #else
49 # error "Please fix <asm/byteorder.h>"
50 #endif
53 uint8_t unused; /* always zero */
54 uint16_t checksum;
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) */
63 struct igmp_v2_msg {
64 uint8_t type;
65 uint8_t max_resp_time;
66 uint16_t checksum;
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
76 * RGMP (RFC-3488)
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
82 #define RGMP_BYE 0xFE
83 #define RGMP_HELLO 0xFF
85 /* IGMPv3 (RFC-3376) */
86 struct igmp_v3_group_record {
87 uint8_t record_type;
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 {
104 uint8_t type;
105 uint8_t reserved1;
106 uint16_t checksum;
107 uint16_t reserved2;
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 {
113 uint8_t type;
114 uint8_t max_resp_code;
115 uint16_t checksum;
116 uint32_t group_address;
117 #if defined(__LITTLE_ENDIAN_BITFIELD)
118 uint8_t qrv :3,
119 s_flag :1,
121 #elif defined(__BIG_ENDIAN_BITFIELD)
122 uint8_t :4,
123 s_flag :1,
124 qrv :3;
125 #else
126 # error "Please fix <asm/byteorder.h>"
127 #endif
128 uint8_t qqic;
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)
144 switch (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";
169 case RGMP_HELLO:
170 return "Hello";
171 case RGMP_BYE:
172 return "Bye";
173 case RGMP_JOIN_GROUP:
174 return "Join Group";
175 case RGMP_LEAVE_GROUP:
176 return "Leave Group";
177 default:
178 return NULL;
182 #define PRINT_FRIENDLY_NAMED_MSG_TYPE(type) \
183 do { \
184 if (friendly_msg_type_name(type)) \
185 tprintf(" Type (0x%.2x, %s)", type, \
186 friendly_msg_type_name(type)); \
187 else \
188 tprintf(" Type (0x%.2x)", type); \
189 } while (0)
191 static char *friendly_group_rec_type_name(uint8_t rec_type)
193 switch (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";
206 default:
207 return NULL;
211 static void dissect_igmp_v0(struct pkt_buff *pkt)
213 char addr[INET_ADDRSTRLEN];
214 uint16_t csum;
216 static const char *reply_codes[] = {
217 "Request Granted",
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));
227 if (msg == NULL)
228 return;
230 tprintf(" [ IGMPv0");
231 PRINT_FRIENDLY_NAMED_MSG_TYPE(msg->type);
233 switch (msg->type) {
234 case IGMP_V0_CREATE_GROUP_REQUEST:
235 switch (msg->code) {
236 case 0:
237 tprintf(", Code (%u, %s)", msg->code, "Public");
238 break;
239 case 1:
240 tprintf(", Code (%u, %s)", msg->code, "Private");
241 break;
242 default:
243 tprintf(", Code (%u)", msg->code);
245 break;
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:
250 if (msg->code < 5)
251 tprintf(", Code (%u, %s)", msg->code, reply_codes[msg->code]);
252 else
253 tprintf(", Code (%u, Request Pending, Retry In %u Seconds)",
254 msg->code, msg->code);
255 break;
256 default:
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");
263 if (csum)
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);
270 tprintf(" ]\n");
273 static void dissect_igmp_v1(struct pkt_buff *pkt)
275 char addr[INET_ADDRSTRLEN];
276 uint16_t csum;
278 struct igmp_v1_msg *msg =
279 (struct igmp_v1_msg *) pkt_pull(pkt, sizeof(*msg));
281 if (msg == NULL)
282 return;
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");
289 if (csum)
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);
294 tprintf(" ]\n");
297 static void dissect_igmp_v2(struct pkt_buff *pkt)
299 char addr[INET_ADDRSTRLEN];
300 uint16_t csum;
302 struct igmp_v2_msg *msg =
303 (struct igmp_v2_msg *) pkt_pull(pkt, sizeof(*msg));
305 if (msg == NULL)
306 return;
308 switch (msg->type) {
309 case RGMP_HELLO:
310 case RGMP_BYE:
311 case RGMP_JOIN_GROUP:
312 case RGMP_LEAVE_GROUP:
313 tprintf(" [ IGMPv2 (RGMP)");
314 break;
315 default:
316 tprintf(" [ IGMPv2");
317 break;
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");
325 if (csum)
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);
330 tprintf(" ]\n");
333 static void dissect_igmp_v3_membership_query(struct pkt_buff *pkt)
335 char addr[INET_ADDRSTRLEN];
336 size_t n;
337 uint16_t csum;
338 uint32_t *src_addr;
340 struct igmp_v3_membership_query *msg =
341 (struct igmp_v3_membership_query *) pkt_pull(pkt, sizeof(*msg));
343 if (msg == NULL)
344 return;
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");
353 if (csum)
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);
367 if (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);
372 while (n--) {
373 src_addr = (uint32_t *)
374 pkt_pull(pkt, sizeof(*src_addr));
375 if (src_addr == NULL)
376 break;
377 inet_ntop(AF_INET, src_addr, addr, sizeof(addr));
378 tprintf(", %s", addr);
380 tprintf(")");
383 tprintf(" ]\n");
386 static void dissect_igmp_v3_membership_report(struct pkt_buff *pkt)
388 char addr[INET_ADDRSTRLEN];
389 size_t m, n;
390 uint16_t csum;
391 uint32_t *src_addr;
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));
397 if (msg == NULL)
398 return;
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");
405 if (csum)
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);
410 tprintf(" ]\n");
412 while (m--) {
413 rec = (struct igmp_v3_group_record *) pkt_pull(pkt, sizeof(*rec));
415 if (rec == NULL)
416 break;
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));
422 else
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);
429 if (n--) {
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);
434 while (n--) {
435 src_addr = (uint32_t *)
436 pkt_pull(pkt, sizeof(*src_addr));
437 if (src_addr == NULL)
438 break;
439 inet_ntop(AF_INET, src_addr, addr, sizeof(addr));
440 tprintf(", %s", addr);
442 tprintf(")");
446 tprintf(" ]\n");
448 tprintf("\n");
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);
464 break;
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);
473 break;
474 case IGMP_V1_MEMBERSHIP_REPORT:
475 if (pkt_len(pkt) == sizeof(struct igmp_v1_msg))
476 dissect_igmp_v1(pkt);
477 break;
478 case RGMP_HELLO:
479 case RGMP_BYE:
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);
486 break;
487 case IGMP_V3_MEMBERSHIP_REPORT:
488 if (pkt_len(pkt) >= sizeof(struct igmp_v3_membership_report))
489 dissect_igmp_v3_membership_report(pkt);
490 break;
494 static void igmp_less(struct pkt_buff *pkt)
496 int version = -1;
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))
508 version = 0;
509 break;
510 case IGMP_MEMBERSHIP_QUERY: /* v1/v2/v3 */
511 if (pkt_len(pkt) >= sizeof(struct igmp_v3_membership_query))
512 version = 3;
513 else if (pkt_len(pkt) == sizeof(struct igmp_v2_msg)
514 && *(pkt_peek(pkt) + 1))
515 version = 2;
516 else if (pkt_len(pkt) == sizeof(struct igmp_v1_msg))
517 version = 1;
518 break;
519 case IGMP_V1_MEMBERSHIP_REPORT:
520 if (pkt_len(pkt) == sizeof(struct igmp_v1_msg))
521 version = 1;
522 break;
523 case RGMP_HELLO:
524 case RGMP_BYE:
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))
530 version = 2;
531 break;
532 case IGMP_V3_MEMBERSHIP_REPORT:
533 if (pkt_len(pkt) >= sizeof(struct igmp_v3_membership_report))
534 version = 3;
535 break;
538 if (version < 0 || version > 3)
539 return;
541 switch (*pkt_peek(pkt)) {
542 case RGMP_HELLO:
543 case RGMP_BYE:
544 case RGMP_JOIN_GROUP:
545 case RGMP_LEAVE_GROUP:
546 tprintf(" IGMPv2 (RGMP)");
547 break;
548 default:
549 tprintf(" IGMPv%u", version);
550 break;
552 PRINT_FRIENDLY_NAMED_MSG_TYPE(*pkt_peek(pkt));
555 struct protocol igmp_ops = {
556 .key = 0x02,
557 .print_full = igmp,
558 .print_less = igmp_less,