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