pbrd: add vlan filters pcp/vlan-id/vlan-flags; ip-protocol any (pbr feature)

Subset: feature in PBR

    New PBR rule fields:

    match ip-protocol (was only tcp|udp, now any value in /etc/protocols)
    match pcp (0-7)
    match vlan (1-4094)
    match vlan (tagged|untagged|untagged-or-zero)

    Filter flags
	Add filter_bm (flags) field internally to indicate which
	filter fields should be considered active. Bit definitions
	as in lib/pbr.h.

	This commit uses only the PBR_FILTER_PCP bit, but other
	fields will be added in future commits. (Fixes bug related
	to determining set/not-set state of pcp filter)

	Shift vlan filter flags to lib/pbr.h

    Changes by:
	Josh Werner <joshuawerner@mitre.org>
	Eli Baum <ebaum@mitre.org>
	G. Paul Ziemba <paulz@labn.net>

Signed-off-by: G. Paul Ziemba <paulz@labn.net>
This commit is contained in:
G. Paul Ziemba 2023-07-19 07:58:02 -07:00
parent ff10abcc89
commit bfd3e8e012
4 changed files with 224 additions and 43 deletions

View File

@ -1,6 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/* Policy Based Routing (PBR) main header
* Copyright (C) 2018 6WIND
* Portions:
* Copyright (c) 2021 The MITRE Corporation.
* Copyright (c) 2023 LabN Consulting, L.L.C.
*/
#ifndef _PBR_H
@ -25,8 +28,7 @@ extern "C" {
* specified.
*/
struct pbr_filter {
uint32_t filter_bm; /* not encoded by zapi
*/
uint32_t filter_bm;
#define PBR_FILTER_SRC_IP (1 << 0)
#define PBR_FILTER_DST_IP (1 << 1)
#define PBR_FILTER_SRC_PORT (1 << 2)
@ -37,18 +39,32 @@ struct pbr_filter {
#define PBR_FILTER_DST_PORT_RANGE (1 << 7)
#define PBR_FILTER_DSFIELD (1 << 8)
#define PBR_FILTER_IP_PROTOCOL (1 << 9)
#define PBR_FILTER_PCP (1 << 10)
#define PBR_FILTER_VLAN_FLAGS (1 << 11)
#define PBR_FILTER_VLAN_ID (1 << 12)
#define PBR_DSFIELD_DSCP (0xfc) /* Upper 6 bits of DS field: DSCP */
#define PBR_DSFIELD_ECN (0x03) /* Lower 2 bits of DS field: BCN */
#define PBR_PCP (0x07) /* 3-bit value 0..7 for prioritization*/
/* Source and Destination IP address with masks. */
#define PBR_VLAN_FLAGS_NO_WILD 0
#define PBR_VLAN_FLAGS_TAGGED (1 << 0)
#define PBR_VLAN_FLAGS_UNTAGGED (1 << 1)
#define PBR_VLAN_FLAGS_UNTAGGED_0 (1 << 2)
/* Source and Destination IP address with masks */
struct prefix src_ip;
struct prefix dst_ip;
/* Source and Destination higher-layer (TCP/UDP) port numbers. */
/* Source and Destination higher-layer (TCP/UDP) port numbers */
uint16_t src_port;
uint16_t dst_port;
/* Filter by VLAN and prioritization */
uint8_t pcp;
uint16_t vlan_id;
uint16_t vlan_flags;
/* Filter by Differentiated Services field */
uint8_t dsfield; /* DSCP (6 bits) & ECN (2 bits) */

View File

@ -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"
@ -101,6 +105,38 @@ static bool pbrms_is_installed(const struct pbr_map_sequence *pbrms,
return false;
}
void pbr_set_match_clause_for_pcp(struct pbr_map_sequence *pbrms, bool set,
uint8_t pcp)
{
bool changed = false;
if (set) {
if (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_PCP) ||
(pcp != pbrms->match_pcp)) {
SET_FLAG(pbrms->filter_bm, PBR_FILTER_PCP);
pbrms->match_pcp = pcp;
changed = true;
}
} else {
if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_PCP)) {
UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_PCP);
changed = true;
}
}
if (changed)
pbr_map_check(pbrms, true);
}
void pbr_set_match_clause_for_vlan(struct pbr_map_sequence *pbrms,
uint16_t vlan_id, uint16_t vlan_flags)
{
if (pbrms) {
pbrms->match_vlan_id = vlan_id;
pbrms->match_vlan_flags = vlan_flags;
pbr_map_check(pbrms, true);
}
}
/* If any sequence is installed on the interface, assume installed */
static bool
pbr_map_interface_is_installed(const struct pbr_map *pbrm,
@ -486,9 +522,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,6 +562,10 @@ struct pbr_map_sequence *pbrms_get(const char *name, uint32_t seqno)
pbrms->ruleno = pbr_nht_get_next_rule(seqno);
pbrms->parent = pbrm;
pbrms->match_vlan_id = 0;
pbrms->match_vlan_flags = 0;
pbrms->match_pcp = 0;
pbrms->action_vlan_id = 0;
pbrms->action_vlan_flags = 0;
pbrms->action_pcp = 0;
@ -594,10 +634,12 @@ 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)
if (!pbrms->src && !pbrms->dst && !pbrms->mark && !pbrms->dsfield &&
!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_PCP) &&
!pbrms->action_pcp && !pbrms->match_vlan_id &&
!pbrms->match_vlan_flags && !pbrms->action_vlan_id &&
!pbrms->action_vlan_flags &&
pbrms->action_queue_id == PBR_MAP_UNDEFINED_QUEUE_ID)
pbrms->reason |= PBR_MAP_INVALID_EMPTY;
}
@ -734,7 +776,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 */

