diff options
Diffstat (limited to 'pimd/pim_iface.c')
-rw-r--r-- | pimd/pim_iface.c | 373 |
1 files changed, 305 insertions, 68 deletions
diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index dcb61160..20e3ba18 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -37,10 +37,12 @@ #include "pim_jp_agg.h" #include "pim_igmp_join.h" #include "pim_vxlan.h" +#include "pim_tib.h" #include "pim6_mld.h" static void pim_if_gm_join_del_all(struct interface *ifp); +static void pim_if_static_group_del_all(struct interface *ifp); static int gm_join_sock(const char *ifname, ifindex_t ifindex, pim_addr group_addr, pim_addr source_addr, @@ -144,6 +146,7 @@ struct pim_interface *pim_if_new(struct interface *ifp, bool gm, bool pim, pim_ifp->gm_enable = gm; pim_ifp->gm_join_list = NULL; + pim_ifp->static_group_list = NULL; pim_ifp->pim_neighbor_list = NULL; pim_ifp->upstream_switch_list = NULL; pim_ifp->pim_generation_id = 0; @@ -188,9 +191,11 @@ void pim_if_delete(struct interface *ifp) assert(pim_ifp); pim_ifp->pim->mcast_if_count--; - if (pim_ifp->gm_join_list) { + if (pim_ifp->gm_join_list) pim_if_gm_join_del_all(ifp); - } + + if (pim_ifp->static_group_list) + pim_if_static_group_del_all(ifp); pim_ifchannel_delete_all(ifp); #if PIM_IPV == 4 @@ -522,77 +527,66 @@ void pim_if_addr_add(struct connected *ifc) detect_address_change(ifp, 0, __func__); - // if (ifc->address->family != AF_INET) - // return; - #if PIM_IPV == 4 - struct in_addr ifaddr = ifc->address->u.prefix4; + if (ifc->address->family == AF_INET) { + struct in_addr ifaddr = ifc->address->u.prefix4; - if (pim_ifp->gm_enable) { - struct gm_sock *igmp; + if (pim_ifp->gm_enable) { + struct gm_sock *igmp; - /* lookup IGMP socket */ - igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->gm_socket_list, - ifaddr); - if (!igmp) { - /* if addr new, add IGMP socket */ - if (ifc->address->family == AF_INET) + /* lookup IGMP socket */ + igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->gm_socket_list, ifaddr); + if (!igmp) { + /* if addr new, add IGMP socket */ pim_igmp_sock_add(pim_ifp->gm_socket_list, ifaddr, ifp, false); - } else if (igmp->mtrace_only) { - igmp_sock_delete(igmp); - pim_igmp_sock_add(pim_ifp->gm_socket_list, ifaddr, ifp, - false); - } + } else if (igmp->mtrace_only) { + igmp_sock_delete(igmp); + pim_igmp_sock_add(pim_ifp->gm_socket_list, ifaddr, ifp, false); + } - /* Replay Static IGMP groups */ - if (pim_ifp->gm_join_list) { - struct listnode *node; - struct listnode *nextnode; - struct gm_join *ij; - int join_fd; - - for (ALL_LIST_ELEMENTS(pim_ifp->gm_join_list, node, - nextnode, ij)) { - /* Close socket and reopen with Source and Group - */ - close(ij->sock_fd); - join_fd = gm_join_sock( - ifp->name, ifp->ifindex, ij->group_addr, - ij->source_addr, pim_ifp); - if (join_fd < 0) { - char group_str[INET_ADDRSTRLEN]; - char source_str[INET_ADDRSTRLEN]; - pim_inet4_dump("<grp?>", ij->group_addr, - group_str, - sizeof(group_str)); - pim_inet4_dump( - "<src?>", ij->source_addr, - source_str, sizeof(source_str)); - zlog_warn( - "%s: gm_join_sock() failure for IGMP group %s source %s on interface %s", - __func__, group_str, source_str, - ifp->name); - /* warning only */ - } else - ij->sock_fd = join_fd; + /* Replay Static IGMP groups */ + if (pim_ifp->gm_join_list) { + struct listnode *node; + struct listnode *nextnode; + struct gm_join *ij; + int join_fd; + + for (ALL_LIST_ELEMENTS(pim_ifp->gm_join_list, node, nextnode, ij)) { + /* Close socket and reopen with Source and Group + */ + close(ij->sock_fd); + join_fd = gm_join_sock(ifp->name, ifp->ifindex, + ij->group_addr, ij->source_addr, + pim_ifp); + if (join_fd < 0) { + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; + pim_inet4_dump("<grp?>", ij->group_addr, group_str, + sizeof(group_str)); + pim_inet4_dump("<src?>", ij->source_addr, + source_str, sizeof(source_str)); + zlog_warn("%s: gm_join_sock() failure for IGMP group %s source %s on interface %s", + __func__, group_str, source_str, + ifp->name); + /* warning only */ + } else + ij->sock_fd = join_fd; + } } - } - } /* igmp */ - else { - struct gm_sock *igmp; - - /* lookup IGMP socket */ - igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->gm_socket_list, - ifaddr); - if (ifc->address->family == AF_INET) { + } /* igmp */ + else { + struct gm_sock *igmp; + + /* lookup IGMP socket */ + igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->gm_socket_list, ifaddr); if (igmp) igmp_sock_delete(igmp); /* if addr new, add IGMP socket */ pim_igmp_sock_add(pim_ifp->gm_socket_list, ifaddr, ifp, true); - } - } /* igmp mtrace only */ + } /* igmp mtrace only */ + } #endif if (pim_ifp->pim_enable) { @@ -1218,6 +1212,11 @@ static void gm_join_free(struct gm_join *ij) XFREE(MTYPE_PIM_IGMP_JOIN, ij); } +static void static_group_free(struct static_group *stgrp) +{ + XFREE(MTYPE_PIM_STATIC_GROUP, stgrp); +} + static struct gm_join *gm_join_find(struct list *join_list, pim_addr group_addr, pim_addr source_addr) { @@ -1232,7 +1231,25 @@ static struct gm_join *gm_join_find(struct list *join_list, pim_addr group_addr, return ij; } - return 0; + return NULL; +} + +static struct static_group *static_group_find(struct list *static_group_list, + pim_addr group_addr, + pim_addr source_addr) +{ + struct listnode *node; + struct static_group *stgrp; + + assert(static_group_list); + + for (ALL_LIST_ELEMENTS_RO(static_group_list, node, stgrp)) { + if ((!pim_addr_cmp(group_addr, stgrp->group_addr)) && + (!pim_addr_cmp(source_addr, stgrp->source_addr))) + return stgrp; + } + + return NULL; } static int gm_join_sock(const char *ifname, ifindex_t ifindex, @@ -1266,7 +1283,8 @@ static int gm_join_sock(const char *ifname, ifindex_t ifindex, } static struct gm_join *gm_join_new(struct interface *ifp, pim_addr group_addr, - pim_addr source_addr) + pim_addr source_addr, + enum gm_join_type join_type) { struct pim_interface *pim_ifp; struct gm_join *ij; @@ -1289,6 +1307,7 @@ static struct gm_join *gm_join_new(struct interface *ifp, pim_addr group_addr, ij->sock_fd = join_fd; ij->group_addr = group_addr; ij->source_addr = source_addr; + ij->join_type = join_type; ij->sock_creation = pim_time_monotonic_sec(); listnode_add(pim_ifp->gm_join_list, ij); @@ -1296,8 +1315,36 @@ static struct gm_join *gm_join_new(struct interface *ifp, pim_addr group_addr, return ij; } +static struct static_group *static_group_new(struct interface *ifp, + pim_addr group_addr, + pim_addr source_addr) +{ + struct pim_interface *pim_ifp; + struct static_group *stgrp; + pim_sgaddr sg; + + pim_ifp = ifp->info; + assert(pim_ifp); + + stgrp = XCALLOC(MTYPE_PIM_STATIC_GROUP, sizeof(*stgrp)); + + stgrp->group_addr = group_addr; + stgrp->source_addr = source_addr; + stgrp->oilp = NULL; + + memset(&sg, 0, sizeof(sg)); + sg.src = source_addr; + sg.grp = group_addr; + + tib_sg_gm_join(pim_ifp->pim, sg, ifp, &(stgrp->oilp)); + + listnode_add(pim_ifp->static_group_list, stgrp); + + return stgrp; +} + ferr_r pim_if_gm_join_add(struct interface *ifp, pim_addr group_addr, - pim_addr source_addr) + pim_addr source_addr, enum gm_join_type join_type) { struct pim_interface *pim_ifp; struct gm_join *ij; @@ -1319,10 +1366,16 @@ ferr_r pim_if_gm_join_add(struct interface *ifp, pim_addr group_addr, * group */ if (ij) { + /* turn an existing join into a "both" join */ + if (ij->join_type != join_type) + ij->join_type = GM_JOIN_BOTH; return ferr_ok(); } - (void)gm_join_new(ifp, group_addr, source_addr); + if (!gm_join_new(ifp, group_addr, source_addr, join_type)) { + return ferr_cfg_invalid("can't join (%pPA,%pPA) on interface %s", + &source_addr, &group_addr, ifp->name); + } if (PIM_DEBUG_GM_EVENTS) { zlog_debug( @@ -1335,7 +1388,7 @@ ferr_r pim_if_gm_join_add(struct interface *ifp, pim_addr group_addr, } int pim_if_gm_join_del(struct interface *ifp, pim_addr group_addr, - pim_addr source_addr) + pim_addr source_addr, enum gm_join_type join_type) { struct pim_interface *pim_ifp; struct gm_join *ij; @@ -1361,6 +1414,20 @@ int pim_if_gm_join_del(struct interface *ifp, pim_addr group_addr, return -3; } + if (ij->join_type != join_type) { + if (ij->join_type != GM_JOIN_BOTH) { + zlog_warn("%s: wrong " GM + " gm_join_type %pPAs source %pPAs on interface %s", + __func__, &group_addr, &source_addr, + ifp->name); + return -4; + } + /* drop back to a single join type from current setting of GM_JOIN_BOTH */ + ij->join_type = (join_type == GM_JOIN_STATIC ? GM_JOIN_PROXY + : GM_JOIN_STATIC); + return 0; + } + if (close(ij->sock_fd)) { zlog_warn( "%s: failure closing sock_fd=%d for " GM @@ -1379,7 +1446,6 @@ int pim_if_gm_join_del(struct interface *ifp, pim_addr group_addr, return 0; } -__attribute__((unused)) static void pim_if_gm_join_del_all(struct interface *ifp) { struct pim_interface *pim_ifp; @@ -1398,7 +1464,160 @@ static void pim_if_gm_join_del_all(struct interface *ifp) return; for (ALL_LIST_ELEMENTS(pim_ifp->gm_join_list, node, nextnode, ij)) - pim_if_gm_join_del(ifp, ij->group_addr, ij->source_addr); + pim_if_gm_join_del(ifp, ij->group_addr, ij->source_addr, + GM_JOIN_STATIC); +} + +ferr_r pim_if_static_group_add(struct interface *ifp, pim_addr group_addr, + pim_addr source_addr) +{ + struct pim_interface *pim_ifp; + struct static_group *stgrp; + + pim_ifp = ifp->info; + if (!pim_ifp) { + return ferr_cfg_invalid("multicast not enabled on interface %s", + ifp->name); + } + + if (!pim_ifp->static_group_list) { + pim_ifp->static_group_list = list_new(); + pim_ifp->static_group_list->del = + (void (*)(void *))static_group_free; + } + + stgrp = static_group_find(pim_ifp->static_group_list, group_addr, + source_addr); + + /* This interface has already been configured with this static group + */ + if (stgrp) + return ferr_ok(); + + (void)static_group_new(ifp, group_addr, source_addr); + + if (PIM_DEBUG_GM_EVENTS) { + zlog_debug("%s: Added static group (S,G)=(%pPA,%pPA) on interface %s", + __func__, &source_addr, &group_addr, ifp->name); + } + + return ferr_ok(); +} + +int pim_if_static_group_del(struct interface *ifp, pim_addr group_addr, + pim_addr source_addr) +{ + struct pim_interface *pim_ifp; + struct static_group *stgrp; + pim_sgaddr sg; + + pim_ifp = ifp->info; + if (!pim_ifp) { + zlog_warn("%s: multicast not enabled on interface %s", __func__, + ifp->name); + return -1; + } + + if (!pim_ifp->static_group_list) { + zlog_warn("%s: no static groups on interface %s", __func__, + ifp->name); + return -2; + } + + stgrp = static_group_find(pim_ifp->static_group_list, group_addr, + source_addr); + if (!stgrp) { + zlog_warn("%s: could not find static group %pPAs source %pPAs on interface %s", + __func__, &group_addr, &source_addr, ifp->name); + return -3; + } + + memset(&sg, 0, sizeof(sg)); + sg.src = source_addr; + sg.grp = group_addr; + + tib_sg_gm_prune(pim_ifp->pim, sg, ifp, &(stgrp->oilp)); + + listnode_delete(pim_ifp->static_group_list, stgrp); + static_group_free(stgrp); + if (listcount(pim_ifp->static_group_list) < 1) { + list_delete(&pim_ifp->static_group_list); + pim_ifp->static_group_list = 0; + } + + return 0; +} + +static void pim_if_static_group_del_all(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + struct listnode *node; + struct listnode *nextnode; + struct static_group *stgrp; + + pim_ifp = ifp->info; + if (!pim_ifp) { + zlog_warn("%s: multicast not enabled on interface %s", __func__, + ifp->name); + return; + } + + if (!pim_ifp->static_group_list) + return; + + for (ALL_LIST_ELEMENTS(pim_ifp->static_group_list, node, nextnode, + stgrp)) + pim_if_static_group_del(ifp, stgrp->group_addr, + stgrp->source_addr); +} + +void pim_if_gm_proxy_init(struct pim_instance *pim, struct interface *oif) +{ + struct interface *ifp; + + FOR_ALL_INTERFACES (pim->vrf, ifp) { + struct pim_interface *pim_ifp = ifp->info; + struct listnode *source_node, *group_node; + struct gm_group *group; + struct gm_source *src; + + if (!pim_ifp) + continue; + + if (ifp == oif) /* skip the source interface */ + continue; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_group_list, group_node, + group)) { + for (ALL_LIST_ELEMENTS_RO(group->group_source_list, + source_node, src)) { + pim_if_gm_join_add(oif, group->group_addr, + src->source_addr, + GM_JOIN_PROXY); + } + } + } /* scan interfaces */ +} + +void pim_if_gm_proxy_finis(struct pim_instance *pim, struct interface *ifp) +{ + struct pim_interface *pim_ifp = ifp->info; + struct listnode *join_node; + struct listnode *next_join_node; + struct gm_join *join; + + if (!pim_ifp) { + zlog_warn("%s: multicast not enabled on interface %s", __func__, + ifp->name); + return; + } + + for (ALL_LIST_ELEMENTS(pim_ifp->gm_join_list, join_node, next_join_node, + join)) { + if (join) + pim_if_gm_join_del(ifp, join->group_addr, + join->source_addr, GM_JOIN_PROXY); + } } /* @@ -1683,6 +1902,14 @@ static int pim_ifp_up(struct interface *ifp) } } } + +#if PIM_IPV == 4 + if (pim->autorp && pim->autorp->do_discovery && pim_ifp && + pim_ifp->pim_enable) + pim_autorp_add_ifp(ifp); +#endif + + pim_cand_addrs_changed(); return 0; } @@ -1719,6 +1946,11 @@ static int pim_ifp_down(struct interface *ifp) pim_ifstat_reset(ifp); } +#if PIM_IPV == 4 + pim_autorp_rm_ifp(ifp); +#endif + + pim_cand_addrs_changed(); return 0; } @@ -1790,6 +2022,11 @@ void pim_pim_interface_delete(struct interface *ifp) if (!pim_ifp) return; +#if PIM_IPV == 4 + if (pim_ifp->pim_enable) + pim_autorp_rm_ifp(ifp); +#endif + pim_ifp->pim_enable = false; pim_if_membership_clear(ifp); |