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