This source file includes following definitions.
- igmp_init
- rti_fill
- rti_find
- rti_delete
- igmp_input
- igmp_joingroup
- igmp_leavegroup
- igmp_fasttimo
- igmp_slowtimo
- igmp_sendpkt
1
2
3
4
5
6
7
8
9
10
11
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
49
50 igmp_timers_are_running = 0;
51 rti_head = 0;
52 }
53
54
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
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
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
200
201
202
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
229
230
231
232
233
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
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
282
283
284
285
286
287
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
297
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
326
327
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
348
349
350
351
352
353
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
365
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
390
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
449
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
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
507
508 MGETHDR(m, M_DONTWAIT, MT_HEADER);
509 if (m == NULL)
510 return;
511
512
513
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
549
550
551 #ifdef MROUTING
552 imo.imo_multicast_loop = (ip_mrouter != NULL);
553 #else
554 imo.imo_multicast_loop = 0;
555 #endif
556
557 ip_output(m, (struct mbuf *)0, (struct route *)0, IP_MULTICASTOPTS,
558 &imo, (void *)NULL);
559
560 ++igmpstat.igps_snd_reports;
561 }