1 /* $NetBSD: igmp.c,v 1.14 2009/04/17 16:05:43 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 size_t send_buflen; /* output packet buffer */ 22 int igmp_socket; /* socket for all network I/O */ 23 u_int32_t allhosts_group; /* All hosts addr in net order */ 24 u_int32_t allrtrs_group; /* All-Routers " in net order */ 25 u_int32_t dvmrp_group; /* DVMRP grp addr in net order */ 26 u_int32_t dvmrp_genid; /* IGMP generation id */ 27 28 /* 29 * Local function definitions. 30 */ 31 /* u_char promoted to u_int */ 32 static const char * packet_kind(u_int type, u_int code); 33 static int igmp_log_level(u_int type, u_int code); 34 35 /* 36 * Open and initialize the igmp socket, and fill in the non-changing 37 * IP header fields in the output packet buffer. 38 */ 39 void 40 init_igmp(void) 41 { 42 struct ip *ip; 43 44 recv_buf = malloc(RECV_BUF_SIZE); 45 send_buf = malloc(RECV_BUF_SIZE); 46 send_buflen = RECV_BUF_SIZE; 47 48 if ((igmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP)) < 0) 49 logit(LOG_ERR, errno, "IGMP socket"); 50 51 k_hdr_include(TRUE); /* include IP header when sending */ 52 k_set_rcvbuf(48*1024); /* lots of input buffering */ 53 k_set_ttl(1); /* restrict multicasts to one hop */ 54 k_set_loop(FALSE); /* disable multicast loopback */ 55 56 ip = (struct ip *)send_buf; 57 ip->ip_hl = sizeof(struct ip) >> 2; 58 ip->ip_v = IPVERSION; 59 ip->ip_tos = 0; 60 ip->ip_off = 0; 61 ip->ip_p = IPPROTO_IGMP; 62 ip->ip_ttl = MAXTTL; /* applies to unicasts only */ 63 64 allhosts_group = htonl(INADDR_ALLHOSTS_GROUP); 65 dvmrp_group = htonl(INADDR_DVMRP_GROUP); 66 allrtrs_group = htonl(INADDR_ALLRTRS_GROUP); 67 } 68 69 #define PIM_QUERY 0 70 #define PIM_REGISTER 1 71 #define PIM_REGISTER_STOP 2 72 #define PIM_JOIN_PRUNE 3 73 #define PIM_RP_REACHABLE 4 74 #define PIM_ASSERT 5 75 #define PIM_GRAFT 6 76 #define PIM_GRAFT_ACK 7 77 78 static const char * 79 packet_kind(u_int type, u_int code) 80 { 81 switch (type) { 82 case IGMP_HOST_MEMBERSHIP_QUERY: return "membership query "; 83 case IGMP_v1_HOST_MEMBERSHIP_REPORT: return "v1 member report "; 84 case IGMP_v2_HOST_MEMBERSHIP_REPORT: return "v2 member report "; 85 case IGMP_HOST_LEAVE_MESSAGE: return "leave message "; 86 case IGMP_DVMRP: 87 switch (code) { 88 case DVMRP_PROBE: return "neighbor probe "; 89 case DVMRP_REPORT: return "route report "; 90 case DVMRP_ASK_NEIGHBORS: return "neighbor request "; 91 case DVMRP_NEIGHBORS: return "neighbor list "; 92 case DVMRP_ASK_NEIGHBORS2: return "neighbor request 2"; 93 case DVMRP_NEIGHBORS2: return "neighbor list 2 "; 94 case DVMRP_PRUNE: return "prune message "; 95 case DVMRP_GRAFT: return "graft message "; 96 case DVMRP_GRAFT_ACK: return "graft message ack "; 97 case DVMRP_INFO_REQUEST: return "info request "; 98 case DVMRP_INFO_REPLY: return "info reply "; 99 default: return "unknown DVMRP msg "; 100 } 101 case IGMP_PIM: 102 switch (code) { 103 case PIM_QUERY: return "PIM Router-Query "; 104 case PIM_REGISTER: return "PIM Register "; 105 case PIM_REGISTER_STOP: return "PIM Register-Stop "; 106 case PIM_JOIN_PRUNE: return "PIM Join/Prune "; 107 case PIM_RP_REACHABLE: return "PIM RP-Reachable "; 108 case PIM_ASSERT: return "PIM Assert "; 109 case PIM_GRAFT: return "PIM Graft "; 110 case PIM_GRAFT_ACK: return "PIM Graft-Ack "; 111 default: return "unknown PIM msg "; 112 } 113 case IGMP_MTRACE_QUERY: return "IGMP trace query "; 114 case IGMP_MTRACE_REPLY: return "IGMP trace reply "; 115 default: return "unknown IGMP msg "; 116 } 117 } 118 119 /* 120 * Process a newly received IGMP packet that is sitting in the input 121 * packet buffer. 122 */ 123 void 124 accept_igmp(int recvlen) 125 { 126 u_int32_t src, dst, group; 127 struct ip *ip; 128 struct igmp *igmp; 129 int ipdatalen, iphdrlen, igmpdatalen; 130 131 if (recvlen < (int)sizeof(struct ip)) { 132 logit(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 logit(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 logit(LOG_WARNING, 0, 158 "received packet from %s shorter (%u bytes) than hdr+data length (%u+%u)", 159 inet_fmt(src), 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 logit(LOG_WARNING, 0, 168 "received IP data field too short (%u bytes) for IGMP, from %s", 169 ipdatalen, inet_fmt(src)); 170 return; 171 } 172 173 logit(LOG_DEBUG, 0, "RECV %s from %-15s to %s", 174 packet_kind(igmp->igmp_type, igmp->igmp_code), 175 inet_fmt(src), inet_fmt(dst)); 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, (u_char *)(igmp+1), 238 igmpdatalen); 239 return; 240 241 case DVMRP_INFO_REPLY: 242 accept_info_reply(src, dst, (u_char *)(igmp+1), igmpdatalen); 243 return; 244 245 default: 246 logit(LOG_INFO, 0, 247 "ignoring unknown DVMRP message code %u from %s to %s", 248 igmp->igmp_code, inet_fmt(src), 249 inet_fmt(dst)); 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 logit(LOG_INFO, 0, 266 "ignoring unknown IGMP message type %x from %s to %s", 267 igmp->igmp_type, inet_fmt(src), 268 inet_fmt(dst)); 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(u_int type, u_int code) 281 { 282 switch (type) { 283 case IGMP_MTRACE_REPLY: 284 return LOG_INFO; 285 286 case IGMP_DVMRP: 287 switch (code) { 288 case DVMRP_NEIGHBORS: 289 case DVMRP_NEIGHBORS2: 290 return LOG_INFO; 291 } 292 } 293 return LOG_WARNING; 294 } 295 296 /* 297 * Construct an IGMP message in the output packet buffer. The caller may 298 * have already placed data in that buffer, of length 'datalen'. Then send 299 * the message from the interface with IP address 'src' to destination 'dst'. 300 */ 301 void 302 send_igmp(u_int32_t src, u_int32_t dst, int type, int code, u_int32_t group, 303 int datalen) 304 { 305 struct sockaddr_in sdst; 306 struct ip *ip; 307 struct igmp *igmp; 308 int setloop; 309 310 setloop = 0; 311 ip = (struct ip *)send_buf; 312 ip->ip_src.s_addr = src; 313 ip->ip_dst.s_addr = dst; 314 ip->ip_len = MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen; 315 316 igmp = (struct igmp *)(send_buf + MIN_IP_HEADER_LEN); 317 igmp->igmp_type = type; 318 igmp->igmp_code = code; 319 igmp->igmp_group.s_addr = group; 320 igmp->igmp_cksum = 0; 321 igmp->igmp_cksum = inet_cksum((u_int16_t *)igmp, 322 IGMP_MINLEN + datalen); 323 324 if (IN_MULTICAST(ntohl(dst))) { 325 k_set_if(src); 326 if (type != IGMP_DVMRP) { 327 setloop = 1; 328 k_set_loop(TRUE); 329 } 330 } 331 332 bzero(&sdst, sizeof(sdst)); 333 sdst.sin_family = AF_INET; 334 #if (defined(BSD) && (BSD >= 199103)) 335 sdst.sin_len = sizeof(sdst); 336 #endif 337 sdst.sin_addr.s_addr = dst; 338 if (sendto(igmp_socket, send_buf, ip->ip_len, 0, 339 (struct sockaddr *)&sdst, sizeof(sdst)) < 0) { 340 if (errno == ENETDOWN) 341 check_vif_state(); 342 else 343 logit(igmp_log_level(type, code), errno, 344 "sendto to %s on %s", 345 inet_fmt(dst), inet_fmt(src)); 346 } 347 348 if (setloop) 349 k_set_loop(FALSE); 350 351 logit(LOG_DEBUG, 0, "SENT %s from %-15s to %s", 352 packet_kind(type, code), inet_fmt(src), 353 inet_fmt(dst)); 354 } 355