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