xref: /netbsd-src/external/bsd/tcpdump/dist/print-igmp.c (revision 26ba0b503b498a5194a71ac319838b7f5497f3fe)
10f74e101Schristos /*
20f74e101Schristos  * Copyright (c) 1988, 1989, 1990, 1991, 1993, 1994, 1995, 1996
30f74e101Schristos  *  The Regents of the University of California.  All rights reserved.
40f74e101Schristos  *
50f74e101Schristos  * Redistribution and use in source and binary forms, with or without
60f74e101Schristos  * modification, are permitted provided that: (1) source code distributions
70f74e101Schristos  * retain the above copyright notice and this paragraph in its entirety, (2)
80f74e101Schristos  * distributions including binary code include the above copyright notice and
90f74e101Schristos  * this paragraph in its entirety in the documentation or other materials
100f74e101Schristos  * provided with the distribution, and (3) all advertising materials mentioning
110f74e101Schristos  * features or use of this software display the following acknowledgement:
120f74e101Schristos  * ``This product includes software developed by the University of California,
130f74e101Schristos  * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
140f74e101Schristos  * the University nor the names of its contributors may be used to endorse
150f74e101Schristos  * or promote products derived from this software without specific prior
160f74e101Schristos  * written permission.
170f74e101Schristos  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
180f74e101Schristos  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
190f74e101Schristos  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
200f74e101Schristos  */
210f74e101Schristos 
2211b3aaa1Schristos #include <sys/cdefs.h>
230f74e101Schristos #ifndef lint
24*26ba0b50Schristos __RCSID("$NetBSD: print-igmp.c,v 1.10 2024/09/02 16:15:31 christos Exp $");
250f74e101Schristos #endif
260f74e101Schristos 
27dc860a36Sspz /* \summary: Internet Group Management Protocol (IGMP) printer */
28dc860a36Sspz 
29c74ad251Schristos /*
30c74ad251Schristos  * specification:
31c74ad251Schristos  *
32c74ad251Schristos  *	RFC 2236 for IGMPv2
33c74ad251Schristos  *	RFC 3376 for IGMPv3
34c74ad251Schristos  *	draft-asaeda-mboned-mtrace-v2 for the mtrace message
35c74ad251Schristos  */
36c74ad251Schristos 
37c74ad251Schristos #include <config.h>
380f74e101Schristos 
39c74ad251Schristos #include "netdissect-stdinc.h"
400f74e101Schristos 
41fdccd7e4Schristos #include "netdissect.h"
420f74e101Schristos #include "addrtoname.h"
43fdccd7e4Schristos #include "extract.h"
440f74e101Schristos 
450f74e101Schristos #ifndef IN_CLASSD
460f74e101Schristos #define IN_CLASSD(i) (((int32_t)(i) & 0xf0000000) == 0xe0000000)
470f74e101Schristos #endif
480f74e101Schristos 
49b3a00663Schristos 
500f74e101Schristos /* (following from ipmulti/mrouted/prune.h) */
510f74e101Schristos 
520f74e101Schristos /*
530f74e101Schristos  * The packet format for a traceroute request.
540f74e101Schristos  */
550f74e101Schristos struct tr_query {
56c74ad251Schristos     nd_uint32_t  tr_src;        /* traceroute source */
57c74ad251Schristos     nd_uint32_t  tr_dst;        /* traceroute destination */
58c74ad251Schristos     nd_uint32_t  tr_raddr;      /* traceroute response address */
59c74ad251Schristos     nd_uint8_t   tr_rttl;       /* response ttl */
60c74ad251Schristos     nd_uint24_t  tr_qid;        /* qid */
610f74e101Schristos };
620f74e101Schristos 
630f74e101Schristos /*
640f74e101Schristos  * Traceroute response format.  A traceroute response has a tr_query at the
650f74e101Schristos  * beginning, followed by one tr_resp for each hop taken.
660f74e101Schristos  */
670f74e101Schristos struct tr_resp {
68c74ad251Schristos     nd_uint32_t tr_qarr;        /* query arrival time */
69c74ad251Schristos     nd_uint32_t tr_inaddr;      /* incoming interface address */
70c74ad251Schristos     nd_uint32_t tr_outaddr;     /* outgoing interface address */
71c74ad251Schristos     nd_uint32_t tr_rmtaddr;     /* parent address in source tree */
72c74ad251Schristos     nd_uint32_t tr_vifin;       /* input packet count on interface */
73c74ad251Schristos     nd_uint32_t tr_vifout;      /* output packet count on interface */
74c74ad251Schristos     nd_uint32_t tr_pktcnt;      /* total incoming packets for src-grp */
75c74ad251Schristos     nd_uint8_t  tr_rproto;      /* routing proto deployed on router */
76c74ad251Schristos     nd_uint8_t  tr_fttl;        /* ttl required to forward on outvif */
77c74ad251Schristos     nd_uint8_t  tr_smask;       /* subnet mask for src addr */
78c74ad251Schristos     nd_uint8_t  tr_rflags;      /* forwarding error codes */
790f74e101Schristos };
800f74e101Schristos 
810f74e101Schristos /* defs within mtrace */
820f74e101Schristos #define TR_QUERY 1
830f74e101Schristos #define TR_RESP 2
840f74e101Schristos 
850f74e101Schristos /* fields for tr_rflags (forwarding error codes) */
860f74e101Schristos #define TR_NO_ERR   0
870f74e101Schristos #define TR_WRONG_IF 1
880f74e101Schristos #define TR_PRUNED   2
890f74e101Schristos #define TR_OPRUNED  3
900f74e101Schristos #define TR_SCOPED   4
910f74e101Schristos #define TR_NO_RTE   5
920f74e101Schristos #define TR_NO_FWD   7
930f74e101Schristos #define TR_NO_SPACE 0x81
940f74e101Schristos #define TR_OLD_ROUTER   0x82
950f74e101Schristos 
960f74e101Schristos /* fields for tr_rproto (routing protocol) */
970f74e101Schristos #define TR_PROTO_DVMRP  1
980f74e101Schristos #define TR_PROTO_MOSPF  2
990f74e101Schristos #define TR_PROTO_PIM    3
1000f74e101Schristos #define TR_PROTO_CBT    4
1010f74e101Schristos 
1020f74e101Schristos /* igmpv3 report types */
103870189d2Schristos static const struct tok igmpv3report2str[] = {
1040f74e101Schristos 	{ 1,	"is_in" },
1050f74e101Schristos 	{ 2,	"is_ex" },
1060f74e101Schristos 	{ 3,	"to_in" },
1070f74e101Schristos 	{ 4,	"to_ex" },
1080f74e101Schristos 	{ 5,	"allow" },
1090f74e101Schristos 	{ 6,	"block" },
1100f74e101Schristos 	{ 0,	NULL }
1110f74e101Schristos };
1120f74e101Schristos 
113a8e08e94Skamil UNALIGNED_OK
1140f74e101Schristos static void
115b3a00663Schristos print_mtrace(netdissect_options *ndo,
116c74ad251Schristos              const char *typename,
117c74ad251Schristos              const u_char *bp, u_int len)
1180f74e101Schristos {
119c74ad251Schristos     const struct tr_query *tr = (const struct tr_query *)(bp + 8);
1200f74e101Schristos 
1210f74e101Schristos     if (len < 8 + sizeof (struct tr_query)) {
122c74ad251Schristos 	ND_PRINT(" [invalid len %u]", len);
1230f74e101Schristos 	return;
1240f74e101Schristos     }
125c74ad251Schristos     ND_PRINT("%s %u: %s to %s reply-to %s",
126c74ad251Schristos         typename,
127c74ad251Schristos         GET_BE_U_3(tr->tr_qid),
128c74ad251Schristos         GET_IPADDR_STRING(tr->tr_src), GET_IPADDR_STRING(tr->tr_dst),
129c74ad251Schristos         GET_IPADDR_STRING(tr->tr_raddr));
130c74ad251Schristos     if (IN_CLASSD(GET_BE_U_4(tr->tr_raddr)))
131c74ad251Schristos         ND_PRINT(" with-ttl %u", GET_U_1(tr->tr_rttl));
1320f74e101Schristos }
1330f74e101Schristos 
1340f74e101Schristos static void
135b3a00663Schristos print_igmpv3_report(netdissect_options *ndo,
136c74ad251Schristos                     const u_char *bp, u_int len)
1370f74e101Schristos {
1380f74e101Schristos     u_int group, nsrcs, ngroups;
139c74ad251Schristos     u_int i, j;
1400f74e101Schristos 
1410f74e101Schristos     /* Minimum len is 16, and should be a multiple of 4 */
1420f74e101Schristos     if (len < 16 || len & 0x03) {
143c74ad251Schristos 	ND_PRINT(" [invalid len %u]", len);
1440f74e101Schristos 	return;
1450f74e101Schristos     }
146c74ad251Schristos     ngroups = GET_BE_U_2(bp + 6);
147c74ad251Schristos     ND_PRINT(", %u group record(s)", ngroups);
148b3a00663Schristos     if (ndo->ndo_vflag > 0) {
1490f74e101Schristos 	/* Print the group records */
1500f74e101Schristos 	group = 8;
1510f74e101Schristos         for (i=0; i<ngroups; i++) {
1520f74e101Schristos 	    if (len < group+8) {
153c74ad251Schristos 		ND_PRINT(" [invalid number of groups]");
1540f74e101Schristos 		return;
1550f74e101Schristos 	    }
156c74ad251Schristos             ND_PRINT(" [gaddr %s", GET_IPADDR_STRING(bp + group + 4));
157c74ad251Schristos 	    ND_PRINT(" %s", tok2str(igmpv3report2str, " [v3-report-#%u]",
158c74ad251Schristos 								GET_U_1(bp + group)));
159c74ad251Schristos             nsrcs = GET_BE_U_2(bp + group + 2);
1600f74e101Schristos 	    /* Check the number of sources and print them */
1610f74e101Schristos 	    if (len < group+8+(nsrcs<<2)) {
162c74ad251Schristos 		ND_PRINT(" [invalid number of sources %u]", nsrcs);
1630f74e101Schristos 		return;
1640f74e101Schristos 	    }
165b3a00663Schristos             if (ndo->ndo_vflag == 1)
166c74ad251Schristos                 ND_PRINT(", %u source(s)", nsrcs);
1670f74e101Schristos             else {
1680f74e101Schristos 		/* Print the sources */
169c74ad251Schristos                 ND_PRINT(" {");
1700f74e101Schristos                 for (j=0; j<nsrcs; j++) {
171c74ad251Schristos 		    ND_PRINT(" %s", GET_IPADDR_STRING(bp + group + 8 + (j << 2)));
1720f74e101Schristos 		}
173c74ad251Schristos                 ND_PRINT(" }");
1740f74e101Schristos             }
1750f74e101Schristos 	    /* Next group record */
1760f74e101Schristos             group += 8 + (nsrcs << 2);
177c74ad251Schristos 	    ND_PRINT("]");
1780f74e101Schristos         }
1790f74e101Schristos     }
1800f74e101Schristos }
1810f74e101Schristos 
1820f74e101Schristos static void
183b3a00663Schristos print_igmpv3_query(netdissect_options *ndo,
184c74ad251Schristos                    const u_char *bp, u_int len)
1850f74e101Schristos {
1860f74e101Schristos     u_int mrc;
187dc860a36Sspz     u_int mrt;
1880f74e101Schristos     u_int nsrcs;
189c74ad251Schristos     u_int i;
1900f74e101Schristos 
191c74ad251Schristos     ND_PRINT(" v3");
1920f74e101Schristos     /* Minimum len is 12, and should be a multiple of 4 */
1930f74e101Schristos     if (len < 12 || len & 0x03) {
194c74ad251Schristos 	ND_PRINT(" [invalid len %u]", len);
1950f74e101Schristos 	return;
1960f74e101Schristos     }
197c74ad251Schristos     mrc = GET_U_1(bp + 1);
1980f74e101Schristos     if (mrc < 128) {
1990f74e101Schristos 	mrt = mrc;
2000f74e101Schristos     } else {
2010f74e101Schristos         mrt = ((mrc & 0x0f) | 0x10) << (((mrc & 0x70) >> 4) + 3);
2020f74e101Schristos     }
2030f74e101Schristos     if (mrc != 100) {
204c74ad251Schristos 	ND_PRINT(" [max resp time ");
2050e9868baSchristos         if (mrt < 600) {
206c74ad251Schristos             ND_PRINT("%.1fs", mrt * 0.1);
2070e9868baSchristos         } else {
208dc860a36Sspz             unsigned_relts_print(ndo, mrt / 10);
2090e9868baSchristos         }
210c74ad251Schristos 	ND_PRINT("]");
2110f74e101Schristos     }
212c74ad251Schristos     if (GET_BE_U_4(bp + 4) == 0)
2130f74e101Schristos 	return;
214c74ad251Schristos     ND_PRINT(" [gaddr %s", GET_IPADDR_STRING(bp + 4));
215c74ad251Schristos     nsrcs = GET_BE_U_2(bp + 10);
2160f74e101Schristos     if (nsrcs > 0) {
2170f74e101Schristos 	if (len < 12 + (nsrcs << 2))
218c74ad251Schristos 	    ND_PRINT(" [invalid number of sources]");
219b3a00663Schristos 	else if (ndo->ndo_vflag > 1) {
220c74ad251Schristos 	    ND_PRINT(" {");
2210f74e101Schristos 	    for (i=0; i<nsrcs; i++) {
222c74ad251Schristos 		ND_PRINT(" %s", GET_IPADDR_STRING(bp + 12 + (i << 2)));
2230f74e101Schristos 	    }
224c74ad251Schristos 	    ND_PRINT(" }");
2250f74e101Schristos 	} else
226c74ad251Schristos 	    ND_PRINT(", %u source(s)", nsrcs);
2270f74e101Schristos     }
228c74ad251Schristos     ND_PRINT("]");
2290f74e101Schristos }
2300f74e101Schristos 
2310f74e101Schristos void
232b3a00663Schristos igmp_print(netdissect_options *ndo,
233c74ad251Schristos            const u_char *bp, u_int len)
2340f74e101Schristos {
2350e9868baSchristos     struct cksum_vec vec[1];
2360e9868baSchristos 
237c74ad251Schristos     ndo->ndo_protocol = "igmp";
238b3a00663Schristos     if (ndo->ndo_qflag) {
239c74ad251Schristos         ND_PRINT("igmp");
2400f74e101Schristos         return;
2410f74e101Schristos     }
2420f74e101Schristos 
243c74ad251Schristos     switch (GET_U_1(bp)) {
2440f74e101Schristos     case 0x11:
245c74ad251Schristos         ND_PRINT("igmp query");
2460f74e101Schristos 	if (len >= 12)
247b3a00663Schristos 	    print_igmpv3_query(ndo, bp, len);
2480f74e101Schristos 	else {
249c74ad251Schristos 	    if (GET_U_1(bp + 1)) {
250c74ad251Schristos 		ND_PRINT(" v2");
251c74ad251Schristos 		if (GET_U_1(bp + 1) != 100)
252c74ad251Schristos 		    ND_PRINT(" [max resp time %u]", GET_U_1(bp + 1));
2530f74e101Schristos 	    } else
254c74ad251Schristos 		ND_PRINT(" v1");
255c74ad251Schristos 	    if (GET_BE_U_4(bp + 4))
256c74ad251Schristos                 ND_PRINT(" [gaddr %s]", GET_IPADDR_STRING(bp + 4));
2570f74e101Schristos             if (len != 8)
258c74ad251Schristos                 ND_PRINT(" [len %u]", len);
2590f74e101Schristos 	}
2600f74e101Schristos         break;
2610f74e101Schristos     case 0x12:
262c74ad251Schristos         ND_PRINT("igmp v1 report %s", GET_IPADDR_STRING(bp + 4));
2630f74e101Schristos         if (len != 8)
264c74ad251Schristos             ND_PRINT(" [len %u]", len);
2650f74e101Schristos         break;
2660f74e101Schristos     case 0x16:
267c74ad251Schristos         ND_PRINT("igmp v2 report %s", GET_IPADDR_STRING(bp + 4));
2680f74e101Schristos         break;
2690f74e101Schristos     case 0x22:
270c74ad251Schristos         ND_PRINT("igmp v3 report");
271b3a00663Schristos 	print_igmpv3_report(ndo, bp, len);
2720f74e101Schristos         break;
2730f74e101Schristos     case 0x17:
274c74ad251Schristos         ND_PRINT("igmp leave %s", GET_IPADDR_STRING(bp + 4));
2750f74e101Schristos         break;
2760f74e101Schristos     case 0x13:
277c74ad251Schristos         ND_PRINT("igmp dvmrp");
2780f74e101Schristos         if (len < 8)
279c74ad251Schristos             ND_PRINT(" [len %u]", len);
2800f74e101Schristos         else
281b3a00663Schristos             dvmrp_print(ndo, bp, len);
2820f74e101Schristos         break;
2830f74e101Schristos     case 0x14:
284c74ad251Schristos         ND_PRINT("igmp pimv1");
285b3a00663Schristos         pimv1_print(ndo, bp, len);
2860f74e101Schristos         break;
2870f74e101Schristos     case 0x1e:
288c74ad251Schristos         print_mtrace(ndo, "mresp", bp, len);
2890f74e101Schristos         break;
2900f74e101Schristos     case 0x1f:
291c74ad251Schristos         print_mtrace(ndo, "mtrace", bp, len);
2920f74e101Schristos         break;
2930f74e101Schristos     default:
294c74ad251Schristos         ND_PRINT("igmp-%u", GET_U_1(bp));
2950f74e101Schristos         break;
2960f74e101Schristos     }
2970f74e101Schristos 
298c74ad251Schristos     if (ndo->ndo_vflag && len >= 4 && ND_TTEST_LEN(bp, len)) {
2990f74e101Schristos         /* Check the IGMP checksum */
3000e9868baSchristos         vec[0].ptr = bp;
3010e9868baSchristos         vec[0].len = len;
3020e9868baSchristos         if (in_cksum(vec, 1))
303c74ad251Schristos             ND_PRINT(" bad igmp cksum %x!", GET_BE_U_2(bp + 2));
3040f74e101Schristos     }
3050f74e101Schristos }
306