1*58cb611aSlukem /* $NetBSD: igmp.c,v 1.14 2009/04/17 16:05:43 lukem Exp $ */
2c60d41a9Swiz
3c60d41a9Swiz /*
4c60d41a9Swiz * The mrouted program is covered by the license in the accompanying file
5c60d41a9Swiz * named "LICENSE". Use of the mrouted program represents acceptance of
6c60d41a9Swiz * the terms and conditions listed in that file.
7c60d41a9Swiz *
8c60d41a9Swiz * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
9c60d41a9Swiz * Leland Stanford Junior University.
10c60d41a9Swiz */
11c60d41a9Swiz
12c60d41a9Swiz
13c60d41a9Swiz #include "defs.h"
14c60d41a9Swiz
15c60d41a9Swiz
16c60d41a9Swiz /*
17c60d41a9Swiz * Exported variables.
18c60d41a9Swiz */
19c60d41a9Swiz char *recv_buf; /* input packet buffer */
20c60d41a9Swiz char *send_buf; /* output packet buffer */
21761f7bebSitojun size_t send_buflen; /* output packet buffer */
22c60d41a9Swiz int igmp_socket; /* socket for all network I/O */
23c60d41a9Swiz u_int32_t allhosts_group; /* All hosts addr in net order */
24c60d41a9Swiz u_int32_t allrtrs_group; /* All-Routers " in net order */
25c60d41a9Swiz u_int32_t dvmrp_group; /* DVMRP grp addr in net order */
26c60d41a9Swiz u_int32_t dvmrp_genid; /* IGMP generation id */
27c60d41a9Swiz
28c60d41a9Swiz /*
29c60d41a9Swiz * Local function definitions.
30c60d41a9Swiz */
31c60d41a9Swiz /* u_char promoted to u_int */
32*58cb611aSlukem static const char * packet_kind(u_int type, u_int code);
33c60d41a9Swiz static int igmp_log_level(u_int type, u_int code);
34c60d41a9Swiz
35c60d41a9Swiz /*
36c60d41a9Swiz * Open and initialize the igmp socket, and fill in the non-changing
37c60d41a9Swiz * IP header fields in the output packet buffer.
38c60d41a9Swiz */
39c60d41a9Swiz void
init_igmp(void)40c60d41a9Swiz init_igmp(void)
41c60d41a9Swiz {
42c60d41a9Swiz struct ip *ip;
43c60d41a9Swiz
44c60d41a9Swiz recv_buf = malloc(RECV_BUF_SIZE);
45c60d41a9Swiz send_buf = malloc(RECV_BUF_SIZE);
46761f7bebSitojun send_buflen = RECV_BUF_SIZE;
47c60d41a9Swiz
48c60d41a9Swiz if ((igmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP)) < 0)
49c60d41a9Swiz logit(LOG_ERR, errno, "IGMP socket");
50c60d41a9Swiz
51c60d41a9Swiz k_hdr_include(TRUE); /* include IP header when sending */
52c60d41a9Swiz k_set_rcvbuf(48*1024); /* lots of input buffering */
53c60d41a9Swiz k_set_ttl(1); /* restrict multicasts to one hop */
54c60d41a9Swiz k_set_loop(FALSE); /* disable multicast loopback */
55c60d41a9Swiz
56c60d41a9Swiz ip = (struct ip *)send_buf;
57c60d41a9Swiz ip->ip_hl = sizeof(struct ip) >> 2;
58c60d41a9Swiz ip->ip_v = IPVERSION;
59c60d41a9Swiz ip->ip_tos = 0;
60c60d41a9Swiz ip->ip_off = 0;
61c60d41a9Swiz ip->ip_p = IPPROTO_IGMP;
62c60d41a9Swiz ip->ip_ttl = MAXTTL; /* applies to unicasts only */
63c60d41a9Swiz
64c60d41a9Swiz allhosts_group = htonl(INADDR_ALLHOSTS_GROUP);
65c60d41a9Swiz dvmrp_group = htonl(INADDR_DVMRP_GROUP);
66c60d41a9Swiz allrtrs_group = htonl(INADDR_ALLRTRS_GROUP);
67c60d41a9Swiz }
68c60d41a9Swiz
69c60d41a9Swiz #define PIM_QUERY 0
70c60d41a9Swiz #define PIM_REGISTER 1
71c60d41a9Swiz #define PIM_REGISTER_STOP 2
72c60d41a9Swiz #define PIM_JOIN_PRUNE 3
73c60d41a9Swiz #define PIM_RP_REACHABLE 4
74c60d41a9Swiz #define PIM_ASSERT 5
75c60d41a9Swiz #define PIM_GRAFT 6
76c60d41a9Swiz #define PIM_GRAFT_ACK 7
77c60d41a9Swiz
78*58cb611aSlukem static const char *
packet_kind(u_int type,u_int code)79c60d41a9Swiz packet_kind(u_int type, u_int code)
80c60d41a9Swiz {
81c60d41a9Swiz switch (type) {
82c60d41a9Swiz case IGMP_HOST_MEMBERSHIP_QUERY: return "membership query ";
83c60d41a9Swiz case IGMP_v1_HOST_MEMBERSHIP_REPORT: return "v1 member report ";
84c60d41a9Swiz case IGMP_v2_HOST_MEMBERSHIP_REPORT: return "v2 member report ";
85c60d41a9Swiz case IGMP_HOST_LEAVE_MESSAGE: return "leave message ";
86c60d41a9Swiz case IGMP_DVMRP:
87c60d41a9Swiz switch (code) {
88c60d41a9Swiz case DVMRP_PROBE: return "neighbor probe ";
89c60d41a9Swiz case DVMRP_REPORT: return "route report ";
90c60d41a9Swiz case DVMRP_ASK_NEIGHBORS: return "neighbor request ";
91c60d41a9Swiz case DVMRP_NEIGHBORS: return "neighbor list ";
92c60d41a9Swiz case DVMRP_ASK_NEIGHBORS2: return "neighbor request 2";
93c60d41a9Swiz case DVMRP_NEIGHBORS2: return "neighbor list 2 ";
94c60d41a9Swiz case DVMRP_PRUNE: return "prune message ";
95c60d41a9Swiz case DVMRP_GRAFT: return "graft message ";
96c60d41a9Swiz case DVMRP_GRAFT_ACK: return "graft message ack ";
97c60d41a9Swiz case DVMRP_INFO_REQUEST: return "info request ";
98c60d41a9Swiz case DVMRP_INFO_REPLY: return "info reply ";
99c60d41a9Swiz default: return "unknown DVMRP msg ";
100c60d41a9Swiz }
101c60d41a9Swiz case IGMP_PIM:
102c60d41a9Swiz switch (code) {
103c60d41a9Swiz case PIM_QUERY: return "PIM Router-Query ";
104c60d41a9Swiz case PIM_REGISTER: return "PIM Register ";
105c60d41a9Swiz case PIM_REGISTER_STOP: return "PIM Register-Stop ";
106c60d41a9Swiz case PIM_JOIN_PRUNE: return "PIM Join/Prune ";
107c60d41a9Swiz case PIM_RP_REACHABLE: return "PIM RP-Reachable ";
108c60d41a9Swiz case PIM_ASSERT: return "PIM Assert ";
109c60d41a9Swiz case PIM_GRAFT: return "PIM Graft ";
110c60d41a9Swiz case PIM_GRAFT_ACK: return "PIM Graft-Ack ";
111c60d41a9Swiz default: return "unknown PIM msg ";
112c60d41a9Swiz }
113c60d41a9Swiz case IGMP_MTRACE_QUERY: return "IGMP trace query ";
114c60d41a9Swiz case IGMP_MTRACE_REPLY: return "IGMP trace reply ";
115c60d41a9Swiz default: return "unknown IGMP msg ";
116c60d41a9Swiz }
117c60d41a9Swiz }
118c60d41a9Swiz
119c60d41a9Swiz /*
120c60d41a9Swiz * Process a newly received IGMP packet that is sitting in the input
121c60d41a9Swiz * packet buffer.
122c60d41a9Swiz */
123c60d41a9Swiz void
accept_igmp(int recvlen)124c60d41a9Swiz accept_igmp(int recvlen)
125c60d41a9Swiz {
126c60d41a9Swiz u_int32_t src, dst, group;
127c60d41a9Swiz struct ip *ip;
128c60d41a9Swiz struct igmp *igmp;
129c60d41a9Swiz int ipdatalen, iphdrlen, igmpdatalen;
130c60d41a9Swiz
131*58cb611aSlukem if (recvlen < (int)sizeof(struct ip)) {
132c60d41a9Swiz logit(LOG_WARNING, 0,
133c60d41a9Swiz "received packet too short (%u bytes) for IP header", recvlen);
134c60d41a9Swiz return;
135c60d41a9Swiz }
136c60d41a9Swiz
137c60d41a9Swiz ip = (struct ip *)recv_buf;
138c60d41a9Swiz src = ip->ip_src.s_addr;
139c60d41a9Swiz dst = ip->ip_dst.s_addr;
140c60d41a9Swiz
141c60d41a9Swiz /*
142c60d41a9Swiz * this is most likely a message from the kernel indicating that
143c60d41a9Swiz * a new src grp pair message has arrived and so, it would be
144c60d41a9Swiz * necessary to install a route into the kernel for this.
145c60d41a9Swiz */
146c60d41a9Swiz if (ip->ip_p == 0) {
147c60d41a9Swiz if (src == 0 || dst == 0)
148c60d41a9Swiz logit(LOG_WARNING, 0, "kernel request not accurate");
149c60d41a9Swiz else
150c60d41a9Swiz add_table_entry(src, dst);
151c60d41a9Swiz return;
152c60d41a9Swiz }
153c60d41a9Swiz
154c60d41a9Swiz iphdrlen = ip->ip_hl << 2;
155c60d41a9Swiz ipdatalen = ip->ip_len;
156c60d41a9Swiz if (iphdrlen + ipdatalen != recvlen) {
157c60d41a9Swiz logit(LOG_WARNING, 0,
158c60d41a9Swiz "received packet from %s shorter (%u bytes) than hdr+data length (%u+%u)",
159ee70b5d6Sdsl inet_fmt(src), recvlen, iphdrlen, ipdatalen);
160c60d41a9Swiz return;
161c60d41a9Swiz }
162c60d41a9Swiz
163c60d41a9Swiz igmp = (struct igmp *)(recv_buf + iphdrlen);
164c60d41a9Swiz group = igmp->igmp_group.s_addr;
165c60d41a9Swiz igmpdatalen = ipdatalen - IGMP_MINLEN;
166c60d41a9Swiz if (igmpdatalen < 0) {
167c60d41a9Swiz logit(LOG_WARNING, 0,
168c60d41a9Swiz "received IP data field too short (%u bytes) for IGMP, from %s",
169ee70b5d6Sdsl ipdatalen, inet_fmt(src));
170c60d41a9Swiz return;
171c60d41a9Swiz }
172c60d41a9Swiz
173c60d41a9Swiz logit(LOG_DEBUG, 0, "RECV %s from %-15s to %s",
174c60d41a9Swiz packet_kind(igmp->igmp_type, igmp->igmp_code),
175ee70b5d6Sdsl inet_fmt(src), inet_fmt(dst));
176c60d41a9Swiz
177c60d41a9Swiz switch (igmp->igmp_type) {
178c60d41a9Swiz
179c60d41a9Swiz case IGMP_HOST_MEMBERSHIP_QUERY:
180c60d41a9Swiz accept_membership_query(src, dst, group, igmp->igmp_code);
181c60d41a9Swiz return;
182c60d41a9Swiz
183c60d41a9Swiz case IGMP_v1_HOST_MEMBERSHIP_REPORT:
184c60d41a9Swiz case IGMP_v2_HOST_MEMBERSHIP_REPORT:
185c60d41a9Swiz accept_group_report(src, dst, group, igmp->igmp_type);
186c60d41a9Swiz return;
187c60d41a9Swiz
188c60d41a9Swiz case IGMP_HOST_LEAVE_MESSAGE:
189c60d41a9Swiz accept_leave_message(src, dst, group);
190c60d41a9Swiz return;
191c60d41a9Swiz
192c60d41a9Swiz case IGMP_DVMRP:
193c60d41a9Swiz group = ntohl(group);
194c60d41a9Swiz
195c60d41a9Swiz switch (igmp->igmp_code) {
196c60d41a9Swiz case DVMRP_PROBE:
197c60d41a9Swiz accept_probe(src, dst,
198c60d41a9Swiz (char *)(igmp+1), igmpdatalen, group);
199c60d41a9Swiz return;
200c60d41a9Swiz
201c60d41a9Swiz case DVMRP_REPORT:
202c60d41a9Swiz accept_report(src, dst,
203c60d41a9Swiz (char *)(igmp+1), igmpdatalen, group);
204c60d41a9Swiz return;
205c60d41a9Swiz
206c60d41a9Swiz case DVMRP_ASK_NEIGHBORS:
207c60d41a9Swiz accept_neighbor_request(src, dst);
208c60d41a9Swiz return;
209c60d41a9Swiz
210c60d41a9Swiz case DVMRP_ASK_NEIGHBORS2:
211c60d41a9Swiz accept_neighbor_request2(src, dst);
212c60d41a9Swiz return;
213c60d41a9Swiz
214c60d41a9Swiz case DVMRP_NEIGHBORS:
215c60d41a9Swiz accept_neighbors(src, dst, (u_char *)(igmp+1), igmpdatalen,
216c60d41a9Swiz group);
217c60d41a9Swiz return;
218c60d41a9Swiz
219c60d41a9Swiz case DVMRP_NEIGHBORS2:
220c60d41a9Swiz accept_neighbors2(src, dst, (u_char *)(igmp+1), igmpdatalen,
221c60d41a9Swiz group);
222c60d41a9Swiz return;
223c60d41a9Swiz
224c60d41a9Swiz case DVMRP_PRUNE:
225c60d41a9Swiz accept_prune(src, dst, (char *)(igmp+1), igmpdatalen);
226c60d41a9Swiz return;
227c60d41a9Swiz
228c60d41a9Swiz case DVMRP_GRAFT:
229c60d41a9Swiz accept_graft(src, dst, (char *)(igmp+1), igmpdatalen);
230c60d41a9Swiz return;
231c60d41a9Swiz
232c60d41a9Swiz case DVMRP_GRAFT_ACK:
233c60d41a9Swiz accept_g_ack(src, dst, (char *)(igmp+1), igmpdatalen);
234c60d41a9Swiz return;
235c60d41a9Swiz
236c60d41a9Swiz case DVMRP_INFO_REQUEST:
2372a512423Smrg accept_info_request(src, dst, (u_char *)(igmp+1),
238c60d41a9Swiz igmpdatalen);
239c60d41a9Swiz return;
240c60d41a9Swiz
241c60d41a9Swiz case DVMRP_INFO_REPLY:
2422a512423Smrg accept_info_reply(src, dst, (u_char *)(igmp+1), igmpdatalen);
243c60d41a9Swiz return;
244c60d41a9Swiz
245c60d41a9Swiz default:
246c60d41a9Swiz logit(LOG_INFO, 0,
247c60d41a9Swiz "ignoring unknown DVMRP message code %u from %s to %s",
248ee70b5d6Sdsl igmp->igmp_code, inet_fmt(src),
249ee70b5d6Sdsl inet_fmt(dst));
250c60d41a9Swiz return;
251c60d41a9Swiz }
252c60d41a9Swiz
253c60d41a9Swiz case IGMP_PIM:
254c60d41a9Swiz return;
255c60d41a9Swiz
256c60d41a9Swiz case IGMP_MTRACE_REPLY:
257c60d41a9Swiz return;
258c60d41a9Swiz
259c60d41a9Swiz case IGMP_MTRACE_QUERY:
260c60d41a9Swiz accept_mtrace(src, dst, group, (char *)(igmp+1),
261c60d41a9Swiz igmp->igmp_code, igmpdatalen);
262c60d41a9Swiz return;
263c60d41a9Swiz
264c60d41a9Swiz default:
265c60d41a9Swiz logit(LOG_INFO, 0,
266c60d41a9Swiz "ignoring unknown IGMP message type %x from %s to %s",
267ee70b5d6Sdsl igmp->igmp_type, inet_fmt(src),
268ee70b5d6Sdsl inet_fmt(dst));
269c60d41a9Swiz return;
270c60d41a9Swiz }
271c60d41a9Swiz }
272c60d41a9Swiz
273c60d41a9Swiz /*
274c60d41a9Swiz * Some IGMP messages are more important than others. This routine
275c60d41a9Swiz * determines the logging level at which to log a send error (often
276c60d41a9Swiz * "No route to host"). This is important when there is asymmetric
277c60d41a9Swiz * reachability and someone is trying to, i.e., mrinfo me periodically.
278c60d41a9Swiz */
279c60d41a9Swiz static int
igmp_log_level(u_int type,u_int code)280c60d41a9Swiz igmp_log_level(u_int type, u_int code)
281c60d41a9Swiz {
282c60d41a9Swiz switch (type) {
283c60d41a9Swiz case IGMP_MTRACE_REPLY:
284c60d41a9Swiz return LOG_INFO;
285c60d41a9Swiz
286c60d41a9Swiz case IGMP_DVMRP:
287c60d41a9Swiz switch (code) {
288c60d41a9Swiz case DVMRP_NEIGHBORS:
289c60d41a9Swiz case DVMRP_NEIGHBORS2:
290c60d41a9Swiz return LOG_INFO;
291c60d41a9Swiz }
292c60d41a9Swiz }
293c60d41a9Swiz return LOG_WARNING;
294c60d41a9Swiz }
295c60d41a9Swiz
296c60d41a9Swiz /*
297c60d41a9Swiz * Construct an IGMP message in the output packet buffer. The caller may
298c60d41a9Swiz * have already placed data in that buffer, of length 'datalen'. Then send
299c60d41a9Swiz * the message from the interface with IP address 'src' to destination 'dst'.
300c60d41a9Swiz */
301c60d41a9Swiz void
send_igmp(u_int32_t src,u_int32_t dst,int type,int code,u_int32_t group,int datalen)302c60d41a9Swiz send_igmp(u_int32_t src, u_int32_t dst, int type, int code, u_int32_t group,
303c60d41a9Swiz int datalen)
304c60d41a9Swiz {
305c60d41a9Swiz struct sockaddr_in sdst;
306c60d41a9Swiz struct ip *ip;
307c60d41a9Swiz struct igmp *igmp;
308c60d41a9Swiz int setloop;
309c60d41a9Swiz
310c60d41a9Swiz setloop = 0;
311c60d41a9Swiz ip = (struct ip *)send_buf;
312c60d41a9Swiz ip->ip_src.s_addr = src;
313c60d41a9Swiz ip->ip_dst.s_addr = dst;
314c60d41a9Swiz ip->ip_len = MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen;
315c60d41a9Swiz
316c60d41a9Swiz igmp = (struct igmp *)(send_buf + MIN_IP_HEADER_LEN);
317c60d41a9Swiz igmp->igmp_type = type;
318c60d41a9Swiz igmp->igmp_code = code;
319c60d41a9Swiz igmp->igmp_group.s_addr = group;
320c60d41a9Swiz igmp->igmp_cksum = 0;
321c60d41a9Swiz igmp->igmp_cksum = inet_cksum((u_int16_t *)igmp,
322c60d41a9Swiz IGMP_MINLEN + datalen);
323c60d41a9Swiz
324c60d41a9Swiz if (IN_MULTICAST(ntohl(dst))) {
325c60d41a9Swiz k_set_if(src);
326c60d41a9Swiz if (type != IGMP_DVMRP) {
327c60d41a9Swiz setloop = 1;
328c60d41a9Swiz k_set_loop(TRUE);
329c60d41a9Swiz }
330c60d41a9Swiz }
331c60d41a9Swiz
332c60d41a9Swiz bzero(&sdst, sizeof(sdst));
333c60d41a9Swiz sdst.sin_family = AF_INET;
334c60d41a9Swiz #if (defined(BSD) && (BSD >= 199103))
335c60d41a9Swiz sdst.sin_len = sizeof(sdst);
336c60d41a9Swiz #endif
337c60d41a9Swiz sdst.sin_addr.s_addr = dst;
338c60d41a9Swiz if (sendto(igmp_socket, send_buf, ip->ip_len, 0,
339c60d41a9Swiz (struct sockaddr *)&sdst, sizeof(sdst)) < 0) {
340c60d41a9Swiz if (errno == ENETDOWN)
341c60d41a9Swiz check_vif_state();
342c60d41a9Swiz else
343c60d41a9Swiz logit(igmp_log_level(type, code), errno,
344c60d41a9Swiz "sendto to %s on %s",
345ee70b5d6Sdsl inet_fmt(dst), inet_fmt(src));
346c60d41a9Swiz }
347c60d41a9Swiz
348c60d41a9Swiz if (setloop)
349c60d41a9Swiz k_set_loop(FALSE);
350c60d41a9Swiz
351c60d41a9Swiz logit(LOG_DEBUG, 0, "SENT %s from %-15s to %s",
352ee70b5d6Sdsl packet_kind(type, code), inet_fmt(src),
353ee70b5d6Sdsl inet_fmt(dst));
354c60d41a9Swiz }
355