]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_igmp.c
pimd: static joins no longer worked
[mirror_frr.git] / pimd / pim_igmp.c
1 /*
2 PIM for Quagga
3 Copyright (C) 2008 Everton da Silva Marques
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; see the file COPYING; if not, write to the
17 Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
18 MA 02110-1301 USA
19
20 $QuaggaId: $Format:%an, %ai, %h$ $
21 */
22
23 #include <zebra.h>
24
25 #include "memory.h"
26 #include "prefix.h"
27 #include "if.h"
28
29 #include "pimd.h"
30 #include "pim_igmp.h"
31 #include "pim_igmpv3.h"
32 #include "pim_iface.h"
33 #include "pim_sock.h"
34 #include "pim_mroute.h"
35 #include "pim_str.h"
36 #include "pim_util.h"
37 #include "pim_time.h"
38 #include "pim_zebra.h"
39
40 #define IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE (1)
41 #define IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE (2)
42 #define IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE (3)
43 #define IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE (4)
44 #define IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES (5)
45 #define IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES (6)
46
47 static void group_timer_off(struct igmp_group *group);
48
49 static struct igmp_group *find_group_by_addr(struct igmp_sock *igmp,
50 struct in_addr group_addr);
51
52 static int igmp_sock_open(struct in_addr ifaddr, int ifindex, uint32_t pim_options)
53 {
54 int fd;
55 int join = 0;
56 struct in_addr group;
57
58 fd = pim_socket_mcast(IPPROTO_IGMP, ifaddr, ifindex, 1 /* loop=true */);
59 if (fd < 0)
60 return -1;
61
62 if (PIM_IF_TEST_IGMP_LISTEN_ALLROUTERS(pim_options)) {
63 if (inet_aton(PIM_ALL_ROUTERS, &group)) {
64 if (!pim_socket_join(fd, group, ifaddr, ifindex))
65 ++join;
66 }
67 else {
68 zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
69 __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
70 PIM_ALL_ROUTERS, errno, safe_strerror(errno));
71 }
72 }
73
74 /*
75 IGMP routers periodically send IGMP general queries to AllSystems=224.0.0.1
76 IGMP routers must receive general queries for querier election.
77 */
78 if (inet_aton(PIM_ALL_SYSTEMS, &group)) {
79 if (!pim_socket_join(fd, group, ifaddr, ifindex))
80 ++join;
81 }
82 else {
83 zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
84 __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
85 PIM_ALL_SYSTEMS, errno, safe_strerror(errno));
86 }
87
88 if (inet_aton(PIM_ALL_IGMP_ROUTERS, &group)) {
89 if (!pim_socket_join(fd, group, ifaddr, ifindex)) {
90 ++join;
91 }
92 }
93 else {
94 zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
95 __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
96 PIM_ALL_IGMP_ROUTERS, errno, safe_strerror(errno));
97 }
98
99 if (!join) {
100 zlog_err("IGMP socket fd=%d could not join any group on interface address %s",
101 fd, inet_ntoa(ifaddr));
102 close(fd);
103 fd = -1;
104 }
105
106 return fd;
107 }
108
109 #undef IGMP_SOCK_DUMP
110
111 #ifdef IGMP_SOCK_DUMP
112 static void igmp_sock_dump(array_t *igmp_sock_array)
113 {
114 int size = array_size(igmp_sock_array);
115 for (int i = 0; i < size; ++i) {
116
117 struct igmp_sock *igmp = array_get(igmp_sock_array, i);
118
119 zlog_debug("%s %s: [%d/%d] igmp_addr=%s fd=%d",
120 __FILE__, __PRETTY_FUNCTION__,
121 i, size,
122 inet_ntoa(igmp->ifaddr),
123 igmp->fd);
124 }
125 }
126 #endif
127
128 struct igmp_sock *pim_igmp_sock_lookup_ifaddr(struct list *igmp_sock_list,
129 struct in_addr ifaddr)
130 {
131 struct listnode *sock_node;
132 struct igmp_sock *igmp;
133
134 #ifdef IGMP_SOCK_DUMP
135 igmp_sock_dump(igmp_sock_list);
136 #endif
137
138 for (ALL_LIST_ELEMENTS_RO(igmp_sock_list, sock_node, igmp))
139 if (ifaddr.s_addr == igmp->ifaddr.s_addr)
140 return igmp;
141
142 return 0;
143 }
144
145 struct igmp_sock *igmp_sock_lookup_by_fd(struct list *igmp_sock_list,
146 int fd)
147 {
148 struct listnode *sock_node;
149 struct igmp_sock *igmp;
150
151 for (ALL_LIST_ELEMENTS_RO(igmp_sock_list, sock_node, igmp))
152 if (fd == igmp->fd)
153 return igmp;
154
155 return 0;
156 }
157
158 static int pim_igmp_other_querier_expire(struct thread *t)
159 {
160 struct igmp_sock *igmp;
161
162 zassert(t);
163 igmp = THREAD_ARG(t);
164 zassert(igmp);
165
166 zassert(igmp->t_other_querier_timer);
167 zassert(!igmp->t_igmp_query_timer);
168
169 if (PIM_DEBUG_IGMP_TRACE) {
170 char ifaddr_str[100];
171 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
172 zlog_debug("%s: Querier %s resuming",
173 __PRETTY_FUNCTION__,
174 ifaddr_str);
175 }
176
177 igmp->t_other_querier_timer = 0;
178
179 /*
180 We are the current querier, then
181 re-start sending general queries.
182 */
183 pim_igmp_general_query_on(igmp);
184
185 return 0;
186 }
187
188 void pim_igmp_other_querier_timer_on(struct igmp_sock *igmp)
189 {
190 long other_querier_present_interval_msec;
191 struct pim_interface *pim_ifp;
192
193 zassert(igmp);
194 zassert(igmp->interface);
195 zassert(igmp->interface->info);
196
197 pim_ifp = igmp->interface->info;
198
199 if (igmp->t_other_querier_timer) {
200 /*
201 There is other querier present already,
202 then reset the other-querier-present timer.
203 */
204
205 if (PIM_DEBUG_IGMP_TRACE) {
206 char ifaddr_str[100];
207 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
208 zlog_debug("Querier %s resetting TIMER event for Other-Querier-Present",
209 ifaddr_str);
210 }
211
212 THREAD_OFF(igmp->t_other_querier_timer);
213 zassert(!igmp->t_other_querier_timer);
214 }
215 else {
216 /*
217 We are the current querier, then stop sending general queries:
218 igmp->t_igmp_query_timer = 0;
219 */
220 pim_igmp_general_query_off(igmp);
221 }
222
223 /*
224 Since this socket is starting the other-querier-present timer,
225 there should not be periodic query timer for this socket.
226 */
227 zassert(!igmp->t_igmp_query_timer);
228
229 /*
230 RFC 3376: 8.5. Other Querier Present Interval
231
232 The Other Querier Present Interval is the length of time that must
233 pass before a multicast router decides that there is no longer
234 another multicast router which should be the querier. This value
235 MUST be ((the Robustness Variable) times (the Query Interval)) plus
236 (one half of one Query Response Interval).
237
238 other_querier_present_interval_msec = \
239 igmp->querier_robustness_variable * \
240 1000 * igmp->querier_query_interval + \
241 100 * (pim_ifp->query_max_response_time_dsec >> 1);
242 */
243 other_querier_present_interval_msec =
244 PIM_IGMP_OQPI_MSEC(igmp->querier_robustness_variable,
245 igmp->querier_query_interval,
246 pim_ifp->igmp_query_max_response_time_dsec);
247
248 if (PIM_DEBUG_IGMP_TRACE) {
249 char ifaddr_str[100];
250 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
251 zlog_debug("Querier %s scheduling %ld.%03ld sec TIMER event for Other-Querier-Present",
252 ifaddr_str,
253 other_querier_present_interval_msec / 1000,
254 other_querier_present_interval_msec % 1000);
255 }
256
257 THREAD_TIMER_MSEC_ON(master, igmp->t_other_querier_timer,
258 pim_igmp_other_querier_expire,
259 igmp, other_querier_present_interval_msec);
260 }
261
262 void pim_igmp_other_querier_timer_off(struct igmp_sock *igmp)
263 {
264 zassert(igmp);
265
266 if (PIM_DEBUG_IGMP_TRACE) {
267 if (igmp->t_other_querier_timer) {
268 char ifaddr_str[100];
269 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
270 zlog_debug("IGMP querier %s fd=%d cancelling other-querier-present TIMER event on %s",
271 ifaddr_str, igmp->fd, igmp->interface->name);
272 }
273 }
274 THREAD_OFF(igmp->t_other_querier_timer);
275 zassert(!igmp->t_other_querier_timer);
276 }
277
278 static int recv_igmp_query(struct igmp_sock *igmp, int query_version,
279 int max_resp_code,
280 struct in_addr from, const char *from_str,
281 char *igmp_msg, int igmp_msg_len)
282 {
283 struct interface *ifp;
284 struct pim_interface *pim_ifp;
285 uint8_t resv_s_qrv = 0;
286 uint8_t s_flag = 0;
287 uint8_t qrv = 0;
288 struct in_addr group_addr;
289 uint16_t recv_checksum;
290 uint16_t checksum;
291 int i;
292
293 //group_addr = *(struct in_addr *)(igmp_msg + 4);
294 memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
295
296 ifp = igmp->interface;
297 pim_ifp = ifp->info;
298
299 recv_checksum = *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET);
300
301 /* for computing checksum */
302 *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = 0;
303
304 checksum = in_cksum(igmp_msg, igmp_msg_len);
305 if (checksum != recv_checksum) {
306 zlog_warn("Recv IGMP query v%d from %s on %s: checksum mismatch: received=%x computed=%x",
307 query_version, from_str, ifp->name, recv_checksum, checksum);
308 return -1;
309 }
310
311 if (PIM_DEBUG_IGMP_PACKETS) {
312 char group_str[100];
313 pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
314 zlog_debug("Recv IGMP query v%d from %s on %s: size=%d checksum=%x group=%s",
315 query_version, from_str, ifp->name,
316 igmp_msg_len, checksum, group_str);
317 }
318
319 /*
320 RFC 3376: 6.6.2. Querier Election
321
322 When a router receives a query with a lower IP address, it sets
323 the Other-Querier-Present timer to Other Querier Present Interval
324 and ceases to send queries on the network if it was the previously
325 elected querier.
326 */
327 if (ntohl(from.s_addr) < ntohl(igmp->ifaddr.s_addr)) {
328
329 if (PIM_DEBUG_IGMP_TRACE) {
330 char ifaddr_str[100];
331 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
332 zlog_debug("%s: local address %s (%u) lost querier election to %s (%u)",
333 ifp->name,
334 ifaddr_str, ntohl(igmp->ifaddr.s_addr),
335 from_str, ntohl(from.s_addr));
336 }
337
338 pim_igmp_other_querier_timer_on(igmp);
339 }
340
341 if (query_version == 3) {
342 /*
343 RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
344
345 Routers adopt the QRV value from the most recently received Query
346 as their own [Robustness Variable] value, unless that most
347 recently received QRV was zero, in which case the receivers use
348 the default [Robustness Variable] value specified in section 8.1
349 or a statically configured value.
350 */
351 resv_s_qrv = igmp_msg[8];
352 qrv = 7 & resv_s_qrv;
353 igmp->querier_robustness_variable = qrv ? qrv : pim_ifp->igmp_default_robustness_variable;
354 }
355
356 /*
357 RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
358
359 Multicast routers that are not the current querier adopt the QQI
360 value from the most recently received Query as their own [Query
361 Interval] value, unless that most recently received QQI was zero,
362 in which case the receiving routers use the default.
363 */
364 if (igmp->t_other_querier_timer && query_version == 3) {
365 /* other querier present */
366 uint8_t qqic;
367 uint16_t qqi;
368 qqic = igmp_msg[9];
369 qqi = igmp_msg_decode8to16(qqic);
370 igmp->querier_query_interval = qqi ? qqi : pim_ifp->igmp_default_query_interval;
371
372 if (PIM_DEBUG_IGMP_TRACE) {
373 char ifaddr_str[100];
374 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
375 zlog_debug("Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
376 ifaddr_str,
377 qqi ? "recv-non-default" : "default",
378 igmp->querier_query_interval,
379 qqic,
380 from_str);
381 }
382 }
383
384 /*
385 RFC 3376: 6.6.1. Timer Updates
386
387 When a router sends or receives a query with a clear Suppress
388 Router-Side Processing flag, it must update its timers to reflect
389 the correct timeout values for the group or sources being queried.
390
391 General queries don't trigger timer update.
392 */
393 if (query_version == 3) {
394 s_flag = (1 << 3) & resv_s_qrv;
395 }
396 else {
397 /* Neither V1 nor V2 have this field. Pimd should really go into
398 * a compatibility mode here and run as V2 (or V1) but it doesn't
399 * so for now, lets just set the flag to suppress these timer updates.
400 */
401 s_flag = 1;
402 }
403
404 if (!s_flag) {
405 /* s_flag is clear */
406
407 if (PIM_INADDR_IS_ANY(group_addr)) {
408 /* this is a general query */
409
410 /* log that general query should have the s_flag set */
411 zlog_warn("General IGMP query v%d from %s on %s: Suppress Router-Side Processing flag is clear",
412 query_version, from_str, ifp->name);
413 }
414 else {
415 struct igmp_group *group;
416
417 /* this is a non-general query: perform timer updates */
418
419 group = find_group_by_addr(igmp, group_addr);
420 if (group) {
421 int recv_num_sources = ntohs(*(uint16_t *)(igmp_msg + IGMP_V3_NUMSOURCES_OFFSET));
422
423 /*
424 RFC 3376: 6.6.1. Timer Updates
425 Query Q(G,A): Source Timer for sources in A are lowered to LMQT
426 Query Q(G): Group Timer is lowered to LMQT
427 */
428 if (recv_num_sources < 1) {
429 /* Query Q(G): Group Timer is lowered to LMQT */
430
431 igmp_group_timer_lower_to_lmqt(group);
432 }
433 else {
434 /* Query Q(G,A): Source Timer for sources in A are lowered to LMQT */
435
436 /* Scan sources in query and lower their timers to LMQT */
437 struct in_addr *sources = (struct in_addr *)(igmp_msg + IGMP_V3_SOURCES_OFFSET);
438 for (i = 0; i < recv_num_sources; ++i) {
439 //struct in_addr src_addr = sources[i];
440 //struct igmp_source *src = igmp_find_source_by_addr(group, src_addr);
441 struct in_addr src_addr;
442 struct igmp_source *src;
443 memcpy(&src_addr, sources + i, sizeof(struct in_addr));
444 src = igmp_find_source_by_addr(group, src_addr);
445 if (src) {
446 igmp_source_timer_lower_to_lmqt(src);
447 }
448 }
449 }
450
451 }
452 else {
453 char group_str[100];
454 pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
455 zlog_warn("IGMP query v%d from %s on %s: could not find group %s for timer update",
456 query_version, from_str, ifp->name, group_str);
457 }
458 }
459 } /* s_flag is clear: timer updates */
460
461 return 0;
462 }
463
464 static int igmp_v3_report(struct igmp_sock *igmp,
465 struct in_addr from, const char *from_str,
466 char *igmp_msg, int igmp_msg_len)
467 {
468 uint16_t recv_checksum;
469 uint16_t checksum;
470 int num_groups;
471 uint8_t *group_record;
472 uint8_t *report_pastend = (uint8_t *) igmp_msg + igmp_msg_len;
473 struct interface *ifp = igmp->interface;
474 int i;
475 int local_ncb = 0;
476
477 if (igmp_msg_len < IGMP_V3_MSG_MIN_SIZE) {
478 zlog_warn("Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
479 from_str, ifp->name, igmp_msg_len, IGMP_V3_MSG_MIN_SIZE);
480 return -1;
481 }
482
483 recv_checksum = *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET);
484
485 /* for computing checksum */
486 *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = 0;
487
488 checksum = in_cksum(igmp_msg, igmp_msg_len);
489 if (checksum != recv_checksum) {
490 zlog_warn("Recv IGMP report v3 from %s on %s: checksum mismatch: received=%x computed=%x",
491 from_str, ifp->name, recv_checksum, checksum);
492 return -1;
493 }
494
495 num_groups = ntohs(*(uint16_t *) (igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET));
496 if (num_groups < 1) {
497 zlog_warn("Recv IGMP report v3 from %s on %s: missing group records",
498 from_str, ifp->name);
499 return -1;
500 }
501
502 if (PIM_DEBUG_IGMP_PACKETS) {
503 zlog_debug("Recv IGMP report v3 from %s on %s: size=%d checksum=%x groups=%d",
504 from_str, ifp->name, igmp_msg_len, checksum, num_groups);
505 }
506
507 group_record = (uint8_t *) igmp_msg + IGMP_V3_REPORT_GROUPPRECORD_OFFSET;
508
509 /* Scan groups */
510 for (i = 0; i < num_groups; ++i) {
511 struct in_addr rec_group;
512 uint8_t *sources;
513 uint8_t *src;
514 int rec_type;
515 int rec_auxdatalen;
516 int rec_num_sources;
517 int j;
518 struct prefix lncb;
519 struct prefix g;
520
521 if ((group_record + IGMP_V3_GROUP_RECORD_MIN_SIZE) > report_pastend) {
522 zlog_warn("Recv IGMP report v3 from %s on %s: group record beyond report end",
523 from_str, ifp->name);
524 return -1;
525 }
526
527 rec_type = group_record[IGMP_V3_GROUP_RECORD_TYPE_OFFSET];
528 rec_auxdatalen = group_record[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET];
529 rec_num_sources = ntohs(* (uint16_t *) (group_record + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET));
530
531 //rec_group = *(struct in_addr *)(group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET);
532 memcpy(&rec_group, group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET, sizeof(struct in_addr));
533
534 if (PIM_DEBUG_IGMP_PACKETS) {
535 zlog_debug("Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%s",
536 from_str, ifp->name, i, rec_type, rec_auxdatalen, rec_num_sources, inet_ntoa(rec_group));
537 }
538
539 /* Scan sources */
540
541 sources = group_record + IGMP_V3_GROUP_RECORD_SOURCE_OFFSET;
542
543 for (j = 0, src = sources; j < rec_num_sources; ++j, src += 4) {
544
545 if ((src + 4) > report_pastend) {
546 zlog_warn("Recv IGMP report v3 from %s on %s: group source beyond report end",
547 from_str, ifp->name);
548 return -1;
549 }
550
551 if (PIM_DEBUG_IGMP_PACKETS) {
552 char src_str[200];
553
554 if (!inet_ntop(AF_INET, src, src_str , sizeof(src_str)))
555 sprintf(src_str, "<source?>");
556
557 zlog_debug("Recv IGMP report v3 from %s on %s: record=%d group=%s source=%s",
558 from_str, ifp->name, i, inet_ntoa(rec_group), src_str);
559 }
560 } /* for (sources) */
561
562
563 lncb.family = AF_INET;
564 lncb.u.prefix4.s_addr = 0x000000E0;
565 lncb.prefixlen = 24;
566
567 g.family = AF_INET;
568 g.u.prefix4 = rec_group;
569 g.prefixlen = 32;
570 /*
571 * If we receive a igmp report with the group in 224.0.0.0/24
572 * then we should ignore it
573 */
574 if (prefix_match(&lncb, &g))
575 local_ncb = 1;
576
577 if (!local_ncb)
578 switch (rec_type) {
579 case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE:
580 igmpv3_report_isin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
581 break;
582 case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE:
583 igmpv3_report_isex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
584 break;
585 case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE:
586 igmpv3_report_toin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
587 break;
588 case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE:
589 igmpv3_report_toex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
590 break;
591 case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES:
592 igmpv3_report_allow(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
593 break;
594 case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES:
595 igmpv3_report_block(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
596 break;
597 default:
598 zlog_warn("Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
599 from_str, ifp->name, rec_type);
600 }
601
602 group_record += 8 + (rec_num_sources << 2) + (rec_auxdatalen << 2);
603 local_ncb = 0;
604
605 } /* for (group records) */
606
607 return 0;
608 }
609
610 static void on_trace(const char *label,
611 struct interface *ifp, struct in_addr from)
612 {
613 if (PIM_DEBUG_IGMP_TRACE) {
614 char from_str[100];
615 pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
616 zlog_debug("%s: from %s on %s",
617 label, from_str, ifp->name);
618 }
619 }
620
621 static int igmp_v2_report(struct igmp_sock *igmp,
622 struct in_addr from, const char *from_str,
623 char *igmp_msg, int igmp_msg_len)
624 {
625 struct interface *ifp = igmp->interface;
626 struct igmp_group *group;
627 struct in_addr group_addr;
628
629 on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
630
631 if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
632 zlog_warn("Recv IGMP report v2 from %s on %s: size=%d other than correct=%d",
633 from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
634 return -1;
635 }
636
637 if (PIM_DEBUG_IGMP_TRACE) {
638 zlog_warn("%s %s: FIXME WRITEME",
639 __FILE__, __PRETTY_FUNCTION__);
640 }
641
642 //group_addr = *(struct in_addr *)(igmp_msg + 4);
643 memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
644
645 /* non-existant group is created as INCLUDE {empty} */
646 group = igmp_add_group_by_addr(igmp, group_addr);
647 if (!group) {
648 return -1;
649 }
650
651 group->last_igmp_v2_report_dsec = pim_time_monotonic_dsec();
652
653 return 0;
654 }
655
656 static int igmp_v2_leave(struct igmp_sock *igmp,
657 struct in_addr from, const char *from_str,
658 char *igmp_msg, int igmp_msg_len)
659 {
660 struct interface *ifp = igmp->interface;
661
662 on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
663
664 if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
665 zlog_warn("Recv IGMP leave v2 from %s on %s: size=%d other than correct=%d",
666 from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
667 return -1;
668 }
669
670 if (PIM_DEBUG_IGMP_TRACE) {
671 zlog_warn("%s %s: FIXME WRITEME",
672 __FILE__, __PRETTY_FUNCTION__);
673 }
674
675 return 0;
676 }
677
678 static int igmp_v1_report(struct igmp_sock *igmp,
679 struct in_addr from, const char *from_str,
680 char *igmp_msg, int igmp_msg_len)
681 {
682 struct interface *ifp = igmp->interface;
683 struct igmp_group *group;
684 struct in_addr group_addr;
685
686 on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
687
688 if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
689 zlog_warn("Recv IGMP report v1 from %s on %s: size=%d other than correct=%d",
690 from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
691 return -1;
692 }
693
694 if (PIM_DEBUG_IGMP_TRACE) {
695 zlog_warn("%s %s: FIXME WRITEME",
696 __FILE__, __PRETTY_FUNCTION__);
697 }
698
699 //group_addr = *(struct in_addr *)(igmp_msg + 4);
700 memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
701
702 /* non-existant group is created as INCLUDE {empty} */
703 group = igmp_add_group_by_addr(igmp, group_addr);
704 if (!group) {
705 return -1;
706 }
707
708 group->last_igmp_v1_report_dsec = pim_time_monotonic_dsec();
709
710 return 0;
711 }
712
713 int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len)
714 {
715 struct ip *ip_hdr;
716 size_t ip_hlen; /* ip header length in bytes */
717 char *igmp_msg;
718 int igmp_msg_len;
719 int msg_type;
720 char from_str[100];
721 char to_str[100];
722
723 if (len < sizeof(*ip_hdr)) {
724 zlog_warn("IGMP packet size=%zu shorter than minimum=%zu",
725 len, sizeof(*ip_hdr));
726 return -1;
727 }
728
729 ip_hdr = (struct ip *) buf;
730
731 pim_inet4_dump("<src?>", ip_hdr->ip_src, from_str , sizeof(from_str));
732 pim_inet4_dump("<dst?>", ip_hdr->ip_dst, to_str , sizeof(to_str));
733
734 ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */
735
736 if (PIM_DEBUG_IGMP_PACKETS) {
737 zlog_debug("Recv IP packet from %s to %s on %s: size=%zu ip_header_size=%zu ip_proto=%d",
738 from_str, to_str, igmp->interface->name, len, ip_hlen, ip_hdr->ip_p);
739 }
740
741 if (ip_hdr->ip_p != PIM_IP_PROTO_IGMP) {
742 zlog_warn("IP packet protocol=%d is not IGMP=%d",
743 ip_hdr->ip_p, PIM_IP_PROTO_IGMP);
744 return -1;
745 }
746
747 if (ip_hlen < PIM_IP_HEADER_MIN_LEN) {
748 zlog_warn("IP packet header size=%zu shorter than minimum=%d",
749 ip_hlen, PIM_IP_HEADER_MIN_LEN);
750 return -1;
751 }
752 if (ip_hlen > PIM_IP_HEADER_MAX_LEN) {
753 zlog_warn("IP packet header size=%zu greater than maximum=%d",
754 ip_hlen, PIM_IP_HEADER_MAX_LEN);
755 return -1;
756 }
757
758 igmp_msg = buf + ip_hlen;
759 msg_type = *igmp_msg;
760 igmp_msg_len = len - ip_hlen;
761
762 if (PIM_DEBUG_IGMP_PACKETS) {
763 zlog_debug("Recv IGMP packet from %s to %s on %s: ttl=%d msg_type=%d msg_size=%d",
764 from_str, to_str, igmp->interface->name, ip_hdr->ip_ttl, msg_type,
765 igmp_msg_len);
766 }
767
768 if (igmp_msg_len < PIM_IGMP_MIN_LEN) {
769 zlog_warn("IGMP message size=%d shorter than minimum=%d",
770 igmp_msg_len, PIM_IGMP_MIN_LEN);
771 return -1;
772 }
773
774 switch (msg_type) {
775 case PIM_IGMP_MEMBERSHIP_QUERY:
776 {
777 int max_resp_code = igmp_msg[1];
778 int query_version;
779
780 /*
781 RFC 3376: 7.1. Query Version Distinctions
782 IGMPv1 Query: length = 8 octets AND Max Resp Code field is zero
783 IGMPv2 Query: length = 8 octets AND Max Resp Code field is non-zero
784 IGMPv3 Query: length >= 12 octets
785 */
786
787 if (igmp_msg_len == 8) {
788 query_version = max_resp_code ? 2 : 1;
789 }
790 else if (igmp_msg_len >= 12) {
791 query_version = 3;
792 }
793 else {
794 zlog_warn("Unknown IGMP query version");
795 return -1;
796 }
797
798 return recv_igmp_query(igmp, query_version, max_resp_code,
799 ip_hdr->ip_src, from_str,
800 igmp_msg, igmp_msg_len);
801 }
802
803 case PIM_IGMP_V3_MEMBERSHIP_REPORT:
804 return igmp_v3_report(igmp, ip_hdr->ip_src, from_str,
805 igmp_msg, igmp_msg_len);
806
807 case PIM_IGMP_V2_MEMBERSHIP_REPORT:
808 return igmp_v2_report(igmp, ip_hdr->ip_src, from_str,
809 igmp_msg, igmp_msg_len);
810
811 case PIM_IGMP_V1_MEMBERSHIP_REPORT:
812 return igmp_v1_report(igmp, ip_hdr->ip_src, from_str,
813 igmp_msg, igmp_msg_len);
814
815 case PIM_IGMP_V2_LEAVE_GROUP:
816 return igmp_v2_leave(igmp, ip_hdr->ip_src, from_str,
817 igmp_msg, igmp_msg_len);
818 }
819
820 zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type);
821
822 return -1;
823 }
824
825 static int pim_igmp_general_query(struct thread *t);
826
827 void pim_igmp_general_query_on(struct igmp_sock *igmp)
828 {
829 struct pim_interface *pim_ifp;
830 int startup_mode;
831 int query_interval;
832
833 zassert(igmp);
834 zassert(igmp->interface);
835
836 /*
837 Since this socket is starting as querier,
838 there should not exist a timer for other-querier-present.
839 */
840 zassert(!igmp->t_other_querier_timer);
841 pim_ifp = igmp->interface->info;
842 zassert(pim_ifp);
843
844 /*
845 RFC 3376: 8.6. Startup Query Interval
846
847 The Startup Query Interval is the interval between General Queries
848 sent by a Querier on startup. Default: 1/4 the Query Interval.
849 */
850 startup_mode = igmp->startup_query_count > 0;
851 if (startup_mode) {
852 --igmp->startup_query_count;
853
854 /* query_interval = pim_ifp->igmp_default_query_interval >> 2; */
855 query_interval = PIM_IGMP_SQI(pim_ifp->igmp_default_query_interval);
856 }
857 else {
858 query_interval = igmp->querier_query_interval;
859 }
860
861 if (PIM_DEBUG_IGMP_TRACE) {
862 char ifaddr_str[100];
863 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
864 zlog_debug("Querier %s scheduling %d-second (%s) TIMER event for IGMP query on fd=%d",
865 ifaddr_str,
866 query_interval,
867 startup_mode ? "startup" : "non-startup",
868 igmp->fd);
869 }
870 igmp->t_igmp_query_timer = 0;
871 zassert(!igmp->t_igmp_query_timer);
872 THREAD_TIMER_ON(master, igmp->t_igmp_query_timer,
873 pim_igmp_general_query,
874 igmp, query_interval);
875 }
876
877 void pim_igmp_general_query_off(struct igmp_sock *igmp)
878 {
879 zassert(igmp);
880
881 if (PIM_DEBUG_IGMP_TRACE) {
882 if (igmp->t_igmp_query_timer) {
883 char ifaddr_str[100];
884 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
885 zlog_debug("IGMP querier %s fd=%d cancelling query TIMER event on %s",
886 ifaddr_str, igmp->fd, igmp->interface->name);
887 }
888 }
889 THREAD_OFF(igmp->t_igmp_query_timer);
890 zassert(!igmp->t_igmp_query_timer);
891 }
892
893 /* Issue IGMP general query */
894 static int pim_igmp_general_query(struct thread *t)
895 {
896 char query_buf[PIM_IGMP_BUFSIZE_WRITE];
897 struct igmp_sock *igmp;
898 struct in_addr dst_addr;
899 struct in_addr group_addr;
900 struct pim_interface *pim_ifp;
901
902 zassert(t);
903
904 igmp = THREAD_ARG(t);
905
906 zassert(igmp);
907 zassert(igmp->interface);
908 zassert(igmp->interface->info);
909
910 pim_ifp = igmp->interface->info;
911
912 /*
913 RFC3376: 4.1.12. IP Destination Addresses for Queries
914
915 In IGMPv3, General Queries are sent with an IP destination address
916 of 224.0.0.1, the all-systems multicast address. Group-Specific
917 and Group-and-Source-Specific Queries are sent with an IP
918 destination address equal to the multicast address of interest.
919 */
920
921 dst_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
922 group_addr.s_addr = PIM_NET_INADDR_ANY;
923
924 if (PIM_DEBUG_IGMP_TRACE) {
925 char querier_str[100];
926 char dst_str[100];
927 pim_inet4_dump("<querier?>", igmp->ifaddr, querier_str,
928 sizeof(querier_str));
929 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
930 zlog_debug("Querier %s issuing IGMP general query to %s on %s",
931 querier_str, dst_str, igmp->interface->name);
932 }
933
934 pim_igmp_send_membership_query(0 /* igmp_group */,
935 igmp->fd,
936 igmp->interface->name,
937 query_buf,
938 sizeof(query_buf),
939 0 /* num_sources */,
940 dst_addr,
941 group_addr,
942 pim_ifp->igmp_query_max_response_time_dsec,
943 1 /* s_flag: always set for general queries */,
944 igmp->querier_robustness_variable,
945 igmp->querier_query_interval);
946
947 pim_igmp_general_query_on(igmp);
948
949 return 0;
950 }
951
952 static int pim_igmp_read(struct thread *t);
953
954 static void igmp_read_on(struct igmp_sock *igmp)
955 {
956 zassert(igmp);
957
958 if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
959 zlog_debug("Scheduling READ event on IGMP socket fd=%d",
960 igmp->fd);
961 }
962 igmp->t_igmp_read = 0;
963 zassert(!igmp->t_igmp_read);
964 THREAD_READ_ON(master, igmp->t_igmp_read, pim_igmp_read, igmp, igmp->fd);
965 }
966
967 static int pim_igmp_read(struct thread *t)
968 {
969 struct igmp_sock *igmp;
970 int fd;
971 struct sockaddr_in from;
972 struct sockaddr_in to;
973 socklen_t fromlen = sizeof(from);
974 socklen_t tolen = sizeof(to);
975 uint8_t buf[PIM_IGMP_BUFSIZE_READ];
976 int len;
977 int ifindex = -1;
978 int result = -1; /* defaults to bad */
979
980 zassert(t);
981
982 igmp = THREAD_ARG(t);
983
984 zassert(igmp);
985
986 fd = THREAD_FD(t);
987
988 zassert(fd == igmp->fd);
989
990 len = pim_socket_recvfromto(fd, buf, sizeof(buf),
991 &from, &fromlen,
992 &to, &tolen,
993 &ifindex);
994 if (len < 0) {
995 zlog_warn("Failure receiving IP IGMP packet on fd=%d: errno=%d: %s",
996 fd, errno, safe_strerror(errno));
997 goto done;
998 }
999
1000 if (PIM_DEBUG_IGMP_PACKETS) {
1001 char from_str[100];
1002 char to_str[100];
1003
1004 if (!inet_ntop(AF_INET, &from.sin_addr, from_str, sizeof(from_str)))
1005 sprintf(from_str, "<from?>");
1006 if (!inet_ntop(AF_INET, &to.sin_addr, to_str, sizeof(to_str)))
1007 sprintf(to_str, "<to?>");
1008
1009 zlog_debug("Recv IP IGMP pkt size=%d from %s to %s on fd=%d on ifindex=%d (sock_ifindex=%d)",
1010 len, from_str, to_str, fd, ifindex, igmp->interface->ifindex);
1011 }
1012
1013 #ifdef PIM_CHECK_RECV_IFINDEX_SANITY
1014 /* ifindex sanity check */
1015 if (ifindex != (int) igmp->interface->ifindex) {
1016 char from_str[100];
1017 char to_str[100];
1018 struct interface *ifp;
1019
1020 if (!inet_ntop(AF_INET, &from.sin_addr, from_str , sizeof(from_str)))
1021 sprintf(from_str, "<from?>");
1022 if (!inet_ntop(AF_INET, &to.sin_addr, to_str , sizeof(to_str)))
1023 sprintf(to_str, "<to?>");
1024
1025 ifp = if_lookup_by_index(ifindex);
1026 if (ifp) {
1027 zassert(ifindex == (int) ifp->ifindex);
1028 }
1029
1030 #ifdef PIM_REPORT_RECV_IFINDEX_MISMATCH
1031 zlog_warn("Interface mismatch: recv IGMP pkt from %s to %s on fd=%d: recv_ifindex=%d (%s) sock_ifindex=%d (%s)",
1032 from_str, to_str, fd,
1033 ifindex, ifp ? ifp->name : "<if-notfound>",
1034 igmp->interface->ifindex, igmp->interface->name);
1035 #endif
1036 goto done;
1037 }
1038 #endif
1039
1040 if (pim_igmp_packet(igmp, (char *)buf, len)) {
1041 goto done;
1042 }
1043
1044 result = 0; /* good */
1045
1046 done:
1047 igmp_read_on(igmp);
1048
1049 return result;
1050 }
1051
1052 static void sock_close(struct igmp_sock *igmp)
1053 {
1054 pim_igmp_other_querier_timer_off(igmp);
1055 pim_igmp_general_query_off(igmp);
1056
1057 if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
1058 if (igmp->t_igmp_read) {
1059 zlog_debug("Cancelling READ event on IGMP socket %s fd=%d on interface %s",
1060 inet_ntoa(igmp->ifaddr), igmp->fd,
1061 igmp->interface->name);
1062 }
1063 }
1064 THREAD_OFF(igmp->t_igmp_read);
1065 zassert(!igmp->t_igmp_read);
1066
1067 if (close(igmp->fd)) {
1068 zlog_err("Failure closing IGMP socket %s fd=%d on interface %s: errno=%d: %s",
1069 inet_ntoa(igmp->ifaddr), igmp->fd, igmp->interface->name,
1070 errno, safe_strerror(errno));
1071 }
1072
1073 if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
1074 zlog_debug("Deleted IGMP socket %s fd=%d on interface %s",
1075 inet_ntoa(igmp->ifaddr), igmp->fd, igmp->interface->name);
1076 }
1077 }
1078
1079 void igmp_startup_mode_on(struct igmp_sock *igmp)
1080 {
1081 struct pim_interface *pim_ifp;
1082
1083 pim_ifp = igmp->interface->info;
1084
1085 /*
1086 RFC 3376: 8.7. Startup Query Count
1087
1088 The Startup Query Count is the number of Queries sent out on
1089 startup, separated by the Startup Query Interval. Default: the
1090 Robustness Variable.
1091 */
1092 igmp->startup_query_count = igmp->querier_robustness_variable;
1093
1094 /*
1095 Since we're (re)starting, reset QQI to default Query Interval
1096 */
1097 igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
1098 }
1099
1100 static void igmp_group_free(struct igmp_group *group)
1101 {
1102 list_free(group->group_source_list);
1103
1104 XFREE(MTYPE_PIM_IGMP_GROUP, group);
1105 }
1106
1107 static void igmp_group_delete(struct igmp_group *group)
1108 {
1109 struct listnode *src_node;
1110 struct listnode *src_nextnode;
1111 struct igmp_source *src;
1112
1113 if (PIM_DEBUG_IGMP_TRACE) {
1114 char group_str[100];
1115 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1116 zlog_debug("Deleting IGMP group %s from socket %d interface %s",
1117 group_str,
1118 group->group_igmp_sock->fd,
1119 group->group_igmp_sock->interface->name);
1120 }
1121
1122 for (ALL_LIST_ELEMENTS(group->group_source_list, src_node, src_nextnode, src)) {
1123 igmp_source_delete(src);
1124 }
1125
1126 if (group->t_group_query_retransmit_timer) {
1127 THREAD_OFF(group->t_group_query_retransmit_timer);
1128 }
1129
1130 group_timer_off(group);
1131 listnode_delete(group->group_igmp_sock->igmp_group_list, group);
1132 igmp_group_free(group);
1133 }
1134
1135 void igmp_group_delete_empty_include(struct igmp_group *group)
1136 {
1137 zassert(!group->group_filtermode_isexcl);
1138 zassert(!listcount(group->group_source_list));
1139
1140 igmp_group_delete(group);
1141 }
1142
1143 void igmp_sock_free(struct igmp_sock *igmp)
1144 {
1145 zassert(!igmp->t_igmp_read);
1146 zassert(!igmp->t_igmp_query_timer);
1147 zassert(!igmp->t_other_querier_timer);
1148 zassert(igmp->igmp_group_list);
1149 zassert(!listcount(igmp->igmp_group_list));
1150
1151 list_free(igmp->igmp_group_list);
1152
1153 XFREE(MTYPE_PIM_IGMP_SOCKET, igmp);
1154 }
1155
1156 void igmp_sock_delete(struct igmp_sock *igmp)
1157 {
1158 struct pim_interface *pim_ifp;
1159 struct listnode *grp_node;
1160 struct listnode *grp_nextnode;
1161 struct igmp_group *grp;
1162
1163 for (ALL_LIST_ELEMENTS(igmp->igmp_group_list, grp_node, grp_nextnode, grp)) {
1164 igmp_group_delete(grp);
1165 }
1166
1167 sock_close(igmp);
1168
1169 pim_ifp = igmp->interface->info;
1170
1171 listnode_delete(pim_ifp->igmp_socket_list, igmp);
1172
1173 igmp_sock_free(igmp);
1174 }
1175
1176 static struct igmp_sock *igmp_sock_new(int fd,
1177 struct in_addr ifaddr,
1178 struct interface *ifp)
1179 {
1180 struct pim_interface *pim_ifp;
1181 struct igmp_sock *igmp;
1182
1183 pim_ifp = ifp->info;
1184
1185 if (PIM_DEBUG_IGMP_TRACE) {
1186 zlog_debug("Creating IGMP socket fd=%d for address %s on interface %s",
1187 fd, inet_ntoa(ifaddr), ifp->name);
1188 }
1189
1190 igmp = XMALLOC(MTYPE_PIM_IGMP_SOCKET, sizeof(*igmp));
1191 if (!igmp) {
1192 zlog_warn("%s %s: XMALLOC() failure",
1193 __FILE__, __PRETTY_FUNCTION__);
1194 return 0;
1195 }
1196
1197 igmp->igmp_group_list = list_new();
1198 if (!igmp->igmp_group_list) {
1199 zlog_err("%s %s: failure: igmp_group_list = list_new()",
1200 __FILE__, __PRETTY_FUNCTION__);
1201 return 0;
1202 }
1203 igmp->igmp_group_list->del = (void (*)(void *)) igmp_group_free;
1204
1205 igmp->fd = fd;
1206 igmp->interface = ifp;
1207 igmp->ifaddr = ifaddr;
1208 igmp->t_igmp_read = 0;
1209 igmp->t_igmp_query_timer = 0;
1210 igmp->t_other_querier_timer = 0; /* no other querier present */
1211 igmp->querier_robustness_variable = pim_ifp->igmp_default_robustness_variable;
1212 igmp->sock_creation = pim_time_monotonic_sec();
1213
1214 /*
1215 igmp_startup_mode_on() will reset QQI:
1216
1217 igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
1218 */
1219 igmp_startup_mode_on(igmp);
1220
1221 igmp_read_on(igmp);
1222 pim_igmp_general_query_on(igmp);
1223
1224 return igmp;
1225 }
1226
1227 struct igmp_sock *pim_igmp_sock_add(struct list *igmp_sock_list,
1228 struct in_addr ifaddr,
1229 struct interface *ifp)
1230 {
1231 struct pim_interface *pim_ifp;
1232 struct igmp_sock *igmp;
1233 int fd;
1234
1235 pim_ifp = ifp->info;
1236
1237 fd = igmp_sock_open(ifaddr, ifp->ifindex, pim_ifp->options);
1238 if (fd < 0) {
1239 zlog_warn("Could not open IGMP socket for %s on %s",
1240 inet_ntoa(ifaddr), ifp->name);
1241 return 0;
1242 }
1243
1244 igmp = igmp_sock_new(fd, ifaddr, ifp);
1245 if (!igmp) {
1246 zlog_err("%s %s: igmp_sock_new() failure",
1247 __FILE__, __PRETTY_FUNCTION__);
1248 close(fd);
1249 return 0;
1250 }
1251
1252 listnode_add(igmp_sock_list, igmp);
1253
1254 #ifdef IGMP_SOCK_DUMP
1255 igmp_sock_dump(igmp_sock_array);
1256 #endif
1257
1258 return igmp;
1259 }
1260
1261 /*
1262 RFC 3376: 6.5. Switching Router Filter-Modes
1263
1264 When a router's filter-mode for a group is EXCLUDE and the group
1265 timer expires, the router filter-mode for the group transitions to
1266 INCLUDE.
1267
1268 A router uses source records with running source timers as its state
1269 for the switch to a filter-mode of INCLUDE. If there are any source
1270 records with source timers greater than zero (i.e., requested to be
1271 forwarded), a router switches to filter-mode of INCLUDE using those
1272 source records. Source records whose timers are zero (from the
1273 previous EXCLUDE mode) are deleted.
1274 */
1275 static int igmp_group_timer(struct thread *t)
1276 {
1277 struct igmp_group *group;
1278
1279 zassert(t);
1280 group = THREAD_ARG(t);
1281 zassert(group);
1282
1283 if (PIM_DEBUG_IGMP_TRACE) {
1284 char group_str[100];
1285 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1286 zlog_debug("%s: Timer for group %s on interface %s",
1287 __PRETTY_FUNCTION__,
1288 group_str, group->group_igmp_sock->interface->name);
1289 }
1290
1291 zassert(group->group_filtermode_isexcl);
1292
1293 group->t_group_timer = NULL;
1294 group->group_filtermode_isexcl = 0;
1295
1296 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1297 igmp_anysource_forward_stop(group);
1298
1299 igmp_source_delete_expired(group->group_source_list);
1300
1301 zassert(!group->t_group_timer);
1302 zassert(!group->group_filtermode_isexcl);
1303
1304 /*
1305 RFC 3376: 6.2.2. Definition of Group Timers
1306
1307 If there are no more source records for the group, delete group
1308 record.
1309 */
1310 if (listcount(group->group_source_list) < 1) {
1311 igmp_group_delete_empty_include(group);
1312 }
1313
1314 return 0;
1315 }
1316
1317 static void group_timer_off(struct igmp_group *group)
1318 {
1319 if (!group->t_group_timer)
1320 return;
1321
1322 if (PIM_DEBUG_IGMP_TRACE) {
1323 char group_str[100];
1324 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1325 zlog_debug("Cancelling TIMER event for group %s on %s",
1326 group_str, group->group_igmp_sock->interface->name);
1327 }
1328
1329 THREAD_OFF(group->t_group_timer);
1330 zassert(!group->t_group_timer);
1331 }
1332
1333 void igmp_group_timer_on(struct igmp_group *group,
1334 long interval_msec, const char *ifname)
1335 {
1336 group_timer_off(group);
1337
1338 if (PIM_DEBUG_IGMP_EVENTS) {
1339 char group_str[100];
1340 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1341 zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s on %s",
1342 interval_msec / 1000,
1343 interval_msec % 1000,
1344 group_str, ifname);
1345 }
1346
1347 /*
1348 RFC 3376: 6.2.2. Definition of Group Timers
1349
1350 The group timer is only used when a group is in EXCLUDE mode and
1351 it represents the time for the *filter-mode* of the group to
1352 expire and switch to INCLUDE mode.
1353 */
1354 zassert(group->group_filtermode_isexcl);
1355
1356 THREAD_TIMER_MSEC_ON(master, group->t_group_timer,
1357 igmp_group_timer,
1358 group, interval_msec);
1359 }
1360
1361 static struct igmp_group *find_group_by_addr(struct igmp_sock *igmp,
1362 struct in_addr group_addr)
1363 {
1364 struct igmp_group *group;
1365 struct listnode *node;
1366
1367 for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, node, group))
1368 if (group_addr.s_addr == group->group_addr.s_addr)
1369 return group;
1370
1371 return 0;
1372 }
1373
1374 struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,
1375 struct in_addr group_addr)
1376 {
1377 struct igmp_group *group;
1378
1379 group = find_group_by_addr(igmp, group_addr);
1380 if (group) {
1381 return group;
1382 }
1383
1384 /*
1385 Non-existant group is created as INCLUDE {empty}:
1386
1387 RFC 3376 - 5.1. Action on Change of Interface State
1388
1389 If no interface state existed for that multicast address before
1390 the change (i.e., the change consisted of creating a new
1391 per-interface record), or if no state exists after the change
1392 (i.e., the change consisted of deleting a per-interface record),
1393 then the "non-existent" state is considered to have a filter mode
1394 of INCLUDE and an empty source list.
1395 */
1396
1397 group = XMALLOC(MTYPE_PIM_IGMP_GROUP, sizeof(*group));
1398 if (!group) {
1399 zlog_warn("%s %s: XMALLOC() failure",
1400 __FILE__, __PRETTY_FUNCTION__);
1401 return 0; /* error, not found, could not create */
1402 }
1403
1404 group->group_source_list = list_new();
1405 if (!group->group_source_list) {
1406 zlog_warn("%s %s: list_new() failure",
1407 __FILE__, __PRETTY_FUNCTION__);
1408 XFREE(MTYPE_PIM_IGMP_GROUP, group); /* discard group */
1409 return 0; /* error, not found, could not initialize */
1410 }
1411 group->group_source_list->del = (void (*)(void *)) igmp_source_free;
1412
1413 group->t_group_timer = NULL;
1414 group->t_group_query_retransmit_timer = NULL;
1415 group->group_specific_query_retransmit_count = 0;
1416 group->group_addr = group_addr;
1417 group->group_igmp_sock = igmp;
1418 group->last_igmp_v1_report_dsec = -1;
1419 group->last_igmp_v2_report_dsec = -1;
1420 group->group_creation = pim_time_monotonic_sec();
1421
1422 /* initialize new group as INCLUDE {empty} */
1423 group->group_filtermode_isexcl = 0; /* 0=INCLUDE, 1=EXCLUDE */
1424
1425 listnode_add(igmp->igmp_group_list, group);
1426
1427 if (PIM_DEBUG_IGMP_TRACE) {
1428 char group_str[100];
1429 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1430 zlog_debug("Creating new IGMP group %s on socket %d interface %s",
1431 group_str, igmp->fd, igmp->interface->name);
1432 }
1433
1434 /*
1435 RFC 3376: 6.2.2. Definition of Group Timers
1436
1437 The group timer is only used when a group is in EXCLUDE mode and
1438 it represents the time for the *filter-mode* of the group to
1439 expire and switch to INCLUDE mode.
1440 */
1441 zassert(!group->group_filtermode_isexcl); /* INCLUDE mode */
1442 zassert(!group->t_group_timer); /* group timer == 0 */
1443
1444 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1445 igmp_anysource_forward_stop(group);
1446
1447 return group;
1448 }