1 /* $NetBSD: igmp.c,v 1.6 1997/10/17 10:38:10 lukem Exp $ */ 2 3 /* 4 * The mrouted program is covered by the license in the accompanying file 5 * named "LICENSE". Use of the mrouted program represents acceptance of 6 * the terms and conditions listed in that file. 7 * 8 * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of 9 * Leland Stanford Junior University. 10 */ 11 12 13 #include "defs.h" 14 15 16 /* 17 * Exported variables. 18 */ 19 char *recv_buf; /* input packet buffer */ 20 char *send_buf; /* output packet buffer */ 21 int igmp_socket; /* socket for all network I/O */ 22 u_int32_t allhosts_group; /* All hosts addr in net order */ 23 u_int32_t allrtrs_group; /* All-Routers " in net order */ 24 u_int32_t dvmrp_group; /* DVMRP grp addr in net order */ 25 u_int32_t dvmrp_genid; /* IGMP generation id */ 26 27 /* 28 * Local function definitions. 29 */ 30 /* u_char promoted to u_int */ 31 static char * packet_kind __P((u_int type, u_int code)); 32 static int igmp_log_level __P((u_int type, u_int code)); 33 34 /* 35 * Open and initialize the igmp socket, and fill in the non-changing 36 * IP header fields in the output packet buffer. 37 */ 38 void 39 init_igmp() 40 { 41 struct ip *ip; 42 43 recv_buf = malloc(RECV_BUF_SIZE); 44 send_buf = malloc(RECV_BUF_SIZE); 45 46 if ((igmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP)) < 0) 47 log(LOG_ERR, errno, "IGMP socket"); 48 49 k_hdr_include(TRUE); /* include IP header when sending */ 50 k_set_rcvbuf(48*1024); /* lots of input buffering */ 51 k_set_ttl(1); /* restrict multicasts to one hop */ 52 k_set_loop(FALSE); /* disable multicast loopback */ 53 54 ip = (struct ip *)send_buf; 55 ip->ip_hl = sizeof(struct ip) >> 2; 56 ip->ip_v = IPVERSION; 57 ip->ip_tos = 0; 58 ip->ip_off = 0; 59 ip->ip_p = IPPROTO_IGMP; 60 ip->ip_ttl = MAXTTL; /* applies to unicasts only */ 61 62 allhosts_group = htonl(INADDR_ALLHOSTS_GROUP); 63 dvmrp_group = htonl(INADDR_DVMRP_GROUP); 64 allrtrs_group = htonl(INADDR_ALLRTRS_GROUP); 65 } 66 67 #define PIM_QUERY 0 68 #define PIM_REGISTER 1 69 #define PIM_REGISTER_STOP 2 70 #define PIM_JOIN_PRUNE 3 71 #define PIM_RP_REACHABLE 4 72 #define PIM_ASSERT 5 73 #define PIM_GRAFT 6 74 #define PIM_GRAFT_ACK 7 75 76 static char * 77 packet_kind(type, code) 78 u_int type, code; 79 { 80 switch (type) { 81 case IGMP_HOST_MEMBERSHIP_QUERY: return "membership query "; 82 case IGMP_v1_HOST_MEMBERSHIP_REPORT: return "v1 member report "; 83 case IGMP_v2_HOST_MEMBERSHIP_REPORT: return "v2 member report "; 84 case IGMP_HOST_LEAVE_MESSAGE: return "leave message "; 85 case IGMP_DVMRP: 86 switch (code) { 87 case DVMRP_PROBE: return "neighbor probe "; 88 case DVMRP_REPORT: return "route report "; 89 case DVMRP_ASK_NEIGHBORS: return "neighbor request "; 90 case DVMRP_NEIGHBORS: return "neighbor list "; 91 case DVMRP_ASK_NEIGHBORS2: return "neighbor request 2"; 92 case DVMRP_NEIGHBORS2: return "neighbor list 2 "; 93 case DVMRP_PRUNE: return "prune message "; 94 case DVMRP_GRAFT: return "graft message "; 95 case DVMRP_GRAFT_ACK: return "graft message ack "; 96 case DVMRP_INFO_REQUEST: return "info request "; 97 case DVMRP_INFO_REPLY: return "info reply "; 98 default: return "unknown DVMRP msg "; 99 } 100 case IGMP_PIM: 101 switch (code) { 102 case PIM_QUERY: return "PIM Router-Query "; 103 case PIM_REGISTER: return "PIM Register "; 104 case PIM_REGISTER_STOP: return "PIM Register-Stop "; 105 case PIM_JOIN_PRUNE: return "PIM Join/Prune "; 106 case PIM_RP_REACHABLE: return "PIM RP-Reachable "; 107 case PIM_ASSERT: return "PIM Assert "; 108 case PIM_GRAFT: return "PIM Graft "; 109 case PIM_GRAFT_ACK: return "PIM Graft-Ack "; 110 default: return "unknown PIM msg "; 111 } 112 case IGMP_MTRACE_QUERY: return "IGMP trace query "; 113 case IGMP_MTRACE_REPLY: return "IGMP trace reply "; 114 default: return "unknown IGMP msg "; 115 } 116 } 117 118 /* 119 * Process a newly received IGMP packet that is sitting in the input 120 * packet buffer. 121 */ 122 void 123 accept_igmp(recvlen) 124 int recvlen; 125 { 126 register u_int32_t src, dst, group; 127 struct ip *ip; 128 struct igmp *igmp; 129 int ipdatalen, iphdrlen, igmpdatalen; 130 131 if (recvlen < sizeof(struct ip)) { 132 log(LOG_WARNING, 0, 133 "received packet too short (%u bytes) for IP header", recvlen); 134 return; 135 } 136 137 ip = (struct ip *)recv_buf; 138 src = ip->ip_src.s_addr; 139 dst = ip->ip_dst.s_addr; 140 141 /* 142 * this is most likely a message from the kernel indicating that 143 * a new src grp pair message has arrived and so, it would be 144 * necessary to install a route into the kernel for this. 145 */ 146 if (ip->ip_p == 0) { 147 if (src == 0 || dst == 0) 148 log(LOG_WARNING, 0, "kernel request not accurate"); 149 else 150 add_table_entry(src, dst); 151 return; 152 } 153 154 iphdrlen = ip->ip_hl << 2; 155 ipdatalen = ip->ip_len; 156 if (iphdrlen + ipdatalen != recvlen) { 157 log(LOG_WARNING, 0, 158 "received packet from %s shorter (%u bytes) than hdr+data length (%u+%u)", 159 inet_fmt(src, s1), recvlen, iphdrlen, ipdatalen); 160 return; 161 } 162 163 igmp = (struct igmp *)(recv_buf + iphdrlen); 164 group = igmp->igmp_group.s_addr; 165 igmpdatalen = ipdatalen - IGMP_MINLEN; 166 if (igmpdatalen < 0) { 167 log(LOG_WARNING, 0, 168 "received IP data field too short (%u bytes) for IGMP, from %s", 169 ipdatalen, inet_fmt(src, s1)); 170 return; 171 } 172 173 log(LOG_DEBUG, 0, "RECV %s from %-15s to %s", 174 packet_kind(igmp->igmp_type, igmp->igmp_code), 175 inet_fmt(src, s1), inet_fmt(dst, s2)); 176 177 switch (igmp->igmp_type) { 178 179 case IGMP_HOST_MEMBERSHIP_QUERY: 180 accept_membership_query(src, dst, group, igmp->igmp_code); 181 return; 182 183 case IGMP_v1_HOST_MEMBERSHIP_REPORT: 184 case IGMP_v2_HOST_MEMBERSHIP_REPORT: 185 accept_group_report(src, dst, group, igmp->igmp_type); 186 return; 187 188 case IGMP_HOST_LEAVE_MESSAGE: 189 accept_leave_message(src, dst, group); 190 return; 191 192 case IGMP_DVMRP: 193 group = ntohl(group); 194 195 switch (igmp->igmp_code) { 196 case DVMRP_PROBE: 197 accept_probe(src, dst, 198 (char *)(igmp+1), igmpdatalen, group); 199 return; 200 201 case DVMRP_REPORT: 202 accept_report(src, dst, 203 (char *)(igmp+1), igmpdatalen, group); 204 return; 205 206 case DVMRP_ASK_NEIGHBORS: 207 accept_neighbor_request(src, dst); 208 return; 209 210 case DVMRP_ASK_NEIGHBORS2: 211 accept_neighbor_request2(src, dst); 212 return; 213 214 case DVMRP_NEIGHBORS: 215 accept_neighbors(src, dst, (u_char *)(igmp+1), igmpdatalen, 216 group); 217 return; 218 219 case DVMRP_NEIGHBORS2: 220 accept_neighbors2(src, dst, (u_char *)(igmp+1), igmpdatalen, 221 group); 222 return; 223 224 case DVMRP_PRUNE: 225 accept_prune(src, dst, (char *)(igmp+1), igmpdatalen); 226 return; 227 228 case DVMRP_GRAFT: 229 accept_graft(src, dst, (char *)(igmp+1), igmpdatalen); 230 return; 231 232 case DVMRP_GRAFT_ACK: 233 accept_g_ack(src, dst, (char *)(igmp+1), igmpdatalen); 234 return; 235 236 case DVMRP_INFO_REQUEST: 237 accept_info_request(src, dst, (char *)(igmp+1), 238 igmpdatalen); 239 return; 240 241 case DVMRP_INFO_REPLY: 242 accept_info_reply(src, dst, (char *)(igmp+1), igmpdatalen); 243 return; 244 245 default: 246 log(LOG_INFO, 0, 247 "ignoring unknown DVMRP message code %u from %s to %s", 248 igmp->igmp_code, inet_fmt(src, s1), 249 inet_fmt(dst, s2)); 250 return; 251 } 252 253 case IGMP_PIM: 254 return; 255 256 case IGMP_MTRACE_REPLY: 257 return; 258 259 case IGMP_MTRACE_QUERY: 260 accept_mtrace(src, dst, group, (char *)(igmp+1), 261 igmp->igmp_code, igmpdatalen); 262 return; 263 264 default: 265 log(LOG_INFO, 0, 266 "ignoring unknown IGMP message type %x from %s to %s", 267 igmp->igmp_type, inet_fmt(src, s1), 268 inet_fmt(dst, s2)); 269 return; 270 } 271 } 272 273 /* 274 * Some IGMP messages are more important than others. This routine 275 * determines the logging level at which to log a send error (often 276 * "No route to host"). This is important when there is asymmetric 277 * reachability and someone is trying to, i.e., mrinfo me periodically. 278 */ 279 static int 280 igmp_log_level(type, code) 281 u_int type, code; 282 { 283 switch (type) { 284 case IGMP_MTRACE_REPLY: 285 return LOG_INFO; 286 287 case IGMP_DVMRP: 288 switch (code) { 289 case DVMRP_NEIGHBORS: 290 case DVMRP_NEIGHBORS2: 291 return LOG_INFO; 292 } 293 } 294 return LOG_WARNING; 295 } 296 297 /* 298 * Construct an IGMP message in the output packet buffer. The caller may 299 * have already placed data in that buffer, of length 'datalen'. Then send 300 * the message from the interface with IP address 'src' to destination 'dst'. 301 */ 302 void 303 send_igmp(src, dst, type, code, group, datalen) 304 u_int32_t src, dst; 305 int type, code; 306 u_int32_t group; 307 int datalen; 308 { 309 struct sockaddr_in sdst; 310 struct ip *ip; 311 struct igmp *igmp; 312 int setloop; 313 314 setloop = 0; 315 ip = (struct ip *)send_buf; 316 ip->ip_src.s_addr = src; 317 ip->ip_dst.s_addr = dst; 318 ip->ip_len = MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen; 319 320 igmp = (struct igmp *)(send_buf + MIN_IP_HEADER_LEN); 321 igmp->igmp_type = type; 322 igmp->igmp_code = code; 323 igmp->igmp_group.s_addr = group; 324 igmp->igmp_cksum = 0; 325 igmp->igmp_cksum = inet_cksum((u_short *)igmp, 326 IGMP_MINLEN + datalen); 327 328 if (IN_MULTICAST(ntohl(dst))) { 329 k_set_if(src); 330 if (type != IGMP_DVMRP) { 331 setloop = 1; 332 k_set_loop(TRUE); 333 } 334 } 335 336 bzero(&sdst, sizeof(sdst)); 337 sdst.sin_family = AF_INET; 338 #if (defined(BSD) && (BSD >= 199103)) 339 sdst.sin_len = sizeof(sdst); 340 #endif 341 sdst.sin_addr.s_addr = dst; 342 if (sendto(igmp_socket, send_buf, ip->ip_len, 0, 343 (struct sockaddr *)&sdst, sizeof(sdst)) < 0) { 344 if (errno == ENETDOWN) 345 check_vif_state(); 346 else 347 log(igmp_log_level(type, code), errno, 348 "sendto to %s on %s", 349 inet_fmt(dst, s1), inet_fmt(src, s2)); 350 } 351 352 if (setloop) 353 k_set_loop(FALSE); 354 355 log(LOG_DEBUG, 0, "SENT %s from %-15s to %s", 356 packet_kind(type, code), inet_fmt(src, s1), inet_fmt(dst, s2)); 357 } 358