#include <zebra.h>
#include <vrf.h>
+#include <sockunion.h>
#include <nexthop.h>
#include <nexthop_group.h>
#include <vty.h>
if (nexthop->next)
nexthop->next->prev = nexthop->prev;
+
+ nh->prev = NULL;
+ nh->next = NULL;
}
void copy_nexthops(struct nexthop **tnh, struct nexthop *nh,
while (nexthop) {
struct nexthop *next = nexthop_next(nexthop);
+ nexthop_del(&nhgc->nhg, nexthop);
if (nhg_hooks.del_nexthop)
nhg_hooks.del_nexthop(nhgc, nexthop);
return RB_FIND(nhgc_entry_head, &nhgc_entries, &find);
}
+static int nhgc_cmp_helper(const char *a, const char *b)
+{
+ if (!a && !b)
+ return 0;
+
+ if (a && !b)
+ return -1;
+
+ if (!a && b)
+ return 1;
+
+ return strcmp(a, b);
+}
+
+static int nhgl_cmp(struct nexthop_hold *nh1, struct nexthop_hold *nh2)
+{
+ int ret;
+
+ ret = sockunion_cmp(&nh1->addr, &nh2->addr);
+ if (ret)
+ return ret;
+
+ ret = nhgc_cmp_helper(nh1->intf, nh2->intf);
+ if (ret)
+ return ret;
+
+ return nhgc_cmp_helper(nh1->nhvrf_name, nh2->nhvrf_name);
+}
+
+static void nhgl_delete(struct nexthop_hold *nh)
+{
+ if (nh->intf)
+ XFREE(MTYPE_TMP, nh->intf);
+
+ if (nh->nhvrf_name)
+ XFREE(MTYPE_TMP, nh->nhvrf_name);
+
+ XFREE(MTYPE_TMP, nh);
+}
+
static struct nexthop_group_cmd *nhgc_get(const char *name)
{
struct nexthop_group_cmd *nhgc;
QOBJ_REG(nhgc, nexthop_group_cmd);
RB_INSERT(nhgc_entry_head, &nhgc_entries, nhgc);
+ nhgc->nhg_list = list_new();
+ nhgc->nhg_list->cmp = (int (*)(void *, void *))nhgl_cmp;
+ nhgc->nhg_list->del = (void (*)(void *))nhgl_delete;
+
if (nhg_hooks.new)
nhg_hooks.new(name);
}
nhg_hooks.delete(nhgc->name);
RB_REMOVE(nhgc_entry_head, &nhgc_entries, nhgc);
+
+ list_delete_and_null(&nhgc->nhg_list);
+
+ XFREE(MTYPE_TMP, nhgc);
}
DEFINE_QOBJ_TYPE(nexthop_group_cmd)
return CMD_SUCCESS;
}
-DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
- "[no] nexthop <A.B.C.D|X:X::X:X>$addr [INTERFACE]$intf [nexthop-vrf NAME$name]",
- NO_STR
- "Specify one of the nexthops in this ECMP group\n"
- "v4 Address\n"
- "v6 Address\n"
- "Interface to use\n"
- "If the nexthop is in a different vrf tell us\n"
- "The nexthop-vrf Name\n")
+static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc,
+ const char *nhvrf_name,
+ const union sockunion *addr,
+ const char *intf)
+{
+ struct nexthop_hold *nh;
+
+ nh = XCALLOC(MTYPE_TMP, sizeof(*nh));
+
+ if (nhvrf_name)
+ nh->nhvrf_name = XSTRDUP(MTYPE_TMP, nhvrf_name);
+ if (intf)
+ nh->intf = XSTRDUP(MTYPE_TMP, intf);
+
+ nh->addr = *addr;
+
+ listnode_add_sort(nhgc->nhg_list, nh);
+}
+
+static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc,
+ const char *nhvrf_name,
+ const union sockunion *addr,
+ const char *intf)
+{
+ struct nexthop_hold *nh;
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
+ if (nhgc_cmp_helper(nhvrf_name, nh->nhvrf_name) == 0 &&
+ sockunion_cmp(addr, &nh->addr) == 0 &&
+ nhgc_cmp_helper(intf, nh->intf) == 0)
+ break;
+ }
+
+ /*
+ * Something has gone seriously wrong, fail gracefully
+ */
+ if (!nh)
+ return;
+
+ list_delete_node(nhgc->nhg_list, node);
+
+ if (nh->nhvrf_name)
+ XFREE(MTYPE_TMP, nh->nhvrf_name);
+ if (nh->intf)
+ XFREE(MTYPE_TMP, nh->intf);
+
+ XFREE(MTYPE_TMP, nh);
+}
+
+static bool nexthop_group_parse_nexthop(struct nexthop *nhop,
+ const union sockunion *addr,
+ const char *intf, const char *name)
{
- VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
struct vrf *vrf;
- struct nexthop nhop;
- struct nexthop *nh;
+
+ memset(nhop, 0, sizeof(*nhop));
if (name)
vrf = vrf_lookup_by_name(name);
else
vrf = vrf_lookup_by_id(VRF_DEFAULT);
- if (!vrf) {
- vty_out(vty, "Specified: %s is non-existent\n", name);
- return CMD_WARNING;
- }
+ if (!vrf)
+ return false;
- memset(&nhop, 0, sizeof(nhop));
- nhop.vrf_id = vrf->vrf_id;
+ nhop->vrf_id = vrf->vrf_id;
if (addr->sa.sa_family == AF_INET) {
- nhop.gate.ipv4.s_addr = addr->sin.sin_addr.s_addr;
+ nhop->gate.ipv4.s_addr = addr->sin.sin_addr.s_addr;
if (intf) {
- nhop.type = NEXTHOP_TYPE_IPV4_IFINDEX;
- nhop.ifindex = ifname2ifindex(intf, vrf->vrf_id);
- if (nhop.ifindex == IFINDEX_INTERNAL) {
- vty_out(vty,
- "Specified Intf %s does not exist in vrf: %s\n",
- intf, vrf->name);
- return CMD_WARNING;
- }
+ nhop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
+ nhop->ifindex = ifname2ifindex(intf, vrf->vrf_id);
+ if (nhop->ifindex == IFINDEX_INTERNAL)
+ return false;
} else
- nhop.type = NEXTHOP_TYPE_IPV4;
+ nhop->type = NEXTHOP_TYPE_IPV4;
} else {
- memcpy(&nhop.gate.ipv6, &addr->sin6.sin6_addr, 16);
+ memcpy(&nhop->gate.ipv6, &addr->sin6.sin6_addr, 16);
if (intf) {
- nhop.type = NEXTHOP_TYPE_IPV6_IFINDEX;
- nhop.ifindex = ifname2ifindex(intf, vrf->vrf_id);
- if (nhop.ifindex == IFINDEX_INTERNAL) {
- vty_out(vty,
- "Specified Intf %s does not exist in vrf: %s\n",
- intf, vrf->name);
- return CMD_WARNING;
- }
+ nhop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
+ nhop->ifindex = ifname2ifindex(intf, vrf->vrf_id);
+ if (nhop->ifindex == IFINDEX_INTERNAL)
+ return false;
} else
- nhop.type = NEXTHOP_TYPE_IPV6;
+ nhop->type = NEXTHOP_TYPE_IPV6;
+ }
+
+ return true;
+}
+
+DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
+ "[no] nexthop <A.B.C.D|X:X::X:X>$addr [INTERFACE]$intf [nexthop-vrf NAME$name]",
+ NO_STR
+ "Specify one of the nexthops in this ECMP group\n"
+ "v4 Address\n"
+ "v6 Address\n"
+ "Interface to use\n"
+ "If the nexthop is in a different vrf tell us\n"
+ "The nexthop-vrf Name\n")
+{
+ VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
+ struct nexthop nhop;
+ struct nexthop *nh;
+ bool legal;
+
+ legal = nexthop_group_parse_nexthop(&nhop, addr, intf, name);
+
+ if (nhop.type == NEXTHOP_TYPE_IPV6
+ && IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) {
+ vty_out(vty,
+ "Specified a v6 LL with no interface, rejecting\n");
+ return CMD_WARNING_CONFIG_FAILED;
}
nh = nexthop_exists(&nhgc->nhg, &nhop);
if (no) {
+ nexthop_group_unsave_nhop(nhgc, name, addr, intf);
if (nh) {
nexthop_del(&nhgc->nhg, nh);
}
} else if (!nh) {
/* must be adding new nexthop since !no and !nexthop_exists */
- nh = nexthop_new();
+ if (legal) {
+ nh = nexthop_new();
+
+ memcpy(nh, &nhop, sizeof(nhop));
+ nexthop_add(&nhgc->nhg.nexthop, nh);
+ }
- memcpy(nh, &nhop, sizeof(nhop));
- nexthop_add(&nhgc->nhg.nexthop, nh);
+ nexthop_group_save_nhop(nhgc, name, addr, intf);
- if (nhg_hooks.add_nexthop)
+ if (legal && nhg_hooks.add_nexthop)
nhg_hooks.add_nexthop(nhgc, nh);
}
vty_out(vty, "\n");
}
+static void nexthop_group_write_nexthop_internal(struct vty *vty,
+ struct nexthop_hold *nh)
+{
+ char buf[100];
+
+ vty_out(vty, "nexthop ");
+
+ vty_out(vty, "%s", sockunion2str(&nh->addr, buf, sizeof(buf)));
+
+ if (nh->intf)
+ vty_out(vty, " %s", nh->intf);
+
+ if (nh->nhvrf_name)
+ vty_out(vty, " nexthop-vrf %s", nh->nhvrf_name);
+
+ vty_out(vty, "\n");
+}
+
static int nexthop_group_write(struct vty *vty)
{
struct nexthop_group_cmd *nhgc;
- struct nexthop *nh;
+ struct nexthop_hold *nh;
RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
+ struct listnode *node;
+
vty_out(vty, "nexthop-group %s\n", nhgc->name);
- for (nh = nhgc->nhg.nexthop; nh; nh = nh->next) {
+ for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
vty_out(vty, " ");
- nexthop_group_write_nexthop(vty, nh);
+ nexthop_group_write_nexthop_internal(vty, nh);
}
vty_out(vty, "!\n");
return 1;
}
+void nexthop_group_enable_vrf(struct vrf *vrf)
+{
+ struct nexthop_group_cmd *nhgc;
+ struct nexthop_hold *nhh;
+
+ RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
+ struct nexthop nhop;
+ struct nexthop *nh;
+
+ if (!nexthop_group_parse_nexthop(&nhop, &nhh->addr,
+ nhh->intf,
+ nhh->nhvrf_name))
+ continue;
+
+ nh = nexthop_exists(&nhgc->nhg, &nhop);
+
+ if (nh)
+ continue;
+
+ if (nhop.vrf_id != vrf->vrf_id)
+ continue;
+
+ nh = nexthop_new();
+
+ memcpy(nh, &nhop, sizeof(nhop));
+ nexthop_add(&nhgc->nhg.nexthop, nh);
+
+ if (nhg_hooks.add_nexthop)
+ nhg_hooks.add_nexthop(nhgc, nh);
+ }
+ }
+}
+
+void nexthop_group_disable_vrf(struct vrf *vrf)
+{
+ struct nexthop_group_cmd *nhgc;
+ struct nexthop_hold *nhh;
+
+ RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
+ struct nexthop nhop;
+ struct nexthop *nh;
+
+ if (!nexthop_group_parse_nexthop(&nhop, &nhh->addr,
+ nhh->intf,
+ nhh->nhvrf_name))
+ continue;
+
+ nh = nexthop_exists(&nhgc->nhg, &nhop);
+
+ if (!nh)
+ continue;
+
+ if (nh->vrf_id != vrf->vrf_id)
+ continue;
+
+ nexthop_del(&nhgc->nhg, nh);
+
+ if (nhg_hooks.del_nexthop)
+ nhg_hooks.del_nexthop(nhgc, nh);
+
+ nexthop_free(nh);
+ }
+ }
+}
+
+void nexthop_group_interface_state_change(struct interface *ifp,
+ ifindex_t oldifindex)
+{
+ struct nexthop_group_cmd *nhgc;
+ struct nexthop_hold *nhh;
+
+ RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
+ struct listnode *node;
+ struct nexthop *nh;
+
+ if (if_is_up(ifp)) {
+ for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
+ struct nexthop nhop;
+
+ if (!nexthop_group_parse_nexthop(
+ &nhop, &nhh->addr, nhh->intf,
+ nhh->nhvrf_name))
+ continue;
+
+ switch (nhop.type) {
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV6:
+ case NEXTHOP_TYPE_BLACKHOLE:
+ continue;
+ case NEXTHOP_TYPE_IFINDEX:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ break;
+ }
+ nh = nexthop_exists(&nhgc->nhg, &nhop);
+
+ if (nh)
+ continue;
+
+ if (ifp->ifindex != nhop.ifindex)
+ continue;
+
+ nh = nexthop_new();
+
+ memcpy(nh, &nhop, sizeof(nhop));
+ nexthop_add(&nhgc->nhg.nexthop, nh);
+
+ if (nhg_hooks.add_nexthop)
+ nhg_hooks.add_nexthop(nhgc, nh);
+ }
+ } else {
+ struct nexthop *next_nh;
+
+ for (nh = nhgc->nhg.nexthop; nh; nh = next_nh) {
+ next_nh = nh->next;
+ switch (nh->type) {
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV6:
+ case NEXTHOP_TYPE_BLACKHOLE:
+ continue;
+ case NEXTHOP_TYPE_IFINDEX:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ break;
+ }
+
+ if (oldifindex != nh->ifindex)
+ continue;
+
+ nexthop_del(&nhgc->nhg, nh);
+
+ if (nhg_hooks.del_nexthop)
+ nhg_hooks.del_nexthop(nhgc, nh);
+
+ nexthop_free(nh);
+ }
+ }
+ }
+}
+
void nexthop_group_init(void (*new)(const char *name),
void (*add_nexthop)(const struct nexthop_group_cmd *nhg,
const struct nexthop *nhop),
(nhop); \
(nhop) = nexthop_next(nhop)
+
+struct nexthop_hold {
+ char *nhvrf_name;
+ union sockunion addr;
+ char *intf;
+};
+
struct nexthop_group_cmd {
RB_ENTRY(nexthop_group_cmd) nhgc_entry;
struct nexthop_group nhg;
+ struct list *nhg_list;
+
QOBJ_FIELDS
};
RB_HEAD(nhgc_entry_head, nexthp_group_cmd);
const struct nexthop *nhop),
void (*delete)(const char *name));
+void nexthop_group_enable_vrf(struct vrf *vrf);
+void nexthop_group_disable_vrf(struct vrf *vrf);
+void nexthop_group_interface_state_change(struct interface *ifp,
+ ifindex_t oldifindex);
+
extern struct nexthop *nexthop_exists(struct nexthop_group *nhg,
struct nexthop *nh);
#include "command.h"
#include "ns.h"
#include "privs.h"
+#include "nexthop_group.h"
/* default VRF ID value used when VRF backend is not NETNS */
#define VRF_DEFAULT_INTERNAL 0
if (vrf_master.vrf_enable_hook)
(*vrf_master.vrf_enable_hook)(vrf);
+ /*
+ * If we have any nexthop group entries that
+ * are awaiting vrf initialization then
+ * let's let people know about it
+ */
+ nexthop_group_enable_vrf(vrf);
+
return 1;
}
#include "mpls.h"
#include "sockopt.h"
#include "pbr.h"
+#include "nexthop_group.h"
DEFINE_MTYPE_STATIC(LIB, ZCLIENT, "Zclient")
DEFINE_MTYPE_STATIC(LIB, REDIST_INST, "Redistribution instance IDs")
void zebra_interface_if_set_value(struct stream *s, struct interface *ifp)
{
uint8_t link_params_status = 0;
+ ifindex_t old_ifindex;
+ old_ifindex = ifp->ifindex;
/* Read interface's index. */
if_set_index(ifp, stream_getl(s));
ifp->status = stream_getc(s);
struct if_link_params *iflp = if_link_params_get(ifp);
link_params_set_value(s, iflp);
}
+
+ nexthop_group_interface_state_change(ifp, old_ifindex);
}
size_t zebra_interface_link_params_write(struct stream *s,
"debug pbr events",
};
-/*
- * Set or unset flags on all debugs for pbrd.
- *
- * flags
- * The flags to set
- *
- * set
- * Whether to set or unset the specified flags
- */
-static void pbr_debug_set_all(uint32_t flags, bool set)
+void pbr_debug_set_all(uint32_t flags, bool set)
{
for (unsigned int i = 0; i < array_size(pbr_debugs); i++) {
DEBUG_FLAGS_SET(pbr_debugs[i], flags, set);
}
}
-/*
- * Check flags on all debugs for pbrd.
- *
- * flags
- * The flags to set
- *
- * Returns:
- * The subset of the given flags that were set in all pbrd debugs
- */
-static uint32_t pbr_debug_check_all(uint32_t flags)
-{
- uint32_t mode = DEBUG_MODE_ALL;
-
- for (unsigned int i = 0; i < array_size(pbr_debugs); i++)
- mode &= DEBUG_MODE_CHECK(pbr_debugs[i], flags);
- return mode;
-}
-
-static int pbr_debug_config_write_helper(struct vty *vty, bool config)
+int pbr_debug_config_write_helper(struct vty *vty, bool config)
{
uint32_t mode = DEBUG_MODE_ALL;
if (config)
mode = DEBUG_MODE_CONF;
- if (pbr_debug_check_all(DEBUG_MODE_CONF) == mode) {
- vty_out(vty, "debug pbr\n");
- return 0;
- }
-
for (unsigned int i = 0; i < array_size(pbr_debugs); i++)
if (DEBUG_MODE_CHECK(pbr_debugs[i], mode))
vty_out(vty, "%s\n", pbr_debugs_conflines[i]);
return pbr_debug_config_write_helper(vty, true);
}
-/* PBR debugging CLI ------------------------------------------------------- */
-/* clang-format off */
-
-DEFPY(debug_pbr,
- debug_pbr_cmd,
- "[no] debug pbr [{map$map|zebra$zebra|nht$nht|events$events}]",
- NO_STR
- DEBUG_STR
- "Policy Based Routing\n"
- "Policy maps\n"
- "PBRD <-> Zebra communications\n"
- "Nexthop tracking\n"
- "Events\n")
-{
- uint32_t mode = DEBUG_NODE2MODE(vty->node);
-
- if (map)
- DEBUG_MODE_SET(&pbr_dbg_map, mode, !no);
- if (zebra)
- DEBUG_MODE_SET(&pbr_dbg_zebra, mode, !no);
- if (nht)
- DEBUG_MODE_SET(&pbr_dbg_nht, mode, !no);
- if (events)
- DEBUG_MODE_SET(&pbr_dbg_event, mode, !no);
-
- /* no specific debug --> act on all of them */
- if (strmatch(argv[argc - 1]->text, "pbr"))
- pbr_debug_set_all(mode, !no);
-
- return CMD_SUCCESS;
-}
-
-DEFUN_NOSH(show_debugging_pbr,
- show_debugging_pbr_cmd,
- "show debugging [pbr]",
- SHOW_STR
- DEBUG_STR
- "Policy Based Routing\n")
-{
- vty_out(vty, "PBR debugging status:\n");
-
- pbr_debug_config_write_helper(vty, false);
-
- return CMD_SUCCESS;
-}
-
-/* clang-format on */
-/* ------------------------------------------------------------------------- */
-
-static struct cmd_node debug_node = {DEBUG_NODE, "", 1};
-
struct debug_callbacks pbr_dbg_cbs = {.debug_set_all = pbr_debug_set_all};
void pbr_debug_init(void)
{
debug_init(&pbr_dbg_cbs);
}
-
-void pbr_debug_init_vty(void)
-{
- install_node(&debug_node, pbr_debug_config_write);
-
- install_element(VIEW_NODE, &debug_pbr_cmd);
- install_element(CONFIG_NODE, &debug_pbr_cmd);
-
- install_element(VIEW_NODE, &show_debugging_pbr_cmd);
-}
void pbr_debug_init(void);
/*
- * Install PBR debugging VTY commands.
+ * Set or unset flags on all debugs for pbrd.
+ *
+ * flags
+ * The flags to set
+ *
+ * set
+ * Whether to set or unset the specified flags
+ */
+void pbr_debug_set_all(uint32_t flags, bool set);
+
+/*
+ * Config write helper.
+ *
+ * vty
+ * Vty to write to
+ *
+ * config
+ * Whether we are writing to show run or saving config file
+ *
+ * Returns:
+ * 0 for convenience
*/
-void pbr_debug_init_vty(void);
+int pbr_debug_config_write_helper(struct vty *vty, bool config);
/*
* Print PBR debugging configuration.
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 && !pbrm->installed)
+ if (pbrm->valid)
pbr_map_install(pbrm);
}
if (pbrm->seqnumbers->count == 0) {
RB_REMOVE(pbr_map_entry_head, &pbr_maps, pbrm);
+
+ bf_free(pbrm->ifi_bitfield);
XFREE(MTYPE_PBR_MAP, pbrm);
}
}
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_lookup_unique(uint32_t unique, ifindex_t ifindex,
+ struct pbr_map_interface **ppmi)
{
struct pbr_map_sequence *pbrms;
struct listnode *snode, *inode;
if (pmi->ifp->ifindex != ifindex)
continue;
+ if (ppmi)
+ *ppmi = pmi;
+
for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, snode,
pbrms)) {
DEBUGD(&pbr_dbg_map, "%s: Comparing %u to %u",
RB_INSERT(pbr_map_entry_head, &pbr_maps, pbrm);
+ bf_init(pbrm->ifi_bitfield, 64);
pbr_map_add_interfaces(pbrm);
}
QOBJ_REG(pbrms, pbr_map_sequence);
listnode_add_sort(pbrm->seqnumbers, pbrms);
-
- pbrm->installed = false;
}
return pbrms;
listnode_delete(pbrm->incoming, pmi);
pmi->pbrm = NULL;
+
+ bf_release_index(pbrm->ifi_bitfield, pmi->install_bit);
XFREE(MTYPE_PBR_MAP_INTERFACE, pmi);
}
pbrms->seqno, pbrms->reason);
}
- for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi))
+ for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) {
pbr_send_pbr_map(pbrms, pmi, install);
+ }
}
void pbr_map_install(struct pbr_map *pbrm)
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)
#ifndef __PBR_MAP_H__
#define __PBR_MAP_H__
+#include <bitfield.h>
+
struct pbr_map {
/*
* RB Tree of the pbr_maps
*/
struct list *incoming;
+ bitfield_t ifi_bitfield;
/*
* If valid is true we think the pbr_map is valid,
* If false, look in individual pbrms to see
* what we think is the invalid reason
*/
bool valid;
-
- bool installed;
};
RB_HEAD(pbr_map_entry_head, pbr_map);
RB_PROTOTYPE(pbr_map_entry_head, pbr_map, pbr_map_entry, pbr_map_compare)
struct pbr_map_interface {
+ uint32_t install_bit;
+
struct interface *ifp;
struct pbr_map *pbrm;
/*
* Are we installed
*/
- bool installed;
+ uint64_t installed;
/*
* A reason of 0 means we think the pbr_map_sequence is good to go
extern struct pbr_map_entry_head pbr_maps;
extern struct pbr_map_sequence *pbrms_get(const char *name, uint32_t seqno);
-extern struct pbr_map_sequence *pbrms_lookup_unique(uint32_t unique,
- ifindex_t ifindex);
+extern struct pbr_map_sequence *
+pbrms_lookup_unique(uint32_t unique, ifindex_t ifindex,
+ struct pbr_map_interface **ppmi);
extern struct pbr_map *pbrm_find(const char *name);
extern void pbr_map_delete(struct pbr_map_sequence *pbrms);
if (pnhgc->table_id == *table_id) {
DEBUGD(&pbr_dbg_nht, "%s: Table ID (%u) matches %s",
__PRETTY_FUNCTION__, *table_id, pnhgc->name);
- pnhgc->installed = true;
- pbr_map_schedule_policy_from_nhg(pnhgc->name);
+
+ /*
+ * If the table has been re-handled by zebra
+ * and we are already installed no need to do
+ * anything here.
+ */
+ if (!pnhgc->installed) {
+ pnhgc->installed = true;
+ pbr_map_schedule_policy_from_nhg(pnhgc->name);
+ }
}
}
install_afi = pbr_nht_which_afi(nhg, nh_afi);
- pnhgc->installed = false;
-
route_add(pnhgc, nhg, install_afi);
}
pbrm->valid = false;
pbrms->nhs_installed = false;
- pbrms->installed = false;
pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS;
memset(&find, 0, sizeof(find));
intf, vrf->name);
return CMD_WARNING_CONFIG_FAILED;
}
- } else
+ } else {
+ if (IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) {
+ vty_out(vty,
+ "Specified a v6 LL with no interface, rejecting\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
nhop.type = NEXTHOP_TYPE_IPV6;
+ }
}
if (pbrms->nhg)
pbr_map_reason_string(pbrms->reason, rbuf,
sizeof(rbuf));
vty_out(vty,
- " Seq: %u rule: %u Installed: %d(%u) Reason: %s\n",
+ " Seq: %u rule: %u Installed: %" PRIu64 "(%u) Reason: %s\n",
pbrms->seqno, pbrms->ruleno, pbrms->installed,
pbrms->unique, pbrms->reason ? rbuf : "Valid");
return CMD_SUCCESS;
}
+/* PBR debugging CLI ------------------------------------------------------- */
+/* clang-format off */
+
+static struct cmd_node debug_node = {DEBUG_NODE, "", 1};
+
+DEFPY(debug_pbr,
+ debug_pbr_cmd,
+ "[no] debug pbr [{map$map|zebra$zebra|nht$nht|events$events}]",
+ NO_STR
+ DEBUG_STR
+ "Policy Based Routing\n"
+ "Policy maps\n"
+ "PBRD <-> Zebra communications\n"
+ "Nexthop tracking\n"
+ "Events\n")
+{
+ uint32_t mode = DEBUG_NODE2MODE(vty->node);
+
+ if (map)
+ DEBUG_MODE_SET(&pbr_dbg_map, mode, !no);
+ if (zebra)
+ DEBUG_MODE_SET(&pbr_dbg_zebra, mode, !no);
+ if (nht)
+ DEBUG_MODE_SET(&pbr_dbg_nht, mode, !no);
+ if (events)
+ DEBUG_MODE_SET(&pbr_dbg_event, mode, !no);
+
+ /* no specific debug --> act on all of them */
+ if (strmatch(argv[argc - 1]->text, "pbr"))
+ pbr_debug_set_all(mode, !no);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_NOSH(show_debugging_pbr,
+ show_debugging_pbr_cmd,
+ "show debugging [pbr]",
+ SHOW_STR
+ DEBUG_STR
+ "Policy Based Routing\n")
+{
+ vty_out(vty, "PBR debugging status:\n");
+
+ pbr_debug_config_write_helper(vty, false);
+
+ return CMD_SUCCESS;
+}
+
+/* clang-format on */
+/* ------------------------------------------------------------------------- */
+
+
static struct cmd_node interface_node = {
INTERFACE_NODE, "%s(config-if)# ", 1 /* vtysh ? yes */
};
install_node(&pbr_map_node,
pbr_vty_map_config_write);
+ /* debug */
+ install_node(&debug_node, pbr_debug_config_write);
+ install_element(VIEW_NODE, &debug_pbr_cmd);
+ install_element(CONFIG_NODE, &debug_pbr_cmd);
+ install_element(VIEW_NODE, &show_debugging_pbr_cmd);
+
install_default(PBRMAP_NODE);
install_element(CONFIG_NODE, &pbr_map_cmd);
install_element(VIEW_NODE, &show_pbr_map_cmd);
install_element(VIEW_NODE, &show_pbr_interface_cmd);
install_element(VIEW_NODE, &show_pbr_nexthop_group_cmd);
-
- pbr_debug_init_vty();
}
/* Zebra structure to hold current status. */
struct zclient *zclient;
-static struct interface *zebra_interface_if_lookup(struct stream *s)
-{
- char ifname_tmp[INTERFACE_NAMSIZ];
-
- /* Read interface name. */
- stream_get(ifname_tmp, s, INTERFACE_NAMSIZ);
-
- /* And look it up. */
- return if_lookup_by_name(ifname_tmp, VRF_DEFAULT);
-}
-
struct pbr_interface *pbr_if_new(struct interface *ifp)
{
struct pbr_interface *pbr_ifp;
zebra_size_t length, vrf_id_t vrf_id)
{
- zebra_interface_if_lookup(zclient->ibuf);
+ zebra_interface_state_read(zclient->ibuf, vrf_id);
return 0;
}
uint32_t seqno, priority, unique;
enum zapi_rule_notify_owner note;
struct pbr_map_sequence *pbrms;
+ struct pbr_map_interface *pmi;
ifindex_t ifi;
+ uint64_t installed;
if (!zapi_rule_notify_decode(zclient->ibuf, &seqno, &priority, &unique,
&ifi, ¬e))
return -1;
- pbrms = pbrms_lookup_unique(unique, ifi);
+ pmi = NULL;
+ pbrms = pbrms_lookup_unique(unique, ifi, &pmi);
if (!pbrms) {
DEBUGD(&pbr_dbg_zebra,
"%s: Failure to lookup pbrms based upon %u",
return 0;
}
+ installed = 1 << pmi->install_bit;
+
switch (note) {
case ZAPI_RULE_FAIL_INSTALL:
DEBUGD(&pbr_dbg_zebra, "%s: Recieved RULE_FAIL_INSTALL",
__PRETTY_FUNCTION__);
- pbrms->installed = false;
+ pbrms->installed &= ~installed;
break;
case ZAPI_RULE_INSTALLED:
- pbrms->installed = true;
+ pbrms->installed |= installed;
DEBUGD(&pbr_dbg_zebra, "%s: Recived RULE_INSTALLED",
__PRETTY_FUNCTION__);
break;
case ZAPI_RULE_REMOVED:
+ pbrms->installed &= ~installed;
DEBUGD(&pbr_dbg_zebra, "%s: Received RULE REMOVED",
__PRETTY_FUNCTION__);
break;
{
struct pbr_map *pbrm = pbrms->parent;
struct stream *s;
+ uint64_t is_installed = 1 << pmi->install_bit;
- DEBUGD(&pbr_dbg_zebra, "%s: for %s %d", __PRETTY_FUNCTION__, pbrm->name,
- install);
+ is_installed &= pbrms->installed;
+
+ DEBUGD(&pbr_dbg_zebra, "%s: for %s %d(%" PRIu64 ")",
+ __PRETTY_FUNCTION__, pbrm->name, install, is_installed);
+
+ /*
+ * If we are installed and asked to do so again
+ * just return. If we are not installed and asked
+ * and asked to delete just return;
+ */
+ if (install && is_installed)
+ return;
+
+ if (!install && !is_installed)
+ return;
s = zclient->obuf;
stream_reset(s);
struct pbr_rule_unique_lookup {
struct zebra_pbr_rule *rule;
uint32_t unique;
+ struct interface *ifp;
};
static int pbr_rule_lookup_unique_walker(struct hash_backet *b, void *data)
struct pbr_rule_unique_lookup *pul = data;
struct zebra_pbr_rule *rule = b->data;
- if (pul->unique == rule->rule.unique) {
+ if (pul->unique == rule->rule.unique && pul->ifp == rule->ifp) {
pul->rule = rule;
return HASHWALK_ABORT;
}
}
static struct zebra_pbr_rule *pbr_rule_lookup_unique(struct zebra_ns *zns,
- uint32_t unique)
+ uint32_t unique,
+ struct interface *ifp)
{
struct pbr_rule_unique_lookup pul;
pul.unique = unique;
+ pul.ifp = ifp;
pul.rule = NULL;
hash_walk(zns->rules_hash, &pbr_rule_lookup_unique_walker, &pul);
void zebra_pbr_add_rule(struct zebra_ns *zns, struct zebra_pbr_rule *rule)
{
struct zebra_pbr_rule *unique =
- pbr_rule_lookup_unique(zns, rule->rule.unique);
+ pbr_rule_lookup_unique(zns, rule->rule.unique, rule->ifp);
(void)hash_get(zns->rules_hash, rule, pbr_rule_alloc_intern);
kernel_add_pbr_rule(rule);
zsend_rule_notify_owner(rule, ZAPI_RULE_FAIL_INSTALL);
break;
case SOUTHBOUND_DELETE_SUCCESS:
+ zsend_rule_notify_owner(rule, ZAPI_RULE_REMOVED);
break;
case SOUTHBOUND_DELETE_FAILURE:
+ zsend_rule_notify_owner(rule, ZAPI_RULE_REMOVED);
break;
}
}
* just rethink it. Yes this is a hammer, but
* a small one
*/
- if (o_re)
+ if (o_re) {
+ SET_FLAG(o_re->status, ROUTE_ENTRY_CHANGED);
rib_queue_add(o_rn);
+ }
}
}
}