Annotation of sys/netinet/igmp.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: igmp.c,v 1.24 2007/07/20 19:00:35 claudio Exp $ */
2: /* $NetBSD: igmp.c,v 1.15 1996/02/13 23:41:25 christos Exp $ */
3:
4: /*
5: * Internet Group Management Protocol (IGMP) routines.
6: *
7: * Written by Steve Deering, Stanford, May 1988.
8: * Modified by Rosen Sharma, Stanford, Aug 1994.
9: * Modified by Bill Fenner, Xerox PARC, Feb 1995.
10: *
11: * MULTICAST Revision: 1.3
12: */
13:
14: #include <sys/param.h>
15: #include <sys/mbuf.h>
16: #include <sys/socket.h>
17: #include <sys/protosw.h>
18:
19: #include <net/if.h>
20: #include <net/route.h>
21:
22: #include <netinet/in.h>
23: #include <netinet/in_var.h>
24: #include <netinet/in_systm.h>
25: #include <netinet/ip.h>
26: #include <netinet/ip_var.h>
27: #include <netinet/igmp.h>
28: #include <netinet/igmp_var.h>
29: #include <dev/rndvar.h>
30:
31: #include <sys/stdarg.h>
32:
33: #define IP_MULTICASTOPTS 0
34:
35: int igmp_timers_are_running;
36: static struct router_info *rti_head;
37: struct igmpstat igmpstat;
38:
39: void igmp_sendpkt(struct in_multi *, int, in_addr_t);
40: int rti_fill(struct in_multi *);
41: struct router_info * rti_find(struct ifnet *);
42:
43: void
44: igmp_init()
45: {
46:
47: /*
48: * To avoid byte-swapping the same value over and over again.
49: */
50: igmp_timers_are_running = 0;
51: rti_head = 0;
52: }
53:
54: /* Return -1 for error. */
55: int
56: rti_fill(inm)
57: struct in_multi *inm;
58: {
59: struct router_info *rti;
60:
61: for (rti = rti_head; rti != 0; rti = rti->rti_next) {
62: if (rti->rti_ifp == inm->inm_ia->ia_ifp) {
63: inm->inm_rti = rti;
64: if (rti->rti_type == IGMP_v1_ROUTER)
65: return (IGMP_v1_HOST_MEMBERSHIP_REPORT);
66: else
67: return (IGMP_v2_HOST_MEMBERSHIP_REPORT);
68: }
69: }
70:
71: rti = (struct router_info *)malloc(sizeof(struct router_info),
72: M_MRTABLE, M_NOWAIT);
73: if (rti == NULL)
74: return (-1);
75: rti->rti_ifp = inm->inm_ia->ia_ifp;
76: rti->rti_type = IGMP_v2_ROUTER;
77: rti->rti_next = rti_head;
78: rti_head = rti;
79: inm->inm_rti = rti;
80: return (IGMP_v2_HOST_MEMBERSHIP_REPORT);
81: }
82:
83: struct router_info *
84: rti_find(ifp)
85: struct ifnet *ifp;
86: {
87: struct router_info *rti;
88:
89: for (rti = rti_head; rti != 0; rti = rti->rti_next) {
90: if (rti->rti_ifp == ifp)
91: return (rti);
92: }
93:
94: rti = (struct router_info *)malloc(sizeof(struct router_info),
95: M_MRTABLE, M_NOWAIT);
96: if (rti == NULL)
97: return (NULL);
98: rti->rti_ifp = ifp;
99: rti->rti_type = IGMP_v2_ROUTER;
100: rti->rti_next = rti_head;
101: rti_head = rti;
102: return (rti);
103: }
104:
105: void
106: rti_delete(ifp)
107: struct ifnet *ifp;
108: {
109: struct router_info *rti, **prti = &rti_head;
110:
111: for (rti = rti_head; rti != 0; rti = rti->rti_next) {
112: if (rti->rti_ifp == ifp) {
113: *prti = rti->rti_next;
114: free(rti, M_MRTABLE);
115: break;
116: }
117: prti = &rti->rti_next;
118: }
119: }
120:
121: void
122: igmp_input(struct mbuf *m, ...)
123: {
124: int iphlen;
125: struct ifnet *ifp = m->m_pkthdr.rcvif;
126: struct ip *ip = mtod(m, struct ip *);
127: struct igmp *igmp;
128: int igmplen;
129: int minlen;
130: struct in_multi *inm;
131: struct in_multistep step;
132: struct router_info *rti;
133: struct in_ifaddr *ia;
134: int timer;
135: va_list ap;
136:
137: va_start(ap, m);
138: iphlen = va_arg(ap, int);
139: va_end(ap);
140:
141: ++igmpstat.igps_rcv_total;
142:
143: igmplen = ntohs(ip->ip_len) - iphlen;
144:
145: /*
146: * Validate lengths
147: */
148: if (igmplen < IGMP_MINLEN) {
149: ++igmpstat.igps_rcv_tooshort;
150: m_freem(m);
151: return;
152: }
153: minlen = iphlen + IGMP_MINLEN;
154: if ((m->m_flags & M_EXT || m->m_len < minlen) &&
155: (m = m_pullup(m, minlen)) == NULL) {
156: ++igmpstat.igps_rcv_tooshort;
157: return;
158: }
159:
160: /*
161: * Validate checksum
162: */
163: m->m_data += iphlen;
164: m->m_len -= iphlen;
165: igmp = mtod(m, struct igmp *);
166: if (in_cksum(m, igmplen)) {
167: ++igmpstat.igps_rcv_badsum;
168: m_freem(m);
169: return;
170: }
171: m->m_data -= iphlen;
172: m->m_len += iphlen;
173: ip = mtod(m, struct ip *);
174:
175: switch (igmp->igmp_type) {
176:
177: case IGMP_HOST_MEMBERSHIP_QUERY:
178: ++igmpstat.igps_rcv_queries;
179:
180: if (ifp->if_flags & IFF_LOOPBACK)
181: break;
182:
183: if (igmp->igmp_code == 0) {
184: rti = rti_find(ifp);
185: if (rti == NULL) {
186: m_freem(m);
187: return;
188: }
189: rti->rti_type = IGMP_v1_ROUTER;
190: rti->rti_age = 0;
191:
192: if (ip->ip_dst.s_addr != INADDR_ALLHOSTS_GROUP) {
193: ++igmpstat.igps_rcv_badqueries;
194: m_freem(m);
195: return;
196: }
197:
198: /*
199: * Start the timers in all of our membership records
200: * for the interface on which the query arrived,
201: * except those that are already running and those
202: * that belong to a "local" group (224.0.0.X).
203: */
204: IN_FIRST_MULTI(step, inm);
205: while (inm != NULL) {
206: if (inm->inm_ia->ia_ifp == ifp &&
207: inm->inm_timer == 0 &&
208: !IN_LOCAL_GROUP(inm->inm_addr.s_addr)) {
209: inm->inm_state = IGMP_DELAYING_MEMBER;
210: inm->inm_timer = IGMP_RANDOM_DELAY(
211: IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ);
212: igmp_timers_are_running = 1;
213: }
214: IN_NEXT_MULTI(step, inm);
215: }
216: } else {
217: if (!IN_MULTICAST(ip->ip_dst.s_addr)) {
218: ++igmpstat.igps_rcv_badqueries;
219: m_freem(m);
220: return;
221: }
222:
223: timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE;
224: if (timer == 0)
225: timer = 1;
226:
227: /*
228: * Start the timers in all of our membership records
229: * for the interface on which the query arrived,
230: * except those that are already running and those
231: * that belong to a "local" group (224.0.0.X). For
232: * timers already running, check if they need to be
233: * reset.
234: */
235: IN_FIRST_MULTI(step, inm);
236: while (inm != NULL) {
237: if (inm->inm_ia->ia_ifp == ifp &&
238: !IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
239: (ip->ip_dst.s_addr == INADDR_ALLHOSTS_GROUP ||
240: ip->ip_dst.s_addr == inm->inm_addr.s_addr)) {
241: switch (inm->inm_state) {
242: case IGMP_DELAYING_MEMBER:
243: if (inm->inm_timer <= timer)
244: break;
245: /* FALLTHROUGH */
246: case IGMP_IDLE_MEMBER:
247: case IGMP_LAZY_MEMBER:
248: case IGMP_AWAKENING_MEMBER:
249: inm->inm_state =
250: IGMP_DELAYING_MEMBER;
251: inm->inm_timer =
252: IGMP_RANDOM_DELAY(timer);
253: igmp_timers_are_running = 1;
254: break;
255: case IGMP_SLEEPING_MEMBER:
256: inm->inm_state =
257: IGMP_AWAKENING_MEMBER;
258: break;
259: }
260: }
261: IN_NEXT_MULTI(step, inm);
262: }
263: }
264:
265: break;
266:
267: case IGMP_v1_HOST_MEMBERSHIP_REPORT:
268: ++igmpstat.igps_rcv_reports;
269:
270: if (ifp->if_flags & IFF_LOOPBACK)
271: break;
272:
273: if (!IN_MULTICAST(igmp->igmp_group.s_addr) ||
274: igmp->igmp_group.s_addr != ip->ip_dst.s_addr) {
275: ++igmpstat.igps_rcv_badreports;
276: m_freem(m);
277: return;
278: }
279:
280: /*
281: * KLUDGE: if the IP source address of the report has an
282: * unspecified (i.e., zero) subnet number, as is allowed for
283: * a booting host, replace it with the correct subnet number
284: * so that a process-level multicast routing daemon can
285: * determine which subnet it arrived from. This is necessary
286: * to compensate for the lack of any way for a process to
287: * determine the arrival interface of an incoming packet.
288: */
289: if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) {
290: IFP_TO_IA(ifp, ia);
291: if (ia)
292: ip->ip_src.s_addr = ia->ia_subnet;
293: }
294:
295: /*
296: * If we belong to the group being reported, stop
297: * our timer for that group.
298: */
299: IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
300: if (inm != NULL) {
301: inm->inm_timer = 0;
302: ++igmpstat.igps_rcv_ourreports;
303:
304: switch (inm->inm_state) {
305: case IGMP_IDLE_MEMBER:
306: case IGMP_LAZY_MEMBER:
307: case IGMP_AWAKENING_MEMBER:
308: case IGMP_SLEEPING_MEMBER:
309: inm->inm_state = IGMP_SLEEPING_MEMBER;
310: break;
311: case IGMP_DELAYING_MEMBER:
312: if (inm->inm_rti->rti_type == IGMP_v1_ROUTER)
313: inm->inm_state = IGMP_LAZY_MEMBER;
314: else
315: inm->inm_state = IGMP_SLEEPING_MEMBER;
316: break;
317: }
318: }
319:
320: break;
321:
322: case IGMP_v2_HOST_MEMBERSHIP_REPORT:
323: #ifdef MROUTING
324: /*
325: * Make sure we don't hear our own membership report. Fast
326: * leave requires knowing that we are the only member of a
327: * group.
328: */
329: IFP_TO_IA(ifp, ia);
330: if (ia && ip->ip_src.s_addr == ia->ia_addr.sin_addr.s_addr)
331: break;
332: #endif
333:
334: ++igmpstat.igps_rcv_reports;
335:
336: if (ifp->if_flags & IFF_LOOPBACK)
337: break;
338:
339: if (!IN_MULTICAST(igmp->igmp_group.s_addr) ||
340: igmp->igmp_group.s_addr != ip->ip_dst.s_addr) {
341: ++igmpstat.igps_rcv_badreports;
342: m_freem(m);
343: return;
344: }
345:
346: /*
347: * KLUDGE: if the IP source address of the report has an
348: * unspecified (i.e., zero) subnet number, as is allowed for
349: * a booting host, replace it with the correct subnet number
350: * so that a process-level multicast routing daemon can
351: * determine which subnet it arrived from. This is necessary
352: * to compensate for the lack of any way for a process to
353: * determine the arrival interface of an incoming packet.
354: */
355: if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) {
356: #ifndef MROUTING
357: IFP_TO_IA(ifp, ia);
358: #endif
359: if (ia)
360: ip->ip_src.s_addr = ia->ia_subnet;
361: }
362:
363: /*
364: * If we belong to the group being reported, stop
365: * our timer for that group.
366: */
367: IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
368: if (inm != NULL) {
369: inm->inm_timer = 0;
370: ++igmpstat.igps_rcv_ourreports;
371:
372: switch (inm->inm_state) {
373: case IGMP_DELAYING_MEMBER:
374: case IGMP_IDLE_MEMBER:
375: case IGMP_AWAKENING_MEMBER:
376: inm->inm_state = IGMP_LAZY_MEMBER;
377: break;
378: case IGMP_LAZY_MEMBER:
379: case IGMP_SLEEPING_MEMBER:
380: break;
381: }
382: }
383:
384: break;
385:
386: }
387:
388: /*
389: * Pass all valid IGMP packets up to any process(es) listening
390: * on a raw IGMP socket.
391: */
392: rip_input(m);
393: }
394:
395: void
396: igmp_joingroup(inm)
397: struct in_multi *inm;
398: {
399: int i, s = splsoftnet();
400:
401: inm->inm_state = IGMP_IDLE_MEMBER;
402:
403: if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
404: (inm->inm_ia->ia_ifp->if_flags & IFF_LOOPBACK) == 0) {
405: if ((i = rti_fill(inm)) == -1) {
406: splx(s);
407: return;
408: }
409: igmp_sendpkt(inm, i, 0);
410: inm->inm_state = IGMP_DELAYING_MEMBER;
411: inm->inm_timer = IGMP_RANDOM_DELAY(
412: IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ);
413: igmp_timers_are_running = 1;
414: } else
415: inm->inm_timer = 0;
416: splx(s);
417: }
418:
419: void
420: igmp_leavegroup(inm)
421: struct in_multi *inm;
422: {
423:
424: switch (inm->inm_state) {
425: case IGMP_DELAYING_MEMBER:
426: case IGMP_IDLE_MEMBER:
427: if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
428: (inm->inm_ia->ia_ifp->if_flags & IFF_LOOPBACK) == 0)
429: if (inm->inm_rti->rti_type != IGMP_v1_ROUTER)
430: igmp_sendpkt(inm, IGMP_HOST_LEAVE_MESSAGE,
431: INADDR_ALLROUTERS_GROUP);
432: break;
433: case IGMP_LAZY_MEMBER:
434: case IGMP_AWAKENING_MEMBER:
435: case IGMP_SLEEPING_MEMBER:
436: break;
437: }
438: }
439:
440: void
441: igmp_fasttimo()
442: {
443: struct in_multi *inm;
444: struct in_multistep step;
445: int s;
446:
447: /*
448: * Quick check to see if any work needs to be done, in order
449: * to minimize the overhead of fasttimo processing.
450: */
451: if (!igmp_timers_are_running)
452: return;
453:
454: s = splsoftnet();
455: igmp_timers_are_running = 0;
456: IN_FIRST_MULTI(step, inm);
457: while (inm != NULL) {
458: if (inm->inm_timer == 0) {
459: /* do nothing */
460: } else if (--inm->inm_timer == 0) {
461: if (inm->inm_state == IGMP_DELAYING_MEMBER) {
462: if (inm->inm_rti->rti_type == IGMP_v1_ROUTER)
463: igmp_sendpkt(inm,
464: IGMP_v1_HOST_MEMBERSHIP_REPORT, 0);
465: else
466: igmp_sendpkt(inm,
467: IGMP_v2_HOST_MEMBERSHIP_REPORT, 0);
468: inm->inm_state = IGMP_IDLE_MEMBER;
469: }
470: } else {
471: igmp_timers_are_running = 1;
472: }
473: IN_NEXT_MULTI(step, inm);
474: }
475: splx(s);
476: }
477:
478: void
479: igmp_slowtimo()
480: {
481: struct router_info *rti;
482: int s;
483:
484: s = splsoftnet();
485: for (rti = rti_head; rti != 0; rti = rti->rti_next) {
486: if (rti->rti_type == IGMP_v1_ROUTER &&
487: ++rti->rti_age >= IGMP_AGE_THRESHOLD) {
488: rti->rti_type = IGMP_v2_ROUTER;
489: }
490: }
491: splx(s);
492: }
493:
494: void
495: igmp_sendpkt(inm, type, addr)
496: struct in_multi *inm;
497: int type;
498: in_addr_t addr;
499: {
500: struct mbuf *m;
501: struct igmp *igmp;
502: struct ip *ip;
503: struct ip_moptions imo;
504: #ifdef MROUTING
505: extern struct socket *ip_mrouter;
506: #endif /* MROUTING */
507:
508: MGETHDR(m, M_DONTWAIT, MT_HEADER);
509: if (m == NULL)
510: return;
511: /*
512: * Assume max_linkhdr + sizeof(struct ip) + IGMP_MINLEN
513: * is smaller than mbuf size returned by MGETHDR.
514: */
515: m->m_data += max_linkhdr;
516: m->m_len = sizeof(struct ip) + IGMP_MINLEN;
517: m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN;
518:
519: ip = mtod(m, struct ip *);
520: ip->ip_tos = 0;
521: ip->ip_len = htons(sizeof(struct ip) + IGMP_MINLEN);
522: ip->ip_off = 0;
523: ip->ip_p = IPPROTO_IGMP;
524: ip->ip_src.s_addr = INADDR_ANY;
525: if (addr) {
526: ip->ip_dst.s_addr = addr;
527: } else {
528: ip->ip_dst = inm->inm_addr;
529: }
530:
531: m->m_data += sizeof(struct ip);
532: m->m_len -= sizeof(struct ip);
533: igmp = mtod(m, struct igmp *);
534: igmp->igmp_type = type;
535: igmp->igmp_code = 0;
536: igmp->igmp_group = inm->inm_addr;
537: igmp->igmp_cksum = 0;
538: igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN);
539: m->m_data -= sizeof(struct ip);
540: m->m_len += sizeof(struct ip);
541:
542: imo.imo_multicast_ifp = inm->inm_ia->ia_ifp;
543: imo.imo_multicast_ttl = 1;
544: #ifdef RSVP_ISI
545: imo.imo_multicast_vif = -1;
546: #endif
547: /*
548: * Request loopback of the report if we are acting as a multicast
549: * router, so that the process-level routing daemon can hear it.
550: */
551: #ifdef MROUTING
552: imo.imo_multicast_loop = (ip_mrouter != NULL);
553: #else
554: imo.imo_multicast_loop = 0;
555: #endif /* MROUTING */
556:
557: ip_output(m, (struct mbuf *)0, (struct route *)0, IP_MULTICASTOPTS,
558: &imo, (void *)NULL);
559:
560: ++igmpstat.igps_snd_reports;
561: }
CVSweb