]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_igmp.c
pimd: Prevent igmp packet loopback.
[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 "if.h"
27
28 #include "pimd.h"
29 #include "pim_igmp.h"
30 #include "pim_igmpv3.h"
31 #include "pim_iface.h"
32 #include "pim_sock.h"
33 #include "pim_mroute.h"
34 #include "pim_str.h"
35 #include "pim_util.h"
36 #include "pim_time.h"
37 #include "pim_zebra.h"
38
39 #define IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE (1)
40 #define IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE (2)
41 #define IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE (3)
42 #define IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE (4)
43 #define IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES (5)
44 #define IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES (6)
45
46 static void group_timer_off(struct igmp_group *group);
47
48 static struct igmp_group *find_group_by_addr(struct igmp_sock *igmp,
49 struct in_addr group_addr);
50
51 static 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
57 fd = pim_socket_mcast(IPPROTO_IGMP, ifaddr, ifindex, 1 /* loop=true */);
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
111 static 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
127 struct 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
144 struct 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
157 static 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
187 void 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
261 void 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
277 static 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
463 static 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
589 static 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
600 static 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} */
625 group = igmp_add_group_by_addr(igmp, group_addr);
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
635 static 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
657 static 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} */
682 group = igmp_add_group_by_addr(igmp, group_addr);
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
692 int 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 /*
721 * When pim starts up we are joining the 224.0.0.13 and 224.0.0.22 multicast
722 * groups. This is causing the kernel to create a igmp packet that pim
723 * turns around and receives. Therefor if we are the originator
724 * of the igmp packet then we can probably just ignore it.
725 */
726 if (ip_hdr->ip_src.s_addr == igmp->ifaddr.s_addr)
727 {
728 if (PIM_DEBUG_IGMP_PACKETS)
729 zlog_debug ("Received IGMP packet from myself, ignoring");
730 return -1;
731 }
732
733 if (ip_hdr->ip_p != PIM_IP_PROTO_IGMP) {
734 zlog_warn("IP packet protocol=%d is not IGMP=%d",
735 ip_hdr->ip_p, PIM_IP_PROTO_IGMP);
736 return -1;
737 }
738
739 if (ip_hlen < PIM_IP_HEADER_MIN_LEN) {
740 zlog_warn("IP packet header size=%zu shorter than minimum=%d",
741 ip_hlen, PIM_IP_HEADER_MIN_LEN);
742 return -1;
743 }
744 if (ip_hlen > PIM_IP_HEADER_MAX_LEN) {
745 zlog_warn("IP packet header size=%zu greater than maximum=%d",
746 ip_hlen, PIM_IP_HEADER_MAX_LEN);
747 return -1;
748 }
749
750 igmp_msg = buf + ip_hlen;
751 msg_type = *igmp_msg;
752 igmp_msg_len = len - ip_hlen;
753
754 if (PIM_DEBUG_IGMP_PACKETS) {
755 zlog_debug("Recv IGMP packet from %s to %s on %s: ttl=%d msg_type=%d msg_size=%d",
756 from_str, to_str, igmp->interface->name, ip_hdr->ip_ttl, msg_type,
757 igmp_msg_len);
758 }
759
760 if (igmp_msg_len < PIM_IGMP_MIN_LEN) {
761 zlog_warn("IGMP message size=%d shorter than minimum=%d",
762 igmp_msg_len, PIM_IGMP_MIN_LEN);
763 return -1;
764 }
765
766 switch (msg_type) {
767 case PIM_IGMP_MEMBERSHIP_QUERY:
768 {
769 int max_resp_code = igmp_msg[1];
770 int query_version;
771
772 /*
773 RFC 3376: 7.1. Query Version Distinctions
774 IGMPv1 Query: length = 8 octets AND Max Resp Code field is zero
775 IGMPv2 Query: length = 8 octets AND Max Resp Code field is non-zero
776 IGMPv3 Query: length >= 12 octets
777 */
778
779 if (igmp_msg_len == 8) {
780 query_version = max_resp_code ? 2 : 1;
781 }
782 else if (igmp_msg_len >= 12) {
783 query_version = 3;
784 }
785 else {
786 zlog_warn("Unknown IGMP query version");
787 return -1;
788 }
789
790 return recv_igmp_query(igmp, query_version, max_resp_code,
791 ip_hdr->ip_src, from_str,
792 igmp_msg, igmp_msg_len);
793 }
794
795 case PIM_IGMP_V3_MEMBERSHIP_REPORT:
796 return igmp_v3_report(igmp, ip_hdr->ip_src, from_str,
797 igmp_msg, igmp_msg_len);
798
799 case PIM_IGMP_V2_MEMBERSHIP_REPORT:
800 return igmp_v2_report(igmp, ip_hdr->ip_src, from_str,
801 igmp_msg, igmp_msg_len);
802
803 case PIM_IGMP_V1_MEMBERSHIP_REPORT:
804 return igmp_v1_report(igmp, ip_hdr->ip_src, from_str,
805 igmp_msg, igmp_msg_len);
806
807 case PIM_IGMP_V2_LEAVE_GROUP:
808 return igmp_v2_leave(igmp, ip_hdr->ip_src, from_str,
809 igmp_msg, igmp_msg_len);
810 }
811
812 zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type);
813
814 return -1;
815 }
816
817 static int pim_igmp_general_query(struct thread *t);
818
819 void pim_igmp_general_query_on(struct igmp_sock *igmp)
820 {
821 struct pim_interface *pim_ifp;
822 int startup_mode;
823 int query_interval;
824
825 zassert(igmp);
826 zassert(igmp->interface);
827
828 /*
829 Since this socket is starting as querier,
830 there should not exist a timer for other-querier-present.
831 */
832 zassert(!igmp->t_other_querier_timer);
833 pim_ifp = igmp->interface->info;
834 zassert(pim_ifp);
835
836 /*
837 RFC 3376: 8.6. Startup Query Interval
838
839 The Startup Query Interval is the interval between General Queries
840 sent by a Querier on startup. Default: 1/4 the Query Interval.
841 */
842 startup_mode = igmp->startup_query_count > 0;
843 if (startup_mode) {
844 --igmp->startup_query_count;
845
846 /* query_interval = pim_ifp->igmp_default_query_interval >> 2; */
847 query_interval = PIM_IGMP_SQI(pim_ifp->igmp_default_query_interval);
848 }
849 else {
850 query_interval = igmp->querier_query_interval;
851 }
852
853 if (PIM_DEBUG_IGMP_TRACE) {
854 char ifaddr_str[100];
855 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
856 zlog_debug("Querier %s scheduling %d-second (%s) TIMER event for IGMP query on fd=%d",
857 ifaddr_str,
858 query_interval,
859 startup_mode ? "startup" : "non-startup",
860 igmp->fd);
861 }
862 igmp->t_igmp_query_timer = 0;
863 zassert(!igmp->t_igmp_query_timer);
864 THREAD_TIMER_ON(master, igmp->t_igmp_query_timer,
865 pim_igmp_general_query,
866 igmp, query_interval);
867 }
868
869 void pim_igmp_general_query_off(struct igmp_sock *igmp)
870 {
871 zassert(igmp);
872
873 if (PIM_DEBUG_IGMP_TRACE) {
874 if (igmp->t_igmp_query_timer) {
875 char ifaddr_str[100];
876 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
877 zlog_debug("IGMP querier %s fd=%d cancelling query TIMER event on %s",
878 ifaddr_str, igmp->fd, igmp->interface->name);
879 }
880 }
881 THREAD_OFF(igmp->t_igmp_query_timer);
882 zassert(!igmp->t_igmp_query_timer);
883 }
884
885 /* Issue IGMP general query */
886 static int pim_igmp_general_query(struct thread *t)
887 {
888 char query_buf[PIM_IGMP_BUFSIZE_WRITE];
889 struct igmp_sock *igmp;
890 struct in_addr dst_addr;
891 struct in_addr group_addr;
892 struct pim_interface *pim_ifp;
893
894 zassert(t);
895
896 igmp = THREAD_ARG(t);
897
898 zassert(igmp);
899 zassert(igmp->interface);
900 zassert(igmp->interface->info);
901
902 pim_ifp = igmp->interface->info;
903
904 /*
905 RFC3376: 4.1.12. IP Destination Addresses for Queries
906
907 In IGMPv3, General Queries are sent with an IP destination address
908 of 224.0.0.1, the all-systems multicast address. Group-Specific
909 and Group-and-Source-Specific Queries are sent with an IP
910 destination address equal to the multicast address of interest.
911 */
912
913 dst_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
914 group_addr.s_addr = PIM_NET_INADDR_ANY;
915
916 if (PIM_DEBUG_IGMP_TRACE) {
917 char querier_str[100];
918 char dst_str[100];
919 pim_inet4_dump("<querier?>", igmp->ifaddr, querier_str,
920 sizeof(querier_str));
921 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
922 zlog_debug("Querier %s issuing IGMP general query to %s on %s",
923 querier_str, dst_str, igmp->interface->name);
924 }
925
926 pim_igmp_send_membership_query(0 /* igmp_group */,
927 igmp->fd,
928 igmp->interface->name,
929 query_buf,
930 sizeof(query_buf),
931 0 /* num_sources */,
932 dst_addr,
933 group_addr,
934 pim_ifp->igmp_query_max_response_time_dsec,
935 1 /* s_flag: always set for general queries */,
936 igmp->querier_robustness_variable,
937 igmp->querier_query_interval);
938
939 pim_igmp_general_query_on(igmp);
940
941 return 0;
942 }
943
944 static int pim_igmp_read(struct thread *t);
945
946 static void igmp_read_on(struct igmp_sock *igmp)
947 {
948 zassert(igmp);
949
950 if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
951 zlog_debug("Scheduling READ event on IGMP socket fd=%d",
952 igmp->fd);
953 }
954 igmp->t_igmp_read = 0;
955 zassert(!igmp->t_igmp_read);
956 THREAD_READ_ON(master, igmp->t_igmp_read, pim_igmp_read, igmp, igmp->fd);
957 }
958
959 static int pim_igmp_read(struct thread *t)
960 {
961 struct igmp_sock *igmp;
962 int fd;
963 struct sockaddr_in from;
964 struct sockaddr_in to;
965 socklen_t fromlen = sizeof(from);
966 socklen_t tolen = sizeof(to);
967 uint8_t buf[PIM_IGMP_BUFSIZE_READ];
968 int len;
969 int ifindex = -1;
970 int result = -1; /* defaults to bad */
971
972 zassert(t);
973
974 igmp = THREAD_ARG(t);
975
976 zassert(igmp);
977
978 fd = THREAD_FD(t);
979
980 zassert(fd == igmp->fd);
981
982 len = pim_socket_recvfromto(fd, buf, sizeof(buf),
983 &from, &fromlen,
984 &to, &tolen,
985 &ifindex);
986 if (len < 0) {
987 zlog_warn("Failure receiving IP IGMP packet on fd=%d: errno=%d: %s",
988 fd, errno, safe_strerror(errno));
989 goto done;
990 }
991
992 if (PIM_DEBUG_IGMP_PACKETS) {
993 char from_str[100];
994 char to_str[100];
995
996 if (!inet_ntop(AF_INET, &from.sin_addr, from_str, sizeof(from_str)))
997 sprintf(from_str, "<from?>");
998 if (!inet_ntop(AF_INET, &to.sin_addr, to_str, sizeof(to_str)))
999 sprintf(to_str, "<to?>");
1000
1001 zlog_debug("Recv IP IGMP pkt size=%d from %s to %s on fd=%d on ifindex=%d (sock_ifindex=%d)",
1002 len, from_str, to_str, fd, ifindex, igmp->interface->ifindex);
1003 }
1004
1005 #ifdef PIM_CHECK_RECV_IFINDEX_SANITY
1006 /* ifindex sanity check */
1007 if (ifindex != (int) igmp->interface->ifindex) {
1008 char from_str[100];
1009 char to_str[100];
1010 struct interface *ifp;
1011
1012 if (!inet_ntop(AF_INET, &from.sin_addr, from_str , sizeof(from_str)))
1013 sprintf(from_str, "<from?>");
1014 if (!inet_ntop(AF_INET, &to.sin_addr, to_str , sizeof(to_str)))
1015 sprintf(to_str, "<to?>");
1016
1017 ifp = if_lookup_by_index(ifindex);
1018 if (ifp) {
1019 zassert(ifindex == (int) ifp->ifindex);
1020 }
1021
1022 #ifdef PIM_REPORT_RECV_IFINDEX_MISMATCH
1023 zlog_warn("Interface mismatch: recv IGMP pkt from %s to %s on fd=%d: recv_ifindex=%d (%s) sock_ifindex=%d (%s)",
1024 from_str, to_str, fd,
1025 ifindex, ifp ? ifp->name : "<if-notfound>",
1026 igmp->interface->ifindex, igmp->interface->name);
1027 #endif
1028 goto done;
1029 }
1030 #endif
1031
1032 if (pim_igmp_packet(igmp, (char *)buf, len)) {
1033 goto done;
1034 }
1035
1036 result = 0; /* good */
1037
1038 done:
1039 igmp_read_on(igmp);
1040
1041 return result;
1042 }
1043
1044 static void sock_close(struct igmp_sock *igmp)
1045 {
1046 pim_igmp_other_querier_timer_off(igmp);
1047 pim_igmp_general_query_off(igmp);
1048
1049 if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
1050 if (igmp->t_igmp_read) {
1051 zlog_debug("Cancelling READ event on IGMP socket %s fd=%d on interface %s",
1052 inet_ntoa(igmp->ifaddr), igmp->fd,
1053 igmp->interface->name);
1054 }
1055 }
1056 THREAD_OFF(igmp->t_igmp_read);
1057 zassert(!igmp->t_igmp_read);
1058
1059 if (close(igmp->fd)) {
1060 zlog_err("Failure closing IGMP socket %s fd=%d on interface %s: errno=%d: %s",
1061 inet_ntoa(igmp->ifaddr), igmp->fd, igmp->interface->name,
1062 errno, safe_strerror(errno));
1063 }
1064
1065 if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
1066 zlog_debug("Deleted IGMP socket %s fd=%d on interface %s",
1067 inet_ntoa(igmp->ifaddr), igmp->fd, igmp->interface->name);
1068 }
1069 }
1070
1071 void igmp_startup_mode_on(struct igmp_sock *igmp)
1072 {
1073 struct pim_interface *pim_ifp;
1074
1075 pim_ifp = igmp->interface->info;
1076
1077 /*
1078 RFC 3376: 8.7. Startup Query Count
1079
1080 The Startup Query Count is the number of Queries sent out on
1081 startup, separated by the Startup Query Interval. Default: the
1082 Robustness Variable.
1083 */
1084 igmp->startup_query_count = igmp->querier_robustness_variable;
1085
1086 /*
1087 Since we're (re)starting, reset QQI to default Query Interval
1088 */
1089 igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
1090 }
1091
1092 static void igmp_group_free(struct igmp_group *group)
1093 {
1094 list_free(group->group_source_list);
1095
1096 XFREE(MTYPE_PIM_IGMP_GROUP, group);
1097 }
1098
1099 static void igmp_group_delete(struct igmp_group *group)
1100 {
1101 struct listnode *src_node;
1102 struct listnode *src_nextnode;
1103 struct igmp_source *src;
1104
1105 if (PIM_DEBUG_IGMP_TRACE) {
1106 char group_str[100];
1107 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1108 zlog_debug("Deleting IGMP group %s from socket %d interface %s",
1109 group_str,
1110 group->group_igmp_sock->fd,
1111 group->group_igmp_sock->interface->name);
1112 }
1113
1114 for (ALL_LIST_ELEMENTS(group->group_source_list, src_node, src_nextnode, src)) {
1115 igmp_source_delete(src);
1116 }
1117
1118 if (group->t_group_query_retransmit_timer) {
1119 THREAD_OFF(group->t_group_query_retransmit_timer);
1120 }
1121
1122 group_timer_off(group);
1123 listnode_delete(group->group_igmp_sock->igmp_group_list, group);
1124 igmp_group_free(group);
1125 }
1126
1127 void igmp_group_delete_empty_include(struct igmp_group *group)
1128 {
1129 zassert(!group->group_filtermode_isexcl);
1130 zassert(!listcount(group->group_source_list));
1131
1132 igmp_group_delete(group);
1133 }
1134
1135 void igmp_sock_free(struct igmp_sock *igmp)
1136 {
1137 zassert(!igmp->t_igmp_read);
1138 zassert(!igmp->t_igmp_query_timer);
1139 zassert(!igmp->t_other_querier_timer);
1140 zassert(igmp->igmp_group_list);
1141 zassert(!listcount(igmp->igmp_group_list));
1142
1143 list_free(igmp->igmp_group_list);
1144
1145 XFREE(MTYPE_PIM_IGMP_SOCKET, igmp);
1146 }
1147
1148 void igmp_sock_delete(struct igmp_sock *igmp)
1149 {
1150 struct pim_interface *pim_ifp;
1151 struct listnode *grp_node;
1152 struct listnode *grp_nextnode;
1153 struct igmp_group *grp;
1154
1155 for (ALL_LIST_ELEMENTS(igmp->igmp_group_list, grp_node, grp_nextnode, grp)) {
1156 igmp_group_delete(grp);
1157 }
1158
1159 sock_close(igmp);
1160
1161 pim_ifp = igmp->interface->info;
1162
1163 listnode_delete(pim_ifp->igmp_socket_list, igmp);
1164
1165 igmp_sock_free(igmp);
1166 }
1167
1168 static struct igmp_sock *igmp_sock_new(int fd,
1169 struct in_addr ifaddr,
1170 struct interface *ifp)
1171 {
1172 struct pim_interface *pim_ifp;
1173 struct igmp_sock *igmp;
1174
1175 pim_ifp = ifp->info;
1176
1177 if (PIM_DEBUG_IGMP_TRACE) {
1178 zlog_debug("Creating IGMP socket fd=%d for address %s on interface %s",
1179 fd, inet_ntoa(ifaddr), ifp->name);
1180 }
1181
1182 igmp = XMALLOC(MTYPE_PIM_IGMP_SOCKET, sizeof(*igmp));
1183 if (!igmp) {
1184 zlog_warn("%s %s: XMALLOC() failure",
1185 __FILE__, __PRETTY_FUNCTION__);
1186 return 0;
1187 }
1188
1189 igmp->igmp_group_list = list_new();
1190 if (!igmp->igmp_group_list) {
1191 zlog_err("%s %s: failure: igmp_group_list = list_new()",
1192 __FILE__, __PRETTY_FUNCTION__);
1193 return 0;
1194 }
1195 igmp->igmp_group_list->del = (void (*)(void *)) igmp_group_free;
1196
1197 igmp->fd = fd;
1198 igmp->interface = ifp;
1199 igmp->ifaddr = ifaddr;
1200 igmp->t_igmp_read = 0;
1201 igmp->t_igmp_query_timer = 0;
1202 igmp->t_other_querier_timer = 0; /* no other querier present */
1203 igmp->querier_robustness_variable = pim_ifp->igmp_default_robustness_variable;
1204 igmp->sock_creation = pim_time_monotonic_sec();
1205
1206 /*
1207 igmp_startup_mode_on() will reset QQI:
1208
1209 igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
1210 */
1211 igmp_startup_mode_on(igmp);
1212
1213 igmp_read_on(igmp);
1214 pim_igmp_general_query_on(igmp);
1215
1216 return igmp;
1217 }
1218
1219 struct igmp_sock *pim_igmp_sock_add(struct list *igmp_sock_list,
1220 struct in_addr ifaddr,
1221 struct interface *ifp)
1222 {
1223 struct pim_interface *pim_ifp;
1224 struct igmp_sock *igmp;
1225 int fd;
1226
1227 pim_ifp = ifp->info;
1228
1229 fd = igmp_sock_open(ifaddr, ifp->ifindex, pim_ifp->options);
1230 if (fd < 0) {
1231 zlog_warn("Could not open IGMP socket for %s on %s",
1232 inet_ntoa(ifaddr), ifp->name);
1233 return 0;
1234 }
1235
1236 igmp = igmp_sock_new(fd, ifaddr, ifp);
1237 if (!igmp) {
1238 zlog_err("%s %s: igmp_sock_new() failure",
1239 __FILE__, __PRETTY_FUNCTION__);
1240 close(fd);
1241 return 0;
1242 }
1243
1244 listnode_add(igmp_sock_list, igmp);
1245
1246 #ifdef IGMP_SOCK_DUMP
1247 igmp_sock_dump(igmp_sock_array);
1248 #endif
1249
1250 return igmp;
1251 }
1252
1253 /*
1254 RFC 3376: 6.5. Switching Router Filter-Modes
1255
1256 When a router's filter-mode for a group is EXCLUDE and the group
1257 timer expires, the router filter-mode for the group transitions to
1258 INCLUDE.
1259
1260 A router uses source records with running source timers as its state
1261 for the switch to a filter-mode of INCLUDE. If there are any source
1262 records with source timers greater than zero (i.e., requested to be
1263 forwarded), a router switches to filter-mode of INCLUDE using those
1264 source records. Source records whose timers are zero (from the
1265 previous EXCLUDE mode) are deleted.
1266 */
1267 static int igmp_group_timer(struct thread *t)
1268 {
1269 struct igmp_group *group;
1270
1271 zassert(t);
1272 group = THREAD_ARG(t);
1273 zassert(group);
1274
1275 if (PIM_DEBUG_IGMP_TRACE) {
1276 char group_str[100];
1277 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1278 zlog_debug("%s: Timer for group %s on interface %s",
1279 __PRETTY_FUNCTION__,
1280 group_str, group->group_igmp_sock->interface->name);
1281 }
1282
1283 zassert(group->group_filtermode_isexcl);
1284
1285 group->t_group_timer = NULL;
1286 group->group_filtermode_isexcl = 0;
1287
1288 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1289 igmp_anysource_forward_stop(group);
1290
1291 igmp_source_delete_expired(group->group_source_list);
1292
1293 zassert(!group->t_group_timer);
1294 zassert(!group->group_filtermode_isexcl);
1295
1296 /*
1297 RFC 3376: 6.2.2. Definition of Group Timers
1298
1299 If there are no more source records for the group, delete group
1300 record.
1301 */
1302 if (listcount(group->group_source_list) < 1) {
1303 igmp_group_delete_empty_include(group);
1304 }
1305
1306 return 0;
1307 }
1308
1309 static void group_timer_off(struct igmp_group *group)
1310 {
1311 if (!group->t_group_timer)
1312 return;
1313
1314 if (PIM_DEBUG_IGMP_TRACE) {
1315 char group_str[100];
1316 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1317 zlog_debug("Cancelling TIMER event for group %s on %s",
1318 group_str, group->group_igmp_sock->interface->name);
1319 }
1320
1321 THREAD_OFF(group->t_group_timer);
1322 zassert(!group->t_group_timer);
1323 }
1324
1325 void igmp_group_timer_on(struct igmp_group *group,
1326 long interval_msec, const char *ifname)
1327 {
1328 group_timer_off(group);
1329
1330 if (PIM_DEBUG_IGMP_EVENTS) {
1331 char group_str[100];
1332 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1333 zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s on %s",
1334 interval_msec / 1000,
1335 interval_msec % 1000,
1336 group_str, ifname);
1337 }
1338
1339 /*
1340 RFC 3376: 6.2.2. Definition of Group Timers
1341
1342 The group timer is only used when a group is in EXCLUDE mode and
1343 it represents the time for the *filter-mode* of the group to
1344 expire and switch to INCLUDE mode.
1345 */
1346 zassert(group->group_filtermode_isexcl);
1347
1348 THREAD_TIMER_MSEC_ON(master, group->t_group_timer,
1349 igmp_group_timer,
1350 group, interval_msec);
1351 }
1352
1353 static struct igmp_group *find_group_by_addr(struct igmp_sock *igmp,
1354 struct in_addr group_addr)
1355 {
1356 struct igmp_group *group;
1357 struct listnode *node;
1358
1359 for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, node, group))
1360 if (group_addr.s_addr == group->group_addr.s_addr)
1361 return group;
1362
1363 return 0;
1364 }
1365
1366 struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,
1367 struct in_addr group_addr)
1368 {
1369 struct igmp_group *group;
1370
1371 group = find_group_by_addr(igmp, group_addr);
1372 if (group) {
1373 return group;
1374 }
1375
1376 /*
1377 Non-existant group is created as INCLUDE {empty}:
1378
1379 RFC 3376 - 5.1. Action on Change of Interface State
1380
1381 If no interface state existed for that multicast address before
1382 the change (i.e., the change consisted of creating a new
1383 per-interface record), or if no state exists after the change
1384 (i.e., the change consisted of deleting a per-interface record),
1385 then the "non-existent" state is considered to have a filter mode
1386 of INCLUDE and an empty source list.
1387 */
1388
1389 group = XMALLOC(MTYPE_PIM_IGMP_GROUP, sizeof(*group));
1390 if (!group) {
1391 zlog_warn("%s %s: XMALLOC() failure",
1392 __FILE__, __PRETTY_FUNCTION__);
1393 return 0; /* error, not found, could not create */
1394 }
1395
1396 group->group_source_list = list_new();
1397 if (!group->group_source_list) {
1398 zlog_warn("%s %s: list_new() failure",
1399 __FILE__, __PRETTY_FUNCTION__);
1400 XFREE(MTYPE_PIM_IGMP_GROUP, group); /* discard group */
1401 return 0; /* error, not found, could not initialize */
1402 }
1403 group->group_source_list->del = (void (*)(void *)) igmp_source_free;
1404
1405 group->t_group_timer = NULL;
1406 group->t_group_query_retransmit_timer = NULL;
1407 group->group_specific_query_retransmit_count = 0;
1408 group->group_addr = group_addr;
1409 group->group_igmp_sock = igmp;
1410 group->last_igmp_v1_report_dsec = -1;
1411 group->last_igmp_v2_report_dsec = -1;
1412 group->group_creation = pim_time_monotonic_sec();
1413
1414 /* initialize new group as INCLUDE {empty} */
1415 group->group_filtermode_isexcl = 0; /* 0=INCLUDE, 1=EXCLUDE */
1416
1417 listnode_add(igmp->igmp_group_list, group);
1418
1419 if (PIM_DEBUG_IGMP_TRACE) {
1420 char group_str[100];
1421 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1422 zlog_debug("Creating new IGMP group %s on socket %d interface %s",
1423 group_str, igmp->fd, igmp->interface->name);
1424 }
1425
1426 /*
1427 RFC 3376: 6.2.2. Definition of Group Timers
1428
1429 The group timer is only used when a group is in EXCLUDE mode and
1430 it represents the time for the *filter-mode* of the group to
1431 expire and switch to INCLUDE mode.
1432 */
1433 zassert(!group->group_filtermode_isexcl); /* INCLUDE mode */
1434 zassert(!group->t_group_timer); /* group timer == 0 */
1435
1436 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1437 igmp_anysource_forward_stop(group);
1438
1439 return group;
1440 }