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