mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-11-04 08:04:27 +00:00 
			
		
		
		
	This commit frees dynamically allocated memory associated
with `pbrms->nhgrp_name` and `pbrms->dst` which were causing memory leaks.
The ASan leak log for reference:
```
=================================================================
==107458==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 56 byte(s) in 1 object(s) allocated from:
    #0 0x7f87d644ca37 in __interceptor_calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:154
    #1 0x7f87d5feaa37 in qcalloc ../lib/memory.c:105
    #2 0x7f87d6054ffd in prefix_new ../lib/prefix.c:1180
    #3 0x55722f3c2885 in pbr_map_match_dst_magic ../pbrd/pbr_vty.c:302
    #4 0x55722f3b5c24 in pbr_map_match_dst pbrd/pbr_vty_clippy.c:228
    #5 0x7f87d5f32d61 in cmd_execute_command_real ../lib/command.c:993
    #6 0x7f87d5f330ee in cmd_execute_command ../lib/command.c:1052
    #7 0x7f87d5f33dc0 in cmd_execute ../lib/command.c:1218
    #8 0x7f87d60e4177 in vty_command ../lib/vty.c:591
    #9 0x7f87d60e905c in vty_execute ../lib/vty.c:1354
    #10 0x7f87d60ef45a in vtysh_read ../lib/vty.c:2362
    #11 0x7f87d60d42d4 in event_call ../lib/event.c:1979
    #12 0x7f87d5fbe828 in frr_run ../lib/libfrr.c:1213
    #13 0x55722f3ac795 in main ../pbrd/pbr_main.c:168
    #14 0x7f87d5b82d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
Direct leak of 2 byte(s) in 1 object(s) allocated from:
    #0 0x7f87d63f39a7 in __interceptor_strdup ../../../../src/libsanitizer/asan/asan_interceptors.cpp:454
    #1 0x7f87d5feaafc in qstrdup ../lib/memory.c:117
    #2 0x55722f3da139 in pbr_nht_set_seq_nhg ../pbrd/pbr_nht.c:551
    #3 0x55722f3c693f in pbr_map_nexthop_group_magic ../pbrd/pbr_vty.c:1140
    #4 0x55722f3bdaae in pbr_map_nexthop_group pbrd/pbr_vty_clippy.c:1284
    #5 0x7f87d5f32d61 in cmd_execute_command_real ../lib/command.c:993
    #6 0x7f87d5f330ee in cmd_execute_command ../lib/command.c:1052
    #7 0x7f87d5f33dc0 in cmd_execute ../lib/command.c:1218
    #8 0x7f87d60e4177 in vty_command ../lib/vty.c:591
    #9 0x7f87d60e905c in vty_execute ../lib/vty.c:1354
    #10 0x7f87d60ef45a in vtysh_read ../lib/vty.c:2362
    #11 0x7f87d60d42d4 in event_call ../lib/event.c:1979
    #12 0x7f87d5fbe828 in frr_run ../lib/libfrr.c:1213
    #13 0x55722f3ac795 in main ../pbrd/pbr_main.c:168
    #14 0x7f87d5b82d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
SUMMARY: AddressSanitizer: 58 byte(s) leaked in 2 allocation(s).
```
Signed-off-by: Keelan Cannoo <keelan.cannoo@icloud.com>
		
	
			
		
			
				
	
	
		
			948 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			948 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
						|
/*
 | 
						|
 * PBR-map Code
 | 
						|
 * Copyright (C) 2018 Cumulus Networks, Inc.
 | 
						|
 *               Donald Sharp
 | 
						|
 * Portions:
 | 
						|
 *		Copyright (c) 2021 The MITRE Corporation.
 | 
						|
 *		Copyright (c) 2023 LabN Consulting, L.L.C.
 | 
						|
 */
 | 
						|
#include <zebra.h>
 | 
						|
 | 
						|
#include "frrevent.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.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;
 | 
						|
}
 | 
						|
 | 
						|
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);
 | 
						|
 | 
						|
	if (pbrms->nhgrp_name)
 | 
						|
		XFREE(MTYPE_TMP, pbrms->nhgrp_name);
 | 
						|
 | 
						|
	prefix_free(&pbrms->dst);
 | 
						|
 | 
						|
	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 = NULL;
 | 
						|
	struct pbr_map_sequence *pbrms = NULL;
 | 
						|
	struct listnode *node = NULL;
 | 
						|
 | 
						|
	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_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)
 | 
						|
{
 | 
						|
	/* clang-format off */
 | 
						|
	if (
 | 
						|
		!CHECK_FLAG(pbrms->filter_bm, (
 | 
						|
			PBR_FILTER_SRC_IP |
 | 
						|
			PBR_FILTER_DST_IP |
 | 
						|
			PBR_FILTER_SRC_PORT |
 | 
						|
			PBR_FILTER_DST_PORT |
 | 
						|
 | 
						|
			PBR_FILTER_IP_PROTOCOL |
 | 
						|
			PBR_FILTER_DSCP |
 | 
						|
			PBR_FILTER_ECN |
 | 
						|
 | 
						|
			PBR_FILTER_FWMARK |
 | 
						|
			PBR_FILTER_PCP |
 | 
						|
			PBR_FILTER_VLAN_ID |
 | 
						|
			PBR_FILTER_VLAN_FLAGS
 | 
						|
		)) &&
 | 
						|
		!CHECK_FLAG(pbrms->action_bm, (
 | 
						|
			PBR_ACTION_SRC_IP |
 | 
						|
			PBR_ACTION_DST_IP |
 | 
						|
			PBR_ACTION_SRC_PORT |
 | 
						|
			PBR_ACTION_DST_PORT |
 | 
						|
 | 
						|
			PBR_ACTION_DSCP |
 | 
						|
			PBR_ACTION_ECN |
 | 
						|
 | 
						|
			PBR_ACTION_PCP |
 | 
						|
			PBR_ACTION_VLAN_ID |
 | 
						|
			PBR_ACTION_VLAN_STRIP_INNER_ANY |
 | 
						|
 | 
						|
			PBR_ACTION_QUEUE_ID
 | 
						|
		))
 | 
						|
	) {
 | 
						|
		pbrms->reason |= PBR_MAP_INVALID_EMPTY;
 | 
						|
	}
 | 
						|
	/* clang-format on */
 | 
						|
}
 | 
						|
 | 
						|
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 (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_ID) &&
 | 
						|
	    (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_STRIP_INNER_ANY)))
 | 
						|
		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;
 | 
						|
}
 |