Merge pull request #1903 from donaldsharp/PBRD

Pbrd
This commit is contained in:
Russ White 2018-04-11 09:06:45 -04:00 committed by GitHub
commit 058054cac1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 3786 additions and 31 deletions

View File

@ -56,6 +56,7 @@ include babeld/subdir.am
include eigrpd/subdir.am
include sharpd/subdir.am
include pimd/subdir.am
include pbrd/subdir.am
SUBDIRS = . @LIBRFP@ @RFPTEST@ \
@BGPD@ \

View File

@ -1377,6 +1377,7 @@ AM_CONDITIONAL(BABELD, test "${enable_babeld}" != "no")
AM_CONDITIONAL(OSPF6D, test "${enable_ospf6d}" != "no")
AM_CONDITIONAL(ISISD, test "${enable_isisd}" != "no")
AM_CONDITIONAL(PIMD, test "${enable_pimd}" != "no")
AM_CONDITIONAL(PBRD, test "${enable_pbrd}" != "no")
if test "${enable_bgp_announce}" = "no";then
AC_DEFINE(DISABLE_BGP_ANNOUNCE,1,Disable BGP installation to zebra)

View File

@ -26,6 +26,10 @@ man_MANS += $(MANPAGE_BUILDDIR)/pimd.8
man_MANS += $(MANPAGE_BUILDDIR)/mtracebis.8
endif
if PBRD
man_MANS += $(MANPAGE_BUILDDIR)/pbrd.8
endif
if BGPD
man_MANS += $(MANPAGE_BUILDDIR)/bgpd.8
endif
@ -140,6 +144,7 @@ EXTRA_DIST = frr-sphinx.mk \
manpages/ospfd.rst \
manpages/pimd.rst \
manpages/ripd.rst \
manpages/pbrd.rst \
manpages/ripngd.rst \
manpages/vtysh.rst \
manpages/watchfrr.rst \
@ -202,6 +207,7 @@ EXTRA_DIST = frr-sphinx.mk \
user/overview.rst \
user/pim.rst \
user/ripd.rst \
user/pbr.rst \
user/ripngd.rst \
user/routemap.rst \
user/routeserver.rst \

View File

@ -122,6 +122,7 @@ These following options control the daemon's VTY (interactive command line) inte
pimd 2611
ldpd 2612
eigrpd 2613
pbrd 2615
Port 2607 is used for ospfd's Opaque LSA API, while port 2600 is used for the (insecure) TCP-ZEBRA interface.

View File

@ -318,6 +318,7 @@ man_pages = [
('ldpd', 'ldpd', fwfrr.format("an LDP "), [], 8),
('nhrpd', 'nhrpd', fwfrr.format("a Next Hop Routing Protocol "), [], 8),
('pimd', 'pimd', fwfrr.format("a PIM "), [], 8),
('pbrd', 'pbrd', fwfrr.format("a PBR "), [], 8),
('mtracebis', 'mtracebis', "a multicast trace client", [], 8),
('ripd', 'ripd', fwfrr.format("a RIP "), [], 8),
('ripngd', 'ripngd', fwfrr.format("a RIPNG "), [], 8),

View File

@ -1,3 +1,3 @@
.. |synopsis-options| replace:: [-d|-t|-dt] [-C] [-f config-file] [-i pid-file] [-z zclient-path] [-u user] [-g group] [-A vty-addr] [-P vty-port] [-M module[:options]] [-N pathspace] [--vty_socket vty-path] [--moduledir module-path]
.. |synopsis-options-hv| replace:: [-h] [-v]
.. |seealso-programs| replace:: zebra(8), vtysh(1), ripd(8), ripngd(8), ospfd(8), ospf6d(8), bgpd(8), isisd(8), babeld(8), nhrpd(8), pimd(8), ldpd(8), eigrpd(8), mtracebis(8)
.. |seealso-programs| replace:: zebra(8), vtysh(1), ripd(8), ripngd(8), ospfd(8), ospf6d(8), bgpd(8), isisd(8), babeld(8), nhrpd(8), pimd(8), pbrd(8), ldpd(8), eigrpd(8), mtracebis(8)

View File

@ -18,6 +18,7 @@ Welcome to FRR's documentation!
ospfclient
ospfd
pimd
pbrd
mtracebis
ripd
ripngd

38
doc/manpages/pbrd.rst Normal file
View File

@ -0,0 +1,38 @@
****
PBRD
****
.. include:: defines.rst
.. |DAEMON| replace:: pbrd
SYNOPSIS
========
|DAEMON| |synopsis-options-hv|
|DAEMON| |synopsis-options|
DESCRIPTION
===========
|DAEMON| is a routing component that works with the FRRouting engine.
OPTIONS
=======
OPTIONS available for the |DAEMON| command:
.. include:: common-options.rst
FILES
=====
|INSTALL_PREFIX_SBIN|/|DAEMON|
The default location of the |DAEMON| binary.
|INSTALL_PREFIX_ETC|/|DAEMON|.conf
The default location of the |DAEMON| config file.
$(PWD)/|DAEMON|.log
If the |DAEMON| process is configured to output logs to a file, then you will find this file in the directory where you started |DAEMON|.
.. include:: epilogue.rst

View File

@ -22,6 +22,7 @@ Welcome to FRR's documentation!
ospfd
ospf6d
pim
pbr
ripd
ripngd
vnc

123
doc/user/pbr.rst Normal file
View File

@ -0,0 +1,123 @@
.. _pbr:
***
PBR
***
:abbr:`PBR` is Policy Based Routing. This implementation supports a very simple
interface to allow admins to influence routing on their router. At this time
you can only match on destination and source prefixes for an incoming interface.
At this point in time, this implementation will only work on Linux.
.. _starting-pbr:
Starting PBR
============
Default configuration file for *pbrd* is :file:`pbrd.conf`. The typical
location of :file:`pbrd.conf` is |INSTALL_PREFIX_ETC|/pbrd.conf.
If the user is using integrated config, then :file:`pbrd.conf` need not be
present and the :file:`frr.conf` is read instead.
.. program:: pbrd
:abbr:`PBR` supports all the common FRR daemon start options which are
documented elsewhere.
.. _nexthop-groups:
Nexthop Groups
==============
Nexthop groups are a way to encapsulate ECMP information together. It's a
listing of ECMP nexthops used to forward packets for when a pbr-map is matched.
.. index:: nexthop-group
.. clicmd:: nexthop-group NAME
Create a nexthop-group with an associated NAME. This will put you into a
sub-mode where you can specify individual nexthops. To exit this mode type
exit or end as per normal conventions for leaving a sub-mode.
.. clicmd:: nexthop [A.B.C.D|X:X::X:XX] [interface] [nexthop-vrf NAME]
Create a v4 or v6 nexthop. All normal rules for creating nexthops that you
are used to are allowed here. The syntax was intentionally kept the same as
creating nexthops as you would for static routes.
.. _pbr-maps:
PBR Maps
========
PBR maps are a way to group policies that we would like to apply
to individual interfaces. These policies when applied are matched
against incoming packets. If matched the nexthop-group or nexthop
is used to forward the packets to the end destination
.. index:: pbr-map
.. clicmd:: pbr-map NAME seq (1-1000)
Create a pbr-map with NAME and sequence number specified. This command puts
you into a new submode for pbr-map specification. To exit this mode type
exit or end as per normal conventions for leaving a sub-mode.
.. index:: match
.. clicmd:: match src-ip PREFIX
When a incoming packet matches the source prefix specified, take the packet
and forward according to the nexthops specified. This command accepts both
v4 and v6 prefixes. This command is used in conjunction of the
:clicmd:`match dst-ip PREFIX` command for matching.
.. clicmd:: match dst-ip PREFIX
When a incoming packet matches the destination prefix specified, take the
packet and forward according to the nexthops specified. This command accepts
both v4 and v6 prefixes. This command is used in conjuction of the
:clicmd:`match src-ip PREFIX` command for matching.
.. clicmd:: set nexthop-group NAME
Use the nexthop-group NAME as the place to forward packets when the match
commands have matched a packet.
.. clicmd:: set nexthop [A.B.C.D|X:X::X:XX] [interface] [nexthop-vrf NAME]
Use this individual nexthop as the place to forward packets when the match
commands have matched a packet.
.. _pbr-policy:
PBR Policy
==========
After you have specified a PBR map, in order for it to be turned on, you must
apply the PBR map to an interface. This policy application to an interface
causes the policy to be installed into the kernel.
.. index:: pbr-policy
.. clicmd:: pbr-policy NAME
This command is available under interface sub-mode. This turns
on the PBR map NAME and allows it to work properly.
.. _pbr-details:
PBR Details
===========
Under the covers a PBR map is translated into two separate constructs in the
Linux kernel.
.. index:: PBR Rules
The PBR map specified creates a `ip rule ...` that is inserted into the Linux
kernel that points to a table to use for forwarding once the rule matches.
.. index:: PBR Tables
The creation of a nexthop or nexthop-group is translated to a default route in a
table with the nexthops specified as the nexthops for the default route.

View File

@ -105,6 +105,7 @@ const char *node_names[] = {
"as list", // AS_LIST_NODE,
"community list", // COMMUNITY_LIST_NODE,
"routemap", // RMAP_NODE,
"pbr-map", // PBRMAP_NODE,
"smux", // SMUX_NODE,
"dump", // DUMP_NODE,
"forwarding", // FORWARDING_NODE,
@ -1312,6 +1313,7 @@ void cmd_exit(struct vty *vty)
case ISIS_NODE:
case KEYCHAIN_NODE:
case RMAP_NODE:
case PBRMAP_NODE:
case VTY_NODE:
vty->node = CONFIG_NODE;
break;
@ -1409,6 +1411,7 @@ DEFUN (config_end,
case BGP_EVPN_VNI_NODE:
case BGP_IPV6L_NODE:
case RMAP_NODE:
case PBRMAP_NODE:
case OSPF_NODE:
case OSPF6_NODE:
case LDP_NODE:

View File

@ -128,6 +128,7 @@ enum node_type {
AS_LIST_NODE, /* AS list node. */
COMMUNITY_LIST_NODE, /* Community list node. */
RMAP_NODE, /* Route map node. */
PBRMAP_NODE, /* PBR map node. */
SMUX_NODE, /* SNMP configuration node. */
DUMP_NODE, /* Packet dump node. */
FORWARDING_NODE, /* IP forwarding node. */

View File

@ -31,6 +31,7 @@
#include "prefix.h"
#include "nexthop.h"
#include "mpls.h"
#include "jhash.h"
DEFINE_MTYPE_STATIC(LIB, NEXTHOP, "Nexthop")
DEFINE_MTYPE_STATIC(LIB, NH_LABEL, "Nexthop label")
@ -240,7 +241,7 @@ void nexthop_del_labels(struct nexthop *nexthop)
}
}
const char *nexthop2str(struct nexthop *nexthop, char *str, int size)
const char *nexthop2str(const struct nexthop *nexthop, char *str, int size)
{
switch (nexthop->type) {
case NEXTHOP_TYPE_IFINDEX:
@ -310,3 +311,15 @@ unsigned int nexthop_level(struct nexthop *nexthop)
return rv;
}
uint32_t nexthop_hash(struct nexthop *nexthop)
{
uint32_t key;
key = jhash_1word(nexthop->vrf_id, 0x45afe398);
key = jhash_1word(nexthop->ifindex, key);
key = jhash_1word(nexthop->type, key);
key = jhash(&nexthop->gate, sizeof(union g_addr), key);
return key;
}

View File

@ -118,6 +118,23 @@ void nexthop_add_labels(struct nexthop *, enum lsp_types_t, uint8_t,
mpls_label_t *);
void nexthop_del_labels(struct nexthop *);
/*
* Hash a nexthop. Suitable for use with hash tables.
*
* This function uses the following values when computing the hash:
* - vrf_id
* - ifindex
* - type
* - gate
*
* nexthop
* The nexthop to hash
*
* Returns:
* 32-bit hash of nexthop
*/
uint32_t nexthop_hash(struct nexthop *nexthop);
extern bool nexthop_same(const struct nexthop *nh1, const struct nexthop *nh2);
extern const char *nexthop_type_to_str(enum nexthop_types_t nh_type);
@ -126,7 +143,7 @@ extern int nexthop_same_no_recurse(const struct nexthop *next1,
extern int nexthop_labels_match(struct nexthop *nh1, struct nexthop *nh2);
extern int nexthop_same_firsthop(struct nexthop *next1, struct nexthop *next2);
extern const char *nexthop2str(struct nexthop *nexthop, char *str, int size);
extern const char *nexthop2str(const struct nexthop *nexthop, char *str, int size);
extern struct nexthop *nexthop_next(struct nexthop *nexthop);
extern unsigned int nexthop_level(struct nexthop *nexthop);
#endif /*_LIB_NEXTHOP_H */

View File

@ -320,7 +320,7 @@ void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh)
char buf[100];
struct vrf *vrf;
vty_out(vty, " nexthop ");
vty_out(vty, "nexthop ");
switch (nh->type) {
case NEXTHOP_TYPE_IFINDEX:
@ -361,8 +361,10 @@ static int nexthop_group_write(struct vty *vty)
RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
vty_out(vty, "nexthop-group %s\n", nhgc->name);
for (nh = nhgc->nhg.nexthop; nh; nh = nh->next)
for (nh = nhgc->nhg.nexthop; nh; nh = nh->next) {
vty_out(vty, " ");
nexthop_group_write_nexthop(vty, nh);
}
vty_out(vty, "!\n");
}

View File

@ -78,6 +78,7 @@ ZEBRA_ROUTE_BGP_DIRECT, bgp-direct, NULL, 'b', 0, 0, "BGP-Direct"
ZEBRA_ROUTE_BGP_DIRECT_EXT, bgp-direct-to-nve-groups, NULL, 'e', 0, 0, "BGP2VNC"
ZEBRA_ROUTE_BABEL, babel, babeld, 'A', 1, 1, "Babel"
ZEBRA_ROUTE_SHARP, sharp, sharpd, 'D', 1, 1, "SHARP"
ZEBRA_ROUTE_PBR, pbr, pbrd, 'F', 1, 1, "PBR"
ZEBRA_ROUTE_ALL, wildcard, none, '-', 0, 0, "-"
@ -103,3 +104,4 @@ ZEBRA_ROUTE_LDP, "Label Distribution Protocol (LDP)"
ZEBRA_ROUTE_VNC_DIRECT, "VNC direct (not via zebra) routes"
ZEBRA_ROUTE_BABEL, "Babel routing protocol (Babel)"
ZEBRA_ROUTE_SHARP, "Super Happy Advanced Routing Protocol (sharpd)"
ZEBRA_ROUTE_PBR, "Policy Based Routing (PBR)"

View File

@ -719,6 +719,7 @@ static void vty_end_config(struct vty *vty)
case BGP_EVPN_NODE:
case BGP_IPV6L_NODE:
case RMAP_NODE:
case PBRMAP_NODE:
case OSPF_NODE:
case OSPF6_NODE:
case LDP_NODE:
@ -1115,6 +1116,7 @@ static void vty_stop_input(struct vty *vty)
case EIGRP_NODE:
case BGP_NODE:
case RMAP_NODE:
case PBRMAP_NODE:
case OSPF_NODE:
case OSPF6_NODE:
case LDP_NODE:

15
pbrd/.gitignore vendored Normal file
View File

@ -0,0 +1,15 @@
!Makefile
Makefile.in
libpbr.a
pbrd
tags
TAGS
.deps
*.o
*.lo
*.la
*.libs
.arch-inventory
.arch-ids
*~
*.loT

10
pbrd/Makefile Normal file
View File

@ -0,0 +1,10 @@
all: ALWAYS
@$(MAKE) -s -C .. pbrd/pbrd
%: ALWAYS
@$(MAKE) -s -C .. pbrd/$@
Makefile:
#nothing
ALWAYS:
.PHONY: ALWAYS makefiles
.SUFFIXES:

173
pbrd/pbr_debug.c Normal file
View File

@ -0,0 +1,173 @@
/*
* PBR - debugging
* Copyright (C) 2018 Cumulus Networks, Inc.
* Quentin Young
*
* This program 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 of the License, or (at your option)
* any later version.
*
* This program 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 "debug.h"
#include "command.h"
#include "vector.h"
#ifndef VTYSH_EXTRACT_PL
#include "pbrd/pbr_debug_clippy.c"
#endif
#include "pbrd/pbr_debug.h"
struct debug pbr_dbg_map = {0, "PBR map"};
struct debug pbr_dbg_zebra = {0, "PBR Zebra communications"};
struct debug pbr_dbg_nht = {0, "PBR nexthop tracking"};
struct debug pbr_dbg_event = {0, "PBR events"};
struct debug *pbr_debugs[] = {&pbr_dbg_map, &pbr_dbg_zebra, &pbr_dbg_nht,
&pbr_dbg_event};
const char *pbr_debugs_conflines[] = {
"debug pbr map",
"debug pbr zebra",
"debug pbr nht",
"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)
{
for (unsigned int i = 0; i < array_size(pbr_debugs); i++) {
DEBUG_FLAGS_SET(pbr_debugs[i], flags, set);
/* if all modes have been turned off, don't preserve options */
if (!DEBUG_MODE_CHECK(pbr_debugs[i], DEBUG_MODE_ALL))
DEBUG_CLEAR(pbr_debugs[i]);
}
}
/*
* 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)
{
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 0;
}
int pbr_debug_config_write(struct vty *vty)
{
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);
}

53
pbrd/pbr_debug.h Normal file
View File

@ -0,0 +1,53 @@
/*
* PBR - debugging
* Copyright (C) 2018 Cumulus Networks, Inc.
* Quentin Young
*
* This program 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 of the License, or (at your option)
* any later version.
*
* This program 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
*/
#ifndef __PBR_DEBUG_H__
#define __PBR_DEBUG_H__
#include <zebra.h>
#include "debug.h"
/* PBR debugging records */
extern struct debug pbr_dbg_map;
extern struct debug pbr_dbg_zebra;
extern struct debug pbr_dbg_nht;
extern struct debug pbr_dbg_event;
/*
* Initialize PBR debugging.
*
* Installs VTY commands and registers callbacks.
*/
void pbr_debug_init(void);
/*
* Install PBR debugging VTY commands.
*/
void pbr_debug_init_vty(void);
/*
* Print PBR debugging configuration.
*
* vty
* VTY to print debugging configuration to.
*/
int pbr_debug_config_write(struct vty *vty);
#endif /* __PBR_DEBUG_H__ */

165
pbrd/pbr_main.c Normal file
View File

@ -0,0 +1,165 @@
/*
* PBR - main 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 <lib/version.h>
#include "getopt.h"
#include "thread.h"
#include "prefix.h"
#include "linklist.h"
#include "if.h"
#include "vector.h"
#include "vty.h"
#include "command.h"
#include "filter.h"
#include "plist.h"
#include "stream.h"
#include "log.h"
#include "memory.h"
#include "privs.h"
#include "sigevent.h"
#include "zclient.h"
#include "keychain.h"
#include "distribute.h"
#include "libfrr.h"
#include "routemap.h"
#include "nexthop.h"
#include "nexthop_group.h"
#include "pbr_nht.h"
#include "pbr_map.h"
#include "pbr_zebra.h"
#include "pbr_vty.h"
#include "pbr_debug.h"
zebra_capabilities_t _caps_p[] = {
ZCAP_NET_RAW, ZCAP_BIND, ZCAP_NET_ADMIN,
};
struct zebra_privs_t pbr_privs = {
#if defined(FRR_USER) && defined(FRR_GROUP)
.user = FRR_USER,
.group = FRR_GROUP,
#endif
#if defined(VTY_GROUP)
.vty_group = VTY_GROUP,
#endif
.caps_p = _caps_p,
.cap_num_p = array_size(_caps_p),
.cap_num_i = 0};
struct option longopts[] = { { 0 } };
/* Master of threads. */
struct thread_master *master;
/* SIGHUP handler. */
static void sighup(void)
{
zlog_info("SIGHUP received");
}
/* SIGINT / SIGTERM handler. */
static void sigint(void)
{
zlog_notice("Terminating on signal");
exit(0);
}
/* SIGUSR1 handler. */
static void sigusr1(void)
{
zlog_rotate();
}
struct quagga_signal_t pbr_signals[] = {
{
.signal = SIGHUP,
.handler = &sighup,
},
{
.signal = SIGUSR1,
.handler = &sigusr1,
},
{
.signal = SIGINT,
.handler = &sigint,
},
{
.signal = SIGTERM,
.handler = &sigint,
},
};
#define PBR_VTY_PORT 2615
FRR_DAEMON_INFO(pbrd, PBR, .vty_port = PBR_VTY_PORT,
.proghelp = "Implementation of PBR.",
.signals = pbr_signals,
.n_signals = array_size(pbr_signals),
.privs = &pbr_privs,)
int main(int argc, char **argv, char **envp)
{
frr_preinit(&pbrd_di, argc, argv);
frr_opt_add("", longopts, "");
while (1) {
int opt;
opt = frr_getopt(argc, argv, NULL);
if (opt == EOF)
break;
switch (opt) {
case 0:
break;
default:
frr_help_exit(1);
break;
}
}
master = frr_init();
pbr_debug_init();
vrf_init(NULL, NULL, NULL, NULL);
nexthop_group_init(pbr_nhgroup_add_cb,
pbr_nhgroup_add_nexthop_cb,
pbr_nhgroup_del_nexthop_cb,
pbr_nhgroup_delete_cb);
pbr_nht_init();
pbr_map_init();
pbr_zebra_init();
pbr_vty_init();
frr_config_fork();
frr_run(master);
/* Not reached. */
return 0;
}

569
pbrd/pbr_map.c Normal file
View File

@ -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;
}

