summaryrefslogtreecommitdiff
path: root/pimd/pim_igmp.c
diff options
context:
space:
mode:
Diffstat (limited to 'pimd/pim_igmp.c')
-rw-r--r--pimd/pim_igmp.c69
1 files changed, 55 insertions, 14 deletions
diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c
index 73dcdbddb4..71b2d9187a 100644
--- a/pimd/pim_igmp.c
+++ b/pimd/pim_igmp.c
@@ -167,6 +167,8 @@ static int pim_igmp_other_querier_expire(struct thread *t)
sizeof(ifaddr_str));
zlog_debug("%s: Querier %s resuming", __func__, ifaddr_str);
}
+ /* Mark the interface address as querier address */
+ igmp->querier_addr = igmp->ifaddr;
/*
We are the current querier, then
@@ -397,6 +399,8 @@ static int igmp_recv_query(struct igmp_sock *igmp, int query_version,
ntohl(igmp->ifaddr.s_addr), from_str,
ntohl(from.s_addr));
}
+ if (ntohl(from.s_addr) < ntohl(igmp->querier_addr.s_addr))
+ igmp->querier_addr.s_addr = from.s_addr;
pim_igmp_other_querier_timer_on(igmp);
}
@@ -469,47 +473,83 @@ static int igmp_v1_recv_report(struct igmp_sock *igmp, struct in_addr from,
return 0;
}
-int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len)
+bool pim_igmp_verify_header(struct ip *ip_hdr, size_t len, size_t *hlen)
{
- struct ip *ip_hdr;
- size_t ip_hlen; /* ip header length in bytes */
char *igmp_msg;
int igmp_msg_len;
int msg_type;
- char from_str[INET_ADDRSTRLEN];
- char to_str[INET_ADDRSTRLEN];
+ size_t ip_hlen; /* ip header length in bytes */
if (len < sizeof(*ip_hdr)) {
zlog_warn("IGMP packet size=%zu shorter than minimum=%zu", len,
sizeof(*ip_hdr));
- return -1;
+ return false;
}
- ip_hdr = (struct ip *)buf;
-
- pim_inet4_dump("<src?>", ip_hdr->ip_src, from_str, sizeof(from_str));
- pim_inet4_dump("<dst?>", ip_hdr->ip_dst, to_str, sizeof(to_str));
-
ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */
+ *hlen = ip_hlen;
if (ip_hlen > len) {
zlog_warn(
"IGMP packet header claims size %zu, but we only have %zu bytes",
ip_hlen, len);
- return -1;
+ return false;
}
- igmp_msg = buf + ip_hlen;
+ igmp_msg = (char *)ip_hdr + ip_hlen;
igmp_msg_len = len - ip_hlen;
+ msg_type = *igmp_msg;
if (igmp_msg_len < PIM_IGMP_MIN_LEN) {
zlog_warn("IGMP message size=%d shorter than minimum=%d",
igmp_msg_len, PIM_IGMP_MIN_LEN);
- return -1;
+ return false;
+ }
+
+ if ((msg_type != PIM_IGMP_MTRACE_RESPONSE)
+ && (msg_type != PIM_IGMP_MTRACE_QUERY_REQUEST)) {
+ if (ip_hdr->ip_ttl != 1) {
+ zlog_warn(
+ "Recv IGMP packet with invalid ttl=%u, discarding the packet",
+ ip_hdr->ip_ttl);
+ return false;
+ }
}
+ if ((msg_type == PIM_IGMP_V3_MEMBERSHIP_REPORT)
+ || ((msg_type == PIM_IGMP_MEMBERSHIP_QUERY)
+ && (igmp_msg_len >= IGMP_V3_SOURCES_OFFSET))) {
+ /* All IGMPv3 messages must be received with TOS set to 0xC0*/
+ if (ip_hdr->ip_tos != IPTOS_PREC_INTERNETCONTROL) {
+ zlog_warn("Received IGMP Packet with invalid TOS %u",
+ ip_hdr->ip_tos);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len)
+{
+ struct ip *ip_hdr = (struct ip *)buf;
+ size_t ip_hlen; /* ip header length in bytes */
+ char *igmp_msg;
+ int igmp_msg_len;
+ int msg_type;
+ char from_str[INET_ADDRSTRLEN];
+ char to_str[INET_ADDRSTRLEN];
+
+ if (!pim_igmp_verify_header(ip_hdr, len, &ip_hlen))
+ return -1;
+
+ igmp_msg = buf + ip_hlen;
+ igmp_msg_len = len - ip_hlen;
msg_type = *igmp_msg;
+ pim_inet4_dump("<src?>", ip_hdr->ip_src, from_str, sizeof(from_str));
+ pim_inet4_dump("<dst?>", ip_hdr->ip_dst, to_str, sizeof(to_str));
+
if (PIM_DEBUG_IGMP_PACKETS) {
zlog_debug(
"Recv IGMP packet from %s to %s on %s: size=%zu ttl=%d msg_type=%d msg_size=%d",
@@ -935,6 +975,7 @@ static struct igmp_sock *igmp_sock_new(int fd, struct in_addr ifaddr,
igmp->fd = fd;
igmp->interface = ifp;
igmp->ifaddr = ifaddr;
+ igmp->querier_addr = ifaddr;
igmp->t_igmp_read = NULL;
igmp->t_igmp_query_timer = NULL;
igmp->t_other_querier_timer = NULL; /* no other querier present */