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