162
pbrd/pbr_map.h Normal file
View File

@ -0,0 +1,162 @@
/*
* PBR-map Header
* 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
*/
#ifndef __PBR_MAP_H__
#define __PBR_MAP_H__
struct pbr_map {
/*
* RB Tree of the pbr_maps
*/
RB_ENTRY(pbr_map) pbr_map_entry;
/*
* The name of the PBR_MAP
*/
#define PBR_MAP_NAMELEN 100
char name[PBR_MAP_NAMELEN];
struct list *seqnumbers;
/*
* The list of incoming interfaces that
* we will apply this policy map onto
*/
struct list *incoming;
/*
* 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 {
struct interface *ifp;
struct pbr_map *pbrm;
bool delete;
};
struct pbr_map_sequence {
struct pbr_map *parent;
/*
* The Unique identifier of this specific pbrms
*/
uint32_t unique;
/*
* The sequence of where we are for display
*/
uint32_t seqno;
/*
* The rule number to install into
*/
uint32_t ruleno;
/*
* Our policy Catchers
*/
struct prefix *src;
struct prefix *dst;
/*
* Family of the src/dst. Needed when deleting since we clear them
*/
unsigned char family;
/*
* The nexthop group we auto create
* for when the user specifies a individual
* nexthop
*/
struct nexthop_group *nhg;
char *internal_nhg_name;
/*
* The name of the nexthop group
* configured in the pbr-map
*/
char *nhgrp_name;
/*
* Do we think are nexthops are installed
*/
bool nhs_installed;
/*
* Are we installed
*/
bool installed;
/*
* A reason of 0 means we think the pbr_map_sequence is good to go
* We can accumuluate multiple failure states
*/
#define PBR_MAP_VALID_SEQUENCE_NUMBER 0
#define PBR_MAP_INVALID_NEXTHOP_GROUP (1 << 0)
#define PBR_MAP_INVALID_NEXTHOP (1 << 1)
#define PBR_MAP_INVALID_NO_NEXTHOPS (1 << 2)
#define PBR_MAP_INVALID_BOTH_NHANDGRP (1 << 3)
#define PBR_MAP_INVALID_SRCDST (1 << 4)
uint64_t reason;
QOBJ_FIELDS
};
DECLARE_QOBJ_TYPE(pbr_map_sequence)
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 *pbrm_find(const char *name);
extern void pbr_map_delete(struct pbr_map_sequence *pbrms);
extern void pbr_map_delete_nexthop_group(struct pbr_map_sequence *pbrms);
extern void pbr_map_add_interface(struct pbr_map *pbrm, struct interface *ifp);
extern void pbr_map_interface_delete(struct pbr_map *pbrm,
struct interface *ifp);
extern void pbr_map_write_interfaces(struct vty *vty, struct interface *ifp);
extern void pbr_map_init(void);
extern bool pbr_map_check_valid(const char *name);
extern void pbr_map_check(struct pbr_map_sequence *pbrms);
extern void pbr_map_check_nh_group_change(const char *nh_group);
extern void pbr_map_reason_string(unsigned int reason, char *buf, int size);
extern void pbr_map_schedule_policy_from_nhg(const char *nh_group);
extern void pbr_map_install(struct pbr_map *pbrm);
extern void pbr_map_policy_install(const char *name);
extern void pbr_map_policy_delete(struct pbr_map *pbrm,
struct pbr_map_interface *pmi);
#endif

27
pbrd/pbr_memory.c Normal file
View File

@ -0,0 +1,27 @@
/*
* PBR memory 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 <memory.h>
#include "pbrd/pbr_memory.h"
DEFINE_MGROUP(PBRD, "pbrd")

24
pbrd/pbr_memory.h Normal file
View File

@ -0,0 +1,24 @@
/*
* pbr memory 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
*/
#ifndef __PBR_MEMORY_H__
DECLARE_MGROUP(PBRD)
#endif

842
pbrd/pbr_nht.c Normal file
View File

