summaryrefslogtreecommitdiffstats
path: root/pimd
diff options
context:
space:
mode:
authorDaniel Walton <dwalton@cumulusnetworks.com>2016-10-20 15:34:29 +0200
committerDonald Sharp <sharpd@cumulusnetworks.com>2016-12-22 02:26:11 +0100
commitb05b72e80b840fd8628b890cb6fa5c8ca7d73dd3 (patch)
treea8d5f0314de5725102affaf9b0968a030548ea5c /pimd
parentpimd: Allow debugs entered in conf t mode to persist (diff)
downloadfrr-b05b72e80b840fd8628b890cb6fa5c8ca7d73dd3.tar.xz
frr-b05b72e80b840fd8628b890cb6fa5c8ca7d73dd3.zip
pimd: add support for IGMPv2
Signed-off-by: Daniel Walton <dwalton@cumulusnetworks.com> Ticket: CM-7962
Diffstat (limited to 'pimd')
-rw-r--r--pimd/Makefile.am4
-rw-r--r--pimd/pim_cmd.c76
-rw-r--r--pimd/pim_iface.c1
-rw-r--r--pimd/pim_iface.h1
-rw-r--r--pimd/pim_igmp.c592
-rw-r--r--pimd/pim_igmp.h20
-rw-r--r--pimd/pim_igmpv2.c190
-rw-r--r--pimd/pim_igmpv2.h41
-rw-r--r--pimd/pim_igmpv3.c465
-rw-r--r--pimd/pim_igmpv3.h44
-rw-r--r--pimd/pim_mroute.c114
-rw-r--r--pimd/pim_vty.c9
12 files changed, 888 insertions, 669 deletions
diff --git a/pimd/Makefile.am b/pimd/Makefile.am
index 07679f279..bb3d1844c 100644
--- a/pimd/Makefile.am
+++ b/pimd/Makefile.am
@@ -48,7 +48,7 @@ noinst_PROGRAMS = test_igmpv3_join
libpim_a_SOURCES = \
pim_memory.c \
pimd.c pim_version.c pim_cmd.c pim_signals.c pim_iface.c \
- pim_vty.c pim_igmp.c pim_sock.c pim_zebra.c \
+ pim_vty.c pim_igmp.c pim_sock.c pim_zebra.c pim_igmpv2.c \
pim_igmpv3.c pim_str.c pim_mroute.c pim_util.c pim_time.c \
pim_oil.c pim_zlookup.c pim_pim.c pim_tlv.c pim_neighbor.c \
pim_hello.c pim_ifchannel.c pim_join.c pim_assert.c \
@@ -60,7 +60,7 @@ libpim_a_SOURCES = \
noinst_HEADERS = \
pim_memory.h \
pimd.h pim_version.h pim_cmd.h pim_signals.h pim_iface.h \
- pim_vty.h pim_igmp.h pim_sock.h pim_zebra.h \
+ pim_vty.h pim_igmp.h pim_sock.h pim_zebra.h pim_igmpv2.h \
pim_igmpv3.h pim_str.h pim_mroute.h pim_util.h pim_time.h \
pim_oil.h pim_zlookup.h pim_pim.h pim_tlv.h pim_neighbor.h \
pim_hello.h pim_ifchannel.h pim_join.h pim_assert.h \
diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c
index e9b3a4e13..c6390271f 100644
--- a/pimd/pim_cmd.c
+++ b/pimd/pim_cmd.c
@@ -510,7 +510,7 @@ static void igmp_show_interfaces(struct vty *vty, u_char uj)
json = json_object_new_object();
else
vty_out(vty,
- "Interface State Address Querier Query Timer Uptime%s",
+ "Interface State Address V Querier Query Timer Uptime%s",
VTY_NEWLINE);
for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) {
@@ -534,6 +534,7 @@ static void igmp_show_interfaces(struct vty *vty, u_char uj)
json_row = json_object_new_object();
json_object_pim_ifp_add(json_row, ifp);
json_object_string_add(json_row, "upTime", uptime);
+ json_object_int_add(json_row, "version", pim_ifp->igmp_version);
if (igmp->t_igmp_query_timer) {
json_object_boolean_true_add(json_row, "querier");
@@ -543,10 +544,11 @@ static void igmp_show_interfaces(struct vty *vty, u_char uj)
json_object_object_add(json, ifp->name, json_row);
} else {
- vty_out(vty, "%-9s %5s %15s %7s %11s %8s%s",
+ vty_out(vty, "%-9s %5s %15s %d %7s %11s %8s%s",
ifp->name,
if_is_up(ifp) ? "up" : "down",
inet_ntoa(igmp->ifaddr),
+ pim_ifp->igmp_version,
igmp->t_igmp_query_timer ? "local" : "other",
query_hhmmss,
uptime,
@@ -627,12 +629,12 @@ static void igmp_show_interfaces_single(struct vty *vty, const char *ifname, u_c
if (uj) {
json_row = json_object_new_object();
json_object_pim_ifp_add(json_row, ifp);
-
json_object_string_add(json_row, "upTime", uptime);
json_object_string_add(json_row, "querier", igmp->t_igmp_query_timer ? "local" : "other");
json_object_int_add(json_row, "queryStartCount", igmp->startup_query_count);
json_object_string_add(json_row, "queryQueryTimer", query_hhmmss);
json_object_string_add(json_row, "queryOtherTimer", other_hhmmss);
+ json_object_int_add(json_row, "version", pim_ifp->igmp_version);
json_object_int_add(json_row, "timerGroupMembershipIntervalMsec", gmi_msec);
json_object_int_add(json_row, "timerLastMemberQueryMsec", lmqt_msec);
json_object_int_add(json_row, "timerOlderHostPresentIntervalMsec", ohpi_msec);
@@ -649,6 +651,7 @@ static void igmp_show_interfaces_single(struct vty *vty, const char *ifname, u_c
vty_out(vty, "State : %s%s", if_is_up(ifp) ? "up" : "down", VTY_NEWLINE);
vty_out(vty, "Address : %s%s", inet_ntoa(pim_ifp->primary_address), VTY_NEWLINE);
vty_out(vty, "Uptime : %s%s", uptime, VTY_NEWLINE);
+ vty_out(vty, "Version : %d%s", pim_ifp->igmp_version, VTY_NEWLINE);
vty_out(vty, "%s", VTY_NEWLINE);
vty_out(vty, "%s", VTY_NEWLINE);
@@ -1974,10 +1977,13 @@ static void igmp_show_groups(struct vty *vty, u_char uj)
json_row = json_object_new_object();
json_object_string_add(json_row, "source", ifaddr_str);
json_object_string_add(json_row, "group", group_str);
- json_object_string_add(json_row, "mode", grp->group_filtermode_isexcl ? "EXCLUDE" : "INCLUDE");
+
+ if (grp->igmp_version == 3)
+ json_object_string_add(json_row, "mode", grp->group_filtermode_isexcl ? "EXCLUDE" : "INCLUDE");
+
json_object_string_add(json_row, "timer", hhmmss);
json_object_int_add(json_row, "sourcesCount", grp->group_source_list ? listcount(grp->group_source_list) : 0);
- json_object_int_add(json_row, "version", igmp_group_compat_mode(igmp, grp));
+ json_object_int_add(json_row, "version", grp->igmp_version);
json_object_string_add(json_row, "uptime", uptime);
json_object_object_add(json_iface, group_str, json_row);
@@ -1986,10 +1992,10 @@ static void igmp_show_groups(struct vty *vty, u_char uj)
ifp->name,
ifaddr_str,
group_str,
- grp->group_filtermode_isexcl ? "EXCL" : "INCL",
+ grp->igmp_version == 3 ? (grp->group_filtermode_isexcl ? "EXCL" : "INCL") : "----",
hhmmss,
grp->group_source_list ? listcount(grp->group_source_list) : 0,
- igmp_group_compat_mode(igmp, grp),
+ grp->igmp_version,
uptime,
VTY_NEWLINE);
}
@@ -3914,6 +3920,56 @@ DEFUN (interface_no_ip_igmp_query_interval,
return CMD_SUCCESS;
}
+DEFUN (interface_ip_igmp_version,
+ interface_ip_igmp_version_cmd,
+ "ip igmp version <2-3>",
+ IP_STR
+ IFACE_IGMP_STR
+ "IGMP version\n"
+ "IGMP version number\n")
+{
+ VTY_DECLVAR_CONTEXT(interface,ifp);
+ struct pim_interface *pim_ifp;
+ int igmp_version;
+
+ pim_ifp = ifp->info;
+
+ if (!pim_ifp) {
+ vty_out(vty,
+ "IGMP not enabled on interface %s. Please enable IGMP first.%s",
+ ifp->name,
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ igmp_version = atoi(argv[3]->arg);
+ pim_ifp->igmp_version = igmp_version;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (interface_no_ip_igmp_version,
+ interface_no_ip_igmp_version_cmd,
+ "no ip igmp version <2-3>",
+ NO_STR
+ IP_STR
+ IFACE_IGMP_STR
+ "IGMP version\n"
+ "IGMP version number\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct pim_interface *pim_ifp;
+
+ pim_ifp = ifp->info;
+
+ if (!pim_ifp)
+ return CMD_SUCCESS;
+
+ pim_ifp->igmp_version = IGMP_DEFAULT_VERSION;
+
+ return CMD_SUCCESS;
+}
+
#define IGMP_QUERY_MAX_RESPONSE_TIME_MIN (1)
#define IGMP_QUERY_MAX_RESPONSE_TIME_MAX (25)
@@ -5073,7 +5129,7 @@ DEFUN (test_igmp_receive_report,
igmp_msg = buf + ip_hlen;
group_record = igmp_msg + IGMP_V3_REPORT_GROUPPRECORD_OFFSET;
*igmp_msg = PIM_IGMP_V3_MEMBERSHIP_REPORT; /* type */
- *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = 0; /* for computing checksum */
+ *(uint16_t *) (igmp_msg + IGMP_CHECKSUM_OFFSET) = 0; /* for computing checksum */
*(uint16_t *) (igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET) = htons(1); /* one group record */
*(uint8_t *) (group_record + IGMP_V3_GROUP_RECORD_TYPE_OFFSET) = record_type;
memcpy(group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET, &grp_addr, sizeof(struct in_addr));
@@ -5097,7 +5153,7 @@ DEFUN (test_igmp_receive_report,
igmp_msg_len = IGMP_V3_MSG_MIN_SIZE + (num_sources << 4); /* v3 report for one single group record */
/* compute checksum */
- *(uint16_t *)(igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = in_cksum(igmp_msg, igmp_msg_len);
+ *(uint16_t *)(igmp_msg + IGMP_CHECKSUM_OFFSET) = in_cksum(igmp_msg, igmp_msg_len);
/* "receive" message */
@@ -5731,6 +5787,8 @@ void pim_cmd_init()
install_element (INTERFACE_NODE, &interface_no_ip_igmp_cmd);
install_element (INTERFACE_NODE, &interface_ip_igmp_join_cmd);
install_element (INTERFACE_NODE, &interface_no_ip_igmp_join_cmd);
+ install_element (INTERFACE_NODE, &interface_ip_igmp_version_cmd);
+ install_element (INTERFACE_NODE, &interface_no_ip_igmp_version_cmd);
install_element (INTERFACE_NODE, &interface_ip_igmp_query_interval_cmd);
install_element (INTERFACE_NODE, &interface_no_ip_igmp_query_interval_cmd);
install_element (INTERFACE_NODE, &interface_ip_igmp_query_max_response_time_cmd);
diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c
index b6012d1ed..ceab93edf 100644
--- a/pimd/pim_iface.c
+++ b/pimd/pim_iface.c
@@ -106,6 +106,7 @@ struct pim_interface *pim_if_new(struct interface *ifp, int igmp, int pim)
pim_ifp->options = 0;
pim_ifp->mroute_vif_index = -1;
+ pim_ifp->igmp_version = IGMP_DEFAULT_VERSION;
pim_ifp->igmp_default_robustness_variable = IGMP_DEFAULT_ROBUSTNESS_VARIABLE;
pim_ifp->igmp_default_query_interval = IGMP_GENERAL_QUERY_INTERVAL;
pim_ifp->igmp_query_max_response_time_dsec = IGMP_QUERY_MAX_RESPONSE_TIME_DSEC;
diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h
index 0be4918d7..87530c602 100644
--- a/pimd/pim_iface.h
+++ b/pimd/pim_iface.h
@@ -65,6 +65,7 @@ struct pim_interface {
ifindex_t mroute_vif_index;
struct in_addr primary_address; /* remember addr to detect change */
+ int igmp_version; /* IGMP version */
int igmp_default_robustness_variable; /* IGMPv3 QRV */
int igmp_default_query_interval; /* IGMPv3 secs between general queries */
int igmp_query_max_response_time_dsec; /* IGMPv3 Max Response Time in dsecs for general queries */
diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c
index e14ff2922..56f2457f9 100644
--- a/pimd/pim_igmp.c
+++ b/pimd/pim_igmp.c
@@ -27,6 +27,7 @@
#include "pimd.h"
#include "pim_igmp.h"
+#include "pim_igmpv2.h"
#include "pim_igmpv3.h"
#include "pim_iface.h"
#include "pim_sock.h"
@@ -36,34 +37,31 @@
#include "pim_time.h"
#include "pim_zebra.h"
-#define IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE (1)
-#define IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE (2)
-#define IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE (3)
-#define IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE (4)
-#define IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES (5)
-#define IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES (6)
-
static void group_timer_off(struct igmp_group *group);
+/* This socket is used for TXing IGMP packets only, IGMP RX happens
+ * in pim_mroute_msg()
+ */
static int igmp_sock_open(struct in_addr ifaddr, ifindex_t ifindex, uint32_t pim_options)
{
int fd;
int join = 0;
struct in_addr group;
- fd = pim_socket_mcast(IPPROTO_IGMP, ifaddr, ifindex, 1 /* loop=true */);
+ fd = pim_socket_mcast(IPPROTO_IGMP, ifaddr, ifindex, 0 /* loop=false */);
+
if (fd < 0)
return -1;
if (PIM_IF_TEST_IGMP_LISTEN_ALLROUTERS(pim_options)) {
if (inet_aton(PIM_ALL_ROUTERS, &group)) {
if (!pim_socket_join(fd, group, ifaddr, ifindex))
- ++join;
+ ++join;
}
else {
zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
- __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
- PIM_ALL_ROUTERS, errno, safe_strerror(errno));
+ __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
+ PIM_ALL_ROUTERS, errno, safe_strerror(errno));
}
}
@@ -77,8 +75,8 @@ static int igmp_sock_open(struct in_addr ifaddr, ifindex_t ifindex, uint32_t pim
}
else {
zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
- __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
- PIM_ALL_SYSTEMS, errno, safe_strerror(errno));
+ __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
+ PIM_ALL_SYSTEMS, errno, safe_strerror(errno));
}
if (inet_aton(PIM_ALL_IGMP_ROUTERS, &group)) {
@@ -88,13 +86,13 @@ static int igmp_sock_open(struct in_addr ifaddr, ifindex_t ifindex, uint32_t pim
}
else {
zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
- __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
- PIM_ALL_IGMP_ROUTERS, errno, safe_strerror(errno));
+ __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr),
+ PIM_ALL_IGMP_ROUTERS, errno, safe_strerror(errno));
}
if (!join) {
zlog_err("IGMP socket fd=%d could not join any group on interface address %s",
- fd, inet_ntoa(ifaddr));
+ fd, inet_ntoa(ifaddr));
close(fd);
fd = -1;
}
@@ -271,31 +269,27 @@ void pim_igmp_other_querier_timer_off(struct igmp_sock *igmp)
zassert(!igmp->t_other_querier_timer);
}
-static int recv_igmp_query(struct igmp_sock *igmp, int query_version,
- int max_resp_code,
- struct in_addr from, const char *from_str,
- char *igmp_msg, int igmp_msg_len)
+static int
+igmp_recv_query(struct igmp_sock *igmp, int query_version,
+ int max_resp_code,
+ struct in_addr from, const char *from_str,
+ char *igmp_msg, int igmp_msg_len)
{
struct interface *ifp;
struct pim_interface *pim_ifp;
- uint8_t resv_s_qrv = 0;
- uint8_t s_flag = 0;
- uint8_t qrv = 0;
struct in_addr group_addr;
uint16_t recv_checksum;
uint16_t checksum;
- int i;
- //group_addr = *(struct in_addr *)(igmp_msg + 4);
memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
ifp = igmp->interface;
pim_ifp = ifp->info;
- recv_checksum = *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET);
+ recv_checksum = *(uint16_t *) (igmp_msg + IGMP_CHECKSUM_OFFSET);
/* for computing checksum */
- *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = 0;
+ *(uint16_t *) (igmp_msg + IGMP_CHECKSUM_OFFSET) = 0;
checksum = in_cksum(igmp_msg, igmp_msg_len);
if (checksum != recv_checksum) {
@@ -304,12 +298,33 @@ static int recv_igmp_query(struct igmp_sock *igmp, int query_version,
return -1;
}
+ /* RFC 3376 defines some guidelines on operating in backwards compatibility
+ * with older versions of IGMP but there are some gaps in the logic:
+ *
+ * - once we drop from say version 3 to version 2 we will never go back to
+ * version 3 even if the node that TXed an IGMP v2 query upgrades to v3
+ *
+ * - The node with the lowest IP is the querier so we will only know to drop
+ * from v3 to v2 if the node that is the querier is also the one that is
+ * running igmp v2. If a non-querier only supports igmp v2 we will have
+ * no way of knowing.
+ *
+ * For now we will simplify things and inform the user that they need to
+ * configure all PIM routers to use the same version of IGMP.
+ */
+ if (query_version != pim_ifp->igmp_version) {
+ zlog_warn("Recv IGMP query v%d from %s on %s but we are using v%d, please "
+ "configure all PIM routers on this subnet to use the same "
+ "IGMP version",
+ query_version, from_str, ifp->name, pim_ifp->igmp_version);
+ return 0;
+ }
+
if (PIM_DEBUG_IGMP_PACKETS) {
char group_str[100];
pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
- zlog_debug("Recv IGMP query v%d from %s on %s: size=%d checksum=%x group=%s",
- query_version, from_str, ifp->name,
- igmp_msg_len, checksum, group_str);
+ zlog_debug("Recv IGMP query v%d from %s on %s for group %s",
+ query_version, from_str, ifp->name, group_str);
}
/*
@@ -321,7 +336,7 @@ static int recv_igmp_query(struct igmp_sock *igmp, int query_version,
elected querier.
*/
if (ntohl(from.s_addr) < ntohl(igmp->ifaddr.s_addr)) {
-
+
if (PIM_DEBUG_IGMP_TRACE) {
char ifaddr_str[100];
pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
@@ -334,272 +349,11 @@ static int recv_igmp_query(struct igmp_sock *igmp, int query_version,
pim_igmp_other_querier_timer_on(igmp);
}
+ /* IGMP version 3 is the only one where we process the RXed query */
if (query_version == 3) {
- /*
- RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
-
- Routers adopt the QRV value from the most recently received Query
- as their own [Robustness Variable] value, unless that most
- recently received QRV was zero, in which case the receivers use
- the default [Robustness Variable] value specified in section 8.1
- or a statically configured value.
- */
- resv_s_qrv = igmp_msg[8];
- qrv = 7 & resv_s_qrv;
- igmp->querier_robustness_variable = qrv ? qrv : pim_ifp->igmp_default_robustness_variable;
- }
-
- /*
- RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
-
- Multicast routers that are not the current querier adopt the QQI
- value from the most recently received Query as their own [Query
- Interval] value, unless that most recently received QQI was zero,
- in which case the receiving routers use the default.
- */
- if (igmp->t_other_querier_timer && query_version == 3) {
- /* other querier present */
- uint8_t qqic;
- uint16_t qqi;
- qqic = igmp_msg[9];
- qqi = igmp_msg_decode8to16(qqic);
- igmp->querier_query_interval = qqi ? qqi : pim_ifp->igmp_default_query_interval;
-
- if (PIM_DEBUG_IGMP_TRACE) {
- char ifaddr_str[100];
- pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
- zlog_debug("Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
- ifaddr_str,
- qqi ? "recv-non-default" : "default",
- igmp->querier_query_interval,
- qqic,
- from_str);
- }
- }
-
- /*
- RFC 3376: 6.6.1. Timer Updates
-
- When a router sends or receives a query with a clear Suppress
- Router-Side Processing flag, it must update its timers to reflect
- the correct timeout values for the group or sources being queried.
-
- General queries don't trigger timer update.
- */
- if (query_version == 3) {
- s_flag = (1 << 3) & resv_s_qrv;
- }
- else {
- /* Neither V1 nor V2 have this field. Pimd should really go into
- * a compatibility mode here and run as V2 (or V1) but it doesn't
- * so for now, lets just set the flag to suppress these timer updates.
- */
- s_flag = 1;
- }
-
- if (!s_flag) {
- /* s_flag is clear */
-
- if (PIM_INADDR_IS_ANY(group_addr)) {
- /* this is a general query */
-
- /* log that general query should have the s_flag set */
- zlog_warn("General IGMP query v%d from %s on %s: Suppress Router-Side Processing flag is clear",
- query_version, from_str, ifp->name);
- }
- else {
- struct igmp_group *group;
-
- /* this is a non-general query: perform timer updates */
-
- group = find_group_by_addr(igmp, group_addr);
- if (group) {
- int recv_num_sources = ntohs(*(uint16_t *)(igmp_msg + IGMP_V3_NUMSOURCES_OFFSET));
-
- /*
- RFC 3376: 6.6.1. Timer Updates
- Query Q(G,A): Source Timer for sources in A are lowered to LMQT
- Query Q(G): Group Timer is lowered to LMQT
- */
- if (recv_num_sources < 1) {
- /* Query Q(G): Group Timer is lowered to LMQT */
-
- igmp_group_timer_lower_to_lmqt(group);
- }
- else {
- /* Query Q(G,A): Source Timer for sources in A are lowered to LMQT */
-
- /* Scan sources in query and lower their timers to LMQT */
- struct in_addr *sources = (struct in_addr *)(igmp_msg + IGMP_V3_SOURCES_OFFSET);
- for (i = 0; i < recv_num_sources; ++i) {
- //struct in_addr src_addr = sources[i];
- //struct igmp_source *src = igmp_find_source_by_addr(group, src_addr);
- struct in_addr src_addr;
- struct igmp_source *src;
- memcpy(&src_addr, sources + i, sizeof(struct in_addr));
- src = igmp_find_source_by_addr(group, src_addr);
- if (src) {
- igmp_source_timer_lower_to_lmqt(src);
- }
- }
- }
-
- }
- else {
- char group_str[100];
- pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
- zlog_warn("IGMP query v%d from %s on %s: could not find group %s for timer update",
- query_version, from_str, ifp->name, group_str);
- }
- }
- } /* s_flag is clear: timer updates */
-
- return 0;
-}
-
-static int igmp_v3_report(struct igmp_sock *igmp,
- struct in_addr from, const char *from_str,
- char *igmp_msg, int igmp_msg_len)
-{
- uint16_t recv_checksum;
- uint16_t checksum;
- int num_groups;
- uint8_t *group_record;
- uint8_t *report_pastend = (uint8_t *) igmp_msg + igmp_msg_len;
- struct interface *ifp = igmp->interface;
- int i;
- int local_ncb = 0;
-
- if (igmp_msg_len < IGMP_V3_MSG_MIN_SIZE) {
- zlog_warn("Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
- from_str, ifp->name, igmp_msg_len, IGMP_V3_MSG_MIN_SIZE);
- return -1;
- }
-
- recv_checksum = *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET);
-
- /* for computing checksum */
- *(uint16_t *) (igmp_msg + IGMP_V3_CHECKSUM_OFFSET) = 0;
-
- checksum = in_cksum(igmp_msg, igmp_msg_len);
- if (checksum != recv_checksum) {
- zlog_warn("Recv IGMP report v3 from %s on %s: checksum mismatch: received=%x computed=%x",
- from_str, ifp->name, recv_checksum, checksum);
- return -1;
+ igmp_v3_recv_query(igmp, from_str, igmp_msg);
}
- num_groups = ntohs(*(uint16_t *) (igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET));
- if (num_groups < 1) {
- zlog_warn("Recv IGMP report v3 from %s on %s: missing group records",
- from_str, ifp->name);
- return -1;
- }
-
- if (PIM_DEBUG_IGMP_PACKETS) {
- zlog_debug("Recv IGMP report v3 from %s on %s: size=%d checksum=%x groups=%d",
- from_str, ifp->name, igmp_msg_len, checksum, num_groups);
- }
-
- group_record = (uint8_t *) igmp_msg + IGMP_V3_REPORT_GROUPPRECORD_OFFSET;
-
- /* Scan groups */
- for (i = 0; i < num_groups; ++i) {
- struct in_addr rec_group;
- uint8_t *sources;
- uint8_t *src;
- int rec_type;
- int rec_auxdatalen;
- int rec_num_sources;
- int j;
- struct prefix lncb;
- struct prefix g;
-
- if ((group_record + IGMP_V3_GROUP_RECORD_MIN_SIZE) > report_pastend) {
- zlog_warn("Recv IGMP report v3 from %s on %s: group record beyond report end",
- from_str, ifp->name);
- return -1;
- }
-
- rec_type = group_record[IGMP_V3_GROUP_RECORD_TYPE_OFFSET];
- rec_auxdatalen = group_record[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET];
- rec_num_sources = ntohs(* (uint16_t *) (group_record + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET));
-
- //rec_group = *(struct in_addr *)(group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET);
- memcpy(&rec_group, group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET, sizeof(struct in_addr));
-
- if (PIM_DEBUG_IGMP_PACKETS) {
- zlog_debug("Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%s",
- from_str, ifp->name, i, rec_type, rec_auxdatalen, rec_num_sources, inet_ntoa(rec_group));
- }
-
- /* Scan sources */
-
- sources = group_record + IGMP_V3_GROUP_RECORD_SOURCE_OFFSET;
-
- for (j = 0, src = sources; j < rec_num_sources; ++j, src += 4) {
-
- if ((src + 4) > report_pastend) {
- zlog_warn("Recv IGMP report v3 from %s on %s: group source beyond report end",
- from_str, ifp->name);
- return -1;
- }
-
- if (PIM_DEBUG_IGMP_PACKETS) {
- char src_str[200];
-
- if (!inet_ntop(AF_INET, src, src_str , sizeof(src_str)))
- sprintf(src_str, "<source?>");
-
- zlog_debug("Recv IGMP report v3 from %s on %s: record=%d group=%s source=%s",
- from_str, ifp->name, i, inet_ntoa(rec_group), src_str);
- }
- } /* for (sources) */
-
-
- lncb.family = AF_INET;
- lncb.u.prefix4.s_addr = 0x000000E0;
- lncb.prefixlen = 24;
-
- g.family = AF_INET;
- g.u.prefix4 = rec_group;
- g.prefixlen = 32;
- /*
- * If we receive a igmp report with the group in 224.0.0.0/24
- * then we should ignore it
- */
- if (prefix_match(&lncb, &g))
- local_ncb = 1;
-
- if (!local_ncb)
- switch (rec_type) {
- case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE:
- igmpv3_report_isin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
- break;
- case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE:
- igmpv3_report_isex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
- break;
- case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE:
- igmpv3_report_toin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
- break;
- case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE:
- igmpv3_report_toex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
- break;
- case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES:
- igmpv3_report_allow(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
- break;
- case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES:
- igmpv3_report_block(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
- break;
- default:
- zlog_warn("Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
- from_str, ifp->name, rec_type);
- }
-
- group_record += 8 + (rec_num_sources << 2) + (rec_auxdatalen << 2);
- local_ncb = 0;
-
- } /* for (group records) */
-
return 0;
}
@@ -614,66 +368,10 @@ static void on_trace(const char *label,
}
}
-static int igmp_v2_report(struct igmp_sock *igmp,
- struct in_addr from, const char *from_str,
- char *igmp_msg, int igmp_msg_len)
-{
- struct interface *ifp = igmp->interface;
- struct igmp_group *group;
- struct in_addr group_addr;
-
- on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
-
- if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
- zlog_warn("Recv IGMP report v2 from %s on %s: size=%d other than correct=%d",
- from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
- return -1;
- }
-
- if (PIM_DEBUG_IGMP_TRACE) {
- zlog_warn("%s %s: FIXME WRITEME",
- __FILE__, __PRETTY_FUNCTION__);
- }
-
- //group_addr = *(struct in_addr *)(igmp_msg + 4);
- memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
-
- /* non-existant group is created as INCLUDE {empty} */
- group = igmp_add_group_by_addr(igmp, group_addr);
- if (!group) {
- return -1;
- }
-
- group->last_igmp_v2_report_dsec = pim_time_monotonic_dsec();
-
- return 0;
-}
-
-static int igmp_v2_leave(struct igmp_sock *igmp,
- struct in_addr from, const char *from_str,
- char *igmp_msg, int igmp_msg_len)
-{
- struct interface *ifp = igmp->interface;
-
- on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
-
- if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
- zlog_warn("Recv IGMP leave v2 from %s on %s: size=%d other than correct=%d",
- from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
- return -1;
- }
-
- if (PIM_DEBUG_IGMP_TRACE) {
- zlog_warn("%s %s: FIXME WRITEME",
- __FILE__, __PRETTY_FUNCTION__);
- }
-
- return 0;
-}
-
-static int igmp_v1_report(struct igmp_sock *igmp,
- struct in_addr from, const char *from_str,
- char *igmp_msg, int igmp_msg_len)
+static int
+igmp_v1_recv_report (struct igmp_sock *igmp,
+ struct in_addr from, const char *from_str,
+ char *igmp_msg, int igmp_msg_len)
{
struct interface *ifp = igmp->interface;
struct igmp_group *group;
@@ -692,7 +390,6 @@ static int igmp_v1_report(struct igmp_sock *igmp,
__FILE__, __PRETTY_FUNCTION__);
}
- //group_addr = *(struct in_addr *)(igmp_msg + 4);
memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
/* non-existant group is created as INCLUDE {empty} */
@@ -791,26 +488,26 @@ int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len)
return -1;
}
- return recv_igmp_query(igmp, query_version, max_resp_code,
+ return igmp_recv_query(igmp, query_version, max_resp_code,
ip_hdr->ip_src, from_str,
igmp_msg, igmp_msg_len);
}
case PIM_IGMP_V3_MEMBERSHIP_REPORT:
- return igmp_v3_report(igmp, ip_hdr->ip_src, from_str,
- igmp_msg, igmp_msg_len);
+ return igmp_v3_recv_report(igmp, ip_hdr->ip_src, from_str,
+ igmp_msg, igmp_msg_len);
case PIM_IGMP_V2_MEMBERSHIP_REPORT:
- return igmp_v2_report(igmp, ip_hdr->ip_src, from_str,
- igmp_msg, igmp_msg_len);
+ return igmp_v2_recv_report(igmp, ip_hdr->ip_src, from_str,
+ igmp_msg, igmp_msg_len);
case PIM_IGMP_V1_MEMBERSHIP_REPORT:
- return igmp_v1_report(igmp, ip_hdr->ip_src, from_str,
- igmp_msg, igmp_msg_len);
+ return igmp_v1_recv_report(igmp, ip_hdr->ip_src, from_str,
+ igmp_msg, igmp_msg_len);
case PIM_IGMP_V2_LEAVE_GROUP:
- return igmp_v2_leave(igmp, ip_hdr->ip_src, from_str,
- igmp_msg, igmp_msg_len);
+ return igmp_v2_recv_leave(igmp, ip_hdr->ip_src, from_str,
+ igmp_msg, igmp_msg_len);
}
zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type);
@@ -895,11 +592,11 @@ void pim_igmp_general_query_off(struct igmp_sock *igmp)
/* Issue IGMP general query */
static int pim_igmp_general_query(struct thread *t)
{
- char query_buf[PIM_IGMP_BUFSIZE_WRITE];
struct igmp_sock *igmp;
struct in_addr dst_addr;
struct in_addr group_addr;
struct pim_interface *pim_ifp;
+ int query_buf_size;
zassert(t);
@@ -911,6 +608,14 @@ static int pim_igmp_general_query(struct thread *t)
pim_ifp = igmp->interface->info;
+ if (pim_ifp->igmp_version == 3) {
+ query_buf_size = PIM_IGMP_BUFSIZE_WRITE;
+ } else {
+ query_buf_size = IGMP_V12_MSG_SIZE;
+ }
+
+ char query_buf[query_buf_size];
+
/*
RFC3376: 4.1.12. IP Destination Addresses for Queries
@@ -933,124 +638,25 @@ static int pim_igmp_general_query(struct thread *t)
querier_str, dst_str, igmp->interface->name);
}
- pim_igmp_send_membership_query(0 /* igmp_group */,
- igmp->fd,
- igmp->interface->name,
- query_buf,
- sizeof(query_buf),
- 0 /* num_sources */,
- dst_addr,
- group_addr,
- pim_ifp->igmp_query_max_response_time_dsec,
- 1 /* s_flag: always set for general queries */,
- igmp->querier_robustness_variable,
- igmp->querier_query_interval);
+ igmp_send_query (pim_ifp->igmp_version,
+ 0 /* igmp_group */,
+ igmp->fd,
+ igmp->interface->name,
+ query_buf,
+ sizeof(query_buf),
+ 0 /* num_sources */,
+ dst_addr,
+ group_addr,
+ pim_ifp->igmp_query_max_response_time_dsec,
+ 1 /* s_flag: always set for general queries */,
+ igmp->querier_robustness_variable,
+ igmp->querier_query_interval);
pim_igmp_general_query_on(igmp);
return 0;
}
-static int pim_igmp_read(struct thread *t);
-
-static void igmp_read_on(struct igmp_sock *igmp)
-{
- zassert(igmp);
-
- if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
- zlog_debug("Scheduling READ event on IGMP socket fd=%d",
- igmp->fd);
- }
- igmp->t_igmp_read = NULL;
- zassert(!igmp->t_igmp_read);
- THREAD_READ_ON(master, igmp->t_igmp_read, pim_igmp_read, igmp, igmp->fd);
-}
-
-static int pim_igmp_read(struct thread *t)
-{
- struct igmp_sock *igmp;
- int fd;
- struct sockaddr_in from;
- struct sockaddr_in to;
- socklen_t fromlen = sizeof(from);
- socklen_t tolen = sizeof(to);
- uint8_t buf[PIM_IGMP_BUFSIZE_READ];
- int len;
- ifindex_t ifindex = -1;
- int result = -1; /* defaults to bad */
-
- zassert(t);
-
- igmp = THREAD_ARG(t);
-
- zassert(igmp);
-
- fd = THREAD_FD(t);
-
- zassert(fd == igmp->fd);
-
- len = pim_socket_recvfromto(fd, buf, sizeof(buf),
- &from, &fromlen,
- &to, &tolen,
- &ifindex);
- if (len < 0) {
- zlog_warn("Failure receiving IP IGMP packet on fd=%d: errno=%d: %s",
- fd, errno, safe_strerror(errno));
- goto done;
- }
-
- if (PIM_DEBUG_IGMP_PACKETS) {
- char from_str[100];
- char to_str[100];
-
- if (!inet_ntop(AF_INET, &from.sin_addr, from_str, sizeof(from_str)))
- sprintf(from_str, "<from?>");
- if (!inet_ntop(AF_INET, &to.sin_addr, to_str, sizeof(to_str)))
- sprintf(to_str, "<to?>");
-
- zlog_debug("Recv IP IGMP pkt size=%d from %s to %s on fd=%d on ifindex=%d (sock_ifindex=%d)",
- len, from_str, to_str, fd, ifindex, igmp->interface->ifindex);
- }
-
-#ifdef PIM_CHECK_RECV_IFINDEX_SANITY
- /* ifindex sanity check */
- if (ifindex != igmp->interface->ifindex) {
- char from_str[100];
- char to_str[100];
- struct interface *ifp;
-
- if (!inet_ntop(AF_INET, &from.sin_addr, from_str , sizeof(from_str)))
- sprintf(from_str, "<from?>");
- if (!inet_ntop(AF_INET, &to.sin_addr, to_str , sizeof(to_str)))
- sprintf(to_str, "<to?>");
-
- ifp = if_lookup_by_index(ifindex);
- if (ifp) {
- zassert(ifindex == ifp->ifindex);
- }
-
-#ifdef PIM_REPORT_RECV_IFINDEX_MISMATCH
- zlog_warn("Interface mismatch: recv IGMP pkt from %s to %s on fd=%d: recv_ifindex=%d (%s) sock_ifindex=%d (%s)",
- from_str, to_str, fd,
- ifindex, ifp ? ifp->name : "<if-notfound>",
- igmp->interface->ifindex, igmp->interface->name);
-#endif
- goto done;
- }
-#endif
-
- if (pim_igmp_packet(igmp, (char *)buf, len)) {
- goto done;
- }
-
- result = 0; /* good */
-
- done:
- igmp_read_on(igmp);
-
- return result;
-}
-
static void sock_close(struct igmp_sock *igmp)
{
pim_igmp_other_querier_timer_off(igmp);
@@ -1219,8 +825,6 @@ static struct igmp_sock *igmp_sock_new(int fd,
igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
*/
igmp_startup_mode_on(igmp);
-
- igmp_read_on(igmp);
pim_igmp_general_query_on(igmp);
return igmp;
@@ -1421,6 +1025,7 @@ struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,
group->last_igmp_v1_report_dsec = -1;
group->last_igmp_v2_report_dsec = -1;
group->group_creation = pim_time_monotonic_sec();
+ group->igmp_version = IGMP_DEFAULT_VERSION;
/* initialize new group as INCLUDE {empty} */
group->group_filtermode_isexcl = 0; /* 0=INCLUDE, 1=EXCLUDE */
@@ -1449,3 +1054,32 @@ struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,
return group;
}
+
+void
+igmp_send_query (int igmp_version,
+ struct igmp_group *group,
+ int fd,
+ const char *ifname,
+ char *query_buf,
+ int query_buf_size,
+ int num_sources,
+ struct in_addr dst_addr,
+ struct in_addr group_addr,
+ int query_max_response_time_dsec,
+ uint8_t s_flag,
+ uint8_t querier_robustness_variable,
+ uint16_t querier_query_interval)
+{
+ if (igmp_version == 3) {
+ igmp_v3_send_query (group, fd, ifname, query_buf,
+ query_buf_size, num_sources,
+ dst_addr, group_addr,
+ query_max_response_time_dsec, s_flag,
+ querier_robustness_variable,
+ querier_query_interval);
+ } else if (igmp_version == 2) {
+ igmp_v2_send_query (group, fd, ifname, query_buf,
+ dst_addr, group_addr,
+ query_max_response_time_dsec);
+ }
+}
diff --git a/pimd/pim_igmp.h b/pimd/pim_igmp.h
index 2067bb30a..fa2c255dd 100644
--- a/pimd/pim_igmp.h
+++ b/pimd/pim_igmp.h
@@ -52,6 +52,7 @@
#define IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET (2)
#define IGMP_V3_GROUP_RECORD_GROUP_OFFSET (4)
#define IGMP_V3_GROUP_RECORD_SOURCE_OFFSET (8)
+#define IGMP_CHECKSUM_OFFSET (2)
/* RFC 3376: 8.1. Robustness Variable - Default: 2 */
#define IGMP_DEFAULT_ROBUSTNESS_VARIABLE (2)
@@ -65,6 +66,8 @@
/* RFC 3376: 8.8. Last Member Query Interval - Default: 10 deciseconds */
#define IGMP_SPECIFIC_QUERY_MAX_RESPONSE_TIME_DSEC (10)
+#define IGMP_DEFAULT_VERSION (3)
+
struct igmp_join {
struct in_addr group_addr;
struct in_addr source_addr;
@@ -152,6 +155,9 @@ struct igmp_group {
since sources have their counters) */
int group_specific_query_retransmit_count;
+ /* compatibility mode - igmp v1, v2 or v3 */
+ int igmp_version;
+
struct in_addr group_addr;
int group_filtermode_isexcl; /* 0=INCLUDE, 1=EXCLUDE */
struct list *group_source_list; /* list of struct igmp_source */
@@ -176,4 +182,18 @@ void igmp_group_timer_on(struct igmp_group *group,
struct igmp_source *
source_new (struct igmp_group *group,
struct in_addr src_addr);
+
+void igmp_send_query(int igmp_version,
+ struct igmp_group *group,
+ int fd,
+ const char *ifname,
+ char *query_buf,
+ int query_buf_size,
+ int num_sources,
+ struct in_addr dst_addr,
+ struct in_addr group_addr,
+ int query_max_response_time_dsec,
+ uint8_t s_flag,
+ uint8_t querier_robustness_variable,
+ uint16_t querier_query_interval);
#endif /* PIM_IGMP_H */
diff --git a/pimd/pim_igmpv2.c b/pimd/pim_igmpv2.c
new file mode 100644
index 000000000..cc78f18d7
--- /dev/null
+++ b/pimd/pim_igmpv2.c
@@ -0,0 +1,190 @@
+/*
+ * PIM for Quagga
+ * Copyright (C) 2016 Cumulus Networks, Inc.
+ * Daniel Walton
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include "pimd.h"
+#include "pim_igmp.h"
+#include "pim_igmpv2.h"
+#include "pim_igmpv3.h"
+#include "pim_str.h"
+#include "pim_time.h"
+#include "pim_util.h"
+
+
+static void
+on_trace (const char *label,
+ struct interface *ifp, struct in_addr from)
+{
+ if (PIM_DEBUG_IGMP_TRACE) {
+ char from_str[100];
+ pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
+ zlog_debug("%s: from %s on %s",
+ label, from_str, ifp->name);
+ }
+}
+
+void
+igmp_v2_send_query (struct igmp_group *group,
+ int fd,
+ const char *ifname,
+ char *query_buf,
+ struct in_addr dst_addr,
+ struct in_addr group_addr,
+ int query_max_response_time_dsec)
+{
+ ssize_t msg_size = 8;
+ uint8_t max_resp_code;
+ ssize_t sent;
+ struct sockaddr_in to;
+ socklen_t tolen;
+ uint16_t checksum;
+
+ /* max_resp_code must be non-zero else this will look like an IGMP v1 query */
+ max_resp_code = igmp_msg_encode16to8(query_max_response_time_dsec);
+ zassert(max_resp_code > 0);
+
+ query_buf[0] = PIM_IGMP_MEMBERSHIP_QUERY;
+ query_buf[1] = max_resp_code;
+ *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) = 0; /* for computing checksum */
+ memcpy(query_buf+4, &group_addr, sizeof(struct in_addr));
+
+ checksum = in_cksum(query_buf, msg_size);
+ *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) = checksum;
+
+ if (PIM_DEBUG_IGMP_PACKETS) {
+ char dst_str[100];
+ char group_str[100];
+ pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
+ pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
+ zlog_debug("Send IGMPv2 QUERY to %s on %s for group %s",
+ dst_str, ifname, group_str);
+ }
+
+ memset(&to, 0, sizeof(to));
+ to.sin_family = AF_INET;
+ to.sin_addr = dst_addr;
+ tolen = sizeof(to);
+
+ sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT,
+ (struct sockaddr *)&to, tolen);
+ if (sent != (ssize_t) msg_size) {
+ char dst_str[100];
+ char group_str[100];
+ pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
+ pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
+ if (sent < 0) {
+ zlog_warn("Send IGMPv2 QUERY failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
+ dst_str, ifname, group_str, msg_size, errno, safe_strerror(errno));
+ }
+ else {
+ zlog_warn("Send IGMPv2 QUERY failed due to %s on %s: group=%s msg_size=%zd: sent=%zd",
+ dst_str, ifname, group_str, msg_size, sent);
+ }
+ return;
+ }
+}
+
+int
+igmp_v2_recv_report (struct igmp_sock *igmp,
+ struct in_addr from, const char *from_str,
+ char *igmp_msg, int igmp_msg_len)
+{
+ struct interface *ifp = igmp->interface;
+ struct in_addr group_addr;
+ char group_str[100];
+
+ on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
+
+ if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
+ zlog_warn("Recv IGMPv2 REPORT from %s on %s: size=%d other than correct=%d",
+ from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
+ return -1;
+ }
+
+ memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
+
+ if (PIM_DEBUG_IGMP_PACKETS) {
+ pim_inet4_dump("<dst?>", group_addr, group_str, sizeof(group_str));
+ zlog_debug("Recv IGMPv2 REPORT from %s on %s for %s",
+ from_str, ifp->name, group_str);
+ }
+
+ /*
+ * RFC 3376
+ * 7.3.2. In the Presence of Older Version Group Members
+ *
+ * When Group Compatibility Mode is IGMPv2, a router internally
+ * translates the following IGMPv2 messages for that group to their
+ * IGMPv3 equivalents:
+ *
+ * IGMPv2 Message IGMPv3 Equivalent
+ * -------------- -----------------
+ * Report IS_EX( {} )
+ * Leave TO_IN( {} )
+ */
+ igmpv3_report_isex (igmp, from, group_addr, 0, NULL, 1);
+
+ return 0;
+}
+
+int
+igmp_v2_recv_leave (struct igmp_sock *igmp,
+ struct in_addr from, const char *from_str,
+ char *igmp_msg, int igmp_msg_len)
+{
+ struct interface *ifp = igmp->interface;
+ struct in_addr group_addr;
+ char group_str[100];
+
+ on_trace(__PRETTY_FUNCTION__, igmp->interface, from);
+
+ if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
+ zlog_warn("Recv IGMPv2 LEAVE from %s on %s: size=%d other than correct=%d",
+ from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
+ return -1;
+ }
+
+ memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
+
+ if (PIM_DEBUG_IGMP_PACKETS) {
+ pim_inet4_dump("<dst?>", group_addr, group_str, sizeof(group_str));
+ zlog_debug("Recv IGMPv2 LEAVE from %s on %s for %s",
+ from_str, ifp->name, group_str);
+ }
+
+ memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
+
+ /*
+ * RFC 3376
+ * 7.3.2. In the Presence of Older Version Group Members
+ *
+ * When Group Compatibility Mode is IGMPv2, a router internally
+ * translates the following IGMPv2 messages for that group to their
+ * IGMPv3 equivalents:
+ *
+ * IGMPv2 Message IGMPv3 Equivalent
+ * -------------- -----------------
+ * Report IS_EX( {} )
+ * Leave TO_IN( {} )
+ */
+ igmpv3_report_toin (igmp, from, group_addr, 0, NULL);
+
+ return 0;
+}
diff --git a/pimd/pim_igmpv2.h b/pimd/pim_igmpv2.h
new file mode 100644
index 000000000..10a247772
--- /dev/null
+++ b/pimd/pim_igmpv2.h
@@ -0,0 +1,41 @@
+/*
+ * PIM for Quagga
+ * Copyright (C) 2016 Cumulus Networks, Inc.
+ * Daniel Walton
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#ifndef PIM_IGMPV2_H
+#define PIM_IGMPV2_H
+
+void igmp_v2_send_query (struct igmp_group *group,
+ int fd,
+ const char *ifname,
+ char *query_buf,
+ struct in_addr dst_addr,
+ struct in_addr group_addr,
+ int query_max_response_time_dsec);
+
+int igmp_v2_recv_report (struct igmp_sock *igmp,
+ struct in_addr from, const char *from_str,
+ char *igmp_msg, int igmp_msg_len);
+
+int igmp_v2_recv_leave (struct igmp_sock *igmp,
+ struct in_addr from, const char *from_str,
+ char *igmp_msg, int igmp_msg_len);
+
+#endif /* PIM_IGMPV2_H */
diff --git a/pimd/pim_igmpv3.c b/pimd/pim_igmpv3.c
index ba75fc221..21a5176a9 100644
--- a/pimd/pim_igmpv3.c
+++ b/pimd/pim_igmpv3.c
@@ -58,50 +58,6 @@ static void on_trace(const char *label,
}
}
-int igmp_group_compat_mode(const struct igmp_sock *igmp,
- const struct igmp_group *group)
-{
- struct pim_interface *pim_ifp;
- int64_t now_dsec;
- long older_host_present_interval_dsec;
-
- zassert(igmp);
- zassert(igmp->interface);
- zassert(igmp->interface->info);
-
- pim_ifp = igmp->interface->info;
-
- /*
- RFC 3376: 8.13. Older Host Present Interval
-
- This value MUST be ((the Robustness Variable) times (the Query
- Interval)) plus (one Query Response Interval).
-
- older_host_present_interval_dsec = \
- igmp->querier_robustness_variable * \
- 10 * igmp->querier_query_interval + \
- pim_ifp->query_max_response_time_dsec;
- */
- older_host_present_interval_dsec =
- PIM_IGMP_OHPI_DSEC(igmp->querier_robustness_variable,
- igmp->querier_query_interval,
- pim_ifp->igmp_query_max_response_time_dsec);
-
- now_dsec = pim_time_monotonic_dsec();
- if (now_dsec < 1) {
- /* broken timer logged by pim_time_monotonic_dsec() */
- return 3;
- }
-
- if ((now_dsec - group->last_igmp_v1_report_dsec) < older_host_present_interval_dsec)
- return 1; /* IGMPv1 */
-
- if ((now_dsec - group->last_igmp_v2_report_dsec) < older_host_present_interval_dsec)
- return 2; /* IGMPv2 */
-
- return 3; /* IGMPv3 */
-}
-
void igmp_group_reset_gmi(struct igmp_group *group)
{
long group_membership_interval_msec;
@@ -705,7 +661,8 @@ static void isex_incl(struct igmp_group *group,
void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from,
struct in_addr group_addr,
- int num_sources, struct in_addr *sources)
+ int num_sources, struct in_addr *sources,
+ int from_igmp_v2_report)
{
struct interface *ifp = igmp->interface;
struct igmp_group *group;
@@ -719,6 +676,10 @@ void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from,
return;
}
+ /* So we can display how we learned the group in our show command output */
+ if (from_igmp_v2_report)
+ group->igmp_version = 2;
+
if (group->group_filtermode_isexcl) {
/* EXCLUDE mode */
isex_excl(group, num_sources, sources);
@@ -1050,17 +1011,25 @@ void igmpv3_report_allow(struct igmp_sock *igmp, struct in_addr from,
*/
static void group_retransmit_group(struct igmp_group *group)
{
- char query_buf[PIM_IGMP_BUFSIZE_WRITE];
struct igmp_sock *igmp;
struct pim_interface *pim_ifp;
long lmqc; /* Last Member Query Count */
long lmqi_msec; /* Last Member Query Interval */
long lmqt_msec; /* Last Member Query Time */
int s_flag;
+ int query_buf_size;
igmp = group->group_igmp_sock;
pim_ifp = igmp->interface->info;
+ if (pim_ifp->igmp_version == 3) {
+ query_buf_size = PIM_IGMP_BUFSIZE_WRITE;
+ } else {
+ query_buf_size = IGMP_V12_MSG_SIZE;
+ }
+
+ char query_buf[query_buf_size];
+
lmqc = igmp->querier_robustness_variable;
lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
lmqt_msec = lmqc * lmqi_msec;
@@ -1090,18 +1059,19 @@ static void group_retransmit_group(struct igmp_group *group)
interest.
*/
- pim_igmp_send_membership_query(group,
- igmp->fd,
- igmp->interface->name,
- query_buf,
- sizeof(query_buf),
- 0 /* num_sources_tosend */,
- group->group_addr /* dst_addr */,
- group->group_addr /* group_addr */,
- pim_ifp->igmp_specific_query_max_response_time_dsec,
- s_flag,
- igmp->querier_robustness_variable,
- igmp->querier_query_interval);
+ igmp_send_query(pim_ifp->igmp_version,
+ group,
+ igmp->fd,
+ igmp->interface->name,
+ query_buf,
+ sizeof(query_buf),
+ 0 /* num_sources_tosend */,
+ group->group_addr /* dst_addr */,
+ group->group_addr /* group_addr */,
+ pim_ifp->igmp_specific_query_max_response_time_dsec,
+ s_flag,
+ igmp->querier_robustness_variable,
+ igmp->querier_query_interval);
}
/*
@@ -1208,19 +1178,19 @@ static int group_retransmit_sources(struct igmp_group *group,
interest.
*/
- pim_igmp_send_membership_query(group,
- igmp->fd,
- igmp->interface->name,
- query_buf1,
- sizeof(query_buf1),
- num_sources_tosend1,
- group->group_addr,
- group->group_addr,
- pim_ifp->igmp_specific_query_max_response_time_dsec,
- 1 /* s_flag */,
- igmp->querier_robustness_variable,
- igmp->querier_query_interval);
-
+ igmp_send_query(pim_ifp->igmp_version,
+ group,
+ igmp->fd,
+ igmp->interface->name,
+ query_buf1,
+ sizeof(query_buf1),
+ num_sources_tosend1,
+ group->group_addr,
+ group->group_addr,
+ pim_ifp->igmp_specific_query_max_response_time_dsec,
+ 1 /* s_flag */,
+ igmp->querier_robustness_variable,
+ igmp->querier_query_interval);
}
} /* send_with_sflag_set */
@@ -1250,19 +1220,19 @@ static int group_retransmit_sources(struct igmp_group *group,
interest.
*/
- pim_igmp_send_membership_query(group,
- igmp->fd,
- igmp->interface->name,
- query_buf2,
- sizeof(query_buf2),
- num_sources_tosend2,
- group->group_addr,
- group->group_addr,
- pim_ifp->igmp_specific_query_max_response_time_dsec,
- 0 /* s_flag */,
- igmp->querier_robustness_variable,
- igmp->querier_query_interval);
-
+ igmp_send_query(pim_ifp->igmp_version,
+ group,
+ igmp->fd,
+ igmp->interface->name,
+ query_buf2,
+ sizeof(query_buf2),
+ num_sources_tosend2,
+ group->group_addr,
+ group->group_addr,
+ pim_ifp->igmp_specific_query_max_response_time_dsec,
+ 0 /* s_flag */,
+ igmp->querier_robustness_variable,
+ igmp->querier_query_interval);
}
}
@@ -1623,32 +1593,19 @@ void igmp_source_timer_lower_to_lmqt(struct igmp_source *source)
igmp_source_timer_on(group, source, lmqt_msec);
}
-/*
- Copy sources to message:
-
- struct in_addr *sources = (struct in_addr *)(query_buf + IGMP_V3_SOURCES_OFFSET);
- if (num_sources > 0) {
- struct listnode *node;
- struct igmp_source *src;
- int i = 0;
-
- for (ALL_LIST_ELEMENTS_RO(source_list, node, src)) {
- sources[i++] = src->source_addr;
- }
- }
-*/
-void pim_igmp_send_membership_query(struct igmp_group *group,
- int fd,
- const char *ifname,
- char *query_buf,
- int query_buf_size,
- int num_sources,
- struct in_addr dst_addr,
- struct in_addr group_addr,
- int query_max_response_time_dsec,
- uint8_t s_flag,
- uint8_t querier_robustness_variable,
- uint16_t querier_query_interval)
+void
+igmp_v3_send_query (struct igmp_group *group,
+ int fd,
+ const char *ifname,
+ char *query_buf,
+ int query_buf_size,
+ int num_sources,
+ struct in_addr dst_addr,
+ struct in_addr group_addr,
+ int query_max_response_time_dsec,
+ uint8_t s_flag,
+ uint8_t querier_robustness_variable,
+ uint16_t querier_query_interval)
{
ssize_t msg_size;
uint8_t max_resp_code;
@@ -1688,7 +1645,7 @@ void pim_igmp_send_membership_query(struct igmp_group *group,
query_buf[0] = PIM_IGMP_MEMBERSHIP_QUERY;
query_buf[1] = max_resp_code;
- *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = 0; /* for computing checksum */
+ *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) = 0; /* for computing checksum */
memcpy(query_buf+4, &group_addr, sizeof(struct in_addr));
query_buf[8] = (s_flag << 3) | querier_robustness_variable;
@@ -1696,18 +1653,17 @@ void pim_igmp_send_membership_query(struct igmp_group *group,
*(uint16_t *)(query_buf + IGMP_V3_NUMSOURCES_OFFSET) = htons(num_sources);
checksum = in_cksum(query_buf, msg_size);
- *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = checksum;
+ *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) = checksum;
if (PIM_DEBUG_IGMP_PACKETS) {
char dst_str[100];
char group_str[100];
pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
- zlog_debug("%s: to %s on %s: group=%s sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x checksum=%x",
- __PRETTY_FUNCTION__,
- dst_str, ifname, group_str, num_sources,
- msg_size, s_flag, querier_robustness_variable,
- querier_query_interval, qqic, checksum);
+ zlog_debug("Send IGMPv3 query to %s on %s for group %s, sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x",
+ dst_str, ifname, group_str,
+ num_sources, msg_size, s_flag, querier_robustness_variable,
+ querier_query_interval, qqic);
}
memset(&to, 0, sizeof(to));
@@ -1723,16 +1679,12 @@ void pim_igmp_send_membership_query(struct igmp_group *group,
pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
if (sent < 0) {
- zlog_warn("%s: sendto() failure to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
- __PRETTY_FUNCTION__,
- dst_str, ifname, group_str, msg_size,
- errno, safe_strerror(errno));
+ zlog_warn("Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
+ dst_str, ifname, group_str, msg_size, errno, safe_strerror(errno));
}
else {
- zlog_warn("%s: sendto() partial to %s on %s: group=%s msg_size=%zd: sent=%zd",
- __PRETTY_FUNCTION__,
- dst_str, ifname, group_str,
- msg_size, sent);
+ zlog_warn("Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: sent=%zd",
+ dst_str, ifname, group_str, msg_size, sent);
}
return;
}
@@ -1760,5 +1712,268 @@ void pim_igmp_send_membership_query(struct igmp_group *group,
dst_str, ifname, group_str, num_sources);
}
}
+}
+
+void
+igmp_v3_recv_query (struct igmp_sock *igmp, const char *from_str, char *igmp_msg)
+{
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ struct in_addr group_addr;
+ uint8_t resv_s_qrv = 0;
+ uint8_t s_flag = 0;
+ uint8_t qrv = 0;
+ int i;
+
+ memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
+ ifp = igmp->interface;
+ pim_ifp = ifp->info;
+
+ /*
+ * RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
+ *
+ * Routers adopt the QRV value from the most recently received Query
+ * as their own [Robustness Variable] value, unless that most
+ * recently received QRV was zero, in which case the receivers use
+ * the default [Robustness Variable] value specified in section 8.1
+ * or a statically configured value.
+ */
+ resv_s_qrv = igmp_msg[8];
+ qrv = 7 & resv_s_qrv;
+ igmp->querier_robustness_variable = qrv ? qrv : pim_ifp->igmp_default_robustness_variable;
+
+ /*
+ * RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
+ *
+ * Multicast routers that are not the current querier adopt the QQI
+ * value from the most recently received Query as their own [Query
+ * Interval] value, unless that most recently received QQI was zero,
+ * in which case the receiving routers use the default.
+ */
+ if (igmp->t_other_querier_timer) {
+ /* other querier present */
+ uint8_t qqic;
+ uint16_t qqi;
+ qqic = igmp_msg[9];
+ qqi = igmp_msg_decode8to16(qqic);
+ igmp->querier_query_interval = qqi ? qqi : pim_ifp->igmp_default_query_interval;
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ char ifaddr_str[100];
+ pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
+ zlog_debug("Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
+ ifaddr_str,
+ qqi ? "recv-non-default" : "default",
+ igmp->querier_query_interval,
+ qqic,
+ from_str);
+ }
+ }
+
+ /*
+ * RFC 3376: 6.6.1. Timer Updates
+ *
+ * When a router sends or receives a query with a clear Suppress
+ * Router-Side Processing flag, it must update its timers to reflect
+ * the correct timeout values for the group or sources being queried.
+ *
+ * General queries don't trigger timer update.
+ */
+ s_flag = (1 << 3) & resv_s_qrv;
+
+ if (!s_flag) {
+ /* s_flag is clear */
+
+ if (PIM_INADDR_IS_ANY(group_addr)) {
+ /* this is a general query */
+ /* log that general query should have the s_flag set */
+ zlog_warn("General IGMP query v3 from %s on %s: Suppress Router-Side Processing flag is clear",
+ from_str, ifp->name);
+ } else {
+ struct igmp_group *group;
+
+ /* this is a non-general query: perform timer updates */
+
+ group = find_group_by_addr(igmp, group_addr);
+ if (group) {
+ int recv_num_sources = ntohs(*(uint16_t *)(igmp_msg + IGMP_V3_NUMSOURCES_OFFSET));
+
+ /*
+ * RFC 3376: 6.6.1. Timer Updates
+ * Query Q(G,A): Source Timer for sources in A are lowered to LMQT
+ * Query Q(G): Group Timer is lowered to LMQT
+ */
+ if (recv_num_sources < 1) {
+ /* Query Q(G): Group Timer is lowered to LMQT */
+
+ igmp_group_timer_lower_to_lmqt(group);
+ } else {
+ /* Query Q(G,A): Source Timer for sources in A are lowered to LMQT */
+
+ /* Scan sources in query and lower their timers to LMQT */
+ struct in_addr *sources = (struct in_addr *)(igmp_msg + IGMP_V3_SOURCES_OFFSET);
+ for (i = 0; i < recv_num_sources; ++i) {
+ struct in_addr src_addr;
+ struct igmp_source *src;
+ memcpy(&src_addr, sources + i, sizeof(struct in_addr));
+ src = igmp_find_source_by_addr(group, src_addr);
+ if (src) {
+ igmp_source_timer_lower_to_lmqt(src);
+ }
+ }
+ }
+ } else {
+ char group_str[100];
+ pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
+ zlog_warn("IGMP query v3 from %s on %s: could not find group %s for timer update",
+ from_str, ifp->name, group_str);
+ }
+ }
+ } /* s_flag is clear: timer updates */
+}
+
+int
+igmp_v3_recv_report (struct igmp_sock *igmp,
+ struct in_addr from, const char *from_str,
+ char *igmp_msg, int igmp_msg_len)
+{
+ uint16_t recv_checksum;
+ uint16_t checksum;
+ int num_groups;
+ uint8_t *group_record;
+ uint8_t *report_pastend = (uint8_t *) igmp_msg + igmp_msg_len;
+ struct interface *ifp = igmp->interface;
+ int i;
+ int local_ncb = 0;
+
+ if (igmp_msg_len < IGMP_V3_MSG_MIN_SIZE) {
+ zlog_warn("Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
+ from_str, ifp->name, igmp_msg_len, IGMP_V3_MSG_MIN_SIZE);
+ return -1;
+ }
+
+ recv_checksum = *(uint16_t *) (igmp_msg + IGMP_CHECKSUM_OFFSET);
+
+ /* for computing checksum */
+ *(uint16_t *) (igmp_msg + IGMP_CHECKSUM_OFFSET) = 0;
+
+ checksum = in_cksum(igmp_msg, igmp_msg_len);
+ if (checksum != recv_checksum) {
+ zlog_warn("Recv IGMP report v3 from %s on %s: checksum mismatch: received=%x computed=%x",
+ from_str, ifp->name, recv_checksum, checksum);
+ return -1;
+ }
+
+ num_groups = ntohs(*(uint16_t *) (igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET));
+ if (num_groups < 1) {
+ zlog_warn("Recv IGMP report v3 from %s on %s: missing group records",
+ from_str, ifp->name);
+ return -1;
+ }
+ if (PIM_DEBUG_IGMP_PACKETS) {
+ zlog_debug("Recv IGMP report v3 from %s on %s: size=%d checksum=%x groups=%d",
+ from_str, ifp->name, igmp_msg_len, checksum, num_groups);
+ }
+
+ group_record = (uint8_t *) igmp_msg + IGMP_V3_REPORT_GROUPPRECORD_OFFSET;
+
+ /* Scan groups */
+ for (i = 0; i < num_groups; ++i) {
+ struct in_addr rec_group;
+ uint8_t *sources;
+ uint8_t *src;
+ int rec_type;
+ int rec_auxdatalen;
+ int rec_num_sources;
+ int j;
+ struct prefix lncb;
+ struct prefix g;
+
+ if ((group_record + IGMP_V3_GROUP_RECORD_MIN_SIZE) > report_pastend) {
+ zlog_warn("Recv IGMP report v3 from %s on %s: group record beyond report end",
+ from_str, ifp->name);
+ return -1;
+ }
+
+ rec_type = group_record[IGMP_V3_GROUP_RECORD_TYPE_OFFSET];
+ rec_auxdatalen = group_record[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET];
+ rec_num_sources = ntohs(* (uint16_t *) (group_record + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET));
+
+ memcpy(&rec_group, group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET, sizeof(struct in_addr));
+
+ if (PIM_DEBUG_IGMP_PACKETS) {
+ zlog_debug("Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%s",
+ from_str, ifp->name, i, rec_type, rec_auxdatalen, rec_num_sources, inet_ntoa(rec_group));
+ }
+
+ /* Scan sources */
+
+ sources = group_record + IGMP_V3_GROUP_RECORD_SOURCE_OFFSET;
+
+ for (j = 0, src = sources; j < rec_num_sources; ++j, src += 4) {
+
+ if ((src + 4) > report_pastend) {
+ zlog_warn("Recv IGMP report v3 from %s on %s: group source beyond report end",
+ from_str, ifp->name);
+ return -1;
+ }
+
+ if (PIM_DEBUG_IGMP_PACKETS) {
+ char src_str[200];
+
+ if (!inet_ntop(AF_INET, src, src_str , sizeof(src_str)))
+ sprintf(src_str, "<source?>");
+
+ zlog_debug("Recv IGMP report v3 from %s on %s: record=%d group=%s source=%s",
+ from_str, ifp->name, i, inet_ntoa(rec_group), src_str);
+ }
+ } /* for (sources) */
+
+
+ lncb.family = AF_INET;
+ lncb.u.prefix4.s_addr = 0x000000E0;
+ lncb.prefixlen = 24;
+
+ g.family = AF_INET;
+ g.u.prefix4 = rec_group;
+ g.prefixlen = 32;
+ /*
+ * If we receive a igmp report with the group in 224.0.0.0/24
+ * then we should ignore it
+ */
+ if (prefix_match(&lncb, &g))
+ local_ncb = 1;
+
+ if (!local_ncb)
+ switch (rec_type) {
+ case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE:
+ igmpv3_report_isin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
+ break;
+ case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE:
+ igmpv3_report_isex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources, 0);
+ break;
+ case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE:
+ igmpv3_report_toin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
+ break;
+ case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE:
+ igmpv3_report_toex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
+ break;
+ case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES:
+ igmpv3_report_allow(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
+ break;
+ case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES:
+ igmpv3_report_block(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
+ break;
+ default:
+ zlog_warn("Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
+ from_str, ifp->name, rec_type);
+ }
+
+ group_record += 8 + (rec_num_sources << 2) + (rec_auxdatalen << 2);
+ local_ncb = 0;
+
+ } /* for (group records) */
+
+ return 0;
}
diff --git a/pimd/pim_igmpv3.h b/pimd/pim_igmpv3.h
index 37baca5c8..33a918bad 100644
--- a/pimd/pim_igmpv3.h
+++ b/pimd/pim_igmpv3.h
@@ -31,6 +31,13 @@
#define IGMP_V3_NUMSOURCES_OFFSET (10)
#define IGMP_V3_SOURCES_OFFSET (12)
+#define IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE (1)
+#define IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE (2)
+#define IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE (3)
+#define IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE (4)
+#define IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES (5)
+#define IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES (6)
+
/* GMI: Group Membership Interval */
#define PIM_IGMP_GMI_MSEC(qrv,qqi,qri_dsec) ((qrv) * (1000 * (qqi)) + 100 * (qri_dsec))
@@ -55,15 +62,13 @@ void igmp_source_free(struct igmp_source *source);
void igmp_source_delete(struct igmp_source *source);
void igmp_source_delete_expired(struct list *source_list);
-int igmp_group_compat_mode(const struct igmp_sock *igmp,
- const struct igmp_group *group);
-
void igmpv3_report_isin(struct igmp_sock *igmp, struct in_addr from,
struct in_addr group_addr,
int num_sources, struct in_addr *sources);
void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from,
struct in_addr group_addr,
- int num_sources, struct in_addr *sources);
+ int num_sources, struct in_addr *sources,
+ int from_igmp_v2_report);
void igmpv3_report_toin(struct igmp_sock *igmp, struct in_addr from,
struct in_addr group_addr,
int num_sources, struct in_addr *sources);
@@ -83,17 +88,24 @@ void igmp_source_timer_lower_to_lmqt(struct igmp_source *source);
struct igmp_source *igmp_find_source_by_addr(struct igmp_group *group,
struct in_addr src_addr);
-void pim_igmp_send_membership_query(struct igmp_group *group,
- int fd,
- const char *ifname,
- char *query_buf,
- int query_buf_size,
- int num_sources,
- struct in_addr dst_addr,
- struct in_addr group_addr,
- int query_max_response_time_dsec,
- uint8_t s_flag,
- uint8_t querier_robustness_variable,
- uint16_t querier_query_interval);
+void igmp_v3_send_query (struct igmp_group *group,
+ int fd,
+ const char *ifname,
+ char *query_buf,
+ int query_buf_size,
+ int num_sources,
+ struct in_addr dst_addr,
+ struct in_addr group_addr,
+ int query_max_response_time_dsec,
+ uint8_t s_flag,
+ uint8_t querier_robustness_variable,
+ uint16_t querier_query_interval);
+
+void igmp_v3_recv_query (struct igmp_sock *igmp, const char *from_str,
+ char *igmp_msg);
+
+int igmp_v3_recv_report (struct igmp_sock *igmp,
+ struct in_addr from, const char *from_str,
+ char *igmp_msg, int igmp_msg_len);
#endif /* PIM_IGMPV3_H */
diff --git a/pimd/pim_mroute.c b/pimd/pim_mroute.c
index b9c97557c..341d73336 100644
--- a/pimd/pim_mroute.c
+++ b/pimd/pim_mroute.c
@@ -399,59 +399,97 @@ pim_mroute_msg_wrvifwhole (int fd, struct interface *ifp, const char *buf)
int pim_mroute_msg(int fd, const char *buf, int buf_size)
{
struct interface *ifp;
+ struct pim_interface *pim_ifp;
const struct ip *ip_hdr;
const struct igmpmsg *msg;
+ char ip_src_str[100] = "";
+ char ip_dst_str[100] = "";
char src_str[100] = "<src?>";
char grp_str[100] = "<grp?>";
+ struct in_addr ifaddr;
+ struct igmp_sock *igmp;
ip_hdr = (const struct ip *) buf;
- /* kernel upcall must have protocol=0 */
- if (ip_hdr->ip_p) {
- /* this is not a kernel upcall */
+ if (ip_hdr->ip_p == IPPROTO_IGMP) {
+
+ /* We have the IP packet but we do not know which interface this packet was
+ * received on. Find the interface that is on the same subnet as the source
+ * of the IP packet.
+ */
+ ifp = if_lookup_address_vrf((void *) &ip_hdr->ip_src, AF_INET, VRF_DEFAULT);
+
+ if (!ifp) {
+ if (PIM_DEBUG_MROUTE_DETAIL) {
+ pim_inet4_dump("<src?>", ip_hdr->ip_src, ip_src_str, sizeof(ip_src_str));
+ pim_inet4_dump("<dst?>", ip_hdr->ip_dst, ip_dst_str, sizeof(ip_dst_str));
+
+ zlog_warn("%s: igmp kernel upcall could not find interface for %s -> %s",
+ __PRETTY_FUNCTION__,
+ ip_src_str,
+ ip_dst_str);
+ }
+ return 0;
+ }
+
+ pim_ifp = ifp->info;
+ ifaddr = pim_find_primary_addr(ifp);
+ igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->igmp_socket_list, ifaddr);
+
+ if (PIM_DEBUG_MROUTE_DETAIL) {
+ pim_inet4_dump("<src?>", ip_hdr->ip_src, ip_src_str, sizeof(ip_src_str));
+ pim_inet4_dump("<dst?>", ip_hdr->ip_dst, ip_dst_str, sizeof(ip_dst_str));
+
+ zlog_warn("%s: igmp kernel upcall on %s for %s -> %s",
+ __PRETTY_FUNCTION__, ifp->name, ip_src_str, ip_dst_str);
+ }
+
+ pim_igmp_packet(igmp, (char *)buf, buf_size);
+
+ } else if (ip_hdr->ip_p) {
if (PIM_DEBUG_MROUTE_DETAIL) {
pim_inet4_dump("<src?>", ip_hdr->ip_src, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", ip_hdr->ip_dst, grp_str, sizeof(grp_str));
- zlog_debug("%s: not a kernel upcall proto=%d src: %s dst: %s msg_size=%d",
- __PRETTY_FUNCTION__, ip_hdr->ip_p, src_str, grp_str, buf_size);
+ zlog_debug("%s: no kernel upcall proto=%d src: %s dst: %s msg_size=%d",
+ __PRETTY_FUNCTION__, ip_hdr->ip_p, src_str, grp_str, buf_size);
}
- return 0;
- }
- msg = (const struct igmpmsg *) buf;
+ } else {
+ msg = (const struct igmpmsg *) buf;
- ifp = pim_if_find_by_vif_index(msg->im_vif);
+ ifp = pim_if_find_by_vif_index(msg->im_vif);
- if (PIM_DEBUG_MROUTE) {
- pim_inet4_dump("<src?>", msg->im_src, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", msg->im_dst, grp_str, sizeof(grp_str));
- zlog_warn("%s: kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d size=%d",
- __PRETTY_FUNCTION__,
- igmpmsgtype2str[msg->im_msgtype],
- msg->im_msgtype,
- ip_hdr->ip_p,
- fd,
- src_str,
- grp_str,
- ifp->name,
- msg->im_vif, buf_size);
- }
+ if (PIM_DEBUG_MROUTE) {
+ pim_inet4_dump("<src?>", msg->im_src, src_str, sizeof(src_str));
+ pim_inet4_dump("<grp?>", msg->im_dst, grp_str, sizeof(grp_str));
+ zlog_warn("%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d size=%d",
+ __PRETTY_FUNCTION__,
+ igmpmsgtype2str[msg->im_msgtype],
+ msg->im_msgtype,
+ ip_hdr->ip_p,
+ fd,
+ src_str,
+ grp_str,
+ ifp->name,
+ msg->im_vif, buf_size);
+ }
- switch (msg->im_msgtype) {
- case IGMPMSG_WRONGVIF:
- return pim_mroute_msg_wrongvif(fd, ifp, msg);
- break;
- case IGMPMSG_NOCACHE:
- return pim_mroute_msg_nocache(fd, ifp, msg);
- break;
- case IGMPMSG_WHOLEPKT:
- return pim_mroute_msg_wholepkt(fd, ifp, (const char *)msg);
- break;
- case IGMPMSG_WRVIFWHOLE:
- return pim_mroute_msg_wrvifwhole (fd, ifp, (const char *)msg);
- break;
- default:
- break;
+ switch (msg->im_msgtype) {
+ case IGMPMSG_WRONGVIF:
+ return pim_mroute_msg_wrongvif(fd, ifp, msg);
+ break;
+ case IGMPMSG_NOCACHE:
+ return pim_mroute_msg_nocache(fd, ifp, msg);
+ break;
+ case IGMPMSG_WHOLEPKT:
+ return pim_mroute_msg_wholepkt(fd, ifp, (const char *)msg);
+ break;
+ case IGMPMSG_WRVIFWHOLE:
+ return pim_mroute_msg_wrvifwhole (fd, ifp, (const char *)msg);
+ break;
+ default:
+ break;
+ }
}
return 0;
diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c
index 3eb04d347..644fb8e69 100644
--- a/pimd/pim_vty.c
+++ b/pimd/pim_vty.c
@@ -215,6 +215,15 @@ int pim_interface_config_write(struct vty *vty)
++writes;
}
+ /* ip igmp version */
+ if (pim_ifp->igmp_version != IGMP_DEFAULT_VERSION)
+ {
+ vty_out(vty, " ip igmp version %d%s",
+ pim_ifp->igmp_version,
+ VTY_NEWLINE);
+ ++writes;
+ }
+
/* IF ip igmp query-interval */
if (pim_ifp->igmp_default_query_interval != IGMP_GENERAL_QUERY_INTERVAL)
{