xref: /netbsd-src/usr.sbin/mrouted/igmp.c (revision 58cb611abb8097a2b7084638a1a04f2f1504424d)
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