@ -0,0 +1,842 @@
/*
* PBR-nht 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 <log.h>
#include <nexthop.h>
#include <nexthop_group.h>
#include <hash.h>
#include <jhash.h>
#include <vty.h>
#include <zclient.h>
#include <debug.h>
#include "pbrd/pbr_nht.h"
#include "pbrd/pbr_map.h"
#include "pbrd/pbr_zebra.h"
#include "pbrd/pbr_memory.h"
#include "pbrd/pbr_debug.h"
DEFINE_MTYPE_STATIC(PBRD, PBR_NHG, "PBR Nexthop Groups")
static struct hash *pbr_nhg_hash;
static struct hash *pbr_nhrc_hash;
static uint32_t pbr_nhg_low_table;
static uint32_t pbr_nhg_high_table;
static uint32_t pbr_nhg_low_rule;
static uint32_t pbr_nhg_high_rule;
static bool nhg_tableid[65535];
static void pbr_nht_install_nexthop_group(struct pbr_nexthop_group_cache *pnhgc,
struct nexthop_group nhg);
static void
pbr_nht_uninstall_nexthop_group(struct pbr_nexthop_group_cache *pnhgc,
struct nexthop_group nhg,
enum nexthop_types_t nh_afi);
/*
* Nexthop refcount.
*/
struct nhrc {
struct nexthop nexthop;
unsigned int refcount;
};
/* Hash functions for pbr_nhrc_hash ---------------------------------------- */
static void *pbr_nhrc_hash_alloc(void *p)
{
struct nhrc *nhrc = XCALLOC(MTYPE_PBR_NHG, sizeof(struct nhrc));
nhrc->nexthop = *(struct nexthop *)p;
return nhrc;
}
static int pbr_nhrc_hash_equal(const void *arg1, const void *arg2)
{
const struct nexthop *nh1, *nh2;
nh1 = arg1;
nh2 = arg2;
return nexthop_same(nh1, nh2);
}
/* ------------------------------------------------------------------------- */
static void *pbr_nh_alloc(void *p)
{
struct pbr_nexthop_cache *new;
struct pbr_nexthop_cache *pnhc = (struct pbr_nexthop_cache *)p;
struct nhrc *nhrc;
new = XCALLOC(MTYPE_PBR_NHG, sizeof(*new));
nhrc = hash_get(pbr_nhrc_hash, pnhc->nexthop, pbr_nhrc_hash_alloc);
new->nexthop = &nhrc->nexthop;
/* Decremented again in pbr_nh_delete */
++nhrc->refcount;
DEBUGD(&pbr_dbg_nht, "%s: Sending nexthop to Zebra",
__PRETTY_FUNCTION__);
pbr_send_rnh(new->nexthop, true);
new->valid = false;
return new;
}
static void pbr_nh_delete(struct pbr_nexthop_cache **pnhc)
{
struct nhrc *nhrc;
nhrc = hash_lookup(pbr_nhrc_hash, (*pnhc)->nexthop);
if (nhrc)
--nhrc->refcount;
if (!nhrc || nhrc->refcount == 0) {
DEBUGD(&pbr_dbg_nht, "%s: Removing nexthop from Zebra",
__PRETTY_FUNCTION__);
pbr_send_rnh((*pnhc)->nexthop, false);
}
if (nhrc && nhrc->refcount == 0) {
hash_release(pbr_nhrc_hash, nhrc);
XFREE(MTYPE_PBR_NHG, nhrc);
}
XFREE(MTYPE_PBR_NHG, *pnhc);
}
static void pbr_nh_delete_iterate(struct hash_backet *b, void *p)
{
pbr_nh_delete((struct pbr_nexthop_cache **)&b->data);
}
static uint32_t pbr_nh_hash_key(void *arg)
{
uint32_t key;
struct pbr_nexthop_cache *pbrnc = (struct pbr_nexthop_cache *)arg;
key = nexthop_hash(pbrnc->nexthop);
return key;
}
static int pbr_nh_hash_equal(const void *arg1, const void *arg2)
{
const struct pbr_nexthop_cache *pbrnc1 =
(const struct pbr_nexthop_cache *)arg1;
const struct pbr_nexthop_cache *pbrnc2 =
(const struct pbr_nexthop_cache *)arg2;
if (pbrnc1->nexthop->vrf_id != pbrnc2->nexthop->vrf_id)
return 0;
if (pbrnc1->nexthop->ifindex != pbrnc2->nexthop->ifindex)
return 0;
if (pbrnc1->nexthop->type != pbrnc2->nexthop->type)
return 0;
switch (pbrnc1->nexthop->type) {
case NEXTHOP_TYPE_IFINDEX:
return 1;
case NEXTHOP_TYPE_IPV4_IFINDEX:
case NEXTHOP_TYPE_IPV4:
return pbrnc1->nexthop->gate.ipv4.s_addr
== pbrnc2->nexthop->gate.ipv4.s_addr;
case NEXTHOP_TYPE_IPV6_IFINDEX:
case NEXTHOP_TYPE_IPV6:
return !memcmp(&pbrnc1->nexthop->gate.ipv6,
&pbrnc2->nexthop->gate.ipv6, 16);
case NEXTHOP_TYPE_BLACKHOLE:
return pbrnc1->nexthop->bh_type == pbrnc2->nexthop->bh_type;
}
/*
* We should not get here
*/
return 0;
}
static void pbr_nhgc_delete(struct pbr_nexthop_group_cache *p)
{
hash_iterate(p->nhh, pbr_nh_delete_iterate, NULL);
hash_free(p->nhh);
XFREE(MTYPE_PBR_NHG, p);
}
static void *pbr_nhgc_alloc(void *p)
{
struct pbr_nexthop_group_cache *new;
struct pbr_nexthop_group_cache *pnhgc =
(struct pbr_nexthop_group_cache *)p;
new = XCALLOC(MTYPE_PBR_NHG, sizeof(*new));
strcpy(new->name, pnhgc->name);
new->table_id = pbr_nht_get_next_tableid();
DEBUGD(&pbr_dbg_nht, "%s: NHT: %s assigned Table ID: %u",
__PRETTY_FUNCTION__, new->name, new->table_id);
new->nhh = hash_create_size(8, pbr_nh_hash_key, pbr_nh_hash_equal,
"PBR NH Cache Hash");
return new;
}
void pbr_nhgroup_add_cb(const char *name)
{
struct pbr_nexthop_group_cache *pnhgc;
struct nexthop_group_cmd *nhgc;
nhgc = nhgc_find(name);
pnhgc = pbr_nht_add_group(name);
DEBUGD(&pbr_dbg_nht, "%s: Added nexthop-group %s", __PRETTY_FUNCTION__,
name);
pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg);
pbr_map_check_nh_group_change(name);
}
void pbr_nhgroup_add_nexthop_cb(const struct nexthop_group_cmd *nhgc,
const struct nexthop *nhop)
{
char debugstr[256];
struct pbr_nexthop_group_cache pnhgc_find = {};
struct pbr_nexthop_group_cache *pnhgc;
struct pbr_nexthop_cache pnhc_find = {};
struct pbr_nexthop_cache *pnhc;
/* find pnhgc by name */
strlcpy(pnhgc_find.name, nhgc->name, sizeof(pnhgc_find.name));
pnhgc = hash_get(pbr_nhg_hash, &pnhgc_find, pbr_nhgc_alloc);
/* create & insert new pnhc into pnhgc->nhh */
pnhc_find.nexthop = (struct nexthop *)nhop;
pnhc = hash_get(pnhgc->nhh, &pnhc_find, pbr_nh_alloc);
pnhc_find.nexthop = NULL;
/* set parent pnhgc */
pnhc->parent = pnhgc;
if (DEBUG_MODE_CHECK(&pbr_dbg_nht, DEBUG_MODE_ALL)) {
nexthop2str(nhop, debugstr, sizeof(debugstr));
DEBUGD(&pbr_dbg_nht, "%s: Added %s to nexthop-group %s",
__PRETTY_FUNCTION__, debugstr, nhgc->name);
}
pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg);
pbr_map_check_nh_group_change(nhgc->name);
}
void pbr_nhgroup_del_nexthop_cb(const struct nexthop_group_cmd *nhgc,
const struct nexthop *nhop)
{
char debugstr[256];
struct pbr_nexthop_group_cache pnhgc_find = {};
struct pbr_nexthop_group_cache *pnhgc;
struct pbr_nexthop_cache pnhc_find = {};
struct pbr_nexthop_cache *pnhc;
enum nexthop_types_t nh_afi = nhop->type;
/* find pnhgc by name */
strlcpy(pnhgc_find.name, nhgc->name, sizeof(pnhgc_find.name));
pnhgc = hash_get(pbr_nhg_hash, &pnhgc_find, pbr_nhgc_alloc);
/* delete pnhc from pnhgc->nhh */
pnhc_find.nexthop = (struct nexthop *)nhop;
pnhc = hash_release(pnhgc->nhh, &pnhc_find);
/* delete pnhc */
pbr_nh_delete(&pnhc);
if (DEBUG_MODE_CHECK(&pbr_dbg_nht, DEBUG_MODE_ALL)) {
nexthop2str(nhop, debugstr, sizeof(debugstr));
DEBUGD(&pbr_dbg_nht, "%s: Removed %s from nexthop-group %s",
__PRETTY_FUNCTION__, debugstr, nhgc->name);
}
if (pnhgc->nhh->count)
pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg);
else
pbr_nht_uninstall_nexthop_group(pnhgc, nhgc->nhg, nh_afi);
pbr_map_check_nh_group_change(nhgc->name);
}
void pbr_nhgroup_delete_cb(const char *name)
{
DEBUGD(&pbr_dbg_nht, "%s: Removed nexthop-group %s",
__PRETTY_FUNCTION__, name);
/* delete group from all pbrms's */
pbr_nht_delete_group(name);
pbr_map_check_nh_group_change(name);
}
#if 0
static struct pbr_nexthop_cache *pbr_nht_lookup_nexthop(struct nexthop *nexthop)
{
return NULL;
}
#endif
static void pbr_nht_find_nhg_from_table_install(struct hash_backet *b,
void *data)
{
struct pbr_nexthop_group_cache *pnhgc =
(struct pbr_nexthop_group_cache *)b->data;
uint32_t *table_id = (uint32_t *)data;
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);
}
}
void pbr_nht_route_installed_for_table(uint32_t table_id)
{
hash_iterate(pbr_nhg_hash, pbr_nht_find_nhg_from_table_install,
&table_id);
}
static void pbr_nht_find_nhg_from_table_remove(struct hash_backet *b,
void *data)
{
;
}
void pbr_nht_route_removed_for_table(uint32_t table_id)
{
hash_iterate(pbr_nhg_hash, pbr_nht_find_nhg_from_table_remove,
&table_id);
}
/*
* Loop through all nexthops in a nexthop group to check that they are all the
* same. If they are not all the same, log this peculiarity.
*
* nhg
* The nexthop group to check
*
* Returns:
* - AFI of last nexthop in the group
* - AFI_MAX on error
*/
static afi_t pbr_nht_which_afi(struct nexthop_group nhg,
enum nexthop_types_t nh_afi)
{
struct nexthop *nexthop;
afi_t install_afi = AFI_MAX;
bool v6, v4, bh;
v6 = v4 = bh = false;
if (!nh_afi) {
for (ALL_NEXTHOPS(nhg, nexthop)) {
nh_afi = nexthop->type;
break;
}
}
switch (nh_afi) {
case NEXTHOP_TYPE_IFINDEX:
break;
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
v6 = true;
install_afi = AFI_IP;
break;
case NEXTHOP_TYPE_IPV6:
case NEXTHOP_TYPE_IPV6_IFINDEX:
v4 = true;
install_afi = AFI_IP6;
break;
case NEXTHOP_TYPE_BLACKHOLE:
bh = true;
install_afi = AFI_MAX;
break;
}
if (!bh && v6 && v4)
DEBUGD(&pbr_dbg_nht,
"%s: Saw both V6 and V4 nexthops...using %s",
__PRETTY_FUNCTION__, afi2str(install_afi));
if (bh && (v6 || v4))
DEBUGD(&pbr_dbg_nht,
"%s: Saw blackhole nexthop(s) with %s%s%s nexthop(s), using AFI_MAX.",
__PRETTY_FUNCTION__, v4 ? "v4" : "",
(v4 && v6) ? " and " : "", v6 ? "v6" : "");
return install_afi;
}
static void pbr_nht_install_nexthop_group(struct pbr_nexthop_group_cache *pnhgc,
struct nexthop_group nhg)
{
afi_t install_afi;
enum nexthop_types_t nh_afi = 0;
install_afi = pbr_nht_which_afi(nhg, nh_afi);
pnhgc->installed = false;
route_add(pnhgc, nhg, install_afi);
}
static void
pbr_nht_uninstall_nexthop_group(struct pbr_nexthop_group_cache *pnhgc,
struct nexthop_group nhg,
enum nexthop_types_t nh_afi)
{
afi_t install_afi;
install_afi = pbr_nht_which_afi(nhg, nh_afi);
pnhgc->installed = false;
pnhgc->valid = false;
route_delete(pnhgc, install_afi);
}
void pbr_nht_change_group(const char *name)
{
struct nexthop_group_cmd *nhgc;
struct pbr_nexthop_group_cache *pnhgc;
struct pbr_nexthop_group_cache find;
struct nexthop *nhop;
nhgc = nhgc_find(name);
if (!nhgc)
return;
memset(&find, 0, sizeof(find));
strcpy(find.name, name);
pnhgc = hash_lookup(pbr_nhg_hash, &find);
if (!pnhgc) {
DEBUGD(&pbr_dbg_nht,
"%s: Could not find nexthop-group cache w/ name '%s'",
__PRETTY_FUNCTION__, name);
return;
}
for (ALL_NEXTHOPS(nhgc->nhg, nhop)) {
struct pbr_nexthop_cache lookup;
struct pbr_nexthop_cache *pnhc;
lookup.nexthop = nhop;
pnhc = hash_lookup(pnhgc->nhh, &lookup);
if (!pnhc) {
pnhc = hash_get(pnhgc->nhh, &lookup, pbr_nh_alloc);
pnhc->parent = pnhgc;
}
}
pbr_nht_install_nexthop_group(pnhgc, nhgc->nhg);
}
char *pbr_nht_nexthop_make_name(char *name, size_t l,
uint32_t seqno, char *buffer)
{
snprintf(buffer, l, "%s%u", name, seqno);
return buffer;
}
void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms)
{
struct pbr_nexthop_group_cache *pnhgc;
struct pbr_nexthop_group_cache find;
struct pbr_nexthop_cache *pnhc;
struct pbr_nexthop_cache lookup;
memset(&find, 0, sizeof(find));
pbr_nht_nexthop_make_name(pbrms->parent->name, PBR_MAP_NAMELEN,
pbrms->seqno, find.name);
if (!pbrms->internal_nhg_name)
pbrms->internal_nhg_name = XSTRDUP(MTYPE_TMP, find.name);
pnhgc = hash_get(pbr_nhg_hash, &find, pbr_nhgc_alloc);
lookup.nexthop = pbrms->nhg->nexthop;
pnhc = hash_get(pnhgc->nhh, &lookup, pbr_nh_alloc);
pnhc->parent = pnhgc;
pbr_nht_install_nexthop_group(pnhgc, *pbrms->nhg);
}
void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence *pbrms)
{
struct pbr_nexthop_group_cache *pnhgc;
struct pbr_nexthop_group_cache find;
struct pbr_nexthop_cache *pnhc;
struct pbr_nexthop_cache lup;
struct pbr_map *pbrm = pbrms->parent;
struct listnode *node;
struct pbr_map_interface *pmi;
struct nexthop *nh;
enum nexthop_types_t nh_afi = 0;
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;
memset(&find, 0, sizeof(find));
strcpy(&find.name[0], pbrms->internal_nhg_name);
pnhgc = hash_lookup(pbr_nhg_hash, &find);
nh = pbrms->nhg->nexthop;
nh_afi = nh->type;
lup.nexthop = nh;
pnhc = hash_lookup(pnhgc->nhh, &lup);
pnhc->parent = NULL;
hash_release(pnhgc->nhh, pnhc);
pbr_nh_delete(&pnhc);
pbr_nht_uninstall_nexthop_group(pnhgc, *pbrms->nhg, nh_afi);
hash_release(pbr_nhg_hash, pnhgc);
nexthop_del(pbrms->nhg, nh);
nexthop_free(nh);
nexthop_group_delete(&pbrms->nhg);
XFREE(MTYPE_TMP, pbrms->internal_nhg_name);
}
struct pbr_nexthop_group_cache *pbr_nht_add_group(const char *name)
{
struct nexthop *nhop;
struct nexthop_group_cmd *nhgc;
struct pbr_nexthop_group_cache *pnhgc;
struct pbr_nexthop_group_cache lookup;
nhgc = nhgc_find(name);
if (!nhgc) {
zlog_warn("%s: Could not find group %s to add",
__PRETTY_FUNCTION__, name);
return NULL;
}
strcpy(lookup.name, name);
pnhgc = hash_get(pbr_nhg_hash, &lookup, pbr_nhgc_alloc);
DEBUGD(&pbr_dbg_nht, "%s: Retrieved NHGC @ %p", __PRETTY_FUNCTION__,
pnhgc);
for (ALL_NEXTHOPS(nhgc->nhg, nhop)) {
struct pbr_nexthop_cache lookup;
struct pbr_nexthop_cache *pnhc;
lookup.nexthop = nhop;
pnhc = hash_lookup(pnhgc->nhh, &lookup);
if (!pnhc) {
pnhc = hash_get(pnhgc->nhh, &lookup, pbr_nh_alloc);
pnhc->parent = pnhgc;
}
}
return pnhgc;
}
void pbr_nht_delete_group(const char *name)
{
struct pbr_map_sequence *pbrms;
struct listnode *snode;
struct pbr_map *pbrm;
struct pbr_nexthop_group_cache pnhgc_find;
struct pbr_nexthop_group_cache *pnhgc;
RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) {
for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, snode, pbrms)) {
if (pbrms->nhgrp_name
&& strmatch(pbrms->nhgrp_name, name)) {
pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS;
nexthop_group_delete(&pbrms->nhg);
pbrms->nhg = NULL;
pbrms->internal_nhg_name = NULL;
pbrm->valid = false;
}
}
}
strlcpy(pnhgc_find.name, name, sizeof(pnhgc_find.name));
pnhgc = hash_release(pbr_nhg_hash, &pnhgc_find);
pbr_nhgc_delete(pnhgc);
}
bool pbr_nht_nexthop_valid(struct nexthop_group *nhg)
{
DEBUGD(&pbr_dbg_nht, "%s: %p", __PRETTY_FUNCTION__, nhg);
return true;
}
bool pbr_nht_nexthop_group_valid(const char *name)
{
struct pbr_nexthop_group_cache *pnhgc;
struct pbr_nexthop_group_cache lookup;
DEBUGD(&pbr_dbg_nht, "%s: %s", __PRETTY_FUNCTION__, name);
strcpy(lookup.name, name);
pnhgc = hash_get(pbr_nhg_hash, &lookup, NULL);
if (!pnhgc)
return false;
DEBUGD(&pbr_dbg_nht, "%s: \t%d %d", __PRETTY_FUNCTION__, pnhgc->valid,
pnhgc->installed);
if (pnhgc->valid && pnhgc->installed)
return true;
return false;
}
struct pbr_nht_individual {
struct zapi_route *nhr;
uint32_t valid;
};
static void pbr_nht_individual_nexthop_update_lookup(struct hash_backet *b,
void *data)
{
struct pbr_nexthop_cache *pnhc = b->data;
struct pbr_nht_individual *pnhi = data;
char buf[PREFIX_STRLEN];
bool old_valid;
old_valid = pnhc->valid;
switch (pnhi->nhr->prefix.family) {
case AF_INET:
if (pnhc->nexthop->gate.ipv4.s_addr
== pnhi->nhr->prefix.u.prefix4.s_addr)
pnhc->valid = !!pnhi->nhr->nexthop_num;
break;
case AF_INET6:
if (memcmp(&pnhc->nexthop->gate.ipv6,
&pnhi->nhr->prefix.u.prefix6, 16)
== 0)
pnhc->valid = !!pnhi->nhr->nexthop_num;
break;
}
DEBUGD(&pbr_dbg_nht, "\tFound %s: old: %d new: %d",
prefix2str(&pnhi->nhr->prefix, buf, sizeof(buf)), old_valid,
pnhc->valid);
if (pnhc->valid)
pnhi->valid += 1;
}
static void pbr_nht_nexthop_update_lookup(struct hash_backet *b, void *data)
{
struct pbr_nexthop_group_cache *pnhgc = b->data;
struct pbr_nht_individual pnhi;
bool old_valid;
old_valid = pnhgc->valid;
pnhi.nhr = (struct zapi_route *)data;
pnhi.valid = 0;
hash_iterate(pnhgc->nhh, pbr_nht_individual_nexthop_update_lookup,
&pnhi);
/*
* If any of the specified nexthops are valid we are valid
*/
pnhgc->valid = !!pnhi.valid;
if (old_valid != pnhgc->valid)
pbr_map_check_nh_group_change(pnhgc->name);
}
void pbr_nht_nexthop_update(struct zapi_route *nhr)
{
hash_iterate(pbr_nhg_hash, pbr_nht_nexthop_update_lookup, nhr);
}
static uint32_t pbr_nhg_hash_key(void *arg)
{
struct pbr_nexthop_group_cache *nhgc =
(struct pbr_nexthop_group_cache *)arg;
return jhash(&nhgc->name, strlen(nhgc->name), 0x52c34a96);
}
static int pbr_nhg_hash_equal(const void *arg1, const void *arg2)
{
const struct pbr_nexthop_group_cache *nhgc1 =
(const struct pbr_nexthop_group_cache *)arg1;
const struct pbr_nexthop_group_cache *nhgc2 =
(const struct pbr_nexthop_group_cache *)arg2;
return !strcmp(nhgc1->name, nhgc2->name);
}
uint32_t pbr_nht_get_next_tableid(void)
{
uint32_t i;
bool found = false;
for (i = pbr_nhg_low_table; i <= pbr_nhg_high_table; i++) {
if (nhg_tableid[i] == false) {
found = true;
break;
}
}
if (found) {
nhg_tableid[i] = true;
return i;
} else
return 0;
}
void pbr_nht_set_tableid_range(uint32_t low, uint32_t high)
{
pbr_nhg_low_table = low;
pbr_nhg_high_table = high;
}
void pbr_nht_write_table_range(struct vty *vty)
{
if (pbr_nhg_low_table != PBR_NHT_DEFAULT_LOW_TABLEID
|| pbr_nhg_high_table != PBR_NHT_DEFAULT_HIGH_TABLEID) {
vty_out(vty, "pbr table range %u %u\n", pbr_nhg_low_table,
pbr_nhg_high_table);
}
}
uint32_t pbr_nht_get_next_rule(uint32_t seqno)
{
return seqno + pbr_nhg_low_rule - 1;
}
void pbr_nht_set_rule_range(uint32_t low, uint32_t high)
{
pbr_nhg_low_rule = low;
pbr_nhg_high_rule = high;
}
void pbr_nht_write_rule_range(struct vty *vty)
{
if (pbr_nhg_low_rule != PBR_NHT_DEFAULT_LOW_RULE
|| pbr_nhg_high_rule != PBR_NHT_DEFAULT_HIGH_RULE) {
vty_out(vty, "pbr rule range %u %u\n", pbr_nhg_low_rule,
pbr_nhg_high_rule);
}
}
uint32_t pbr_nht_get_table(const char *name)
{
struct pbr_nexthop_group_cache find;
struct pbr_nexthop_group_cache *pnhgc;
memset(&find, 0, sizeof(find));
strcpy(find.name, name);
pnhgc = hash_lookup(pbr_nhg_hash, &find);
if (!pnhgc) {
DEBUGD(&pbr_dbg_nht,
"%s: Could not find nexthop-group cache w/ name '%s'",
__PRETTY_FUNCTION__, name);
return 5000;
}
return pnhgc->table_id;
}
bool pbr_nht_get_installed(const char *name)
{
struct pbr_nexthop_group_cache find;
struct pbr_nexthop_group_cache *pnhgc;
memset(&find, 0, sizeof(find));
strcpy(find.name, name);
pnhgc = hash_lookup(pbr_nhg_hash, &find);
if (!pnhgc)
return false;
return pnhgc->installed;
}
static void pbr_nht_show_nhg_nexthops(struct hash_backet *b, void *data)
{
struct pbr_nexthop_cache *pnhc = b->data;
struct vty *vty = data;
vty_out(vty, "\tValid: %d ", pnhc->valid);
nexthop_group_write_nexthop(vty, pnhc->nexthop);
}
struct pbr_nht_show {
struct vty *vty;
const char *name;
};
static void pbr_nht_show_nhg(struct hash_backet *b, void *data)
{
struct pbr_nexthop_group_cache *pnhgc = b->data;
struct pbr_nht_show *pns = data;
struct vty *vty;
if (pns->name && strcmp(pns->name, pnhgc->name) != 0)
return;
vty = pns->vty;
vty_out(vty, "Nexthop-Group: %s Table: %u Valid: %d Installed: %d\n",
pnhgc->name, pnhgc->table_id, pnhgc->valid, pnhgc->installed);
hash_iterate(pnhgc->nhh, pbr_nht_show_nhg_nexthops, vty);
}
void pbr_nht_show_nexthop_group(struct vty *vty, const char *name)
{
struct pbr_nht_show pns;
pns.vty = vty;
pns.name = name;
hash_iterate(pbr_nhg_hash, pbr_nht_show_nhg, &pns);
}
void pbr_nht_init(void)
{
pbr_nhg_hash = hash_create_size(
16, pbr_nhg_hash_key, pbr_nhg_hash_equal, "PBR NHG Cache Hash");
pbr_nhrc_hash =
hash_create_size(16, (unsigned int (*)(void *))nexthop_hash,
pbr_nhrc_hash_equal, "PBR NH Hash");
pbr_nhg_low_table = PBR_NHT_DEFAULT_LOW_TABLEID;
pbr_nhg_high_table = PBR_NHT_DEFAULT_HIGH_TABLEID;
pbr_nhg_low_rule = PBR_NHT_DEFAULT_LOW_RULE;
pbr_nhg_high_rule = PBR_NHT_DEFAULT_HIGH_RULE;
memset(&nhg_tableid, 0, 65535 * sizeof(uint8_t));
}

