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