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