From bfd3e8e012f22c62f628f836532e9805b10ae493 Mon Sep 17 00:00:00 2001 From: "G. Paul Ziemba" Date: Wed, 19 Jul 2023 07:58:02 -0700 Subject: [PATCH] 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 Eli Baum G. Paul Ziemba Signed-off-by: G. Paul Ziemba --- lib/pbr.h | 44 ++++++++++++------ pbrd/pbr_map.c | 57 ++++++++++++++++++++---- pbrd/pbr_map.h | 50 ++++++++++++++------- pbrd/pbr_vty.c | 118 ++++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 225 insertions(+), 44 deletions(-) diff --git a/lib/pbr.h b/lib/pbr.h index e8563afb3b..f4b6633812 100644 --- a/lib/pbr.h +++ b/lib/pbr.h @@ -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,30 +28,43 @@ extern "C" { * specified. */ struct pbr_filter { - uint32_t filter_bm; /* not encoded by zapi - */ -#define PBR_FILTER_SRC_IP (1 << 0) -#define PBR_FILTER_DST_IP (1 << 1) -#define PBR_FILTER_SRC_PORT (1 << 2) -#define PBR_FILTER_DST_PORT (1 << 3) -#define PBR_FILTER_FWMARK (1 << 4) -#define PBR_FILTER_PROTO (1 << 5) -#define PBR_FILTER_SRC_PORT_RANGE (1 << 6) -#define PBR_FILTER_DST_PORT_RANGE (1 << 7) -#define PBR_FILTER_DSFIELD (1 << 8) -#define PBR_FILTER_IP_PROTOCOL (1 << 9) + 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) +#define PBR_FILTER_DST_PORT (1 << 3) +#define PBR_FILTER_FWMARK (1 << 4) +#define PBR_FILTER_PROTO (1 << 5) +#define PBR_FILTER_SRC_PORT_RANGE (1 << 6) +#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) */ diff --git a/pbrd/pbr_map.c b/pbrd/pbr_map.c index 16cea3b4cd..758e08b565 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 @@ -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 */ diff --git a/pbrd/pbr_map.h b/pbrd/pbr_map.h index c9da431b38..3afb199565 100644 --- a/pbrd/pbr_map.h +++ b/pbrd/pbr_map.h @@ -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 diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index ee9ee32f8b..bc83c2d61c 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 @@ -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 [$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); -- 2.39.5