Annotation of embedaddon/mrouted/igmp.c, revision 1.1
1.1 ! misho 1: /*
! 2: * The mrouted program is covered by the license in the accompanying file
! 3: * named "LICENSE". Use of the mrouted program represents acceptance of
! 4: * the terms and conditions listed in that file.
! 5: *
! 6: * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
! 7: * Leland Stanford Junior University.
! 8: */
! 9:
! 10: #include "defs.h"
! 11:
! 12: /*
! 13: * Exported variables.
! 14: */
! 15: char *recv_buf; /* input packet buffer */
! 16: char *send_buf; /* output packet buffer */
! 17: int igmp_socket; /* socket for all network I/O */
! 18: u_int32 allhosts_group; /* All hosts addr in net order */
! 19: u_int32 allrtrs_group; /* All-Routers " in net order */
! 20: u_int32 dvmrp_group; /* DVMRP grp addr in net order */
! 21: u_int32 dvmrp_genid; /* IGMP generation id */
! 22:
! 23: /*
! 24: * Local function definitions.
! 25: */
! 26: /* u_char promoted to u_int */
! 27: static int igmp_log_level(u_int type, u_int code);
! 28:
! 29: /*
! 30: * Open and initialize the igmp socket, and fill in the non-changing
! 31: * IP header fields in the output packet buffer.
! 32: */
! 33: void init_igmp(void)
! 34: {
! 35: struct ip *ip;
! 36:
! 37: recv_buf = malloc(RECV_BUF_SIZE);
! 38: send_buf = malloc(RECV_BUF_SIZE);
! 39:
! 40: if ((igmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP)) < 0)
! 41: logit(LOG_ERR, errno, "IGMP socket");
! 42:
! 43: k_hdr_include(TRUE); /* include IP header when sending */
! 44: k_set_rcvbuf(256*1024,48*1024); /* lots of input buffering */
! 45: k_set_ttl(1); /* restrict multicasts to one hop */
! 46: k_set_loop(FALSE); /* disable multicast loopback */
! 47:
! 48: ip = (struct ip *)send_buf;
! 49: memset(ip, 0, sizeof(struct ip));
! 50: /*
! 51: * Fields zeroed that aren't filled in later:
! 52: * - IP ID (let the kernel fill it in)
! 53: * - Offset (we don't send fragments)
! 54: * - Checksum (let the kernel fill it in)
! 55: */
! 56: ip->ip_v = IPVERSION;
! 57: ip->ip_hl = sizeof(struct ip) >> 2;
! 58: ip->ip_tos = 0xc0; /* Internet Control */
! 59: ip->ip_ttl = MAXTTL; /* applies to unicasts only */
! 60: ip->ip_p = IPPROTO_IGMP;
! 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: char *igmp_packet_kind(u_int type, u_int code)
! 77: {
! 78: static char unknown[20];
! 79:
! 80: switch (type) {
! 81: case IGMP_MEMBERSHIP_QUERY: return "membership query ";
! 82: case IGMP_V1_MEMBERSHIP_REPORT: return "V1 member report ";
! 83: case IGMP_V2_MEMBERSHIP_REPORT: return "V2 member report ";
! 84: case IGMP_V2_LEAVE_GROUP: 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:
! 99: snprintf(unknown, sizeof(unknown), "unknown DVMRP %3d ", code);
! 100: return unknown;
! 101: }
! 102: case IGMP_PIM:
! 103: switch (code) {
! 104: case PIM_QUERY: return "PIM Router-Query ";
! 105: case PIM_REGISTER: return "PIM Register ";
! 106: case PIM_REGISTER_STOP: return "PIM Register-Stop ";
! 107: case PIM_JOIN_PRUNE: return "PIM Join/Prune ";
! 108: case PIM_RP_REACHABLE: return "PIM RP-Reachable ";
! 109: case PIM_ASSERT: return "PIM Assert ";
! 110: case PIM_GRAFT: return "PIM Graft ";
! 111: case PIM_GRAFT_ACK: return "PIM Graft-Ack ";
! 112: default:
! 113: snprintf(unknown, sizeof(unknown), "unknown PIM msg%3d", code);
! 114: return unknown;
! 115: }
! 116: case IGMP_MTRACE: return "IGMP trace query ";
! 117: case IGMP_MTRACE_RESP: return "IGMP trace reply ";
! 118: default:
! 119: snprintf(unknown, sizeof(unknown), "unk: 0x%02x/0x%02x ", type, code);
! 120: return unknown;
! 121: }
! 122: }
! 123:
! 124: int igmp_debug_kind(u_int type, u_int code)
! 125: {
! 126: switch (type) {
! 127: case IGMP_MEMBERSHIP_QUERY: return DEBUG_IGMP;
! 128: case IGMP_V1_MEMBERSHIP_REPORT: return DEBUG_IGMP;
! 129: case IGMP_V2_MEMBERSHIP_REPORT: return DEBUG_IGMP;
! 130: case IGMP_V2_LEAVE_GROUP: return DEBUG_IGMP;
! 131: case IGMP_DVMRP:
! 132: switch (code) {
! 133: case DVMRP_PROBE: return DEBUG_PEER;
! 134: case DVMRP_REPORT: return DEBUG_ROUTE;
! 135: case DVMRP_ASK_NEIGHBORS: return 0;
! 136: case DVMRP_NEIGHBORS: return 0;
! 137: case DVMRP_ASK_NEIGHBORS2: return 0;
! 138: case DVMRP_NEIGHBORS2: return 0;
! 139: case DVMRP_PRUNE: return DEBUG_PRUNE;
! 140: case DVMRP_GRAFT: return DEBUG_PRUNE;
! 141: case DVMRP_GRAFT_ACK: return DEBUG_PRUNE;
! 142: case DVMRP_INFO_REQUEST: return 0;
! 143: case DVMRP_INFO_REPLY: return 0;
! 144: default: return 0;
! 145: }
! 146: case IGMP_PIM:
! 147: switch (code) {
! 148: case PIM_QUERY: return 0;
! 149: case PIM_REGISTER: return 0;
! 150: case PIM_REGISTER_STOP: return 0;
! 151: case PIM_JOIN_PRUNE: return 0;
! 152: case PIM_RP_REACHABLE: return 0;
! 153: case PIM_ASSERT: return 0;
! 154: case PIM_GRAFT: return 0;
! 155: case PIM_GRAFT_ACK: return 0;
! 156: default: return 0;
! 157: }
! 158: case IGMP_MTRACE: return DEBUG_TRACE;
! 159: case IGMP_MTRACE_RESP: return DEBUG_TRACE;
! 160: default: return DEBUG_IGMP;
! 161: }
! 162: }
! 163:
! 164: /*
! 165: * Process a newly received IGMP packet that is sitting in the input
! 166: * packet buffer.
! 167: */
! 168: void accept_igmp(size_t recvlen)
! 169: {
! 170: register u_int32 src, dst, group;
! 171: struct ip *ip;
! 172: struct igmp *igmp;
! 173: int ipdatalen, iphdrlen, igmpdatalen;
! 174:
! 175: if (recvlen < sizeof(struct ip)) {
! 176: logit(LOG_WARNING, 0,
! 177: "received packet too short (%u bytes) for IP header", recvlen);
! 178: return;
! 179: }
! 180:
! 181: ip = (struct ip *)recv_buf;
! 182: src = ip->ip_src.s_addr;
! 183: dst = ip->ip_dst.s_addr;
! 184:
! 185: /*
! 186: * this is most likely a message from the kernel indicating that
! 187: * a new src grp pair message has arrived and so, it would be
! 188: * necessary to install a route into the kernel for this.
! 189: */
! 190: if (ip->ip_p == 0) {
! 191: if (src == 0 || dst == 0)
! 192: logit(LOG_WARNING, 0, "kernel request not accurate");
! 193: else
! 194: add_table_entry(src, dst);
! 195: return;
! 196: }
! 197:
! 198: iphdrlen = ip->ip_hl << 2;
! 199: ipdatalen = ntohs(ip->ip_len) - iphdrlen;
! 200: if ((size_t)(iphdrlen + ipdatalen) != recvlen) {
! 201: logit(LOG_WARNING, 0,
! 202: "received packet from %s shorter (%u bytes) than hdr+data length (%u+%u)",
! 203: inet_fmt(src, s1, sizeof(s1)), recvlen, iphdrlen, ipdatalen);
! 204: return;
! 205: }
! 206:
! 207: igmp = (struct igmp *)(recv_buf + iphdrlen);
! 208: group = igmp->igmp_group.s_addr;
! 209: igmpdatalen = ipdatalen - IGMP_MINLEN;
! 210: if (igmpdatalen < 0) {
! 211: logit(LOG_WARNING, 0,
! 212: "received IP data field too short (%u bytes) for IGMP, from %s",
! 213: ipdatalen, inet_fmt(src, s1, sizeof(s1)));
! 214: return;
! 215: }
! 216:
! 217: IF_DEBUG(DEBUG_PKT|igmp_debug_kind(igmp->igmp_type, igmp->igmp_code))
! 218: logit(LOG_DEBUG, 0, "RECV %s from %-15s to %s",
! 219: igmp_packet_kind(igmp->igmp_type, igmp->igmp_code),
! 220: inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2)));
! 221:
! 222: switch (igmp->igmp_type) {
! 223:
! 224: case IGMP_MEMBERSHIP_QUERY:
! 225: accept_membership_query(src, dst, group, igmp->igmp_code);
! 226: return;
! 227:
! 228: case IGMP_V1_MEMBERSHIP_REPORT:
! 229: case IGMP_V2_MEMBERSHIP_REPORT:
! 230: accept_group_report(src, dst, group, igmp->igmp_type);
! 231: return;
! 232:
! 233: case IGMP_V2_LEAVE_GROUP:
! 234: accept_leave_message(src, dst, group);
! 235: return;
! 236:
! 237: case IGMP_DVMRP:
! 238: group = ntohl(group);
! 239:
! 240: switch (igmp->igmp_code) {
! 241: case DVMRP_PROBE:
! 242: accept_probe(src, dst, (char *)(igmp+1), igmpdatalen, group);
! 243: return;
! 244:
! 245: case DVMRP_REPORT:
! 246: accept_report(src, dst, (char *)(igmp+1), igmpdatalen, group);
! 247: return;
! 248:
! 249: case DVMRP_ASK_NEIGHBORS:
! 250: accept_neighbor_request(src, dst);
! 251: return;
! 252:
! 253: case DVMRP_ASK_NEIGHBORS2:
! 254: accept_neighbor_request2(src, dst);
! 255: return;
! 256:
! 257: case DVMRP_NEIGHBORS:
! 258: accept_neighbors(src, dst, (u_char *)(igmp+1), igmpdatalen, group);
! 259: return;
! 260:
! 261: case DVMRP_NEIGHBORS2:
! 262: accept_neighbors2(src, dst, (u_char *)(igmp+1), igmpdatalen, group);
! 263: return;
! 264:
! 265: case DVMRP_PRUNE:
! 266: accept_prune(src, dst, (char *)(igmp+1), igmpdatalen);
! 267: return;
! 268:
! 269: case DVMRP_GRAFT:
! 270: accept_graft(src, dst, (char *)(igmp+1), igmpdatalen);
! 271: return;
! 272:
! 273: case DVMRP_GRAFT_ACK:
! 274: accept_g_ack(src, dst, (char *)(igmp+1), igmpdatalen);
! 275: return;
! 276:
! 277: case DVMRP_INFO_REQUEST:
! 278: accept_info_request(src, dst, (u_char *)(igmp+1), igmpdatalen);
! 279: return;
! 280:
! 281: case DVMRP_INFO_REPLY:
! 282: accept_info_reply(src, dst, (u_char *)(igmp+1), igmpdatalen);
! 283: return;
! 284:
! 285: default:
! 286: logit(LOG_INFO, 0,
! 287: "ignoring unknown DVMRP message code %u from %s to %s",
! 288: igmp->igmp_code, inet_fmt(src, s1, sizeof(s1)),
! 289: inet_fmt(dst, s2, sizeof(s2)));
! 290: return;
! 291: }
! 292:
! 293: case IGMP_PIM:
! 294: return;
! 295:
! 296: case IGMP_MTRACE_RESP:
! 297: return;
! 298:
! 299: case IGMP_MTRACE:
! 300: accept_mtrace(src, dst, group, (char *)(igmp+1),
! 301: igmp->igmp_code, igmpdatalen);
! 302: return;
! 303:
! 304: default:
! 305: logit(LOG_INFO, 0,
! 306: "ignoring unknown IGMP message type %x from %s to %s",
! 307: igmp->igmp_type, inet_fmt(src, s1, sizeof(s1)),
! 308: inet_fmt(dst, s2, sizeof(s2)));
! 309: return;
! 310: }
! 311: }
! 312:
! 313: /*
! 314: * Some IGMP messages are more important than others. This routine
! 315: * determines the logging level at which to log a send error (often
! 316: * "No route to host"). This is important when there is asymmetric
! 317: * reachability and someone is trying to, i.e., mrinfo me periodically.
! 318: */
! 319: static int igmp_log_level(u_int type, u_int code)
! 320: {
! 321: switch (type) {
! 322: case IGMP_MTRACE_RESP:
! 323: return LOG_INFO;
! 324:
! 325: case IGMP_DVMRP:
! 326: switch (code) {
! 327: case DVMRP_NEIGHBORS:
! 328: case DVMRP_NEIGHBORS2:
! 329: return LOG_INFO;
! 330: }
! 331: }
! 332: return LOG_WARNING;
! 333: }
! 334:
! 335: /*
! 336: * Construct an IGMP message in the output packet buffer. The caller may
! 337: * have already placed data in that buffer, of length 'datalen'.
! 338: */
! 339: size_t build_igmp(u_int32 src, u_int32 dst, int type, int code, u_int32 group, int datalen)
! 340: {
! 341: struct ip *ip;
! 342: struct igmp *igmp;
! 343: extern int curttl;
! 344: size_t len = MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen;
! 345:
! 346: ip = (struct ip *)send_buf;
! 347: ip->ip_src.s_addr = src;
! 348: ip->ip_dst.s_addr = dst;
! 349: ip->ip_len = htons(len);
! 350: if (IN_MULTICAST(ntohl(dst))) {
! 351: ip->ip_ttl = curttl;
! 352: } else {
! 353: ip->ip_ttl = MAXTTL;
! 354: }
! 355:
! 356: igmp = (struct igmp *)(send_buf + MIN_IP_HEADER_LEN);
! 357: igmp->igmp_type = type;
! 358: igmp->igmp_code = code;
! 359: igmp->igmp_group.s_addr = group;
! 360: igmp->igmp_cksum = 0;
! 361: igmp->igmp_cksum = inet_cksum((u_int16_t *)igmp,
! 362: IGMP_MINLEN + datalen);
! 363:
! 364: return len;
! 365: }
! 366:
! 367: /*
! 368: * Call build_igmp() to build an IGMP message in the output packet buffer.
! 369: * Then send the message from the interface with IP address 'src' to
! 370: * destination 'dst'.
! 371: */
! 372: void send_igmp(u_int32 src, u_int32 dst, int type, int code, u_int32 group, int datalen)
! 373: {
! 374: struct sockaddr_in sdst;
! 375: int setloop = 0;
! 376: size_t len;
! 377:
! 378: len = build_igmp(src, dst, type, code, group, datalen);
! 379:
! 380: if (IN_MULTICAST(ntohl(dst))) {
! 381: k_set_if(src);
! 382: if (type != IGMP_DVMRP || dst == allhosts_group) {
! 383: setloop = 1;
! 384: k_set_loop(TRUE);
! 385: }
! 386: }
! 387:
! 388: memset(&sdst, 0, sizeof(sdst));
! 389: sdst.sin_family = AF_INET;
! 390: #ifdef HAVE_SA_LEN
! 391: sdst.sin_len = sizeof(sdst);
! 392: #endif
! 393: sdst.sin_addr.s_addr = dst;
! 394: if (sendto(igmp_socket, send_buf, len, 0, (struct sockaddr *)&sdst, sizeof(sdst)) < 0) {
! 395: if (errno == ENETDOWN)
! 396: check_vif_state();
! 397: else
! 398: logit(igmp_log_level(type, code), errno,
! 399: "sendto to %s on %s",
! 400: inet_fmt(dst, s1, sizeof(s1)), inet_fmt(src, s2, sizeof(s2)));
! 401: }
! 402:
! 403: if (setloop)
! 404: k_set_loop(FALSE);
! 405:
! 406: IF_DEBUG(DEBUG_PKT|igmp_debug_kind(type, code))
! 407: logit(LOG_DEBUG, 0, "SENT %s from %-15s to %s",
! 408: igmp_packet_kind(type, code), src == INADDR_ANY ? "INADDR_ANY" :
! 409: inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2)));
! 410: }
! 411:
! 412: /**
! 413: * Local Variables:
! 414: * version-control: t
! 415: * indent-tabs-mode: t
! 416: * c-file-style: "ellemtel"
! 417: * c-basic-offset: 4
! 418: * End:
! 419: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>