115
pbrd/pbr_nht.h Normal file
View File

@ -0,0 +1,115 @@
/*
* PBR-nht Header
* 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
*/
#ifndef __PBR_NHT_H__
#define __PBR_NHT_H__
#include <lib/zclient.h>
#include <lib/nexthop_group.h>
#include "pbr_map.h"
struct pbr_nexthop_group_cache {
char name[PBR_MAP_NAMELEN];
uint32_t table_id;
struct hash *nhh;
/*
* If all nexthops are considered valid
*/
bool valid;
bool installed;
};
struct pbr_nexthop_cache {
struct pbr_nexthop_group_cache *parent;
struct nexthop *nexthop;
bool valid;
};
extern void pbr_nht_write_table_range(struct vty *vty);
#define PBR_NHT_DEFAULT_LOW_TABLEID 10000
#define PBR_NHT_DEFAULT_HIGH_TABLEID 11000
extern void pbr_nht_set_tableid_range(uint32_t low, uint32_t high);
/*
* Get the next tableid to use for installation
*/
extern uint32_t pbr_nht_get_next_tableid(void);
/*
* Get the next rule number to use for installation
*/
extern void pbr_nht_write_rule_range(struct vty *vty);
#define PBR_NHT_DEFAULT_LOW_RULE 300
#define PBR_NHT_DEFAULT_HIGH_RULE 1300
extern void pbr_nht_set_rule_range(uint32_t low, uint32_t high);
extern uint32_t pbr_nht_get_next_rule(uint32_t seqno);
extern void pbr_nhgroup_add_cb(const char *name);
extern void pbr_nhgroup_add_nexthop_cb(const struct nexthop_group_cmd *nhg,
const struct nexthop *nhop);
extern void pbr_nhgroup_del_nexthop_cb(const struct nexthop_group_cmd *nhg,
const struct nexthop *nhop);
extern void pbr_nhgroup_delete_cb(const char *name);
extern bool pbr_nht_nexthop_valid(struct nexthop_group *nhg);
extern bool pbr_nht_nexthop_group_valid(const char *name);
extern struct pbr_nexthop_group_cache *pbr_nht_add_group(const char *name);
extern void pbr_nht_change_group(const char *name);
extern void pbr_nht_delete_group(const char *name);
extern void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms);
extern void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence *pbrms);
/*
* Given the tableid of the installed default
* route, find the nexthop-group associated with
* it, then find all pbr-maps that use it and
* install/delete them as well.
*/
extern void pbr_nht_route_installed_for_table(uint32_t table_id);
extern void pbr_nht_route_removed_for_table(uint32_t table_id);
/*
* Given the nexthop group name, lookup the associated
* tableid with it
*/
extern uint32_t pbr_nht_get_table(const char *name);
extern bool pbr_nht_get_installed(const char *name);
extern char *pbr_nht_nexthop_make_name(char *name, size_t l, uint32_t seqno,
char *buffer);
extern void pbr_nht_show_nexthop_group(struct vty *vty, const char *name);
/*
* When we get a callback from zebra about a nexthop changing
*/
extern void pbr_nht_nexthop_update(struct zapi_route *nhr);
extern void pbr_nht_init(void);
#endif

