3 Copyright (C) 2008 Everton da Silva Marques
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.
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.
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,
20 $QuaggaId: $Format:%an, %ai, %h$ $
29 #include "pim_igmpv3.h"
30 #include "pim_iface.h"
32 #include "pim_mroute.h"
36 #include "pim_zebra.h"
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)
45 static void group_timer_off(struct igmp_group *group);
47 static struct igmp_group *find_group_by_addr(struct igmp_sock *igmp,
48 struct in_addr group_addr);
50 static int igmp_sock_open(struct in_addr ifaddr, ifindex_t ifindex,
57 fd = pim_socket_mcast(IPPROTO_IGMP, ifaddr, 1 /* loop=true */);
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))
67 zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
68 __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
69 PIM_ALL_ROUTERS, errno, safe_strerror(errno));
74 IGMP routers periodically send IGMP general queries to AllSystems=224.0.0.1
75 IGMP routers must receive general queries for querier election.
77 if (inet_aton(PIM_ALL_SYSTEMS, &group)) {
78 if (!pim_socket_join(fd, group, ifaddr, ifindex))
82 zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
83 __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
84 PIM_ALL_SYSTEMS, errno, safe_strerror(errno));
87 if (inet_aton(PIM_ALL_IGMP_ROUTERS, &group)) {
88 if (!pim_socket_join(fd, group, ifaddr, ifindex)) {
93 zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
94 __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
95 PIM_ALL_IGMP_ROUTERS, errno, safe_strerror(errno));
99 zlog_err("IGMP socket fd=%d could not join any group on interface address %s",
100 fd, inet_ntoa(ifaddr));
108 #undef IGMP_SOCK_DUMP
110 #ifdef IGMP_SOCK_DUMP
111 static void igmp_sock_dump(array_t *igmp_sock_array)
113 int size = array_size(igmp_sock_array);
116 for (i = 0; i < size; ++i) {
118 struct igmp_sock *igmp = array_get(igmp_sock_array, i);
120 zlog_debug("%s %s: [%d/%d] igmp_addr=%s fd=%d",
121 __FILE__, __PRETTY_FUNCTION__,
123 inet_ntoa(igmp->ifaddr),
129 struct igmp_sock *pim_igmp_sock_lookup_ifaddr(struct list *igmp_sock_list,
130 struct in_addr ifaddr)
132 struct listnode *sock_node;
133 struct igmp_sock *igmp;
135 #ifdef IGMP_SOCK_DUMP
136 igmp_sock_dump(igmp_sock_list);
139 for (ALL_LIST_ELEMENTS_RO(igmp_sock_list, sock_node, igmp))
140 if (ifaddr.s_addr == igmp->ifaddr.s_addr)
146 struct igmp_sock *igmp_sock_lookup_by_fd(struct list *igmp_sock_list,
149 struct listnode *sock_node;
150 struct igmp_sock *igmp;
152 for (ALL_LIST_ELEMENTS_RO(igmp_sock_list, sock_node, igmp))
159 static int pim_igmp_other_querier_expire(struct thread *t)
161 struct igmp_sock *igmp;
164 igmp = THREAD_ARG(t);
167 zassert(igmp->t_other_querier_timer);
168 zassert(!igmp->t_igmp_query_timer);
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",
178 igmp->t_other_querier_timer = 0;
181 We are the current querier, then
182 re-start sending general queries.
184 pim_igmp_general_query_on(igmp);
189 void pim_igmp_other_querier_timer_on(struct igmp_sock *igmp)
191 long other_querier_present_interval_msec;
192 struct pim_interface *pim_ifp;
195 zassert(igmp->interface);
196 zassert(igmp->interface->info);
198 pim_ifp = igmp->interface->info;
200 if (igmp->t_other_querier_timer) {
202 There is other querier present already,
203 then reset the other-querier-present timer.
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",
213 THREAD_OFF(igmp->t_other_querier_timer);
214 zassert(!igmp->t_other_querier_timer);
218 We are the current querier, then stop sending general queries:
219 igmp->t_igmp_query_timer = 0;
221 pim_igmp_general_query_off(igmp);
225 Since this socket is starting the other-querier-present timer,
226 there should not be periodic query timer for this socket.
228 zassert(!igmp->t_igmp_query_timer);
231 RFC 3376: 8.5. Other Querier Present Interval
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).
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);
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);
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",
254 other_querier_present_interval_msec / 1000,
255 other_querier_present_interval_msec % 1000);
258 THREAD_TIMER_MSEC_ON(master, igmp->t_other_querier_timer,
259 pim_igmp_other_querier_expire,
260 igmp, other_querier_present_interval_msec);
263 void pim_igmp_other_querier_timer_off(struct igmp_sock *igmp)
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);
275 THREAD_OFF(igmp->t_other_querier_timer);
276 zassert(!igmp->t_other_querier_timer);
279 static int recv_igmp_query(struct igmp_sock *igmp, int query_version,
281 struct in_addr from, const char *from_str,
282 char *igmp_msg, int igmp_msg_len)
284 struct interface *ifp;
285 struct pim_interface *pim_ifp;
286 uint8_t resv_s_qrv = 0;
289 struct in_addr group_addr;
290 uint16_t recv_checksum;
294 //group_addr = *(struct in_addr *)(igmp_msg + 4);
295 memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
297 ifp = igmp->interface;
300 recv_checksum = *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET);
302 /* for computing checksum */
303 *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = 0;
305 checksum = in_cksum(igmp_msg, igmp_msg_len);
306 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);
312 if (PIM_DEBUG_IGMP_PACKETS) {
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);
321 RFC 3376: 6.6.2. Querier Election
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
328 if (ntohl(from.s_addr) < ntohl(igmp->ifaddr.s_addr)) {
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)",
335 ifaddr_str, ntohl(igmp->ifaddr.s_addr),
336 from_str, ntohl(from.s_addr));
339 pim_igmp_other_querier_timer_on(igmp);
342 if (query_version == 3) {
344 RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
346 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.
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;
358 RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
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.
365 if (igmp->t_other_querier_timer && query_version == 3) {
366 /* other querier present */
370 qqi = igmp_msg_decode8to16(qqic);
371 igmp->querier_query_interval = qqi ? qqi : pim_ifp->igmp_default_query_interval;
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)",
378 qqi ? "recv-non-default" : "default",
379 igmp->querier_query_interval,
386 RFC 3376: 6.6.1. Timer Updates
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.
392 General queries don't trigger timer update.
394 if (query_version == 3) {
395 s_flag = (1 << 3) & resv_s_qrv;
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.
406 /* s_flag is clear */
408 if (PIM_INADDR_IS_ANY(group_addr)) {
409 /* this is a general query */
411 /* log that general query should have the s_flag set */
412 zlog_warn("General IGMP query v%d from %s on %s: Suppress Router-Side Processing flag is clear",
413 query_version, from_str, ifp->name);
416 struct igmp_group *group;
418 /* this is a non-general query: perform timer updates */
420 group = find_group_by_addr(igmp, group_addr);
422 int recv_num_sources = ntohs(*(uint16_t *)(igmp_msg + IGMP_V3_NUMSOURCES_OFFSET));
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
429 if (recv_num_sources < 1) {
430 /* Query Q(G): Group Timer is lowered to LMQT */
432 igmp_group_timer_lower_to_lmqt(group);
435 /* Query Q(G,A): Source Timer for sources in A are lowered to LMQT */
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);
439 for (i = 0; i < recv_num_sources; ++i) {
440 //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);
447 igmp_source_timer_lower_to_lmqt(src);
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);
460 } /* s_flag is clear: timer updates */
465 static 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)
469 uint16_t recv_checksum;
472 uint8_t *group_record;
473 uint8_t *report_pastend = (uint8_t *) igmp_msg + igmp_msg_len;
474 struct interface *ifp = igmp->interface;
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);
483 recv_checksum = *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET);
485 /* for computing checksum */
486 *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = 0;
488 checksum = in_cksum(igmp_msg, igmp_msg_len);
489 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);
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);
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);
507 group_record = (uint8_t *) igmp_msg + IGMP_V3_REPORT_GROUPPRECORD_OFFSET;
510 for (i = 0; i < num_groups; ++i) {
511 struct in_addr rec_group;
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);
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));
529 //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));
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));
539 sources = group_record + IGMP_V3_GROUP_RECORD_SOURCE_OFFSET;
541 for (j = 0, src = sources; j < rec_num_sources; ++j, src += 4) {
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);
549 if (PIM_DEBUG_IGMP_PACKETS) {
552 if (!inet_ntop(AF_INET, src, src_str , sizeof(src_str)))
553 sprintf(src_str, "<source?>");
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);
558 } /* for (sources) */
561 case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE:
562 igmpv3_report_isin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
564 case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE:
565 igmpv3_report_isex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
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);
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);
573 case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES:
574 igmpv3_report_allow(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
576 case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES:
577 igmpv3_report_block(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
580 zlog_warn("Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
581 from_str, ifp->name, rec_type);
584 group_record += 8 + (rec_num_sources << 2) + (rec_auxdatalen << 2);
586 } /* for (group records) */
591 static void on_trace(const char *label,
592 struct interface *ifp, struct in_addr from)
594 if (PIM_DEBUG_IGMP_TRACE) {
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);
602 static 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)
606 struct interface *ifp = igmp->interface;
607 struct igmp_group *group;
608 struct in_addr group_addr;
610 on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
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);
618 if (PIM_DEBUG_IGMP_TRACE) {
619 zlog_warn("%s %s: FIXME WRITEME",
620 __FILE__, __PRETTY_FUNCTION__);
623 //group_addr = *(struct in_addr *)(igmp_msg + 4);
624 memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
626 /* non-existant group is created as INCLUDE {empty} */
627 group = igmp_add_group_by_addr(igmp, group_addr);
632 group->last_igmp_v2_report_dsec = pim_time_monotonic_dsec();
637 static 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)
641 struct interface *ifp = igmp->interface;
643 on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
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);
651 if (PIM_DEBUG_IGMP_TRACE) {
652 zlog_warn("%s %s: FIXME WRITEME",
653 __FILE__, __PRETTY_FUNCTION__);
659 static 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)
663 struct interface *ifp = igmp->interface;
664 struct igmp_group *group;
665 struct in_addr group_addr;
667 on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
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);
675 if (PIM_DEBUG_IGMP_TRACE) {
676 zlog_warn("%s %s: FIXME WRITEME",
677 __FILE__, __PRETTY_FUNCTION__);
680 //group_addr = *(struct in_addr *)(igmp_msg + 4);
681 memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
683 /* non-existant group is created as INCLUDE {empty} */
684 group = igmp_add_group_by_addr(igmp, group_addr);
689 group->last_igmp_v1_report_dsec = pim_time_monotonic_dsec();
694 int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len)
697 size_t ip_hlen; /* ip header length in bytes */
704 if (len < sizeof(*ip_hdr)) {
705 zlog_warn("IGMP packet size=%zu shorter than minimum=%zu",
706 len, sizeof(*ip_hdr));
710 ip_hdr = (struct ip *) buf;
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));
715 ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */
717 if (PIM_DEBUG_IGMP_PACKETS) {
718 zlog_debug("Recv IP packet from %s to %s on %s: size=%zu ip_header_size=%zu ip_proto=%d",
719 from_str, to_str, igmp->interface->name, len, ip_hlen, ip_hdr->ip_p);
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);
728 if (ip_hlen < PIM_IP_HEADER_MIN_LEN) {
729 zlog_warn("IP packet header size=%zu shorter than minimum=%d",
730 ip_hlen, PIM_IP_HEADER_MIN_LEN);
733 if (ip_hlen > PIM_IP_HEADER_MAX_LEN) {
734 zlog_warn("IP packet header size=%zu greater than maximum=%d",
735 ip_hlen, PIM_IP_HEADER_MAX_LEN);
739 igmp_msg = buf + ip_hlen;
740 msg_type = *igmp_msg;
741 igmp_msg_len = len - ip_hlen;
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,
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);
756 case PIM_IGMP_MEMBERSHIP_QUERY:
758 int max_resp_code = igmp_msg[1];
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
768 if (igmp_msg_len == 8) {
769 query_version = max_resp_code ? 2 : 1;
771 else if (igmp_msg_len >= 12) {
775 zlog_warn("Unknown IGMP query version");
779 return recv_igmp_query(igmp, query_version, max_resp_code,
780 ip_hdr->ip_src, from_str,
781 igmp_msg, igmp_msg_len);
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);
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);
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);
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);
801 zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type);
806 static int pim_igmp_general_query(struct thread *t);
808 void pim_igmp_general_query_on(struct igmp_sock *igmp)
810 struct pim_interface *pim_ifp;
815 zassert(igmp->interface);
818 Since this socket is starting as querier,
819 there should not exist a timer for other-querier-present.
821 zassert(!igmp->t_other_querier_timer);
822 pim_ifp = igmp->interface->info;
826 RFC 3376: 8.6. Startup Query Interval
828 The Startup Query Interval is the interval between General Queries
829 sent by a Querier on startup. Default: 1/4 the Query Interval.
831 startup_mode = igmp->startup_query_count > 0;
833 --igmp->startup_query_count;
835 /* query_interval = pim_ifp->igmp_default_query_interval >> 2; */
836 query_interval = PIM_IGMP_SQI(pim_ifp->igmp_default_query_interval);
839 query_interval = igmp->querier_query_interval;
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",
848 startup_mode ? "startup" : "non-startup",
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);
858 void pim_igmp_general_query_off(struct igmp_sock *igmp)
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);
870 THREAD_OFF(igmp->t_igmp_query_timer);
871 zassert(!igmp->t_igmp_query_timer);
874 /* Issue IGMP general query */
875 static int pim_igmp_general_query(struct thread *t)
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;
885 igmp = THREAD_ARG(t);
888 zassert(igmp->interface);
889 zassert(igmp->interface->info);
891 pim_ifp = igmp->interface->info;
894 RFC3376: 4.1.12. IP Destination Addresses for Queries
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.
902 dst_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
903 group_addr.s_addr = PIM_NET_INADDR_ANY;
905 if (PIM_DEBUG_IGMP_TRACE) {
906 char querier_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);
915 pim_igmp_send_membership_query(0 /* igmp_group */,
917 igmp->interface->name,
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);
928 pim_igmp_general_query_on(igmp);
933 static int pim_igmp_read(struct thread *t);
935 static void igmp_read_on(struct igmp_sock *igmp)
939 if (PIM_DEBUG_IGMP_TRACE) {
940 zlog_debug("Scheduling READ event on IGMP socket fd=%d",
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);
948 static int pim_igmp_read(struct thread *t)
950 struct igmp_sock *igmp;
952 struct sockaddr_in from;
953 struct sockaddr_in to;
954 socklen_t fromlen = sizeof(from);
955 socklen_t tolen = sizeof(to);
956 uint8_t buf[PIM_IGMP_BUFSIZE_READ];
958 ifindex_t ifindex = -1;
959 int result = -1; /* defaults to bad */
963 igmp = THREAD_ARG(t);
969 zassert(fd == igmp->fd);
971 len = pim_socket_recvfromto(fd, buf, sizeof(buf),
976 zlog_warn("Failure receiving IP IGMP packet on fd=%d: errno=%d: %s",
977 fd, errno, safe_strerror(errno));
981 if (PIM_DEBUG_IGMP_PACKETS) {
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?>");
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);
994 #ifdef PIM_CHECK_RECV_IFINDEX_SANITY
995 /* ifindex sanity check */
996 if (ifindex != (int) igmp->interface->ifindex) {
999 struct interface *ifp;
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?>");
1006 ifp = if_lookup_by_index(ifindex);
1008 zassert(ifindex == (int) ifp->ifindex);
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);
1021 if (pim_igmp_packet(igmp, (char *)buf, len)) {
1025 result = 0; /* good */
1033 static void sock_close(struct igmp_sock *igmp)
1035 pim_igmp_other_querier_timer_off(igmp);
1036 pim_igmp_general_query_off(igmp);
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);
1045 THREAD_OFF(igmp->t_igmp_read);
1046 zassert(!igmp->t_igmp_read);
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,
1051 errno, safe_strerror(errno));
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);
1060 void igmp_startup_mode_on(struct igmp_sock *igmp)
1062 struct pim_interface *pim_ifp;
1064 pim_ifp = igmp->interface->info;
1067 RFC 3376: 8.7. Startup Query Count
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.
1073 igmp->startup_query_count = igmp->querier_robustness_variable;
1076 Since we're (re)starting, reset QQI to default Query Interval
1078 igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
1081 static void igmp_group_free(struct igmp_group *group)
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));
1088 list_free(group->group_source_list);
1090 XFREE(MTYPE_PIM_IGMP_GROUP, group);
1093 static void igmp_group_delete(struct igmp_group *group)
1095 struct listnode *src_node;
1096 struct listnode *src_nextnode;
1097 struct igmp_source *src;
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",
1104 group->group_igmp_sock->fd,
1105 group->group_igmp_sock->interface->name);
1108 for (ALL_LIST_ELEMENTS(group->group_source_list, src_node, src_nextnode, src)) {
1109 igmp_source_delete(src);
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);
1117 group_timer_off(group);
1118 listnode_delete(group->group_igmp_sock->igmp_group_list, group);
1119 igmp_group_free(group);
1122 void igmp_group_delete_empty_include(struct igmp_group *group)
1124 zassert(!group->group_filtermode_isexcl);
1125 zassert(!listcount(group->group_source_list));
1127 igmp_group_delete(group);
1130 void igmp_sock_free(struct igmp_sock *igmp)
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));
1138 list_free(igmp->igmp_group_list);
1140 XFREE(MTYPE_PIM_IGMP_SOCKET, igmp);
1143 void igmp_sock_delete(struct igmp_sock *igmp)
1145 struct pim_interface *pim_ifp;
1146 struct listnode *grp_node;
1147 struct listnode *grp_nextnode;
1148 struct igmp_group *grp;
1150 for (ALL_LIST_ELEMENTS(igmp->igmp_group_list, grp_node, grp_nextnode, grp)) {
1151 igmp_group_delete(grp);
1156 pim_ifp = igmp->interface->info;
1158 listnode_delete(pim_ifp->igmp_socket_list, igmp);
1160 igmp_sock_free(igmp);
1163 static struct igmp_sock *igmp_sock_new(int fd,
1164 struct in_addr ifaddr,
1165 struct interface *ifp)
1167 struct pim_interface *pim_ifp;
1168 struct igmp_sock *igmp;
1170 pim_ifp = ifp->info;
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);
1177 igmp = XMALLOC(MTYPE_PIM_IGMP_SOCKET, sizeof(*igmp));
1179 zlog_warn("%s %s: XMALLOC() failure",
1180 __FILE__, __PRETTY_FUNCTION__);
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__);
1190 igmp->igmp_group_list->del = (void (*)(void *)) igmp_group_free;
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();
1202 igmp_startup_mode_on() will reset QQI:
1204 igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
1206 igmp_startup_mode_on(igmp);
1209 pim_igmp_general_query_on(igmp);
1214 struct igmp_sock *pim_igmp_sock_add(struct list *igmp_sock_list,
1215 struct in_addr ifaddr,
1216 struct interface *ifp)
1218 struct pim_interface *pim_ifp;
1219 struct igmp_sock *igmp;
1222 pim_ifp = ifp->info;
1224 fd = igmp_sock_open(ifaddr, ifp->ifindex, pim_ifp->options);
1226 zlog_warn("Could not open IGMP socket for %s on %s",
1227 inet_ntoa(ifaddr), ifp->name);
1231 igmp = igmp_sock_new(fd, ifaddr, ifp);
1233 zlog_err("%s %s: igmp_sock_new() failure",
1234 __FILE__, __PRETTY_FUNCTION__);
1239 listnode_add(igmp_sock_list, igmp);
1241 #ifdef IGMP_SOCK_DUMP
1242 igmp_sock_dump(igmp_sock_array);
1249 RFC 3376: 6.5. Switching Router Filter-Modes
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
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.
1262 static int igmp_group_timer(struct thread *t)
1264 struct igmp_group *group;
1267 group = THREAD_ARG(t);
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);
1278 zassert(group->group_filtermode_isexcl);
1280 group->t_group_timer = 0;
1281 group->group_filtermode_isexcl = 0;
1283 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1284 igmp_anysource_forward_stop(group);
1286 igmp_source_delete_expired(group->group_source_list);
1288 zassert(!group->t_group_timer);
1289 zassert(!group->group_filtermode_isexcl);
1292 RFC 3376: 6.2.2. Definition of Group Timers
1294 If there are no more source records for the group, delete group
1297 if (listcount(group->group_source_list) < 1) {
1298 igmp_group_delete_empty_include(group);
1304 static void group_timer_off(struct igmp_group *group)
1306 if (!group->t_group_timer)
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);
1316 THREAD_OFF(group->t_group_timer);
1317 zassert(!group->t_group_timer);
1320 void igmp_group_timer_on(struct igmp_group *group,
1321 long interval_msec, const char *ifname)
1323 group_timer_off(group);
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,
1335 RFC 3376: 6.2.2. Definition of Group Timers
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.
1341 zassert(group->group_filtermode_isexcl);
1343 THREAD_TIMER_MSEC_ON(master, group->t_group_timer,
1345 group, interval_msec);
1348 static struct igmp_group *find_group_by_addr(struct igmp_sock *igmp,
1349 struct in_addr group_addr)
1351 struct igmp_group *group;
1352 struct listnode *node;
1354 for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, node, group))
1355 if (group_addr.s_addr == group->group_addr.s_addr)
1361 struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,
1362 struct in_addr group_addr)
1364 struct igmp_group *group;
1366 group = find_group_by_addr(igmp, group_addr);
1372 Non-existant group is created as INCLUDE {empty}:
1374 RFC 3376 - 5.1. Action on Change of Interface State
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.
1384 group = XMALLOC(MTYPE_PIM_IGMP_GROUP, sizeof(*group));
1386 zlog_warn("%s %s: XMALLOC() failure",
1387 __FILE__, __PRETTY_FUNCTION__);
1388 return 0; /* error, not found, could not create */
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 */
1398 group->group_source_list->del = (void (*)(void *)) igmp_source_free;
1400 group->t_group_timer = NULL;
1401 group->t_group_query_retransmit_timer = NULL;
1402 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();
1409 /* initialize new group as INCLUDE {empty} */
1410 group->group_filtermode_isexcl = 0; /* 0=INCLUDE, 1=EXCLUDE */
1412 listnode_add(igmp->igmp_group_list, group);
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",
1418 group_str, igmp->fd, igmp->interface->name);
1422 RFC 3376: 6.2.2. Definition of Group Timers
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.
1428 zassert(!group->group_filtermode_isexcl); /* INCLUDE mode */
1429 zassert(!group->t_group_timer); /* group timer == 0 */
1431 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1432 igmp_anysource_forward_stop(group);