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