585
pbrd/pbr_vty.c Normal file
View File

@ -0,0 +1,585 @@
/*
* PBR - vty 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 "vty.h"
#include "command.h"
#include "prefix.h"
#include "vrf.h"
#include "nexthop.h"
#include "nexthop_group.h"
#include "log.h"
#include "json.h"
#include "debug.h"
#include "pbrd/pbr_nht.h"
#include "pbrd/pbr_map.h"
#include "pbrd/pbr_zebra.h"
#include "pbrd/pbr_vty.h"
#include "pbrd/pbr_debug.h"
#ifndef VTYSH_EXTRACT_PL
#include "pbrd/pbr_vty_clippy.c"
#endif
DEFUN_NOSH(pbr_map, pbr_map_cmd, "pbr-map WORD seq (1-1000)",
"Create pbr-map or enter pbr-map command mode\n"
"The name of the PBR MAP\n"
"Sequence to insert in existing pbr-map entry\n"
"Sequence number\n")
{
const char *pbrm_name = argv[1]->arg;
uint32_t seqno = atoi(argv[3]->arg);
struct pbr_map_sequence *pbrms;
pbrms = pbrms_get(pbrm_name, seqno);
VTY_PUSH_CONTEXT(PBRMAP_NODE, pbrms);
return CMD_SUCCESS;
}
DEFUN_NOSH(no_pbr_map, no_pbr_map_cmd, "no pbr-map WORD [seq (1-65535)]",
NO_STR
"Delete pbr-map\n"
"The name of the PBR MAP\n"
"Sequence to delete from existing pbr-map entry\n"
"Sequence number\n")
{
const char *pbrm_name = argv[2]->arg;
uint32_t seqno = 0;
struct pbr_map *pbrm = pbrm_find(pbrm_name);
struct pbr_map_sequence *pbrms;
struct listnode *node, *next_node;
if (argc > 3)
seqno = atoi(argv[4]->arg);
if (!pbrm) {
vty_out(vty, "pbr-map %s not found\n", pbrm_name);
return CMD_SUCCESS;
}
for (ALL_LIST_ELEMENTS(pbrm->seqnumbers, node, next_node, pbrms)) {
if (seqno && pbrms->seqno != seqno)
continue;
pbr_map_delete(pbrms);
}
return CMD_SUCCESS;
}
DEFPY(pbr_map_match_src, pbr_map_match_src_cmd,
"[no] match src-ip <A.B.C.D/M|X:X::X:X/M>$prefix",
NO_STR
"Match the rest of the command\n"
"Choose the src ip or ipv6 prefix to use\n"
"v4 Prefix\n"
"v6 Prefix\n")
{
struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
pbrms->family = prefix->family;
if (!no) {
if (prefix_same(pbrms->src, prefix))
return CMD_SUCCESS;
if (!pbrms->src)
pbrms->src = prefix_new();
prefix_copy(pbrms->src, prefix);
} else {
prefix_free(pbrms->src);
pbrms->src = 0;
}
pbr_map_check(pbrms);
return CMD_SUCCESS;
}
DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd,
"[no] match dst-ip <A.B.C.D/M|X:X::X:X/M>$prefix",
NO_STR
"Match the rest of the command\n"
"Choose the src ip or ipv6 prefix to use\n"
"v4 Prefix\n"
"v6 Prefix\n")
{
struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
pbrms->family = prefix->family;
if (!no) {
if (prefix_same(pbrms->dst, prefix))
return CMD_SUCCESS;
if (!pbrms->dst)
pbrms->dst = prefix_new();
prefix_copy(pbrms->dst, prefix);
} else {
prefix_free(pbrms->dst);
pbrms->dst = NULL;
}
pbr_map_check(pbrms);
return CMD_SUCCESS;
}
DEFPY(pbr_map_nexthop_group, pbr_map_nexthop_group_cmd,
"[no] set nexthop-group NAME$name",
NO_STR
"Set for the PBR-MAP\n"
"nexthop-group to use\n"
"The name of the nexthop-group\n")
{
struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
struct nexthop_group_cmd *nhgc;
if (pbrms->nhg) {
vty_out(vty,
"A `set nexthop XX` command already exists, please remove that first\n");
return CMD_WARNING_CONFIG_FAILED;
}
nhgc = nhgc_find(name);
if (!nhgc) {
vty_out(vty, "Specified nexthop-group %s does not exist\n",
name);
vty_out(vty, "PBR-MAP will not be applied until it is created\n");
}
if (no) {
if (pbrms->nhgrp_name && strcmp(name, pbrms->nhgrp_name) == 0)
pbr_map_delete_nexthop_group(pbrms);
else {
vty_out(vty,
"Nexthop Group specified: %s does not exist to remove",
name);
return CMD_WARNING_CONFIG_FAILED;
}
} else {
if (pbrms->nhgrp_name) {
if (strcmp(name, pbrms->nhgrp_name) != 0) {
vty_out(vty,
"Please delete current nexthop group before modifying current one");
return CMD_WARNING_CONFIG_FAILED;
}
return CMD_SUCCESS;
}
pbrms->nhgrp_name = XSTRDUP(MTYPE_TMP, name);
pbr_map_check(pbrms);
}
return CMD_SUCCESS;
}
DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd,
"[no] set nexthop <A.B.C.D|X:X::X:X>$addr [INTERFACE]$intf [nexthop-vrf NAME$name]",
NO_STR
"Set for the PBR-MAP\n"
"Specify one of the nexthops in this map\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")
{
struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
struct vrf *vrf;
struct nexthop nhop;
struct nexthop *nh;
if (pbrms->nhgrp_name) {
vty_out(vty,
"Please unconfigure the nexthop group before adding an individual nexthop");
return CMD_WARNING_CONFIG_FAILED;
}
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_CONFIG_FAILED;
}
memset(&nhop, 0, sizeof(nhop));
nhop.vrf_id = vrf->vrf_id;
if (addr->sa.sa_family == AF_INET) {
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_CONFIG_FAILED;
}
} else
nhop.type = NEXTHOP_TYPE_IPV4;
} else {
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_CONFIG_FAILED;
}
} else
nhop.type = NEXTHOP_TYPE_IPV6;
}
if (pbrms->nhg)
nh = nexthop_exists(pbrms->nhg, &nhop);
else {
char buf[100];
if (no) {
vty_out(vty, "No nexthops to delete");
return CMD_WARNING_CONFIG_FAILED;
}
pbrms->nhg = nexthop_group_new();
pbrms->internal_nhg_name =
XSTRDUP(MTYPE_TMP,
pbr_nht_nexthop_make_name(pbrms->parent->name,
PBR_MAP_NAMELEN,
pbrms->seqno,
buf));
nh = NULL;
}
if (no) {
if (nh)
pbr_nht_delete_individual_nexthop(pbrms);
} else if (!nh) {
if (pbrms->nhg->nexthop) {
vty_out(vty,
"If you would like more than one nexthop please use nexthop-groups");
return CMD_WARNING_CONFIG_FAILED;
}
/* must be adding new nexthop since !no and !nexthop_exists */
nh = nexthop_new();
memcpy(nh, &nhop, sizeof(nhop));
nexthop_add(&pbrms->nhg->nexthop, nh);
pbr_nht_add_individual_nexthop(pbrms);
pbr_map_check(pbrms);
}
return CMD_SUCCESS;
}
DEFPY (pbr_policy,
pbr_policy_cmd,
"[no] pbr-policy NAME$mapname",
NO_STR
"Policy to use\n"
"Name of the pbr-map to apply\n")
{
VTY_DECLVAR_CONTEXT(interface, ifp);
struct pbr_map *pbrm, *old_pbrm;
struct pbr_interface *pbr_ifp = ifp->info;
pbrm = pbrm_find(mapname);
if (!pbr_ifp) {
/*
* Some one could have fat fingered the interface
* name
*/
pbr_ifp = pbr_if_new(ifp);
}
if (no) {
if (strcmp(pbr_ifp->mapname, mapname) == 0) {
strcpy(pbr_ifp->mapname, "");
if (pbrm)
pbr_map_interface_delete(pbrm, ifp);
}
} else {
if (strcmp(pbr_ifp->mapname, "") == 0) {
strcpy(pbr_ifp->mapname, mapname);
if (pbrm)
pbr_map_add_interface(pbrm, ifp);
} else {
if (!(strcmp(pbr_ifp->mapname, mapname) == 0)) {
old_pbrm = pbrm_find(pbr_ifp->mapname);
if (old_pbrm)
pbr_map_interface_delete(old_pbrm, ifp);
strcpy(pbr_ifp->mapname, mapname);
if (pbrm)
pbr_map_add_interface(pbrm, ifp);
}
}
}
return CMD_SUCCESS;
}
DEFPY (show_pbr,
show_pbr_cmd,
"show pbr [json$json]",
SHOW_STR
"Policy Based Routing\n"
JSON_STR)
{
pbr_nht_write_table_range(vty);
pbr_nht_write_rule_range(vty);
return CMD_SUCCESS;
}
DEFPY (show_pbr_map,
show_pbr_map_cmd,
"show pbr map [NAME$name] [detail$detail] [json$json]",
SHOW_STR
"Policy Based Routing\n"
"PBR Map\n"
"PBR Map Name\n"
"Detailed information\n"
JSON_STR)
{
struct pbr_map_sequence *pbrms;
struct pbr_map *pbrm;
struct listnode *node;
char buf[PREFIX_STRLEN];
char rbuf[64];
RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) {
if (name && strcmp(name, pbrm->name) != 0)
continue;
vty_out(vty, " pbr-map %s valid: %d\n", pbrm->name,
pbrm->valid);
for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) {
if (pbrms->reason)
pbr_map_reason_string(pbrms->reason, rbuf,
sizeof(rbuf));
vty_out(vty,
" Seq: %u rule: %u Installed: %d(%u) Reason: %s\n",
pbrms->seqno, pbrms->ruleno, pbrms->installed,
pbrms->unique, pbrms->reason ? rbuf : "Valid");
if (pbrms->src)
vty_out(vty, "\tSRC Match: %s\n",
prefix2str(pbrms->src, buf,
sizeof(buf)));
if (pbrms->dst)
vty_out(vty, "\tDST Match: %s\n",
prefix2str(pbrms->dst, buf,
sizeof(buf)));
if (pbrms->nhgrp_name) {
vty_out(vty,
"\tNexthop-Group: %s(%u) Installed: %u(%d)\n",
pbrms->nhgrp_name,
pbr_nht_get_table(pbrms->nhgrp_name),
pbrms->nhs_installed,
pbr_nht_get_installed(
pbrms->nhgrp_name));
} else if (pbrms->nhg) {
vty_out(vty, " ");
nexthop_group_write_nexthop(
vty, pbrms->nhg->nexthop);
vty_out(vty,
"\tInstalled: %u(%d) Tableid: %d\n",
pbrms->nhs_installed,
pbr_nht_get_installed(
pbrms->internal_nhg_name),
pbr_nht_get_table(
pbrms->internal_nhg_name));
} else {
vty_out(vty,
"\tNexthop-Group: Unknown Installed: 0(0)\n");
}
}
}
return CMD_SUCCESS;
}
DEFPY(show_pbr_nexthop_group,
show_pbr_nexthop_group_cmd,
"show pbr nexthop-groups [WORD$word]",
SHOW_STR
"Policy Based Routing\n"
"Nexthop Groups\n"
"Optional Name of the nexthop group\n")
{
pbr_nht_show_nexthop_group(vty, word);
return CMD_SUCCESS;
}
DEFPY (show_pbr_interface,
show_pbr_interface_cmd,
"show pbr interface [NAME$name] [json$json]",
SHOW_STR
"Policy Based Routing\n"
"PBR Interface\n"
"PBR Interface Name\n"
JSON_STR)
{
struct interface *ifp;
struct vrf *vrf;
struct pbr_interface *pbr_ifp;
RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) {
FOR_ALL_INTERFACES(vrf, ifp) {
struct pbr_map *pbrm;
if (!ifp->info)
continue;
if (name && strcmp(ifp->name, name) != 0)
continue;
pbr_ifp = ifp->info;
if (strcmp(pbr_ifp->mapname, "") == 0)
continue;
pbrm = pbrm_find(pbr_ifp->mapname);
vty_out(vty, " %s(%d) with pbr-policy %s", ifp->name,
ifp->ifindex, pbr_ifp->mapname);
if (!pbrm)
vty_out(vty, " (map doesn't exist)");
vty_out(vty, "\n");
}
}
return CMD_SUCCESS;
}
static struct cmd_node interface_node = {
INTERFACE_NODE, "%s(config-if)# ", 1 /* vtysh ? yes */
};
static int pbr_interface_config_write(struct vty *vty)
{
struct interface *ifp;
struct vrf *vrf;
RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
FOR_ALL_INTERFACES (vrf, ifp) {
if (vrf->vrf_id == VRF_DEFAULT)
vty_frame(vty, "interface %s\n", ifp->name);
else
vty_frame(vty, "interface %s vrf %s\n",
ifp->name, vrf->name);
pbr_map_write_interfaces(vty, ifp);
vty_endframe(vty, "!\n");
}
}
return 1;
}
/* PBR map node structure. */
static struct cmd_node pbr_map_node = {PBRMAP_NODE, "%s(config-pbr-map)# ", 1};
static int pbr_vty_map_config_write_sequence(struct vty *vty,
struct pbr_map *pbrm,
struct pbr_map_sequence *pbrms)
{
char buff[PREFIX_STRLEN];
vty_out(vty, "pbr-map %s seq %u\n", pbrm->name, pbrms->seqno);
if (pbrms->src)
vty_out(vty, " match src-ip %s\n",
prefix2str(pbrms->src, buff, sizeof(buff)));
if (pbrms->dst)
vty_out(vty, " match dst-ip %s\n",
prefix2str(pbrms->dst, buff, sizeof(buff)));
if (pbrms->nhgrp_name)
vty_out(vty, " set nexthop-group %s\n", pbrms->nhgrp_name);
if (pbrms->nhg) {
vty_out(vty, " set ");
nexthop_group_write_nexthop(vty, pbrms->nhg->nexthop);
}
vty_out(vty, "!\n");
return 1;
}
static int pbr_vty_map_config_write(struct vty *vty)
{
struct pbr_map *pbrm;
pbr_nht_write_table_range(vty);
pbr_nht_write_rule_range(vty);
RB_FOREACH(pbrm, pbr_map_entry_head, &pbr_maps) {
struct pbr_map_sequence *pbrms;
struct listnode *node;
for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms))
pbr_vty_map_config_write_sequence(vty, pbrm, pbrms);
}
return 1;
}
void pbr_vty_init(void)
{
install_node(&interface_node,
pbr_interface_config_write);
if_cmd_init();
install_node(&pbr_map_node,
pbr_vty_map_config_write);
install_default(PBRMAP_NODE);
install_element(CONFIG_NODE, &pbr_map_cmd);
install_element(CONFIG_NODE, &no_pbr_map_cmd);
install_element(INTERFACE_NODE, &pbr_policy_cmd);
install_element(PBRMAP_NODE, &pbr_map_match_src_cmd);
install_element(PBRMAP_NODE, &pbr_map_match_dst_cmd);
install_element(PBRMAP_NODE, &pbr_map_nexthop_group_cmd);
install_element(PBRMAP_NODE, &pbr_map_nexthop_cmd);
install_element(VIEW_NODE, &show_pbr_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();
}

24
pbrd/pbr_vty.h Normal file
View File

@ -0,0 +1,24 @@
/*
* VTY library for PBR
* 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
*/
#ifndef __PBR_VTY_H__
#define __PBR_VTY_H__
extern void pbr_vty_init(void);
#endif

527
pbrd/pbr_zebra.c Normal file
View File

@ -0,0 +1,527 @@
/*
* Zebra connect 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 "command.h"
#include "network.h"
#include "prefix.h"
#include "routemap.h"
#include "table.h"
#include "stream.h"
#include "memory.h"
#include "zclient.h"
#include "filter.h"
#include "plist.h"
#include "log.h"
#include "nexthop.h"
#include "nexthop_group.h"
#include "pbr_nht.h"
#include "pbr_map.h"
#include "pbr_memory.h"
#include "pbr_zebra.h"
#include "pbr_debug.h"
DEFINE_MTYPE_STATIC(PBRD, PBR_INTERFACE, "PBR Interface")
/* 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;
zassert(ifp);
zassert(!ifp->info);
pbr_ifp = XCALLOC(MTYPE_PBR_INTERFACE, sizeof(*pbr_ifp));
if (!pbr_ifp) {
zlog_err("%s: PBR XCALLOC(%zu) failure", __PRETTY_FUNCTION__,
sizeof(*pbr_ifp));
return 0;
}
return (pbr_ifp);
}
/* Inteface addition message from zebra. */
static int interface_add(int command, struct zclient *zclient,
zebra_size_t length, vrf_id_t vrf_id)
{
struct interface *ifp;
ifp = zebra_interface_add_read(zclient->ibuf, vrf_id);
if (!ifp)
return 0;
if (!ifp->info) {
struct pbr_interface *pbr_ifp;
pbr_ifp = pbr_if_new(ifp);
ifp->info = pbr_ifp;
}
return 0;
}
static int interface_delete(int command, struct zclient *zclient,
zebra_size_t length, vrf_id_t vrf_id)
{
struct interface *ifp;
struct stream *s;
s = zclient->ibuf;
/* zebra_interface_state_read () updates interface structure in iflist
*/
ifp = zebra_interface_state_read(s, vrf_id);
if (ifp == NULL)
return 0;
if_set_index(ifp, IFINDEX_INTERNAL);
return 0;
}
static int interface_address_add(int command, struct zclient *zclient,
zebra_size_t length, vrf_id_t vrf_id)
{
zebra_interface_address_read(command, zclient->ibuf, vrf_id);
return 0;
}
static int interface_address_delete(int command, struct zclient *zclient,
zebra_size_t length, vrf_id_t vrf_id)
{
struct connected *c;
c = zebra_interface_address_read(command, zclient->ibuf, vrf_id);
if (!c)
return 0;
connected_free(c);
return 0;
}
static int interface_state_up(int command, struct zclient *zclient,
zebra_size_t length, vrf_id_t vrf_id)
{
zebra_interface_if_lookup(zclient->ibuf);
return 0;
}
static int interface_state_down(int command, struct zclient *zclient,
zebra_size_t length, vrf_id_t vrf_id)
{
zebra_interface_state_read(zclient->ibuf, vrf_id);
return 0;
}
static int route_notify_owner(int command, struct zclient *zclient,
zebra_size_t length, vrf_id_t vrf_id)
{
struct prefix p;
enum zapi_route_notify_owner note;
uint32_t table_id;
char buf[PREFIX_STRLEN];
prefix2str(&p, buf, sizeof(buf));
if (!zapi_route_notify_decode(zclient->ibuf, &p, &table_id, &note))
return -1;
switch (note) {
case ZAPI_ROUTE_FAIL_INSTALL:
DEBUGD(&pbr_dbg_zebra,
"%s: [%s] Route install failure for table: %u",
__PRETTY_FUNCTION__, buf, table_id);
break;
case ZAPI_ROUTE_BETTER_ADMIN_WON:
DEBUGD(&pbr_dbg_zebra,
"%s: [%s] Route better admin distance won for table: %u",
__PRETTY_FUNCTION__, buf, table_id);
break;
case ZAPI_ROUTE_INSTALLED:
DEBUGD(&pbr_dbg_zebra,
"%s: [%s] Route installed succeeded for table: %u",
__PRETTY_FUNCTION__, buf, table_id);
pbr_nht_route_installed_for_table(table_id);
break;
case ZAPI_ROUTE_REMOVED:
DEBUGD(&pbr_dbg_zebra,
"%s: [%s] Route Removed succeeded for table: %u",
__PRETTY_FUNCTION__, buf, table_id);
pbr_nht_route_removed_for_table(table_id);
break;
case ZAPI_ROUTE_REMOVE_FAIL:
DEBUGD(&pbr_dbg_zebra,
"%s: [%s] Route remove fail for table: %u",
__PRETTY_FUNCTION__, buf, table_id);
break;
}
return 0;
}
static int rule_notify_owner(int command, struct zclient *zclient,
zebra_size_t length, vrf_id_t vrf_id)
{
uint32_t seqno, priority, unique;
enum zapi_rule_notify_owner note;
struct pbr_map_sequence *pbrms;
ifindex_t ifi;
if (!zapi_rule_notify_decode(zclient->ibuf, &seqno, &priority, &unique,
&ifi, &note))
return -1;
pbrms = pbrms_lookup_unique(unique, ifi);
if (!pbrms) {
DEBUGD(&pbr_dbg_zebra,
"%s: Failure to lookup pbrms based upon %u",
__PRETTY_FUNCTION__, unique);
return 0;
}
switch (note) {
case ZAPI_RULE_FAIL_INSTALL:
DEBUGD(&pbr_dbg_zebra, "%s: Recieved RULE_FAIL_INSTALL",
__PRETTY_FUNCTION__);
pbrms->installed = false;
break;
case ZAPI_RULE_INSTALLED:
pbrms->installed = true;
DEBUGD(&pbr_dbg_zebra, "%s: Recived RULE_INSTALLED",
__PRETTY_FUNCTION__);
break;
case ZAPI_RULE_REMOVED:
DEBUGD(&pbr_dbg_zebra, "%s: Received RULE REMOVED",
__PRETTY_FUNCTION__);
break;
}
return 0;
}
static void zebra_connected(struct zclient *zclient)
{
zclient_send_reg_requests(zclient, VRF_DEFAULT);
}
static void route_add_helper(struct zapi_route *api, struct nexthop_group nhg,
uint8_t install_afi)
{
struct zapi_nexthop *api_nh;
struct nexthop *nhop;
int i;
api->prefix.family = install_afi;
i = 0;
for (ALL_NEXTHOPS(nhg, nhop)) {
api_nh = &api->nexthops[i];
api_nh->vrf_id = nhop->vrf_id;
api_nh->type = nhop->type;
switch (nhop->type) {
case NEXTHOP_TYPE_IPV4:
api_nh->gate.ipv4 = nhop->gate.ipv4;
break;
case NEXTHOP_TYPE_IPV4_IFINDEX:
api_nh->gate.ipv4 = nhop->gate.ipv4;
api_nh->ifindex = nhop->ifindex;
break;
case NEXTHOP_TYPE_IFINDEX:
api_nh->ifindex = nhop->ifindex;
break;
case NEXTHOP_TYPE_IPV6:
memcpy(&api_nh->gate.ipv6, &nhop->gate.ipv6, 16);
break;
case NEXTHOP_TYPE_IPV6_IFINDEX:
api_nh->ifindex = nhop->ifindex;
memcpy(&api_nh->gate.ipv6, &nhop->gate.ipv6, 16);
break;
case NEXTHOP_TYPE_BLACKHOLE:
api_nh->bh_type = nhop->bh_type;
break;
}
i++;
}
api->nexthop_num = i;
zclient_route_send(ZEBRA_ROUTE_ADD, zclient, api);
}
/*
* This function assumes a default route is being
* installed into the appropriate tableid
*/
void route_add(struct pbr_nexthop_group_cache *pnhgc, struct nexthop_group nhg,
afi_t install_afi)
{
struct zapi_route api;
memset(&api, 0, sizeof(api));
api.vrf_id = VRF_DEFAULT;
api.type = ZEBRA_ROUTE_PBR;
api.safi = SAFI_UNICAST;
/*
* Sending a default route
*/
api.tableid = pnhgc->table_id;
SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION);
SET_FLAG(api.message, ZAPI_MESSAGE_TABLEID);
SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
switch (install_afi) {
case AFI_MAX:
route_add_helper(&api, nhg, AF_INET);
route_add_helper(&api, nhg, AF_INET6);
break;
case AFI_IP:
route_add_helper(&api, nhg, AF_INET);
break;
case AFI_IP6:
route_add_helper(&api, nhg, AF_INET6);
break;
case AFI_L2VPN:
DEBUGD(&pbr_dbg_zebra,
"%s: Asked to install unsupported route type: L2VPN",
__PRETTY_FUNCTION__);
break;
}
}
/*
* This function assumes a default route is being
* removed from the appropriate tableid
*/
void route_delete(struct pbr_nexthop_group_cache *pnhgc, afi_t afi)
{
struct zapi_route api;
memset(&api, 0, sizeof(api));
api.vrf_id = VRF_DEFAULT;
api.type = ZEBRA_ROUTE_PBR;
api.safi = SAFI_UNICAST;
api.tableid = pnhgc->table_id;
SET_FLAG(api.message, ZAPI_MESSAGE_TABLEID);
switch (afi) {
case AFI_IP:
api.prefix.family = AF_INET;
zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api);
break;
case AFI_IP6:
api.prefix.family = AF_INET6;
zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api);
break;
case AFI_MAX:
api.prefix.family = AF_INET;
zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api);
api.prefix.family = AF_INET6;
zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api);
break;
case AFI_L2VPN:
DEBUGD(&pbr_dbg_zebra,
"%s: Asked to delete unsupported route type: L2VPN",
__PRETTY_FUNCTION__);
break;
}
}
static int pbr_zebra_nexthop_update(int command, struct zclient *zclient,
zebra_size_t length, vrf_id_t vrf_id)
{
struct zapi_route nhr;
char buf[PREFIX2STR_BUFFER];
uint32_t i;
zapi_nexthop_update_decode(zclient->ibuf, &nhr);
if (DEBUG_MODE_CHECK(&pbr_dbg_zebra, DEBUG_MODE_ALL)) {
DEBUGD(&pbr_dbg_zebra, "%s: Received Nexthop update: %s",
__PRETTY_FUNCTION__,
prefix2str(&nhr.prefix, buf, sizeof(buf)));
DEBUGD(&pbr_dbg_zebra, "%s: (\tNexthops(%u)",
__PRETTY_FUNCTION__, nhr.nexthop_num);
for (i = 0; i < nhr.nexthop_num; i++) {
DEBUGD(&pbr_dbg_zebra,
"%s: \tType: %d: vrf: %d, ifindex: %d gate: %s",
__PRETTY_FUNCTION__, nhr.nexthops[i].type,
nhr.nexthops[i].vrf_id, nhr.nexthops[i].ifindex,
inet_ntoa(nhr.nexthops[i].gate.ipv4));
}
}
pbr_nht_nexthop_update(&nhr);
return 1;
}
extern struct zebra_privs_t pbr_privs;
void pbr_zebra_init(void)
{
struct zclient_options opt = { .receive_notify = true };
zclient = zclient_new_notify(master, &opt);
zclient_init(zclient, ZEBRA_ROUTE_PBR, 0, &pbr_privs);
zclient->zebra_connected = zebra_connected;
zclient->interface_add = interface_add;
zclient->interface_delete = interface_delete;
zclient->interface_up = interface_state_up;
zclient->interface_down = interface_state_down;
zclient->interface_address_add = interface_address_add;
zclient->interface_address_delete = interface_address_delete;
zclient->route_notify_owner = route_notify_owner;
zclient->rule_notify_owner = rule_notify_owner;
zclient->nexthop_update = pbr_zebra_nexthop_update;
}
void pbr_send_rnh(struct nexthop *nhop, bool reg)
{
uint32_t command;
struct prefix p;
command = (reg) ?
ZEBRA_NEXTHOP_REGISTER : ZEBRA_NEXTHOP_UNREGISTER;
memset(&p, 0, sizeof(p));
switch (nhop->type) {
case NEXTHOP_TYPE_IFINDEX:
case NEXTHOP_TYPE_BLACKHOLE:
return;
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
p.family = AF_INET;
p.u.prefix4.s_addr = nhop->gate.ipv4.s_addr;
p.prefixlen = 32;
break;
case NEXTHOP_TYPE_IPV6:
case NEXTHOP_TYPE_IPV6_IFINDEX:
p.family = AF_INET6;
memcpy(&p.u.prefix6, &nhop->gate.ipv6, 16);
p.prefixlen = 128;
break;
}
if (zclient_send_rnh(zclient, command, &p,
false, nhop->vrf_id) < 0) {
zlog_warn("%s: Failure to send nexthop to zebra",
__PRETTY_FUNCTION__);
}
}
static void pbr_encode_pbr_map_sequence_prefix(struct stream *s,
struct prefix *p,
unsigned char family)
{
struct prefix any;
if (!p) {
memset(&any, 0, sizeof(any));
any.family = family;
p = &any;
}
stream_putc(s, p->family);
stream_putc(s, p->prefixlen);
stream_put(s, &p->u.prefix, prefix_blen(p));
}
static void pbr_encode_pbr_map_sequence(struct stream *s,
struct pbr_map_sequence *pbrms,
struct interface *ifp)
{
unsigned char family;
family = AF_INET;
if (pbrms->family)
family = pbrms->family;
stream_putl(s, pbrms->seqno);
stream_putl(s, pbrms->ruleno);
stream_putl(s, pbrms->unique);
pbr_encode_pbr_map_sequence_prefix(s, pbrms->src, family);
stream_putw(s, 0); /* src port */
pbr_encode_pbr_map_sequence_prefix(s, pbrms->dst, family);
stream_putw(s, 0); /* dst port */
if (pbrms->nhgrp_name)
stream_putl(s, pbr_nht_get_table(pbrms->nhgrp_name));
else if (pbrms->nhg)
stream_putl(s, pbr_nht_get_table(pbrms->internal_nhg_name));
stream_putl(s, ifp->ifindex);
}
void pbr_send_pbr_map(struct pbr_map_sequence *pbrms,
struct pbr_map_interface *pmi, bool install)
{
struct pbr_map *pbrm = pbrms->parent;
struct stream *s;
DEBUGD(&pbr_dbg_zebra, "%s: for %s %d", __PRETTY_FUNCTION__, pbrm->name,
install);
s = zclient->obuf;
stream_reset(s);
zclient_create_header(s,
install ? ZEBRA_RULE_ADD : ZEBRA_RULE_DELETE,
VRF_DEFAULT);
/*
* We are sending one item at a time at the moment
*/
stream_putl(s, 1);
DEBUGD(&pbr_dbg_zebra, "%s: \t%s %s %d %s %u",
__PRETTY_FUNCTION__, install ? "Installing" : "Deleting",
pbrm->name, install, pmi->ifp->name, pmi->delete);
pbr_encode_pbr_map_sequence(s, pbrms, pmi->ifp);
stream_putw_at(s, 0, stream_get_endp(s));
zclient_send_message(zclient);
}

