print-igmp.c revision 276788
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#define NETDISSECT_REWORKED
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27#include <tcpdump-stdinc.h>
28
29#include "interface.h"
30#include "addrtoname.h"
31#include "extract.h"            /* must come after interface.h */
32
33#ifndef IN_CLASSD
34#define IN_CLASSD(i) (((int32_t)(i) & 0xf0000000) == 0xe0000000)
35#endif
36
37static const char tstr[] = "[|igmp]";
38
39/* (following from ipmulti/mrouted/prune.h) */
40
41/*
42 * The packet format for a traceroute request.
43 */
44struct tr_query {
45    uint32_t  tr_src;          /* traceroute source */
46    uint32_t  tr_dst;          /* traceroute destination */
47    uint32_t  tr_raddr;        /* traceroute response address */
48    uint32_t  tr_rttlqid;      /* response ttl and qid */
49};
50
51#define TR_GETTTL(x)        (int)(((x) >> 24) & 0xff)
52#define TR_GETQID(x)        ((x) & 0x00ffffff)
53
54/*
55 * Traceroute response format.  A traceroute response has a tr_query at the
56 * beginning, followed by one tr_resp for each hop taken.
57 */
58struct tr_resp {
59    uint32_t tr_qarr;          /* query arrival time */
60    uint32_t tr_inaddr;        /* incoming interface address */
61    uint32_t tr_outaddr;       /* outgoing interface address */
62    uint32_t tr_rmtaddr;       /* parent address in source tree */
63    uint32_t tr_vifin;         /* input packet count on interface */
64    uint32_t tr_vifout;        /* output packet count on interface */
65    uint32_t tr_pktcnt;        /* total incoming packets for src-grp */
66    uint8_t  tr_rproto;      /* routing proto deployed on router */
67    uint8_t  tr_fttl;        /* ttl required to forward on outvif */
68    uint8_t  tr_smask;       /* subnet mask for src addr */
69    uint8_t  tr_rflags;      /* forwarding error codes */
70};
71
72/* defs within mtrace */
73#define TR_QUERY 1
74#define TR_RESP 2
75
76/* fields for tr_rflags (forwarding error codes) */
77#define TR_NO_ERR   0
78#define TR_WRONG_IF 1
79#define TR_PRUNED   2
80#define TR_OPRUNED  3
81#define TR_SCOPED   4
82#define TR_NO_RTE   5
83#define TR_NO_FWD   7
84#define TR_NO_SPACE 0x81
85#define TR_OLD_ROUTER   0x82
86
87/* fields for tr_rproto (routing protocol) */
88#define TR_PROTO_DVMRP  1
89#define TR_PROTO_MOSPF  2
90#define TR_PROTO_PIM    3
91#define TR_PROTO_CBT    4
92
93/* igmpv3 report types */
94static const struct tok igmpv3report2str[] = {
95	{ 1,	"is_in" },
96	{ 2,	"is_ex" },
97	{ 3,	"to_in" },
98	{ 4,	"to_ex" },
99	{ 5,	"allow" },
100	{ 6,	"block" },
101	{ 0,	NULL }
102};
103
104static void
105print_mtrace(netdissect_options *ndo,
106             register const u_char *bp, register u_int len)
107{
108    register const struct tr_query *tr = (const struct tr_query *)(bp + 8);
109
110    ND_TCHECK(*tr);
111    if (len < 8 + sizeof (struct tr_query)) {
112	ND_PRINT((ndo, " [invalid len %d]", len));
113	return;
114    }
115    ND_PRINT((ndo, "mtrace %u: %s to %s reply-to %s",
116        TR_GETQID(EXTRACT_32BITS(&tr->tr_rttlqid)),
117        ipaddr_string(ndo, &tr->tr_src), ipaddr_string(ndo, &tr->tr_dst),
118        ipaddr_string(ndo, &tr->tr_raddr)));
119    if (IN_CLASSD(EXTRACT_32BITS(&tr->tr_raddr)))
120        ND_PRINT((ndo, " with-ttl %d", TR_GETTTL(EXTRACT_32BITS(&tr->tr_rttlqid))));
121    return;
122trunc:
123    ND_PRINT((ndo, "%s", tstr));
124}
125
126static void
127print_mresp(netdissect_options *ndo,
128            register const u_char *bp, register u_int len)
129{
130    register const struct tr_query *tr = (const struct tr_query *)(bp + 8);
131
132    ND_TCHECK(*tr);
133    if (len < 8 + sizeof (struct tr_query)) {
134	ND_PRINT((ndo, " [invalid len %d]", len));
135	return;
136    }
137    ND_PRINT((ndo, "mresp %lu: %s to %s reply-to %s",
138        (u_long)TR_GETQID(EXTRACT_32BITS(&tr->tr_rttlqid)),
139        ipaddr_string(ndo, &tr->tr_src), ipaddr_string(ndo, &tr->tr_dst),
140        ipaddr_string(ndo, &tr->tr_raddr)));
141    if (IN_CLASSD(EXTRACT_32BITS(&tr->tr_raddr)))
142        ND_PRINT((ndo, " with-ttl %d", TR_GETTTL(EXTRACT_32BITS(&tr->tr_rttlqid))));
143    return;
144trunc:
145    ND_PRINT((ndo, "%s", tstr));
146}
147
148static void
149print_igmpv3_report(netdissect_options *ndo,
150                    register const u_char *bp, register u_int len)
151{
152    u_int group, nsrcs, ngroups;
153    register u_int i, j;
154
155    /* Minimum len is 16, and should be a multiple of 4 */
156    if (len < 16 || len & 0x03) {
157	ND_PRINT((ndo, " [invalid len %d]", len));
158	return;
159    }
160    ND_TCHECK2(bp[6], 2);
161    ngroups = EXTRACT_16BITS(&bp[6]);
162    ND_PRINT((ndo, ", %d group record(s)", ngroups));
163    if (ndo->ndo_vflag > 0) {
164	/* Print the group records */
165	group = 8;
166        for (i=0; i<ngroups; i++) {
167	    if (len < group+8) {
168		ND_PRINT((ndo, " [invalid number of groups]"));
169		return;
170	    }
171	    ND_TCHECK2(bp[group+4], 4);
172            ND_PRINT((ndo, " [gaddr %s", ipaddr_string(ndo, &bp[group+4])));
173	    ND_PRINT((ndo, " %s", tok2str(igmpv3report2str, " [v3-report-#%d]",
174								bp[group])));
175            nsrcs = EXTRACT_16BITS(&bp[group+2]);
176	    /* Check the number of sources and print them */
177	    if (len < group+8+(nsrcs<<2)) {
178		ND_PRINT((ndo, " [invalid number of sources %d]", nsrcs));
179		return;
180	    }
181            if (ndo->ndo_vflag == 1)
182                ND_PRINT((ndo, ", %d source(s)", nsrcs));
183            else {
184		/* Print the sources */
185                ND_PRINT((ndo, " {"));
186                for (j=0; j<nsrcs; j++) {
187		    ND_TCHECK2(bp[group+8+(j<<2)], 4);
188		    ND_PRINT((ndo, " %s", ipaddr_string(ndo, &bp[group+8+(j<<2)])));
189		}
190                ND_PRINT((ndo, " }"));
191            }
192	    /* Next group record */
193            group += 8 + (nsrcs << 2);
194	    ND_PRINT((ndo, "]"));
195        }
196    }
197    return;
198trunc:
199    ND_PRINT((ndo, "%s", tstr));
200}
201
202static void
203print_igmpv3_query(netdissect_options *ndo,
204                   register const u_char *bp, register u_int len)
205{
206    u_int mrc;
207    int mrt;
208    u_int nsrcs;
209    register u_int i;
210
211    ND_PRINT((ndo, " v3"));
212    /* Minimum len is 12, and should be a multiple of 4 */
213    if (len < 12 || len & 0x03) {
214	ND_PRINT((ndo, " [invalid len %d]", len));
215	return;
216    }
217    ND_TCHECK(bp[1]);
218    mrc = bp[1];
219    if (mrc < 128) {
220	mrt = mrc;
221    } else {
222        mrt = ((mrc & 0x0f) | 0x10) << (((mrc & 0x70) >> 4) + 3);
223    }
224    if (mrc != 100) {
225	ND_PRINT((ndo, " [max resp time "));
226        if (mrt < 600) {
227            ND_PRINT((ndo, "%.1fs", mrt * 0.1));
228        } else {
229            relts_print(ndo, mrt / 10);
230        }
231	ND_PRINT((ndo, "]"));
232    }
233    ND_TCHECK2(bp[4], 4);
234    if (EXTRACT_32BITS(&bp[4]) == 0)
235	return;
236    ND_PRINT((ndo, " [gaddr %s", ipaddr_string(ndo, &bp[4])));
237    ND_TCHECK2(bp[10], 2);
238    nsrcs = EXTRACT_16BITS(&bp[10]);
239    if (nsrcs > 0) {
240	if (len < 12 + (nsrcs << 2))
241	    ND_PRINT((ndo, " [invalid number of sources]"));
242	else if (ndo->ndo_vflag > 1) {
243	    ND_PRINT((ndo, " {"));
244	    for (i=0; i<nsrcs; i++) {
245		ND_TCHECK2(bp[12+(i<<2)], 4);
246		ND_PRINT((ndo, " %s", ipaddr_string(ndo, &bp[12+(i<<2)])));
247	    }
248	    ND_PRINT((ndo, " }"));
249	} else
250	    ND_PRINT((ndo, ", %d source(s)", nsrcs));
251    }
252    ND_PRINT((ndo, "]"));
253    return;
254trunc:
255    ND_PRINT((ndo, "%s", tstr));
256}
257
258void
259igmp_print(netdissect_options *ndo,
260           register const u_char *bp, register u_int len)
261{
262    struct cksum_vec vec[1];
263
264    if (ndo->ndo_qflag) {
265        ND_PRINT((ndo, "igmp"));
266        return;
267    }
268
269    ND_TCHECK(bp[0]);
270    switch (bp[0]) {
271    case 0x11:
272        ND_PRINT((ndo, "igmp query"));
273	if (len >= 12)
274	    print_igmpv3_query(ndo, bp, len);
275	else {
276            ND_TCHECK(bp[1]);
277	    if (bp[1]) {
278		ND_PRINT((ndo, " v2"));
279		if (bp[1] != 100)
280		    ND_PRINT((ndo, " [max resp time %d]", bp[1]));
281	    } else
282		ND_PRINT((ndo, " v1"));
283            ND_TCHECK2(bp[4], 4);
284	    if (EXTRACT_32BITS(&bp[4]))
285                ND_PRINT((ndo, " [gaddr %s]", ipaddr_string(ndo, &bp[4])));
286            if (len != 8)
287                ND_PRINT((ndo, " [len %d]", len));
288	}
289        break;
290    case 0x12:
291        ND_TCHECK2(bp[4], 4);
292        ND_PRINT((ndo, "igmp v1 report %s", ipaddr_string(ndo, &bp[4])));
293        if (len != 8)
294            ND_PRINT((ndo, " [len %d]", len));
295        break;
296    case 0x16:
297        ND_TCHECK2(bp[4], 4);
298        ND_PRINT((ndo, "igmp v2 report %s", ipaddr_string(ndo, &bp[4])));
299        break;
300    case 0x22:
301        ND_PRINT((ndo, "igmp v3 report"));
302	print_igmpv3_report(ndo, bp, len);
303        break;
304    case 0x17:
305        ND_TCHECK2(bp[4], 4);
306        ND_PRINT((ndo, "igmp leave %s", ipaddr_string(ndo, &bp[4])));
307        break;
308    case 0x13:
309        ND_PRINT((ndo, "igmp dvmrp"));
310        if (len < 8)
311            ND_PRINT((ndo, " [len %d]", len));
312        else
313            dvmrp_print(ndo, bp, len);
314        break;
315    case 0x14:
316        ND_PRINT((ndo, "igmp pimv1"));
317        pimv1_print(ndo, bp, len);
318        break;
319    case 0x1e:
320        print_mresp(ndo, bp, len);
321        break;
322    case 0x1f:
323        print_mtrace(ndo, bp, len);
324        break;
325    default:
326        ND_PRINT((ndo, "igmp-%d", bp[0]));
327        break;
328    }
329
330    if (ndo->ndo_vflag && ND_TTEST2(bp[0], len)) {
331        /* Check the IGMP checksum */
332        vec[0].ptr = bp;
333        vec[0].len = len;
334        if (in_cksum(vec, 1))
335            ND_PRINT((ndo, " bad igmp cksum %x!", EXTRACT_16BITS(&bp[2])));
336    }
337    return;
338trunc:
339    ND_PRINT((ndo, "%s", tstr));
340}
341