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