42
pbrd/pbr_zebra.h Normal file
View File

@ -0,0 +1,42 @@
/*
* Zebra connect library for PBR
* 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
*/
#ifndef __PBR_ZEBRA_H__
#define __PBR_ZEBRA_H__
struct pbr_interface {
char mapname[100];
};
extern struct thread_master *master;
extern void pbr_zebra_init(void);
extern void route_add(struct pbr_nexthop_group_cache *pnhgc,
struct nexthop_group nhg, afi_t install_afi);
extern void route_delete(struct pbr_nexthop_group_cache *pnhgc,
afi_t install_afi);
extern void pbr_send_rnh(struct nexthop *nhop, bool reg);
extern void pbr_send_pbr_map(struct pbr_map_sequence *pbrms,
struct pbr_map_interface *pmi, bool install);
extern struct pbr_interface *pbr_if_new(struct interface *ifp);
#endif

3
pbrd/pbrd.conf.sample Normal file
View File

@ -0,0 +1,3 @@
!
!
log stdout

37
pbrd/subdir.am Normal file
View File

@ -0,0 +1,37 @@
#
# pbrd
#
if PBRD
noinst_LIBRARIES += pbrd/libpbr.a
sbin_PROGRAMS += pbrd/pbrd
dist_examples_DATA += pbrd/pbrd.conf.sample
endif
pbrd_libpbr_a_SOURCES = \
pbrd/pbr_zebra.c \
pbrd/pbr_vty.c \
pbrd/pbr_map.c \
pbrd/pbr_memory.c \
pbrd/pbr_nht.c \
pbrd/pbr_debug.c \
# end
noinst_HEADERS += \
pbrd/pbr_map.h \
pbrd/pbr_memory.h \
pbrd/pbr_nht.h \
pbrd/pbr_vty.h \
pbrd/pbr_zebra.h \
pbrd/pbr_debug.h \
# end
pbrd/pbr_vty_clippy.c: $(CLIPPY_DEPS)
pbrd/pbr_vty.$(OBJEXT): pbrd/pbr_vty_clippy.c
pbrd/pbr_debug_clippy.c: $(CLIPPY_DEPS)
pbrd/pbr_debug.$(OBJEXT): pbrd/pbr_debug_clippy.c
pbrd_pbrd_SOURCES = pbrd/pbr_main.c
pbrd_pbrd_LDADD = pbrd/libpbr.a lib/libfrr.la @LIBCAP@

