diff options
Diffstat (limited to 'usr.sbin/mrouted/igmp.c')
-rw-r--r-- | usr.sbin/mrouted/igmp.c | 266 |
1 files changed, 143 insertions, 123 deletions
diff --git a/usr.sbin/mrouted/igmp.c b/usr.sbin/mrouted/igmp.c index 5feab13e0a418..203bd267fa8f8 100644 --- a/usr.sbin/mrouted/igmp.c +++ b/usr.sbin/mrouted/igmp.c @@ -5,14 +5,11 @@ * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. - * - * - * igmp.c,v 3.8.4.19 1998/01/06 01:57:43 fenner Exp */ #ifndef lint static const char rcsid[] = - "$Id: igmp.c,v 1.14 1998/01/16 07:17:41 charnier Exp $"; + "$Id$"; #endif /* not lint */ #include "defs.h" @@ -29,9 +26,27 @@ u_int32 dvmrp_group; /* DVMRP grp addr in net order */ u_int32 dvmrp_genid; /* IGMP generation id */ /* + * Private variables + */ +static char router_alert[4]; /* Router Alert IP Option */ +#ifndef IPOPT_RA +#define IPOPT_RA 148 +#endif +#ifdef SUNOS5 +static char no_op[4]; /* Null IP Option */ +static int ip_addlen = 0; /* Workaround for Option bug #2*/ +#endif +#define SEND_RA(x) (((x) == IGMP_MEMBERSHIP_QUERY) || \ + ((x) == IGMP_V1_MEMBERSHIP_REPORT) || \ + ((x) == IGMP_V2_MEMBERSHIP_REPORT) || \ + ((x) == IGMP_V2_LEAVE_GROUP) || \ + ((x) == IGMP_MTRACE)) + +/* * Local function definitions. */ /* u_char promoted to u_int */ +static char * packet_kind __P((u_int type, u_int code)); static int igmp_log_level __P((u_int type, u_int code)); /* @@ -42,6 +57,9 @@ void init_igmp() { struct ip *ip; +#ifdef SUNOS5 + u_int32 localhost = htonl(0x7f000001); +#endif recv_buf = malloc(RECV_BUF_SIZE); send_buf = malloc(RECV_BUF_SIZE); @@ -50,27 +68,82 @@ init_igmp() log(LOG_ERR, errno, "IGMP socket"); k_hdr_include(TRUE); /* include IP header when sending */ - k_set_rcvbuf(256*1024,48*1024); /* lots of input buffering */ + k_set_rcvbuf(48*1024); /* lots of input buffering */ k_set_ttl(1); /* restrict multicasts to one hop */ k_set_loop(FALSE); /* disable multicast loopback */ ip = (struct ip *)send_buf; - bzero(ip, sizeof(struct ip)); - /* - * Fields zeroed that aren't filled in later: - * - IP ID (let the kernel fill it in) - * - Offset (we don't send fragments) - * - Checksum (let the kernel fill it in) - */ - ip->ip_v = IPVERSION; ip->ip_hl = sizeof(struct ip) >> 2; - ip->ip_tos = 0xc0; /* Internet Control */ - ip->ip_ttl = MAXTTL; /* applies to unicasts only */ + ip->ip_v = IPVERSION; + ip->ip_tos = 0; + ip->ip_off = 0; ip->ip_p = IPPROTO_IGMP; + ip->ip_ttl = MAXTTL; /* applies to unicasts only */ allhosts_group = htonl(INADDR_ALLHOSTS_GROUP); dvmrp_group = htonl(INADDR_DVMRP_GROUP); allrtrs_group = htonl(INADDR_ALLRTRS_GROUP); + + router_alert[0] = IPOPT_RA; /* Router Alert */ + router_alert[1] = 4; /* 4 bytes */ + router_alert[2] = 0; + router_alert[3] = 0; + +#ifdef SUNOS5 + no_op[0] = IPOPT_NOP; + no_op[1] = IPOPT_NOP; + no_op[2] = IPOPT_NOP; + no_op[3] = IPOPT_NOP; + + setsockopt(igmp_socket, IPPROTO_IP, IP_OPTIONS, no_op, sizeof(no_op)); + /* + * Check if the kernel adds the options length to the packet + * length. Send myself an IGMP packet of type 0 (illegal), + * with 4 IPOPT_NOP options, my PID (for collision detection) + * and 4 bytes of zero (so that the checksum works whether + * the 4 bytes of zero get truncated or not). + */ + bzero(send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN, 8); + *(int *)(send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN) = getpid(); + send_igmp(localhost, localhost, 0, 0, 0, 8); + while (1) { + int recvlen, dummy = 0; + + recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE, + 0, NULL, &dummy); + /* 8 == 4 bytes of options and 4 bytes of PID */ + if (recvlen >= MIN_IP_HEADER_LEN + IGMP_MINLEN + 8) { + struct ip *ip = (struct ip *)recv_buf; + struct igmp *igmp; + int *p; + + if (ip->ip_hl != 6 || + ip->ip_p != IPPROTO_IGMP || + ip->ip_src.s_addr != localhost || + ip->ip_dst.s_addr != localhost) + continue; + + igmp = (struct igmp *)(recv_buf + (ip->ip_hl << 2)); + if (igmp->igmp_group.s_addr != 0) + continue; + if (igmp->igmp_type != 0 || igmp->igmp_code != 0) + continue; + + p = (int *)((char *)igmp + IGMP_MINLEN); + if (*p != getpid()) + continue; + + if (ip->ip_len == IGMP_MINLEN + 4) + ip_addlen = 4; + else if (ip->ip_len == IGMP_MINLEN + 8) + ip_addlen = 0; + else + log(LOG_ERR, 0, "while checking for Solaris bug: Sent %d bytes and got back %d!", IGMP_MINLEN + 8, ip->ip_len); + + break; + } + } +#endif } #define PIM_QUERY 0 @@ -82,33 +155,29 @@ init_igmp() #define PIM_GRAFT 6 #define PIM_GRAFT_ACK 7 -char * -igmp_packet_kind(type, code) +static char * +packet_kind(type, code) u_int type, code; { - static char unknown[20]; - switch (type) { - case IGMP_MEMBERSHIP_QUERY: return "membership query "; - case IGMP_V1_MEMBERSHIP_REPORT: return "V1 member report "; - case IGMP_V2_MEMBERSHIP_REPORT: return "V2 member report "; - case IGMP_V2_LEAVE_GROUP: return "leave message "; + case IGMP_HOST_MEMBERSHIP_QUERY: return "membership query "; + case IGMP_HOST_MEMBERSHIP_REPORT: return "V1 member report "; + case IGMP_HOST_NEW_MEMBERSHIP_REPORT: return "V2 member report "; + case IGMP_HOST_LEAVE_MESSAGE: return "leave message "; case IGMP_DVMRP: switch (code) { - case DVMRP_PROBE: return "neighbor probe "; - case DVMRP_REPORT: return "route report "; - case DVMRP_ASK_NEIGHBORS: return "neighbor request "; - case DVMRP_NEIGHBORS: return "neighbor list "; - case DVMRP_ASK_NEIGHBORS2: return "neighbor request 2"; - case DVMRP_NEIGHBORS2: return "neighbor list 2 "; + case DVMRP_PROBE: return "neighbor probe "; + case DVMRP_REPORT: return "route report "; + case DVMRP_ASK_NEIGHBORS: return "neighbor request "; + case DVMRP_NEIGHBORS: return "neighbor list "; + case DVMRP_ASK_NEIGHBORS2: return "neighbor request 2"; + case DVMRP_NEIGHBORS2: return "neighbor list 2 "; case DVMRP_PRUNE: return "prune message "; case DVMRP_GRAFT: return "graft message "; case DVMRP_GRAFT_ACK: return "graft message ack "; case DVMRP_INFO_REQUEST: return "info request "; case DVMRP_INFO_REPLY: return "info reply "; - default: - sprintf(unknown, "unknown DVMRP %3d ", code); - return unknown; + default: return "unknown DVMRP msg "; } case IGMP_PIM: switch (code) { @@ -120,57 +189,11 @@ igmp_packet_kind(type, code) case PIM_ASSERT: return "PIM Assert "; case PIM_GRAFT: return "PIM Graft "; case PIM_GRAFT_ACK: return "PIM Graft-Ack "; - default: - sprintf(unknown, "unknown PIM msg%3d", code); - return unknown; + default: return "unknown PIM msg "; } case IGMP_MTRACE: return "IGMP trace query "; case IGMP_MTRACE_RESP: return "IGMP trace reply "; - default: - sprintf(unknown, "unk: 0x%02x/0x%02x ", type, code); - return unknown; - } -} - -int -igmp_debug_kind(type, code) - u_int type, code; -{ - switch (type) { - case IGMP_MEMBERSHIP_QUERY: return DEBUG_IGMP; - case IGMP_V1_MEMBERSHIP_REPORT: return DEBUG_IGMP; - case IGMP_V2_MEMBERSHIP_REPORT: return DEBUG_IGMP; - case IGMP_V2_LEAVE_GROUP: return DEBUG_IGMP; - case IGMP_DVMRP: - switch (code) { - case DVMRP_PROBE: return DEBUG_PEER; - case DVMRP_REPORT: return DEBUG_ROUTE; - case DVMRP_ASK_NEIGHBORS: return 0; - case DVMRP_NEIGHBORS: return 0; - case DVMRP_ASK_NEIGHBORS2: return 0; - case DVMRP_NEIGHBORS2: return 0; - case DVMRP_PRUNE: return DEBUG_PRUNE; - case DVMRP_GRAFT: return DEBUG_PRUNE; - case DVMRP_GRAFT_ACK: return DEBUG_PRUNE; - case DVMRP_INFO_REQUEST: return 0; - case DVMRP_INFO_REPLY: return 0; - default: return 0; - } - case IGMP_PIM: - switch (code) { - case PIM_QUERY: return 0; - case PIM_REGISTER: return 0; - case PIM_REGISTER_STOP: return 0; - case PIM_JOIN_PRUNE: return 0; - case PIM_RP_REACHABLE: return 0; - case PIM_ASSERT: return 0; - case PIM_GRAFT: return 0; - case PIM_GRAFT_ACK: return 0; - default: return 0; - } - case IGMP_MTRACE: return DEBUG_TRACE; - case IGMP_MTRACE_RESP: return DEBUG_TRACE; - default: return DEBUG_IGMP; + default: return "unknown IGMP msg "; } } @@ -211,11 +234,7 @@ accept_igmp(recvlen) } iphdrlen = ip->ip_hl << 2; -#ifdef RAW_INPUT_IS_RAW - ipdatalen = ntohs(ip->ip_len) - iphdrlen; -#else ipdatalen = ip->ip_len; -#endif if (iphdrlen + ipdatalen != recvlen) { log(LOG_WARNING, 0, "received packet from %s shorter (%u bytes) than hdr+data length (%u+%u)", @@ -233,23 +252,22 @@ accept_igmp(recvlen) return; } - IF_DEBUG(DEBUG_PKT|igmp_debug_kind(igmp->igmp_type, igmp->igmp_code)) log(LOG_DEBUG, 0, "RECV %s from %-15s to %s", - igmp_packet_kind(igmp->igmp_type, igmp->igmp_code), + packet_kind(igmp->igmp_type, igmp->igmp_code), inet_fmt(src, s1), inet_fmt(dst, s2)); switch (igmp->igmp_type) { - case IGMP_MEMBERSHIP_QUERY: + case IGMP_HOST_MEMBERSHIP_QUERY: accept_membership_query(src, dst, group, igmp->igmp_code); return; - case IGMP_V1_MEMBERSHIP_REPORT: - case IGMP_V2_MEMBERSHIP_REPORT: + case IGMP_HOST_MEMBERSHIP_REPORT: + case IGMP_HOST_NEW_MEMBERSHIP_REPORT: accept_group_report(src, dst, group, igmp->igmp_type); return; - case IGMP_V2_LEAVE_GROUP: + case IGMP_HOST_LEAVE_MESSAGE: accept_leave_message(src, dst, group); return; @@ -360,31 +378,32 @@ igmp_log_level(type, code) /* * Construct an IGMP message in the output packet buffer. The caller may - * have already placed data in that buffer, of length 'datalen'. + * have already placed data in that buffer, of length 'datalen'. Then send + * the message from the interface with IP address 'src' to destination 'dst'. */ void -build_igmp(src, dst, type, code, group, datalen) +send_igmp(src, dst, type, code, group, datalen) u_int32 src, dst; int type, code; u_int32 group; int datalen; { + struct sockaddr_in sdst; struct ip *ip; struct igmp *igmp; - extern int curttl; + int setloop = 0; + static int raset = 0; + int sendra = 0; + int sendlen; ip = (struct ip *)send_buf; ip->ip_src.s_addr = src; ip->ip_dst.s_addr = dst; ip->ip_len = MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen; -#ifdef RAW_OUTPUT_IS_RAW - ip->ip_len = htons(ip->ip_len); + sendlen = ip->ip_len; +#ifdef SUNOS5 + ip->ip_len += ip_addlen; #endif - if (IN_MULTICAST(ntohl(dst))) { - ip->ip_ttl = curttl; - } else { - ip->ip_ttl = MAXTTL; - } igmp = (struct igmp *)(send_buf + MIN_IP_HEADER_LEN); igmp->igmp_type = type; @@ -393,24 +412,6 @@ build_igmp(src, dst, type, code, group, datalen) igmp->igmp_cksum = 0; igmp->igmp_cksum = inet_cksum((u_short *)igmp, IGMP_MINLEN + datalen); -} - -/* - * Call build_igmp() to build an IGMP message in the output packet buffer. - * Then send the message from the interface with IP address 'src' to - * destination 'dst'. - */ -void -send_igmp(src, dst, type, code, group, datalen) - u_int32 src, dst; - int type, code; - u_int32 group; - int datalen; -{ - struct sockaddr_in sdst; - int setloop = 0; - - build_igmp(src, dst, type, code, group, datalen); if (IN_MULTICAST(ntohl(dst))) { k_set_if(src); @@ -418,16 +419,36 @@ send_igmp(src, dst, type, code, group, datalen) setloop = 1; k_set_loop(TRUE); } + if (SEND_RA(type)) + sendra = 1; + } + + if (sendra && !raset) { + setsockopt(igmp_socket, IPPROTO_IP, IP_OPTIONS, + router_alert, sizeof(router_alert)); + raset = 1; + } else if (!sendra && raset) { +#ifdef SUNOS5 + /* + * SunOS5 < 5.6 cannot properly reset the IP_OPTIONS "socket" + * option. Instead, set up a string of 4 no-op's. + */ + setsockopt(igmp_socket, IPPROTO_IP, IP_OPTIONS, + no_op, sizeof(no_op)); +#else + setsockopt(igmp_socket, IPPROTO_IP, IP_OPTIONS, + NULL, 0); +#endif + raset = 0; } bzero(&sdst, sizeof(sdst)); sdst.sin_family = AF_INET; -#ifdef HAVE_SA_LEN +#if (defined(BSD) && (BSD >= 199103)) sdst.sin_len = sizeof(sdst); #endif sdst.sin_addr.s_addr = dst; - if (sendto(igmp_socket, send_buf, - MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen, 0, + if (sendto(igmp_socket, send_buf, sendlen, 0, (struct sockaddr *)&sdst, sizeof(sdst)) < 0) { if (errno == ENETDOWN) check_vif_state(); @@ -440,8 +461,7 @@ send_igmp(src, dst, type, code, group, datalen) if (setloop) k_set_loop(FALSE); - IF_DEBUG(DEBUG_PKT|igmp_debug_kind(type, code)) log(LOG_DEBUG, 0, "SENT %s from %-15s to %s", - igmp_packet_kind(type, code), src == INADDR_ANY ? "INADDR_ANY" : + packet_kind(type, code), src == INADDR_ANY ? "INADDR_ANY" : inet_fmt(src, s1), inet_fmt(dst, s2)); } |