igmp.c revision 1.3 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.3 1995/06/01 02:25:53 mycroft Exp $
11 */
12
13
14 #include "defs.h"
15
16
17 /*
18 * Exported variables.
19 */
20 char *recv_buf; /* input packet buffer */
21 char *send_buf; /* output packet buffer */
22 int igmp_socket; /* socket for all network I/O */
23 u_int32_t allhosts_group; /* All hosts addr in net order */
24 u_int32_t allrtrs_group; /* All-Routers " in net order */
25 u_int32_t dvmrp_group; /* DVMRP grp addr in net order */
26 u_int32_t dvmrp_genid; /* IGMP generation id */
27
28 /*
29 * Open and initialize the igmp socket, and fill in the non-changing
30 * IP header fields in the output packet buffer.
31 */
32 void init_igmp()
33 {
34 struct ip *ip;
35
36 recv_buf = malloc(RECV_BUF_SIZE);
37 send_buf = malloc(RECV_BUF_SIZE);
38
39 if ((igmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP)) < 0)
40 log(LOG_ERR, errno, "IGMP socket");
41
42 k_hdr_include(TRUE); /* include IP header when sending */
43 k_set_rcvbuf(48*1024); /* lots of input buffering */
44 k_set_ttl(1); /* restrict multicasts to one hop */
45 k_set_loop(FALSE); /* disable multicast loopback */
46
47 ip = (struct ip *)send_buf;
48 ip->ip_hl = sizeof(struct ip) >> 2;
49 ip->ip_v = IPVERSION;
50 ip->ip_tos = 0;
51 ip->ip_off = 0;
52 ip->ip_p = IPPROTO_IGMP;
53 ip->ip_ttl = MAXTTL; /* applies to unicasts only */
54
55 allhosts_group = htonl(INADDR_ALLHOSTS_GROUP);
56 dvmrp_group = htonl(INADDR_DVMRP_GROUP);
57 allrtrs_group = htonl(INADDR_ALLRTRS_GROUP);
58 }
59
60 #define PIM_QUERY 0
61 #define PIM_REGISTER 1
62 #define PIM_REGISTER_STOP 2
63 #define PIM_JOIN_PRUNE 3
64 #define PIM_RP_REACHABLE 4
65 #define PIM_ASSERT 5
66 #define PIM_GRAFT 6
67 #define PIM_GRAFT_ACK 7
68
69 static char *packet_kind(type, code)
70 u_char type, code;
71 {
72 switch (type) {
73 case IGMP_HOST_MEMBERSHIP_QUERY: return "membership query ";
74 case IGMP_v1_HOST_MEMBERSHIP_REPORT: return "membership report ";
75 case IGMP_v2_HOST_MEMBERSHIP_REPORT: return "new member report ";
76 case IGMP_HOST_LEAVE_MESSAGE: return "leave message ";
77 case IGMP_DVMRP:
78 switch (code) {
79 case DVMRP_PROBE: return "neighbor probe ";
80 case DVMRP_REPORT: return "route report ";
81 case DVMRP_ASK_NEIGHBORS: return "neighbor request ";
82 case DVMRP_NEIGHBORS: return "neighbor list ";
83 case DVMRP_ASK_NEIGHBORS2: return "neighbor request 2";
84 case DVMRP_NEIGHBORS2: return "neighbor list 2 ";
85 case DVMRP_PRUNE: return "prune message ";
86 case DVMRP_GRAFT: return "graft message ";
87 case DVMRP_GRAFT_ACK: return "graft message ack ";
88 default: return "unknown DVMRP msg ";
89 }
90 case IGMP_PIM:
91 switch (code) {
92 case PIM_QUERY: return "PIM Router-Query ";
93 case PIM_REGISTER: return "PIM Register ";
94 case PIM_REGISTER_STOP: return "PIM Register-Stop ";
95 case PIM_JOIN_PRUNE: return "PIM Join/Prune ";
96 case PIM_RP_REACHABLE: return "PIM RP-Reachable ";
97 case PIM_ASSERT: return "PIM Assert ";
98 case PIM_GRAFT: return "PIM Graft ";
99 case PIM_GRAFT_ACK: return "PIM Graft-Ack ";
100 default: return "unknown PIM msg ";
101 }
102 case IGMP_MTRACE_QUERY: return "IGMP trace query ";
103 case IGMP_MTRACE_REPLY: return "IGMP trace reply ";
104 default: return "unknown IGMP msg ";
105 }
106 }
107
108 /*
109 * Process a newly received IGMP packet that is sitting in the input
110 * packet buffer.
111 */
112 void accept_igmp(recvlen)
113 int recvlen;
114 {
115 register u_int32_t src, dst, group;
116 struct ip *ip;
117 struct igmp *igmp;
118 int ipdatalen, iphdrlen, igmpdatalen;
119
120 if (recvlen < sizeof(struct ip)) {
121 log(LOG_WARNING, 0,
122 "received packet too short (%u bytes) for IP header", recvlen);
123 return;
124 }
125
126 ip = (struct ip *)recv_buf;
127 src = ip->ip_src.s_addr;
128 dst = ip->ip_dst.s_addr;
129
130 /*
131 * this is most likely a message from the kernel indicating that
132 * a new src grp pair message has arrived and so, it would be
133 * necessary to install a route into the kernel for this.
134 */
135 if (ip->ip_p == 0) {
136 if (src == 0 || dst == 0)
137 log(LOG_WARNING, 0, "kernel request not accurate");
138 else
139 add_table_entry(src, dst);
140 return;
141 }
142
143 iphdrlen = ip->ip_hl << 2;
144 ipdatalen = ip->ip_len;
145 if (iphdrlen + ipdatalen != recvlen) {
146 log(LOG_WARNING, 0,
147 "received packet shorter (%u bytes) than hdr+data length (%u+%u)",
148 recvlen, iphdrlen, ipdatalen);
149 return;
150 }
151
152 igmp = (struct igmp *)(recv_buf + iphdrlen);
153 group = igmp->igmp_group.s_addr;
154 igmpdatalen = ipdatalen - IGMP_MINLEN;
155 if (igmpdatalen < 0) {
156 log(LOG_WARNING, 0,
157 "received IP data field too short (%u bytes) for IGMP, from %s",
158 ipdatalen, inet_fmt(src, s1));
159 return;
160 }
161
162 log(LOG_DEBUG, 0, "RECV %s from %-15s to %s",
163 packet_kind(igmp->igmp_type, igmp->igmp_code),
164 inet_fmt(src, s1), inet_fmt(dst, s2));
165
166 switch (igmp->igmp_type) {
167
168 case IGMP_HOST_MEMBERSHIP_QUERY:
169 accept_membership_query(src, dst, group, igmp->igmp_code);
170 return;
171
172 case IGMP_v1_HOST_MEMBERSHIP_REPORT:
173 case IGMP_v2_HOST_MEMBERSHIP_REPORT:
174 accept_group_report(src, dst, group, igmp->igmp_type);
175 return;
176
177 case IGMP_HOST_LEAVE_MESSAGE:
178 accept_leave_message(src, dst, group);
179 return;
180
181 case IGMP_DVMRP:
182 group = ntohl(group);
183
184 switch (igmp->igmp_code) {
185 case DVMRP_PROBE:
186 accept_probe(src, dst,
187 (char *)(igmp+1), igmpdatalen, group);
188 return;
189
190 case DVMRP_REPORT:
191 accept_report(src, dst,
192 (char *)(igmp+1), igmpdatalen, group);
193 return;
194
195 case DVMRP_ASK_NEIGHBORS:
196 accept_neighbor_request(src, dst);
197 return;
198
199 case DVMRP_ASK_NEIGHBORS2:
200 accept_neighbor_request2(src, dst);
201 return;
202
203 case DVMRP_NEIGHBORS:
204 accept_neighbors(src, dst, (char *)(igmp+1), igmpdatalen,
205 group);
206 return;
207
208 case DVMRP_NEIGHBORS2:
209 accept_neighbors2(src, dst, (char *)(igmp+1), igmpdatalen,
210 group);
211 return;
212
213 case DVMRP_PRUNE:
214 accept_prune(src, dst, (char *)(igmp+1), igmpdatalen);
215 return;
216
217 case DVMRP_GRAFT:
218 accept_graft(src, dst, (char *)(igmp+1), igmpdatalen);
219 return;
220
221 case DVMRP_GRAFT_ACK:
222 accept_g_ack(src, dst, (char *)(igmp+1), igmpdatalen);
223 return;
224
225 default:
226 log(LOG_INFO, 0,
227 "ignoring unknown DVMRP message code %u from %s to %s",
228 igmp->igmp_code, inet_fmt(src, s1),
229 inet_fmt(dst, s2));
230 return;
231 }
232
233 case IGMP_PIM:
234 return;
235
236 case IGMP_MTRACE_REPLY:
237 return;
238
239 case IGMP_MTRACE_QUERY:
240 accept_mtrace(src, dst, group, (char *)(igmp+1),
241 igmp->igmp_code, igmpdatalen);
242 return;
243
244 default:
245 log(LOG_INFO, 0,
246 "ignoring unknown IGMP message type %x from %s to %s",
247 igmp->igmp_type, inet_fmt(src, s1),
248 inet_fmt(dst, s2));
249 return;
250 }
251 }
252
253
254 /*
255 * Construct an IGMP message in the output packet buffer. The caller may
256 * have already placed data in that buffer, of length 'datalen'. Then send
257 * the message from the interface with IP address 'src' to destination 'dst'.
258 */
259 void
260 send_igmp(src, dst, type, code, group, datalen)
261 u_int32_t src, dst;
262 int type, code;
263 u_int32_t group;
264 int datalen;
265 {
266 static struct sockaddr_in sdst;
267 struct ip *ip;
268 struct igmp *igmp;
269
270 ip = (struct ip *)send_buf;
271 ip->ip_src.s_addr = src;
272 ip->ip_dst.s_addr = dst;
273 ip->ip_len = MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen;
274
275 igmp = (struct igmp *)(send_buf + MIN_IP_HEADER_LEN);
276 igmp->igmp_type = type;
277 igmp->igmp_code = code;
278 igmp->igmp_group.s_addr = group;
279 igmp->igmp_cksum = 0;
280 igmp->igmp_cksum = inet_cksum((u_short *)igmp,
281 IGMP_MINLEN + datalen);
282
283 if (IN_MULTICAST(ntohl(dst))) k_set_if(src);
284 if (dst == allhosts_group) k_set_loop(TRUE);
285
286 bzero(&sdst, sizeof(sdst));
287 sdst.sin_family = AF_INET;
288 #if (defined(BSD) && (BSD >= 199103))
289 sdst.sin_len = sizeof(sdst);
290 #endif
291 sdst.sin_addr.s_addr = dst;
292 if (sendto(igmp_socket, send_buf, ip->ip_len, 0,
293 (struct sockaddr *)&sdst, sizeof(sdst)) < 0) {
294 if (errno == ENETDOWN)
295 check_vif_state();
296 else
297 log(LOG_WARNING, errno,
298 "sendto to %s on %s",
299 inet_fmt(dst, s1), inet_fmt(src, s2));
300 }
301
302 if (dst == allhosts_group) k_set_loop(FALSE);
303
304 log(LOG_DEBUG, 0, "SENT %s from %-15s to %s",
305 packet_kind(type, code), inet_fmt(src, s1), inet_fmt(dst, s2));
306 }
307