2244 lines
58 KiB
C
2244 lines
58 KiB
C
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||
|
/*
|
||
|
* 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>
|
||
|
|
||
|
#include "vty.h"
|
||
|
#include "command.h"
|
||
|
#include "prefix.h"
|
||
|
#include "vrf.h"
|
||
|
#include "nexthop.h"
|
||
|
#include "nexthop_group.h"
|
||
|
#include "nexthop_group_private.h"
|
||
|
#include "log.h"
|
||
|
#include "json.h"
|
||
|
#include "debug.h"
|
||
|
#include "pbr.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"
|
||
|
#include "pbrd/pbr_vty_clippy.c"
|
||
|
|
||
|
/* 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;
|
||
|
|
||
|
pbrms = pbrms_get(pbrm_name, seqno);
|
||
|
VTY_PUSH_CONTEXT(PBRMAP_NODE, pbrms);
|
||
|
|
||
|
return CMD_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/* 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);
|
||
|
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);
|
||
|
pbr_map_sequence_delete(pbrms);
|
||
|
}
|
||
|
|
||
|
return CMD_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/***********************************************************************
|
||
|
* pbrms/rule Match L3 Fields
|
||
|
***********************************************************************/
|
||
|
|
||
|
/*
|
||
|
* 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;
|
||
|
}
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* 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 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 (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 (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_IP)) {
|
||
|
if (prefix_same(pbrms->src, prefix))
|
||
|
return CMD_SUCCESS;
|
||
|
} else
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/* 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 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 (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 (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DST_IP)) {
|
||
|
if (prefix_same(pbrms->dst, prefix))
|
||
|
return CMD_SUCCESS;
|
||
|
} else
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/* 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 = NULL;
|
||
|
|
||
|
if (!pbrms)
|
||
|
return CMD_WARNING_CONFIG_FAILED;
|
||
|
|
||
|
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 (!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;
|
||
|
}
|
||
|
|
||
|
/* 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 (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_PORT))
|
||
|
return CMD_SUCCESS;
|
||
|
UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_PORT);
|
||
|
goto check;
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/* 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 (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DST_PORT))
|
||
|
return CMD_SUCCESS;
|
||
|
UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_DST_PORT);
|
||
|
goto check;
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/* 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);
|
||
|
|
||
|
if (!pbrms)
|
||
|
return CMD_WARNING_CONFIG_FAILED;
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
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->filter_bm, PBR_FILTER_DSCP) &&
|
||
|
((pbrms->dsfield & PBR_DSFIELD_DSCP) == shifted_dscp)) {
|
||
|
return CMD_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/* 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;
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/* 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 (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_ID))
|
||
|
return CMD_SUCCESS;
|
||
|
UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_ID);
|
||
|
goto check;
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/***********************************************************************
|
||
|
* 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)
|
||
|
return CMD_WARNING_CONFIG_FAILED;
|
||
|
|
||
|
#ifndef GNU_LINUX
|
||
|
vty_out(vty, "pbr marks are not supported on this platform\n");
|
||
|
return CMD_WARNING_CONFIG_FAILED;
|
||
|
#endif
|
||
|
|
||
|
if (no) {
|
||
|
if (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_FWMARK))
|
||
|
return CMD_SUCCESS;
|
||
|
UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_FWMARK);
|
||
|
goto check;
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/***********************************************************************
|
||
|
* 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")
|
||
|
{
|
||
|
/* 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;
|
||
|
}
|
||
|
|
||
|
/* 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")
|
||
|
{
|
||
|
/* 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;
|
||
|
}
|
||
|
|
||
|
/* 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")
|
||
|
{
|
||
|
/* 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;
|
||
|
}
|
||
|
|
||
|
/* 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")
|
||
|
{
|
||
|
/* 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_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;
|
||
|
|
||
|
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) {
|
||
|
if (!CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DSCP))
|
||
|
return CMD_SUCCESS;
|
||
|
UNSET_FLAG(pbrms->action_bm, PBR_ACTION_DSCP);
|
||
|
goto check;
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/* 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) {
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***********************************************************************
|
||
|
* 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) {
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/* 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) {
|
||
|
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"
|
||
|
"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)
|
||
|
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 (pbrms->nhgrp_name && strcmp(name, pbrms->nhgrp_name) == 0)
|
||
|
return CMD_SUCCESS;
|
||
|
|
||
|
/* This is new/replacement config */
|
||
|
pbrms_clear_set_config(pbrms);
|
||
|
|
||
|
pbr_nht_set_seq_nhg(pbrms, name);
|
||
|
|
||
|
pbr_map_check(pbrms, true);
|
||
|
|
||
|
return CMD_SUCCESS;
|
||
|
}
|
||
|
|
||
|
DEFPY(no_pbr_map_nexthop_group, no_pbr_map_nexthop_group_cmd,
|
||
|
"no set nexthop-group [NHGNAME$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);
|
||
|
|
||
|
if (!pbrms)
|
||
|
return CMD_WARNING_CONFIG_FAILED;
|
||
|
|
||
|
pbrms_clear_set_config(pbrms);
|
||
|
|
||
|
return CMD_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/* 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"
|
||
|
"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;
|
||
|
struct nexthop *nh = NULL;
|
||
|
|
||
|
if (!pbrms)
|
||
|
return CMD_WARNING_CONFIG_FAILED;
|
||
|
|
||
|
if (vrf_name)
|
||
|
vrf = vrf_lookup_by_name(vrf_name);
|
||
|
else
|
||
|
vrf = vrf_lookup_by_id(VRF_DEFAULT);
|
||
|
|
||
|
if (!vrf) {
|
||
|
vty_out(vty, "Specified VRF: %s is non-existent\n", vrf_name);
|
||
|
return CMD_WARNING_CONFIG_FAILED;
|
||
|
}
|
||
|
|
||
|
memset(&nhop, 0, sizeof(nhop));
|
||
|
nhop.vrf_id = vrf->vrf_id;
|
||
|
|
||
|
if (intf) {
|
||
|
struct interface *ifp = NULL;
|
||
|
struct interface *ifptmp;
|
||
|
struct vrf *vrftmp;
|
||
|
int count = 0;
|
||
|
|
||
|
if (vrf_is_backend_netns() && vrf_name) {
|
||
|
ifp = if_lookup_by_name_vrf(intf, vrf);
|
||
|
} else {
|
||
|
RB_FOREACH (vrftmp, vrf_name_head, &vrfs_by_name) {
|
||
|
ifptmp = if_lookup_by_name_vrf(intf, vrftmp);
|
||
|
if (ifptmp) {
|
||
|
ifp = ifptmp;
|
||
|
count++;
|
||
|
if (!vrf_is_backend_netns())
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!ifp) {
|
||
|
vty_out(vty, "Specified Intf %s does not exist\n",
|
||
|
intf);
|
||
|
return CMD_WARNING_CONFIG_FAILED;
|
||
|
}
|
||
|
if (count > 1) {
|
||
|
vty_out(vty,
|
||
|
"Specified Intf %s exists in multiple VRFs\n",
|
||
|
intf);
|
||
|
vty_out(vty, "You must specify the nexthop-vrf\n");
|
||
|
return CMD_WARNING_CONFIG_FAILED;
|
||
|
}
|
||
|
if (ifp->vrf->vrf_id != vrf->vrf_id)
|
||
|
vty_out(vty,
|
||
|
"Specified Intf %s is not in vrf %s but is in vrf %s, using actual vrf\n",
|
||
|
ifp->name, vrf->name, ifp->vrf->name);
|
||
|
nhop.ifindex = ifp->ifindex;
|
||
|
nhop.vrf_id = ifp->vrf->vrf_id;
|
||
|
}
|
||
|
|
||
|
if (addr) {
|
||
|
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;
|
||
|
else
|
||
|
nhop.type = NEXTHOP_TYPE_IPV4;
|
||
|
} else {
|
||
|
nhop.gate.ipv6 = addr->sin6.sin6_addr;
|
||
|
if (intf)
|
||
|
nhop.type = NEXTHOP_TYPE_IPV6_IFINDEX;
|
||
|
else {
|
||
|
if (IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) {
|
||
|
vty_out(vty,
|
||
|
"Specified a v6 LL with no interface, rejecting\n");
|
||
|
return CMD_WARNING_CONFIG_FAILED;
|
||
|
}
|
||
|
nhop.type = NEXTHOP_TYPE_IPV6;
|
||
|
}
|
||
|
}
|
||
|
} 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);
|
||
|
|
||
|
if (nh) /* Same config re-entered */
|
||
|
goto done;
|
||
|
|
||
|
/* This is new/replacement config */
|
||
|
pbrms_clear_set_config(pbrms);
|
||
|
|
||
|
pbr_nht_add_individual_nexthop(pbrms, &nhop);
|
||
|
|
||
|
pbr_map_check(pbrms, true);
|
||
|
|
||
|
done:
|
||
|
if (nhop.type == NEXTHOP_TYPE_IFINDEX
|
||
|
|| (nhop.type == NEXTHOP_TYPE_IPV6_IFINDEX
|
||
|
&& IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6))) {
|
||
|
struct interface *ifp;
|
||
|
|
||
|
ifp = if_lookup_by_index(nhop.ifindex, nhop.vrf_id);
|
||
|
if (ifp)
|
||
|
pbr_nht_nexthop_interface_update(ifp);
|
||
|
}
|
||
|
|
||
|
return CMD_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/* 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"
|
||
|
"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)
|
||
|
return CMD_WARNING_CONFIG_FAILED;
|
||
|
|
||
|
pbrms_clear_set_config(pbrms);
|
||
|
|
||
|
return CMD_SUCCESS;
|
||
|
}
|
||
|
|
||
|
DEFPY(pbr_map_vrf, pbr_map_vrf_cmd,
|
||
|
"set vrf <NAME$vrf_name|unchanged>",
|
||
|
"Set for the PBR-MAP\n"
|
||
|
"Specify the VRF for this map\n"
|
||
|
"The VRF Name\n"
|
||
|
"Use the interface's VRF for lookup\n")
|
||
|
{
|
||
|
struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
|
||
|
|
||
|
if (!pbrms)
|
||
|
return CMD_WARNING_CONFIG_FAILED;
|
||
|
|
||
|
/*
|
||
|
* If an equivalent set vrf * exists, just return success.
|
||
|
*/
|
||
|
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->forwarding_type == PBR_FT_VRF_UNCHANGED))
|
||
|
/* Unchanged already set */
|
||
|
return CMD_SUCCESS;
|
||
|
|
||
|
if (vrf_name && !pbr_vrf_lookup_by_name(vrf_name)) {
|
||
|
vty_out(vty, "Specified: %s is non-existent\n", vrf_name);
|
||
|
return CMD_WARNING_CONFIG_FAILED;
|
||
|
}
|
||
|
|
||
|
/* This is new/replacement config */
|
||
|
pbrms_clear_set_config(pbrms);
|
||
|
|
||
|
if (vrf_name) {
|
||
|
pbrms->vrf_lookup = true;
|
||
|
pbrms->forwarding_type = PBR_FT_SETVRF;
|
||
|
strlcpy(pbrms->vrf_name, vrf_name, sizeof(pbrms->vrf_name));
|
||
|
} else {
|
||
|
pbrms->forwarding_type = PBR_FT_VRF_UNCHANGED;
|
||
|
pbrms->vrf_unchanged = true;
|
||
|
}
|
||
|
|
||
|
pbr_map_check(pbrms, true);
|
||
|
|
||
|
return CMD_SUCCESS;
|
||
|
}
|
||
|
|
||
|
DEFPY(no_pbr_map_vrf, no_pbr_map_vrf_cmd,
|
||
|
"no set vrf [<NAME$vrf_name|unchanged>]",
|
||
|
NO_STR
|
||
|
"Set for the PBR-MAP\n"
|
||
|
"Specify the VRF for this map\n"
|
||
|
"The VRF Name\n"
|
||
|
"Use the interface's VRF for lookup\n")
|
||
|
{
|
||
|
struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
|
||
|
|
||
|
if (!pbrms)
|
||
|
return CMD_WARNING_CONFIG_FAILED;
|
||
|
|
||
|
pbrms_clear_set_config(pbrms);
|
||
|
|
||
|
return CMD_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/***********************************************************************
|
||
|
* 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;
|
||
|
|
||
|
old_pbrm = NULL;
|
||
|
pbrm = pbrm_find(mapname);
|
||
|
|
||
|
if (!pbr_ifp) {
|
||
|
/* we don't want one and we don't have one, so... */
|
||
|
if (no)
|
||
|
return CMD_SUCCESS;
|
||
|
|
||
|
/* Some one could have fat fingered the interface name */
|
||
|
pbr_ifp = pbr_if_new(ifp);
|
||
|
}
|
||
|
|
||
|
if (no) {
|
||
|
if (strcmp(pbr_ifp->mapname, mapname) == 0) {
|
||
|
pbr_ifp->mapname[0] = '\0';
|
||
|
if (pbrm)
|
||
|
pbr_map_interface_delete(pbrm, ifp);
|
||
|
}
|
||
|
} else {
|
||
|
if (strcmp(pbr_ifp->mapname, "") != 0) {
|
||
|
old_pbrm = pbrm_find(pbr_ifp->mapname);
|
||
|
|
||
|
/*
|
||
|
* So if we have an old pbrm we should only
|
||
|
* delete it if we are actually deleting and
|
||
|
* moving to a new pbrm
|
||
|
*/
|
||
|
if (old_pbrm && old_pbrm != pbrm)
|
||
|
pbr_map_interface_delete(old_pbrm, ifp);
|
||
|
}
|
||
|
snprintf(pbr_ifp->mapname, sizeof(pbr_ifp->mapname),
|
||
|
"%s", mapname);
|
||
|
|
||
|
/*
|
||
|
* So only reinstall if the old_pbrm and this pbrm are
|
||
|
* different.
|
||
|
*/
|
||
|
if (pbrm && pbrm != old_pbrm)
|
||
|
pbr_map_add_interface(pbrm, ifp);
|
||
|
}
|
||
|
|
||
|
return CMD_SUCCESS;
|
||
|
}
|
||
|
|
||
|
DEFPY (show_pbr,
|
||
|
show_pbr_cmd,
|
||
|
"show pbr",
|
||
|
SHOW_STR
|
||
|
PBR_STR)
|
||
|
{
|
||
|
pbr_nht_write_table_range(vty);
|
||
|
pbr_nht_write_rule_range(vty);
|
||
|
|
||
|
return CMD_SUCCESS;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
pbrms_nexthop_group_write_individual_nexthop(
|
||
|
struct vty *vty, const struct pbr_map_sequence *pbrms)
|
||
|
{
|
||
|
struct pbr_nexthop_group_cache find;
|
||
|
struct pbr_nexthop_group_cache *pnhgc;
|
||
|
struct pbr_nexthop_cache lookup;
|
||
|
struct pbr_nexthop_cache *pnhc;
|
||
|
|
||
|
memset(&find, 0, sizeof(find));
|
||
|
strlcpy(find.name, pbrms->internal_nhg_name, sizeof(find.name));
|
||
|
|
||
|
pnhgc = hash_lookup(pbr_nhg_hash, &find);
|
||
|
assert(pnhgc);
|
||
|
|
||
|
lookup.nexthop = *pbrms->nhg->nexthop;
|
||
|
pnhc = hash_lookup(pnhgc->nhh, &lookup);
|
||
|
|
||
|
nexthop_group_write_nexthop_simple(
|
||
|
vty, pbrms->nhg->nexthop,
|
||
|
pnhc->nexthop.ifindex != 0 ? pnhc->intf_name : NULL);
|
||
|
if (pnhc->nexthop.vrf_id != VRF_DEFAULT)
|
||
|
vty_out(vty, " nexthop-vrf %s", pnhc->vrf_name);
|
||
|
|
||
|
vty_out(vty, "\n");
|
||
|
}
|
||
|
|
||
|
static void vty_show_pbrms(struct vty *vty,
|
||
|
const struct pbr_map_sequence *pbrms, bool detail)
|
||
|
{
|
||
|
char rbuf[64];
|
||
|
|
||
|
if (pbrms->reason)
|
||
|
pbr_map_reason_string(pbrms->reason, rbuf, sizeof(rbuf));
|
||
|
|
||
|
vty_out(vty, " Seq: %u rule: %u\n", pbrms->seqno, pbrms->ruleno);
|
||
|
|
||
|
if (detail)
|
||
|
vty_out(vty, " Installed: %" PRIu64 "(%u) Reason: %s\n",
|
||
|
pbrms->installed, pbrms->unique,
|
||
|
pbrms->reason ? rbuf : "Valid");
|
||
|
else
|
||
|
vty_out(vty, " Installed: %s Reason: %s\n",
|
||
|
pbrms->installed ? "yes" : "no",
|
||
|
pbrms->reason ? rbuf : "Valid");
|
||
|
|
||
|
/* 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 (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_IP))
|
||
|
vty_out(vty, " SRC IP Match: %pFX\n", pbrms->src);
|
||
|
if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DST_IP))
|
||
|
vty_out(vty, " DST IP Match: %pFX\n", pbrms->dst);
|
||
|
|
||
|
if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_PORT))
|
||
|
vty_out(vty, " SRC Port Match: %u\n", pbrms->src_prt);
|
||
|
if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DST_PORT))
|
||
|
vty_out(vty, " DST Port Match: %u\n", pbrms->dst_prt);
|
||
|
|
||
|
if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DSCP))
|
||
|
vty_out(vty, " DSCP Match: %u\n",
|
||
|
(pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2);
|
||
|
if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_ECN))
|
||
|
vty_out(vty, " ECN Match: %u\n",
|
||
|
pbrms->dsfield & PBR_DSFIELD_ECN);
|
||
|
|
||
|
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");
|
||
|
}
|
||
|
|
||
|
/* 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 (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_ID))
|
||
|
vty_out(vty, " Set VLAN ID %u\n", pbrms->action_vlan_id);
|
||
|
if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_STRIP_INNER_ANY))
|
||
|
vty_out(vty, " Strip VLAN ID\n");
|
||
|
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);
|
||
|
|
||
|
|
||
|
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)
|
||
|
vty_out(vty,
|
||
|
" Installed: %u(%d) Tableid: %u\n",
|
||
|
pbrms->nhs_installed,
|
||
|
pbr_nht_get_installed(pbrms->nhgrp_name),
|
||
|
pbr_nht_get_table(pbrms->nhgrp_name));
|
||
|
else
|
||
|
vty_out(vty, " Installed: %s Tableid: %u\n",
|
||
|
pbr_nht_get_installed(pbrms->nhgrp_name) ? "yes"
|
||
|
: "no",
|
||
|
pbr_nht_get_table(pbrms->nhgrp_name));
|
||
|
break;
|
||
|
case PBR_FT_NEXTHOP_SINGLE:
|
||
|
assert(pbrms->internal_nhg_name);
|
||
|
vty_out(vty, " ");
|
||
|
pbrms_nexthop_group_write_individual_nexthop(vty, pbrms);
|
||
|
if (detail)
|
||
|
vty_out(vty,
|
||
|
" Installed: %u(%d) Tableid: %u\n",
|
||
|
pbrms->nhs_installed,
|
||
|
pbr_nht_get_installed(pbrms->internal_nhg_name),
|
||
|
pbr_nht_get_table(pbrms->internal_nhg_name));
|
||
|
else
|
||
|
vty_out(vty, " Installed: %s Tableid: %u\n",
|
||
|
pbr_nht_get_installed(pbrms->internal_nhg_name)
|
||
|
? "yes"
|
||
|
: "no",
|
||
|
pbr_nht_get_table(pbrms->internal_nhg_name));
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void vty_json_pbrms(json_object *j, struct vty *vty,
|
||
|
const struct pbr_map_sequence *pbrms)
|
||
|
{
|
||
|
json_object *jpbrm, *nexthop_group;
|
||
|
char *nhg_name = pbrms->nhgrp_name ? pbrms->nhgrp_name
|
||
|
: pbrms->internal_nhg_name;
|
||
|
char rbuf[64];
|
||
|
|
||
|
jpbrm = json_object_new_object();
|
||
|
|
||
|
json_object_int_add(jpbrm, "id", pbrms->unique);
|
||
|
|
||
|
if (pbrms->reason)
|
||
|
pbr_map_reason_string(pbrms->reason, rbuf, sizeof(rbuf));
|
||
|
|
||
|
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", pbrms->installed);
|
||
|
json_object_string_add(jpbrm, "installedReason",
|
||
|
pbrms->reason ? rbuf : "Valid");
|
||
|
|
||
|
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",
|
||
|
pbr_nht_get_table(nhg_name));
|
||
|
json_object_string_add(nexthop_group, "name", nhg_name);
|
||
|
json_object_boolean_add(nexthop_group, "installed",
|
||
|
pbr_nht_get_installed(nhg_name));
|
||
|
json_object_int_add(nexthop_group, "installedInternally",
|
||
|
pbrms->nhs_installed);
|
||
|
|
||
|
json_object_object_add(jpbrm, "nexthopGroup", nexthop_group);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* 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 (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DST_IP))
|
||
|
json_object_string_addf(jpbrm, "matchDst", "%pFX", pbrms->dst);
|
||
|
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 (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);
|
||
|
}
|
||
|
|
||
|
static void vty_show_pbr_map(struct vty *vty, const struct pbr_map *pbrm,
|
||
|
bool detail)
|
||
|
{
|
||
|
struct pbr_map_sequence *pbrms;
|
||
|
struct listnode *node;
|
||
|
|
||
|
vty_out(vty, " pbr-map %s valid: %s\n", pbrm->name,
|
||
|
pbrm->valid ? "yes" : "no");
|
||
|
|
||
|
for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms))
|
||
|
vty_show_pbrms(vty, pbrms, detail);
|
||
|
}
|
||
|
|
||
|
static void vty_json_pbr_map(json_object *j, struct vty *vty,
|
||
|
const struct pbr_map *pbrm)
|
||
|
{
|
||
|
struct pbr_map_sequence *pbrms;
|
||
|
struct listnode *node;
|
||
|
json_object *jpbrms;
|
||
|
|
||
|
json_object_string_add(j, "name", pbrm->name);
|
||
|
json_object_boolean_add(j, "valid", pbrm->valid);
|
||
|
|
||
|
jpbrms = json_object_new_array();
|
||
|
|
||
|
for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms))
|
||
|
vty_json_pbrms(jpbrms, vty, pbrms);
|
||
|
|
||
|
json_object_object_add(j, "policies", jpbrms);
|
||
|
}
|
||
|
|
||
|
DEFPY (show_pbr_map,
|
||
|
show_pbr_map_cmd,
|
||
|
"show pbr map [NAME$name] [detail$detail] [json$json]",
|
||
|
SHOW_STR
|
||
|
PBR_STR
|
||
|
"PBR Map\n"
|
||
|
"PBR Map Name\n"
|
||
|
"Detailed information\n"
|
||
|
JSON_STR)
|
||
|
{
|
||
|
struct pbr_map *pbrm;
|
||
|
json_object *j = NULL;
|
||
|
|
||
|
if (json)
|
||
|
j = json_object_new_array();
|
||
|
|
||
|
RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) {
|
||
|
json_object *this_map = NULL;
|
||
|
if (name && strcmp(name, pbrm->name) != 0)
|
||
|
continue;
|
||
|
|
||
|
if (j)
|
||
|
this_map = json_object_new_object();
|
||
|
|
||
|
if (this_map) {
|
||
|
vty_json_pbr_map(this_map, vty, pbrm);
|
||
|
|
||
|
json_object_array_add(j, this_map);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
vty_show_pbr_map(vty, pbrm, detail);
|
||
|
}
|
||
|
|
||
|
if (j)
|
||
|
vty_json(vty, j);
|
||
|
|
||
|
return CMD_SUCCESS;
|
||
|
}
|
||
|
|
||
|
DEFPY(show_pbr_nexthop_group,
|
||
|
show_pbr_nexthop_group_cmd,
|
||
|
"show pbr nexthop-groups [WORD$word] [json$json]",
|
||
|
SHOW_STR
|
||
|
PBR_STR
|
||
|
"Nexthop Groups\n"
|
||
|
"Optional Name of the nexthop group\n"
|
||
|
JSON_STR)
|
||
|
{
|
||
|
json_object *j = NULL;
|
||
|
|
||
|
if (json)
|
||
|
j = json_object_new_array();
|
||
|
|
||
|
if (j) {
|
||
|
pbr_nht_json_nexthop_group(j, word);
|
||
|
|
||
|
vty_json(vty, j);
|
||
|
} else
|
||
|
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
|
||
|
PBR_STR
|
||
|
"PBR Interface\n"
|
||
|
"PBR Interface Name\n"
|
||
|
JSON_STR)
|
||
|
{
|
||
|
struct interface *ifp;
|
||
|
struct vrf *vrf;
|
||
|
struct pbr_interface *pbr_ifp;
|
||
|
json_object *j = NULL;
|
||
|
|
||
|
if (json)
|
||
|
j = json_object_new_array();
|
||
|
|
||
|
RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) {
|
||
|
FOR_ALL_INTERFACES(vrf, ifp) {
|
||
|
struct pbr_map *pbrm;
|
||
|
json_object *this_iface = NULL;
|
||
|
|
||
|
if (j)
|
||
|
this_iface = json_object_new_object();
|
||
|
|
||
|
if (!ifp->info) {
|
||
|
json_object_free(this_iface);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (name && strcmp(ifp->name, name) != 0) {
|
||
|
json_object_free(this_iface);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
pbr_ifp = ifp->info;
|
||
|
|
||
|
if (strcmp(pbr_ifp->mapname, "") == 0) {
|
||
|
json_object_free(this_iface);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
pbrm = pbrm_find(pbr_ifp->mapname);
|
||
|
|
||
|
if (this_iface) {
|
||
|
json_object_string_add(this_iface, "name",
|
||
|
ifp->name);
|
||
|
json_object_int_add(this_iface, "index",
|
||
|
ifp->ifindex);
|
||
|
json_object_string_add(this_iface, "policy",
|
||
|
pbr_ifp->mapname);
|
||
|
json_object_boolean_add(this_iface, "valid",
|
||
|
pbrm);
|
||
|
|
||
|
json_object_array_add(j, this_iface);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
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");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (j)
|
||
|
vty_json(vty, j);
|
||
|
|
||
|
return CMD_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/* PBR debugging CLI ------------------------------------------------------- */
|
||
|
|
||
|
static struct cmd_node debug_node = {
|
||
|
.name = "debug",
|
||
|
.node = DEBUG_NODE,
|
||
|
.prompt = "",
|
||
|
.config_write = pbr_debug_config_write,
|
||
|
};
|
||
|
|
||
|
DEFPY(debug_pbr,
|
||
|
debug_pbr_cmd,
|
||
|
"[no] debug pbr [{map$map|zebra$zebra|nht$nht|events$events}]",
|
||
|
NO_STR
|
||
|
DEBUG_STR
|
||
|
PBR_STR
|
||
|
"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
|
||
|
PBR_STR)
|
||
|
{
|
||
|
vty_out(vty, "PBR debugging status:\n");
|
||
|
|
||
|
pbr_debug_config_write_helper(vty, false);
|
||
|
|
||
|
cmd_show_lib_debugs(vty);
|
||
|
|
||
|
return CMD_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/* ------------------------------------------------------------------------- */
|
||
|
|
||
|
|
||
|
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_vty_config_start(vty, ifp);
|
||
|
|
||
|
if (ifp->desc)
|
||
|
vty_out(vty, " description %s\n", ifp->desc);
|
||
|
|
||
|
pbr_map_write_interfaces(vty, ifp);
|
||
|
|
||
|
if_vty_config_end(vty);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static int pbr_vty_map_config_write(struct vty *vty);
|
||
|
/* PBR map node structure. */
|
||
|
static struct cmd_node pbr_map_node = {
|
||
|
.name = "pbr-map",
|
||
|
.node = PBRMAP_NODE,
|
||
|
.parent_node = CONFIG_NODE,
|
||
|
.prompt = "%s(config-pbr-map)# ",
|
||
|
.config_write = pbr_vty_map_config_write,
|
||
|
};
|
||
|
|
||
|
static int pbr_vty_map_config_write_sequence(struct vty *vty,
|
||
|
struct pbr_map *pbrm,
|
||
|
struct pbr_map_sequence *pbrms)
|
||
|
{
|
||
|
vty_out(vty, "pbr-map %s seq %u\n", pbrm->name, pbrms->seqno);
|
||
|
|
||
|
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 (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DST_IP))
|
||
|
vty_out(vty, " match dst-ip %pFX\n", pbrms->dst);
|
||
|
|
||
|
if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_PORT))
|
||
|
vty_out(vty, " match src-port %u\n", pbrms->src_prt);
|
||
|
if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DST_PORT))
|
||
|
vty_out(vty, " match dst-port %u\n", pbrms->dst_prt);
|
||
|
|
||
|
if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DSCP))
|
||
|
vty_out(vty, " match dscp %u\n",
|
||
|
(pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2);
|
||
|
|
||
|
if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_ECN))
|
||
|
vty_out(vty, " match ecn %u\n",
|
||
|
pbrms->dsfield & PBR_DSFIELD_ECN);
|
||
|
|
||
|
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");
|
||
|
}
|
||
|
|
||
|
/* meta */
|
||
|
if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_FWMARK))
|
||
|
vty_out(vty, " match mark %u\n", pbrms->mark);
|
||
|
|
||
|
/*
|
||
|
* action clauses
|
||
|
*/
|
||
|
|
||
|
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);
|
||
|
|
||
|
/* meta */
|
||
|
if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_QUEUE_ID))
|
||
|
vty_out(vty, " set queue-id %d\n", pbrms->action_queue_id);
|
||
|
|
||
|
switch (pbrms->forwarding_type) {
|
||
|
case PBR_FT_UNSPEC:
|
||
|
break;
|
||
|
case PBR_FT_VRF_UNCHANGED:
|
||
|
vty_out(vty, " set vrf unchanged\n");
|
||
|
break;
|
||
|
case PBR_FT_SETVRF:
|
||
|
assert(pbrms->vrf_name);
|
||
|
vty_out(vty, " set vrf %s\n", pbrms->vrf_name);
|
||
|
break;
|
||
|
case PBR_FT_NEXTHOP_GROUP:
|
||
|
assert(pbrms->nhgrp_name);
|
||
|
vty_out(vty, " set nexthop-group %s\n", pbrms->nhgrp_name);
|
||
|
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");
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
static void pbr_map_completer(vector comps, struct cmd_token *token)
|
||
|
{
|
||
|
struct pbr_map *pbrm;
|
||
|
|
||
|
RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps)
|
||
|
vector_set(comps, XSTRDUP(MTYPE_COMPLETION, pbrm->name));
|
||
|
}
|
||
|
|
||
|
static const struct cmd_variable_handler pbr_map_name[] = {
|
||
|
{
|
||
|
.tokenname = "PBRMAP", .completions = pbr_map_completer,
|
||
|
},
|
||
|
{
|
||
|
.completions = NULL
|
||
|
}
|
||
|
};
|
||
|
|
||
|
extern struct zebra_privs_t pbr_privs;
|
||
|
|
||
|
void pbr_vty_init(void)
|
||
|
{
|
||
|
cmd_variable_handler_register(pbr_map_name);
|
||
|
|
||
|
vrf_cmd_init(NULL);
|
||
|
|
||
|
if_cmd_init(pbr_interface_config_write);
|
||
|
|
||
|
install_node(&pbr_map_node);
|
||
|
|
||
|
/* debug */
|
||
|
install_node(&debug_node);
|
||
|
install_element(ENABLE_NODE, &debug_pbr_cmd);
|
||
|
install_element(CONFIG_NODE, &debug_pbr_cmd);
|
||
|
install_element(ENABLE_NODE, &show_debugging_pbr_cmd);
|
||
|
|
||
|
install_default(PBRMAP_NODE);
|
||
|
|
||
|
install_element(CONFIG_NODE, &pbr_map_cmd);
|
||
|
install_element(CONFIG_NODE, &no_pbr_map_cmd);
|
||
|
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);
|
||
|
install_element(PBRMAP_NODE, &pbr_map_match_src_cmd);
|
||
|
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);
|
||
|
install_element(PBRMAP_NODE, &no_pbr_map_nexthop_cmd);
|
||
|
install_element(PBRMAP_NODE, &pbr_map_vrf_cmd);
|
||
|
install_element(PBRMAP_NODE, &no_pbr_map_vrf_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);
|
||
|
}
|