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