Annotation of embedaddon/pimd/igmp.c, revision 1.1.1.1
1.1 misho 1: /*
2: * Copyright (c) 1998-2001
3: * University of Southern California/Information Sciences Institute.
4: * All rights reserved.
5: *
6: * Redistribution and use in source and binary forms, with or without
7: * modification, are permitted provided that the following conditions
8: * are met:
9: * 1. Redistributions of source code must retain the above copyright
10: * notice, this list of conditions and the following disclaimer.
11: * 2. Redistributions in binary form must reproduce the above copyright
12: * notice, this list of conditions and the following disclaimer in the
13: * documentation and/or other materials provided with the distribution.
14: * 3. Neither the name of the project nor the names of its contributors
15: * may be used to endorse or promote products derived from this software
16: * without specific prior written permission.
17: *
18: * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
19: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21: * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
22: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28: * SUCH DAMAGE.
29: */
30: /*
31: * $Id: igmp.c,v 1.18 2002/09/26 00:59:29 pavlin Exp $
32: */
33: /*
34: * Part of this program has been derived from mrouted.
35: * The mrouted program is covered by the license in the accompanying file
36: * named "LICENSE.mrouted".
37: *
38: * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
39: * Leland Stanford Junior University.
40: *
41: */
42:
43: #include "defs.h"
44:
45: /*
46: * Exported variables.
47: */
48: char *igmp_recv_buf; /* input packet buffer */
49: char *igmp_send_buf; /* output packet buffer */
50: int igmp_socket; /* socket for all network I/O */
51: uint32_t allhosts_group; /* allhosts addr in net order */
52: uint32_t allrouters_group; /* All-Routers addr in net order */
53: uint32_t allreports_group; /* All IGMP routers in net order */
54:
55: #ifdef RAW_OUTPUT_IS_RAW
56: extern int curttl;
57: #endif /* RAW_OUTPUT_IS_RAW */
58:
59: /*
60: * Local functions definitions.
61: */
62: static void igmp_read (int i, fd_set *rfd);
63: static void accept_igmp (ssize_t recvlen);
64:
65:
66: /*
67: * Open and initialize the igmp socket, and fill in the non-changing
68: * IP header fields in the output packet buffer.
69: */
70: void init_igmp(void)
71: {
72: struct ip *ip;
73: char *router_alert;
74:
75: igmp_recv_buf = calloc(1, RECV_BUF_SIZE);
76: igmp_send_buf = calloc(1, SEND_BUF_SIZE);
77: if (!igmp_recv_buf || !igmp_send_buf)
78: logit(LOG_ERR, 0, "Ran out of memory in init_igmp()");
79:
80: if ((igmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP)) < 0)
81: logit(LOG_ERR, errno, "Failed creating IGMP socket in init_igmp()");
82:
83: k_hdr_include(igmp_socket, TRUE); /* include IP header when sending */
84: k_set_sndbuf(igmp_socket, SO_SEND_BUF_SIZE_MAX,
85: SO_SEND_BUF_SIZE_MIN); /* lots of output buffering */
86: k_set_rcvbuf(igmp_socket, SO_RECV_BUF_SIZE_MAX,
87: SO_RECV_BUF_SIZE_MIN); /* lots of input buffering */
88: k_set_ttl(igmp_socket, MINTTL); /* restrict multicasts to one hop */
89: k_set_loop(igmp_socket, FALSE); /* disable multicast loopback */
90:
91: ip = (struct ip *)igmp_send_buf;
92: memset(ip, 0, IP_IGMP_HEADER_LEN);
93: ip->ip_v = IPVERSION;
94: ip->ip_hl = IP_IGMP_HEADER_LEN >> 2;
95: ip->ip_tos = 0xc0; /* Internet Control */
96: ip->ip_id = 0; /* let kernel fill in */
97: ip->ip_off = 0;
98: ip->ip_ttl = MAXTTL; /* applies to unicasts only */
99: ip->ip_p = IPPROTO_IGMP;
100: ip->ip_sum = 0; /* let kernel fill in */
101:
102: /* Enable RFC2113 IP Router Alert. Per spec this is required to
103: * force certain routers/switches to inspect this frame. */
104: router_alert = igmp_send_buf + sizeof(struct ip);
105: router_alert[0] = IPOPT_RA;
106: router_alert[1] = 4;
107: router_alert[2] = 0;
108: router_alert[3] = 0;
109:
110: /* Everywhere in the daemon we use network-byte-order */
111: allhosts_group = htonl(INADDR_ALLHOSTS_GROUP);
112: allrouters_group = htonl(INADDR_ALLRTRS_GROUP);
113: allreports_group = htonl(INADDR_ALLRPTS_GROUP);
114:
115: if (register_input_handler(igmp_socket, igmp_read) < 0)
116: logit(LOG_ERR, 0, "Failed registering igmp_read() as an input handler in init_igmp()");
117: }
118:
119:
120: /* Read an IGMP message */
121: static void igmp_read(int i __attribute__((unused)), fd_set *rfd __attribute__((unused)))
122: {
123: ssize_t len;
124: socklen_t dummy = 0;
125:
126: while ((len = recvfrom(igmp_socket, igmp_recv_buf, RECV_BUF_SIZE, 0, NULL, &dummy)) < 0) {
127: if (errno == EINTR)
128: continue; /* Received signal, retry syscall. */
129:
130: logit(LOG_ERR, errno, "Failed recvfrom() in igmp_read()");
131: return;
132: }
133:
134: accept_igmp(len);
135: }
136:
137: /*
138: * Process a newly received IGMP packet that is sitting in the input
139: * packet buffer.
140: */
141: static void accept_igmp(ssize_t recvlen)
142: {
143: int ipdatalen, iphdrlen, igmpdatalen;
144: uint32_t src, dst, group;
145: struct ip *ip;
146: struct igmp *igmp;
147: int igmp_version = 3;
148:
149: if (recvlen < (ssize_t)sizeof(struct ip)) {
150: logit(LOG_WARNING, 0, "Received IGMP packet too short (%u bytes) for IP header", recvlen);
151: return;
152: }
153:
154: ip = (struct ip *)igmp_recv_buf;
155: src = ip->ip_src.s_addr;
156: dst = ip->ip_dst.s_addr;
157:
158: /* packets sent up from kernel to daemon have ip->ip_p = 0 */
159: if (ip->ip_p == 0) {
160: #if 0 /* XXX */
161: if (src == 0 || dst == 0)
162: logit(LOG_WARNING, 0, "Kernel request not accurate, src %s dst %s",
163: inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2)));
164: else
165: #endif
166: process_kernel_call();
167: return;
168: }
169:
170: iphdrlen = ip->ip_hl << 2;
171: #if 0
172: #ifdef HAVE_IP_HDRINCL_BSD_ORDER
173: #ifdef __NetBSD__
174: ipdatalen = ip->ip_len; /* The NetBSD kernel subtracts hlen for us, unfortunately. */
175: #else
176: ipdatalen = ip->ip_len - iphdrlen;
177: #endif
178: #else
179: ipdatalen = ntohs(ip->ip_len) - iphdrlen;
180: #endif
181: #else /* !0 */
182: ipdatalen = recvlen - iphdrlen;
183: #endif /* O */
184:
185: if (iphdrlen + ipdatalen != recvlen) {
186: logit(LOG_WARNING, 0, "Received packet from %s shorter (%u bytes) than hdr+data length (%u+%u)",
187: inet_fmt(src, s1, sizeof(s1)), recvlen, iphdrlen, ipdatalen);
188: return;
189: }
190:
191: igmp = (struct igmp *)(igmp_recv_buf + iphdrlen);
192: group = igmp->igmp_group.s_addr;
193: igmpdatalen = ipdatalen - IGMP_MINLEN;
194:
195: if (igmpdatalen < 0) {
196: logit(LOG_WARNING, 0, "Received IP data field too short (%u bytes) for IGMP, from %s",
197: ipdatalen, inet_fmt(src, s1, sizeof(s1)));
198: return;
199: }
200:
201: IF_DEBUG(DEBUG_IGMP)
202: logit(LOG_DEBUG, 0, "Received %s from %s to %s",
203: packet_kind(IPPROTO_IGMP, igmp->igmp_type, igmp->igmp_code),
204: inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2)));
205:
206: switch (igmp->igmp_type) {
207: case IGMP_MEMBERSHIP_QUERY:
208: /* RFC 3376:7.1 */
209: if (ipdatalen == 8) {
210: if (igmp->igmp_code == 0)
211: igmp_version = 1;
212: else
213: igmp_version = 2;
214: } else if (ipdatalen >= 12) {
215: igmp_version = 3;
216: } else {
217: logit(LOG_DEBUG, 0, "Received invalid IGMP Membership query: Max Resp Code = %d, length = %d",
218: igmp->igmp_code, ipdatalen);
219: }
220: accept_membership_query(src, dst, group, igmp->igmp_code, igmp_version);
221: return;
222:
223: case IGMP_V1_MEMBERSHIP_REPORT:
224: case IGMP_V2_MEMBERSHIP_REPORT:
225: accept_group_report(src, dst, group, igmp->igmp_type);
226: return;
227:
228: case IGMP_V2_LEAVE_GROUP:
229: accept_leave_message(src, dst, group);
230: return;
231:
232: case IGMP_V3_MEMBERSHIP_REPORT:
233: if (igmpdatalen < IGMP_V3_GROUP_RECORD_MIN_SIZE) {
234: logit(LOG_DEBUG, 0, "Too short IGMP v3 Membership report: igmpdatalen(%d) < MIN(%d)", igmpdatalen, IGMP_V3_GROUP_RECORD_MIN_SIZE);
235: return;
236: }
237: accept_membership_report(src, dst, (struct igmpv3_report *)(igmp_recv_buf + iphdrlen), recvlen - iphdrlen);
238: return;
239:
240: case IGMP_DVMRP:
241: /* XXX: TODO: most of the stuff below is not implemented. We are still
242: * only PIM router.
243: */
244: group = ntohl(group);
245:
246: switch (igmp->igmp_code) {
247: case DVMRP_PROBE:
248: dvmrp_accept_probe(src, dst, (uint8_t *)(igmp+1), igmpdatalen, group);
249: return;
250:
251: case DVMRP_REPORT:
252: dvmrp_accept_report(src, dst, (uint8_t *)(igmp+1), igmpdatalen, group);
253: return;
254:
255: case DVMRP_ASK_NEIGHBORS:
256: accept_neighbor_request(src, dst);
257: return;
258:
259: case DVMRP_ASK_NEIGHBORS2:
260: accept_neighbor_request2(src, dst);
261: return;
262:
263: case DVMRP_NEIGHBORS:
264: dvmrp_accept_neighbors(src, dst, (uint8_t *)(igmp+1), igmpdatalen, group);
265: return;
266:
267: case DVMRP_NEIGHBORS2:
268: dvmrp_accept_neighbors2(src, dst, (uint8_t *)(igmp+1), igmpdatalen, group);
269: return;
270:
271: case DVMRP_PRUNE:
272: dvmrp_accept_prune(src, dst, (uint8_t *)(igmp+1), igmpdatalen);
273: return;
274:
275: case DVMRP_GRAFT:
276: dvmrp_accept_graft(src, dst, (uint8_t *)(igmp+1), igmpdatalen);
277: return;
278:
279: case DVMRP_GRAFT_ACK:
280: dvmrp_accept_g_ack(src, dst, (uint8_t *)(igmp+1), igmpdatalen);
281: return;
282:
283: case DVMRP_INFO_REQUEST:
284: dvmrp_accept_info_request(src, dst, (uint8_t *)(igmp+1), igmpdatalen);
285: return;
286:
287: case DVMRP_INFO_REPLY:
288: dvmrp_accept_info_reply(src, dst, (uint8_t *)(igmp+1), igmpdatalen);
289: return;
290:
291: default:
292: logit(LOG_INFO, 0, "Ignoring unknown DVMRP message code %u from %s to %s",
293: igmp->igmp_code, inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2)));
294: return;
295: }
296:
297: case IGMP_PIM:
298: return; /* TODO: this is PIM v1 message. Handle it?. */
299:
300: case IGMP_MTRACE_RESP:
301: return; /* TODO: implement it */
302:
303: case IGMP_MTRACE:
304: accept_mtrace(src, dst, group, (char *)(igmp+1), igmp->igmp_code, igmpdatalen);
305: return;
306:
307: default:
308: logit(LOG_INFO, 0, "Ignoring unknown IGMP message type %x from %s to %s",
309: igmp->igmp_type, inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2)));
310: return;
311: }
312: }
313:
314: static void send_ip_frame(uint32_t src, uint32_t dst, int type, int code, char *buf, size_t len)
315: {
316: int setloop = 0;
317: struct ip *ip;
318: struct sockaddr_in sin;
319: char source[20], dest[20];
320:
321: /* Prepare the IP header */
322: len += IP_IGMP_HEADER_LEN;
323: ip = (struct ip *)buf;
324: ip->ip_id = 0; /* let kernel fill in */
325: ip->ip_off = 0;
326: ip->ip_src.s_addr = src;
327: ip->ip_dst.s_addr = dst;
328: #ifdef HAVE_IP_HDRINCL_BSD_ORDER
329: ip->ip_len = len;
330: #else
331: ip->ip_len = htons(len);
332: #endif
333:
334: if (IN_MULTICAST(ntohl(dst))) {
335: k_set_if(igmp_socket, src);
336: if (type != IGMP_DVMRP || dst == allhosts_group) {
337: setloop = 1;
338: k_set_loop(igmp_socket, TRUE);
339: }
340: #ifdef RAW_OUTPUT_IS_RAW
341: ip->ip_ttl = curttl;
342: } else {
343: ip->ip_ttl = MAXTTL;
344: #endif
345: }
346:
347: memset(&sin, 0, sizeof(sin));
348: sin.sin_family = AF_INET;
349: sin.sin_addr.s_addr = dst;
350: #ifdef HAVE_SA_LEN
351: sin.sin_len = sizeof(sin);
352: #endif
353:
354: IF_DEBUG(DEBUG_IGMP)
355: logit(LOG_DEBUG, 0, "Send %s from %s to %s",
356: packet_kind(IPPROTO_IGMP, type, code),
357: src == INADDR_ANY_N ? "INADDR_ANY" :
358: inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2)));
359:
360: while (sendto(igmp_socket, buf, len, 0, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
361: if (errno == EINTR)
362: continue; /* Received signal, retry syscall. */
363: if (errno == ENETDOWN || errno == ENODEV)
364: check_vif_state();
365: else if (errno == EPERM || errno == EHOSTUNREACH)
366: logit(LOG_WARNING, 0, "Not allowed to send IGMP message from %s to %s, possibly firewall"
367: #ifdef __linux__
368: ", or SELinux policy violation,"
369: #endif
370: " related problem."
371: ,
372: inet_fmt(src, source, sizeof(source)), inet_fmt(dst, dest, sizeof(dest)));
373: else
374: logit(log_level(IPPROTO_IGMP, type, code), errno, "Sendto to %s on %s",
375: inet_fmt(dst, s1, sizeof(s1)), inet_fmt(src, s2, sizeof(s2)));
376:
377: if (setloop)
378: k_set_loop(igmp_socket, FALSE);
379:
380: return;
381: }
382:
383: if (setloop)
384: k_set_loop(igmp_socket, FALSE);
385:
386: IF_DEBUG(DEBUG_PKT | debug_kind(IPPROTO_IGMP, type, code)) {
387: logit(LOG_DEBUG, 0, "SENT %5d bytes %s from %-15s to %s", len,
388: packet_kind(IPPROTO_IGMP, type, code),
389: src == INADDR_ANY_N
390: ? "INADDR_ANY"
391: : inet_fmt(src, s1, sizeof(s1)),
392: inet_fmt(dst, s2, sizeof(s2)));
393: }
394: }
395:
396: /*
397: * RFC-3376 states that Max Resp Code (MRC) and Querier's Query Interval Code
398: * (QQIC) should be presented in floating point value if their value exceeds
399: * 128. The following formula is used by IGMPv3 clients to calculate the
400: * actual value of the floating point:
401: *
402: * 0 1 2 3 4 5 6 7
403: * +-+-+-+-+-+-+-+-+
404: * |1| exp | mant |
405: * +-+-+-+-+-+-+-+-+
406: *
407: * QQI / MRT = (mant | 0x10) << (exp + 3)
408: *
409: * This requires us to find the largest set (fls) bit in the 15-bit number
410: * and set the exponent based on its index in the bits 15-8. ie.
411: *
412: * exponent 0: igmp_fls(0000 0000 1000 0010)
413: * exponent 5: igmp_fls(0001 0000 0000 0000)
414: * exponent 7: igmp_fls(0111 0101 0000 0000)
415: *
416: * and set that as the exponent. The mantissa is set to the last 4 bits
417: * remaining after the (3 + exponent) shifts to the right.
418: *
419: * Note!
420: * The numbers 31744-32767 are the maximum we can present with floating
421: * point that has an exponent of 3 and a mantissa of 4. After this the
422: * implementation just wraps around back to zero.
423: */
424: static inline uint8_t igmp_floating_point(unsigned int mantissa)
425: {
426: unsigned int exponent;
427:
428: /* Wrap around numbers larger than 2^15, since those can not be
429: * presented with 7-bit floating point. */
430: mantissa &= 0x00007FFF;
431:
432: /* If top 8 bits are zero. */
433: if (!(mantissa & 0x00007F80))
434: return mantissa;
435:
436: /* Shift the mantissa and mark this code floating point. */
437: mantissa >>= 3;
438: /* At this point the actual exponent (bits 7-5) are still 0, but the
439: * exponent might be incremented below. */
440: exponent = 0x00000080;
441:
442: /* If bits 7-4 are not zero. */
443: if (mantissa & 0x00000F00) {
444: mantissa >>= 4;
445: /* The index of largest set bit is at least 4. */
446: exponent |= 0x00000040;
447: }
448:
449: /* If bits 7-6 OR bits 3-2 are not zero. */
450: if (mantissa & 0x000000C0) {
451: mantissa >>= 2;
452: /* The index of largest set bit is atleast 6 if we shifted the
453: * mantissa earlier or atleast 2 if we did not shift it. */
454: exponent |= 0x00000020;
455: }
456:
457: /* If bit 7 OR bit 3 OR bit 1 is not zero. */
458: if (mantissa & 0x00000020) {
459: mantissa >>= 1;
460: /* The index of largest set bit is atleast 7 if we shifted the
461: * mantissa two times earlier or atleast 3 if we shifted the
462: * mantissa last time or atleast 1 if we did not shift it. */
463: exponent |= 0x00000010;
464: }
465:
466: return exponent | (mantissa & 0x0000000F);
467: }
468:
469: void send_igmp(char *buf, uint32_t src, uint32_t dst, int type, int code, uint32_t group, int datalen)
470: {
471: size_t len = IGMP_MINLEN + datalen;
472: struct igmpv3_query *igmp;
473:
474: igmp = (struct igmpv3_query *)(buf + IP_IGMP_HEADER_LEN);
475: igmp->type = type;
476: if (datalen >= 4)
477: igmp->code = igmp_floating_point(code);
478: else
479: igmp->code = code;
480: igmp->group = group;
481: igmp->csum = 0;
482: igmp->csum = inet_cksum((uint16_t *)igmp, len);
483:
484: if (datalen >= 4) {
485: igmp->qrv = 2;
486: igmp->qqic = igmp_floating_point(igmp_query_interval);
487: }
488:
489: send_ip_frame(src, dst, type, code, buf, len);
490: }
491:
492: /**
493: * Local Variables:
494: * version-control: t
495: * indent-tabs-mode: t
496: * c-file-style: "ellemtel"
497: * c-basic-offset: 4
498: * End:
499: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>