xref: /freebsd/contrib/tcpdump/print-igmp.c (revision 2008043f386721d58158e37e0d7e50df8095942d)
1 /*
2  * Copyright (c) 1988, 1989, 1990, 1991, 1993, 1994, 1995, 1996
3  *  The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that: (1) source code distributions
7  * retain the above copyright notice and this paragraph in its entirety, (2)
8  * distributions including binary code include the above copyright notice and
9  * this paragraph in its entirety in the documentation or other materials
10  * provided with the distribution, and (3) all advertising materials mentioning
11  * features or use of this software display the following acknowledgement:
12  * ``This product includes software developed by the University of California,
13  * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
14  * the University nor the names of its contributors may be used to endorse
15  * or promote products derived from this software without specific prior
16  * written permission.
17  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
18  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
19  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20  */
21 
22 /* \summary: Internet Group Management Protocol (IGMP) printer */
23 
24 /*
25  * specification:
26  *
27  *	RFC 2236 for IGMPv2
28  *	RFC 3376 for IGMPv3
29  *	draft-asaeda-mboned-mtrace-v2 for the mtrace message
30  */
31 
32 #ifdef HAVE_CONFIG_H
33 #include <config.h>
34 #endif
35 
36 #include "netdissect-stdinc.h"
37 
38 #include "netdissect.h"
39 #include "addrtoname.h"
40 #include "extract.h"
41 
42 #ifndef IN_CLASSD
43 #define IN_CLASSD(i) (((int32_t)(i) & 0xf0000000) == 0xe0000000)
44 #endif
45 
46 
47 /* (following from ipmulti/mrouted/prune.h) */
48 
49 /*
50  * The packet format for a traceroute request.
51  */
52 struct tr_query {
53     nd_uint32_t  tr_src;        /* traceroute source */
54     nd_uint32_t  tr_dst;        /* traceroute destination */
55     nd_uint32_t  tr_raddr;      /* traceroute response address */
56     nd_uint8_t   tr_rttl;       /* response ttl */
57     nd_uint24_t  tr_qid;        /* qid */
58 };
59 
60 /*
61  * Traceroute response format.  A traceroute response has a tr_query at the
62  * beginning, followed by one tr_resp for each hop taken.
63  */
64 struct tr_resp {
65     nd_uint32_t tr_qarr;        /* query arrival time */
66     nd_uint32_t tr_inaddr;      /* incoming interface address */
67     nd_uint32_t tr_outaddr;     /* outgoing interface address */
68     nd_uint32_t tr_rmtaddr;     /* parent address in source tree */
69     nd_uint32_t tr_vifin;       /* input packet count on interface */
70     nd_uint32_t tr_vifout;      /* output packet count on interface */
71     nd_uint32_t tr_pktcnt;      /* total incoming packets for src-grp */
72     nd_uint8_t  tr_rproto;      /* routing proto deployed on router */
73     nd_uint8_t  tr_fttl;        /* ttl required to forward on outvif */
74     nd_uint8_t  tr_smask;       /* subnet mask for src addr */
75     nd_uint8_t  tr_rflags;      /* forwarding error codes */
76 };
77 
78 /* defs within mtrace */
79 #define TR_QUERY 1
80 #define TR_RESP 2
81 
82 /* fields for tr_rflags (forwarding error codes) */
83 #define TR_NO_ERR   0
84 #define TR_WRONG_IF 1
85 #define TR_PRUNED   2
86 #define TR_OPRUNED  3
87 #define TR_SCOPED   4
88 #define TR_NO_RTE   5
89 #define TR_NO_FWD   7
90 #define TR_NO_SPACE 0x81
91 #define TR_OLD_ROUTER   0x82
92 
93 /* fields for tr_rproto (routing protocol) */
94 #define TR_PROTO_DVMRP  1
95 #define TR_PROTO_MOSPF  2
96 #define TR_PROTO_PIM    3
97 #define TR_PROTO_CBT    4
98 
99 /* igmpv3 report types */
100 static const struct tok igmpv3report2str[] = {
101 	{ 1,	"is_in" },
102 	{ 2,	"is_ex" },
103 	{ 3,	"to_in" },
104 	{ 4,	"to_ex" },
105 	{ 5,	"allow" },
106 	{ 6,	"block" },
107 	{ 0,	NULL }
108 };
109 
110 static void
111 print_mtrace(netdissect_options *ndo,
112              const char *typename,
113              const u_char *bp, u_int len)
114 {
115     const struct tr_query *tr = (const struct tr_query *)(bp + 8);
116 
117     if (len < 8 + sizeof (struct tr_query)) {
118 	ND_PRINT(" [invalid len %u]", len);
119 	return;
120     }
121     ND_PRINT("%s %u: %s to %s reply-to %s",
122         typename,
123         GET_BE_U_3(tr->tr_qid),
124         GET_IPADDR_STRING(tr->tr_src), GET_IPADDR_STRING(tr->tr_dst),
125         GET_IPADDR_STRING(tr->tr_raddr));
126     if (IN_CLASSD(GET_BE_U_4(tr->tr_raddr)))
127         ND_PRINT(" with-ttl %u", GET_U_1(tr->tr_rttl));
128 }
129 
130 static void
131 print_igmpv3_report(netdissect_options *ndo,
132                     const u_char *bp, u_int len)
133 {
134     u_int group, nsrcs, ngroups;
135     u_int i, j;
136 
137     /* Minimum len is 16, and should be a multiple of 4 */
138     if (len < 16 || len & 0x03) {
139 	ND_PRINT(" [invalid len %u]", len);
140 	return;
141     }
142     ngroups = GET_BE_U_2(bp + 6);
143     ND_PRINT(", %u group record(s)", ngroups);
144     if (ndo->ndo_vflag > 0) {
145 	/* Print the group records */
146 	group = 8;
147         for (i=0; i<ngroups; i++) {
148 	    if (len < group+8) {
149 		ND_PRINT(" [invalid number of groups]");
150 		return;
151 	    }
152             ND_PRINT(" [gaddr %s", GET_IPADDR_STRING(bp + group + 4));
153 	    ND_PRINT(" %s", tok2str(igmpv3report2str, " [v3-report-#%u]",
154 								GET_U_1(bp + group)));
155             nsrcs = GET_BE_U_2(bp + group + 2);
156 	    /* Check the number of sources and print them */
157 	    if (len < group+8+(nsrcs<<2)) {
158 		ND_PRINT(" [invalid number of sources %u]", nsrcs);
159 		return;
160 	    }
161             if (ndo->ndo_vflag == 1)
162                 ND_PRINT(", %u source(s)", nsrcs);
163             else {
164 		/* Print the sources */
165                 ND_PRINT(" {");
166                 for (j=0; j<nsrcs; j++) {
167 		    ND_PRINT(" %s", GET_IPADDR_STRING(bp + group + 8 + (j << 2)));
168 		}
169                 ND_PRINT(" }");
170             }
171 	    /* Next group record */
172             group += 8 + (nsrcs << 2);
173 	    ND_PRINT("]");
174         }
175     }
176 }
177 
178 static void
179 print_igmpv3_query(netdissect_options *ndo,
180                    const u_char *bp, u_int len)
181 {
182     u_int mrc;
183     u_int mrt;
184     u_int nsrcs;
185     u_int i;
186 
187     ND_PRINT(" v3");
188     /* Minimum len is 12, and should be a multiple of 4 */
189     if (len < 12 || len & 0x03) {
190 	ND_PRINT(" [invalid len %u]", len);
191 	return;
192     }
193     mrc = GET_U_1(bp + 1);
194     if (mrc < 128) {
195 	mrt = mrc;
196     } else {
197         mrt = ((mrc & 0x0f) | 0x10) << (((mrc & 0x70) >> 4) + 3);
198     }
199     if (mrc != 100) {
200 	ND_PRINT(" [max resp time ");
201         if (mrt < 600) {
202             ND_PRINT("%.1fs", mrt * 0.1);
203         } else {
204             unsigned_relts_print(ndo, mrt / 10);
205         }
206 	ND_PRINT("]");
207     }
208     if (GET_BE_U_4(bp + 4) == 0)
209 	return;
210     ND_PRINT(" [gaddr %s", GET_IPADDR_STRING(bp + 4));
211     nsrcs = GET_BE_U_2(bp + 10);
212     if (nsrcs > 0) {
213 	if (len < 12 + (nsrcs << 2))
214 	    ND_PRINT(" [invalid number of sources]");
215 	else if (ndo->ndo_vflag > 1) {
216 	    ND_PRINT(" {");
217 	    for (i=0; i<nsrcs; i++) {
218 		ND_PRINT(" %s", GET_IPADDR_STRING(bp + 12 + (i << 2)));
219 	    }
220 	    ND_PRINT(" }");
221 	} else
222 	    ND_PRINT(", %u source(s)", nsrcs);
223     }
224     ND_PRINT("]");
225 }
226 
227 void
228 igmp_print(netdissect_options *ndo,
229            const u_char *bp, u_int len)
230 {
231     struct cksum_vec vec[1];
232 
233     ndo->ndo_protocol = "igmp";
234     if (ndo->ndo_qflag) {
235         ND_PRINT("igmp");
236         return;
237     }
238 
239     switch (GET_U_1(bp)) {
240     case 0x11:
241         ND_PRINT("igmp query");
242 	if (len >= 12)
243 	    print_igmpv3_query(ndo, bp, len);
244 	else {
245 	    if (GET_U_1(bp + 1)) {
246 		ND_PRINT(" v2");
247 		if (GET_U_1(bp + 1) != 100)
248 		    ND_PRINT(" [max resp time %u]", GET_U_1(bp + 1));
249 	    } else
250 		ND_PRINT(" v1");
251 	    if (GET_BE_U_4(bp + 4))
252                 ND_PRINT(" [gaddr %s]", GET_IPADDR_STRING(bp + 4));
253             if (len != 8)
254                 ND_PRINT(" [len %u]", len);
255 	}
256         break;
257     case 0x12:
258         ND_PRINT("igmp v1 report %s", GET_IPADDR_STRING(bp + 4));
259         if (len != 8)
260             ND_PRINT(" [len %u]", len);
261         break;
262     case 0x16:
263         ND_PRINT("igmp v2 report %s", GET_IPADDR_STRING(bp + 4));
264         break;
265     case 0x22:
266         ND_PRINT("igmp v3 report");
267 	print_igmpv3_report(ndo, bp, len);
268         break;
269     case 0x17:
270         ND_PRINT("igmp leave %s", GET_IPADDR_STRING(bp + 4));
271         break;
272     case 0x13:
273         ND_PRINT("igmp dvmrp");
274         if (len < 8)
275             ND_PRINT(" [len %u]", len);
276         else
277             dvmrp_print(ndo, bp, len);
278         break;
279     case 0x14:
280         ND_PRINT("igmp pimv1");
281         pimv1_print(ndo, bp, len);
282         break;
283     case 0x1e:
284         print_mtrace(ndo, "mresp", bp, len);
285         break;
286     case 0x1f:
287         print_mtrace(ndo, "mtrace", bp, len);
288         break;
289     default:
290         ND_PRINT("igmp-%u", GET_U_1(bp));
291         break;
292     }
293 
294     if (ndo->ndo_vflag && len >= 4 && ND_TTEST_LEN(bp, len)) {
295         /* Check the IGMP checksum */
296         vec[0].ptr = bp;
297         vec[0].len = len;
298         if (in_cksum(vec, 1))
299             ND_PRINT(" bad igmp cksum %x!", GET_BE_U_2(bp + 2));
300     }
301 }
302