View File

@ -50,6 +50,7 @@ nhrpd=no
eigrpd=no
babeld=no
sharpd=no
pbrd=no
#
# Command line options for the daemons
#
@ -66,6 +67,7 @@ nhrpd_options=("-A 127.0.0.1")
eigrpd_options=("-A 127.0.0.1")
babeld_options=("-A 127.0.0.1")
sharpd_options=("-A 127.0.0.1")
pbrd_options=("-A 127.0.0.1")
#
# If the vtysh_enable is yes, then the unified config is read

View File

@ -33,7 +33,7 @@ V_PATH=/var/run/frr
# Local Daemon selection may be done by using /etc/frr/daemons.
# See /usr/share/doc/frr/README.Debian.gz for further information.
# Keep zebra first and do not list watchfrr!
DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd pimd ldpd nhrpd eigrpd babeld"
DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd pimd pbrd ldpd nhrpd eigrpd babeld"
MAX_INSTANCES=5
RELOAD_SCRIPT=/usr/lib/frr/frr-reload.py

View File

@ -27,6 +27,7 @@
%{!?with_bgp_vnc: %global with_bgp_vnc 0 }
%{!?with_pimd: %global with_pimd 1 }
%{!?with_rpki: %global with_rpki 0 }
%{!?with_pbrd: %global with_pbrd 1 }
# path defines
%define _sysconfdir /etc/frr
@ -86,7 +87,7 @@
%{!?frr_gid: %global frr_gid 92 }
%{!?vty_gid: %global vty_gid 85 }
%define daemon_list zebra ripd ospfd bgpd isisd ripngd ospf6d
%define daemon_list zebra ripd ospfd bgpd isisd ripngd ospf6d pbrd
%if %{with_ldpd}
%define daemon_ldpd ldpd
@ -100,6 +101,12 @@
%define daemon_pimd ""
%endif
%if %{with_pbrd}
%define daemon_pbrd pbrd
%else
%define daemon_pbrd ""
%endif
%if %{with_nhrpd}
%define daemon_nhrpd nhrpd
%else
@ -124,7 +131,7 @@
%define daemon_watchfrr ""
%endif
%define all_daemons %{daemon_list} %{daemon_ldpd} %{daemon_pimd} %{daemon_nhrpd} %{daemon_eigrpd} %{daemon_babeld} %{daemon_watchfrr}
%define all_daemons %{daemon_list} %{daemon_ldpd} %{daemon_pimd} %{daemon_nhrpd} %{daemon_eigrpd} %{daemon_babeld} %{daemon_watchfrr} %{daemon_pbrd}
# allow build dir to be kept
%{!?keep_build: %global keep_build 0 }
@ -182,7 +189,7 @@ protocol. It takes multi-server and multi-thread approach to resolve
the current complexity of the Internet.
FRRouting supports BGP4, OSPFv2, OSPFv3, ISIS, RIP, RIPng, PIM, LDP
NHRP, Babel and EIGRP.
NHRP, Babel, PBR and EIGRP.
FRRouting is a fork of Quagga.
@ -266,6 +273,11 @@ developing OSPF-API and frr applications.
%else
--disable-pimd \
%endif
%if %{with_pbrd}
--enable-pbrd \
%else
--disable-pbrd \
%endif
%if %{with_nhrpd}
--enable-nhrpd \
%else
@ -414,6 +426,9 @@ zebra_spec_add_service nhrpd 2610/tcp "NHRPd vty"
%if %{with_pimd}
zebra_spec_add_service pimd 2611/tcp "PIMd vty"
%endif
%if %{with_pbrd}
zebra_spec_add_service pbrd 2615/tcp "PBRd vty"
%endif
%if %{with_ldpd}
zebra_spec_add_service ldpd 2612/tcp "LDPd vty"
%endif
@ -547,6 +562,9 @@ rm -rf %{buildroot}
%if %{with_pimd}
%{_sbindir}/pimd
%endif
%if %{with_pbrd}
%{_sbindir}/pbrd
%endif
%{_sbindir}/isisd
%if %{with_ldpd}
%{_sbindir}/ldpd

View File

@ -34,3 +34,4 @@ nhrpd=no
eigrpd=no
babeld=no
sharpd=no
pbrd=no

View File

@ -17,6 +17,7 @@ nhrpd_options=" --daemon -A 127.0.0.1"
eigrpd_options=" --daemon -A 127.0.0.1"
babeld_options=" --daemon -A 127.0.0.1"
sharpd_options=" --daemon -A 127.0.0.1"
pbrd_options=" --daemon -A 127.0.0.1"
# The list of daemons to watch is automatically generated by the init script.
watchfrr_enable=yes

View File

@ -21,7 +21,7 @@ V_PATH=/var/run/frr
# Local Daemon selection may be done by using /etc/frr/daemons.
# See /usr/share/doc/frr/README.Debian.gz for further information.
# Keep zebra first and do not list watchfrr!
DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd"
DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd pbrd"
MAX_INSTANCES=5
RELOAD_SCRIPT=/usr/lib/frr/frr-reload.py

View File

@ -141,6 +141,11 @@ if SNMP
vtysh_scan += $(top_srcdir)/lib/agentx.c
endif
if PBRD
vtysh_scan += $(top_srcdir)/pbrd/pbr_vty.c
vtysh_scan += $(top_srcdir)/pbrd/pbr_debug.c
endif
vtysh_cmd_FILES = $(vtysh_scan) \
$(top_srcdir)/lib/keychain.c $(top_srcdir)/lib/routemap.c \
$(top_srcdir)/lib/filter.c $(top_srcdir)/lib/plist.c \
@ -148,6 +153,7 @@ vtysh_cmd_FILES = $(vtysh_scan) \
$(top_srcdir)/lib/vrf.c \
$(top_srcdir)/lib/vty.c $(top_srcdir)/zebra/debug.c \
$(top_srcdir)/lib/logicalrouter.c \
$(top_srcdir)/lib/nexthop_group.c \
$(top_srcdir)/zebra/interface.c \
$(top_srcdir)/zebra/irdp_interface.c \
$(top_srcdir)/zebra/rtadv.c $(top_srcdir)/zebra/zebra_vty.c \

View File

