| 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 | * $Id: igmp.c,v 1.5 1993/06/23 18:47:17 pavel Exp $ |
| 11 | */ |
| 12 | |
| 13 | |
| 14 | #include "defs.h" |
| 15 | |
| 16 | |
| 17 | /* |
| 18 | * Exported variables. |
| 19 | */ |
| 20 | char recv_buf[MAX_IP_PACKET_LEN]; /* input packet buffer */ |
| 21 | char send_buf[MAX_IP_PACKET_LEN]; /* output packet buffer */ |
| 22 | int igmp_socket; /* socket for all network I/O */ |
| 23 | u_long allhosts_group; /* allhosts addr in net order */ |
| 24 | u_long dvmrp_group; /* DVMRP grp addr in net order */ |
| 25 | |
| 26 | |
| 27 | /* |
| 28 | * Open and initialize the igmp socket, and fill in the non-changing |
| 29 | * IP header fields in the output packet buffer. |
| 30 | */ |
| 31 | void init_igmp() |
| 32 | { |
| 33 | struct ip *ip; |
| 34 | |
| 35 | if ((igmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP)) < 0) |
| 36 | log(LOG_ERR, errno, "IGMP socket"); |
| 37 | |
| 38 | k_hdr_include(TRUE); /* include IP header when sending */ |
| 39 | k_set_rcvbuf(48*1024); /* lots of input buffering */ |
| 40 | k_set_ttl(1); /* restrict multicasts to one hop */ |
| 41 | k_set_loop(FALSE); /* disable multicast loopback */ |
| 42 | |
| 43 | ip = (struct ip *)send_buf; |
| 44 | ip->ip_tos = 0; |
| 45 | ip->ip_off = 0; |
| 46 | ip->ip_p = IPPROTO_IGMP; |
| 47 | ip->ip_ttl = MAXTTL; /* applies to unicasts only */ |
| 48 | |
| 49 | allhosts_group = htonl(INADDR_ALLHOSTS_GROUP); |
| 50 | dvmrp_group = htonl(INADDR_DVMRP_GROUP); |
| 51 | } |
| 52 | |
| 53 | static char *packet_kind(type, code) |
| 54 | u_char type, code; |
| 55 | { |
| 56 | switch (type) { |
| 57 | case IGMP_HOST_MEMBERSHIP_QUERY: return "membership query "; |
| 58 | case IGMP_HOST_MEMBERSHIP_REPORT: return "membership report "; |
| 59 | case IGMP_DVMRP: |
| 60 | switch (code) { |
| 61 | case DVMRP_PROBE: return "neighbor probe "; |
| 62 | case DVMRP_REPORT: return "route report "; |
| 63 | case DVMRP_ASK_NEIGHBORS: return "neighbor request "; |
| 64 | case DVMRP_NEIGHBORS: return "neighbor list "; |
| 65 | case DVMRP_ASK_NEIGHBORS2: return "neighbor request 2"; |
| 66 | case DVMRP_NEIGHBORS2: return "neighbor list 2 "; |
| 67 | default: return "unknown DVMRP msg "; |
| 68 | } |
| 69 | default: return "unknown IGMP msg "; |
| 70 | } |
| 71 | } |
| 72 | |
| 73 | /* |
| 74 | * Process a newly received IGMP packet that is sitting in the input |
| 75 | * packet buffer. |
| 76 | */ |
| 77 | void accept_igmp(recvlen) |
| 78 | int recvlen; |
| 79 | { |
| 80 | register vifi_t vifi; |
| 81 | register u_long src, dst, group; |
| 82 | struct ip *ip; |
| 83 | struct igmp *igmp; |
| 84 | int ipdatalen, iphdrlen, igmpdatalen; |
| 85 | |
| 86 | if (recvlen < sizeof(struct ip)) { |
| 87 | log(LOG_WARNING, 0, |
| 88 | "received packet too short (%u bytes) for IP header", recvlen); |
| 89 | return; |
| 90 | } |
| 91 | |
| 92 | ip = (struct ip *)recv_buf; |
| 93 | src = ip->ip_src.s_addr; |
| 94 | dst = ip->ip_dst.s_addr; |
| 95 | iphdrlen = ip->ip_hl << 2; |
| 96 | ipdatalen = ip->ip_len; |
| 97 | if (iphdrlen + ipdatalen != recvlen) { |
| 98 | log(LOG_WARNING, 0, |
| 99 | "received packet shorter (%u bytes) than hdr+data length (%u+%u)", |
| 100 | recvlen, iphdrlen, ipdatalen); |
| 101 | return; |
| 102 | } |
| 103 | |
| 104 | igmp = (struct igmp *)(recv_buf + iphdrlen); |
| 105 | group = igmp->igmp_group.s_addr; |
| 106 | igmpdatalen = ipdatalen - IGMP_MINLEN; |
| 107 | if (igmpdatalen < 0) { |
| 108 | log(LOG_WARNING, 0, |
| 109 | "received IP data field too short (%u bytes) for IGMP, from %s", |
| 110 | ipdatalen, inet_fmt(src, s1)); |
| 111 | return; |
| 112 | } |
| 113 | |
| 114 | log(LOG_DEBUG, 0, "RECV %s from %-15s to %s", |
| 115 | packet_kind(igmp->igmp_type, igmp->igmp_code), |
| 116 | inet_fmt(src, s1), inet_fmt(dst, s2)); |
| 117 | |
| 118 | switch (igmp->igmp_type) { |
| 119 | |
| 120 | case IGMP_HOST_MEMBERSHIP_QUERY: |
| 121 | return; /* Answered automatically by the kernel. */ |
| 122 | |
| 123 | case IGMP_HOST_MEMBERSHIP_REPORT: |
| 124 | accept_group_report(src, dst, group); |
| 125 | return; |
| 126 | |
| 127 | case IGMP_DVMRP: |
| 128 | switch (igmp->igmp_code) { |
| 129 | |
| 130 | case DVMRP_PROBE: |
| 131 | accept_probe(src, dst); |
| 132 | return; |
| 133 | |
| 134 | case DVMRP_REPORT: |
| 135 | accept_report(src, dst, |
| 136 | (char *)(igmp+1), igmpdatalen); |
| 137 | return; |
| 138 | |
| 139 | case DVMRP_ASK_NEIGHBORS: |
| 140 | accept_neighbor_request(src, dst); |
| 141 | return; |
| 142 | |
| 143 | case DVMRP_ASK_NEIGHBORS2: |
| 144 | accept_neighbor_request2(src, dst); |
| 145 | return; |
| 146 | |
| 147 | case DVMRP_NEIGHBORS: |
| 148 | accept_neighbors(src, dst, (char *)(igmp+1), igmpdatalen, |
| 149 | group); |
| 150 | return; |
| 151 | |
| 152 | case DVMRP_NEIGHBORS2: |
| 153 | accept_neighbors2(src, dst, (char *)(igmp+1), igmpdatalen, |
| 154 | group); |
| 155 | return; |
| 156 | |
| 157 | default: |
| 158 | log(LOG_INFO, 0, |
| 159 | "ignoring unknown DVMRP message code %u from %s to %s", |
| 160 | igmp->igmp_code, inet_fmt(src, s1), |
| 161 | inet_fmt(dst, s2)); |
| 162 | return; |
| 163 | } |
| 164 | |
| 165 | default: |
| 166 | log(LOG_INFO, 0, |
| 167 | "ignoring unknown IGMP message type %u from %s to %s", |
| 168 | igmp->igmp_type, inet_fmt(src, s1), |
| 169 | inet_fmt(dst, s2)); |
| 170 | return; |
| 171 | } |
| 172 | } |
| 173 | |
| 174 | |
| 175 | /* |
| 176 | * Construct an IGMP message in the output packet buffer. The caller may |
| 177 | * have already placed data in that buffer, of length 'datalen'. Then send |
| 178 | * the message from the interface with IP address 'src' to destination 'dst'. |
| 179 | */ |
| 180 | void send_igmp(src, dst, type, code, group, datalen) |
| 181 | u_long src, dst; |
| 182 | int type, code; |
| 183 | u_long group; |
| 184 | int datalen; |
| 185 | { |
| 186 | static struct sockaddr_in sdst = {AF_INET}; |
| 187 | struct ip *ip; |
| 188 | struct igmp *igmp; |
| 189 | |
| 190 | ip = (struct ip *)send_buf; |
| 191 | ip->ip_src.s_addr = src; |
| 192 | ip->ip_dst.s_addr = dst; |
| 193 | ip->ip_len = MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen; |
| 194 | |
| 195 | igmp = (struct igmp *)(send_buf + MIN_IP_HEADER_LEN); |
| 196 | igmp->igmp_type = type; |
| 197 | igmp->igmp_code = code; |
| 198 | igmp->igmp_group.s_addr = group; |
| 199 | igmp->igmp_cksum = 0; |
| 200 | igmp->igmp_cksum = inet_cksum((u_short *)igmp, |
| 201 | IGMP_MINLEN + datalen); |
| 202 | |
| 203 | if (IN_MULTICAST(ntohl(dst))) k_set_if(src); |
| 204 | if (dst == allhosts_group) k_set_loop(TRUE); |
| 205 | |
| 206 | sdst.sin_addr.s_addr = dst; |
| 207 | if (sendto(igmp_socket, send_buf, ip->ip_len, 0, |
| 208 | (struct sockaddr *)&sdst, sizeof(sdst)) < 0) { |
| 209 | if (errno == ENETDOWN) check_vif_state(); |
| 210 | else log(LOG_WARNING, errno, "sendto on %s", inet_fmt(src, s1)); |
| 211 | } |
| 212 | |
| 213 | if (dst == allhosts_group) k_set_loop(FALSE); |
| 214 | |
| 215 | log(LOG_DEBUG, 0, "SENT %s from %-15s to %s", |
| 216 | packet_kind(type, code), inet_fmt(src, s1), inet_fmt(dst, s2)); |
| 217 | } |