diff options
Diffstat (limited to 'pimd/pim_igmp.c')
| -rw-r--r-- | pimd/pim_igmp.c | 69 |
1 files changed, 55 insertions, 14 deletions
diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c index 73dcdbddb4..426112d4d6 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 -1; + } } + 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 -1; + } + } + + 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 */ |
