diff options
Diffstat (limited to 'pimd/pim_igmp.c')
| -rw-r--r-- | pimd/pim_igmp.c | 176 |
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 |
