Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / packet-igmp.c
blob234ab049cb17d7334091c0f737f1da4f20c44ab2
1 /* packet-igmp.c
2 * Routines for IGMP packet disassembly
3 * 2001 Ronnie Sahlberg
4 * 2007 Thomas Morin
5 * <See AUTHORS for emails>
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
11 * SPDX-License-Identifier: GPL-2.0-or-later
14 IGMP is defined in the following RFCs
15 RFC988 Version 0 Obsolete
16 RFC1054 Version 1
17 RFC1112 Version 1 (same as RFC1054 as far as we are concerned)
18 RFC2236 Version 2
19 RFC3376 Version 3
21 Size in bytes for each packet
22 type RFC988 RFC1054 RFC2236 RFC3376 DVMRP MRDISC MSNIP IGAP RGMP
23 v0 v1 v2 v3 v1/v3
24 0x01 20
25 0x02 20
26 0x03 20
27 0x04 20
28 0x05 20
29 0x06 20
30 0x07 20
31 0x08 20
32 0x11 8* 8* >=12
33 0x12 8* 8*
34 0x13 x
35 0x16 8
36 0x17 8
37 0x22 >=8
38 0x23 >=8b
39 0x24 >=8a 8b
40 0x25 4a >=8b
41 0x26 4a
42 0x40 ??c
43 0x41 ??c
44 0x42 ??c
45 0xfc 8
46 0xfd 8
47 0xfe 8
48 0xff 8
50 * Differs in second byte of protocol. Always 0 in V1
53 Multicast traceroute was taken from
54 draft-ietf-idmr-traceroute-ipm-07.txt
56 Size in bytes for each packet
57 type draft-ietf-idmr-traceroute-ipm-07.ps
58 0x1e 24 + n*32
59 0x1f 24 + n*32 (n == 0 for Query)
61 x DVMRP Protocol see packet-dvmrp.c
63 DVMRP is defined in the following RFCs
64 RFC1075 Version 1
65 draft-ietf-idmr-dvmrp-v3-10.txt Version 3
67 V1 and V3 can be distinguished by looking at bytes 6 and 7 in the
68 IGMP header.
69 If header[6]==0xff and header[7]==0x03 we have version 3.
71 a MRDISC Protocol see packet-mrdisc.c
73 MRDISC : IGMP Multicast Router DISCovery
74 draft-ietf-idmr-igmp-mrdisc-06.txt
75 TTL == 1 and IP.DST==224.0.0.2 for all packets
77 b MSNIP Protocol see packet-msnip.c
79 MSNIP : Multicast Source Notification of Interest Protocol
80 draft-ietf-idmr-msnip-00.txt
81 0x23, 0x24 are sent with ip.dst==224.0.0.22
82 0x25 is sent as unicast.
84 c IGAP Protocol see packet-igap.c
86 IGAP : Internet Group membership Authentication Protocol
87 draft-hayashi-igap-03.txt
89 d RGMP Protocol see packet-rgmp.c
91 RGMP : Router-port Group Management Protocol
92 RFC3488
93 TTL == 1 and IP.DST==224.0.0.25 for all packets
97 #include "config.h"
99 #include <epan/packet.h>
100 #include <epan/expert.h>
101 #include <epan/range.h>
102 #include <epan/to_str.h>
103 #include <epan/ipproto.h>
104 #include <epan/in_cksum.h>
105 #include <epan/tfs.h>
106 #include <wsutil/array.h>
108 #include <wsutil/str_util.h>
109 #include "packet-igmp.h"
111 void proto_register_igmp(void);
112 void proto_reg_handoff_igmp(void);
114 static dissector_handle_t igmp_handle, igmpv0_handle, igmpv1_handle, igmpv2_handle;
116 static int proto_igmp;
117 static int hf_type;
118 static int hf_reserved;
119 static int hf_version;
120 static int hf_group_type;
121 static int hf_reply_code;
122 static int hf_reply_pending;
123 static int hf_checksum;
124 static int hf_checksum_status;
125 static int hf_identifier;
126 static int hf_access_key;
127 static int hf_max_resp;
128 static int hf_max_resp_exp;
129 static int hf_max_resp_mant;
130 static int hf_suppress;
131 static int hf_qrv;
132 static int hf_qqic;
133 static int hf_num_src;
134 static int hf_saddr;
135 static int hf_num_grp_recs;
136 static int hf_record_type;
137 static int hf_aux_data_len;
138 static int hf_maddr;
139 static int hf_aux_data;
140 static int hf_data;
141 static int hf_mtrace_max_hops;
142 static int hf_mtrace_saddr;
143 static int hf_mtrace_raddr;
144 static int hf_mtrace_rspaddr;
145 static int hf_mtrace_resp_ttl;
146 static int hf_mtrace_q_id;
147 static int hf_mtrace_q_arrival;
148 static int hf_mtrace_q_inaddr;
149 static int hf_mtrace_q_outaddr;
150 static int hf_mtrace_q_prevrtr;
151 static int hf_mtrace_q_inpkt;
152 static int hf_mtrace_q_outpkt;
153 static int hf_mtrace_q_total;
154 static int hf_mtrace_q_rtg_proto;
155 static int hf_mtrace_q_fwd_ttl;
156 static int hf_mtrace_q_mbz;
157 static int hf_mtrace_q_s;
158 static int hf_mtrace_q_src_mask;
159 static int hf_mtrace_q_fwd_code;
161 static int ett_igmp;
162 static int ett_group_record;
163 static int ett_max_resp;
164 static int ett_mtrace_block;
166 static expert_field ei_checksum;
168 static dissector_table_t subdissector_table;
170 #define IGMP_TRACEROUTE_HDR_LEN 24
171 #define IGMP_TRACEROUTE_RSP_LEN 32
173 static const value_string commands[] = {
174 {IGMP_V0_CREATE_GROUP_REQUEST, "Create Group Request" },
175 {IGMP_V0_CREATE_GROUP_REPLY, "Create Group Reply" },
176 {IGMP_V0_JOIN_GROUP_REQUEST, "Join Group Request" },
177 {IGMP_V0_JOIN_GROUP_REPLY, "Join Group Reply" },
178 {IGMP_V0_LEAVE_GROUP_REQUEST, "Leave Group Request" },
179 {IGMP_V0_LEAVE_GROUP_REPLY, "Leave Group Reply" },
180 {IGMP_V0_CONFIRM_GROUP_REQUEST, "Confirm Group Request" },
181 {IGMP_V0_CONFIRM_GROUP_REPLY, "Confirm Group Reply" },
182 {IGMP_V1_HOST_MEMBERSHIP_QUERY, "Membership Query" },
183 {IGMP_V1_HOST_MEMBERSHIP_REPORT,"Membership Report" },
184 {IGMP_DVMRP, "DVMRP Protocol" },
185 {IGMP_V1_PIM_ROUTING_MESSAGE, "PIM Routing Message" },
186 {IGMP_V2_MEMBERSHIP_REPORT, "Membership Report" },
187 {IGMP_V2_LEAVE_GROUP, "Leave Group" },
188 {IGMP_TRACEROUTE_RESPONSE, "Traceroute Response" },
189 {IGMP_TRACEROUTE_QUERY_REQ, "Traceroute Query or Request" },
190 {IGMP_V3_MEMBERSHIP_REPORT, "Membership Report" },
191 {0, NULL}
194 #define IGMP_V3_S 0x08
195 #define IGMP_V3_QRV_MASK 0x07
197 #define IGMP_MAX_RESP_EXP 0x70
198 #define IGMP_MAX_RESP_MANT 0x0f
200 #define IGMP_V0_GROUP_PUBLIC 0x00
201 #define IGMP_V0_GROUP_PRIVATE 0x01
203 static const value_string vs_group_type[] = {
204 {IGMP_V0_GROUP_PUBLIC, "Public Group" },
205 {IGMP_V0_GROUP_PRIVATE, "Private Group" },
206 {0, NULL}
209 #define IGMP_V0_REPLY_GRANTED 0x00
210 #define IGMP_V0_REPLY_NO_RESOURCES 0x01
211 #define IGMP_V0_REPLY_INVALID_CODE 0x02
212 #define IGMP_V0_REPLY_INVALID_GROUP 0x03
213 #define IGMP_V0_REPLY_INVALID_KEY 0x04
215 static const value_string vs_reply_code[] = {
216 {IGMP_V0_REPLY_GRANTED, "Request Granted" },
217 {IGMP_V0_REPLY_NO_RESOURCES, "Request Denied, No Resources" },
218 {IGMP_V0_REPLY_INVALID_CODE, "Request Denied, Invalid Code" },
219 {IGMP_V0_REPLY_INVALID_GROUP, "Request Denied, Invalid Group" },
220 {IGMP_V0_REPLY_INVALID_KEY, "Request Denied, Invalid Key" },
221 {0, NULL}
224 static const true_false_string tfs_s = {
225 "SUPPRESS router side processing",
226 "Do not suppress router side processing"
229 #define IGMP_V3_MODE_IS_INCLUDE 1
230 #define IGMP_V3_MODE_IS_EXCLUDE 2
231 #define IGMP_V3_CHANGE_TO_INCLUDE_MODE 3
232 #define IGMP_V3_CHANGE_TO_EXCLUDE_MODE 4
233 #define IGMP_V3_ALLOW_NEW_SOURCES 5
234 #define IGMP_V3_BLOCK_OLD_SOURCES 6
236 static const value_string vs_record_type[] = {
237 {IGMP_V3_MODE_IS_INCLUDE, "Mode Is Include" },
238 {IGMP_V3_MODE_IS_EXCLUDE, "Mode Is Exclude" },
239 {IGMP_V3_CHANGE_TO_INCLUDE_MODE,"Change To Include Mode" },
240 {IGMP_V3_CHANGE_TO_EXCLUDE_MODE,"Change To Exclude Mode" },
241 {IGMP_V3_ALLOW_NEW_SOURCES, "Allow New Sources" },
242 {IGMP_V3_BLOCK_OLD_SOURCES, "Block Old Sources" },
243 { 0, NULL}
246 static const value_string mtrace_rtg_vals[] = {
247 {1, "DVMRP" },
248 {2, "MOSPF" },
249 {3, "PIM" },
250 {4, "CBT" },
251 {5, "PIM using special routing table" },
252 {6, "PIM using a static route" },
253 {7, "DVMRP using a static route" },
254 {8, "PIM using MBGP (aka BGP4+) route" },
255 {9, "CBT using special routing table" },
256 {10, "CBT using a static route" },
257 {11, "PIM using state created by Assert processing" },
258 {0, NULL}
261 static const value_string mtrace_fwd_code_vals[] = {
262 {0x00, "NO_ERROR" },
263 {0x01, "WRONG_IF" },
264 {0x02, "PRUNE_SENT" },
265 {0x03, "PRUNE_RCVD" },
266 {0x04, "SCOPED" },
267 {0x05, "NO_ROUTE" },
268 {0x06, "WRONG_LAST_HOP" },
269 {0x07, "NOT_FORWARDING" },
270 {0x08, "REACHED_RP" },
271 {0x09, "RPF_IF" },
272 {0x0A, "NO_MULTICAST" },
273 {0x0B, "INFO_HIDDEN" },
274 {0x81, "NO_SPACE" },
275 {0x82, "OLD_ROUTER" },
276 {0x83, "ADMIN_PROHIB" },
277 {0, NULL}
280 void igmp_checksum(proto_tree *tree, tvbuff_t *tvb, int hf_index,
281 int hf_index_status, expert_field* ei_index, packet_info *pinfo, unsigned len)
283 vec_t cksum_vec[1];
285 if (len == 0) {
287 * Checksum the entire IGMP packet.
289 len = tvb_reported_length(tvb);
292 if (!pinfo->fragmented && tvb_captured_length(tvb) >= len) {
294 * The packet isn't part of a fragmented datagram and isn't
295 * truncated, so we can checksum it.
297 SET_CKSUM_VEC_TVB(cksum_vec[0], tvb, 0, len);
298 proto_tree_add_checksum(tree, tvb, 2, hf_index, hf_index_status, ei_index, pinfo, in_cksum(&cksum_vec[0], 1),
299 ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY|PROTO_CHECKSUM_IN_CKSUM);
300 } else
301 proto_tree_add_checksum(tree, tvb, 2, hf_index, hf_index_status, ei_index, pinfo, 0, ENC_BIG_ENDIAN, PROTO_CHECKSUM_NO_FLAGS);
303 return;
306 static proto_tree*
307 dissect_igmp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int* offset, unsigned char* type, int version)
309 proto_item* ti;
310 proto_tree* igmp_tree;
312 col_add_fstr(pinfo->cinfo, COL_PROTOCOL, "IGMPv%d", version);
313 col_clear(pinfo->cinfo, COL_INFO);
315 ti = proto_tree_add_item(tree, proto_igmp, tvb, 0, -1, ENC_NA);
316 igmp_tree = proto_item_add_subtree(ti, ett_igmp);
318 *type = tvb_get_uint8(tvb, 0);
319 col_add_str(pinfo->cinfo, COL_INFO, val_to_str(*type, commands, "Unknown Type:0x%02x"));
321 /* version of IGMP protocol */
322 ti = proto_tree_add_uint(igmp_tree, hf_version, tvb, 0, 0, version);
323 proto_item_set_generated(ti);
325 /* type of command */
326 proto_tree_add_item(igmp_tree, hf_type, tvb, 0, 1, ENC_BIG_ENDIAN);
327 *offset = 1;
329 return igmp_tree;
333 /* Unknown IGMP message type */
334 static int
335 dissect_igmp_unknown(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
337 proto_item* ti;
338 proto_tree* tree;
339 int len;
340 int offset = 0;
341 unsigned char type;
343 col_set_str(pinfo->cinfo, COL_PROTOCOL, "IGMP");
344 col_clear(pinfo->cinfo, COL_INFO);
346 ti = proto_tree_add_item(parent_tree, proto_igmp, tvb, offset, -1, ENC_NA);
347 tree = proto_item_add_subtree(ti, ett_igmp);
349 type = tvb_get_uint8(tvb, offset);
350 col_add_str(pinfo->cinfo, COL_INFO,
351 val_to_str(type, commands, "Unknown Type:0x%02x"));
353 /* type of command */
354 proto_tree_add_uint(tree, hf_type, tvb, offset, 1, type);
355 offset += 1;
357 /* Just call the rest of it "data" */
358 len = tvb_captured_length_remaining(tvb, offset);
359 proto_tree_add_item(tree, hf_data, tvb, offset, -1, ENC_NA);
360 offset += len;
362 return offset;
367 /*************************************************************
368 * IGMP Protocol dissectors
369 *************************************************************/
370 static int
371 dissect_v3_max_resp(tvbuff_t *tvb, proto_tree *parent_tree, int offset)
373 proto_tree *tree;
374 proto_item *item;
375 uint8_t bits;
376 uint32_t tsecs;
378 bits = tvb_get_uint8(tvb, offset);
379 if (bits&0x80) {
380 tsecs = ((bits&IGMP_MAX_RESP_MANT)|0x10);
381 tsecs = tsecs << ( ((bits&IGMP_MAX_RESP_EXP)>>4) + 3);
382 } else {
383 tsecs = bits;
386 item = proto_tree_add_uint_format_value(parent_tree, hf_max_resp, tvb,
387 offset, 1, tsecs, "%.1f sec (0x%02x)",tsecs*0.1,bits);
389 if (bits&0x80) {
390 tree = proto_item_add_subtree(item, ett_max_resp);
392 proto_tree_add_uint(tree, hf_max_resp_exp, tvb, offset, 1,
393 bits);
394 proto_tree_add_uint(tree, hf_max_resp_mant, tvb, offset, 1,
395 bits);
398 offset += 1;
400 return offset;
403 static int
404 dissect_v3_sqrv_bits(tvbuff_t *tvb, proto_tree *parent_tree, int offset)
406 static int * const bits[] = {
407 &hf_suppress,
408 &hf_qrv,
409 NULL
412 proto_tree_add_bitmask_list(parent_tree, tvb, offset, 1, bits, ENC_NA);
413 offset += 1;
415 return offset;
418 static int
419 dissect_v3_group_record(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, int offset)
421 proto_tree *tree;
422 proto_item *item;
423 int old_offset = offset;
424 uint8_t adl;
425 uint16_t num;
426 const char *maddr_str;
427 uint8_t record_type;
429 tree = proto_tree_add_subtree_format(parent_tree, tvb, offset, -1,
430 ett_group_record, &item, "Group Record : %s %s",
431 tvb_ip_to_str(pinfo->pool, tvb, offset+4),
432 val_to_str_const(tvb_get_uint8(tvb, offset), vs_record_type,"")
435 /* record type */
436 record_type = tvb_get_uint8(tvb, offset);
437 proto_tree_add_item(tree, hf_record_type, tvb, offset, 1, ENC_BIG_ENDIAN);
438 offset += 1;
440 /* aux data len */
441 adl = tvb_get_uint8(tvb, offset);
442 proto_tree_add_uint(tree, hf_aux_data_len, tvb, offset, 1, adl);
443 offset += 1;
445 /*number of sources*/
446 num = tvb_get_ntohs(tvb, offset);
447 proto_tree_add_uint(tree, hf_num_src, tvb, offset, 2, num);
448 offset += 2;
450 /* multicast address */
451 proto_tree_add_item(tree, hf_maddr, tvb, offset, 4, ENC_BIG_ENDIAN);
452 maddr_str = tvb_ip_to_str(pinfo->pool, tvb, offset);
453 offset += 4;
455 if (num == 0) {
456 switch(record_type) {
457 case IGMP_V3_MODE_IS_INCLUDE:
458 case IGMP_V3_CHANGE_TO_INCLUDE_MODE:
459 col_append_fstr(pinfo->cinfo, COL_INFO, " / Leave group %s", maddr_str);
460 break;
461 case IGMP_V3_MODE_IS_EXCLUDE:
462 case IGMP_V3_CHANGE_TO_EXCLUDE_MODE:
463 col_append_fstr(pinfo->cinfo, COL_INFO,
464 " / Join group %s for any sources", maddr_str);
465 break;
466 case IGMP_V3_ALLOW_NEW_SOURCES:
467 col_append_fstr(pinfo->cinfo, COL_INFO,
468 " / Group %s, ALLOW_NEW_SOURCES but no source specified (?)",
469 maddr_str);
470 break;
471 case IGMP_V3_BLOCK_OLD_SOURCES:
472 col_append_fstr(pinfo->cinfo, COL_INFO,
473 " / Group %s, BLOCK_OLD_SOURCES but no source specified (?)",
474 maddr_str);
475 break;
476 default:
477 col_append_fstr(pinfo->cinfo, COL_INFO,
478 " / Group %s, unknown record type (?)", maddr_str);
479 break;
481 } else {
482 switch(record_type) {
483 case IGMP_V3_MODE_IS_INCLUDE:
484 case IGMP_V3_CHANGE_TO_INCLUDE_MODE:
485 col_append_fstr(pinfo->cinfo, COL_INFO,
486 " / Join group %s for source%s {",
487 maddr_str, (num>1) ? "s in" : "");
488 break;
489 case IGMP_V3_MODE_IS_EXCLUDE:
490 case IGMP_V3_CHANGE_TO_EXCLUDE_MODE:
491 col_append_fstr(pinfo->cinfo, COL_INFO,
492 " / Join group %s, for source%s {",
493 maddr_str, (num>1) ? "s not in" : " not");
494 break;
495 case IGMP_V3_ALLOW_NEW_SOURCES:
496 col_append_fstr(pinfo->cinfo, COL_INFO,
497 " / Group %s, new source%s {",
498 maddr_str, (num>1) ? "s" : "");
499 break;
500 case IGMP_V3_BLOCK_OLD_SOURCES:
501 col_append_fstr(pinfo->cinfo, COL_INFO,
502 " / Group %s, block source%s {",
503 maddr_str, (num>1) ? "s" : "");
504 break;
505 default:
506 col_append_fstr(pinfo->cinfo, COL_INFO,
507 " / Group %s, unknown record type (?), sources {",
508 maddr_str);
509 break;
513 /* source addresses */
514 while(num--){
515 col_append_fstr(pinfo->cinfo, COL_INFO, "%s%s",
516 tvb_ip_to_str(pinfo->pool, tvb, offset), (num?", ":"}"));
518 proto_tree_add_item(tree, hf_saddr, tvb, offset, 4, ENC_BIG_ENDIAN);
519 offset += 4;
522 /* aux data */
523 if(adl){
524 proto_tree_add_item(tree, hf_aux_data, tvb, offset, adl*4, ENC_NA);
525 offset += adl*4;
528 proto_item_set_len(item, offset-old_offset);
529 return offset;
532 /* dissectors for version 3, rfc3376 */
533 static int
534 dissect_igmp_v3_report(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_)
536 proto_tree* tree;
537 uint16_t num;
538 int offset;
539 unsigned char type;
541 tree = dissect_igmp_common(tvb, pinfo, parent_tree, &offset, &type, 3);
543 proto_tree_add_item(tree, hf_reserved, tvb, offset, 1, ENC_NA);
544 offset += 1;
546 /* checksum */
547 igmp_checksum(tree, tvb, hf_checksum, hf_checksum_status, &ei_checksum, pinfo, 0);
548 offset += 2;
550 proto_tree_add_item(tree, hf_reserved, tvb, offset, 2, ENC_NA);
551 offset += 2;
553 /* number of group records */
554 num = tvb_get_ntohs(tvb, offset);
555 if (!num)
556 col_append_str(pinfo->cinfo, COL_INFO, " - General query");
558 proto_tree_add_uint(tree, hf_num_grp_recs, tvb, offset, 2, num);
559 offset += 2;
561 while (num--)
562 offset = dissect_v3_group_record(tvb, pinfo, tree, offset);
564 return offset;
567 static int
568 dissect_igmp_v3_query(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_)
570 proto_tree* tree;
571 uint16_t num;
572 int offset;
573 unsigned char type;
575 tree = dissect_igmp_common(tvb, pinfo, parent_tree, &offset, &type, 3);
577 num = tvb_get_ntohs(tvb, offset+9);
578 /* max resp code */
579 offset = dissect_v3_max_resp(tvb, tree, offset);
581 /* checksum */
582 igmp_checksum(tree, tvb, hf_checksum, hf_checksum_status, &ei_checksum, pinfo, 0);
583 offset += 2;
585 /* group address */
586 proto_tree_add_item(tree, hf_maddr, tvb, offset, 4, ENC_BIG_ENDIAN);
588 if (!tvb_get_ipv4(tvb, offset)) {
589 col_append_str(pinfo->cinfo, COL_INFO, ", general");
590 } else {
591 col_append_fstr(pinfo->cinfo, COL_INFO, ", specific for group %s",
592 tvb_ip_to_str(pinfo->pool, tvb, offset));
594 offset +=4;
596 /* bitmask for S and QRV */
597 offset = dissect_v3_sqrv_bits(tvb, tree, offset);
599 /* qqic */
600 proto_tree_add_item(tree, hf_qqic, tvb, offset, 1, ENC_BIG_ENDIAN);
601 offset += 1;
603 /*number of sources*/
604 proto_tree_add_uint(tree, hf_num_src, tvb, offset, 2, num);
605 if (num) {
606 col_append_fstr(pinfo->cinfo, COL_INFO, ", source%s {", (num>1)?"s":"");
608 offset += 2;
610 while(num--){
611 col_append_fstr(pinfo->cinfo, COL_INFO, "%s%s", tvb_ip_to_str(pinfo->pool, tvb, offset), (num?", ":"}"));
612 proto_tree_add_item(tree, hf_saddr, tvb, offset, 4, ENC_BIG_ENDIAN);
613 offset += 4;
616 return offset;
619 /* dissector for version 2 query and report, rfc2236 */
620 static int
621 dissect_igmp_v2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_)
623 proto_tree* tree;
624 uint8_t tsecs;
625 int offset;
626 unsigned char type;
628 tree = dissect_igmp_common(tvb, pinfo, parent_tree, &offset, &type, 2);
630 /* max resp time */
631 tsecs = tvb_get_uint8(tvb, offset);
632 proto_tree_add_uint_format_value(tree, hf_max_resp, tvb,
633 offset, 1, tsecs, "%.1f sec (0x%02x)", tsecs*0.1,tsecs);
634 offset += 1;
636 /* checksum */
637 igmp_checksum(tree, tvb, hf_checksum, hf_checksum_status, &ei_checksum, pinfo, 8);
638 offset += 2;
640 /* group address */
641 proto_tree_add_item(tree, hf_maddr, tvb, offset, 4, ENC_BIG_ENDIAN);
643 if (! tvb_get_ipv4(tvb, offset)) {
644 col_append_str(pinfo->cinfo, COL_INFO, ", general");
645 } else {
646 switch(type)
648 case IGMP_V2_LEAVE_GROUP:
649 col_append_fstr(pinfo->cinfo, COL_INFO, " %s", tvb_ip_to_str(pinfo->pool, tvb, offset));
650 break;
651 case IGMP_V1_HOST_MEMBERSHIP_QUERY:
652 col_append_fstr(pinfo->cinfo, COL_INFO, ", specific for group %s", tvb_ip_to_str(pinfo->pool, tvb, offset));
653 break;
654 default: /* IGMP_V2_MEMBERSHIP_REPORT is the only case left */
655 col_append_fstr(pinfo->cinfo, COL_INFO, " group %s", tvb_ip_to_str(pinfo->pool, tvb, offset));
656 break;
659 offset +=4;
661 return offset;
664 /* dissector for version 1 query and report, rfc1054 */
665 static int
666 dissect_igmp_v1(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_)
668 proto_tree* tree;
669 int offset;
670 unsigned char type;
672 tree = dissect_igmp_common(tvb, pinfo, parent_tree, &offset, &type, 1);
674 /* skip unused byte */
675 proto_tree_add_item(tree, hf_reserved, tvb, offset, 1, ENC_NA);
676 offset += 1;
678 /* checksum */
679 igmp_checksum(tree, tvb, hf_checksum, hf_checksum_status, &ei_checksum, pinfo, 8);
680 offset += 2;
682 /* group address */
683 proto_tree_add_item(tree, hf_maddr, tvb, offset, 4, ENC_BIG_ENDIAN);
684 offset +=4;
686 return offset;
689 /* dissector for version 0, rfc988 */
690 static int
691 dissect_igmp_v0(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_)
693 proto_tree* tree;
694 unsigned char code;
695 int offset;
696 unsigned char type;
698 tree = dissect_igmp_common(tvb, pinfo, parent_tree, &offset, &type, 0);
700 /* Code */
701 code = tvb_get_uint8(tvb, offset);
702 if (type==IGMP_V0_CREATE_GROUP_REQUEST) {
703 proto_tree_add_uint(tree, hf_group_type, tvb, offset, 1, code);
704 } else if (!(type&0x01)) {
705 if (code <5) {
706 proto_tree_add_uint(tree, hf_reply_code, tvb, offset, 1, code);
707 } else {
708 proto_tree_add_uint(tree, hf_reply_pending, tvb, offset, 1, code);
711 offset += 1;
713 /* checksum */
714 igmp_checksum(tree, tvb, hf_checksum, hf_checksum_status, &ei_checksum, pinfo, 20);
715 offset += 2;
717 /* identifier */
718 proto_tree_add_item(tree, hf_identifier, tvb, offset, 4, ENC_BIG_ENDIAN);
719 offset += 4;
721 /* group address */
722 proto_tree_add_item(tree, hf_maddr, tvb, offset, 4, ENC_BIG_ENDIAN);
723 offset += 4;
725 /* access key */
726 proto_tree_add_item(tree, hf_access_key, tvb, offset, 8, ENC_NA);
727 offset += 8;
729 return offset;
732 static int
733 dissect_igmp_mquery(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data)
735 if ( tvb_reported_length(tvb)>=12 ) {
736 /* version 3 */
737 return dissect_igmp_v3_query(tvb, pinfo, parent_tree, data);
740 /* v1 and v2 differs in second byte of header */
741 if (tvb_get_uint8(tvb, 1)) {
742 return dissect_igmp_v2(tvb, pinfo, parent_tree, data);
745 return dissect_igmp_v1(tvb, pinfo, parent_tree, data);
748 /* dissector for multicast traceroute, rfc???? */
749 static int
750 dissect_igmp_mtrace(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_)
752 proto_tree* tree;
753 proto_item* ti;
754 int offset = 0;
755 unsigned char type;
756 const char *typestr, *blocks = NULL;
757 char buf[20];
759 ti = proto_tree_add_item(parent_tree, proto_igmp, tvb, offset, -1, ENC_NA);
760 tree = proto_item_add_subtree(ti, ett_igmp);
762 col_set_str(pinfo->cinfo, COL_PROTOCOL, "IGMP");
763 col_clear(pinfo->cinfo, COL_INFO);
765 /* All multicast traceroute packets (Query, Request and
766 * Response) have the same fixed header. Request and Response
767 * have one or more response data blocks following this fixed
768 * header. Since Query and Request share the same IGMP type,
769 * the method to differentiate between them is to check the
770 * IGMP packet length. Queries are only
771 * IGMP_TRACEROUTE_HDR_LEN bytes long.
773 type = tvb_get_uint8(tvb, offset);
774 if (type == IGMP_TRACEROUTE_RESPONSE) {
775 int i = (tvb_reported_length_remaining(tvb, offset) - IGMP_TRACEROUTE_HDR_LEN) / IGMP_TRACEROUTE_RSP_LEN;
776 snprintf(buf, sizeof buf, ", %d block%s", i, plurality(i, "", "s"));
777 typestr = "Traceroute Response";
778 blocks = buf;
779 } else if (tvb_reported_length_remaining(tvb, offset) == IGMP_TRACEROUTE_HDR_LEN)
780 typestr = "Traceroute Query";
781 else
782 typestr = "Traceroute Request";
784 col_set_str(pinfo->cinfo, COL_INFO, typestr);
785 if (blocks)
786 col_append_str(pinfo->cinfo, COL_INFO, blocks);
788 proto_tree_add_uint_format_value(tree, hf_type, tvb, offset, 1, type,
789 "%s (0x%02x)", typestr, type);
790 offset += 1;
792 /* maximum number of hops that the requester wants to trace */
793 proto_tree_add_item(tree, hf_mtrace_max_hops, tvb, offset, 1, ENC_BIG_ENDIAN);
794 offset += 1;
796 /* checksum */
797 igmp_checksum(tree, tvb, hf_checksum, hf_checksum_status, &ei_checksum, pinfo, 0);
798 offset += 2;
800 /* group address to be traced */
801 proto_tree_add_item(tree, hf_maddr, tvb, offset, 4, ENC_BIG_ENDIAN);
802 offset += 4;
804 /* address of multicast source for the path being traced */
805 proto_tree_add_item(tree, hf_mtrace_saddr, tvb, offset, 4, ENC_BIG_ENDIAN);
806 offset += 4;
808 /* address of multicast receiver for the path being traced */
809 proto_tree_add_item(tree, hf_mtrace_raddr, tvb, offset, 4, ENC_BIG_ENDIAN);
810 offset += 4;
812 /* address where the completed traceroute response packet gets sent */
813 proto_tree_add_item(tree, hf_mtrace_rspaddr, tvb, offset, 4, ENC_BIG_ENDIAN);
814 offset += 4;
816 /* for multicasted responses, TTL at which to multicast the response */
817 proto_tree_add_item(tree, hf_mtrace_resp_ttl, tvb, offset, 1, ENC_BIG_ENDIAN);
818 offset += 1;
820 /* unique identifier for this traceroute request (for e.g. duplicate/delay detection) */
821 proto_tree_add_item(tree, hf_mtrace_q_id, tvb, offset, 3, ENC_BIG_ENDIAN);
822 offset += 3;
824 /* If this was Query, we only had the fixed header */
825 if (tvb_reported_length_remaining(tvb, offset) == 0)
826 return offset;
828 /* Loop through the response data blocks */
829 while (tvb_reported_length_remaining(tvb, offset) >= IGMP_TRACEROUTE_RSP_LEN) {
830 proto_tree *block_tree;
832 block_tree = proto_tree_add_subtree_format(tree, tvb, offset, IGMP_TRACEROUTE_RSP_LEN,
833 ett_mtrace_block, NULL, "Response data block: %s -> %s, Proto: %s, Forwarding Code: %s",
834 tvb_ip_to_str(pinfo->pool, tvb, offset + 4),
835 tvb_ip_to_str(pinfo->pool, tvb, offset + 8),
836 val_to_str_const(tvb_get_uint8(tvb, offset + 28), mtrace_rtg_vals, "Unknown"),
837 val_to_str_const(tvb_get_uint8(tvb, offset + 31), mtrace_fwd_code_vals, "Unknown"));
839 /* Query Arrival Time */
840 proto_tree_add_item(block_tree, hf_mtrace_q_arrival, tvb, offset, 4, ENC_BIG_ENDIAN);
841 offset += 4;
843 /* Incoming Interface Address */
844 proto_tree_add_item(block_tree, hf_mtrace_q_inaddr, tvb, offset, 4, ENC_BIG_ENDIAN);
845 offset += 4;
847 /* Outgoing Interface Address */
848 proto_tree_add_item(block_tree, hf_mtrace_q_outaddr, tvb, offset, 4, ENC_BIG_ENDIAN);
849 offset += 4;
851 /* Previous-Hop Router Address */
852 proto_tree_add_item(block_tree, hf_mtrace_q_prevrtr, tvb, offset, 4, ENC_BIG_ENDIAN);
853 offset += 4;
855 /* Input packet count on incoming interface */
856 proto_tree_add_item(block_tree, hf_mtrace_q_inpkt, tvb, offset, 4, ENC_BIG_ENDIAN);
857 offset += 4;
859 /* Output packet count on outgoing interface */
860 proto_tree_add_item(block_tree, hf_mtrace_q_outpkt, tvb, offset, 4, ENC_BIG_ENDIAN);
861 offset += 4;
863 /* Total number of packets for this source-group pair */
864 proto_tree_add_item(block_tree, hf_mtrace_q_total, tvb, offset, 4, ENC_BIG_ENDIAN);
865 offset += 4;
867 /* Routing protocol in use between this and previous-hop router */
868 proto_tree_add_item(block_tree, hf_mtrace_q_rtg_proto, tvb, offset, 1, ENC_BIG_ENDIAN);
869 offset += 1;
871 /* TTL that a packet is required to be forwarded */
872 proto_tree_add_item(block_tree, hf_mtrace_q_fwd_ttl, tvb, offset, 1, ENC_BIG_ENDIAN);
873 offset += 1;
875 /* Must be zeroed and ignored bit, S bit and src network mask length */
876 proto_tree_add_item(block_tree, hf_mtrace_q_mbz, tvb, offset, 1, ENC_BIG_ENDIAN);
877 proto_tree_add_item(block_tree, hf_mtrace_q_s, tvb, offset, 1, ENC_BIG_ENDIAN);
878 proto_tree_add_item(block_tree, hf_mtrace_q_src_mask, tvb, offset, 1, ENC_BIG_ENDIAN);
879 offset += 1;
881 /* Forwarding information/error code */
882 proto_tree_add_item(block_tree, hf_mtrace_q_fwd_code, tvb, offset, 1, ENC_BIG_ENDIAN);
883 offset += 1;
886 return offset;
889 static int
890 dissect_igmp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_)
892 int offset = 0;
893 unsigned char type;
895 type = tvb_get_uint8(tvb, offset);
897 if (!dissector_try_uint(subdissector_table, type, tvb, pinfo, parent_tree))
899 dissect_igmp_unknown(tvb, pinfo, parent_tree);
901 return tvb_captured_length(tvb);
904 void
905 proto_register_igmp(void)
907 static hf_register_info hf[] = {
908 { &hf_type,
909 { "Type", "igmp.type", FT_UINT8, BASE_HEX,
910 VALS(commands), 0, "IGMP Packet Type", HFILL }},
912 { &hf_reserved,
913 { "Reserved", "igmp.reserved", FT_BYTES, BASE_NONE,
914 NULL, 0, "IGMP Reserved", HFILL }},
916 { &hf_version,
917 { "IGMP Version", "igmp.version", FT_UINT8, BASE_DEC,
918 NULL, 0, NULL, HFILL }},
920 { &hf_group_type,
921 { "Type Of Group", "igmp.group_type", FT_UINT8, BASE_DEC,
922 VALS(vs_group_type), 0, "IGMP V0 Type Of Group", HFILL }},
924 { &hf_reply_code,
925 { "Reply", "igmp.reply", FT_UINT8, BASE_DEC,
926 VALS(vs_reply_code), 0, "IGMP V0 Reply", HFILL }},
928 { &hf_reply_pending,
929 { "Reply Pending", "igmp.reply.pending", FT_UINT8, BASE_DEC,
930 NULL, 0, "IGMP V0 Reply Pending, Retry in this many seconds", HFILL }},
932 { &hf_checksum,
933 { "Checksum", "igmp.checksum", FT_UINT16, BASE_HEX,
934 NULL, 0, "IGMP Checksum", HFILL }},
936 { &hf_checksum_status,
937 { "Checksum Status", "igmp.checksum.status", FT_UINT8, BASE_NONE,
938 VALS(proto_checksum_vals), 0x0, NULL, HFILL }},
940 { &hf_identifier,
941 { "Identifier", "igmp.identifier", FT_UINT32, BASE_DEC,
942 NULL, 0, "IGMP V0 Identifier", HFILL }},
944 { &hf_access_key,
945 { "Access Key", "igmp.access_key", FT_BYTES, BASE_NONE,
946 NULL, 0, "IGMP V0 Access Key", HFILL }},
948 { &hf_max_resp,
949 { "Max Resp Time", "igmp.max_resp", FT_UINT8, BASE_DEC,
950 NULL, 0, "Max Response Time", HFILL }},
952 { &hf_suppress,
953 { "S", "igmp.s", FT_BOOLEAN, 8,
954 TFS(&tfs_s), IGMP_V3_S, "Suppress Router Side Processing", HFILL }},
956 { &hf_qrv,
957 { "QRV", "igmp.qrv", FT_UINT8, BASE_DEC,
958 NULL, IGMP_V3_QRV_MASK, "Querier's Robustness Value", HFILL }},
960 { &hf_qqic,
961 { "QQIC", "igmp.qqic", FT_UINT8, BASE_DEC,
962 NULL, 0, "Querier's Query Interval Code", HFILL }},
964 { &hf_num_src,
965 { "Num Src", "igmp.num_src", FT_UINT16, BASE_DEC,
966 NULL, 0, "Number Of Sources", HFILL }},
968 { &hf_saddr,
969 { "Source Address", "igmp.saddr", FT_IPv4, BASE_NONE,
970 NULL, 0, NULL, HFILL }},
972 { &hf_num_grp_recs,
973 { "Num Group Records", "igmp.num_grp_recs", FT_UINT16, BASE_DEC,
974 NULL, 0, "Number Of Group Records", HFILL }},
976 { &hf_record_type,
977 { "Record Type", "igmp.record_type", FT_UINT8, BASE_DEC,
978 VALS(vs_record_type), 0, NULL, HFILL }},
980 { &hf_aux_data_len,
981 { "Aux Data Len", "igmp.aux_data_len", FT_UINT8, BASE_DEC,
982 NULL, 0, "Aux Data Len, In units of 32bit words", HFILL }},
984 { &hf_maddr,
985 { "Multicast Address", "igmp.maddr", FT_IPv4, BASE_NONE,
986 NULL, 0, NULL, HFILL }},
988 { &hf_aux_data,
989 { "Aux Data", "igmp.aux_data", FT_BYTES, BASE_NONE,
990 NULL, 0, "IGMP V3 Auxiliary Data", HFILL }},
992 { &hf_data,
993 { "Data", "igmp.data", FT_BYTES, BASE_NONE,
994 NULL, 0, NULL, HFILL }},
996 { &hf_max_resp_exp,
997 { "Exponent", "igmp.max_resp.exp", FT_UINT8, BASE_HEX,
998 NULL, IGMP_MAX_RESP_EXP, "Maximum Response Time, Exponent", HFILL }},
1000 { &hf_max_resp_mant,
1001 { "Mantissa", "igmp.max_resp.mant", FT_UINT8, BASE_HEX,
1002 NULL, IGMP_MAX_RESP_MANT, "Maximum Response Time, Mantissa", HFILL }},
1004 { &hf_mtrace_max_hops,
1005 { "# hops", "igmp.mtrace.max_hops", FT_UINT8, BASE_DEC,
1006 NULL, 0, "Maximum Number of Hops to Trace", HFILL }},
1008 { &hf_mtrace_saddr,
1009 { "Source Address", "igmp.mtrace.saddr", FT_IPv4, BASE_NONE,
1010 NULL, 0, "Multicast Source for the Path Being Traced", HFILL }},
1012 { &hf_mtrace_raddr,
1013 { "Receiver Address", "igmp.mtrace.raddr", FT_IPv4, BASE_NONE,
1014 NULL, 0, "Multicast Receiver for the Path Being Traced", HFILL }},
1016 { &hf_mtrace_rspaddr,
1017 { "Response Address", "igmp.mtrace.rspaddr", FT_IPv4, BASE_NONE,
1018 NULL, 0, "Destination of Completed Traceroute Response", HFILL }},
1020 { &hf_mtrace_resp_ttl,
1021 { "Response TTL", "igmp.mtrace.resp_ttl", FT_UINT8, BASE_DEC,
1022 NULL, 0, "TTL for Multicasted Responses", HFILL }},
1024 { &hf_mtrace_q_id,
1025 { "Query ID", "igmp.mtrace.q_id", FT_UINT24, BASE_DEC,
1026 NULL, 0, "Identifier for this Traceroute Request", HFILL }},
1028 { &hf_mtrace_q_arrival,
1029 { "Query Arrival", "igmp.mtrace.q_arrival", FT_UINT32, BASE_DEC,
1030 NULL, 0, "Query Arrival Time", HFILL }},
1032 { &hf_mtrace_q_inaddr,
1033 { "In itf addr", "igmp.mtrace.q_inaddr", FT_IPv4, BASE_NONE,
1034 NULL, 0, "Incoming Interface Address", HFILL }},
1036 { &hf_mtrace_q_outaddr,
1037 { "Out itf addr", "igmp.mtrace.q_outaddr", FT_IPv4, BASE_NONE,
1038 NULL, 0, "Outgoing Interface Address", HFILL }},
1040 { &hf_mtrace_q_prevrtr,
1041 { "Previous rtr addr", "igmp.mtrace.q_prevrtr", FT_IPv4, BASE_NONE,
1042 NULL, 0, "Previous-Hop Router Address", HFILL }},
1044 { &hf_mtrace_q_inpkt,
1045 { "In pkts", "igmp.mtrace.q_inpkt", FT_UINT32, BASE_DEC,
1046 NULL, 0, "Input packet count on incoming interface", HFILL }},
1048 { &hf_mtrace_q_outpkt,
1049 { "Out pkts", "igmp.mtrace.q_outpkt", FT_UINT32, BASE_DEC,
1050 NULL, 0, "Output packet count on outgoing interface", HFILL }},
1052 { &hf_mtrace_q_total,
1053 { "S,G pkt count", "igmp.mtrace.q_total", FT_UINT32, BASE_DEC,
1054 NULL, 0, "Total number of packets for this source-group pair", HFILL }},
1056 { &hf_mtrace_q_rtg_proto,
1057 { "Rtg Protocol", "igmp.mtrace.q_rtg_proto", FT_UINT8, BASE_DEC,
1058 VALS(mtrace_rtg_vals), 0, "Routing protocol between this and previous hop rtr", HFILL }},
1060 { &hf_mtrace_q_fwd_ttl,
1061 { "FwdTTL", "igmp.mtrace.q_fwd_ttl", FT_UINT8, BASE_DEC,
1062 NULL, 0, "TTL required for forwarding", HFILL }},
1064 { &hf_mtrace_q_mbz,
1065 { "MBZ", "igmp.mtrace.q_mbz", FT_UINT8, BASE_HEX,
1066 NULL, 0x80, "Must be zeroed on transmission and ignored on reception", HFILL }},
1068 { &hf_mtrace_q_s,
1069 { "S", "igmp.mtrace.q_s", FT_UINT8, BASE_HEX,
1070 NULL, 0x40, "Set if S,G packet count is for source network", HFILL }},
1072 { &hf_mtrace_q_src_mask,
1073 { "Src Mask", "igmp.mtrace.q_src_mask", FT_UINT8, BASE_HEX,
1074 NULL, 0x3F, "Source mask length. 63 when forwarding on group state", HFILL }},
1076 { &hf_mtrace_q_fwd_code,
1077 { "Forwarding Code", "igmp.mtrace.q_fwd_code", FT_UINT8, BASE_HEX,
1078 VALS(mtrace_fwd_code_vals), 0, "Forwarding information/error code", HFILL }},
1081 static int *ett[] = {
1082 &ett_igmp,
1083 &ett_group_record,
1084 &ett_max_resp,
1085 &ett_mtrace_block,
1088 static ei_register_info ei[] = {
1089 { &ei_checksum, { "igmp.bad_checksum", PI_CHECKSUM, PI_ERROR, "Bad checksum", EXPFILL }},
1092 expert_module_t* expert_igmp;
1094 proto_igmp = proto_register_protocol("Internet Group Management Protocol", "IGMP", "igmp");
1095 proto_register_field_array(proto_igmp, hf, array_length(hf));
1096 proto_register_subtree_array(ett, array_length(ett));
1097 expert_igmp = expert_register_protocol(proto_igmp);
1098 expert_register_field_array(expert_igmp, ei, array_length(ei));
1100 subdissector_table = register_dissector_table("igmp.type", "IGMP commands", proto_igmp, FT_UINT32, BASE_HEX);
1102 igmp_handle = register_dissector("igmp", dissect_igmp, proto_igmp);
1103 igmpv0_handle = register_dissector("igmp_v0", dissect_igmp_v0, proto_igmp);
1104 igmpv1_handle = register_dissector("igmp_v1", dissect_igmp_v1, proto_igmp);
1105 igmpv2_handle = register_dissector("igmp_v2", dissect_igmp_v2, proto_igmp);
1108 void
1109 proto_reg_handoff_igmp(void)
1111 dissector_handle_t igmp_mquery_handle, igmp_mtrace_handle, igmp_report_handle;
1112 range_t *igmpv0_range = NULL;
1114 dissector_add_uint("ip.proto", IP_PROTO_IGMP, igmp_handle);
1116 /* IGMP v0 */
1117 range_convert_str(NULL, &igmpv0_range, "0-15", 15);
1118 dissector_add_uint_range("igmp.type", igmpv0_range, igmpv0_handle);
1119 wmem_free(NULL, igmpv0_range);
1121 /* IGMP v1 */
1122 dissector_add_uint("igmp.type", IGMP_V1_HOST_MEMBERSHIP_REPORT, igmpv1_handle);
1124 /* IGMP v2 */
1125 dissector_add_uint("igmp.type", IGMP_V2_MEMBERSHIP_REPORT, igmpv2_handle);
1126 dissector_add_uint("igmp.type", IGMP_V2_LEAVE_GROUP, igmpv2_handle);
1128 /* IGMP_V1_HOST_MEMBERSHIP_QUERY, all versions */
1129 igmp_mquery_handle = create_dissector_handle(dissect_igmp_mquery, proto_igmp);
1130 dissector_add_uint("igmp.type", IGMP_V1_HOST_MEMBERSHIP_QUERY, igmp_mquery_handle);
1132 igmp_report_handle = create_dissector_handle(dissect_igmp_v3_report, proto_igmp);
1133 dissector_add_uint("igmp.type", IGMP_V3_MEMBERSHIP_REPORT, igmp_report_handle);
1135 igmp_mtrace_handle = create_dissector_handle(dissect_igmp_mtrace, proto_igmp);
1136 dissector_add_uint("igmp.type", IGMP_TRACEROUTE_RESPONSE, igmp_mtrace_handle);
1137 dissector_add_uint("igmp.type", IGMP_TRACEROUTE_QUERY_REQ, igmp_mtrace_handle);
1141 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1143 * Local variables:
1144 * c-basic-offset: 8
1145 * tab-width: 8
1146 * indent-tabs-mode: t
1147 * End:
1149 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
1150 * :indentSize=8:tabSize=8:noTabs=false: