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 */
52struct 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 */
64struct 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 */
100static 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
110static void
111print_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
130static void
131print_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
178static void
179print_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
227void
228igmp_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