summaryrefslogtreecommitdiff
path: root/pbrd
diff options
context:
space:
mode:
Diffstat (limited to 'pbrd')
-rw-r--r--pbrd/pbr_main.c28
-rw-r--r--pbrd/pbr_map.c64
-rw-r--r--pbrd/pbr_map.h69
-rw-r--r--pbrd/pbr_nht.c15
-rw-r--r--pbrd/pbr_nht.h2
-rw-r--r--pbrd/pbr_vty.c1474
-rw-r--r--pbrd/pbr_zebra.c232
-rw-r--r--pbrd/pbr_zebra.h1
8 files changed, 1413 insertions, 472 deletions
diff --git a/pbrd/pbr_main.c b/pbrd/pbr_main.c
index c4708d3f08..6695b537a8 100644
--- a/pbrd/pbr_main.c
+++ b/pbrd/pbr_main.c
@@ -71,6 +71,8 @@ static void sigint(void)
pbr_vrf_terminate();
+ pbr_zebra_destroy();
+
frr_fini();
exit(0);
@@ -101,26 +103,26 @@ struct frr_signal_t pbr_signals[] = {
},
};
-#define PBR_VTY_PORT 2615
-
static const struct frr_yang_module_info *const pbrd_yang_modules[] = {
&frr_filter_info,
&frr_interface_info,
&frr_vrf_info,
};
-FRR_DAEMON_INFO(pbrd, PBR, .vty_port = PBR_VTY_PORT,
-
- .proghelp = "Implementation of PBR.",
+/* clang-format off */
+FRR_DAEMON_INFO(pbrd, PBR,
+ .vty_port = PBR_VTY_PORT,
+ .proghelp = "Implementation of PBR.",
- .signals = pbr_signals,
- .n_signals = array_size(pbr_signals),
+ .signals = pbr_signals,
+ .n_signals = array_size(pbr_signals),
- .privs = &pbr_privs,
+ .privs = &pbr_privs,
- .yang_modules = pbrd_yang_modules,
- .n_yang_modules = array_size(pbrd_yang_modules),
+ .yang_modules = pbrd_yang_modules,
+ .n_yang_modules = array_size(pbrd_yang_modules),
);
+/* clang-format on */
int main(int argc, char **argv, char **envp)
{
@@ -158,8 +160,10 @@ int main(int argc, char **argv, char **envp)
access_list_init();
pbr_nht_init();
pbr_map_init();
- if_zapi_callbacks(pbr_ifp_create, pbr_ifp_up,
- pbr_ifp_down, pbr_ifp_destroy);
+ hook_register_prio(if_real, 0, pbr_ifp_create);
+ hook_register_prio(if_up, 0, pbr_ifp_up);
+ hook_register_prio(if_down, 0, pbr_ifp_down);
+ hook_register_prio(if_unreal, 0, pbr_ifp_destroy);
pbr_zebra_init();
pbr_vrf_init();
pbr_vty_init();
diff --git a/pbrd/pbr_map.c b/pbrd/pbr_map.c
index 16cea3b4cd..8f7a46377c 100644
--- a/pbrd/pbr_map.c
+++ b/pbrd/pbr_map.c
@@ -3,6 +3,9 @@
* PBR-map Code
* Copyright (C) 2018 Cumulus Networks, Inc.
* Donald Sharp
+ * Portions:
+ * Copyright (c) 2021 The MITRE Corporation.
+ * Copyright (c) 2023 LabN Consulting, L.L.C.
*/
#include <zebra.h>
@@ -16,6 +19,7 @@
#include "memory.h"
#include "log.h"
#include "vty.h"
+#include "pbr.h"
#include "pbr_nht.h"
#include "pbr_map.h"
@@ -58,7 +62,7 @@ static int pbr_map_sequence_compare(const struct pbr_map_sequence *pbrms1,
return 1;
}
-static void pbr_map_sequence_delete(struct pbr_map_sequence *pbrms)
+void pbr_map_sequence_delete(struct pbr_map_sequence *pbrms)
{
XFREE(MTYPE_TMP, pbrms->internal_nhg_name);
@@ -353,6 +357,11 @@ extern void pbr_map_delete(struct pbr_map_sequence *pbrms)
if (pbrms->nhg)
pbr_nht_delete_individual_nexthop(pbrms);
+ if (pbrms->nhgrp_name)
+ XFREE(MTYPE_TMP, pbrms->nhgrp_name);
+
+ prefix_free(&pbrms->dst);
+
listnode_delete(pbrm->seqnumbers, pbrms);
if (pbrm->seqnumbers->count == 0) {
@@ -486,9 +495,9 @@ uint8_t pbr_map_decode_dscp_enum(const char *name)
struct pbr_map_sequence *pbrms_get(const char *name, uint32_t seqno)
{
- struct pbr_map *pbrm;
- struct pbr_map_sequence *pbrms;
- struct listnode *node;
+ struct pbr_map *pbrm = NULL;
+ struct pbr_map_sequence *pbrms = NULL;
+ struct listnode *node = NULL;
pbrm = pbrm_find(name);
if (!pbrm) {
@@ -526,10 +535,6 @@ struct pbr_map_sequence *pbrms_get(const char *name, uint32_t seqno)
pbrms->ruleno = pbr_nht_get_next_rule(seqno);
pbrms->parent = pbrm;
- pbrms->action_vlan_id = 0;
- pbrms->action_vlan_flags = 0;
- pbrms->action_pcp = 0;
-
pbrms->action_queue_id = PBR_MAP_UNDEFINED_QUEUE_ID;
pbrms->reason =
@@ -594,11 +599,42 @@ pbr_map_sequence_check_nexthops_valid(struct pbr_map_sequence *pbrms)
static void pbr_map_sequence_check_not_empty(struct pbr_map_sequence *pbrms)
{
- if (!pbrms->src && !pbrms->dst && !pbrms->mark && !pbrms->dsfield
- && !pbrms->action_vlan_id && !pbrms->action_vlan_flags
- && !pbrms->action_pcp
- && pbrms->action_queue_id == PBR_MAP_UNDEFINED_QUEUE_ID)
+ /* clang-format off */
+ if (
+ !CHECK_FLAG(pbrms->filter_bm, (
+ PBR_FILTER_SRC_IP |
+ PBR_FILTER_DST_IP |
+ PBR_FILTER_SRC_PORT |
+ PBR_FILTER_DST_PORT |
+
+ PBR_FILTER_IP_PROTOCOL |
+ PBR_FILTER_DSCP |
+ PBR_FILTER_ECN |
+
+ PBR_FILTER_FWMARK |
+ PBR_FILTER_PCP |
+ PBR_FILTER_VLAN_ID |
+ PBR_FILTER_VLAN_FLAGS
+ )) &&
+ !CHECK_FLAG(pbrms->action_bm, (
+ PBR_ACTION_SRC_IP |
+ PBR_ACTION_DST_IP |
+ PBR_ACTION_SRC_PORT |
+ PBR_ACTION_DST_PORT |
+
+ PBR_ACTION_DSCP |
+ PBR_ACTION_ECN |
+
+ PBR_ACTION_PCP |
+ PBR_ACTION_VLAN_ID |
+ PBR_ACTION_VLAN_STRIP_INNER_ANY |
+
+ PBR_ACTION_QUEUE_ID
+ ))
+ ) {
pbrms->reason |= PBR_MAP_INVALID_EMPTY;
+ }
+ /* clang-format on */
}
static void pbr_map_sequence_check_vlan_actions(struct pbr_map_sequence *pbrms)
@@ -611,7 +647,8 @@ static void pbr_map_sequence_check_vlan_actions(struct pbr_map_sequence *pbrms)
* The strip vlan action removes any inner tag, so it is invalid to
* specify both a set and strip action.
*/
- if ((pbrms->action_vlan_id != 0) && (pbrms->action_vlan_flags != 0))
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_ID) &&
+ (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_STRIP_INNER_ANY)))
pbrms->reason |= PBR_MAP_INVALID_SET_STRIP_VLAN;
}
@@ -734,7 +771,6 @@ void pbr_map_policy_delete(struct pbr_map *pbrm, struct pbr_map_interface *pmi)
struct pbr_map_sequence *pbrms;
bool sent = false;
-
for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms))
if (pbr_send_pbr_map(pbrms, pmi, false, true))
sent = true; /* rule removal sent to zebra */
diff --git a/pbrd/pbr_map.h b/pbrd/pbr_map.h
index c9da431b38..9fb674bd6e 100644
--- a/pbrd/pbr_map.h
+++ b/pbrd/pbr_map.h
@@ -3,6 +3,9 @@
* PBR-map Header
* Copyright (C) 2018 Cumulus Networks, Inc.
* Donald Sharp
+ * Portions:
+ * Copyright (c) 2023 LabN Consulting, L.L.C.
+ * Copyright (c) 2021 The MITRE Corporation
*/
#ifndef __PBR_MAP_H__
#define __PBR_MAP_H__
@@ -53,6 +56,14 @@ struct pbr_map_interface {
bool delete;
};
+enum pbr_forwarding_type {
+ PBR_FT_UNSPEC = 0,
+ PBR_FT_VRF_UNCHANGED,
+ PBR_FT_SETVRF,
+ PBR_FT_NEXTHOP_GROUP,
+ PBR_FT_NEXTHOP_SINGLE,
+};
+
struct pbr_map_sequence {
struct pbr_map *parent;
@@ -71,40 +82,64 @@ struct pbr_map_sequence {
*/
uint32_t ruleno;
+
+ /*****************************************************************
+ * Filter fields
+ * gpz 230716: I hope to replace all of the filter fields with
+ * 'struct pbr_filter' from lib/pbr.h.
+ *****************************************************************/
+
/*
- * src and dst ports
+ * same bit definitions as in lib/pbr.h
*/
+ uint32_t filter_bm;
+
+ /* Family of the src/dst. Needed when deleting since we clear them */
+ unsigned char family;
+
+ /* src and dst IP addresses */
+ struct prefix *src;
+ struct prefix *dst;
+
+ /* src and dst UDP/TCP ports */
uint16_t src_prt;
uint16_t dst_prt;
- /*
- * The ip protocol we want to match on
- */
uint8_t ip_proto;
- /*
- * Our policy Catchers
- */
- struct prefix *src;
- struct prefix *dst;
+ uint8_t match_pcp;
+ uint16_t match_vlan_id; /* bits defined in lib/pbr.h */
+
+ uint16_t match_vlan_flags;
+
uint8_t dsfield;
uint32_t mark;
+ /*****************************************************************
+ * Action fields
+ *****************************************************************/
+
/*
- * Actions
+ * same bit definitions as in lib/pbr.h
*/
+ uint32_t action_bm;
+
+ union sockunion action_src;
+ union sockunion action_dst;
+
+ uint16_t action_src_port;
+ uint16_t action_dst_port;
+
+ uint8_t action_dscp;
+ uint8_t action_ecn;
+
uint8_t action_pcp;
uint8_t action_vlan_id;
-#define PBR_MAP_STRIP_INNER_ANY (1 << 0)
- uint8_t action_vlan_flags;
#define PBR_MAP_UNDEFINED_QUEUE_ID 0
uint32_t action_queue_id;
- /*
- * Family of the src/dst. Needed when deleting since we clear them
- */
- unsigned char family;
+ enum pbr_forwarding_type forwarding_type;
/*
* Use interface's vrf.
@@ -216,6 +251,8 @@ extern void pbr_map_policy_install(const char *name);
extern void pbr_map_policy_delete(struct pbr_map *pbrm,
struct pbr_map_interface *pmi);
+extern void pbr_map_sequence_delete(struct pbr_map_sequence *pbrms);
+
extern void pbr_map_check_vrf_nh_group_change(const char *nh_group,
struct pbr_vrf *pbr_vrf,
uint32_t old_vrf_id);
diff --git a/pbrd/pbr_nht.c b/pbrd/pbr_nht.c
index 405a2d6588..ff252f8505 100644
--- a/pbrd/pbr_nht.c
+++ b/pbrd/pbr_nht.c
@@ -534,6 +534,7 @@ void pbr_nht_set_seq_nhg_data(struct pbr_map_sequence *pbrms,
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
pbrms->family = AF_INET;
+ break;
case NEXTHOP_TYPE_IFINDEX:
case NEXTHOP_TYPE_BLACKHOLE:
break;
@@ -549,6 +550,7 @@ void pbr_nht_set_seq_nhg(struct pbr_map_sequence *pbrms, const char *name)
return;
pbrms->nhgrp_name = XSTRDUP(MTYPE_TMP, name);
+ pbrms->forwarding_type = PBR_FT_NEXTHOP_GROUP;
nhgc = nhgc_find(name);
if (!nhgc)
@@ -572,6 +574,7 @@ void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms,
MTYPE_TMP,
pbr_nht_nexthop_make_name(pbrms->parent->name, PBR_NHC_NAMELEN,
pbrms->seqno, buf));
+ pbrms->forwarding_type = PBR_FT_NEXTHOP_SINGLE;
nh = nexthop_new();
memcpy(nh, nhop, sizeof(*nh));
@@ -647,7 +650,15 @@ static void pbr_nht_release_individual_nexthop(struct pbr_map_sequence *pbrms)
void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence *pbrms)
{
- pbr_map_delete_nexthops(pbrms);
+ struct pbr_map *pbrm = pbrms->parent;
+
+ /* The idea here is to send a delete command to zebra only once,
+ * and set 'valid' and 'installed' to false only when the last
+ * rule is being deleted. In other words, the pbr common should be
+ * updated only when the last rule is being updated or deleted.
+ */
+ if (pbrm->seqnumbers->count == 1)
+ pbr_map_delete_nexthops(pbrms);
pbr_nht_release_individual_nexthop(pbrms);
}
@@ -887,7 +898,7 @@ static void pbr_nht_individual_nexthop_update(struct pbr_nexthop_cache *pnhc,
pbr_nht_individual_nexthop_interface_update(pnhc, pnhi);
break;
}
- /* Intentional fall thru */
+ fallthrough;
case NEXTHOP_TYPE_IPV4_IFINDEX:
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV6:
diff --git a/pbrd/pbr_nht.h b/pbrd/pbr_nht.h
index 9b67492fbc..a702a57155 100644
--- a/pbrd/pbr_nht.h
+++ b/pbrd/pbr_nht.h
@@ -36,7 +36,7 @@ struct pbr_nexthop_cache {
struct pbr_nexthop_group_cache *parent;
char vrf_name[VRF_NAMSIZ + 1];
- char intf_name[INTERFACE_NAMSIZ + 1];
+ char intf_name[IFNAMSIZ + 1];
struct nexthop nexthop;
diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c
index c4e36ebd46..64d88847c8 100644
--- a/pbrd/pbr_vty.c
+++ b/pbrd/pbr_vty.c
@@ -3,6 +3,9 @@
* PBR - vty code
* Copyright (C) 2018 Cumulus Networks, Inc.
* Donald Sharp
+ * Portions:
+ * Copyright (c) 2021 The MITRE Corporation.
+ * Copyright (c) 2023 LabN Consulting, L.L.C.
*/
#include <zebra.h>
@@ -25,12 +28,61 @@
#include "pbrd/pbr_debug.h"
#include "pbrd/pbr_vty_clippy.c"
-DEFUN_NOSH(pbr_map, pbr_map_cmd, "pbr-map PBRMAP seq (1-700)",
+/* clang-format off */
+DEFPY (pbr_set_table_range,
+ pbr_set_table_range_cmd,
+ "pbr table range (10000-4294966272)$lb (10000-4294966272)$ub",
+ PBR_STR
+ "Set table ID range\n"
+ "Set table ID range\n"
+ "Lower bound for table ID range\n"
+ "Upper bound for table ID range\n")
+{
+ /* clang-format on */
+ /* upper bound is 2^32 - 2^10 */
+ int ret = CMD_WARNING;
+ const int minrange = 1000;
+
+ /* validate given bounds */
+ if (lb > ub)
+ vty_out(vty, "%% Lower bound must be less than upper bound\n");
+ else if (ub - lb < minrange)
+ vty_out(vty, "%% Range breadth must be at least %d\n", minrange);
+ else {
+ ret = CMD_SUCCESS;
+ pbr_nht_set_tableid_range((uint32_t)lb, (uint32_t)ub);
+ }
+
+ return ret;
+}
+
+/* clang-format off */
+DEFPY (no_pbr_set_table_range,
+ no_pbr_set_table_range_cmd,
+ "no pbr table range [(10000-4294966272)$lb (10000-4294966272)$ub]",
+ NO_STR
+ PBR_STR
+ "Set table ID range\n"
+ "Set table ID range\n"
+ "Lower bound for table ID range\n"
+ "Upper bound for table ID range\n")
+{
+ /* clang-format on */
+ pbr_nht_set_tableid_range(PBR_NHT_DEFAULT_LOW_TABLEID,
+ PBR_NHT_DEFAULT_HIGH_TABLEID);
+ return CMD_SUCCESS;
+}
+
+/* clang-format off */
+DEFUN_NOSH(pbr_map,
+ pbr_map_cmd,
+ "pbr-map PBRMAP seq (1-700)",
"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")
{
+ /* clang-format on */
const char *pbrm_name = argv[1]->arg;
uint32_t seqno = atoi(argv[3]->arg);
struct pbr_map_sequence *pbrms;
@@ -41,13 +93,17 @@ DEFUN_NOSH(pbr_map, pbr_map_cmd, "pbr-map PBRMAP seq (1-700)",
return CMD_SUCCESS;
}
-DEFUN_NOSH(no_pbr_map, no_pbr_map_cmd, "no pbr-map PBRMAP [seq (1-700)]",
+/* clang-format off */
+DEFUN_NOSH(no_pbr_map,
+ no_pbr_map_cmd,
+ "no pbr-map PBRMAP [seq (1-700)]",
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")
{
+ /* clang-format on */
const char *pbrm_name = argv[2]->arg;
uint32_t seqno = 0;
struct pbr_map *pbrm = pbrm_find(pbrm_name);
@@ -67,300 +123,540 @@ DEFUN_NOSH(no_pbr_map, no_pbr_map_cmd, "no pbr-map PBRMAP [seq (1-700)]",
continue;
pbr_map_delete(pbrms);
+ pbr_map_sequence_delete(pbrms);
}
return CMD_SUCCESS;
}
-DEFPY(pbr_set_table_range,
- pbr_set_table_range_cmd,
- "pbr table range (10000-4294966272)$lb (10000-4294966272)$ub",
- PBR_STR
- "Set table ID range\n"
- "Set table ID range\n"
- "Lower bound for table ID range\n"
- "Upper bound for table ID range\n")
-{
- /* upper bound is 2^32 - 2^10 */
- int ret = CMD_WARNING;
- const int minrange = 1000;
+/***********************************************************************
+ * pbrms/rule Match L3 Fields
+ ***********************************************************************/
- /* validate given bounds */
- if (lb > ub)
- vty_out(vty, "%% Lower bound must be less than upper bound\n");
- else if (ub - lb < minrange)
- vty_out(vty, "%% Range breadth must be at least %d\n", minrange);
- else {
- ret = CMD_SUCCESS;
- pbr_nht_set_tableid_range((uint32_t) lb, (uint32_t) ub);
+/*
+ * Address Family Matters
+ *
+ * Linux Kernel constraints
+ * ------------------------
+ * The underlying linux kernel dataplane requires that rules be
+ * installed into an IPv4-specific or an IPv6-specific database.
+ *
+ * Not only do we need to designate an address-family for rule
+ * installation, but we ALSO must have the same address-family
+ * available to be able to delete the rule from the correct kernel
+ * database.
+ *
+ * Determining the address-family
+ * ------------------------------
+ * In the current code, we do our best to infer the correct family
+ * from any configured IP-address match or set clauses in a rule.
+ * Absent any of those fields, the NHT code also tries to glean the
+ * address family from resolved nexthops or nexthop-groups. All of
+ * those opportunistic address-family determinations are stored in
+ * the "family" field of struct pbr_map_sequence.
+ *
+ * This "family" field value is needed particularly when deleting
+ * a rule piece-by-piece because at the end, the match/set fields
+ * will be empty. Maybe it would be possible to handle this issue
+ * as an internal zebra matter in the future.
+ *
+ * We also attempt to maintain address-family consistency among the
+ * various configured fields in a rule. So far, these fields are
+ * src/dst IP-address match/set values.
+ *
+ * It is probably possible to perform the same address-family check in
+ * the CLI for single nexthops (set nexthop A.B.C.D|X:X::X:X) but the
+ * address-family is not immediately available for nexthop-groups.
+ * In both the single-nexthop and nexthop-group, the NHT resolution code
+ * sets the "family" field of struct pbr_map_sequence asynchronously.
+ *
+ * There isn't currently any flagging of rules that have a consistent
+ * set of src/dst IP-address match/set values but an asynchronously-resolved
+ * nexthop-group that has a different address-family.
+ *
+ * The match/set IP-address handlers below blindly set "family"; it's
+ * probably possible to wrongly set "family" to, e.g., IPv4 this way after
+ * a v6 NHG has been resolved and break rule removal. It's not clear
+ * how to best address this potential issue.
+ */
+static bool pbr_family_consistent(struct pbr_map_sequence *pbrms,
+ uint8_t family, uint32_t skip_filter_bm,
+ uint32_t skip_action_bm, const char **msg)
+{
+ uint32_t filter_bm = pbrms->filter_bm & ~skip_filter_bm;
+ uint32_t action_bm = pbrms->action_bm & ~skip_action_bm;
+
+ if (CHECK_FLAG(filter_bm, PBR_FILTER_SRC_IP) &&
+ (family != pbrms->src->family)) {
+ if (msg)
+ *msg = "match src-ip";
+ return false;
}
-
- return ret;
+ if (CHECK_FLAG(filter_bm, PBR_FILTER_DST_IP) &&
+ (family != pbrms->dst->family)) {
+ if (msg)
+ *msg = "match dst-ip";
+ return false;
+ }
+ if (CHECK_FLAG(action_bm, PBR_ACTION_SRC_IP) &&
+ (family != sockunion_family(&pbrms->action_src))) {
+ if (msg)
+ *msg = "set src-ip";
+ return false;
+ }
+ if (CHECK_FLAG(filter_bm, PBR_ACTION_DST_IP) &&
+ (family != sockunion_family(&pbrms->action_dst))) {
+ if (msg)
+ *msg = "set dst-ip";
+ return false;
+ }
+ return true;
}
-DEFPY(no_pbr_set_table_range, no_pbr_set_table_range_cmd,
- "no pbr table range [(10000-4294966272)$lb (10000-4294966272)$ub]",
- NO_STR
- PBR_STR
- "Set table ID range\n"
- "Set table ID range\n"
- "Lower bound for table ID range\n"
- "Upper bound for table ID range\n")
-{
- pbr_nht_set_tableid_range(PBR_NHT_DEFAULT_LOW_TABLEID,
- PBR_NHT_DEFAULT_HIGH_TABLEID);
- 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",
+/* clang-format off */
+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"
+ "Choose the src ipv4 or ipv6 prefix to use\n"
"v4 Prefix\n"
"v6 Prefix\n")
{
+ /* clang-format on */
struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+ const char *fmsg = NULL;
if (!pbrms)
return CMD_WARNING_CONFIG_FAILED;
- if (pbrms->dst && prefix->family != pbrms->dst->family) {
- vty_out(vty, "Cannot mismatch families within match src/dst\n");
- return CMD_WARNING_CONFIG_FAILED;
+ if (no) {
+ if (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_IP))
+ return CMD_SUCCESS;
+ prefix_free(&pbrms->src);
+ UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_IP);
+ goto check;
}
+ assert(prefix);
+ if (!pbr_family_consistent(pbrms, prefix->family, PBR_FILTER_SRC_IP, 0,
+ &fmsg)) {
+ vty_out(vty, "Address family mismatch (%s)\n", fmsg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
pbrms->family = prefix->family;
- if (!no) {
- if (pbrms->src) {
- if (prefix_same(pbrms->src, prefix))
- return CMD_SUCCESS;
- } else
- pbrms->src = prefix_new();
-
- prefix_copy(pbrms->src, prefix);
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_IP)) {
+ if (prefix_same(pbrms->src, prefix))
+ return CMD_SUCCESS;
} else
- prefix_free(&pbrms->src);
+ pbrms->src = prefix_new();
+ prefix_copy(pbrms->src, prefix);
+ SET_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_IP);
+
+check:
pbr_map_check(pbrms, true);
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",
+/* clang-format off */
+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 dst ip or ipv6 prefix to use\n"
+ "Choose the dst ipv4 or ipv6 prefix to use\n"
"v4 Prefix\n"
"v6 Prefix\n")
{
+ /* clang-format on */
struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+ const char *fmsg = NULL;
if (!pbrms)
return CMD_WARNING_CONFIG_FAILED;
- if (pbrms->src && prefix->family != pbrms->src->family) {
- vty_out(vty, "Cannot mismatch families within match src/dst\n");
- return CMD_WARNING_CONFIG_FAILED;
+ if (no) {
+ if (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DST_IP))
+ return CMD_SUCCESS;
+ prefix_free(&pbrms->dst);
+ UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_DST_IP);
+ goto check;
}
+ assert(prefix);
+ if (!pbr_family_consistent(pbrms, prefix->family, PBR_FILTER_DST_IP, 0,
+ &fmsg)) {
+ vty_out(vty, "Address family mismatch (%s)\n", fmsg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
pbrms->family = prefix->family;
- if (!no) {
- if (pbrms->dst) {
- if (prefix_same(pbrms->dst, prefix))
- return CMD_SUCCESS;
- } else
- pbrms->dst = prefix_new();
-
- prefix_copy(pbrms->dst, prefix);
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DST_IP)) {
+ if (prefix_same(pbrms->dst, prefix))
+ return CMD_SUCCESS;
} else
- prefix_free(&pbrms->dst);
+ pbrms->dst = prefix_new();
+ prefix_copy(pbrms->dst, prefix);
+ SET_FLAG(pbrms->filter_bm, PBR_FILTER_DST_IP);
+
+check:
pbr_map_check(pbrms, true);
return CMD_SUCCESS;
}
-DEFPY(pbr_map_match_ip_proto, pbr_map_match_ip_proto_cmd,
- "[no] match ip-protocol [tcp|udp]$ip_proto",
- NO_STR
- "Match the rest of the command\n"
- "Choose an ip-protocol\n"
- "Match on tcp flows\n"
- "Match on udp flows\n")
+/* clang-format off */
+DEFPY (pbr_map_match_ip_proto,
+ pbr_map_match_ip_proto_cmd,
+ "[no] match ip-protocol ![PROTO$ip_proto]",
+ NO_STR
+ "Match the rest of the command\n"
+ "Choose an ip-protocol\n"
+ "Protocol name\n")
{
+ /* clang-format on */
struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
- struct protoent *p;
+ struct protoent *p = NULL;
if (!pbrms)
return CMD_WARNING_CONFIG_FAILED;
- if (!no) {
+ if (no) {
+ if (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_IP_PROTOCOL))
+ return CMD_SUCCESS;
+ UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_IP_PROTOCOL);
+ goto check;
+ }
+
+ if (ip_proto)
p = getprotobyname(ip_proto);
- if (!p) {
- vty_out(vty, "Unable to convert %s to proto id\n",
- ip_proto);
- return CMD_WARNING;
- }
- pbrms->ip_proto = p->p_proto;
- } else
- pbrms->ip_proto = 0;
+ if (!ip_proto || !p) {
+ vty_out(vty, "Unable to convert %s to proto id\n",
+ (ip_proto ? ip_proto : "(null)"));
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ pbrms->ip_proto = p->p_proto;
+ SET_FLAG(pbrms->filter_bm, PBR_FILTER_IP_PROTOCOL);
+
+check:
+ pbr_map_check(pbrms, true);
return CMD_SUCCESS;
}
-DEFPY(pbr_map_match_src_port, pbr_map_match_src_port_cmd,
- "[no] match src-port (1-65535)$port",
- NO_STR
- "Match the rest of the command\n"
- "Choose the source port to use\n"
- "The Source Port\n")
+/* clang-format off */
+DEFPY (pbr_map_match_src_port,
+ pbr_map_match_src_port_cmd,
+ "[no] match src-port ![(1-65535)$port]",
+ NO_STR
+ "Match the rest of the command\n"
+ "Choose the source port to use\n"
+ "The Source Port\n")
{
+ /* clang-format on */
struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
if (!pbrms)
return CMD_WARNING_CONFIG_FAILED;
- if (!no) {
- if (pbrms->src_prt == port)
+ if (no) {
+ if (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_PORT))
return CMD_SUCCESS;
- else
- pbrms->src_prt = port;
- } else
- pbrms->src_prt = 0;
+ UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_PORT);
+ goto check;
+ }
- pbr_map_check(pbrms, true);
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_PORT) &&
+ (pbrms->src_prt == port)) {
+ return CMD_SUCCESS;
+ }
+ pbrms->src_prt = port;
+ SET_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_PORT);
+check:
+ pbr_map_check(pbrms, true);
return CMD_SUCCESS;
}
-DEFPY(pbr_map_match_dst_port, pbr_map_match_dst_port_cmd,
- "[no] match dst-port (1-65535)$port",
- NO_STR
- "Match the rest of the command\n"
- "Choose the destination port to use\n"
- "The Destination Port\n")
+/* clang-format off */
+DEFPY (pbr_map_match_dst_port,
+ pbr_map_match_dst_port_cmd,
+ "[no] match dst-port ![(1-65535)$port]",
+ NO_STR
+ "Match the rest of the command\n"
+ "Choose the destination port to use\n"
+ "The Destination Port\n")
{
+ /* clang-format on */
struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
if (!pbrms)
return CMD_WARNING_CONFIG_FAILED;
- if (!no) {
- if (pbrms->dst_prt == port)
+ if (no) {
+ if (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DST_PORT))
return CMD_SUCCESS;
- else
- pbrms->dst_prt = port;
- } else
- pbrms->dst_prt = 0;
+ UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_DST_PORT);
+ goto check;
+ }
- pbr_map_check(pbrms, true);
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DST_PORT) &&
+ (pbrms->dst_prt == port)) {
+ return CMD_SUCCESS;
+ }
+ pbrms->dst_prt = port;
+ SET_FLAG(pbrms->filter_bm, PBR_FILTER_DST_PORT);
+check:
+ pbr_map_check(pbrms, true);
return CMD_SUCCESS;
}
-DEFPY(pbr_map_match_dscp, pbr_map_match_dscp_cmd,
- "[no] match dscp DSCP$dscp",
- NO_STR
- "Match the rest of the command\n"
- "Match based on IP DSCP field\n"
- "DSCP value (below 64) or standard codepoint name\n")
+/* clang-format off */
+DEFPY (pbr_map_match_dscp,
+ pbr_map_match_dscp_cmd,
+ "[no] match dscp ![DSCP$dscp]",
+ NO_STR
+ "Match the rest of the command\n"
+ "Match based on IP DSCP field\n"
+ "DSCP value (below 64) or standard codepoint name\n")
{
+ /* clang-format on */
struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
- char dscpname[100];
- uint8_t rawDscp;
if (!pbrms)
return CMD_WARNING_CONFIG_FAILED;
- /* Discriminate dscp enums (cs0, cs1 etc.) and numbers */
- bool isANumber = true;
- for (int i = 0; i < (int)strlen(dscp); i++) {
- /* Letters are not numbers */
- if (!isdigit(dscp[i]))
- isANumber = false;
+ if (no) {
+ if (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DSCP))
+ return CMD_SUCCESS;
+ UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_DSCP);
+ pbrms->dsfield &= ~PBR_DSFIELD_DSCP;
+ goto check;
+ }
- /* Lowercase the dscp enum (if needed) */
- if (isupper(dscp[i]))
- dscpname[i] = tolower(dscp[i]);
- else
- dscpname[i] = dscp[i];
+ unsigned long ul_dscp;
+ char *pend = NULL;
+ uint8_t shifted_dscp;
+
+ assert(dscp);
+ ul_dscp = strtoul(dscp, &pend, 0);
+ if (pend && *pend)
+ ul_dscp = pbr_map_decode_dscp_enum(dscp);
+
+ if (ul_dscp > (PBR_DSFIELD_DSCP >> 2)) {
+ vty_out(vty, "Invalid dscp value: %s%s\n", dscp,
+ ((pend && *pend) ? "" : " (numeric value must be in range 0-63)"));
+ return CMD_WARNING_CONFIG_FAILED;
}
- dscpname[strlen(dscp)] = '\0';
- if (isANumber) {
- /* dscp passed is a regular number */
- long dscpAsNum = strtol(dscp, NULL, 0);
+ shifted_dscp = (ul_dscp << 2) & PBR_DSFIELD_DSCP;
- if (dscpAsNum > PBR_DSFIELD_DSCP >> 2) {
- /* Refuse to install on overflow */
- vty_out(vty, "dscp (%s) must be less than 64\n", dscp);
- return CMD_WARNING_CONFIG_FAILED;
- }
- rawDscp = dscpAsNum;
- } else {
- /* check dscp if it is an enum like cs0 */
- rawDscp = pbr_map_decode_dscp_enum(dscpname);
- if (rawDscp > PBR_DSFIELD_DSCP) {
- vty_out(vty, "Invalid dscp value: %s\n", dscpname);
- return CMD_WARNING_CONFIG_FAILED;
- }
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DSCP) &&
+ ((pbrms->dsfield & PBR_DSFIELD_DSCP) == shifted_dscp)) {
+ return CMD_SUCCESS;
}
- if (!no) {
- if (((pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2) == rawDscp)
+ /* Set the DSCP bits of the DSField */
+ pbrms->dsfield = (pbrms->dsfield & ~PBR_DSFIELD_DSCP) | shifted_dscp;
+ SET_FLAG(pbrms->filter_bm, PBR_FILTER_DSCP);
+
+check:
+ pbr_map_check(pbrms, true);
+ return CMD_SUCCESS;
+}
+
+/* clang-format off */
+DEFPY (pbr_map_match_ecn,
+ pbr_map_match_ecn_cmd,
+ "[no] match ecn ![(0-3)$ecn]",
+ NO_STR
+ "Match the rest of the command\n"
+ "Match based on IP ECN field\n"
+ "Explicit Congestion Notification\n")
+{
+ /* clang-format on */
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+
+ if (!pbrms)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (no) {
+ if (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_ECN))
return CMD_SUCCESS;
+ UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_ECN);
+ pbrms->dsfield &= ~PBR_DSFIELD_ECN;
+ goto check;
+ }
- /* Set the DSCP bits of the DSField */
- pbrms->dsfield =
- (pbrms->dsfield & ~PBR_DSFIELD_DSCP) | (rawDscp << 2);
- } else {
- pbrms->dsfield &= ~PBR_DSFIELD_DSCP;
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_ECN) &&
+ ((pbrms->dsfield & PBR_DSFIELD_ECN) == ecn)) {
+ return CMD_SUCCESS;
}
+ /* Set the ECN bits of the DSField */
+ pbrms->dsfield = (pbrms->dsfield & ~PBR_DSFIELD_ECN) | ecn;
+ SET_FLAG(pbrms->filter_bm, PBR_FILTER_ECN);
+
+check:
pbr_map_check(pbrms, true);
+ return CMD_SUCCESS;
+}
+/***********************************************************************
+ * pbrms/rule Match L2 fields
+ ***********************************************************************/
+
+/* clang-format off */
+DEFPY (pbr_map_match_pcp,
+ pbr_map_match_pcp_cmd,
+ "[no] match pcp ![(0-7)$pcp]",
+ NO_STR
+ "Match spec follows\n"
+ "Match based on 802.1p Priority Code Point (PCP) value\n"
+ "PCP value to match\n")
+{
+ /* clang-format on */
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+
+ if (!pbrms)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (no) {
+ if (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_PCP))
+ return CMD_SUCCESS;
+ UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_PCP);
+ goto check;
+ }
+
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_PCP) &&
+ (pbrms->match_pcp == pcp)) {
+ return CMD_SUCCESS;
+ }
+
+ pbrms->match_pcp = pcp;
+ SET_FLAG(pbrms->filter_bm, PBR_FILTER_PCP);
+
+check:
+ pbr_map_check(pbrms, true);
return CMD_SUCCESS;
}
-DEFPY(pbr_map_match_ecn, pbr_map_match_ecn_cmd,
- "[no] match ecn (0-3)$ecn",
- NO_STR
- "Match the rest of the command\n"
- "Match based on IP ECN field\n"
- "Explicit Congestion Notification\n")
+/* clang-format off */
+DEFPY (pbr_map_match_vlan_id,
+ pbr_map_match_vlan_id_cmd,
+ "[no] match vlan ![(1-4094)$vlan_id]",
+ NO_STR
+ "Match spec follows\n"
+ "Match based on VLAN ID\n"
+ "VLAN ID to match\n")
{
+ /* clang-format on */
struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
if (!pbrms)
return CMD_WARNING_CONFIG_FAILED;
- if (!no) {
- if ((pbrms->dsfield & PBR_DSFIELD_ECN) == ecn)
+ if (no) {
+ if (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_ID))
return CMD_SUCCESS;
+ UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_ID);
+ goto check;
+ }
- /* Set the ECN bits of the DSField */
- pbrms->dsfield = (pbrms->dsfield & ~PBR_DSFIELD_ECN) | ecn;
- } else {
- pbrms->dsfield &= ~PBR_DSFIELD_ECN;
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_ID) &&
+ (pbrms->match_vlan_id == vlan_id)) {
+ return CMD_SUCCESS;
}
+ /*
+ * Maintaining previous behavior: setting a vlan_id match
+ * automatically clears any vlan_flags matching.
+ */
+ UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_FLAGS);
+ SET_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_ID);
+ pbrms->match_vlan_id = vlan_id;
+
+check:
pbr_map_check(pbrms, true);
+ return CMD_SUCCESS;
+}
+
+/* clang-format off */
+DEFPY (pbr_map_match_vlan_tag,
+ pbr_map_match_vlan_tag_cmd,
+ "[no] match vlan ![<tagged|untagged|untagged-or-zero>$tag_type]",
+ NO_STR
+ "Match the rest of the command\n"
+ "Match based on VLAN tagging\n"
+ "Match all tagged frames\n"
+ "Match all untagged frames\n"
+ "Match untagged frames, or tagged frames with id zero\n")
+{
+ /* clang-format on */
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+ uint16_t vlan_flags;
+
+ if (!pbrms)
+ return CMD_WARNING_CONFIG_FAILED;
+ if (no) {
+ if (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_FLAGS))
+ return CMD_SUCCESS;
+ UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_FLAGS);
+ goto check;
+ }
+
+ assert(tag_type);
+ if (strmatch(tag_type, "tagged"))
+ vlan_flags = PBR_VLAN_FLAGS_TAGGED;
+ else if (strmatch(tag_type, "untagged"))
+ vlan_flags = PBR_VLAN_FLAGS_UNTAGGED;
+ else if (strmatch(tag_type, "untagged-or-zero"))
+ vlan_flags = PBR_VLAN_FLAGS_UNTAGGED_0;
+ else {
+ vty_out(vty, "unknown vlan flag\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_FLAGS) &&
+ (pbrms->match_vlan_flags == vlan_flags)) {
+ return CMD_SUCCESS;
+ }
+
+ /*
+ * Maintaining previous behavior: setting a vlan_flags match
+ * automatically clears any vlan_id matching.
+ */
+ UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_ID);
+ SET_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_FLAGS);
+ pbrms->match_vlan_flags = vlan_flags;
+
+check:
+ pbr_map_check(pbrms, true);
return CMD_SUCCESS;
}
-DEFPY(pbr_map_match_mark, pbr_map_match_mark_cmd,
- "[no] match mark (1-4294967295)$mark",
+/***********************************************************************
+ * pbrms/rule Match meta
+ ***********************************************************************/
+
+/* clang-format off */
+DEFPY (pbr_map_match_mark,
+ pbr_map_match_mark_cmd,
+ "[no] match mark ![(1-4294967295)$mark]",
NO_STR
"Match the rest of the command\n"
"Choose the mark value to use\n"
"mark\n")
{
+ /* clang-format on */
struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
if (!pbrms)
@@ -371,139 +667,466 @@ DEFPY(pbr_map_match_mark, pbr_map_match_mark_cmd,
return CMD_WARNING_CONFIG_FAILED;
#endif
- if (!no) {
- if (pbrms->mark)
- if (pbrms->mark == (uint32_t)mark)
- return CMD_SUCCESS;
+ if (no) {
+ if (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_FWMARK))
+ return CMD_SUCCESS;
+ UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_FWMARK);
+ goto check;
+ }
- pbrms->mark = (uint32_t)mark;
- } else
- pbrms->mark = 0;
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_FWMARK) &&
+ (pbrms->mark == (uint32_t)mark)) {
+ return CMD_SUCCESS;
+ }
+ pbrms->mark = (uint32_t)mark;
+ SET_FLAG(pbrms->filter_bm, PBR_FILTER_FWMARK);
+
+check:
pbr_map_check(pbrms, true);
return CMD_SUCCESS;
}
-static void pbrms_clear_set_vrf_config(struct pbr_map_sequence *pbrms)
+/***********************************************************************
+ * pbrms/rule Action Set L3 Fields
+ ***********************************************************************/
+
+/* clang-format off */
+DEFPY (pbr_map_action_src,
+ pbr_map_action_src_cmd,
+ "[no] set src-ip ![<A.B.C.D|X:X::X:X>$su]",
+ NO_STR
+ "Set command\n"
+ "Set the src ipv4 or ipv6 prefix\n"
+ "v4 Prefix\n"
+ "v6 Prefix\n")
{
- if (pbrms->vrf_lookup || pbrms->vrf_unchanged) {
- pbr_map_delete_vrf(pbrms);
- pbrms->vrf_name[0] = '\0';
- pbrms->vrf_lookup = false;
- pbrms->vrf_unchanged = false;
+ /* clang-format on */
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+ const char *fmsg = NULL;
+
+ if (!pbrms)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (no) {
+ if (!CHECK_FLAG(pbrms->action_bm, PBR_ACTION_SRC_IP))
+ return CMD_SUCCESS;
+ UNSET_FLAG(pbrms->action_bm, PBR_ACTION_SRC_IP);
+ goto check;
+ }
+
+ assert(su);
+ if (!pbr_family_consistent(pbrms, sockunion_family(su),
+ PBR_ACTION_SRC_IP, 0, &fmsg)) {
+ vty_out(vty, "Address family mismatch (%s)\n", fmsg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ pbrms->family = sockunion_family(su);
+
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_SRC_IP) &&
+ (sockunion_same(&pbrms->action_src, su))) {
+ return CMD_SUCCESS;
}
+ pbrms->action_src = *su;
+ SET_FLAG(pbrms->action_bm, PBR_ACTION_SRC_IP);
+
+check:
+ pbr_map_check(pbrms, true);
+ return CMD_SUCCESS;
}
-static void pbrms_clear_set_nhg_config(struct pbr_map_sequence *pbrms)
+/* clang-format off */
+DEFPY (pbr_map_action_dst,
+ pbr_map_action_dst_cmd,
+ "[no] set dst-ip ![<A.B.C.D|X:X::X:X>$su]",
+ NO_STR
+ "Set command\n"
+ "Set the dst ipv4 or ipv6 prefix\n"
+ "v4 Prefix\n"
+ "v6 Prefix\n")
{
- if (pbrms->nhgrp_name)
- pbr_map_delete_nexthops(pbrms);
+ /* clang-format on */
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+ const char *fmsg = NULL;
+
+ if (!pbrms)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (no) {
+ if (!CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DST_IP))
+ return CMD_SUCCESS;
+ UNSET_FLAG(pbrms->action_bm, PBR_ACTION_DST_IP);
+ goto check;
+ }
+
+ assert(su);
+ if (!pbr_family_consistent(pbrms, sockunion_family(su),
+ PBR_ACTION_DST_IP, 0, &fmsg)) {
+ vty_out(vty, "Address family mismatch (%s)\n", fmsg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ pbrms->family = sockunion_family(su);
+
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DST_IP) &&
+ (sockunion_same(&pbrms->action_dst, su))) {
+ return CMD_SUCCESS;
+ }
+ pbrms->action_dst = *su;
+ SET_FLAG(pbrms->action_bm, PBR_ACTION_DST_IP);
+
+check:
+ pbr_map_check(pbrms, true);
+ return CMD_SUCCESS;
}
-static void pbrms_clear_set_nexthop_config(struct pbr_map_sequence *pbrms)
+/* clang-format off */
+DEFPY (pbr_map_action_src_port,
+ pbr_map_action_src_port_cmd,
+ "[no] set src-port ![(1-65535)$port]",
+ NO_STR
+ "Set command\n"
+ "Set Source Port\n"
+ "The Source Port\n")
{
- if (pbrms->nhg)
- pbr_nht_delete_individual_nexthop(pbrms);
+ /* clang-format on */
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+
+ if (!pbrms)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (no) {
+ if (!CHECK_FLAG(pbrms->action_bm, PBR_ACTION_SRC_PORT))
+ return CMD_SUCCESS;
+ UNSET_FLAG(pbrms->action_bm, PBR_ACTION_SRC_PORT);
+ goto check;
+ }
+
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_SRC_PORT) &&
+ (pbrms->action_src_port == port))
+ return CMD_SUCCESS;
+
+ pbrms->action_src_port = port;
+ SET_FLAG(pbrms->action_bm, PBR_ACTION_SRC_PORT);
+
+check:
+ pbr_map_check(pbrms, true);
+ return CMD_SUCCESS;
}
-static void pbrms_clear_set_config(struct pbr_map_sequence *pbrms)
+/* clang-format off */
+DEFPY (pbr_map_action_dst_port,
+ pbr_map_action_dst_port_cmd,
+ "[no] set dst-port ![(1-65535)$port]",
+ NO_STR
+ "Set command\n"
+ "Set Destination Port\n"
+ "The Destination Port\n")
{
- pbrms_clear_set_vrf_config(pbrms);
- pbrms_clear_set_nhg_config(pbrms);
- pbrms_clear_set_nexthop_config(pbrms);
+ /* clang-format on */
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
- pbrms->nhs_installed = false;
-}
+ if (!pbrms)
+ return CMD_WARNING_CONFIG_FAILED;
+ if (no) {
+ if (!CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DST_PORT))
+ return CMD_SUCCESS;
+ UNSET_FLAG(pbrms->action_bm, PBR_ACTION_DST_PORT);
+ goto check;
+ }
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DST_PORT) &&
+ (pbrms->action_dst_port == port))
+ return CMD_SUCCESS;
-DEFPY(pbr_map_action_queue_id, pbr_map_action_queue_id_cmd,
- "[no] set queue-id <(1-65535)$queue_id>",
- NO_STR
- "Set the rest of the command\n"
- "Set based on egress port queue id\n"
- "A valid value in range 1..65535 \n")
+ SET_FLAG(pbrms->action_bm, PBR_ACTION_DST_PORT);
+ pbrms->action_dst_port = port;
+
+check:
+ pbr_map_check(pbrms, true);
+ return CMD_SUCCESS;
+}
+
+/* clang-format off */
+DEFPY (pbr_map_action_dscp,
+ pbr_map_action_dscp_cmd,
+ "[no] set dscp ![DSCP$dscp]",
+ NO_STR
+ "Set command\n"
+ "Set IP DSCP field\n"
+ "DSCP numeric value (0-63) or standard codepoint name\n")
{
+ /* clang-format on */
struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
if (!pbrms)
return CMD_WARNING_CONFIG_FAILED;
- if (!no)
- pbrms->action_queue_id = queue_id;
- else if ((uint32_t)queue_id == pbrms->action_queue_id)
- pbrms->action_queue_id = PBR_MAP_UNDEFINED_QUEUE_ID;
+ if (no) {
+ if (!CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DSCP))
+ return CMD_SUCCESS;
+ UNSET_FLAG(pbrms->action_bm, PBR_ACTION_DSCP);
+ goto check;
+ }
- pbr_map_check(pbrms, true);
+ unsigned long ul_dscp;
+ char *pend = NULL;
+ uint8_t shifted_dscp;
+
+ assert(dscp);
+ ul_dscp = strtoul(dscp, &pend, 0);
+ if (pend && *pend)
+ ul_dscp = pbr_map_decode_dscp_enum(dscp);
+
+ if (ul_dscp > (PBR_DSFIELD_DSCP >> 2)) {
+ vty_out(vty, "Invalid dscp value: %s%s\n", dscp,
+ ((pend && *pend) ? "" : " (numeric value must be in range 0-63)"));
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ shifted_dscp = (ul_dscp << 2) & PBR_DSFIELD_DSCP;
+
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DSCP) &&
+ (pbrms->action_dscp == shifted_dscp)) {
+ return CMD_SUCCESS;
+ }
+ SET_FLAG(pbrms->action_bm, PBR_ACTION_DSCP);
+ pbrms->action_dscp = shifted_dscp;
+check:
+ pbr_map_check(pbrms, true);
return CMD_SUCCESS;
}
-DEFPY(pbr_map_action_pcp, pbr_map_action_pcp_cmd, "[no] set pcp <(0-7)$pcp>",
- NO_STR
- "Set the rest of the command\n"
- "Set based on 802.1p Priority Code Point (PCP) value\n"
- "A valid value in range 0..7\n")
+/* clang-format off */
+DEFPY (pbr_map_action_ecn,
+ pbr_map_action_ecn_cmd,
+ "[no] set ecn ![(0-3)$ecn]",
+ NO_STR
+ "Set command\n"
+ "Set IP ECN field\n"
+ "Explicit Congestion Notification value\n")
{
+ /* clang-format on */
struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
if (!pbrms)
return CMD_WARNING_CONFIG_FAILED;
- if (!no)
- pbrms->action_pcp = pcp;
- else if (pcp == pbrms->action_pcp)
- pbrms->action_pcp = 0;
+ if (no) {
+ if (!CHECK_FLAG(pbrms->action_bm, PBR_ACTION_ECN))
+ return CMD_SUCCESS;
+ UNSET_FLAG(pbrms->action_bm, PBR_ACTION_ECN);
+ goto check;
+ }
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_ECN) &&
+ (pbrms->action_ecn == ecn)) {
+ return CMD_SUCCESS;
+ }
+ SET_FLAG(pbrms->action_bm, PBR_ACTION_ECN);
+ pbrms->action_ecn = ecn;
+check:
pbr_map_check(pbrms, true);
-
return CMD_SUCCESS;
}
-DEFPY(pbr_map_action_vlan_id, pbr_map_action_vlan_id_cmd,
- "[no] set vlan <(1-4094)$vlan_id>",
- NO_STR
- "Set the rest of the command\n"
- "Set action for VLAN tagging\n"
- "A valid value in range 1..4094\n")
+
+/***********************************************************************
+ * pbrms/rule Action Set Meta
+ ***********************************************************************/
+
+/* clang-format off */
+DEFPY (pbr_map_action_queue_id,
+ pbr_map_action_queue_id_cmd,
+ "[no] set queue-id ![(1-65535)$queue_id]",
+ NO_STR
+ "Set the rest of the command\n"
+ "Set based on egress port queue id\n"
+ "A valid value in range 1..65535 \n")
{
+ /* clang-format on */
struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
if (!pbrms)
return CMD_WARNING_CONFIG_FAILED;
- if (!no)
- pbrms->action_vlan_id = vlan_id;
- else if (pbrms->action_vlan_id == vlan_id)
- pbrms->action_vlan_id = 0;
+ if (no) {
+ if (!CHECK_FLAG(pbrms->action_bm, PBR_ACTION_QUEUE_ID))
+ return CMD_SUCCESS;
+ UNSET_FLAG(pbrms->action_bm, PBR_ACTION_QUEUE_ID);
+ goto check;
+ }
+
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_QUEUE_ID) &&
+ (pbrms->action_queue_id == (uint32_t)queue_id)) {
+ return CMD_SUCCESS;
+ }
+ pbrms->action_queue_id = (uint32_t)queue_id;
+ SET_FLAG(pbrms->action_bm, PBR_ACTION_QUEUE_ID);
+check:
pbr_map_check(pbrms, true);
+ return CMD_SUCCESS;
+}
+
+
+/***********************************************************************
+ * pbrms/rule Action Set L2 Fields
+ ***********************************************************************/
+
+/* clang-format off */
+DEFPY (pbr_map_action_pcp,
+ pbr_map_action_pcp_cmd,
+ "[no] set pcp ![(0-7)$pcp]",
+ NO_STR
+ "Set the rest of the command\n"
+ "Set based on 802.1p Priority Code Point (PCP) value\n"
+ "A valid value in range 0..7\n")
+{
+ /* clang-format on */
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+
+ if (!pbrms)
+ return CMD_WARNING_CONFIG_FAILED;
+ if (no) {
+ if (!CHECK_FLAG(pbrms->action_bm, PBR_ACTION_PCP))
+ return CMD_SUCCESS;
+ UNSET_FLAG(pbrms->action_bm, PBR_ACTION_PCP);
+ goto check;
+ }
+
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_PCP) &&
+ pbrms->action_pcp == pcp) {
+ return CMD_SUCCESS;
+ }
+
+ pbrms->action_pcp = pcp;
+ SET_FLAG(pbrms->action_bm, PBR_ACTION_PCP);
+
+check:
+ pbr_map_check(pbrms, true);
return CMD_SUCCESS;
}
-DEFPY(pbr_map_action_strip_vlan, pbr_map_action_strip_vlan_cmd,
- "[no] strip vlan",
- NO_STR
- "Strip the vlan tags from frame\n"
- "Strip any inner vlan tag \n")
+/* clang-format off */
+DEFPY (pbr_map_action_vlan_id,
+ pbr_map_action_vlan_id_cmd,
+ "[no] set vlan ![(1-4094)$vlan_id]",
+ NO_STR
+ "Set the rest of the command\n"
+ "Set action for VLAN tagging\n"
+ "A valid value in range 1..4094\n")
{
+ /* clang-format on */
struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
if (!pbrms)
return CMD_WARNING_CONFIG_FAILED;
- if (!no)
- pbrms->action_vlan_flags = PBR_MAP_STRIP_INNER_ANY;
- else
- pbrms->action_vlan_flags = 0;
+ if (no) {
+ if (!CHECK_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_ID))
+ return CMD_SUCCESS;
+ UNSET_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_ID);
+ goto check;
+ }
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_ID) &&
+ (pbrms->action_vlan_id == vlan_id)) {
+ return CMD_SUCCESS;
+ }
+
+ /*
+ * Setting a vlan_id action automatically clears any strip-inner action
+ */
+ pbrms->action_vlan_id = vlan_id;
+ UNSET_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_STRIP_INNER_ANY);
+ SET_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_ID);
+
+check:
pbr_map_check(pbrms, true);
+ return CMD_SUCCESS;
+}
+
+/* clang-format off */
+DEFPY (pbr_map_action_strip_vlan,
+ pbr_map_action_strip_vlan_cmd,
+ "[no] strip vlan",
+ NO_STR
+ "Strip the vlan tags from frame\n"
+ "Strip any inner vlan tag\n")
+{
+ /* clang-format on */
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+
+ if (!pbrms)
+ return CMD_WARNING_CONFIG_FAILED;
+ if (no) {
+ if (!CHECK_FLAG(pbrms->action_bm,
+ PBR_ACTION_VLAN_STRIP_INNER_ANY))
+ return CMD_SUCCESS;
+ UNSET_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_STRIP_INNER_ANY);
+ goto check;
+ }
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_STRIP_INNER_ANY))
+ return CMD_SUCCESS;
+
+ /*
+ * Setting a strip-inner action automatically clears any vlan_id action
+ */
+ UNSET_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_ID);
+ SET_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_STRIP_INNER_ANY);
+
+check:
+ pbr_map_check(pbrms, true);
return CMD_SUCCESS;
}
+/***********************************************************************
+ * pbrms/rule Action Forwarding
+ ***********************************************************************/
+
+static void pbrms_clear_set_vrf_config(struct pbr_map_sequence *pbrms)
+{
+ if (pbrms->vrf_lookup || pbrms->vrf_unchanged) {
+ pbr_map_delete_vrf(pbrms);
+ pbrms->vrf_name[0] = '\0';
+ pbrms->vrf_lookup = false;
+ pbrms->vrf_unchanged = false;
+ }
+}
+
+static void pbrms_clear_set_nhg_config(struct pbr_map_sequence *pbrms)
+{
+ if (pbrms->nhgrp_name)
+ pbr_map_delete_nexthops(pbrms);
+}
+
+static void pbrms_clear_set_nexthop_config(struct pbr_map_sequence *pbrms)
+{
+ if (pbrms->nhg)
+ pbr_nht_delete_individual_nexthop(pbrms);
+}
+
+static void pbrms_clear_set_config(struct pbr_map_sequence *pbrms)
+{
+ pbrms_clear_set_vrf_config(pbrms);
+ pbrms_clear_set_nhg_config(pbrms);
+ pbrms_clear_set_nexthop_config(pbrms);
+
+ pbrms->nhs_installed = false;
+
+ pbrms->forwarding_type = PBR_FT_UNSPEC;
+
+ /* clear advisory flag indicating nexthop == blackhole */
+ UNSET_FLAG(pbrms->action_bm, PBR_ACTION_DROP);
+}
+
+
+
DEFPY(pbr_map_nexthop_group, pbr_map_nexthop_group_cmd,
"set nexthop-group NHGNAME$name",
"Set for the PBR-MAP\n"
@@ -554,22 +1177,27 @@ DEFPY(no_pbr_map_nexthop_group, no_pbr_map_nexthop_group_cmd,
return CMD_SUCCESS;
}
-DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd,
- "set nexthop\
+/* clang-format off */
+DEFPY (pbr_map_nexthop,
+ pbr_map_nexthop_cmd,
+ "set nexthop\
<\
<A.B.C.D|X:X::X:X>$addr [INTERFACE$intf]\
|INTERFACE$intf\
+ |blackhole$bh\
>\
[nexthop-vrf NAME$vrf_name]",
- "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"
- "Interface to use\n"
- "If the nexthop is in a different vrf tell us\n"
- "The nexthop-vrf Name\n")
+ "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"
+ "Interface to use\n"
+ "Blackhole route\n"
+ "If the nexthop is in a different vrf tell us\n"
+ "The nexthop-vrf Name\n")
{
+ /* clang-format on */
struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
struct vrf *vrf;
struct nexthop nhop;
@@ -651,8 +1279,13 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd,
nhop.type = NEXTHOP_TYPE_IPV6;
}
}
- } else
+ } else if (bh) {
+ nhop.type = NEXTHOP_TYPE_BLACKHOLE;
+ /* advisory flag for non-linux dataplanes */
+ SET_FLAG(pbrms->action_bm, PBR_ACTION_DROP);
+ } else {
nhop.type = NEXTHOP_TYPE_IFINDEX;
+ }
if (pbrms->nhg)
nh = nexthop_exists(pbrms->nhg, &nhop);
@@ -681,23 +1314,28 @@ done:
return CMD_SUCCESS;
}
-DEFPY(no_pbr_map_nexthop, no_pbr_map_nexthop_cmd,
- "no set nexthop\
+/* clang-format off */
+DEFPY (no_pbr_map_nexthop,
+ no_pbr_map_nexthop_cmd,
+ "no set nexthop\
[<\
<A.B.C.D|X:X::X:X>$addr [INTERFACE$intf]\
|INTERFACE$intf\
+ |blackhole$bh\
>\
[nexthop-vrf NAME$vrf_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"
- "Interface to use\n"
- "If the nexthop is in a different vrf tell us\n"
- "The nexthop-vrf Name\n")
+ 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"
+ "Interface to use\n"
+ "Blackhole route\n"
+ "If the nexthop is in a different vrf tell us\n"
+ "The nexthop-vrf Name\n")
{
+ /* clang-format on */
struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
if (!pbrms)
@@ -723,10 +1361,12 @@ DEFPY(pbr_map_vrf, pbr_map_vrf_cmd,
/*
* If an equivalent set vrf * exists, just return success.
*/
- if (vrf_name && pbrms->vrf_lookup
- && strncmp(pbrms->vrf_name, vrf_name, sizeof(pbrms->vrf_name)) == 0)
+ if ((pbrms->forwarding_type == PBR_FT_SETVRF) &&
+ vrf_name &&
+ strncmp(pbrms->vrf_name, vrf_name, sizeof(pbrms->vrf_name)) == 0)
return CMD_SUCCESS;
- else if (!vrf_name && pbrms->vrf_unchanged) /* Unchanged already set */
+ else if (!vrf_name && (pbrms->forwarding_type == PBR_FT_VRF_UNCHANGED))
+ /* Unchanged already set */
return CMD_SUCCESS;
if (vrf_name && !pbr_vrf_lookup_by_name(vrf_name)) {
@@ -739,9 +1379,12 @@ DEFPY(pbr_map_vrf, pbr_map_vrf_cmd,
if (vrf_name) {
pbrms->vrf_lookup = true;
+ pbrms->forwarding_type = PBR_FT_SETVRF;
strlcpy(pbrms->vrf_name, vrf_name, sizeof(pbrms->vrf_name));
- } else
+ } else {
+ pbrms->forwarding_type = PBR_FT_VRF_UNCHANGED;
pbrms->vrf_unchanged = true;
+ }
pbr_map_check(pbrms, true);
@@ -766,13 +1409,19 @@ DEFPY(no_pbr_map_vrf, no_pbr_map_vrf_cmd,
return CMD_SUCCESS;
}
-DEFPY (pbr_policy,
+/***********************************************************************
+ * Policy
+ ***********************************************************************/
+
+/* clang-format off */
+DEFPY (pbr_policy,
pbr_policy_cmd,
"[no] pbr-policy PBRMAP$mapname",
NO_STR
"Policy to use\n"
"Name of the pbr-map to apply\n")
{
+ /* clang-format on */
VTY_DECLVAR_CONTEXT(interface, ifp);
struct pbr_map *pbrm, *old_pbrm;
struct pbr_interface *pbr_ifp = ifp->info;
@@ -879,43 +1528,93 @@ static void vty_show_pbrms(struct vty *vty,
pbrms->installed ? "yes" : "no",
pbrms->reason ? rbuf : "Valid");
- if (pbrms->ip_proto) {
+ /* match clauses first */
+
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_IP_PROTOCOL)) {
struct protoent *p;
p = getprotobynumber(pbrms->ip_proto);
vty_out(vty, " IP Protocol Match: %s\n", p->p_name);
}
- if (pbrms->src)
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_IP))
vty_out(vty, " SRC IP Match: %pFX\n", pbrms->src);
- if (pbrms->dst)
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DST_IP))
vty_out(vty, " DST IP Match: %pFX\n", pbrms->dst);
- if (pbrms->src_prt)
+
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_PORT))
vty_out(vty, " SRC Port Match: %u\n", pbrms->src_prt);
- if (pbrms->dst_prt)
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DST_PORT))
vty_out(vty, " DST Port Match: %u\n", pbrms->dst_prt);
- if (pbrms->dsfield & PBR_DSFIELD_DSCP)
+
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DSCP))
vty_out(vty, " DSCP Match: %u\n",
(pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2);
- if (pbrms->dsfield & PBR_DSFIELD_ECN)
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_ECN))
vty_out(vty, " ECN Match: %u\n",
pbrms->dsfield & PBR_DSFIELD_ECN);
- if (pbrms->mark)
+
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_FWMARK))
vty_out(vty, " MARK Match: %u\n", pbrms->mark);
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_PCP))
+ vty_out(vty, " PCP Match: %d\n", pbrms->match_pcp);
+
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_ID))
+ vty_out(vty, " Match VLAN ID: %u\n",
+ pbrms->match_vlan_id);
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_FLAGS)) {
+ if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_TAGGED)
+ vty_out(vty, " Match VLAN tagged frames\n");
+ if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_UNTAGGED)
+ vty_out(vty, " Match VLAN untagged frames\n");
+ if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_UNTAGGED_0)
+ vty_out(vty, " Match VLAN untagged or ID 0\n");
+ }
- if (pbrms->action_queue_id != PBR_MAP_UNDEFINED_QUEUE_ID)
- vty_out(vty, " Set Queue ID %u\n",
- pbrms->action_queue_id);
+ /* set actions */
+
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_SRC_IP))
+ vty_out(vty, " Set SRC IP: %pSU\n", &pbrms->action_src);
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DST_IP))
+ vty_out(vty, " Set DST IP: %pSU\n", &pbrms->action_dst);
+
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_SRC_PORT))
+ vty_out(vty, " Set Src port: %u\n",
+ pbrms->action_src_port);
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DST_PORT))
+ vty_out(vty, " Set Dst port: %u\n",
+ pbrms->action_dst_port);
+
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DSCP))
+ vty_out(vty, " Set DSCP: %u\n", (pbrms->action_dscp) >> 2);
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_ECN))
+ vty_out(vty, " Set ECN: %u\n", pbrms->action_ecn);
- if (pbrms->action_vlan_id != 0)
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_ID))
vty_out(vty, " Set VLAN ID %u\n", pbrms->action_vlan_id);
- if (pbrms->action_vlan_flags == PBR_MAP_STRIP_INNER_ANY)
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_STRIP_INNER_ANY))
vty_out(vty, " Strip VLAN ID\n");
- if (pbrms->action_pcp)
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_PCP))
vty_out(vty, " Set PCP %u\n", pbrms->action_pcp);
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_QUEUE_ID))
+ vty_out(vty, " Set Queue ID: %u\n",
+ pbrms->action_queue_id);
+
- if (pbrms->nhgrp_name) {
+ switch (pbrms->forwarding_type) {
+ case PBR_FT_UNSPEC:
+ vty_out(vty, " Nexthop-Group: Unknown Installed: no\n");
+ break;
+ case PBR_FT_VRF_UNCHANGED:
+ vty_out(vty, " VRF Unchanged (use interface vrf)\n");
+ break;
+ case PBR_FT_SETVRF:
+ assert(pbrms->vrf_name);
+ vty_out(vty, " VRF Lookup: %s\n", pbrms->vrf_name);
+ break;
+ case PBR_FT_NEXTHOP_GROUP:
+ assert(pbrms->nhgrp_name);
vty_out(vty, " Nexthop-Group: %s\n", pbrms->nhgrp_name);
if (detail)
@@ -929,8 +1628,9 @@ static void vty_show_pbrms(struct vty *vty,
pbr_nht_get_installed(pbrms->nhgrp_name) ? "yes"
: "no",
pbr_nht_get_table(pbrms->nhgrp_name));
-
- } else if (pbrms->nhg) {
+ break;
+ case PBR_FT_NEXTHOP_SINGLE:
+ assert(pbrms->internal_nhg_name);
vty_out(vty, " ");
pbrms_nexthop_group_write_individual_nexthop(vty, pbrms);
if (detail)
@@ -945,13 +1645,7 @@ static void vty_show_pbrms(struct vty *vty,
? "yes"
: "no",
pbr_nht_get_table(pbrms->internal_nhg_name));
-
- } else if (pbrms->vrf_unchanged) {
- vty_out(vty, " VRF Unchanged (use interface vrf)\n");
- } else if (pbrms->vrf_lookup) {
- vty_out(vty, " VRF Lookup: %s\n", pbrms->vrf_name);
- } else {
- vty_out(vty, " Nexthop-Group: Unknown Installed: no\n");
+ break;
}
}
@@ -973,12 +1667,22 @@ static void vty_json_pbrms(json_object *j, struct vty *vty,
json_object_int_add(jpbrm, "sequenceNumber", pbrms->seqno);
json_object_int_add(jpbrm, "ruleNumber", pbrms->ruleno);
json_object_boolean_add(jpbrm, "vrfUnchanged", pbrms->vrf_unchanged);
- json_object_boolean_add(jpbrm, "installed",
- pbr_nht_get_installed(nhg_name));
+ json_object_boolean_add(jpbrm, "installed", pbrms->installed);
json_object_string_add(jpbrm, "installedReason",
pbrms->reason ? rbuf : "Valid");
- if (nhg_name) {
+ switch (pbrms->forwarding_type) {
+ case PBR_FT_UNSPEC:
+ break;
+ case PBR_FT_VRF_UNCHANGED:
+ break;
+ case PBR_FT_SETVRF:
+ assert(pbrms->vrf_name);
+ json_object_string_add(jpbrm, "vrfName", pbrms->vrf_name);
+ break;
+ case PBR_FT_NEXTHOP_GROUP:
+ case PBR_FT_NEXTHOP_SINGLE:
+ assert(nhg_name);
nexthop_group = json_object_new_object();
json_object_int_add(nexthop_group, "tableId",
@@ -990,24 +1694,93 @@ static void vty_json_pbrms(json_object *j, struct vty *vty,
pbrms->nhs_installed);
json_object_object_add(jpbrm, "nexthopGroup", nexthop_group);
+ break;
}
- if (pbrms->vrf_lookup)
- json_object_string_add(jpbrm, "vrfName", pbrms->vrf_name);
- if (pbrms->src)
+ /*
+ * Match clauses
+ */
+
+ /* IP Header */
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_IP_PROTOCOL))
+ json_object_int_add(jpbrm, "matchIpProtocol", pbrms->ip_proto);
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_IP))
json_object_string_addf(jpbrm, "matchSrc", "%pFX", pbrms->src);
- if (pbrms->dst)
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DST_IP))
json_object_string_addf(jpbrm, "matchDst", "%pFX", pbrms->dst);
- if (pbrms->mark)
- json_object_int_add(jpbrm, "matchMark", pbrms->mark);
- if (pbrms->dsfield & PBR_DSFIELD_DSCP)
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_PORT))
+ json_object_int_add(jpbrm, "matchSrcPort", pbrms->src_prt);
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DST_PORT))
+ json_object_int_add(jpbrm, "matchDstPort", pbrms->dst_prt);
+
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DSCP))
json_object_int_add(jpbrm, "matchDscp",
(pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2);
- if (pbrms->dsfield & PBR_DSFIELD_ECN)
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_ECN))
json_object_int_add(jpbrm, "matchEcn",
pbrms->dsfield & PBR_DSFIELD_ECN);
+ /* L2 headers */
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_PCP))
+ json_object_int_add(jpbrm, "matchPcp", pbrms->match_pcp);
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_ID))
+ json_object_int_add(jpbrm, "matchVlanId", pbrms->match_vlan_id);
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_FLAGS)) {
+ const char *p = "?";
+
+ if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_TAGGED)
+ p = "tagged";
+ if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_UNTAGGED)
+ p = "untagged";
+ if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_UNTAGGED_0)
+ p = "untagged-or-0";
+
+ json_object_string_addf(jpbrm, "matchVlanFlags", "%s", p);
+ }
+
+ /* meta */
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_FWMARK))
+ json_object_int_add(jpbrm, "matchMark", pbrms->mark);
+
+ /*
+ * action clauses
+ */
+
+ /* IP header fields */
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_SRC_IP))
+ json_object_string_addf(jpbrm, "actionSetSrcIpAddr", "%pSU",
+ &pbrms->action_src);
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DST_IP))
+ json_object_string_addf(jpbrm, "actionSetDstIpAddr", "%pSU",
+ &pbrms->action_dst);
+
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_SRC_PORT))
+ json_object_int_add(jpbrm, "actionSetSrcPort",
+ pbrms->action_src_port);
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DST_PORT))
+ json_object_int_add(jpbrm, "actionSetDstPort",
+ pbrms->action_dst_port);
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DSCP))
+ json_object_int_add(jpbrm, "actionSetDscp",
+ pbrms->action_dscp >> 2);
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_ECN))
+ json_object_int_add(jpbrm, "actionSetEcn", pbrms->action_ecn);
+
+ /* L2 header fields */
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_STRIP_INNER_ANY))
+ json_object_boolean_true_add(jpbrm, "actionVlanStripInnerAny");
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_ID))
+ json_object_int_add(jpbrm, "actionSetVlanId",
+ pbrms->action_vlan_id);
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_PCP))
+ json_object_int_add(jpbrm, "actionSetPcp", pbrms->action_pcp);
+
+ /* meta */
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_QUEUE_ID))
+ json_object_int_add(jpbrm, "actionSetQueueId",
+ pbrms->action_queue_id);
+
json_object_array_add(j, jpbrm);
}
@@ -1044,7 +1817,7 @@ static void vty_json_pbr_map(json_object *j, struct vty *vty,
DEFPY (show_pbr_map,
show_pbr_map_cmd,
- "show pbr map [NAME$name] [detail$detail|json$json]",
+ "show pbr map [NAME$name] [detail$detail] [json$json]",
SHOW_STR
PBR_STR
"PBR Map\n"
@@ -1273,60 +2046,99 @@ static int pbr_vty_map_config_write_sequence(struct vty *vty,
{
vty_out(vty, "pbr-map %s seq %u\n", pbrm->name, pbrms->seqno);
- if (pbrms->src)
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_IP_PROTOCOL)) {
+ struct protoent *p;
+
+ p = getprotobynumber(pbrms->ip_proto);
+ vty_out(vty, " match ip-protocol %s\n", p->p_name);
+ }
+
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_IP))
vty_out(vty, " match src-ip %pFX\n", pbrms->src);
- if (pbrms->dst)
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DST_IP))
vty_out(vty, " match dst-ip %pFX\n", pbrms->dst);
- if (pbrms->src_prt)
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_PORT))
vty_out(vty, " match src-port %u\n", pbrms->src_prt);
- if (pbrms->dst_prt)
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DST_PORT))
vty_out(vty, " match dst-port %u\n", pbrms->dst_prt);
- if (pbrms->ip_proto) {
- struct protoent *p;
-
- p = getprotobynumber(pbrms->ip_proto);
- vty_out(vty, " match ip-protocol %s\n", p->p_name);
- }
-
- if (pbrms->dsfield & PBR_DSFIELD_DSCP)
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DSCP))
vty_out(vty, " match dscp %u\n",
(pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2);
- if (pbrms->dsfield & PBR_DSFIELD_ECN)
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_ECN))
vty_out(vty, " match ecn %u\n",
pbrms->dsfield & PBR_DSFIELD_ECN);
- if (pbrms->mark)
- vty_out(vty, " match mark %u\n", pbrms->mark);
-
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_PCP))
+ vty_out(vty, " match pcp %d\n", pbrms->match_pcp);
+
+ /* L2 headers */
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_ID))
+ vty_out(vty, " match vlan %u\n", pbrms->match_vlan_id);
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_FLAGS)) {
+ if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_TAGGED)
+ vty_out(vty, " match vlan tagged\n");
+ if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_UNTAGGED)
+ vty_out(vty, " match vlan untagged\n");
+ if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_UNTAGGED_0)
+ vty_out(vty, " match vlan untagged-or-zero\n");
+ }
- if (pbrms->action_queue_id != PBR_MAP_UNDEFINED_QUEUE_ID)
- vty_out(vty, " set queue-id %d\n", pbrms->action_queue_id);
+ /* meta */
+ if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_FWMARK))
+ vty_out(vty, " match mark %u\n", pbrms->mark);
- if (pbrms->action_pcp)
- vty_out(vty, " set pcp %d\n", pbrms->action_pcp);
+ /*
+ * action clauses
+ */
- if (pbrms->action_vlan_id)
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_SRC_IP))
+ vty_out(vty, " set src-ip %pSU\n", &pbrms->action_src);
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DST_IP))
+ vty_out(vty, " set dst-ip %pSU\n", &pbrms->action_dst);
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_SRC_PORT))
+ vty_out(vty, " set src-port %d\n", pbrms->action_src_port);
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DST_PORT))
+ vty_out(vty, " set dst-port %d\n", pbrms->action_dst_port);
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DSCP))
+ vty_out(vty, " set dscp %u\n", (pbrms->action_dscp) >> 2);
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_ECN))
+ vty_out(vty, " set ecn %u\n", pbrms->action_ecn);
+
+ /* L2 header fields */
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_STRIP_INNER_ANY))
+ vty_out(vty, " strip vlan any\n");
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_ID))
vty_out(vty, " set vlan %u\n", pbrms->action_vlan_id);
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_PCP))
+ vty_out(vty, " set pcp %d\n", pbrms->action_pcp);
- if (pbrms->action_vlan_flags == PBR_MAP_STRIP_INNER_ANY)
- vty_out(vty, " strip vlan any\n");
+ /* meta */
+ if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_QUEUE_ID))
+ vty_out(vty, " set queue-id %d\n", pbrms->action_queue_id);
- if (pbrms->vrf_unchanged)
+ switch (pbrms->forwarding_type) {
+ case PBR_FT_UNSPEC:
+ break;
+ case PBR_FT_VRF_UNCHANGED:
vty_out(vty, " set vrf unchanged\n");
-
- if (pbrms->vrf_lookup)
+ break;
+ case PBR_FT_SETVRF:
+ assert(pbrms->vrf_name);
vty_out(vty, " set vrf %s\n", pbrms->vrf_name);
-
- if (pbrms->nhgrp_name)
+ break;
+ case PBR_FT_NEXTHOP_GROUP:
+ assert(pbrms->nhgrp_name);
vty_out(vty, " set nexthop-group %s\n", pbrms->nhgrp_name);
-
- if (pbrms->nhg) {
+ break;
+ case PBR_FT_NEXTHOP_SINGLE:
+ assert(pbrms->nhg);
vty_out(vty, " set ");
pbrms_nexthop_group_write_individual_nexthop(vty, pbrms);
+ break;
}
vty_out(vty, "exit\n");
@@ -1394,6 +2206,7 @@ void pbr_vty_init(void)
install_element(CONFIG_NODE, &pbr_set_table_range_cmd);
install_element(CONFIG_NODE, &no_pbr_set_table_range_cmd);
install_element(INTERFACE_NODE, &pbr_policy_cmd);
+
install_element(PBRMAP_NODE, &pbr_map_match_ip_proto_cmd);
install_element(PBRMAP_NODE, &pbr_map_match_src_port_cmd);
install_element(PBRMAP_NODE, &pbr_map_match_dst_port_cmd);
@@ -1401,11 +2214,22 @@ void pbr_vty_init(void)
install_element(PBRMAP_NODE, &pbr_map_match_dst_cmd);
install_element(PBRMAP_NODE, &pbr_map_match_dscp_cmd);
install_element(PBRMAP_NODE, &pbr_map_match_ecn_cmd);
+ install_element(PBRMAP_NODE, &pbr_map_match_vlan_id_cmd);
+ install_element(PBRMAP_NODE, &pbr_map_match_vlan_tag_cmd);
+ install_element(PBRMAP_NODE, &pbr_map_match_pcp_cmd);
install_element(PBRMAP_NODE, &pbr_map_match_mark_cmd);
+
install_element(PBRMAP_NODE, &pbr_map_action_queue_id_cmd);
install_element(PBRMAP_NODE, &pbr_map_action_strip_vlan_cmd);
install_element(PBRMAP_NODE, &pbr_map_action_vlan_id_cmd);
install_element(PBRMAP_NODE, &pbr_map_action_pcp_cmd);
+ install_element(PBRMAP_NODE, &pbr_map_action_src_cmd);
+ install_element(PBRMAP_NODE, &pbr_map_action_dst_cmd);
+ install_element(PBRMAP_NODE, &pbr_map_action_dscp_cmd);
+ install_element(PBRMAP_NODE, &pbr_map_action_ecn_cmd);
+ install_element(PBRMAP_NODE, &pbr_map_action_src_port_cmd);
+ install_element(PBRMAP_NODE, &pbr_map_action_dst_port_cmd);
+
install_element(PBRMAP_NODE, &pbr_map_nexthop_group_cmd);
install_element(PBRMAP_NODE, &no_pbr_map_nexthop_group_cmd);
install_element(PBRMAP_NODE, &pbr_map_nexthop_cmd);
diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c
index 097c9f2964..dd15beaff4 100644
--- a/pbrd/pbr_zebra.c
+++ b/pbrd/pbr_zebra.c
@@ -3,6 +3,9 @@
* Zebra connect code.
* Copyright (C) 2018 Cumulus Networks, Inc.
* Donald Sharp
+ * Portions:
+ * Copyright (c) 2021 The MITRE Corporation.
+ * Copyright (c) 2023 LabN Consulting, L.L.C.
*/
#include <zebra.h>
@@ -20,6 +23,7 @@
#include "log.h"
#include "nexthop.h"
#include "nexthop_group.h"
+#include "pbr.h"
#include "pbr_nht.h"
#include "pbr_map.h"
@@ -125,29 +129,6 @@ int pbr_ifp_down(struct interface *ifp)
return 0;
}
-static int interface_vrf_update(ZAPI_CALLBACK_ARGS)
-{
- struct interface *ifp;
- vrf_id_t new_vrf_id;
-
- ifp = zebra_interface_vrf_update_read(zclient->ibuf, vrf_id,
- &new_vrf_id);
-
- if (!ifp) {
- DEBUGD(&pbr_dbg_zebra, "%s: VRF change interface not found",
- __func__);
-
- return 0;
- }
-
- DEBUGD(&pbr_dbg_zebra, "%s: %s VRF change %u -> %u", __func__,
- ifp->name, vrf_id, new_vrf_id);
-
- if_update_to_new_vrf(ifp, new_vrf_id);
-
- return 0;
-}
-
static int route_notify_owner(ZAPI_CALLBACK_ARGS)
{
struct prefix p;
@@ -197,7 +178,7 @@ static int rule_notify_owner(ZAPI_CALLBACK_ARGS)
enum zapi_rule_notify_owner note;
struct pbr_map_sequence *pbrms;
struct pbr_map_interface *pmi;
- char ifname[INTERFACE_NAMSIZ + 1];
+ char ifname[IFNAMSIZ + 1];
uint64_t installed;
if (!zapi_rule_notify_decode(zclient->ibuf, &seqno, &priority, &unique,
@@ -241,6 +222,8 @@ static int rule_notify_owner(ZAPI_CALLBACK_ARGS)
static void zebra_connected(struct zclient *zclient)
{
DEBUGD(&pbr_dbg_zebra, "%s: Registering for fun and profit", __func__);
+
+ zebra_route_notify_send(ZEBRA_ROUTE_NOTIFY_REQUEST, zclient, true);
zclient_send_reg_requests(zclient, VRF_DEFAULT);
}
@@ -383,38 +366,30 @@ void route_delete(struct pbr_nexthop_group_cache *pnhgc, afi_t afi)
}
}
-static int pbr_zebra_nexthop_update(ZAPI_CALLBACK_ARGS)
+static void pbr_zebra_nexthop_update(struct vrf *vrf, struct prefix *matched,
+ struct zapi_route *nhr)
{
- struct zapi_route nhr;
- struct prefix matched;
uint32_t i;
- if (!zapi_nexthop_update_decode(zclient->ibuf, &matched, &nhr)) {
- zlog_err("Failure to decode Nexthop update message");
- return 0;
- }
-
if (DEBUG_MODE_CHECK(&pbr_dbg_zebra, DEBUG_MODE_ALL)) {
-
DEBUGD(&pbr_dbg_zebra,
"%s: Received Nexthop update: %pFX against %pFX",
- __func__, &matched, &nhr.prefix);
+ __func__, matched, &nhr->prefix);
DEBUGD(&pbr_dbg_zebra, "%s: (Nexthops(%u)", __func__,
- nhr.nexthop_num);
+ nhr->nexthop_num);
- for (i = 0; i < nhr.nexthop_num; i++) {
+ for (i = 0; i < nhr->nexthop_num; i++) {
DEBUGD(&pbr_dbg_zebra,
"%s: Type: %d: vrf: %d, ifindex: %d gate: %pI4",
- __func__, nhr.nexthops[i].type,
- nhr.nexthops[i].vrf_id, nhr.nexthops[i].ifindex,
- &nhr.nexthops[i].gate.ipv4);
+ __func__, nhr->nexthops[i].type,
+ nhr->nexthops[i].vrf_id, nhr->nexthops[i].ifindex,
+ &nhr->nexthops[i].gate.ipv4);
}
}
- nhr.prefix = matched;
- pbr_nht_nexthop_update(&nhr);
- return 1;
+ nhr->prefix = *matched;
+ pbr_nht_nexthop_update(nhr);
}
extern struct zebra_privs_t pbr_privs;
@@ -422,21 +397,28 @@ extern struct zebra_privs_t pbr_privs;
static zclient_handler *const pbr_handlers[] = {
[ZEBRA_INTERFACE_ADDRESS_ADD] = interface_address_add,
[ZEBRA_INTERFACE_ADDRESS_DELETE] = interface_address_delete,
- [ZEBRA_INTERFACE_VRF_UPDATE] = interface_vrf_update,
[ZEBRA_ROUTE_NOTIFY_OWNER] = route_notify_owner,
[ZEBRA_RULE_NOTIFY_OWNER] = rule_notify_owner,
- [ZEBRA_NEXTHOP_UPDATE] = pbr_zebra_nexthop_update,
};
void pbr_zebra_init(void)
{
- struct zclient_options opt = { .receive_notify = true };
-
- zclient = zclient_new(master, &opt, pbr_handlers,
+ zclient = zclient_new(master, &zclient_options_default, pbr_handlers,
array_size(pbr_handlers));
zclient_init(zclient, ZEBRA_ROUTE_PBR, 0, &pbr_privs);
zclient->zebra_connected = zebra_connected;
+ zclient->nexthop_update = pbr_zebra_nexthop_update;
+}
+
+void pbr_zebra_destroy(void)
+{
+ if (zclient == NULL)
+ return;
+
+ zclient_stop(zclient);
+ zclient_free(zclient);
+ zclient = NULL;
}
void pbr_send_rnh(struct nexthop *nhop, bool reg)
@@ -479,27 +461,9 @@ void pbr_send_rnh(struct nexthop *nhop, bool reg)
}
}
-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_vrf(struct stream *s,
- const struct pbr_map_sequence *pbrms,
- const struct interface *ifp)
+static uint32_t pbr_map_sequence_vrf(const struct pbr_map_sequence *pbrms,
+ const struct interface *ifp)
{
struct pbr_vrf *pbr_vrf;
@@ -510,46 +474,116 @@ pbr_encode_pbr_map_sequence_vrf(struct stream *s,
if (!pbr_vrf) {
DEBUGD(&pbr_dbg_zebra, "%s: VRF not found", __func__);
- return;
+ return 0;
}
- stream_putl(s, pbr_vrf->vrf->data.l.table_id);
+ return pbr_vrf->vrf->data.l.table_id;
+
}
-static void pbr_encode_pbr_map_sequence(struct stream *s,
+/*
+ * 230716 gpz note: it would be worthwhile for pbrd to represent
+ * its rules internally using the lib/pbr.h structures to help
+ * move toward a more common structure across pbrd, bgpd, and zebra.
+ */
+static bool pbr_encode_pbr_map_sequence(struct stream *s,
struct pbr_map_sequence *pbrms,
struct interface *ifp)
{
- unsigned char family;
+ struct pbr_rule r;
+ uint8_t family;
+
+ /*
+ * Opportunistic address family field is set when any of the IP
+ * address match/set fields is set, or when a NH/NHG is resolved.
+ * The value is needed by zebra for the underlying netlink
+ * messaging, particularly in delete operations, because it
+ * selects the rule database (IPv4 vs. IPv6).
+ *
+ * Historically the value has been encoded into any unused
+ * "match src/dst address" fields and picked off in zebra.
+ */
family = AF_INET;
if (pbrms->family)
family = pbrms->family;
- stream_putl(s, pbrms->seqno);
- stream_putl(s, pbrms->ruleno);
- stream_putl(s, pbrms->unique);
- stream_putc(s, pbrms->ip_proto); /* The ip_proto */
- pbr_encode_pbr_map_sequence_prefix(s, pbrms->src, family);
- stream_putw(s, pbrms->src_prt);
- pbr_encode_pbr_map_sequence_prefix(s, pbrms->dst, family);
- stream_putw(s, pbrms->dst_prt);
- stream_putc(s, pbrms->dsfield);
- stream_putl(s, pbrms->mark);
+ if (pbrms->src)
+ assert(family == pbrms->src->family);
+ if (pbrms->dst)
+ assert(family == pbrms->dst->family);
- stream_putl(s, pbrms->action_queue_id);
+ /*
+ * Convert struct pbr_map_sequence to canonical form
+ */
+ memset(&r, 0, sizeof(r));
+ r.seq = pbrms->seqno;
+ r.priority = pbrms->ruleno;
+ r.unique = pbrms->unique;
+
+ r.family = pbrms->family;
- stream_putw(s, pbrms->action_vlan_id);
- stream_putw(s, pbrms->action_vlan_flags);
- stream_putw(s, pbrms->action_pcp);
+ /* filter */
+ r.filter.filter_bm = pbrms->filter_bm;
+ if (pbrms->src)
+ r.filter.src_ip = *pbrms->src;
+ else
+ r.filter.src_ip.family = family;
+ if (pbrms->dst)
+ r.filter.dst_ip = *pbrms->dst;
+ else
+ r.filter.dst_ip.family = family;
+ r.filter.src_port = pbrms->src_prt;
+ r.filter.dst_port = pbrms->dst_prt;
+ r.filter.pcp = pbrms->match_pcp;
+ r.filter.vlan_id = pbrms->match_vlan_id;
+ r.filter.vlan_flags = pbrms->match_vlan_flags;
+ r.filter.dsfield = pbrms->dsfield;
+ r.filter.fwmark = pbrms->mark;
+ r.filter.ip_proto = pbrms->ip_proto;
+ r.filter.filter_bm = pbrms->filter_bm;
+
+ /* actions */
+
+ r.action.flags = pbrms->action_bm;
+
+ SET_FLAG(r.action.flags, PBR_ACTION_TABLE); /* always valid */
+
+ /*
+ * if the user does not use the command "set vrf name unchanged"
+ * then pbr_encode_pbr_map_sequence_vrf will not be called
+ */
if (pbrms->vrf_unchanged || pbrms->vrf_lookup)
- pbr_encode_pbr_map_sequence_vrf(s, pbrms, ifp);
+ r.action.table = pbr_map_sequence_vrf(pbrms, ifp);
else if (pbrms->nhgrp_name)
- stream_putl(s, pbr_nht_get_table(pbrms->nhgrp_name));
+ r.action.table = pbr_nht_get_table(pbrms->nhgrp_name);
else if (pbrms->nhg)
- stream_putl(s, pbr_nht_get_table(pbrms->internal_nhg_name));
- stream_put(s, ifp->name, INTERFACE_NAMSIZ);
+ r.action.table = pbr_nht_get_table(pbrms->internal_nhg_name);
+ else {
+ /* Not valid for install without table */
+ return false;
+ }
+
+ r.action.queue_id = pbrms->action_queue_id;
+
+ r.action.src_ip = pbrms->action_src;
+ r.action.dst_ip = pbrms->action_dst;
+
+ r.action.src_port = pbrms->action_src_port;
+ r.action.dst_port = pbrms->action_dst_port;
+
+ r.action.dscp = pbrms->action_dscp;
+ r.action.ecn = pbrms->action_ecn;
+
+ r.action.pcp = pbrms->action_pcp;
+ r.action.vlan_id = pbrms->action_vlan_id;
+
+ strlcpy(r.ifname, ifp->name, sizeof(r.ifname));
+
+ zapi_pbr_rule_encode(s, &r);
+
+ return true;
}
bool pbr_send_pbr_map(struct pbr_map_sequence *pbrms,
@@ -561,9 +595,6 @@ bool pbr_send_pbr_map(struct pbr_map_sequence *pbrms,
is_installed &= pbrms->installed;
- DEBUGD(&pbr_dbg_zebra, "%s: for %s %d(%" PRIu64 ")", __func__,
- pbrm->name, install, is_installed);
-
/*
* If we are installed and asked to do so again and the config
* has not changed, just return.
@@ -584,20 +615,17 @@ bool pbr_send_pbr_map(struct pbr_map_sequence *pbrms,
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: %s %s seq %u %d %s %u", __func__,
install ? "Installing" : "Deleting", pbrm->name, pbrms->seqno,
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);
+ if (pbr_encode_pbr_map_sequence(s, pbrms, pmi->ifp)) {
+ stream_putw_at(s, 0, stream_get_endp(s));
+ zclient_send_message(zclient);
+ } else {
+ DEBUGD(&pbr_dbg_zebra, "%s: %s seq %u encode failed, skipped",
+ __func__, pbrm->name, pbrms->seqno);
+ }
return true;
}
diff --git a/pbrd/pbr_zebra.h b/pbrd/pbr_zebra.h
index ef844ef797..5cbb1fd682 100644
--- a/pbrd/pbr_zebra.h
+++ b/pbrd/pbr_zebra.h
@@ -14,6 +14,7 @@ struct pbr_interface {
extern struct event_loop *master;
extern void pbr_zebra_init(void);
+extern void pbr_zebra_destroy(void);
extern void route_add(struct pbr_nexthop_group_cache *pnhgc,
struct nexthop_group nhg, afi_t install_afi);