@ -99,6 +99,9 @@ foreach (@ARGV) {
elsif ($file =~ /lib\/ns\.c$/) {
$protocol = "VTYSH_ZEBRA";
}
elsif ($file =~ /lib\/nexthop_group\.c$/) {
$protocol = "VTYSH_PBRD";
}
elsif ($file =~ /lib\/plist\.c$/) {
if ($defun_array[1] =~ m/ipv6/) {
$protocol = "VTYSH_RIPNGD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ZEBRA|VTYSH_BABELD|VTYSH_ISISD";

View File

@ -79,6 +79,7 @@ struct vtysh_client vtysh_client[] = {
{.fd = -1, .name = "babeld", .flag = VTYSH_BABELD, .next = NULL},
{.fd = -1, .name = "sharpd", .flag = VTYSH_SHARPD, .next = NULL},
{.fd = -1, .name = "watchfrr", .flag = VTYSH_WATCHFRR, .next = NULL},
{.fd = -1, .name = "pbrd", .flag = VTYSH_PBRD, .next = NULL},
};
enum vtysh_write_integrated vtysh_write_integrated =
@ -1022,8 +1023,15 @@ static struct cmd_node vrf_node = {
VRF_NODE, "%s(config-vrf)# ",
};
static struct cmd_node nh_group_node = {
NH_GROUP_NODE,
"%s(config-nh-group)# ",
};
static struct cmd_node rmap_node = {RMAP_NODE, "%s(config-route-map)# "};
static struct cmd_node pbr_map_node = {PBRMAP_NODE, "%s(config-pbr-map)# "};
static struct cmd_node zebra_node = {ZEBRA_NODE, "%s(config-router)# "};
static struct cmd_node bgp_vpnv4_node = {BGP_VPNV4_NODE,
@ -1529,6 +1537,24 @@ DEFUNSH(VTYSH_RMAP, vtysh_route_map, vtysh_route_map_cmd,
return CMD_SUCCESS;
}
DEFUNSH(VTYSH_PBRD, vtysh_pbr_map, vtysh_pbr_map_cmd,
"pbr-map NAME seq (1-1000)",
"Create pbr-map or enter pbr-map command mode\n"
"The name of the PBR MAP\n"
"Sequence to insert to/delete from existing pbr-map entry\n"
"Sequence number\n")
{
vty->node = PBRMAP_NODE;
return CMD_SUCCESS;
}
DEFSH(VTYSH_PBRD, vtysh_no_pbr_map_cmd, "no pbr-map WORD [seq (1-65535)]",
NO_STR
"Delete pbr-map\n"
"The name of the PBR MAP\n"
"Sequence to delete from existing pbr-map entry\n"
"Sequence number\n")
DEFUNSH(VTYSH_ALL, vtysh_line_vty, vtysh_line_vty_cmd, "line vty",
"Configure a terminal line\n"
"Virtual terminal\n")
@ -1588,6 +1614,7 @@ static int vtysh_exit(struct vty *vty)
case LDP_L2VPN_NODE:
case ISIS_NODE:
case RMAP_NODE:
case PBRMAP_NODE:
case VTY_NODE:
case KEYCHAIN_NODE:
vtysh_execute("end");
@ -1734,6 +1761,18 @@ DEFUNSH(VTYSH_RMAP, vtysh_quit_rmap, vtysh_quit_rmap_cmd, "quit",
return vtysh_exit_rmap(self, vty, argc, argv);
}
DEFUNSH(VTYSH_PBRD, vtysh_exit_pbr_map, vtysh_exit_pbr_map_cmd, "exit",
"Exit current mode and down to previous mode\n")
{
return vtysh_exit(vty);
}
DEFUNSH(VTYSH_PBRD, vtysh_quit_pbr_map, vtysh_quit_pbr_map_cmd, "quit",
"Exit current mode and down to previous mode\n")
{
return vtysh_exit_rmap(self, vty, argc, argv);
}
DEFUNSH(VTYSH_BGPD, vtysh_exit_bgpd, vtysh_exit_bgpd_cmd, "exit",
"Exit current mode and down to previous mode\n")
{
@ -1877,6 +1916,20 @@ DEFSH(VTYSH_ZEBRA, vtysh_no_logicalrouter_cmd,
"The Name Space\n"
"The file name in " NS_RUN_DIR ", or a full pathname\n")
DEFUNSH(VTYSH_PBRD, vtysh_nexthop_group, vtysh_nexthop_group_cmd,
"nexthop-group NAME",
"Nexthop Group configuration\n"
"Name of the Nexthop Group\n")
{
vty->node = NH_GROUP_NODE;
return CMD_SUCCESS;
}
DEFSH(VTYSH_PBRD, vtysh_no_nexthop_group_cmd, "no nexthop-group NAME",
NO_STR
"Nexthop Group Configuration\n"
"Name of the Nexthop Group\n")
DEFUNSH(VTYSH_VRF, vtysh_vrf, vtysh_vrf_cmd, "vrf NAME",
"Select a VRF to configure\n"
"VRF's name\n")
@ -1915,6 +1968,18 @@ DEFUNSH(VTYSH_VRF, vtysh_quit_vrf, vtysh_quit_vrf_cmd, "quit",
return vtysh_exit_vrf(self, vty, argc, argv);
}
DEFUNSH(VTYSH_PBRD, vtysh_exit_nexthop_group, vtysh_exit_nexthop_group_cmd,
"exit", "Exit current mode and down to previous mode\n")
{
return vtysh_exit(vty);
}
DEFUNSH(VTYSH_VRF, vtysh_quit_nexthop_group, vtysh_quit_nexthop_group_cmd,
"quit", "Exit current mode and down to previous mode\n")
{
return vtysh_exit_nexthop_group(self, vty, argc, argv);
}
/* TODO Implement interface description commands in ripngd, ospf6d
* and isisd. */
DEFSH(VTYSH_ZEBRA | VTYSH_RIPD | VTYSH_OSPFD | VTYSH_EIGRPD,
@ -1990,7 +2055,7 @@ DEFUN (vtysh_show_work_queues,
DEFUN (vtysh_show_work_queues_daemon,
vtysh_show_work_queues_daemon_cmd,
"show work-queues <zebra|ripd|ripngd|ospfd|ospf6d|bgpd|isisd>",
"show work-queues <zebra|ripd|ripngd|ospfd|ospf6d|bgpd|isisd|pbrd>",
SHOW_STR
"Work Queue information\n"
"For the zebra daemon\n"
@ -1999,7 +2064,8 @@ DEFUN (vtysh_show_work_queues_daemon,
"For the ospf daemon\n"
"For the ospfv6 daemon\n"
"For the bgp daemon\n"
"For the isis daemon\n")
"For the isis daemon\n"
"For the pbr daemon\n")
{
int idx_protocol = 2;
unsigned int i;
@ -3155,7 +3221,9 @@ void vtysh_init_vty(void)
install_node(&link_params_node, NULL);
install_node(&logicalrouter_node, NULL);
install_node(&vrf_node, NULL);
install_node(&nh_group_node, NULL);
install_node(&rmap_node, NULL);
install_node(&pbr_map_node, NULL);
install_node(&zebra_node, NULL);
install_node(&bgp_vpnv4_node, NULL);
install_node(&bgp_vpnv6_node, NULL);
@ -3284,6 +3352,8 @@ void vtysh_init_vty(void)
install_element(KEYCHAIN_KEY_NODE, &vtysh_quit_ripd_cmd);
install_element(RMAP_NODE, &vtysh_exit_rmap_cmd);
install_element(RMAP_NODE, &vtysh_quit_rmap_cmd);
install_element(PBRMAP_NODE, &vtysh_exit_pbr_map_cmd);
install_element(PBRMAP_NODE, &vtysh_quit_pbr_map_cmd);
install_element(VTY_NODE, &vtysh_exit_line_vty_cmd);
install_element(VTY_NODE, &vtysh_quit_line_vty_cmd);
@ -3324,6 +3394,7 @@ void vtysh_init_vty(void)
install_element(KEYCHAIN_NODE, &vtysh_end_all_cmd);
install_element(KEYCHAIN_KEY_NODE, &vtysh_end_all_cmd);
install_element(RMAP_NODE, &vtysh_end_all_cmd);
install_element(PBRMAP_NODE, &vtysh_end_all_cmd);
install_element(VTY_NODE, &vtysh_end_all_cmd);
install_element(INTERFACE_NODE, &vtysh_interface_desc_cmd);
@ -3346,6 +3417,11 @@ void vtysh_init_vty(void)
install_element(LOGICALROUTER_NODE, &vtysh_exit_logicalrouter_cmd);
install_element(LOGICALROUTER_NODE, &vtysh_quit_logicalrouter_cmd);
install_element(CONFIG_NODE, &vtysh_nexthop_group_cmd);
install_element(NH_GROUP_NODE, &vtysh_end_all_cmd);
install_element(NH_GROUP_NODE, &vtysh_exit_nexthop_group_cmd);
install_element(NH_GROUP_NODE, &vtysh_quit_nexthop_group_cmd);
install_element(VRF_NODE, &vtysh_end_all_cmd);
install_element(VRF_NODE, &vtysh_exit_vrf_cmd);
install_element(VRF_NODE, &vtysh_quit_vrf_cmd);
@ -3419,6 +3495,8 @@ void vtysh_init_vty(void)
install_element(CONFIG_NODE, &key_chain_cmd);
install_element(CONFIG_NODE, &vtysh_route_map_cmd);
install_element(CONFIG_NODE, &vtysh_pbr_map_cmd);
install_element(CONFIG_NODE, &vtysh_no_pbr_map_cmd);
install_element(CONFIG_NODE, &vtysh_line_vty_cmd);
install_element(KEYCHAIN_NODE, &key_cmd);
install_element(KEYCHAIN_NODE, &key_chain_cmd);
@ -3435,6 +3513,7 @@ void vtysh_init_vty(void)
install_element(CONFIG_NODE, &vtysh_vrf_cmd);
install_element(CONFIG_NODE, &vtysh_no_vrf_cmd);
install_element(CONFIG_NODE, &vtysh_no_nexthop_group_cmd);
/* "write terminal" command. */
install_element(ENABLE_NODE, &vtysh_write_terminal_cmd);

View File

@ -24,29 +24,30 @@
#include "memory.h"
DECLARE_MGROUP(MVTYSH)
#define VTYSH_ZEBRA 0x01
#define VTYSH_RIPD 0x02
#define VTYSH_RIPNGD 0x04
#define VTYSH_OSPFD 0x08
#define VTYSH_OSPF6D 0x10
#define VTYSH_BGPD 0x20
#define VTYSH_ISISD 0x40
#define VTYSH_PIMD 0x100
#define VTYSH_LDPD 0x200
#define VTYSH_WATCHFRR 0x400
#define VTYSH_NHRPD 0x800
#define VTYSH_EIGRPD 0x1000
#define VTYSH_BABELD 0x2000
#define VTYSH_SHARPD 0x4000
#define VTYSH_ZEBRA 0x0001
#define VTYSH_RIPD 0x0002
#define VTYSH_RIPNGD 0x0004
#define VTYSH_OSPFD 0x0008
#define VTYSH_OSPF6D 0x0010
#define VTYSH_BGPD 0x0020
#define VTYSH_ISISD 0x0040
#define VTYSH_PIMD 0x0080
#define VTYSH_LDPD 0x0100
#define VTYSH_WATCHFRR 0x0200
#define VTYSH_NHRPD 0x0400
#define VTYSH_EIGRPD 0x0800
#define VTYSH_BABELD 0x1000
#define VTYSH_SHARPD 0x2000
#define VTYSH_PBRD 0x4000
/* commands in REALLYALL are crucial to correct vtysh operation */
#define VTYSH_REALLYALL ~0U
/* watchfrr is not in ALL since library CLI functions should not be
* run on it (logging & co. should stay in a fixed/frozen config, and
* things like prefix lists are not even initialised) */
#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_SHARPD
#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_SHARPD|VTYSH_PBRD
#define VTYSH_RMAP VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_EIGRPD|VTYSH_SHARPD
#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD
#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_PBRD
#define VTYSH_NS VTYSH_ZEBRA
#define VTYSH_VRF VTYSH_ZEBRA|VTYSH_PIMD

View File

@ -205,6 +205,9 @@ void vtysh_config_parse_line(void *arg, const char *line)
config = config_get(LOGICALROUTER_NODE, line);
else if (strncmp(line, "vrf", strlen("vrf")) == 0)
config = config_get(VRF_NODE, line);
else if (strncmp(line, "nexthop-group", strlen("nexthop-group"))
== 0)
config = config_get(NH_GROUP_NODE, line);
else if (strncmp(line, "router-id", strlen("router-id")) == 0)
config = config_get(ZEBRA_NODE, line);
else if (strncmp(line, "router rip", strlen("router rip")) == 0)
@ -235,6 +238,8 @@ void vtysh_config_parse_line(void *arg, const char *line)
config = config_get(ISIS_NODE, line);
else if (strncmp(line, "route-map", strlen("route-map")) == 0)
config = config_get(RMAP_NODE, line);
else if (strncmp(line, "pbr-map", strlen("pbr-map")) == 0)
config = config_get(PBRMAP_NODE, line);
else if (strncmp(line, "access-list", strlen("access-list"))
== 0)
config = config_get(ACCESS_NODE, line);

View File

@ -77,9 +77,6 @@ static int netlink_rule_update(int cmd, struct zebra_pbr_rule *rule)
req.frh.family = family;
req.frh.action = FR_ACT_TO_TBL;
if (cmd == RTM_NEWRULE)
req.n.nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
/* rule's pref # */
addattr32(&req.n, sizeof(req), FRA_PRIORITY, rule->priority);

View File

@ -514,6 +514,59 @@ static void zebra_rnh_notify_protocol_clients(vrf_id_t vrfid, int family,
}
}
static void zebra_rnh_process_pbr_tables(int family,
struct route_node *nrn,
struct rnh *rnh,
struct route_node *prn,
struct route_entry *re)
{
struct zebra_ns_table *znst;
struct route_entry *o_re;
struct route_node *o_rn;
struct listnode *node;
struct zserv *client;
struct zebra_ns *zns;
afi_t afi = AFI_IP;
if (family == AF_INET6)
afi = AFI_IP6;
/*
* We are only concerned about nexthops that change for
* anyone using PBR
*/
for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client)) {
if (client->proto == ZEBRA_ROUTE_PBR)
break;
}
if (!client)
return;
zns = zebra_ns_lookup(NS_DEFAULT);
RB_FOREACH (znst, zebra_ns_table_head, &zns->ns_tables) {
if (afi != znst->afi)
continue;
for (o_rn = route_top(znst->table);
o_rn; o_rn = srcdest_route_next(o_rn)) {
RNODE_FOREACH_RE (o_rn, o_re) {
if (o_re->type == ZEBRA_ROUTE_PBR)
break;
}
/*
* If we have a PBR route and a nexthop changes
* just rethink it. Yes this is a hammer, but
* a small one
*/
if (o_re)
rib_queue_add(o_rn);
}
}
}
static void zebra_rnh_process_static_routes(vrf_id_t vrfid, int family,
struct route_node *nrn,
struct rnh *rnh,
@ -752,6 +805,9 @@ static void zebra_rnh_eval_nexthop_entry(vrf_id_t vrfid, int family, int force,
zebra_rnh_process_static_routes(vrfid, family, nrn, rnh, prn,
rnh->state);
zebra_rnh_process_pbr_tables(family, nrn, rnh, prn,
rnh->state);
/* Process pseudowires attached to this nexthop */
zebra_rnh_process_pseudowires(vrfid, rnh);
}