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 #include "defs.h"
11 
12 /*
13  * Exported variables.
14  */
15 char		*recv_buf; 		     /* input packet buffer         */
16 char		*send_buf; 		     /* output packet buffer        */
17 int		igmp_socket;		     /* socket for all network I/O  */
18 uint32_t		allhosts_group;		     /* All hosts addr in net order */
19 uint32_t		allrtrs_group;		     /* All-Routers "  in net order */
20 uint32_t		dvmrp_group;		     /* DVMRP grp addr in net order */
21 uint32_t		dvmrp_genid;		     /* IGMP generation id          */
22 
23 /*
24  * Local function definitions.
25  */
26 /* uint8_t promoted to uint32_t */
27 static int	igmp_log_level(uint32_t type, uint32_t code);
28 
29 /*
30  * Open and initialize the igmp socket, and fill in the non-changing
31  * IP header fields in the output packet buffer.
32  */
init_igmp(void)33 void init_igmp(void)
34 {
35     struct ip *ip;
36 
37     recv_buf = malloc(RECV_BUF_SIZE);
38     send_buf = malloc(RECV_BUF_SIZE);
39 
40     if ((igmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP)) < 0)
41 	logit(LOG_ERR, errno, "IGMP socket");
42 
43     k_hdr_include(TRUE);	/* include IP header when sending */
44     k_set_rcvbuf(256*1024,48*1024);	/* lots of input buffering        */
45     k_set_ttl(1);		/* restrict multicasts to one hop */
46     k_set_loop(FALSE);		/* disable multicast loopback     */
47 
48     ip         = (struct ip *)send_buf;
49     memset(ip, 0, sizeof(struct ip));
50     /*
51      * Fields zeroed that aren't filled in later:
52      * - IP ID (let the kernel fill it in)
53      * - Offset (we don't send fragments)
54      * - Checksum (let the kernel fill it in)
55      */
56     ip->ip_v   = IPVERSION;
57     ip->ip_hl  = sizeof(struct ip) >> 2;
58     ip->ip_tos = 0xc0;		/* Internet Control */
59     ip->ip_ttl = MAXTTL;	/* applies to unicasts only */
60     ip->ip_p   = IPPROTO_IGMP;
61 
62     allhosts_group = htonl(INADDR_ALLHOSTS_GROUP);
63     dvmrp_group    = htonl(INADDR_DVMRP_GROUP);
64     allrtrs_group  = htonl(INADDR_ALLRTRS_GROUP);
65 }
66 
67 #define PIM_QUERY        0
68 #define PIM_REGISTER     1
69 #define PIM_REGISTER_STOP 	2
70 #define PIM_JOIN_PRUNE   3
71 #define PIM_RP_REACHABLE 4
72 #define PIM_ASSERT       5
73 #define PIM_GRAFT        6
74 #define PIM_GRAFT_ACK    7
75 
igmp_packet_kind(uint32_t type,uint32_t code)76 char *igmp_packet_kind(uint32_t type, uint32_t code)
77 {
78     static char unknown[20];
79 
80     switch (type) {
81 	case IGMP_MEMBERSHIP_QUERY:		return "membership query  ";
82 	case IGMP_V1_MEMBERSHIP_REPORT:		return "V1 member report  ";
83 	case IGMP_V2_MEMBERSHIP_REPORT:		return "V2 member report  ";
84 	case IGMP_V2_LEAVE_GROUP:		return "leave message     ";
85 	case IGMP_DVMRP:
86 	  switch (code) {
87 	    case DVMRP_PROBE:			return "neighbor probe    ";
88 	    case DVMRP_REPORT:			return "route report      ";
89 	    case DVMRP_ASK_NEIGHBORS:		return "neighbor request  ";
90 	    case DVMRP_NEIGHBORS:		return "neighbor list     ";
91 	    case DVMRP_ASK_NEIGHBORS2:		return "neighbor request 2";
92 	    case DVMRP_NEIGHBORS2:		return "neighbor list 2   ";
93 	    case DVMRP_PRUNE:			return "prune message     ";
94 	    case DVMRP_GRAFT:			return "graft message     ";
95 	    case DVMRP_GRAFT_ACK:		return "graft message ack ";
96 	    case DVMRP_INFO_REQUEST:		return "info request      ";
97 	    case DVMRP_INFO_REPLY:		return "info reply        ";
98 	    default:
99 		    snprintf(unknown, sizeof(unknown), "unknown DVMRP %3d ", code);
100 		    return unknown;
101 	  }
102  	case IGMP_PIM:
103  	  switch (code) {
104  	    case PIM_QUERY:			return "PIM Router-Query  ";
105  	    case PIM_REGISTER:			return "PIM Register      ";
106  	    case PIM_REGISTER_STOP:		return "PIM Register-Stop ";
107  	    case PIM_JOIN_PRUNE:		return "PIM Join/Prune    ";
108  	    case PIM_RP_REACHABLE:		return "PIM RP-Reachable  ";
109  	    case PIM_ASSERT:			return "PIM Assert        ";
110  	    case PIM_GRAFT:			return "PIM Graft         ";
111  	    case PIM_GRAFT_ACK:			return "PIM Graft-Ack     ";
112  	    default:
113  		    snprintf(unknown, sizeof(unknown), "unknown PIM msg%3d", code);
114 		    return unknown;
115  	  }
116 	case IGMP_MTRACE:			return "IGMP trace query  ";
117 	case IGMP_MTRACE_RESP:			return "IGMP trace reply  ";
118 	default:
119 		snprintf(unknown, sizeof(unknown), "unk: 0x%02x/0x%02x    ", type, code);
120 		return unknown;
121     }
122 }
123 
igmp_debug_kind(uint32_t type,uint32_t code)124 int igmp_debug_kind(uint32_t type, uint32_t code)
125 {
126     switch (type) {
127 	case IGMP_MEMBERSHIP_QUERY:		return DEBUG_IGMP;
128 	case IGMP_V1_MEMBERSHIP_REPORT:		return DEBUG_IGMP;
129 	case IGMP_V2_MEMBERSHIP_REPORT:		return DEBUG_IGMP;
130 	case IGMP_V2_LEAVE_GROUP:		return DEBUG_IGMP;
131 	case IGMP_DVMRP:
132 	  switch (code) {
133 	    case DVMRP_PROBE:			return DEBUG_PEER;
134 	    case DVMRP_REPORT:			return DEBUG_ROUTE;
135 	    case DVMRP_ASK_NEIGHBORS:		return 0;
136 	    case DVMRP_NEIGHBORS:		return 0;
137 	    case DVMRP_ASK_NEIGHBORS2:		return 0;
138 	    case DVMRP_NEIGHBORS2:		return 0;
139 	    case DVMRP_PRUNE:			return DEBUG_PRUNE;
140 	    case DVMRP_GRAFT:			return DEBUG_PRUNE;
141 	    case DVMRP_GRAFT_ACK:		return DEBUG_PRUNE;
142 	    case DVMRP_INFO_REQUEST:		return 0;
143 	    case DVMRP_INFO_REPLY:		return 0;
144 	    default:				return 0;
145 	  }
146 	case IGMP_PIM:
147 	  switch (code) {
148 	    case PIM_QUERY:			return 0;
149 	    case PIM_REGISTER:			return 0;
150 	    case PIM_REGISTER_STOP:		return 0;
151 	    case PIM_JOIN_PRUNE:		return 0;
152 	    case PIM_RP_REACHABLE:		return 0;
153 	    case PIM_ASSERT:			return 0;
154 	    case PIM_GRAFT:			return 0;
155 	    case PIM_GRAFT_ACK:			return 0;
156 	    default:				return 0;
157 	  }
158 	case IGMP_MTRACE:			return DEBUG_TRACE;
159 	case IGMP_MTRACE_RESP:			return DEBUG_TRACE;
160 	default:				return DEBUG_IGMP;
161     }
162 }
163 
164 /*
165  * Process a newly received IGMP packet that is sitting in the input
166  * packet buffer.
167  */
accept_igmp(size_t recvlen)168 void accept_igmp(size_t recvlen)
169 {
170     uint32_t src, dst, group;
171     struct ip *ip;
172     struct igmp *igmp;
173     int ipdatalen, iphdrlen, igmpdatalen;
174 
175     if (recvlen < sizeof(struct ip)) {
176 	logit(LOG_WARNING, 0,
177 	    "received packet too short (%u bytes) for IP header", recvlen);
178 	return;
179     }
180 
181     ip        = (struct ip *)recv_buf;
182     src       = ip->ip_src.s_addr;
183     dst       = ip->ip_dst.s_addr;
184 
185     /*
186      * this is most likely a message from the kernel indicating that
187      * a new src grp pair message has arrived and so, it would be
188      * necessary to install a route into the kernel for this.
189      */
190     if (ip->ip_p == 0) {
191 	if (src == 0 || dst == 0)
192 	    logit(LOG_WARNING, 0, "kernel request not accurate");
193 	else
194 	    add_table_entry(src, dst);
195 	return;
196     }
197 
198     iphdrlen  = ip->ip_hl << 2;
199 #ifdef HAVE_IP_HDRINCL_BSD_ORDER
200     ipdatalen = ip->ip_len - iphdrlen;
201 #else
202     ipdatalen = ntohs(ip->ip_len) - iphdrlen;
203 #endif
204     if ((size_t)(iphdrlen + ipdatalen) != recvlen) {
205 	logit(LOG_WARNING, 0,
206 	    "received packet from %s shorter (%u bytes) than hdr+data length (%u+%u)",
207 	    inet_fmt(src, s1, sizeof(s1)), recvlen, iphdrlen, ipdatalen);
208 	return;
209     }
210 
211     igmp        = (struct igmp *)(recv_buf + iphdrlen);
212     group       = igmp->igmp_group.s_addr;
213     igmpdatalen = ipdatalen - IGMP_MINLEN;
214     if (igmpdatalen < 0) {
215 	logit(LOG_WARNING, 0,
216 	    "received IP data field too short (%u bytes) for IGMP, from %s",
217 	    ipdatalen, inet_fmt(src, s1, sizeof(s1)));
218 	return;
219     }
220 
221     IF_DEBUG(DEBUG_PKT|igmp_debug_kind(igmp->igmp_type, igmp->igmp_code)) {
222 	logit(LOG_DEBUG, 0, "RECV %s from %-15s to %s",
223 	      igmp_packet_kind(igmp->igmp_type, igmp->igmp_code),
224 	      inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2)));
225     }
226 
227     switch (igmp->igmp_type) {
228 
229 	case IGMP_MEMBERSHIP_QUERY:
230 	    accept_membership_query(src, dst, group, igmp->igmp_code);
231 	    return;
232 
233 	case IGMP_V1_MEMBERSHIP_REPORT:
234 	case IGMP_V2_MEMBERSHIP_REPORT:
235 	    accept_group_report(src, dst, group, igmp->igmp_type);
236 	    return;
237 
238 	case IGMP_V2_LEAVE_GROUP:
239 	    accept_leave_message(src, dst, group);
240 	    return;
241 
242 	case IGMP_DVMRP:
243 	    group = ntohl(group);
244 
245 	    switch (igmp->igmp_code) {
246 		case DVMRP_PROBE:
247 		    accept_probe(src, dst, (char *)(igmp+1), igmpdatalen, group);
248 		    return;
249 
250 		case DVMRP_REPORT:
251 		    accept_report(src, dst, (char *)(igmp+1), igmpdatalen, group);
252 		    return;
253 
254 		case DVMRP_ASK_NEIGHBORS:
255 		    accept_neighbor_request(src, dst);
256 		    return;
257 
258 		case DVMRP_ASK_NEIGHBORS2:
259 		    accept_neighbor_request2(src, dst);
260 		    return;
261 
262 		case DVMRP_NEIGHBORS:
263 		    accept_neighbors(src, dst, (uint8_t *)(igmp+1), igmpdatalen, group);
264 		    return;
265 
266 		case DVMRP_NEIGHBORS2:
267 		    accept_neighbors2(src, dst, (uint8_t *)(igmp+1), igmpdatalen, group);
268 		    return;
269 
270 		case DVMRP_PRUNE:
271 		    accept_prune(src, dst, (char *)(igmp+1), igmpdatalen);
272 		    return;
273 
274 		case DVMRP_GRAFT:
275 		    accept_graft(src, dst, (char *)(igmp+1), igmpdatalen);
276 		    return;
277 
278 		case DVMRP_GRAFT_ACK:
279 		    accept_g_ack(src, dst, (char *)(igmp+1), igmpdatalen);
280 		    return;
281 
282 		case DVMRP_INFO_REQUEST:
283 		    accept_info_request(src, dst, (uint8_t *)(igmp+1), igmpdatalen);
284 		    return;
285 
286 		case DVMRP_INFO_REPLY:
287 		    accept_info_reply(src, dst, (uint8_t *)(igmp+1), igmpdatalen);
288 		    return;
289 
290 		default:
291 		    logit(LOG_INFO, 0,
292 		     "ignoring unknown DVMRP message code %u from %s to %s",
293 		     igmp->igmp_code, inet_fmt(src, s1, sizeof(s1)),
294 		     inet_fmt(dst, s2, sizeof(s2)));
295 		    return;
296 	    }
297 
298  	case IGMP_PIM:
299  	    return;
300 
301 	case IGMP_MTRACE_RESP:
302 	    return;
303 
304 	case IGMP_MTRACE:
305 	    accept_mtrace(src, dst, group, (char *)(igmp+1),
306 		   igmp->igmp_code, igmpdatalen);
307 	    return;
308 
309 	default:
310 	    logit(LOG_INFO, 0,
311 		"ignoring unknown IGMP message type %x from %s to %s",
312 		igmp->igmp_type, inet_fmt(src, s1, sizeof(s1)),
313 		inet_fmt(dst, s2, sizeof(s2)));
314 	    return;
315     }
316 }
317 
318 /*
319  * Some IGMP messages are more important than others.  This routine
320  * determines the logging level at which to log a send error (often
321  * "No route to host").  This is important when there is asymmetric
322  * reachability and someone is trying to, i.e., mrinfo me periodically.
323  */
igmp_log_level(uint32_t type,uint32_t code)324 static int igmp_log_level(uint32_t type, uint32_t code)
325 {
326     switch (type) {
327 	case IGMP_MTRACE_RESP:
328 	    return LOG_INFO;
329 
330 	case IGMP_DVMRP:
331 	  switch (code) {
332 	    case DVMRP_NEIGHBORS:
333 	    case DVMRP_NEIGHBORS2:
334 		return LOG_INFO;
335 	  }
336     }
337     return LOG_WARNING;
338 }
339 
340 /*
341  * Construct an IGMP message in the output packet buffer.  The caller may
342  * have already placed data in that buffer, of length 'datalen'.
343  */
build_igmp(uint32_t src,uint32_t dst,int type,int code,uint32_t group,int datalen)344 size_t build_igmp(uint32_t src, uint32_t dst, int type, int code, uint32_t group, int datalen)
345 {
346     struct ip *ip;
347     struct igmp *igmp;
348     extern int curttl;
349     size_t len = MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen;
350 
351     ip                      = (struct ip *)send_buf;
352     ip->ip_src.s_addr       = src;
353     ip->ip_dst.s_addr       = dst;
354 #ifdef HAVE_IP_HDRINCL_BSD_ORDER
355     ip->ip_len              = len;
356 #else
357     ip->ip_len              = htons(len);
358 #endif
359     if (IN_MULTICAST(ntohl(dst))) {
360 	ip->ip_ttl = curttl;
361     } else {
362 	ip->ip_ttl = MAXTTL;
363     }
364 
365     igmp                    = (struct igmp *)(send_buf + MIN_IP_HEADER_LEN);
366     igmp->igmp_type         = type;
367     igmp->igmp_code         = code;
368     igmp->igmp_group.s_addr = group;
369     igmp->igmp_cksum        = 0;
370     igmp->igmp_cksum        = inet_cksum((uint16_t *)igmp,
371 					 IGMP_MINLEN + datalen);
372 
373     return len;
374 }
375 
376 /*
377  * Call build_igmp() to build an IGMP message in the output packet buffer.
378  * Then send the message from the interface with IP address 'src' to
379  * destination 'dst'.
380  */
send_igmp(uint32_t src,uint32_t dst,int type,int code,uint32_t group,int datalen)381 void send_igmp(uint32_t src, uint32_t dst, int type, int code, uint32_t group, int datalen)
382 {
383     struct sockaddr_in sdst;
384     int setloop = 0;
385     size_t len;
386 
387     len = build_igmp(src, dst, type, code, group, datalen);
388 
389     if (IN_MULTICAST(ntohl(dst))) {
390 	k_set_if(src);
391 	if (type != IGMP_DVMRP || dst == allhosts_group) {
392 	    setloop = 1;
393 	    k_set_loop(TRUE);
394 	}
395     }
396 
397     memset(&sdst, 0, sizeof(sdst));
398     sdst.sin_family = AF_INET;
399 #ifdef HAVE_SA_LEN
400     sdst.sin_len = sizeof(sdst);
401 #endif
402     sdst.sin_addr.s_addr = dst;
403     if (sendto(igmp_socket, send_buf, len, 0, (struct sockaddr *)&sdst, sizeof(sdst)) < 0) {
404 	if (errno == ENETDOWN)
405 	    check_vif_state();
406 	else
407 	    logit(igmp_log_level(type, code), errno,
408 		"sendto to %s on %s",
409 		inet_fmt(dst, s1, sizeof(s1)), inet_fmt(src, s2, sizeof(s2)));
410     }
411 
412     if (setloop)
413 	    k_set_loop(FALSE);
414 
415     IF_DEBUG(DEBUG_PKT|igmp_debug_kind(type, code))
416     logit(LOG_DEBUG, 0, "SENT %s from %-15s to %s",
417 	igmp_packet_kind(type, code), src == INADDR_ANY ? "INADDR_ANY" :
418 				 inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2)));
419 }
420 
421 /**
422  * Local Variables:
423  *  version-control: t
424  *  indent-tabs-mode: t
425  *  c-file-style: "ellemtel"
426  *  c-basic-offset: 4
427  * End:
428  */
429