diff options
Diffstat (limited to 'pbrd/pbr_map.c')
| -rw-r--r-- | pbrd/pbr_map.c | 569 |
1 files changed, 569 insertions, 0 deletions
diff --git a/pbrd/pbr_map.c b/pbrd/pbr_map.c new file mode 100644 index 0000000000..ea79320a71 --- /dev/null +++ b/pbrd/pbr_map.c @@ -0,0 +1,569 @@ +/* + * PBR-map Code + * Copyright (C) 2018 Cumulus Networks, Inc. + * Donald Sharp + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include <zebra.h> + +#include "thread.h" +#include "linklist.h" +#include "prefix.h" +#include "table.h" +#include "vrf.h" +#include "nexthop.h" +#include "nexthop_group.h" +#include "memory.h" +#include "log.h" +#include "vty.h" + +#include "pbr_nht.h" +#include "pbr_map.h" +#include "pbr_zebra.h" +#include "pbr_memory.h" +#include "pbr_debug.h" + +DEFINE_MTYPE_STATIC(PBRD, PBR_MAP, "PBR Map") +DEFINE_MTYPE_STATIC(PBRD, PBR_MAP_SEQNO, "PBR Map Sequence") +DEFINE_MTYPE_STATIC(PBRD, PBR_MAP_INTERFACE, "PBR Map Interface") + +static uint32_t pbr_map_sequence_unique; + +static inline int pbr_map_compare(const struct pbr_map *pbrmap1, + const struct pbr_map *pbrmap2); + +RB_GENERATE(pbr_map_entry_head, pbr_map, pbr_map_entry, pbr_map_compare) + +struct pbr_map_entry_head pbr_maps = RB_INITIALIZER(&pbr_maps); + +DEFINE_QOBJ_TYPE(pbr_map_sequence) + +static inline int pbr_map_compare(const struct pbr_map *pbrmap1, + const struct pbr_map *pbrmap2) +{ + return strcmp(pbrmap1->name, pbrmap2->name); +} + +static int pbr_map_sequence_compare(const struct pbr_map_sequence *pbrms1, + const struct pbr_map_sequence *pbrms2) +{ + if (pbrms1->seqno == pbrms2->seqno) + return 0; + + if (pbrms1->seqno < pbrms2->seqno) + return -1; + + return 1; +} + +static void pbr_map_sequence_delete(struct pbr_map_sequence *pbrms) +{ + if (pbrms->internal_nhg_name) + XFREE(MTYPE_TMP, pbrms->internal_nhg_name); + + XFREE(MTYPE_PBR_MAP_SEQNO, pbrms); +} + +static int pbr_map_interface_compare(const struct pbr_map_interface *pmi1, + const struct pbr_map_interface *pmi2) +{ + return strcmp(pmi1->ifp->name, pmi2->ifp->name); +} + +static void pbr_map_interface_list_delete(struct pbr_map_interface *pmi) +{ + struct pbr_map_interface *pmi_int; + struct listnode *node, *nnode; + struct pbr_map *pbrm; + + RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) { + for (ALL_LIST_ELEMENTS(pbrm->incoming, node, nnode, pmi_int)) { + if (pmi == pmi_int) { + pbr_map_policy_delete(pbrm, pmi); + return; + } + } + } +} + +static const char *pbr_map_reason_str[] = { + "Invalid NH-group", "Invalid NH", "No Nexthops", + "Both NH and NH-Group", "Invalid Src or Dst", "Deleting Sequence", +}; + +void pbr_map_reason_string(unsigned int reason, char *buf, int size) +{ + unsigned int bit; + int len = 0; + + if (!buf) + return; + + for (bit = 0; bit < array_size(pbr_map_reason_str); bit++) { + if ((reason & (1 << bit)) && (len < size)) { + len += snprintf((buf + len), (size - len), "%s%s", + (len > 0) ? ", " : "", + pbr_map_reason_str[bit]); + } + } +} + + +void pbr_map_interface_delete(struct pbr_map *pbrm, struct interface *ifp_del) +{ + + struct listnode *node; + struct pbr_map_interface *pmi; + + for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, node, pmi)) { + if (ifp_del == pmi->ifp) + break; + } + + if (pmi) + pbr_map_policy_delete(pbrm, pmi); +} + +void pbr_map_add_interface(struct pbr_map *pbrm, struct interface *ifp_add) +{ + struct listnode *node; + struct pbr_map_interface *pmi; + + for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, node, pmi)) { + if (ifp_add == pmi->ifp) + return; + } + + pmi = XCALLOC(MTYPE_PBR_MAP_INTERFACE, sizeof(*pmi)); + pmi->ifp = ifp_add; + pmi->pbrm = pbrm; + listnode_add_sort(pbrm->incoming, pmi); + + pbr_map_check_valid(pbrm->name); + if (pbrm->valid && !pbrm->installed) + pbr_map_install(pbrm); +} + +void pbr_map_write_interfaces(struct vty *vty, struct interface *ifp) +{ + struct pbr_interface *pbr_ifp = ifp->info; + + if (pbr_ifp + && strncmp(pbr_ifp->mapname, "", sizeof(pbr_ifp->mapname)) != 0) + vty_out(vty, " pbr-policy %s\n", pbr_ifp->mapname); +} + +struct pbr_map *pbrm_find(const char *name) +{ + struct pbr_map pbrm; + + strlcpy(pbrm.name, name, sizeof(pbrm.name)); + + return RB_FIND(pbr_map_entry_head, &pbr_maps, &pbrm); +} + +extern void pbr_map_delete(struct pbr_map_sequence *pbrms) +{ + struct pbr_map *pbrm; + struct listnode *inode; + struct pbr_map_interface *pmi; + + pbrm = pbrms->parent; + + for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) + pbr_send_pbr_map(pbrms, pmi, false); + + if (pbrms->nhg) + pbr_nht_delete_individual_nexthop(pbrms); + + listnode_delete(pbrm->seqnumbers, pbrms); + + if (pbrm->seqnumbers->count == 0) { + RB_REMOVE(pbr_map_entry_head, &pbr_maps, pbrm); + XFREE(MTYPE_PBR_MAP, pbrm); + } +} + +void pbr_map_delete_nexthop_group(struct pbr_map_sequence *pbrms) +{ + struct pbr_map *pbrm = pbrms->parent; + struct listnode *node; + struct pbr_map_interface *pmi; + + if (pbrm->valid && pbrms->nhs_installed && pbrm->incoming->count) { + for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, node, pmi)) + pbr_send_pbr_map(pbrms, pmi, false); + } + + pbrm->valid = false; + pbrms->nhs_installed = false; + pbrms->installed = false; + pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS; + pbrms->nhgrp_name = NULL; +} + +struct pbr_map_sequence *pbrms_lookup_unique(uint32_t unique, + ifindex_t ifindex) +{ + struct pbr_map_sequence *pbrms; + struct listnode *snode, *inode; + struct pbr_map_interface *pmi; + struct pbr_map *pbrm; + + RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) { + for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) { + if (pmi->ifp->ifindex != ifindex) + continue; + + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, snode, + pbrms)) { + DEBUGD(&pbr_dbg_map, "%s: Comparing %u to %u", + __PRETTY_FUNCTION__, pbrms->unique, + unique); + if (pbrms->unique == unique) + return pbrms; + } + } + } + + return NULL; +} + +static void pbr_map_add_interfaces(struct pbr_map *pbrm) +{ + struct interface *ifp; + struct pbr_interface *pbr_ifp; + struct vrf *vrf; + + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + FOR_ALL_INTERFACES (vrf, ifp) { + if (ifp->info) { + pbr_ifp = ifp->info; + if (strcmp(pbrm->name, pbr_ifp->mapname) == 0) + pbr_map_add_interface(pbrm, ifp); + } + } + } +} + +struct pbr_map_sequence *pbrms_get(const char *name, uint32_t seqno) +{ + struct pbr_map *pbrm; + struct pbr_map_sequence *pbrms; + struct listnode *node; + + pbrm = pbrm_find(name); + if (!pbrm) { + pbrm = XCALLOC(MTYPE_PBR_MAP, sizeof(*pbrm)); + strcpy(pbrm->name, name); + + pbrm->seqnumbers = list_new(); + pbrm->seqnumbers->cmp = + (int (*)(void *, void *))pbr_map_sequence_compare; + pbrm->seqnumbers->del = + (void (*)(void *))pbr_map_sequence_delete; + + pbrm->incoming = list_new(); + pbrm->incoming->cmp = + (int (*)(void *, void *))pbr_map_interface_compare; + pbrm->incoming->del = + (void (*)(void *))pbr_map_interface_list_delete; + + RB_INSERT(pbr_map_entry_head, &pbr_maps, pbrm); + + pbr_map_add_interfaces(pbrm); + } + + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) { + if (pbrms->seqno == seqno) + break; + + } + + if (!pbrms) { + pbrms = XCALLOC(MTYPE_PBR_MAP_SEQNO, sizeof(*pbrms)); + pbrms->unique = pbr_map_sequence_unique++; + pbrms->seqno = seqno; + pbrms->ruleno = pbr_nht_get_next_rule(seqno); + pbrms->parent = pbrm; + pbrms->reason = + PBR_MAP_INVALID_SRCDST | + PBR_MAP_INVALID_NO_NEXTHOPS; + + QOBJ_REG(pbrms, pbr_map_sequence); + listnode_add_sort(pbrm->seqnumbers, pbrms); + + pbrm->installed = false; + } + + return pbrms; +} + +static void +pbr_map_sequence_check_nexthops_valid(struct pbr_map_sequence *pbrms) +{ + /* + * Check validness of the nexthop or nexthop-group + */ + if (!pbrms->nhg && !pbrms->nhgrp_name) + pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS; + + if (pbrms->nhg && pbrms->nhgrp_name) + pbrms->reason |= PBR_MAP_INVALID_BOTH_NHANDGRP; + + if (pbrms->nhg && + !pbr_nht_nexthop_group_valid(pbrms->internal_nhg_name)) + pbrms->reason |= PBR_MAP_INVALID_NEXTHOP; + + if (pbrms->nhgrp_name) { + if (!pbr_nht_nexthop_group_valid(pbrms->nhgrp_name)) + pbrms->reason |= PBR_MAP_INVALID_NEXTHOP_GROUP; + else + pbrms->nhs_installed = true; + } +} + +static void pbr_map_sequence_check_src_dst_valid(struct pbr_map_sequence *pbrms) +{ + if (!pbrms->src && !pbrms->dst) + pbrms->reason |= PBR_MAP_INVALID_SRCDST; +} + +/* + * Checks to see if we think that the pbmrs is valid. If we think + * the config is valid return true. + */ +static void pbr_map_sequence_check_valid(struct pbr_map_sequence *pbrms) +{ + pbr_map_sequence_check_nexthops_valid(pbrms); + + pbr_map_sequence_check_src_dst_valid(pbrms); +} + +static bool pbr_map_check_valid_internal(struct pbr_map *pbrm) +{ + struct pbr_map_sequence *pbrms; + struct listnode *node; + + pbrm->valid = true; + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) { + pbrms->reason = 0; + pbr_map_sequence_check_valid(pbrms); + /* + * A pbr_map_sequence that is invalid causes + * the whole shebang to be invalid + */ + if (pbrms->reason != 0) + pbrm->valid = false; + } + + return pbrm->valid; +} + +/* + * For a given PBR-MAP check to see if we think it is a + * valid config or not. If so note that it is and return + * that we are valid. + */ +bool pbr_map_check_valid(const char *name) +{ + struct pbr_map *pbrm; + + pbrm = pbrm_find(name); + if (!pbrm) { + DEBUGD(&pbr_dbg_map, + "%s: Specified PBR-MAP(%s) does not exist?", + __PRETTY_FUNCTION__, name); + return false; + } + + pbr_map_check_valid_internal(pbrm); + return pbrm->valid; +} + +void pbr_map_schedule_policy_from_nhg(const char *nh_group) +{ + struct pbr_map_sequence *pbrms; + struct pbr_map *pbrm; + struct listnode *node; + + RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) { + DEBUGD(&pbr_dbg_map, "%s: Looking at %s", __PRETTY_FUNCTION__, + pbrm->name); + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) { + DEBUGD(&pbr_dbg_map, "\tNH Grp name: %s", + pbrms->nhgrp_name ? pbrms->nhgrp_name : "NULL"); + + if (pbrms->nhgrp_name + && (strcmp(nh_group, pbrms->nhgrp_name) == 0)) { + pbrms->nhs_installed = true; + + pbr_map_check(pbrms); + } + + if (pbrms->nhg + && (strcmp(nh_group, pbrms->internal_nhg_name) + == 0)) { + pbrms->nhs_installed = true; + + pbr_map_check(pbrms); + } + } + } +} + +void pbr_map_policy_install(const char *name) +{ + struct pbr_map_sequence *pbrms; + struct pbr_map *pbrm; + struct listnode *node, *inode; + struct pbr_map_interface *pmi; + + DEBUGD(&pbr_dbg_map, "%s: for %s", __PRETTY_FUNCTION__, name); + pbrm = pbrm_find(name); + if (!pbrm) + return; + + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) { + DEBUGD(&pbr_dbg_map, + "%s: Looking at what to install %s(%u) %d %d", + __PRETTY_FUNCTION__, name, pbrms->seqno, pbrm->valid, + pbrms->nhs_installed); + + if (pbrm->valid && pbrms->nhs_installed && pbrm->incoming->count) { + DEBUGD(&pbr_dbg_map, "\tInstalling %s %u", + pbrm->name, pbrms->seqno); + for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) + pbr_send_pbr_map(pbrms, pmi, true); + } + } +} + +void pbr_map_policy_delete(struct pbr_map *pbrm, struct pbr_map_interface *pmi) +{ + struct listnode *node; + struct pbr_map_sequence *pbrms; + + + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) + pbr_send_pbr_map(pbrms, pmi, false); + + listnode_delete(pbrm->incoming, pmi); + pmi->pbrm = NULL; + XFREE(MTYPE_PBR_MAP_INTERFACE, pmi); +} + +/* + * For a nexthop group specified, see if any of the pbr-maps + * are using it and if so, check to see that we are still + * valid for usage. If we are valid then schedule the installation/deletion + * of the pbr-policy. + */ +void pbr_map_check_nh_group_change(const char *nh_group) +{ + struct pbr_map_sequence *pbrms; + struct pbr_map *pbrm; + struct listnode *node, *inode; + struct pbr_map_interface *pmi; + bool found_name; + + RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) { + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) { + found_name = false; + if (pbrms->nhgrp_name) + found_name = + !strcmp(nh_group, pbrms->nhgrp_name); + else if (pbrms->nhg) + found_name = !strcmp(nh_group, + pbrms->internal_nhg_name); + + if (found_name) { + bool original = pbrm->valid; + + pbr_map_check_valid_internal(pbrm); + + if (pbrm->valid && (original != pbrm->valid)) + pbr_map_install(pbrm); + + if (pbrm->valid == false) + for (ALL_LIST_ELEMENTS_RO( + pbrm->incoming, inode, + pmi)) + pbr_send_pbr_map(pbrms, pmi, + false); + } + } + } +} + +void pbr_map_check(struct pbr_map_sequence *pbrms) +{ + struct pbr_map *pbrm; + struct listnode *inode; + struct pbr_map_interface *pmi; + bool install; + + pbrm = pbrms->parent; + DEBUGD(&pbr_dbg_map, "%s: for %s(%u)", __PRETTY_FUNCTION__, + pbrm->name, pbrms->seqno); + if (pbr_map_check_valid(pbrm->name)) + DEBUGD(&pbr_dbg_map, "We are totally valid %s\n", + pbrm->name); + + DEBUGD(&pbr_dbg_map, "%s: Installing %s(%u) reason: %" PRIu64, + __PRETTY_FUNCTION__, pbrm->name, pbrms->seqno, pbrms->reason); + + if (pbrms->reason == PBR_MAP_VALID_SEQUENCE_NUMBER) { + install = true; + DEBUGD(&pbr_dbg_map, "%s: Installing %s(%u) reason: %" PRIu64, + __PRETTY_FUNCTION__, pbrm->name, pbrms->seqno, + pbrms->reason); + DEBUGD(&pbr_dbg_map, + "\tSending PBR_MAP_POLICY_INSTALL event"); + } else { + install = false; + DEBUGD(&pbr_dbg_map, + "%s: Removing %s(%u) reason: %" PRIu64, + __PRETTY_FUNCTION__, pbrm->name, + pbrms->seqno, pbrms->reason); + } + + for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) + pbr_send_pbr_map(pbrms, pmi, install); +} + +void pbr_map_install(struct pbr_map *pbrm) +{ + struct listnode *node, *inode; + struct pbr_map_sequence *pbrms; + struct pbr_map_interface *pmi; + + if (!pbrm->incoming->count) + return; + + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) + for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) + pbr_send_pbr_map(pbrms, pmi, true); + + pbrm->installed = true; +} + +void pbr_map_init(void) +{ + RB_INIT(pbr_map_entry_head, &pbr_maps); + + pbr_map_sequence_unique = 1; +} |
