1 /* $NetBSD: igmp.c,v 1.5 1995/12/10 10:07:01 mycroft 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(u_int type, u_int code); 32 static int igmp_log_level(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(void) 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)) == -1) 47 logit(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 = htons(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(u_int type, u_int code) 78 { 79 switch (type) { 80 case IGMP_HOST_MEMBERSHIP_QUERY: return "membership query "; 81 case IGMP_v1_HOST_MEMBERSHIP_REPORT: return "v1 member report "; 82 case IGMP_v2_HOST_MEMBERSHIP_REPORT: return "v2 member report "; 83 case IGMP_HOST_LEAVE_MESSAGE: return "leave message "; 84 case IGMP_DVMRP: 85 switch (code) { 86 case DVMRP_PROBE: return "neighbor probe "; 87 case DVMRP_REPORT: return "route report "; 88 case DVMRP_ASK_NEIGHBORS: return "neighbor request "; 89 case DVMRP_NEIGHBORS: return "neighbor list "; 90 case DVMRP_ASK_NEIGHBORS2: return "neighbor request 2"; 91 case DVMRP_NEIGHBORS2: return "neighbor list 2 "; 92 case DVMRP_PRUNE: return "prune message "; 93 case DVMRP_GRAFT: return "graft message "; 94 case DVMRP_GRAFT_ACK: return "graft message ack "; 95 case DVMRP_INFO_REQUEST: return "info request "; 96 case DVMRP_INFO_REPLY: return "info reply "; 97 default: return "unknown DVMRP msg "; 98 } 99 case IGMP_PIM: 100 switch (code) { 101 case PIM_QUERY: return "PIM Router-Query "; 102 case PIM_REGISTER: return "PIM Register "; 103 case PIM_REGISTER_STOP: return "PIM Register-Stop "; 104 case PIM_JOIN_PRUNE: return "PIM Join/Prune "; 105 case PIM_RP_REACHABLE: return "PIM RP-Reachable "; 106 case PIM_ASSERT: return "PIM Assert "; 107 case PIM_GRAFT: return "PIM Graft "; 108 case PIM_GRAFT_ACK: return "PIM Graft-Ack "; 109 default: return "unknown PIM msg "; 110 } 111 case IGMP_MTRACE_QUERY: return "IGMP trace query "; 112 case IGMP_MTRACE_REPLY: return "IGMP trace reply "; 113 default: return "unknown IGMP msg "; 114 } 115 } 116 117 /* 118 * Process a newly received IGMP packet that is sitting in the input 119 * packet buffer. 120 */ 121 void 122 accept_igmp(int recvlen) 123 { 124 u_int32_t src, dst, group; 125 struct ip *ip; 126 struct igmp *igmp; 127 int ipdatalen, iphdrlen, igmpdatalen; 128 129 if (recvlen < sizeof(struct ip)) { 130 logit(LOG_WARNING, 0, 131 "received packet too short (%u bytes) for IP header", recvlen); 132 return; 133 } 134 135 ip = (struct ip *)recv_buf; 136 src = ip->ip_src.s_addr; 137 dst = ip->ip_dst.s_addr; 138 139 /* 140 * this is most likely a message from the kernel indicating that 141 * a new src grp pair message has arrived and so, it would be 142 * necessary to install a route into the kernel for this. 143 */ 144 if (ip->ip_p == 0) { 145 if (src == 0 || dst == 0) 146 logit(LOG_WARNING, 0, "kernel request not accurate"); 147 else 148 add_table_entry(src, dst); 149 return; 150 } 151 152 iphdrlen = ip->ip_hl << 2; 153 ipdatalen = ntohs(ip->ip_len) - iphdrlen; 154 if (iphdrlen + ipdatalen != recvlen) { 155 logit(LOG_WARNING, 0, 156 "received packet from %s shorter (%u bytes) than hdr+data length (%u+%u)", 157 inet_fmt(src, s1), recvlen, iphdrlen, ipdatalen); 158 return; 159 } 160 161 igmp = (struct igmp *)(recv_buf + iphdrlen); 162 group = igmp->igmp_group.s_addr; 163 igmpdatalen = ipdatalen - IGMP_MINLEN; 164 if (igmpdatalen < 0) { 165 logit(LOG_WARNING, 0, 166 "received IP data field too short (%u bytes) for IGMP, from %s", 167 ipdatalen, inet_fmt(src, s1)); 168 return; 169 } 170 171 logit(LOG_DEBUG, 0, "RECV %s from %-15s to %s", 172 packet_kind(igmp->igmp_type, igmp->igmp_code), 173 inet_fmt(src, s1), inet_fmt(dst, s2)); 174 175 switch (igmp->igmp_type) { 176 177 case IGMP_HOST_MEMBERSHIP_QUERY: 178 accept_membership_query(src, dst, group, igmp->igmp_code); 179 return; 180 181 case IGMP_v1_HOST_MEMBERSHIP_REPORT: 182 case IGMP_v2_HOST_MEMBERSHIP_REPORT: 183 accept_group_report(src, dst, group, igmp->igmp_type); 184 return; 185 186 case IGMP_HOST_LEAVE_MESSAGE: 187 accept_leave_message(src, dst, group); 188 return; 189 190 case IGMP_DVMRP: 191 group = ntohl(group); 192 193 switch (igmp->igmp_code) { 194 case DVMRP_PROBE: 195 accept_probe(src, dst, 196 (char *)(igmp+1), igmpdatalen, group); 197 return; 198 199 case DVMRP_REPORT: 200 accept_report(src, dst, 201 (char *)(igmp+1), igmpdatalen, group); 202 return; 203 204 case DVMRP_ASK_NEIGHBORS: 205 accept_neighbor_request(src, dst); 206 return; 207 208 case DVMRP_ASK_NEIGHBORS2: 209 accept_neighbor_request2(src, dst); 210 return; 211 212 case DVMRP_NEIGHBORS: 213 accept_neighbors(src, dst, (u_char *)(igmp+1), igmpdatalen, 214 group); 215 return; 216 217 case DVMRP_NEIGHBORS2: 218 accept_neighbors2(src, dst, (u_char *)(igmp+1), igmpdatalen, 219 group); 220 return; 221 222 case DVMRP_PRUNE: 223 accept_prune(src, dst, (char *)(igmp+1), igmpdatalen); 224 return; 225 226 case DVMRP_GRAFT: 227 accept_graft(src, dst, (char *)(igmp+1), igmpdatalen); 228 return; 229 230 case DVMRP_GRAFT_ACK: 231 accept_g_ack(src, dst, (char *)(igmp+1), igmpdatalen); 232 return; 233 234 case DVMRP_INFO_REQUEST: 235 accept_info_request(src, dst, (char *)(igmp+1), 236 igmpdatalen); 237 return; 238 239 case DVMRP_INFO_REPLY: 240 accept_info_reply(src, dst, (char *)(igmp+1), igmpdatalen); 241 return; 242 243 default: 244 logit(LOG_INFO, 0, 245 "ignoring unknown DVMRP message code %u from %s to %s", 246 igmp->igmp_code, inet_fmt(src, s1), 247 inet_fmt(dst, s2)); 248 return; 249 } 250 251 case IGMP_PIM: 252 return; 253 254 case IGMP_MTRACE_REPLY: 255 return; 256 257 case IGMP_MTRACE_QUERY: 258 accept_mtrace(src, dst, group, (char *)(igmp+1), 259 igmp->igmp_code, igmpdatalen); 260 return; 261 262 default: 263 logit(LOG_INFO, 0, 264 "ignoring unknown IGMP message type %x from %s to %s", 265 igmp->igmp_type, inet_fmt(src, s1), 266 inet_fmt(dst, s2)); 267 return; 268 } 269 } 270 271 /* 272 * Some IGMP messages are more important than others. This routine 273 * determines the logging level at which to log a send error (often 274 * "No route to host"). This is important when there is asymmetric 275 * reachability and someone is trying to, i.e., mrinfo me periodically. 276 */ 277 static int 278 igmp_log_level(u_int type, u_int code) 279 { 280 switch (type) { 281 case IGMP_MTRACE_REPLY: 282 return LOG_INFO; 283 284 case IGMP_DVMRP: 285 switch (code) { 286 case DVMRP_NEIGHBORS: 287 case DVMRP_NEIGHBORS2: 288 return LOG_INFO; 289 } 290 } 291 return LOG_WARNING; 292 } 293 294 /* 295 * Construct an IGMP message in the output packet buffer. The caller may 296 * have already placed data in that buffer, of length 'datalen'. Then send 297 * the message from the interface with IP address 'src' to destination 'dst'. 298 */ 299 void 300 send_igmp(u_int32_t src, u_int32_t dst, int type, int code, 301 u_int32_t group, int datalen) 302 { 303 struct sockaddr_in sdst; 304 struct ip *ip; 305 struct igmp *igmp; 306 int setloop = 0; 307 308 ip = (struct ip *)send_buf; 309 ip->ip_src.s_addr = src; 310 ip->ip_dst.s_addr = dst; 311 ip->ip_len = htons(MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen); 312 313 igmp = (struct igmp *)(send_buf + MIN_IP_HEADER_LEN); 314 igmp->igmp_type = type; 315 igmp->igmp_code = code; 316 igmp->igmp_group.s_addr = group; 317 igmp->igmp_cksum = 0; 318 igmp->igmp_cksum = inet_cksum((u_int16_t *)igmp, 319 IGMP_MINLEN + datalen); 320 321 if (IN_MULTICAST(ntohl(dst))) { 322 k_set_if(src); 323 if (type != IGMP_DVMRP) { 324 setloop = 1; 325 k_set_loop(TRUE); 326 } 327 } 328 329 bzero(&sdst, sizeof(sdst)); 330 sdst.sin_family = AF_INET; 331 sdst.sin_len = sizeof(sdst); 332 sdst.sin_addr.s_addr = dst; 333 if (sendto(igmp_socket, send_buf, ntohs(ip->ip_len), 0, 334 (struct sockaddr *)&sdst, sizeof(sdst)) == -1) { 335 if (errno == ENETDOWN) 336 check_vif_state(); 337 else 338 logit(igmp_log_level(type, code), errno, 339 "sendto to %s on %s", 340 inet_fmt(dst, s1), inet_fmt(src, s2)); 341 } 342 343 if (setloop) 344 k_set_loop(FALSE); 345 346 logit(LOG_DEBUG, 0, "SENT %s from %-15s to %s", 347 packet_kind(type, code), inet_fmt(src, s1), inet_fmt(dst, s2)); 348 } 349