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