View File

@ -3,6 +3,7 @@
* PBR-map Header
* Copyright (C) 2018 Cumulus Networks, Inc.
* Donald Sharp
* Copyright (c) 2023 LabN Consulting, L.L.C.
*/
#ifndef __PBR_MAP_H__
#define __PBR_MAP_H__
@ -71,28 +72,43 @@ 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;
/*
* Actions
*/
/*****************************************************************
* Action fields
*****************************************************************/
uint8_t action_pcp;
uint8_t action_vlan_id;
#define PBR_MAP_STRIP_INNER_ANY (1 << 0)
@ -101,11 +117,6 @@ struct pbr_map_sequence {
#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;
/*
* Use interface's vrf.
*/
@ -222,4 +233,9 @@ extern void pbr_map_check_vrf_nh_group_change(const char *nh_group,
extern void pbr_map_check_interface_nh_group_change(const char *nh_group,
struct interface *ifp,
ifindex_t oldifindex);
extern void pbr_set_match_clause_for_vlan(struct pbr_map_sequence *pbrms,
uint16_t vlan_id,
uint16_t vlan_flags);
extern void pbr_set_match_clause_for_pcp(struct pbr_map_sequence *pbrms,
bool set, uint8_t pcp);
#endif

View File

@ -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,6 +28,83 @@
#include "pbrd/pbr_debug.h"
#include "pbrd/pbr_vty_clippy.c"
/* 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)
pbr_set_match_clause_for_pcp(pbrms, !no, pcp);
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) {
if (!no) {
pbr_set_match_clause_for_vlan(pbrms, vlan_id, 0);
} else {
/* if the user previously set a vlan_id value */
if (pbrms->match_vlan_id != 0) {
if (vlan_id == pbrms->match_vlan_id) {
pbr_set_match_clause_for_vlan(pbrms, 0,
0);
}
}
}
}
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);
if (!pbrms)
return CMD_WARNING;
if (!no) {
if (strmatch(tag_type, "tagged")) {
pbr_set_match_clause_for_vlan(pbrms, 0,
PBR_VLAN_FLAGS_TAGGED);
} else if (strmatch(tag_type, "untagged")) {
pbr_set_match_clause_for_vlan(pbrms, 0,
PBR_VLAN_FLAGS_UNTAGGED);
} else if (strmatch(tag_type, "untagged-or-zero")) {
pbr_set_match_clause_for_vlan(pbrms, 0,
PBR_VLAN_FLAGS_UNTAGGED_0);
}
} else {
pbr_set_match_clause_for_vlan(pbrms, 0, PBR_VLAN_FLAGS_NO_WILD);
}
return CMD_SUCCESS;
}
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"
@ -185,12 +265,11 @@ DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd,
}
DEFPY(pbr_map_match_ip_proto, pbr_map_match_ip_proto_cmd,
"[no] match ip-protocol [tcp|udp]$ip_proto",
"[no] match ip-protocol PROTO$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")
"Protocol name\n")
{
struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
struct protoent *p;
@ -215,6 +294,8 @@ DEFPY(pbr_map_match_ip_proto, pbr_map_match_ip_proto_cmd,
} else
pbrms->ip_proto = 0;
pbr_map_check(pbrms, true);
return CMD_SUCCESS;
}
@ -899,6 +980,7 @@ static void vty_show_pbrms(struct vty *vty,
vty_out(vty, " SRC Port Match: %u\n", pbrms->src_prt);
if (pbrms->dst_prt)
vty_out(vty, " DST Port Match: %u\n", pbrms->dst_prt);
if (pbrms->dsfield & PBR_DSFIELD_DSCP)
vty_out(vty, " DSCP Match: %u\n",
(pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2);
@ -907,9 +989,21 @@ static void vty_show_pbrms(struct vty *vty,
pbrms->dsfield & PBR_DSFIELD_ECN);
if (pbrms->mark)
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 (pbrms->match_vlan_id != 0)
vty_out(vty, " Match VLAN ID: %u\n",
pbrms->match_vlan_id);
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",
vty_out(vty, " Set Queue ID: %u\n",
pbrms->action_queue_id);
if (pbrms->action_vlan_id != 0)
@ -1306,7 +1400,18 @@ static int pbr_vty_map_config_write_sequence(struct vty *vty,
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);
if ((pbrms->match_vlan_id) &&
(pbrms->match_vlan_flags == PBR_VLAN_FLAGS_NO_WILD))
vty_out(vty, " match vlan %u\n", pbrms->match_vlan_id);
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);
@ -1406,6 +1511,9 @@ 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);