|
|
1.1 root 1: /*
2: * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3: *
4: * @APPLE_LICENSE_HEADER_START@
5: *
6: * The contents of this file constitute Original Code as defined in and
7: * are subject to the Apple Public Source License Version 1.1 (the
8: * "License"). You may not use this file except in compliance with the
9: * License. Please obtain a copy of the License at
10: * http://www.apple.com/publicsource and read it before using this file.
11: *
12: * This Original Code and all software distributed under the License are
13: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17: * License for the specific language governing rights and limitations
18: * under the License.
19: *
20: * @APPLE_LICENSE_HEADER_END@
21: */
22: /*
23: * Copyright (c) 1988 Stephen Deering.
24: * Copyright (c) 1992, 1993
25: * The Regents of the University of California. All rights reserved.
26: *
27: * This code is derived from software contributed to Berkeley by
28: * Stephen Deering of Stanford University.
29: *
30: * Redistribution and use in source and binary forms, with or without
31: * modification, are permitted provided that the following conditions
32: * are met:
33: * 1. Redistributions of source code must retain the above copyright
34: * notice, this list of conditions and the following disclaimer.
35: * 2. Redistributions in binary form must reproduce the above copyright
36: * notice, this list of conditions and the following disclaimer in the
37: * documentation and/or other materials provided with the distribution.
38: * 3. All advertising materials mentioning features or use of this software
39: * must display the following acknowledgement:
40: * This product includes software developed by the University of
41: * California, Berkeley and its contributors.
42: * 4. Neither the name of the University nor the names of its contributors
43: * may be used to endorse or promote products derived from this software
44: * without specific prior written permission.
45: *
46: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
47: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
48: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
49: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
50: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
51: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
52: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
53: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
54: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
55: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
56: * SUCH DAMAGE.
57: *
58: * @(#)igmp.c 8.1 (Berkeley) 7/19/93
59: */
60:
61: /*
62: * Internet Group Management Protocol (IGMP) routines.
63: *
64: * Written by Steve Deering, Stanford, May 1988.
65: * Modified by Rosen Sharma, Stanford, Aug 1994.
66: * Modified by Bill Fenner, Xerox PARC, Feb 1995.
67: * Modified to fully comply to IGMPv2 by Bill Fenner, Oct 1995.
68: *
69: * MULTICAST Revision: 3.5.1.4
70: */
71:
72: #include <sys/param.h>
73: #include <sys/systm.h>
74: #include <sys/malloc.h>
75: #include <sys/mbuf.h>
76: #include <sys/socket.h>
77: #include <sys/protosw.h>
78: #include <sys/kernel.h>
79: #include <sys/sysctl.h>
80:
81: #include <net/if.h>
82: #include <net/route.h>
83:
84: #include <netinet/in.h>
85: #include <netinet/in_var.h>
86: #include <netinet/in_systm.h>
87: #include <netinet/ip.h>
88: #include <netinet/ip_var.h>
89: #include <netinet/igmp.h>
90: #include <netinet/igmp_var.h>
91:
92: static MALLOC_DEFINE(M_IGMP, "igmp", "igmp state");
93:
94: static struct router_info *
95: find_rti __P((struct ifnet *ifp));
96:
97: static struct igmpstat igmpstat;
98:
99: SYSCTL_STRUCT(_net_inet_igmp, IGMPCTL_STATS, stats, CTLFLAG_RD,
100: &igmpstat, igmpstat, "");
101:
102: static int igmp_timers_are_running;
103: static u_long igmp_all_hosts_group;
104: static u_long igmp_all_rtrs_group;
105: static struct mbuf *router_alert;
106: static struct router_info *Head;
107:
108: static void igmp_sendpkt __P((struct in_multi *, int, unsigned long));
109:
110: void
111: igmp_init()
112: {
113: struct ipoption *ra;
114:
115: /*
116: * To avoid byte-swapping the same value over and over again.
117: */
118: igmp_all_hosts_group = htonl(INADDR_ALLHOSTS_GROUP);
119: igmp_all_rtrs_group = htonl(INADDR_ALLRTRS_GROUP);
120:
121: igmp_timers_are_running = 0;
122:
123: /*
124: * Construct a Router Alert option to use in outgoing packets
125: */
126: MGET(router_alert, M_DONTWAIT, MT_DATA);
127: ra = mtod(router_alert, struct ipoption *);
128: ra->ipopt_dst.s_addr = 0;
129: ra->ipopt_list[0] = IPOPT_RA; /* Router Alert Option */
130: ra->ipopt_list[1] = 0x04; /* 4 bytes long */
131: ra->ipopt_list[2] = 0x00;
132: ra->ipopt_list[3] = 0x00;
133: router_alert->m_len = sizeof(ra->ipopt_dst) + ra->ipopt_list[1];
134:
135: Head = (struct router_info *) 0;
136: }
137:
138: static struct router_info *
139: find_rti(ifp)
140: struct ifnet *ifp;
141: {
142: register struct router_info *rti = Head;
143:
144: #if IGMP_DEBUG
145: printf("[igmp.c, _find_rti] --> entering \n");
146: #endif
147: while (rti) {
148: if (rti->rti_ifp == ifp) {
149: #if IGMP_DEBUG
150: printf("[igmp.c, _find_rti] --> found old entry \n");
151: #endif
152: return rti;
153: }
154: rti = rti->rti_next;
155: }
156:
157: #if ISFB31
158: MALLOC(rti, struct router_info *, sizeof *rti, M_IGMP, M_NOWAIT);
159: #else
160: MALLOC(rti, struct router_info *, sizeof *rti, M_TEMP, M_NOWAIT);
161: #endif
162: rti->rti_ifp = ifp;
163: rti->rti_type = IGMP_V2_ROUTER;
164: rti->rti_time = 0;
165: rti->rti_next = Head;
166: Head = rti;
167: #if IGMP_DEBUG
168: printf("[igmp.c, _find_rti] --> created an entry \n");
169: #endif
170: return rti;
171: }
172:
173: void
174: igmp_input(m, iphlen)
175: register struct mbuf *m;
176: register int iphlen;
177: {
178: register struct igmp *igmp;
179: register struct ip *ip;
180: register int igmplen;
181: register struct ifnet *ifp = m->m_pkthdr.rcvif;
182: register int minlen;
183: register struct in_multi *inm;
184: register struct in_ifaddr *ia;
185: struct in_multistep step;
186: struct router_info *rti;
187:
188: int timer; /** timer value in the igmp query header **/
189:
190: ++igmpstat.igps_rcv_total;
191:
192: ip = mtod(m, struct ip *);
193: igmplen = ip->ip_len;
194:
195: /*
196: * Validate lengths
197: */
198: if (igmplen < IGMP_MINLEN) {
199: ++igmpstat.igps_rcv_tooshort;
200: m_freem(m);
201: return;
202: }
203: minlen = iphlen + IGMP_MINLEN;
204: if ((m->m_flags & M_EXT || m->m_len < minlen) &&
205: (m = m_pullup(m, minlen)) == 0) {
206: ++igmpstat.igps_rcv_tooshort;
207: return;
208: }
209:
210: /*
211: * Validate checksum
212: */
213: m->m_data += iphlen;
214: m->m_len -= iphlen;
215: igmp = mtod(m, struct igmp *);
216: if (in_cksum(m, igmplen)) {
217: ++igmpstat.igps_rcv_badsum;
218: m_freem(m);
219: return;
220: }
221: m->m_data -= iphlen;
222: m->m_len += iphlen;
223:
224: ip = mtod(m, struct ip *);
225: timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE;
226: if (timer == 0)
227: timer = 1;
228: rti = find_rti(ifp);
229:
230: /*
231: * In the IGMPv2 specification, there are 3 states and a flag.
232: *
233: * In Non-Member state, we simply don't have a membership record.
234: * In Delaying Member state, our timer is running (inm->inm_timer)
235: * In Idle Member state, our timer is not running (inm->inm_timer==0)
236: *
237: * The flag is inm->inm_state, it is set to IGMP_OTHERMEMBER if
238: * we have heard a report from another member, or IGMP_IREPORTEDLAST
239: * if I sent the last report.
240: */
241: switch (igmp->igmp_type) {
242:
243: case IGMP_MEMBERSHIP_QUERY:
244: ++igmpstat.igps_rcv_queries;
245:
246: if (ifp->if_flags & IFF_LOOPBACK)
247: break;
248:
249: if (igmp->igmp_code == 0) {
250: /*
251: * Old router. Remember that the querier on this
252: * interface is old, and set the timer to the
253: * value in RFC 1112.
254: */
255:
256: rti->rti_type = IGMP_V1_ROUTER;
257: rti->rti_time = 0;
258:
259: timer = IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ;
260:
261: if (ip->ip_dst.s_addr != igmp_all_hosts_group ||
262: igmp->igmp_group.s_addr != 0) {
263: ++igmpstat.igps_rcv_badqueries;
264: m_freem(m);
265: return;
266: }
267: } else {
268: /*
269: * New router. Simply do the new validity check.
270: */
271:
272: if (igmp->igmp_group.s_addr != 0 &&
273: !IN_MULTICAST(ntohl(igmp->igmp_group.s_addr))) {
274: ++igmpstat.igps_rcv_badqueries;
275: m_freem(m);
276: return;
277: }
278: }
279:
280: /*
281: * - Start the timers in all of our membership records
282: * that the query applies to for the interface on
283: * which the query arrived excl. those that belong
284: * to the "all-hosts" group (224.0.0.1).
285: * - Restart any timer that is already running but has
286: * a value longer than the requested timeout.
287: * - Use the value specified in the query message as
288: * the maximum timeout.
289: */
290: IN_FIRST_MULTI(step, inm);
291: while (inm != NULL) {
292: if (inm->inm_ifp == ifp &&
293: inm->inm_addr.s_addr != igmp_all_hosts_group &&
294: (igmp->igmp_group.s_addr == 0 ||
295: igmp->igmp_group.s_addr == inm->inm_addr.s_addr)) {
296: if (inm->inm_timer == 0 ||
297: inm->inm_timer > timer) {
298: inm->inm_timer =
299: IGMP_RANDOM_DELAY(timer);
300: igmp_timers_are_running = 1;
301: }
302: }
303: IN_NEXT_MULTI(step, inm);
304: }
305:
306: break;
307:
308: case IGMP_V1_MEMBERSHIP_REPORT:
309: case IGMP_V2_MEMBERSHIP_REPORT:
310: /*
311: * For fast leave to work, we have to know that we are the
312: * last person to send a report for this group. Reports
313: * can potentially get looped back if we are a multicast
314: * router, so discard reports sourced by me.
315: */
316: IFP_TO_IA(ifp, ia);
317: if (ia && ip->ip_src.s_addr == IA_SIN(ia)->sin_addr.s_addr)
318: break;
319:
320: ++igmpstat.igps_rcv_reports;
321:
322: if (ifp->if_flags & IFF_LOOPBACK)
323: break;
324:
325: if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr))) {
326: ++igmpstat.igps_rcv_badreports;
327: m_freem(m);
328: return;
329: }
330:
331: /*
332: * KLUDGE: if the IP source address of the report has an
333: * unspecified (i.e., zero) subnet number, as is allowed for
334: * a booting host, replace it with the correct subnet number
335: * so that a process-level multicast routing demon can
336: * determine which subnet it arrived from. This is necessary
337: * to compensate for the lack of any way for a process to
338: * determine the arrival interface of an incoming packet.
339: */
340: if ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) == 0)
341: if (ia) ip->ip_src.s_addr = htonl(ia->ia_subnet);
342:
343: /*
344: * If we belong to the group being reported, stop
345: * our timer for that group.
346: */
347: IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
348:
349: if (inm != NULL) {
350: inm->inm_timer = 0;
351: ++igmpstat.igps_rcv_ourreports;
352:
353: inm->inm_state = IGMP_OTHERMEMBER;
354: }
355:
356: break;
357: }
358:
359: /*
360: * Pass all valid IGMP packets up to any process(es) listening
361: * on a raw IGMP socket.
362: */
363: rip_input(m, iphlen);
364: }
365:
366: void
367: igmp_joingroup(inm)
368: struct in_multi *inm;
369: {
370: int s = splnet();
371:
372: if (inm->inm_addr.s_addr == igmp_all_hosts_group
373: || inm->inm_ifp->if_flags & IFF_LOOPBACK) {
374: inm->inm_timer = 0;
375: inm->inm_state = IGMP_OTHERMEMBER;
376: } else {
377: inm->inm_rti = find_rti(inm->inm_ifp);
378: igmp_sendpkt(inm, inm->inm_rti->rti_type, 0);
379: inm->inm_timer = IGMP_RANDOM_DELAY(
380: IGMP_MAX_HOST_REPORT_DELAY*PR_FASTHZ);
381: inm->inm_state = IGMP_IREPORTEDLAST;
382: igmp_timers_are_running = 1;
383: }
384: splx(s);
385: }
386:
387: void
388: igmp_leavegroup(inm)
389: struct in_multi *inm;
390: {
391: if (inm->inm_state == IGMP_IREPORTEDLAST &&
392: inm->inm_addr.s_addr != igmp_all_hosts_group &&
393: !(inm->inm_ifp->if_flags & IFF_LOOPBACK) &&
394: inm->inm_rti->rti_type != IGMP_V1_ROUTER)
395: igmp_sendpkt(inm, IGMP_V2_LEAVE_GROUP, igmp_all_rtrs_group);
396: }
397:
398: void
399: igmp_fasttimo()
400: {
401: register struct in_multi *inm;
402: struct in_multistep step;
403: int s;
404:
405: /*
406: * Quick check to see if any work needs to be done, in order
407: * to minimize the overhead of fasttimo processing.
408: */
409:
410: if (!igmp_timers_are_running)
411: return;
412:
413: s = splnet();
414: igmp_timers_are_running = 0;
415: IN_FIRST_MULTI(step, inm);
416: while (inm != NULL) {
417: if (inm->inm_timer == 0) {
418: /* do nothing */
419: } else if (--inm->inm_timer == 0) {
420: igmp_sendpkt(inm, inm->inm_rti->rti_type, 0);
421: inm->inm_state = IGMP_IREPORTEDLAST;
422: } else {
423: igmp_timers_are_running = 1;
424: }
425: IN_NEXT_MULTI(step, inm);
426: }
427: splx(s);
428: }
429:
430: void
431: igmp_slowtimo()
432: {
433: int s = splnet();
434: register struct router_info *rti = Head;
435:
436: #if IGMP_DEBUG
437: printf("[igmp.c,_slowtimo] -- > entering \n");
438: #endif
439: while (rti) {
440: if (rti->rti_type == IGMP_V1_ROUTER) {
441: rti->rti_time++;
442: if (rti->rti_time >= IGMP_AGE_THRESHOLD) {
443: rti->rti_type = IGMP_V2_ROUTER;
444: }
445: }
446: rti = rti->rti_next;
447: }
448: #if IGMP_DEBUG
449: printf("[igmp.c,_slowtimo] -- > exiting \n");
450: #endif
451: splx(s);
452: }
453:
454: static struct route igmprt;
455:
456: static void
457: igmp_sendpkt(inm, type, addr)
458: struct in_multi *inm;
459: int type;
460: unsigned long addr;
461: {
462: struct mbuf *m;
463: struct igmp *igmp;
464: struct ip *ip;
465: struct ip_moptions imo;
466:
467: MGETHDR(m, M_DONTWAIT, MT_HEADER);
468: if (m == NULL)
469: return;
470:
471: m->m_pkthdr.rcvif = loif;
472: m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN;
473: MH_ALIGN(m, IGMP_MINLEN + sizeof(struct ip));
474: m->m_data += sizeof(struct ip);
475: m->m_len = IGMP_MINLEN;
476: igmp = mtod(m, struct igmp *);
477: igmp->igmp_type = type;
478: igmp->igmp_code = 0;
479: igmp->igmp_group = inm->inm_addr;
480: igmp->igmp_cksum = 0;
481: igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN);
482:
483: m->m_data -= sizeof(struct ip);
484: m->m_len += sizeof(struct ip);
485: ip = mtod(m, struct ip *);
486: ip->ip_tos = 0;
487: ip->ip_len = sizeof(struct ip) + IGMP_MINLEN;
488: ip->ip_off = 0;
489: ip->ip_p = IPPROTO_IGMP;
490: ip->ip_src.s_addr = INADDR_ANY;
491: ip->ip_dst.s_addr = addr ? addr : igmp->igmp_group.s_addr;
492:
493: imo.imo_multicast_ifp = inm->inm_ifp;
494: imo.imo_multicast_ttl = 1;
495: imo.imo_multicast_vif = -1;
496: /*
497: * Request loopback of the report if we are acting as a multicast
498: * router, so that the process-level routing demon can hear it.
499: */
500: imo.imo_multicast_loop = (ip_mrouter != NULL);
501:
502: /*
503: * XXX
504: * Do we have to worry about reentrancy here? Don't think so.
505: */
506: ip_output(m, router_alert, &igmprt, 0, &imo);
507:
508: ++igmpstat.igps_snd_reports;
509: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.