diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:53:30 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:53:30 +0000 |
commit | 2c7cac91ed6e7db0f6937923d2b57f97dbdbc337 (patch) | |
tree | c05dc0f8e6aa3accc84e3e5cffc933ed94941383 /pbrd/pbr_map.c | |
parent | Initial commit. (diff) | |
download | frr-2c7cac91ed6e7db0f6937923d2b57f97dbdbc337.tar.xz frr-2c7cac91ed6e7db0f6937923d2b57f97dbdbc337.zip |
Adding upstream version 8.4.4.upstream/8.4.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | pbrd/pbr_map.c | 924 |
1 files changed, 924 insertions, 0 deletions
diff --git a/pbrd/pbr_map.c b/pbrd/pbr_map.c new file mode 100644 index 0000000..5daac55 --- /dev/null +++ b/pbrd/pbr_map.c @@ -0,0 +1,924 @@ +/* + * 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" +#include "pbr_vrf.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 bool pbr_map_check_valid_internal(struct pbr_map *pbrm); +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) +{ + XFREE(MTYPE_TMP, pbrms->internal_nhg_name); + + QOBJ_UNREG(pbrms); + 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 bool pbrms_is_installed(const struct pbr_map_sequence *pbrms, + const struct pbr_map_interface *pmi) +{ + uint64_t is_installed = (uint64_t)1 << pmi->install_bit; + + is_installed &= pbrms->installed; + + if (is_installed) + return true; + + return false; +} + +/* If any sequence is installed on the interface, assume installed */ +static bool +pbr_map_interface_is_installed(const struct pbr_map *pbrm, + const struct pbr_map_interface *check_pmi) +{ + + struct pbr_map_sequence *pbrms; + struct pbr_map_interface *pmi; + struct listnode *node, *inode; + + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) + for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) + if (pmi == check_pmi && pbrms_is_installed(pbrms, pmi)) + return true; + + return false; +} + +static bool pbr_map_interface_is_valid(const struct pbr_map_interface *pmi) +{ + /* Don't install rules without a real ifindex on the incoming interface. + * + * This can happen when we have config for an interface that does not + * exist or when an interface is changing vrfs. + */ + if (pmi->ifp && pmi->ifp->ifindex != IFINDEX_INTERNAL) + return true; + + return false; +} + +static void pbr_map_pbrms_update_common(struct pbr_map_sequence *pbrms, + bool install, bool changed) +{ + struct pbr_map *pbrm; + struct listnode *node; + struct pbr_map_interface *pmi; + + pbrm = pbrms->parent; + + if (pbrms->nhs_installed && pbrm->incoming->count) { + for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, node, pmi)) { + if (!pmi->ifp) + continue; + + if (install && !pbr_map_interface_is_valid(pmi)) + continue; + + pbr_send_pbr_map(pbrms, pmi, install, changed); + } + } +} + +static void pbr_map_pbrms_install(struct pbr_map_sequence *pbrms, bool changed) +{ + pbr_map_pbrms_update_common(pbrms, true, changed); +} + +static void pbr_map_pbrms_uninstall(struct pbr_map_sequence *pbrms) +{ + pbr_map_pbrms_update_common(pbrms, false, false); +} + +static const char *const pbr_map_reason_str[] = { + "Invalid NH-group", "Invalid NH", "No Nexthops", + "Both NH and NH-Group", "Invalid Src or Dst", "Invalid VRF", + "Both VLAN Set and Strip", "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_final_interface_deletion(struct pbr_map *pbrm, + struct pbr_map_interface *pmi) +{ + if (pmi->delete && !pbr_map_interface_is_installed(pbrm, pmi)) { + listnode_delete(pbrm->incoming, pmi); + pmi->pbrm = NULL; + + bf_release_index(pbrm->ifi_bitfield, pmi->install_bit); + XFREE(MTYPE_PBR_MAP_INTERFACE, pmi); + } +} + +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); + + bf_assign_index(pbrm->ifi_bitfield, pmi->install_bit); + pbr_map_check_valid(pbrm->name); + if (pbrm->valid) + pbr_map_install(pbrm); +} + +static int +pbr_map_policy_interface_update_common(const struct interface *ifp, + struct pbr_interface **pbr_ifp, + struct pbr_map **pbrm) +{ + if (!ifp->info) { + DEBUGD(&pbr_dbg_map, "%s: %s has no pbr_interface info", + __func__, ifp->name); + return -1; + } + + *pbr_ifp = ifp->info; + + *pbrm = pbrm_find((*pbr_ifp)->mapname); + + if (!*pbrm) { + DEBUGD(&pbr_dbg_map, "%s: applied PBR-MAP(%s) does not exist?", + __func__, (*pbr_ifp)->mapname); + return -1; + } + + return 0; +} + +void pbr_map_policy_interface_update(const struct interface *ifp, bool state_up) +{ + struct pbr_interface *pbr_ifp; + struct pbr_map_sequence *pbrms; + struct pbr_map *pbrm; + struct listnode *node, *inode; + struct pbr_map_interface *pmi; + + if (pbr_map_policy_interface_update_common(ifp, &pbr_ifp, &pbrm)) + return; + + DEBUGD(&pbr_dbg_map, "%s: %s %s rules on interface %s", __func__, + pbr_ifp->mapname, (state_up ? "installing" : "removing"), + ifp->name); + + /* + * Walk the list and install/remove maps on the interface. + */ + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) + for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) + if (pmi->ifp == ifp && pbr_map_interface_is_valid(pmi)) + pbr_send_pbr_map(pbrms, pmi, state_up, true); +} + +static void pbrms_vrf_update(struct pbr_map_sequence *pbrms, + const struct pbr_vrf *pbr_vrf) +{ + const char *vrf_name = pbr_vrf_name(pbr_vrf); + + if (pbrms->vrf_lookup + && (strncmp(vrf_name, pbrms->vrf_name, sizeof(pbrms->vrf_name)) + == 0)) { + DEBUGD(&pbr_dbg_map, " Seq %u uses vrf %s (%u), updating map", + pbrms->seqno, vrf_name, pbr_vrf_id(pbr_vrf)); + + pbr_map_check(pbrms, false); + } +} + +/* Vrf enabled/disabled */ +void pbr_map_vrf_update(const struct pbr_vrf *pbr_vrf) +{ + struct pbr_map *pbrm; + struct pbr_map_sequence *pbrms; + struct listnode *node; + + if (!pbr_vrf) + return; + + bool enabled = pbr_vrf_is_enabled(pbr_vrf); + + DEBUGD(&pbr_dbg_map, "%s: %s (%u) %s, updating pbr maps", __func__, + pbr_vrf_name(pbr_vrf), pbr_vrf_id(pbr_vrf), + enabled ? "enabled" : "disabled"); + + RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) { + DEBUGD(&pbr_dbg_map, "%s: Looking at %s", __func__, pbrm->name); + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) + pbrms_vrf_update(pbrms, pbr_vrf); + } +} + +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, 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); + + bf_free(pbrm->ifi_bitfield); + XFREE(MTYPE_PBR_MAP, pbrm); + } +} + +static void pbr_map_delete_common(struct pbr_map_sequence *pbrms) +{ + struct pbr_map *pbrm = pbrms->parent; + + pbr_map_pbrms_uninstall(pbrms); + + pbrm->valid = false; + pbrms->nhs_installed = false; + pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS; + XFREE(MTYPE_TMP, pbrms->nhgrp_name); +} + +void pbr_map_delete_nexthops(struct pbr_map_sequence *pbrms) +{ + pbr_map_delete_common(pbrms); +} + +void pbr_map_delete_vrf(struct pbr_map_sequence *pbrms) +{ + pbr_map_delete_common(pbrms); +} + +struct pbr_map_sequence *pbrms_lookup_unique(uint32_t unique, char *ifname, + struct pbr_map_interface **ppmi) +{ + 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 (strcmp(pmi->ifp->name, ifname) != 0) + continue; + + if (ppmi) + *ppmi = pmi; + + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, snode, + pbrms)) { + DEBUGD(&pbr_dbg_map, "%s: Comparing %u to %u", + __func__, 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); + } + } + } +} + +/* Decodes a standardized DSCP into its representative value */ +uint8_t pbr_map_decode_dscp_enum(const char *name) +{ + /* Standard Differentiated Services Field Codepoints */ + if (!strcmp(name, "cs0")) + return 0; + if (!strcmp(name, "cs1")) + return 8; + if (!strcmp(name, "cs2")) + return 16; + if (!strcmp(name, "cs3")) + return 24; + if (!strcmp(name, "cs4")) + return 32; + if (!strcmp(name, "cs5")) + return 40; + if (!strcmp(name, "cs6")) + return 48; + if (!strcmp(name, "cs7")) + return 56; + if (!strcmp(name, "af11")) + return 10; + if (!strcmp(name, "af12")) + return 12; + if (!strcmp(name, "af13")) + return 14; + if (!strcmp(name, "af21")) + return 18; + if (!strcmp(name, "af22")) + return 20; + if (!strcmp(name, "af23")) + return 22; + if (!strcmp(name, "af31")) + return 26; + if (!strcmp(name, "af32")) + return 28; + if (!strcmp(name, "af33")) + return 30; + if (!strcmp(name, "af41")) + return 34; + if (!strcmp(name, "af42")) + return 36; + if (!strcmp(name, "af43")) + return 38; + if (!strcmp(name, "ef")) + return 46; + if (!strcmp(name, "voice-admit")) + return 44; + + /* No match? Error out */ + return -1; +} + +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)); + snprintf(pbrm->name, sizeof(pbrm->name), "%s", 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); + + bf_init(pbrm->ifi_bitfield, 64); + 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->action_vlan_id = 0; + pbrms->action_vlan_flags = 0; + pbrms->action_pcp = 0; + + pbrms->action_queue_id = PBR_MAP_UNDEFINED_QUEUE_ID; + + pbrms->reason = + PBR_MAP_INVALID_EMPTY | + PBR_MAP_INVALID_NO_NEXTHOPS; + pbrms->vrf_name[0] = '\0'; + + QOBJ_REG(pbrms, pbr_map_sequence); + listnode_add_sort(pbrm->seqnumbers, pbrms); + } + + return pbrms; +} + +static void +pbr_map_sequence_check_nexthops_valid(struct pbr_map_sequence *pbrms) +{ + /* Check if any are present first */ + if (!pbrms->vrf_unchanged && !pbrms->vrf_lookup && !pbrms->nhg + && !pbrms->nhgrp_name) { + pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS; + return; + } + + /* + * Check validness of vrf. + */ + + /* This one can be considered always valid */ + if (pbrms->vrf_unchanged) + pbrms->nhs_installed = true; + + if (pbrms->vrf_lookup) { + struct pbr_vrf *pbr_vrf = + pbr_vrf_lookup_by_name(pbrms->vrf_name); + + if (pbr_vrf && pbr_vrf_is_valid(pbr_vrf)) + pbrms->nhs_installed = true; + else + pbrms->reason |= PBR_MAP_INVALID_VRF; + } + + /* + * Check validness of the nexthop or nexthop-group + */ + + /* Only nexthop or nexthop group allowed */ + 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_not_empty(struct pbr_map_sequence *pbrms) +{ + if (!pbrms->src && !pbrms->dst && !pbrms->mark && !pbrms->dsfield + && !pbrms->action_vlan_id && !pbrms->action_vlan_flags + && !pbrms->action_pcp + && pbrms->action_queue_id == PBR_MAP_UNDEFINED_QUEUE_ID) + pbrms->reason |= PBR_MAP_INVALID_EMPTY; +} + +static void pbr_map_sequence_check_vlan_actions(struct pbr_map_sequence *pbrms) +{ + /* The set vlan tag action does the following: + * 1. If the frame is untagged, it tags the frame with the + * configured VLAN ID. + * 2. If the frame is tagged, if replaces the tag. + * + * The strip vlan action removes any inner tag, so it is invalid to + * specify both a set and strip action. + */ + if ((pbrms->action_vlan_id != 0) && (pbrms->action_vlan_flags != 0)) + pbrms->reason |= PBR_MAP_INVALID_SET_STRIP_VLAN; +} + + +/* + * 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_vlan_actions(pbrms); + pbr_map_sequence_check_not_empty(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?", __func__, + name); + return false; + } + + pbr_map_check_valid_internal(pbrm); + return pbrm->valid; +} + +void pbr_map_schedule_policy_from_nhg(const char *nh_group, bool installed) +{ + 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", __func__, pbrm->name); + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) { + DEBUGD(&pbr_dbg_map, " NH Grp name: %s", + pbrms->nhgrp_name ? + pbrms->nhgrp_name : pbrms->internal_nhg_name); + + if (pbrms->nhgrp_name + && (strcmp(nh_group, pbrms->nhgrp_name) == 0)) { + pbrms->nhs_installed = installed; + + pbr_map_check(pbrms, false); + } + + if (pbrms->nhg + && (strcmp(nh_group, pbrms->internal_nhg_name) + == 0)) { + pbrms->nhs_installed = installed; + + pbr_map_check(pbrms, false); + } + } + } +} + +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", __func__, 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", __func__, + name, pbrms->seqno, pbrm->valid, pbrms->nhs_installed); + + if (pbrm->valid && pbrms->nhs_installed + && pbrm->incoming->count) { + DEBUGD(&pbr_dbg_map, " Installing %s %u", pbrm->name, + pbrms->seqno); + for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) + if (pbr_map_interface_is_valid(pmi)) + pbr_send_pbr_map(pbrms, pmi, true, + false); + } + } +} + +void pbr_map_policy_delete(struct pbr_map *pbrm, struct pbr_map_interface *pmi) +{ + struct listnode *node; + struct pbr_map_sequence *pbrms; + bool sent = false; + + + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) + if (pbr_send_pbr_map(pbrms, pmi, false, true)) + sent = true; /* rule removal sent to zebra */ + + pmi->delete = true; + + /* + * If we actually sent something for deletion, wait on zapi callback + * before clearing data. + */ + if (sent) + return; + + pbr_map_final_interface_deletion(pbrm, 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; + + /* Set data we were waiting on */ + if (pbrms->nhgrp_name) + pbr_nht_set_seq_nhg_data( + pbrms, + nhgc_find(pbrms->nhgrp_name)); + + 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, false); + } + } + } +} + +void pbr_map_check_vrf_nh_group_change(const char *nh_group, + struct pbr_vrf *pbr_vrf, + uint32_t old_vrf_id) +{ + struct pbr_map *pbrm; + struct pbr_map_sequence *pbrms; + struct listnode *node; + + + RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) { + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) { + if (pbrms->nhgrp_name) + continue; + + if (pbrms->nhg == NULL) + continue; + + if (strcmp(nh_group, pbrms->internal_nhg_name)) + continue; + + if (pbrms->nhg->nexthop == NULL) + continue; + + if (pbrms->nhg->nexthop->vrf_id != old_vrf_id) + continue; + + pbrms->nhg->nexthop->vrf_id = pbr_vrf_id(pbr_vrf); + } + } +} + +void pbr_map_check_interface_nh_group_change(const char *nh_group, + struct interface *ifp, + ifindex_t oldifindex) +{ + struct pbr_map *pbrm; + struct pbr_map_sequence *pbrms; + struct listnode *node; + + RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) { + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) { + if (pbrms->nhgrp_name) + continue; + + if (pbrms->nhg == NULL) + continue; + + if (strcmp(nh_group, pbrms->internal_nhg_name)) + continue; + + if (pbrms->nhg->nexthop == NULL) + continue; + + if (pbrms->nhg->nexthop->ifindex != oldifindex) + continue; + + pbrms->nhg->nexthop->ifindex = ifp->ifindex; + } + } +} + +void pbr_map_check(struct pbr_map_sequence *pbrms, bool changed) +{ + struct pbr_map *pbrm; + bool install; + + pbrm = pbrms->parent; + DEBUGD(&pbr_dbg_map, "%s: for %s(%u)", __func__, pbrm->name, + pbrms->seqno); + if (pbr_map_check_valid(pbrm->name)) + DEBUGD(&pbr_dbg_map, "We are totally valid %s", + pbrm->name); + + if (pbrms->reason == PBR_MAP_VALID_SEQUENCE_NUMBER) { + install = true; + DEBUGD(&pbr_dbg_map, "%s: Installing %s(%u) reason: %" PRIu64, + __func__, pbrm->name, pbrms->seqno, pbrms->reason); + DEBUGD(&pbr_dbg_map, + " Sending PBR_MAP_POLICY_INSTALL event"); + } else { + install = false; + DEBUGD(&pbr_dbg_map, "%s: Removing %s(%u) reason: %" PRIu64, + __func__, pbrm->name, pbrms->seqno, pbrms->reason); + } + + if (install) + pbr_map_pbrms_install(pbrms, changed); + else + pbr_map_pbrms_uninstall(pbrms); +} + +void pbr_map_install(struct pbr_map *pbrm) +{ + struct pbr_map_sequence *pbrms; + struct listnode *node; + + if (!pbrm->incoming->count) + return; + + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) + pbr_map_pbrms_install(pbrms, false); +} + +void pbr_map_init(void) +{ + RB_INIT(pbr_map_entry_head, &pbr_maps); + + pbr_map_sequence_unique = 1; +} |