proto_ipv4: don't trim length of pkt_buff
[netsniff-ng.git] / src / proto_igmp.c
blob923707f42403935c2e94f18cecc3c58272c1f2ad
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%.16x)", msg->access_key);
271 tprintf(" ]\n\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\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\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 (%u)", 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(")");
385 tprintf(" ]\n\n");
388 static void dissect_igmp_v3_membership_report(struct pkt_buff *pkt)
390 char addr[INET_ADDRSTRLEN];
391 size_t m, n;
392 uint16_t csum;
393 uint32_t *src_addr;
395 struct igmp_v3_group_record *rec;
396 struct igmp_v3_membership_report *msg =
397 (struct igmp_v3_membership_report *) pkt_pull(pkt, sizeof(*msg));
399 if (msg == NULL)
400 return;
402 tprintf(" [ IGMPv3");
403 PRINT_FRIENDLY_NAMED_MSG_TYPE(msg->type);
404 csum = calc_csum(msg, sizeof(*msg) + pkt_len(pkt), 0);
405 tprintf(", CSum (0x%.4x) is %s", ntohs(msg->checksum), csum ?
406 colorize_start_full(black, red) "bogus (!)" colorize_end() : "ok");
407 if (csum)
408 tprintf(" - %s should be %x%s", colorize_start_full(black, red),
409 csum_expected(msg->checksum, csum), colorize_end());
410 m = ntohs(msg->number_of_group_records);
411 tprintf(", Num Group Rec (%u)", m);
412 tprintf(" ]\n");
414 while (m--) {
415 rec = (struct igmp_v3_group_record *) pkt_pull(pkt, sizeof(*rec));
417 if (rec == NULL)
418 break;
420 tprintf(" [ Group Record");
421 if (friendly_group_rec_type_name(rec->record_type))
422 tprintf(" Type (%u, %s)", rec->record_type,
423 friendly_group_rec_type_name(rec->record_type));
424 else
425 tprintf(" Type (%u)", rec->record_type);
426 n = ntohs(rec->number_of_sources);
427 tprintf(", Num Src (%u)", n);
428 inet_ntop(AF_INET, &rec->multicast_address, addr, sizeof(addr));
429 tprintf(", Multicast Addr (%s)", addr);
431 if (n--) {
432 src_addr = (uint32_t *) pkt_pull(pkt, sizeof(*src_addr));
433 if (src_addr != NULL) {
434 inet_ntop(AF_INET, src_addr, addr, sizeof(addr));
435 tprintf(", Src Addr (%s", addr);
436 while (n--) {
437 src_addr = (uint32_t *)
438 pkt_pull(pkt, sizeof(*src_addr));
439 if (src_addr != NULL)
440 break;
441 inet_ntop(AF_INET, src_addr, addr, sizeof(addr));
442 tprintf(", %s", addr);
444 tprintf(")");
448 tprintf(" ]\n");
450 tprintf("\n");
453 static void igmp(struct pkt_buff *pkt)
455 switch (*pkt_peek(pkt)) {
456 case IGMP_V0_CREATE_GROUP_REQUEST:
457 case IGMP_V0_CREATE_GROUP_REPLY:
458 case IGMP_V0_JOIN_GROUP_REQUEST:
459 case IGMP_V0_JOIN_GROUP_REPLY:
460 case IGMP_V0_LEAVE_GROUP_REQUEST:
461 case IGMP_V0_LEAVE_GROUP_REPLY:
462 case IGMP_V0_CONFIRM_GROUP_REQUEST:
463 case IGMP_V0_CONFIRM_GROUP_REPLY:
464 if (pkt_len(pkt) == sizeof(struct igmp_v0_msg))
465 dissect_igmp_v0(pkt);
466 break;
467 case IGMP_MEMBERSHIP_QUERY: /* v1/v2/v3 */
468 if (pkt_len(pkt) >= sizeof(struct igmp_v3_membership_query))
469 dissect_igmp_v3_membership_query(pkt);
470 else if (pkt_len(pkt) == sizeof(struct igmp_v2_msg)
471 && *(pkt_peek(pkt) + 1))
472 dissect_igmp_v2(pkt);
473 else if (pkt_len(pkt) == sizeof(struct igmp_v1_msg))
474 dissect_igmp_v1(pkt);
475 break;
476 case IGMP_V1_MEMBERSHIP_REPORT:
477 if (pkt_len(pkt) == sizeof(struct igmp_v1_msg))
478 dissect_igmp_v1(pkt);
479 break;
480 case RGMP_HELLO:
481 case RGMP_BYE:
482 case RGMP_JOIN_GROUP:
483 case RGMP_LEAVE_GROUP:
484 case IGMP_V2_MEMBERSHIP_REPORT:
485 case IGMP_V2_LEAVE_GROUP:
486 if (pkt_len(pkt) == sizeof(struct igmp_v2_msg))
487 dissect_igmp_v2(pkt);
488 break;
489 case IGMP_V3_MEMBERSHIP_REPORT:
490 if (pkt_len(pkt) >= sizeof(struct igmp_v3_membership_report))
491 dissect_igmp_v3_membership_report(pkt);
492 break;
496 static void igmp_less(struct pkt_buff *pkt)
498 int version = -1;
500 switch (*pkt_peek(pkt)) {
501 case IGMP_V0_CREATE_GROUP_REQUEST:
502 case IGMP_V0_CREATE_GROUP_REPLY:
503 case IGMP_V0_JOIN_GROUP_REQUEST:
504 case IGMP_V0_JOIN_GROUP_REPLY:
505 case IGMP_V0_LEAVE_GROUP_REQUEST:
506 case IGMP_V0_LEAVE_GROUP_REPLY:
507 case IGMP_V0_CONFIRM_GROUP_REQUEST:
508 case IGMP_V0_CONFIRM_GROUP_REPLY:
509 if (pkt_len(pkt) == sizeof(struct igmp_v0_msg))
510 version = 0;
511 break;
512 case IGMP_MEMBERSHIP_QUERY: /* v1/v2/v3 */
513 if (pkt_len(pkt) >= sizeof(struct igmp_v3_membership_query))
514 version = 3;
515 else if (pkt_len(pkt) == sizeof(struct igmp_v2_msg)
516 && *(pkt_peek(pkt) + 1))
517 version = 2;
518 else if (pkt_len(pkt) == sizeof(struct igmp_v1_msg))
519 version = 1;
520 break;
521 case IGMP_V1_MEMBERSHIP_REPORT:
522 if (pkt_len(pkt) == sizeof(struct igmp_v1_msg))
523 version = 1;
524 break;
525 case RGMP_HELLO:
526 case RGMP_BYE:
527 case RGMP_JOIN_GROUP:
528 case RGMP_LEAVE_GROUP:
529 case IGMP_V2_MEMBERSHIP_REPORT:
530 case IGMP_V2_LEAVE_GROUP:
531 if (pkt_len(pkt) == sizeof(struct igmp_v2_msg))
532 version = 2;
533 break;
534 case IGMP_V3_MEMBERSHIP_REPORT:
535 if (pkt_len(pkt) >= sizeof(struct igmp_v3_membership_report))
536 version = 3;
537 break;
540 if (version < 0 || version > 3)
541 return;
543 switch (*pkt_peek(pkt)) {
544 case RGMP_HELLO:
545 case RGMP_BYE:
546 case RGMP_JOIN_GROUP:
547 case RGMP_LEAVE_GROUP:
548 tprintf(" IGMPv2 (RGMP)");
549 break;
550 default:
551 tprintf(" IGMPv%u", version);
552 break;
554 PRINT_FRIENDLY_NAMED_MSG_TYPE(*pkt_peek(pkt));
557 struct protocol igmp_ops = {
558 .key = 0x02,
559 .print_full = igmp,
560 .print_less = igmp_less,
563 EXPORT_SYMBOL(igmp_ops);