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.c176
1 files changed, 114 insertions, 62 deletions
diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c
index 73dcdbddb4..795c96c838 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",
@@ -631,7 +671,6 @@ void pim_igmp_general_query_on(struct igmp_sock *igmp)
ifaddr_str, query_interval,
startup_mode ? "startup" : "non-startup", igmp->fd);
}
- igmp->t_igmp_query_timer = NULL;
thread_add_timer(router->master, pim_igmp_general_query, igmp,
query_interval, &igmp->t_igmp_query_timer);
}
@@ -770,13 +809,8 @@ static void igmp_group_free(struct igmp_group *group)
XFREE(MTYPE_PIM_IGMP_GROUP, group);
}
-static void igmp_group_count_incr(struct igmp_sock *igmp)
+static void igmp_group_count_incr(struct pim_interface *pim_ifp)
{
- struct pim_interface *pim_ifp = igmp->interface->info;
-
- if (!pim_ifp)
- return;
-
++pim_ifp->pim->igmp_group_count;
if (pim_ifp->pim->igmp_group_count
== pim_ifp->pim->igmp_watermark_limit) {
@@ -787,13 +821,8 @@ static void igmp_group_count_incr(struct igmp_sock *igmp)
}
}
-static void igmp_group_count_decr(struct igmp_sock *igmp)
+static void igmp_group_count_decr(struct pim_interface *pim_ifp)
{
- struct pim_interface *pim_ifp = igmp->interface->info;
-
- if (!pim_ifp)
- return;
-
if (pim_ifp->pim->igmp_group_count == 0) {
zlog_warn("Cannot decrement igmp group count below 0(vrf: %s)",
VRF_LOGNAME(pim_ifp->pim->vrf));
@@ -808,14 +837,14 @@ void igmp_group_delete(struct igmp_group *group)
struct listnode *src_node;
struct listnode *src_nextnode;
struct igmp_source *src;
+ struct pim_interface *pim_ifp = group->interface->info;
if (PIM_DEBUG_IGMP_TRACE) {
char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str,
sizeof(group_str));
- zlog_debug("Deleting IGMP group %s from socket %d interface %s",
- group_str, group->group_igmp_sock->fd,
- group->group_igmp_sock->interface->name);
+ zlog_debug("Deleting IGMP group %s from interface %s",
+ group_str, group->interface->name);
}
for (ALL_LIST_ELEMENTS(group->group_source_list, src_node, src_nextnode,
@@ -826,9 +855,9 @@ void igmp_group_delete(struct igmp_group *group)
THREAD_OFF(group->t_group_query_retransmit_timer);
group_timer_off(group);
- igmp_group_count_decr(group->group_igmp_sock);
- listnode_delete(group->group_igmp_sock->igmp_group_list, group);
- hash_release(group->group_igmp_sock->igmp_group_hash, group);
+ igmp_group_count_decr(pim_ifp);
+ listnode_delete(pim_ifp->igmp_group_list, group);
+ hash_release(pim_ifp->igmp_group_hash, group);
igmp_group_free(group);
}
@@ -846,11 +875,6 @@ void igmp_sock_free(struct igmp_sock *igmp)
assert(!igmp->t_igmp_read);
assert(!igmp->t_igmp_query_timer);
assert(!igmp->t_other_querier_timer);
- assert(igmp->igmp_group_list);
- assert(!listcount(igmp->igmp_group_list));
-
- list_delete(&igmp->igmp_group_list);
- hash_free(igmp->igmp_group_hash);
XFREE(MTYPE_PIM_IGMP_SOCKET, igmp);
}
@@ -858,14 +882,6 @@ void igmp_sock_free(struct igmp_sock *igmp)
void igmp_sock_delete(struct igmp_sock *igmp)
{
struct pim_interface *pim_ifp;
- struct listnode *grp_node;
- struct listnode *grp_nextnode;
- struct igmp_group *grp;
-
- for (ALL_LIST_ELEMENTS(igmp->igmp_group_list, grp_node, grp_nextnode,
- grp)) {
- igmp_group_delete(grp);
- }
sock_close(igmp);
@@ -874,6 +890,9 @@ void igmp_sock_delete(struct igmp_sock *igmp)
listnode_delete(pim_ifp->igmp_socket_list, igmp);
igmp_sock_free(igmp);
+
+ if (!listcount(pim_ifp->igmp_socket_list))
+ pim_igmp_if_reset(pim_ifp);
}
void igmp_sock_delete_all(struct interface *ifp)
@@ -908,12 +927,50 @@ static bool igmp_group_hash_equal(const void *arg1, const void *arg2)
return false;
}
+void pim_igmp_if_init(struct pim_interface *pim_ifp, struct interface *ifp)
+{
+ char hash_name[64];
+
+ pim_ifp->igmp_socket_list = list_new();
+ pim_ifp->igmp_socket_list->del = (void (*)(void *))igmp_sock_free;
+
+ pim_ifp->igmp_group_list = list_new();
+ pim_ifp->igmp_group_list->del = (void (*)(void *))igmp_group_free;
+
+ snprintf(hash_name, sizeof(hash_name), "IGMP %s hash", ifp->name);
+ pim_ifp->igmp_group_hash = hash_create(
+ igmp_group_hash_key, igmp_group_hash_equal, hash_name);
+}
+
+void pim_igmp_if_reset(struct pim_interface *pim_ifp)
+{
+ struct listnode *grp_node, *grp_nextnode;
+ struct igmp_group *grp;
+
+ for (ALL_LIST_ELEMENTS(pim_ifp->igmp_group_list, grp_node, grp_nextnode,
+ grp)) {
+ igmp_group_delete(grp);
+ }
+}
+
+void pim_igmp_if_fini(struct pim_interface *pim_ifp)
+{
+ pim_igmp_if_reset(pim_ifp);
+
+ assert(pim_ifp->igmp_group_list);
+ assert(!listcount(pim_ifp->igmp_group_list));
+
+ list_delete(&pim_ifp->igmp_group_list);
+ hash_free(pim_ifp->igmp_group_hash);
+
+ list_delete(&pim_ifp->igmp_socket_list);
+}
+
static struct igmp_sock *igmp_sock_new(int fd, struct in_addr ifaddr,
struct interface *ifp, int mtrace_only)
{
struct pim_interface *pim_ifp;
struct igmp_sock *igmp;
- char hash_name[64];
pim_ifp = ifp->info;
@@ -925,16 +982,10 @@ static struct igmp_sock *igmp_sock_new(int fd, struct in_addr ifaddr,
igmp = XCALLOC(MTYPE_PIM_IGMP_SOCKET, sizeof(*igmp));
- igmp->igmp_group_list = list_new();
- igmp->igmp_group_list->del = (void (*)(void *))igmp_group_free;
-
- snprintf(hash_name, sizeof(hash_name), "IGMP %s hash", ifp->name);
- igmp->igmp_group_hash = hash_create(igmp_group_hash_key,
- igmp_group_hash_equal, hash_name);
-
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 */
@@ -1000,7 +1051,6 @@ static void igmp_read_on(struct igmp_sock *igmp)
zlog_debug("Scheduling READ event on IGMP socket fd=%d",
igmp->fd);
}
- igmp->t_igmp_read = NULL;
thread_add_read(router->master, pim_igmp_read, igmp, igmp->fd,
&igmp->t_igmp_read);
}
@@ -1073,7 +1123,7 @@ static int igmp_group_timer(struct thread *t)
pim_inet4_dump("<group?>", group->group_addr, group_str,
sizeof(group_str));
zlog_debug("%s: Timer for group %s on interface %s", __func__,
- group_str, group->group_igmp_sock->interface->name);
+ group_str, group->interface->name);
}
assert(group->group_filtermode_isexcl);
@@ -1110,7 +1160,7 @@ static void group_timer_off(struct igmp_group *group)
pim_inet4_dump("<group?>", group->group_addr, group_str,
sizeof(group_str));
zlog_debug("Cancelling TIMER event for group %s on %s",
- group_str, group->group_igmp_sock->interface->name);
+ group_str, group->interface->name);
}
THREAD_OFF(group->t_group_timer);
}
@@ -1147,16 +1197,18 @@ struct igmp_group *find_group_by_addr(struct igmp_sock *igmp,
struct in_addr group_addr)
{
struct igmp_group lookup;
+ struct pim_interface *pim_ifp = igmp->interface->info;
lookup.group_addr.s_addr = group_addr.s_addr;
- return hash_lookup(igmp->igmp_group_hash, &lookup);
+ return hash_lookup(pim_ifp->igmp_group_hash, &lookup);
}
struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,
struct in_addr group_addr)
{
struct igmp_group *group;
+ struct pim_interface *pim_ifp = igmp->interface->info;
group = find_group_by_addr(igmp, group_addr);
if (group) {
@@ -1198,7 +1250,7 @@ struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,
group->t_group_query_retransmit_timer = NULL;
group->group_specific_query_retransmit_count = 0;
group->group_addr = group_addr;
- group->group_igmp_sock = igmp;
+ group->interface = igmp->interface;
group->last_igmp_v1_report_dsec = -1;
group->last_igmp_v2_report_dsec = -1;
group->group_creation = pim_time_monotonic_sec();
@@ -1207,8 +1259,8 @@ struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,
/* initialize new group as INCLUDE {empty} */
group->group_filtermode_isexcl = 0; /* 0=INCLUDE, 1=EXCLUDE */
- listnode_add(igmp->igmp_group_list, group);
- group = hash_get(igmp->igmp_group_hash, group, hash_alloc_intern);
+ listnode_add(pim_ifp->igmp_group_list, group);
+ group = hash_get(pim_ifp->igmp_group_hash, group, hash_alloc_intern);
if (PIM_DEBUG_IGMP_TRACE) {
char group_str[INET_ADDRSTRLEN];
@@ -1219,7 +1271,7 @@ struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,
group_str, igmp->fd, igmp->interface->name);
}
- igmp_group_count_incr(igmp);
+ igmp_group_count_incr(pim_ifp);
/*
RFC 3376: 6.2.2. Definition of Group Timers