From c5d60c4a6766f4a9b531718e50bf541e60ddfc71 Mon Sep 17 00:00:00 2001 From: "G. Paul Ziemba" Date: Thu, 27 Jul 2023 11:15:39 -0700 Subject: [PATCH 1/7] pbrd: pbr_vty.c organize/style No functional changes: just arrange CLI handlers into logical order and standardize DEFPY formatting. Signed-off-by: G. Paul Ziemba --- pbrd/pbr_vty.c | 545 ++++++++++++++++++++++++++++--------------------- 1 file changed, 314 insertions(+), 231 deletions(-) diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index 8e9673482e..be75a63f9e 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -29,89 +29,60 @@ #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") +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 */ - struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + /* upper bound is 2^32 - 2^10 */ + int ret = CMD_WARNING; + const int minrange = 1000; - if (pbrms) - pbr_set_match_clause_for_pcp(pbrms, !no, pcp); + /* 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 */ -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) { - assert(tag_type); - 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)", +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; @@ -122,13 +93,17 @@ DEFUN_NOSH(pbr_map, pbr_map_cmd, "pbr-map PBRMAP seq (1-700)", return CMD_SUCCESS; } -DEFUN_NOSH(no_pbr_map, no_pbr_map_cmd, "no pbr-map PBRMAP [seq (1-700)]", +/* clang-format off */ +DEFUN_NOSH(no_pbr_map, + no_pbr_map_cmd, + "no pbr-map PBRMAP [seq (1-700)]", NO_STR "Delete pbr-map\n" "The name of the PBR MAP\n" "Sequence to delete from existing pbr-map entry\n" "Sequence number\n") { + /* clang-format on */ const char *pbrm_name = argv[2]->arg; uint32_t seqno = 0; struct pbr_map *pbrm = pbrm_find(pbrm_name); @@ -153,47 +128,13 @@ DEFUN_NOSH(no_pbr_map, no_pbr_map_cmd, "no pbr-map PBRMAP [seq (1-700)]", return CMD_SUCCESS; } -DEFPY(pbr_set_table_range, - pbr_set_table_range_cmd, - "pbr table range (10000-4294966272)$lb (10000-4294966272)$ub", - PBR_STR - "Set table ID range\n" - "Set table ID range\n" - "Lower bound for table ID range\n" - "Upper bound for table ID range\n") -{ - /* upper bound is 2^32 - 2^10 */ - int ret = CMD_WARNING; - const int minrange = 1000; +/*********************************************************************** + * pbrms/rule Match L3 Fields + ***********************************************************************/ - /* validate given bounds */ - if (lb > ub) - vty_out(vty, "%% Lower bound must be less than upper bound\n"); - else if (ub - lb < minrange) - vty_out(vty, "%% Range breadth must be at least %d\n", minrange); - else { - ret = CMD_SUCCESS; - pbr_nht_set_tableid_range((uint32_t) lb, (uint32_t) ub); - } - - return ret; -} - -DEFPY(no_pbr_set_table_range, no_pbr_set_table_range_cmd, - "no pbr table range [(10000-4294966272)$lb (10000-4294966272)$ub]", - NO_STR - PBR_STR - "Set table ID range\n" - "Set table ID range\n" - "Lower bound for table ID range\n" - "Upper bound for table ID range\n") -{ - pbr_nht_set_tableid_range(PBR_NHT_DEFAULT_LOW_TABLEID, - PBR_NHT_DEFAULT_HIGH_TABLEID); - return CMD_SUCCESS; -} - -DEFPY(pbr_map_match_src, pbr_map_match_src_cmd, +/* clang-format off */ +DEFPY (pbr_map_match_src, + pbr_map_match_src_cmd, "[no] match src-ip $prefix", NO_STR "Match the rest of the command\n" @@ -201,6 +142,7 @@ DEFPY(pbr_map_match_src, pbr_map_match_src_cmd, "v4 Prefix\n" "v6 Prefix\n") { + /* clang-format on */ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); if (!pbrms) @@ -229,7 +171,9 @@ DEFPY(pbr_map_match_src, pbr_map_match_src_cmd, return CMD_SUCCESS; } -DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd, +/* clang-format off */ +DEFPY (pbr_map_match_dst, + pbr_map_match_dst_cmd, "[no] match dst-ip $prefix", NO_STR "Match the rest of the command\n" @@ -237,6 +181,7 @@ DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd, "v4 Prefix\n" "v6 Prefix\n") { + /* clang-format on */ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); if (!pbrms) @@ -246,7 +191,6 @@ DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd, vty_out(vty, "Cannot mismatch families within match src/dst\n"); return CMD_WARNING_CONFIG_FAILED; } - pbrms->family = prefix->family; if (!no) { @@ -265,13 +209,16 @@ DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd, return CMD_SUCCESS; } -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 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; @@ -300,13 +247,16 @@ DEFPY(pbr_map_match_ip_proto, pbr_map_match_ip_proto_cmd, return CMD_SUCCESS; } -DEFPY(pbr_map_match_src_port, pbr_map_match_src_port_cmd, - "[no] match src-port (1-65535)$port", - NO_STR - "Match the rest of the command\n" - "Choose the source port to use\n" - "The Source Port\n") +/* clang-format off */ +DEFPY (pbr_map_match_src_port, + pbr_map_match_src_port_cmd, + "[no] match src-port (1-65535)$port", + NO_STR + "Match the rest of the command\n" + "Choose the source port to use\n" + "The Source Port\n") { + /* clang-format on */ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); if (!pbrms) @@ -325,13 +275,16 @@ DEFPY(pbr_map_match_src_port, pbr_map_match_src_port_cmd, return CMD_SUCCESS; } -DEFPY(pbr_map_match_dst_port, pbr_map_match_dst_port_cmd, - "[no] match dst-port (1-65535)$port", - NO_STR - "Match the rest of the command\n" - "Choose the destination port to use\n" - "The Destination Port\n") +/* clang-format off */ +DEFPY (pbr_map_match_dst_port, + pbr_map_match_dst_port_cmd, + "[no] match dst-port (1-65535)$port", + NO_STR + "Match the rest of the command\n" + "Choose the destination port to use\n" + "The Destination Port\n") { + /* clang-format on */ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); if (!pbrms) @@ -350,13 +303,16 @@ DEFPY(pbr_map_match_dst_port, pbr_map_match_dst_port_cmd, return CMD_SUCCESS; } -DEFPY(pbr_map_match_dscp, pbr_map_match_dscp_cmd, - "[no] match dscp DSCP$dscp", - NO_STR - "Match the rest of the command\n" - "Match based on IP DSCP field\n" - "DSCP value (below 64) or standard codepoint name\n") +/* clang-format off */ +DEFPY (pbr_map_match_dscp, + pbr_map_match_dscp_cmd, + "[no] match dscp DSCP$dscp", + NO_STR + "Match the rest of the command\n" + "Match based on IP DSCP field\n" + "DSCP value (below 64) or standard codepoint name\n") { + /* clang-format on */ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); char dscpname[100]; uint8_t rawDscp; @@ -414,13 +370,16 @@ DEFPY(pbr_map_match_dscp, pbr_map_match_dscp_cmd, return CMD_SUCCESS; } -DEFPY(pbr_map_match_ecn, pbr_map_match_ecn_cmd, - "[no] match ecn (0-3)$ecn", - NO_STR - "Match the rest of the command\n" - "Match based on IP ECN field\n" - "Explicit Congestion Notification\n") +/* clang-format off */ +DEFPY (pbr_map_match_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) @@ -441,13 +400,104 @@ DEFPY(pbr_map_match_ecn, pbr_map_match_ecn_cmd, return CMD_SUCCESS; } -DEFPY(pbr_map_match_mark, pbr_map_match_mark_cmd, +/*********************************************************************** + * 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) + 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) { + assert(tag_type); + 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; +} + +/*********************************************************************** + * 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) @@ -472,6 +522,119 @@ DEFPY(pbr_map_match_mark, pbr_map_match_mark_cmd, 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) + pbrms->action_queue_id = queue_id; + else if ((uint32_t)queue_id == pbrms->action_queue_id) + pbrms->action_queue_id = PBR_MAP_UNDEFINED_QUEUE_ID; + + 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) + pbrms->action_pcp = pcp; + else if (pcp == pbrms->action_pcp) + pbrms->action_pcp = 0; + + 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) + pbrms->action_vlan_id = vlan_id; + else if (pbrms->action_vlan_id == vlan_id) + pbrms->action_vlan_id = 0; + + 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) + pbrms->action_vlan_flags = PBR_MAP_STRIP_INNER_ANY; + else + pbrms->action_vlan_flags = 0; + + 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) { @@ -504,92 +667,6 @@ static void pbrms_clear_set_config(struct pbr_map_sequence *pbrms) } -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") -{ - struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); - - if (!pbrms) - return CMD_WARNING_CONFIG_FAILED; - - if (!no) - pbrms->action_queue_id = queue_id; - else if ((uint32_t)queue_id == pbrms->action_queue_id) - pbrms->action_queue_id = PBR_MAP_UNDEFINED_QUEUE_ID; - - pbr_map_check(pbrms, true); - - return CMD_SUCCESS; -} - -DEFPY(pbr_map_action_pcp, pbr_map_action_pcp_cmd, "[no] set pcp <(0-7)$pcp>", - NO_STR - "Set the rest of the command\n" - "Set based on 802.1p Priority Code Point (PCP) value\n" - "A valid value in range 0..7\n") -{ - struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); - - if (!pbrms) - return CMD_WARNING_CONFIG_FAILED; - - if (!no) - pbrms->action_pcp = pcp; - else if (pcp == pbrms->action_pcp) - pbrms->action_pcp = 0; - - pbr_map_check(pbrms, true); - - return CMD_SUCCESS; -} - -DEFPY(pbr_map_action_vlan_id, pbr_map_action_vlan_id_cmd, - "[no] set vlan <(1-4094)$vlan_id>", - NO_STR - "Set the rest of the command\n" - "Set action for VLAN tagging\n" - "A valid value in range 1..4094\n") -{ - struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); - - if (!pbrms) - return CMD_WARNING_CONFIG_FAILED; - - if (!no) - pbrms->action_vlan_id = vlan_id; - else if (pbrms->action_vlan_id == vlan_id) - pbrms->action_vlan_id = 0; - - pbr_map_check(pbrms, true); - - return CMD_SUCCESS; -} - -DEFPY(pbr_map_action_strip_vlan, pbr_map_action_strip_vlan_cmd, - "[no] strip vlan", - NO_STR - "Strip the vlan tags from frame\n" - "Strip any inner vlan tag \n") -{ - struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); - - if (!pbrms) - return CMD_WARNING_CONFIG_FAILED; - - if (!no) - pbrms->action_vlan_flags = PBR_MAP_STRIP_INNER_ANY; - else - pbrms->action_vlan_flags = 0; - - pbr_map_check(pbrms, true); - - return CMD_SUCCESS; -} - DEFPY(pbr_map_nexthop_group, pbr_map_nexthop_group_cmd, "set nexthop-group NHGNAME$name", @@ -853,13 +930,19 @@ DEFPY(no_pbr_map_vrf, no_pbr_map_vrf_cmd, return CMD_SUCCESS; } -DEFPY (pbr_policy, +/*********************************************************************** + * Policy + ***********************************************************************/ + +/* clang-format off */ +DEFPY (pbr_policy, pbr_policy_cmd, "[no] pbr-policy PBRMAP$mapname", NO_STR "Policy to use\n" "Name of the pbr-map to apply\n") { + /* clang-format on */ VTY_DECLVAR_CONTEXT(interface, ifp); struct pbr_map *pbrm, *old_pbrm; struct pbr_interface *pbr_ifp = ifp->info; From 0e1bf005dc847b5a8fd329dccd6b5c2d5951ab52 Mon Sep 17 00:00:00 2001 From: "G. Paul Ziemba" Date: Sun, 30 Jul 2023 11:52:57 -0700 Subject: [PATCH 2/7] lib: pbr.h: remove unused PBR_FILTER_PROTO Signed-off-by: G. Paul Ziemba --- lib/pbr.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pbr.h b/lib/pbr.h index 1a3d562ed9..5414e97446 100644 --- a/lib/pbr.h +++ b/lib/pbr.h @@ -34,17 +34,17 @@ struct pbr_filter { #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_IP_PROTOCOL (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*/ #define PBR_VLAN_FLAGS_NO_WILD 0 From 09262f1b720ff22631e34222321099187bfd37d8 Mon Sep 17 00:00:00 2001 From: "G. Paul Ziemba" Date: Sun, 30 Jul 2023 12:14:05 -0700 Subject: [PATCH 3/7] pbrd: add nexthop drop type Signed-off-by: G. Paul Ziemba --- doc/user/pbr.rst | 39 ++++++++++++++++--------------- lib/nexthop_group.c | 1 + pbrd/pbr_vty.c | 57 ++++++++++++++++++++++++++++----------------- 3 files changed, 56 insertions(+), 41 deletions(-) diff --git a/doc/user/pbr.rst b/doc/user/pbr.rst index 17f3504898..4d90f5cb6f 100644 --- a/doc/user/pbr.rst +++ b/doc/user/pbr.rst @@ -161,6 +161,26 @@ specified in the rule are also applied to the packet. VLAN-matching facilities, so this field will be ignored unless other dataplane providers are used. +.. clicmd:: set nexthop-group NAME + + Action: + forward the packet using nexthop-group NAME. + +.. clicmd:: set nexthop [A.B.C.D|X:X::X:XX|blackhole] [interface] [nexthop-vrf NAME] + + Action: + forward the packet using the specified single nexthop. + If `blackhole`, packets will be sent to a blackhole route and dropped. + +.. clicmd:: set vrf unchanged|NAME + + Action: + If set to ``unchanged``, the rule will use the vrf table the interface + is in as its lookup. + If set to NAME, the rule will use that vrf table as its lookup. + + Not supported with NETNS VRF backend. + .. clicmd:: set queue-id (1-65535) Action: @@ -195,25 +215,6 @@ specified in the rule are also applied to the packet. so this field will be ignored unless another dataplane provider is used. It is invalid to specify both a `strip` and `set vlan` action. -.. clicmd:: set nexthop-group NAME - - Action: - forward the packet using nexthop-group NAME. - -.. clicmd:: set nexthop [A.B.C.D|X:X::X:XX] [interface] [nexthop-vrf NAME] - - Action: - forward the packet using the specified single nexthop. - -.. clicmd:: set vrf unchanged|NAME - - Action: - If set to ``unchanged``, the rule will use the vrf table the interface - is in as its lookup. - If set to NAME, the rule will use that vrf table as its lookup. - - Not supported with NETNS VRF backend. - .. clicmd:: show pbr map [NAME] [detail|json] Display pbr maps either all or by ``NAME``. If ``detail`` is set, it will diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c index 0613fc6736..25370eba48 100644 --- a/lib/nexthop_group.c +++ b/lib/nexthop_group.c @@ -1047,6 +1047,7 @@ void nexthop_group_write_nexthop_simple(struct vty *vty, vty_out(vty, "%pI6 %s", &nh->gate.ipv6, ifname); break; case NEXTHOP_TYPE_BLACKHOLE: + vty_out(vty, "%s", "drop"); break; } } diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index be75a63f9e..5ee262b9c7 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -718,22 +718,27 @@ DEFPY(no_pbr_map_nexthop_group, no_pbr_map_nexthop_group_cmd, return CMD_SUCCESS; } -DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd, - "set nexthop\ +/* clang-format off */ +DEFPY (pbr_map_nexthop, + pbr_map_nexthop_cmd, + "set nexthop\ <\ $addr [INTERFACE$intf]\ |INTERFACE$intf\ + |blackhole$bh\ >\ [nexthop-vrf NAME$vrf_name]", - "Set for the PBR-MAP\n" - "Specify one of the nexthops in this map\n" - "v4 Address\n" - "v6 Address\n" - "Interface to use\n" - "Interface to use\n" - "If the nexthop is in a different vrf tell us\n" - "The nexthop-vrf Name\n") + "Set for the PBR-MAP\n" + "Specify one of the nexthops in this map\n" + "v4 Address\n" + "v6 Address\n" + "Interface to use\n" + "Interface to use\n" + "Blackhole route\n" + "If the nexthop is in a different vrf tell us\n" + "The nexthop-vrf Name\n") { + /* clang-format on */ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); struct vrf *vrf; struct nexthop nhop; @@ -815,8 +820,11 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd, nhop.type = NEXTHOP_TYPE_IPV6; } } - } else + } else if (bh) { + nhop.type = NEXTHOP_TYPE_BLACKHOLE; + } else { nhop.type = NEXTHOP_TYPE_IFINDEX; + } if (pbrms->nhg) nh = nexthop_exists(pbrms->nhg, &nhop); @@ -845,23 +853,28 @@ done: return CMD_SUCCESS; } -DEFPY(no_pbr_map_nexthop, no_pbr_map_nexthop_cmd, - "no set nexthop\ +/* clang-format off */ +DEFPY (no_pbr_map_nexthop, + no_pbr_map_nexthop_cmd, + "no set nexthop\ [<\ $addr [INTERFACE$intf]\ |INTERFACE$intf\ + |blackhole$bh\ >\ [nexthop-vrf NAME$vrf_name]]", - NO_STR - "Set for the PBR-MAP\n" - "Specify one of the nexthops in this map\n" - "v4 Address\n" - "v6 Address\n" - "Interface to use\n" - "Interface to use\n" - "If the nexthop is in a different vrf tell us\n" - "The nexthop-vrf Name\n") + NO_STR + "Set for the PBR-MAP\n" + "Specify one of the nexthops in this map\n" + "v4 Address\n" + "v6 Address\n" + "Interface to use\n" + "Interface to use\n" + "Blackhole route\n" + "If the nexthop is in a different vrf tell us\n" + "The nexthop-vrf Name\n") { + /* clang-format on */ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); if (!pbrms) From 5572f45d51c587691384a9ec3a195e6a01540930 Mon Sep 17 00:00:00 2001 From: "G. Paul Ziemba" Date: Tue, 1 Aug 2023 06:49:38 -0700 Subject: [PATCH 4/7] pbrd: PBR_FILTER_DSFIELD -> {PBR_FILTER_DSCP,PBR_FILTER_ECN} DSCP and ECN matching are configured independently. Maintain these values in independent fields in pbrd, zapi, and zebra. Signed-off-by: G. Paul Ziemba --- lib/pbr.h | 3 ++- pbrd/pbr_vty.c | 4 ++++ zebra/rule_netlink.c | 4 ++-- zebra/zebra_pbr.c | 4 ++-- zebra/zebra_pbr.h | 2 -- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/pbr.h b/lib/pbr.h index 5414e97446..1c89f2f552 100644 --- a/lib/pbr.h +++ b/lib/pbr.h @@ -37,7 +37,8 @@ struct pbr_filter { #define PBR_FILTER_IP_PROTOCOL (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_DSCP (1 << 8) +#define PBR_FILTER_ECN (1 << 9) #define PBR_FILTER_PCP (1 << 10) #define PBR_FILTER_VLAN_FLAGS (1 << 11) #define PBR_FILTER_VLAN_ID (1 << 12) diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index 5ee262b9c7..cffd3794c2 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -361,8 +361,10 @@ DEFPY (pbr_map_match_dscp, /* Set the DSCP bits of the DSField */ pbrms->dsfield = (pbrms->dsfield & ~PBR_DSFIELD_DSCP) | (rawDscp << 2); + SET_FLAG(pbrms->filter_bm, PBR_FILTER_DSCP); } else { pbrms->dsfield &= ~PBR_DSFIELD_DSCP; + UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_DSCP); } pbr_map_check(pbrms, true); @@ -391,8 +393,10 @@ DEFPY (pbr_map_match_ecn, /* Set the ECN bits of the DSField */ pbrms->dsfield = (pbrms->dsfield & ~PBR_DSFIELD_ECN) | ecn; + SET_FLAG(pbrms->filter_bm, PBR_FILTER_ECN); } else { pbrms->dsfield &= ~PBR_DSFIELD_ECN; + UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_ECN); } pbr_map_check(pbrms, true); diff --git a/zebra/rule_netlink.c b/zebra/rule_netlink.c index 3bb4936b81..bc96e12902 100644 --- a/zebra/rule_netlink.c +++ b/zebra/rule_netlink.c @@ -117,8 +117,8 @@ static ssize_t netlink_rule_msg_encode( } /* dsfield, if specified; mask off the ECN bits */ - if (filter_bm & PBR_FILTER_DSFIELD) - req->frh.tos = dsfield & 0xfc; + if (filter_bm & PBR_FILTER_DSCP) + req->frh.tos = dsfield & PBR_DSFIELD_DSCP; /* protocol to match on */ if (filter_bm & PBR_FILTER_IP_PROTOCOL) diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c index eac93dca41..7560071e59 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -522,12 +522,12 @@ void zebra_pbr_show_rule_unit(struct zebra_pbr_rule *rule, struct vty *vty) if (prule->filter.filter_bm & PBR_FILTER_DST_PORT) vty_out(vty, " DST Port Match: %u\n", prule->filter.dst_port); - if (prule->filter.filter_bm & PBR_FILTER_DSFIELD) { + if (prule->filter.filter_bm & PBR_FILTER_DSCP) vty_out(vty, " DSCP Match: %u\n", (prule->filter.dsfield & PBR_DSFIELD_DSCP) >> 2); + if (prule->filter.filter_bm & PBR_FILTER_ECN) vty_out(vty, " ECN Match: %u\n", prule->filter.dsfield & PBR_DSFIELD_ECN); - } if (prule->filter.filter_bm & PBR_FILTER_FWMARK) vty_out(vty, " MARK Match: %u\n", prule->filter.fwmark); diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h index 15ad4e35cf..ddc1460d40 100644 --- a/zebra/zebra_pbr.h +++ b/zebra/zebra_pbr.h @@ -61,8 +61,6 @@ struct zebra_pbr_rule { (r->rule.filter.filter_bm & PBR_FILTER_SRC_PORT) #define IS_RULE_FILTERING_ON_DST_PORT(r) \ (r->rule.filter.filter_bm & PBR_FILTER_DST_PORT) -#define IS_RULE_FILTERING_ON_DSFIELD(r) \ - (r->rule.filter.filter_bm & PBR_FILTER_DSFIELD) #define IS_RULE_FILTERING_ON_FWMARK(r) \ (r->rule.filter.filter_bm & PBR_FILTER_FWMARK) From c47fd378f33b721cc32b788c6ce89353b1076416 Mon Sep 17 00:00:00 2001 From: "G. Paul Ziemba" Date: Sun, 30 Jul 2023 19:14:01 -0700 Subject: [PATCH 5/7] pbrd: add explicit 'family' field for rules In the netlink-mediated kernel dataplane, each rule is stored in either an IPv4-specific database or an IPv6-specific database. PBRD opportunistically gleans each rule's address family value from its source or destination IP address match value (if either exists), or from its nexthop or nexthop-group (if it exists). The 'family' value is particularly needed for netlink during incremental rule deletion when none of the above fields remain set. Before now, this address family has been encoded by occult means in the (possibly otherwise unset) source/destination IP match fields in ZAPI and zebra. This commit documents the reasons for maintaining the 'family' field in the PBRD rule structure, adds a 'family' field in the common lib/pbr.h rule structure, and carries it explicitly in ZAPI. Signed-off-by: G. Paul Ziemba --- bgpd/bgp_zebra.c | 1 + lib/pbr.h | 1 + lib/zclient.c | 2 ++ pbrd/pbr_zebra.c | 14 ++++++++++---- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 8ff6b63e05..9784c1c837 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -2737,6 +2737,7 @@ static void bgp_encode_pbr_rule_action(struct stream *s, else r.unique = pbra->unique; + r.family = fam; /* filter */ diff --git a/lib/pbr.h b/lib/pbr.h index 1c89f2f552..61a5eb07f6 100644 --- a/lib/pbr.h +++ b/lib/pbr.h @@ -114,6 +114,7 @@ struct pbr_action { */ struct pbr_rule { vrf_id_t vrf_id; + uint8_t family; /* netlink: select which rule database */ uint32_t seq; uint32_t priority; diff --git a/lib/zclient.c b/lib/zclient.c index 294a78feb0..4648b285fd 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -1702,6 +1702,7 @@ int zapi_pbr_rule_encode(struct stream *s, struct pbr_rule *r) */ stream_putl(s, 1); + stream_putc(s, r->family); stream_putl(s, r->seq); stream_putl(s, r->priority); stream_putl(s, r->unique); @@ -1723,6 +1724,7 @@ bool zapi_pbr_rule_decode(struct stream *s, struct pbr_rule *r) memset(r, 0, sizeof(*r)); + STREAM_GETC(s, r->family); STREAM_GETL(s, r->seq); STREAM_GETL(s, r->priority); STREAM_GETL(s, r->unique); diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c index 030c4c1114..adcf449cfa 100644 --- a/pbrd/pbr_zebra.c +++ b/pbrd/pbr_zebra.c @@ -517,10 +517,14 @@ static bool pbr_encode_pbr_map_sequence(struct stream *s, uint8_t family; /* - * There seems to be some effort in pbr_vty.c to keep the three - * copies of "family" equal. Not sure if the reason goes beyond - * ensuring consistency in ZAPI encoding. In any case, it might - * be handled better as an internal matter for the encoder (TBD). + * Opportunistic address family field is set when any of the IP + * address match/set fields is set, or when a NH/NHG is resolved. + * The value is needed by zebra for the underlying netlink + * messaging, particularly in delete operations, because it + * selects the rule database (IPv4 vs. IPv6). + * + * Historically the value has been encoded into any unused + * "match src/dst address" fields and picked off in zebra. */ family = AF_INET; if (pbrms->family) @@ -539,6 +543,8 @@ static bool pbr_encode_pbr_map_sequence(struct stream *s, r.priority = pbrms->ruleno; r.unique = pbrms->unique; + r.family = pbrms->family; + /* filter */ r.filter.filter_bm = pbrms->filter_bm; if (pbrms->src) From 887367a01c0e978e992935ae93f3df4e3c1bd86c Mon Sep 17 00:00:00 2001 From: "G. Paul Ziemba" Date: Sun, 30 Jul 2023 21:33:10 -0700 Subject: [PATCH 6/7] pbrd: use flags to indicate active fields Before now, PBRD used non-zero values to imply that a rule's match or action field was active. This approach was getting cumbersome for fields where 0 is a valid active value and various field-specific magic values had to be used. This commit changes PBRD to use a flag bit per field to indicate that the field is active. Signed-off-by: G. Paul Ziemba --- lib/pbr.h | 13 +- lib/zclient.c | 116 +++++-- pbrd/pbr_map.c | 77 ++--- pbrd/pbr_map.h | 26 +- pbrd/pbr_nht.c | 2 + pbrd/pbr_vty.c | 806 ++++++++++++++++++++++++++++++++--------------- pbrd/pbr_zebra.c | 38 +-- zebra/zapi_msg.c | 27 +- 8 files changed, 719 insertions(+), 386 deletions(-) diff --git a/lib/pbr.h b/lib/pbr.h index 61a5eb07f6..ae01ae7978 100644 --- a/lib/pbr.h +++ b/lib/pbr.h @@ -57,7 +57,7 @@ struct pbr_filter { struct prefix src_ip; struct prefix dst_ip; - /* Source and Destination higher-layer (TCP/UDP) port numbers */ + /* Source and Destination layer 4 (TCP/UDP/etc.) port numbers */ uint16_t src_port; uint16_t dst_port; @@ -88,11 +88,11 @@ struct pbr_filter { struct pbr_action { uint32_t flags; -#define PBR_ACTION_TABLE (1 << 0) -#define PBR_ACTION_QUEUE_ID (1 << 1) -#define PBR_ACTION_PCP (1 << 2) -#define PBR_ACTION_VLAN_ID (1 << 3) -#define PBR_ACTION_VLAN_FLAGS (1 << 4) +#define PBR_ACTION_TABLE (1 << 0) +#define PBR_ACTION_QUEUE_ID (1 << 1) +#define PBR_ACTION_PCP (1 << 2) +#define PBR_ACTION_VLAN_ID (1 << 3) +#define PBR_ACTION_VLAN_STRIP_INNER_ANY (1 << 4) uint32_t table; uint32_t queue_id; @@ -100,7 +100,6 @@ struct pbr_action { /* VLAN */ uint8_t pcp; uint16_t vlan_id; - uint16_t vlan_flags; }; diff --git a/lib/zclient.c b/lib/zclient.c index 4648b285fd..97c829c90d 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -1631,40 +1631,79 @@ static void zapi_pbr_rule_filter_encode(struct stream *s, struct pbr_filter *f) assert((f->src_ip.family == AF_INET) || (f->src_ip.family == AF_INET6)); stream_putl(s, f->filter_bm); - stream_putc(s, f->ip_proto); + + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_IP_PROTOCOL)) + stream_putc(s, f->ip_proto); /* addresses */ - zapi_encode_prefix(s, &f->src_ip, f->src_ip.family); - zapi_encode_prefix(s, &f->dst_ip, f->dst_ip.family); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_SRC_IP)) + zapi_encode_prefix(s, &f->src_ip, f->src_ip.family); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_DST_IP)) + zapi_encode_prefix(s, &f->dst_ip, f->dst_ip.family); /* port numbers */ - stream_putw(s, f->src_port); - stream_putw(s, f->dst_port); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_SRC_PORT)) + stream_putw(s, f->src_port); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_DST_PORT)) + stream_putw(s, f->dst_port); + + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_DSCP)) + stream_putc(s, f->dsfield & PBR_DSFIELD_DSCP); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_ECN)) + stream_putc(s, f->dsfield & PBR_DSFIELD_ECN); /* vlan */ - stream_putc(s, f->pcp); - stream_putw(s, f->vlan_id); - stream_putw(s, f->vlan_flags); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_PCP)) + stream_putc(s, f->pcp); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_VLAN_ID)) + stream_putw(s, f->vlan_id); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_VLAN_FLAGS)) + stream_putw(s, f->vlan_flags); - stream_putc(s, f->dsfield); - stream_putl(s, f->fwmark); + + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_FWMARK)) + stream_putl(s, f->fwmark); } static bool zapi_pbr_rule_filter_decode(struct stream *s, struct pbr_filter *f) { + uint8_t dscp = 0; + uint8_t ecn = 0; + STREAM_GETL(s, f->filter_bm); - STREAM_GETC(s, f->ip_proto); - if (!zapi_decode_prefix(s, &(f->src_ip))) - goto stream_failure; - if (!zapi_decode_prefix(s, &(f->dst_ip))) - goto stream_failure; - STREAM_GETW(s, f->src_port); - STREAM_GETW(s, f->dst_port); - STREAM_GETC(s, f->pcp); - STREAM_GETW(s, f->vlan_id); - STREAM_GETW(s, f->vlan_flags); - STREAM_GETC(s, f->dsfield); - STREAM_GETL(s, f->fwmark); + + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_IP_PROTOCOL)) + STREAM_GETC(s, f->ip_proto); + + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_SRC_IP)) + if (!zapi_decode_prefix(s, &(f->src_ip))) + goto stream_failure; + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_DST_IP)) + if (!zapi_decode_prefix(s, &(f->dst_ip))) + goto stream_failure; + + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_SRC_PORT)) + STREAM_GETW(s, f->src_port); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_DST_PORT)) + STREAM_GETW(s, f->dst_port); + + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_DSCP)) + STREAM_GETC(s, dscp); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_ECN)) + STREAM_GETC(s, ecn); + f->dsfield = (dscp & PBR_DSFIELD_DSCP) | (ecn & PBR_DSFIELD_ECN); + + /* vlan */ + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_PCP)) + STREAM_GETC(s, f->pcp); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_VLAN_ID)) + STREAM_GETW(s, f->vlan_id); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_VLAN_FLAGS)) + STREAM_GETW(s, f->vlan_flags); + + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_FWMARK)) + STREAM_GETL(s, f->fwmark); + return true; stream_failure: @@ -1674,21 +1713,34 @@ stream_failure: static void zapi_pbr_rule_action_encode(struct stream *s, struct pbr_action *a) { stream_putl(s, a->flags); - stream_putl(s, a->table); - stream_putl(s, a->queue_id); - stream_putc(s, a->pcp); - stream_putw(s, a->vlan_id); - stream_putw(s, a->vlan_flags); + + if (CHECK_FLAG(a->flags, PBR_ACTION_TABLE)) + stream_putl(s, a->table); + if (CHECK_FLAG(a->flags, PBR_ACTION_QUEUE_ID)) + stream_putl(s, a->queue_id); + + /* L2 */ + if (CHECK_FLAG(a->flags, PBR_ACTION_PCP)) + stream_putc(s, a->pcp); + if (CHECK_FLAG(a->flags, PBR_ACTION_VLAN_ID)) + stream_putw(s, a->vlan_id); } static bool zapi_pbr_rule_action_decode(struct stream *s, struct pbr_action *a) { STREAM_GETL(s, a->flags); - STREAM_GETL(s, a->table); - STREAM_GETL(s, a->queue_id); - STREAM_GETC(s, a->pcp); - STREAM_GETW(s, a->vlan_id); - STREAM_GETW(s, a->vlan_flags); + + if (CHECK_FLAG(a->flags, PBR_ACTION_TABLE)) + STREAM_GETL(s, a->table); + if (CHECK_FLAG(a->flags, PBR_ACTION_QUEUE_ID)) + STREAM_GETL(s, a->queue_id); + + /* L2 */ + if (CHECK_FLAG(a->flags, PBR_ACTION_PCP)) + STREAM_GETC(s, a->pcp); + if (CHECK_FLAG(a->flags, PBR_ACTION_VLAN_ID)) + STREAM_GETW(s, a->vlan_id); + return true; stream_failure: diff --git a/pbrd/pbr_map.c b/pbrd/pbr_map.c index 758e08b565..30148fe093 100644 --- a/pbrd/pbr_map.c +++ b/pbrd/pbr_map.c @@ -105,38 +105,6 @@ 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, @@ -562,14 +530,6 @@ struct pbr_map_sequence *pbrms_get(const char *name, uint32_t seqno) pbrms->ruleno = pbr_nht_get_next_rule(seqno); pbrms->parent = pbrm; - pbrms->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; - pbrms->action_queue_id = PBR_MAP_UNDEFINED_QUEUE_ID; pbrms->reason = @@ -634,13 +594,35 @@ 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 && - !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) + /* clang-format off */ + if ( + !CHECK_FLAG(pbrms->filter_bm, ( + PBR_FILTER_SRC_IP | + PBR_FILTER_DST_IP | + PBR_FILTER_SRC_PORT | + PBR_FILTER_DST_PORT | + + PBR_FILTER_IP_PROTOCOL | + PBR_FILTER_DSCP | + PBR_FILTER_ECN | + + PBR_FILTER_FWMARK | + PBR_FILTER_PCP | + PBR_FILTER_VLAN_ID | + PBR_FILTER_VLAN_FLAGS + )) && + !CHECK_FLAG(pbrms->action_bm, ( + + PBR_ACTION_PCP | + PBR_ACTION_VLAN_ID | + PBR_ACTION_VLAN_STRIP_INNER_ANY | + + PBR_ACTION_QUEUE_ID + )) + ) { pbrms->reason |= PBR_MAP_INVALID_EMPTY; + } + /* clang-format on */ } static void pbr_map_sequence_check_vlan_actions(struct pbr_map_sequence *pbrms) @@ -653,7 +635,8 @@ static void pbr_map_sequence_check_vlan_actions(struct pbr_map_sequence *pbrms) * The strip vlan action removes any inner tag, so it is invalid to * specify both a set and strip action. */ - if ((pbrms->action_vlan_id != 0) && (pbrms->action_vlan_flags != 0)) + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_ID) && + (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_STRIP_INNER_ANY))) pbrms->reason |= PBR_MAP_INVALID_SET_STRIP_VLAN; } diff --git a/pbrd/pbr_map.h b/pbrd/pbr_map.h index 3afb199565..700bc93f72 100644 --- a/pbrd/pbr_map.h +++ b/pbrd/pbr_map.h @@ -3,7 +3,9 @@ * PBR-map Header * Copyright (C) 2018 Cumulus Networks, Inc. * Donald Sharp - * Copyright (c) 2023 LabN Consulting, L.L.C. + * Portions: + * Copyright (c) 2023 LabN Consulting, L.L.C. + * Copyright (c) 2021 The MITRE Corporation */ #ifndef __PBR_MAP_H__ #define __PBR_MAP_H__ @@ -54,6 +56,14 @@ struct pbr_map_interface { bool delete; }; +enum pbr_forwarding_type { + PBR_FT_UNSPEC = 0, + PBR_FT_VRF_UNCHANGED, + PBR_FT_SETVRF, + PBR_FT_NEXTHOP_GROUP, + PBR_FT_NEXTHOP_SINGLE, +}; + struct pbr_map_sequence { struct pbr_map *parent; @@ -109,14 +119,19 @@ struct pbr_map_sequence { * Action fields *****************************************************************/ + /* + * same bit definitions as in lib/pbr.h + */ + uint32_t action_bm; + uint8_t action_pcp; uint8_t action_vlan_id; -#define PBR_MAP_STRIP_INNER_ANY (1 << 0) - uint8_t action_vlan_flags; #define PBR_MAP_UNDEFINED_QUEUE_ID 0 uint32_t action_queue_id; + enum pbr_forwarding_type forwarding_type; + /* * Use interface's vrf. */ @@ -233,9 +248,4 @@ 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_nht.c b/pbrd/pbr_nht.c index 405a2d6588..4f7882fb22 100644 --- a/pbrd/pbr_nht.c +++ b/pbrd/pbr_nht.c @@ -549,6 +549,7 @@ void pbr_nht_set_seq_nhg(struct pbr_map_sequence *pbrms, const char *name) return; pbrms->nhgrp_name = XSTRDUP(MTYPE_TMP, name); + pbrms->forwarding_type = PBR_FT_NEXTHOP_GROUP; nhgc = nhgc_find(name); if (!nhgc) @@ -572,6 +573,7 @@ void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms, MTYPE_TMP, pbr_nht_nexthop_make_name(pbrms->parent->name, PBR_NHC_NAMELEN, pbrms->seqno, buf)); + pbrms->forwarding_type = PBR_FT_NEXTHOP_SINGLE; nh = nexthop_new(); memcpy(nh, nhop, sizeof(*nh)); diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index cffd3794c2..0d8fa6939a 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -132,10 +132,78 @@ DEFUN_NOSH(no_pbr_map, * 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; + + 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; + } + return true; +} + + /* clang-format off */ DEFPY (pbr_map_match_src, pbr_map_match_src_cmd, - "[no] match src-ip $prefix", + "[no] match src-ip ![$prefix]", NO_STR "Match the rest of the command\n" "Choose the src ip or ipv6 prefix to use\n" @@ -144,28 +212,37 @@ DEFPY (pbr_map_match_src, { /* clang-format on */ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + const char *fmsg = NULL; if (!pbrms) return CMD_WARNING_CONFIG_FAILED; - if (pbrms->dst && prefix->family != pbrms->dst->family) { - vty_out(vty, "Cannot mismatch families within match src/dst\n"); - return CMD_WARNING_CONFIG_FAILED; + if (no) { + if (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_IP)) + return CMD_SUCCESS; + prefix_free(&pbrms->src); + UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_IP); + goto check; } + assert(prefix); + if (!pbr_family_consistent(pbrms, prefix->family, PBR_FILTER_SRC_IP, 0, + &fmsg)) { + vty_out(vty, "Address family mismatch (%s)\n", fmsg); + return CMD_WARNING_CONFIG_FAILED; + } pbrms->family = prefix->family; - if (!no) { - if (pbrms->src) { - if (prefix_same(pbrms->src, prefix)) - return CMD_SUCCESS; - } else - pbrms->src = prefix_new(); - - prefix_copy(pbrms->src, prefix); + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_IP)) { + if (prefix_same(pbrms->src, prefix)) + return CMD_SUCCESS; } else - prefix_free(&pbrms->src); + pbrms->src = prefix_new(); + prefix_copy(pbrms->src, prefix); + SET_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_IP); + +check: pbr_map_check(pbrms, true); return CMD_SUCCESS; @@ -174,7 +251,7 @@ DEFPY (pbr_map_match_src, /* clang-format off */ DEFPY (pbr_map_match_dst, pbr_map_match_dst_cmd, - "[no] match dst-ip $prefix", + "[no] match dst-ip ![$prefix]", NO_STR "Match the rest of the command\n" "Choose the dst ip or ipv6 prefix to use\n" @@ -183,27 +260,37 @@ DEFPY (pbr_map_match_dst, { /* clang-format on */ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + const char *fmsg = NULL; if (!pbrms) return CMD_WARNING_CONFIG_FAILED; - if (pbrms->src && prefix->family != pbrms->src->family) { - vty_out(vty, "Cannot mismatch families within match src/dst\n"); + if (no) { + if (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DST_IP)) + return CMD_SUCCESS; + prefix_free(&pbrms->dst); + UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_DST_IP); + goto check; + } + + assert(prefix); + if (!pbr_family_consistent(pbrms, prefix->family, PBR_FILTER_DST_IP, 0, + &fmsg)) { + vty_out(vty, "Address family mismatch (%s)\n", fmsg); return CMD_WARNING_CONFIG_FAILED; } pbrms->family = prefix->family; - if (!no) { - if (pbrms->dst) { - if (prefix_same(pbrms->dst, prefix)) - return CMD_SUCCESS; - } else - pbrms->dst = prefix_new(); - - prefix_copy(pbrms->dst, prefix); + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DST_IP)) { + if (prefix_same(pbrms->dst, prefix)) + return CMD_SUCCESS; } else - prefix_free(&pbrms->dst); + pbrms->dst = prefix_new(); + prefix_copy(pbrms->dst, prefix); + SET_FLAG(pbrms->filter_bm, PBR_FILTER_DST_IP); + +check: pbr_map_check(pbrms, true); return CMD_SUCCESS; @@ -212,7 +299,7 @@ DEFPY (pbr_map_match_dst, /* clang-format off */ DEFPY (pbr_map_match_ip_proto, pbr_map_match_ip_proto_cmd, - "[no] match ip-protocol PROTO$ip_proto", + "[no] match ip-protocol ![PROTO$ip_proto]", NO_STR "Match the rest of the command\n" "Choose an ip-protocol\n" @@ -220,37 +307,39 @@ DEFPY (pbr_map_match_ip_proto, { /* clang-format on */ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); - struct protoent *p; + struct protoent *p = NULL; if (!pbrms) return CMD_WARNING_CONFIG_FAILED; - if (!no) { - if (!ip_proto) { - vty_out(vty, "Unable to convert (null) to proto id\n"); - return CMD_WARNING; - } + if (no) { + if (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_IP_PROTOCOL)) + return CMD_SUCCESS; + UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_IP_PROTOCOL); + goto check; + } + if (ip_proto) p = getprotobyname(ip_proto); - if (!p) { - vty_out(vty, "Unable to convert %s to proto id\n", - ip_proto); - return CMD_WARNING; - } - pbrms->ip_proto = p->p_proto; - } else - pbrms->ip_proto = 0; + if (!ip_proto || !p) { + vty_out(vty, "Unable to convert %s to proto id\n", + (ip_proto ? ip_proto : "(null)")); + return CMD_WARNING_CONFIG_FAILED; + } + pbrms->ip_proto = p->p_proto; + SET_FLAG(pbrms->filter_bm, PBR_FILTER_IP_PROTOCOL); + +check: pbr_map_check(pbrms, true); - return CMD_SUCCESS; } /* clang-format off */ DEFPY (pbr_map_match_src_port, pbr_map_match_src_port_cmd, - "[no] match src-port (1-65535)$port", + "[no] match src-port ![(1-65535)$port]", NO_STR "Match the rest of the command\n" "Choose the source port to use\n" @@ -262,23 +351,29 @@ DEFPY (pbr_map_match_src_port, if (!pbrms) return CMD_WARNING_CONFIG_FAILED; - if (!no) { - if (pbrms->src_prt == port) + if (no) { + if (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_PORT)) return CMD_SUCCESS; - else - pbrms->src_prt = port; - } else - pbrms->src_prt = 0; + UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_PORT); + goto check; + } + 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] match dst-port ![(1-65535)$port]", NO_STR "Match the rest of the command\n" "Choose the destination port to use\n" @@ -290,23 +385,29 @@ DEFPY (pbr_map_match_dst_port, if (!pbrms) return CMD_WARNING_CONFIG_FAILED; - if (!no) { - if (pbrms->dst_prt == port) + if (no) { + if (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DST_PORT)) return CMD_SUCCESS; - else - pbrms->dst_prt = port; - } else - pbrms->dst_prt = 0; + UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_DST_PORT); + goto check; + } + 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] match dscp ![DSCP$dscp]", NO_STR "Match the rest of the command\n" "Match based on IP DSCP field\n" @@ -314,68 +415,52 @@ DEFPY (pbr_map_match_dscp, { /* clang-format on */ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); - char dscpname[100]; - uint8_t rawDscp; if (!pbrms) return CMD_WARNING_CONFIG_FAILED; - /* Discriminate dscp enums (cs0, cs1 etc.) and numbers */ - bool isANumber = true; - for (int i = 0; i < (int)strlen(dscp); i++) { - /* Letters are not numbers */ - if (!isdigit(dscp[i])) - isANumber = false; - - /* Lowercase the dscp enum (if needed) */ - if (isupper(dscp[i])) - dscpname[i] = tolower(dscp[i]); - else - dscpname[i] = dscp[i]; - } - dscpname[strlen(dscp)] = '\0'; - - if (isANumber) { - /* dscp passed is a regular number */ - long dscpAsNum = strtol(dscp, NULL, 0); - - if (dscpAsNum > PBR_DSFIELD_DSCP >> 2) { - /* Refuse to install on overflow */ - vty_out(vty, "dscp (%s) must be less than 64\n", dscp); - return CMD_WARNING_CONFIG_FAILED; - } - rawDscp = dscpAsNum; - } else { - /* check dscp if it is an enum like cs0 */ - rawDscp = pbr_map_decode_dscp_enum(dscpname); - if (rawDscp > PBR_DSFIELD_DSCP) { - vty_out(vty, "Invalid dscp value: %s\n", dscpname); - return CMD_WARNING_CONFIG_FAILED; - } - } - - if (!no) { - if (((pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2) == rawDscp) + if (no) { + if (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DSCP)) return CMD_SUCCESS; - - /* Set the DSCP bits of the DSField */ - pbrms->dsfield = - (pbrms->dsfield & ~PBR_DSFIELD_DSCP) | (rawDscp << 2); - SET_FLAG(pbrms->filter_bm, PBR_FILTER_DSCP); - } else { - pbrms->dsfield &= ~PBR_DSFIELD_DSCP; UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_DSCP); + pbrms->dsfield &= ~PBR_DSFIELD_DSCP; + goto check; } - pbr_map_check(pbrms, true); + unsigned long ul_dscp; + char *pend; + uint8_t raw_dscp; + assert(dscp); + ul_dscp = strtol(dscp, &pend, 0); + if (*pend) + raw_dscp = pbr_map_decode_dscp_enum(dscp); + else + raw_dscp = ul_dscp << 2; + if (raw_dscp > PBR_DSFIELD_DSCP) { + vty_out(vty, "Invalid dscp value: %s%s\n", dscp, + (pend ? "" : " (numeric value must be in range 0-63)")); + return CMD_WARNING_CONFIG_FAILED; + } + + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DSCP) && + (((pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2) == raw_dscp)) { + return CMD_SUCCESS; + } + + /* Set the DSCP bits of the DSField */ + pbrms->dsfield = (pbrms->dsfield & ~PBR_DSFIELD_DSCP) | (raw_dscp << 2); + 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] match ecn ![(0-3)$ecn]", NO_STR "Match the rest of the command\n" "Match based on IP ECN field\n" @@ -387,20 +472,25 @@ DEFPY (pbr_map_match_ecn, if (!pbrms) return CMD_WARNING_CONFIG_FAILED; - if (!no) { - if ((pbrms->dsfield & PBR_DSFIELD_ECN) == ecn) + if (no) { + if (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_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); - } else { - pbrms->dsfield &= ~PBR_DSFIELD_ECN; UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_ECN); + pbrms->dsfield &= ~PBR_DSFIELD_ECN; + goto check; } - pbr_map_check(pbrms, true); + 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; } @@ -409,7 +499,9 @@ DEFPY (pbr_map_match_ecn, ***********************************************************************/ /* clang-format off */ -DEFPY (pbr_map_match_pcp, pbr_map_match_pcp_cmd, "[no] match pcp <(0-7)$pcp>", +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" @@ -418,16 +510,33 @@ DEFPY (pbr_map_match_pcp, pbr_map_match_pcp_cmd, "[no] match pcp <(0-7)$pcp>", /* 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); + 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] match vlan ![(1-4094)$vlan_id]", NO_STR "Match spec follows\n" "Match based on VLAN ID\n" @@ -436,19 +545,31 @@ DEFPY (pbr_map_match_vlan_id, /* 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); - } - } - } + 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; } @@ -465,26 +586,45 @@ DEFPY (pbr_map_match_vlan_tag, { /* clang-format on */ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + uint16_t vlan_flags; if (!pbrms) - return CMD_WARNING; + return CMD_WARNING_CONFIG_FAILED; - if (!no) { - assert(tag_type); - 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); + 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; } @@ -495,7 +635,7 @@ DEFPY (pbr_map_match_vlan_tag, /* clang-format off */ DEFPY (pbr_map_match_mark, pbr_map_match_mark_cmd, - "[no] match mark (1-4294967295)$mark", + "[no] match mark ![(1-4294967295)$mark]", NO_STR "Match the rest of the command\n" "Choose the mark value to use\n" @@ -512,15 +652,22 @@ DEFPY (pbr_map_match_mark, return CMD_WARNING_CONFIG_FAILED; #endif - if (!no) { - if (pbrms->mark) - if (pbrms->mark == (uint32_t)mark) - return CMD_SUCCESS; + if (no) { + if (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_FWMARK)) + return CMD_SUCCESS; + UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_FWMARK); + goto check; + } - pbrms->mark = (uint32_t)mark; - } else - pbrms->mark = 0; + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_FWMARK) && + (pbrms->mark == (uint32_t)mark)) { + return CMD_SUCCESS; + } + pbrms->mark = (uint32_t)mark; + SET_FLAG(pbrms->filter_bm, PBR_FILTER_FWMARK); + +check: pbr_map_check(pbrms, true); return CMD_SUCCESS; @@ -533,7 +680,7 @@ DEFPY (pbr_map_match_mark, /* clang-format off */ DEFPY (pbr_map_action_queue_id, pbr_map_action_queue_id_cmd, - "[no] set queue-id <(1-65535)$queue_id>", + "[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" @@ -545,13 +692,22 @@ DEFPY (pbr_map_action_queue_id, if (!pbrms) return CMD_WARNING_CONFIG_FAILED; - if (!no) - pbrms->action_queue_id = queue_id; - else if ((uint32_t)queue_id == pbrms->action_queue_id) - pbrms->action_queue_id = PBR_MAP_UNDEFINED_QUEUE_ID; + if (no) { + if (!CHECK_FLAG(pbrms->action_bm, PBR_ACTION_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; } @@ -563,7 +719,7 @@ DEFPY (pbr_map_action_queue_id, /* clang-format off */ DEFPY (pbr_map_action_pcp, pbr_map_action_pcp_cmd, - "[no] set pcp <(0-7)$pcp>", + "[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" @@ -575,20 +731,30 @@ DEFPY (pbr_map_action_pcp, if (!pbrms) return CMD_WARNING_CONFIG_FAILED; - if (!no) - pbrms->action_pcp = pcp; - else if (pcp == pbrms->action_pcp) - pbrms->action_pcp = 0; + if (no) { + if (!CHECK_FLAG(pbrms->action_bm, PBR_ACTION_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] set vlan ![(1-4094)$vlan_id]", NO_STR "Set the rest of the command\n" "Set action for VLAN tagging\n" @@ -600,13 +766,27 @@ DEFPY (pbr_map_action_vlan_id, if (!pbrms) return CMD_WARNING_CONFIG_FAILED; - if (!no) - pbrms->action_vlan_id = vlan_id; - else if (pbrms->action_vlan_id == vlan_id) - pbrms->action_vlan_id = 0; + if (no) { + if (!CHECK_FLAG(pbrms->action_bm, PBR_ACTION_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; } @@ -616,7 +796,7 @@ DEFPY (pbr_map_action_strip_vlan, "[no] strip vlan", NO_STR "Strip the vlan tags from frame\n" - "Strip any inner vlan tag \n") + "Strip any inner vlan tag\n") { /* clang-format on */ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); @@ -624,13 +804,24 @@ DEFPY (pbr_map_action_strip_vlan, if (!pbrms) return CMD_WARNING_CONFIG_FAILED; - if (!no) - pbrms->action_vlan_flags = PBR_MAP_STRIP_INNER_ANY; - else - pbrms->action_vlan_flags = 0; + if (no) { + if (!CHECK_FLAG(pbrms->action_bm, + PBR_ACTION_VLAN_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; } @@ -668,6 +859,8 @@ static void pbrms_clear_set_config(struct pbr_map_sequence *pbrms) pbrms_clear_set_nexthop_config(pbrms); pbrms->nhs_installed = false; + + pbrms->forwarding_type = PBR_FT_UNSPEC; } @@ -904,10 +1097,11 @@ DEFPY(pbr_map_vrf, pbr_map_vrf_cmd, /* * If an equivalent set vrf * exists, just return success. */ - if (vrf_name && pbrms->vrf_lookup - && strncmp(pbrms->vrf_name, vrf_name, sizeof(pbrms->vrf_name)) == 0) + if ((pbrms->forwarding_type == PBR_FT_SETVRF) && + strncmp(pbrms->vrf_name, vrf_name, sizeof(pbrms->vrf_name)) == 0) return CMD_SUCCESS; - else if (!vrf_name && pbrms->vrf_unchanged) /* Unchanged already set */ + else if (!vrf_name && (pbrms->forwarding_type == PBR_FT_VRF_UNCHANGED)) + /* Unchanged already set */ return CMD_SUCCESS; if (vrf_name && !pbr_vrf_lookup_by_name(vrf_name)) { @@ -920,9 +1114,12 @@ DEFPY(pbr_map_vrf, pbr_map_vrf_cmd, if (vrf_name) { pbrms->vrf_lookup = true; + pbrms->forwarding_type = PBR_FT_SETVRF; strlcpy(pbrms->vrf_name, vrf_name, sizeof(pbrms->vrf_name)); - } else + } else { + pbrms->forwarding_type = PBR_FT_VRF_UNCHANGED; pbrms->vrf_unchanged = true; + } pbr_map_check(pbrms, true); @@ -1066,56 +1263,77 @@ static void vty_show_pbrms(struct vty *vty, pbrms->installed ? "yes" : "no", pbrms->reason ? rbuf : "Valid"); - if (pbrms->ip_proto) { + /* match clauses first */ + + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_IP_PROTOCOL)) { struct protoent *p; p = getprotobynumber(pbrms->ip_proto); vty_out(vty, " IP Protocol Match: %s\n", p->p_name); } - if (pbrms->src) + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_IP)) vty_out(vty, " SRC IP Match: %pFX\n", pbrms->src); - if (pbrms->dst) + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DST_IP)) vty_out(vty, " DST IP Match: %pFX\n", pbrms->dst); - if (pbrms->src_prt) + + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_PORT)) vty_out(vty, " SRC Port Match: %u\n", pbrms->src_prt); - if (pbrms->dst_prt) + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DST_PORT)) vty_out(vty, " DST Port Match: %u\n", pbrms->dst_prt); - if (pbrms->dsfield & PBR_DSFIELD_DSCP) + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DSCP)) vty_out(vty, " DSCP Match: %u\n", (pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2); - if (pbrms->dsfield & PBR_DSFIELD_ECN) + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_ECN)) vty_out(vty, " ECN Match: %u\n", pbrms->dsfield & PBR_DSFIELD_ECN); - if (pbrms->mark) + + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_FWMARK)) vty_out(vty, " MARK Match: %u\n", pbrms->mark); if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_PCP)) vty_out(vty, " PCP Match: %d\n", pbrms->match_pcp); - if (pbrms->match_vlan_id != 0) + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_ID)) 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 (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_FLAGS)) { + if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_TAGGED) + vty_out(vty, " Match VLAN tagged frames\n"); + if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_UNTAGGED) + vty_out(vty, " Match VLAN untagged frames\n"); + if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_UNTAGGED_0) + vty_out(vty, " Match VLAN untagged or ID 0\n"); + } - if (pbrms->action_queue_id != PBR_MAP_UNDEFINED_QUEUE_ID) + /* set actions */ + + + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_QUEUE_ID)) vty_out(vty, " Set Queue ID: %u\n", pbrms->action_queue_id); - if (pbrms->action_vlan_id != 0) + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_ID)) vty_out(vty, " Set VLAN ID %u\n", pbrms->action_vlan_id); - if (pbrms->action_vlan_flags == PBR_MAP_STRIP_INNER_ANY) + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_STRIP_INNER_ANY)) vty_out(vty, " Strip VLAN ID\n"); - if (pbrms->action_pcp) + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_PCP)) vty_out(vty, " Set PCP %u\n", pbrms->action_pcp); - if (pbrms->nhgrp_name) { + switch (pbrms->forwarding_type) { + case PBR_FT_UNSPEC: + vty_out(vty, " Nexthop-Group: Unknown Installed: no\n"); + break; + case PBR_FT_VRF_UNCHANGED: + vty_out(vty, " VRF Unchanged (use interface vrf)\n"); + break; + case PBR_FT_SETVRF: + assert(pbrms->vrf_name); + vty_out(vty, " VRF Lookup: %s\n", pbrms->vrf_name); + break; + case PBR_FT_NEXTHOP_GROUP: + assert(pbrms->nhgrp_name); vty_out(vty, " Nexthop-Group: %s\n", pbrms->nhgrp_name); if (detail) @@ -1129,8 +1347,9 @@ static void vty_show_pbrms(struct vty *vty, pbr_nht_get_installed(pbrms->nhgrp_name) ? "yes" : "no", pbr_nht_get_table(pbrms->nhgrp_name)); - - } else if (pbrms->nhg) { + break; + case PBR_FT_NEXTHOP_SINGLE: + assert(pbrms->internal_nhg_name); vty_out(vty, " "); pbrms_nexthop_group_write_individual_nexthop(vty, pbrms); if (detail) @@ -1145,13 +1364,7 @@ static void vty_show_pbrms(struct vty *vty, ? "yes" : "no", pbr_nht_get_table(pbrms->internal_nhg_name)); - - } else if (pbrms->vrf_unchanged) { - vty_out(vty, " VRF Unchanged (use interface vrf)\n"); - } else if (pbrms->vrf_lookup) { - vty_out(vty, " VRF Lookup: %s\n", pbrms->vrf_name); - } else { - vty_out(vty, " Nexthop-Group: Unknown Installed: no\n"); + break; } } @@ -1178,7 +1391,18 @@ static void vty_json_pbrms(json_object *j, struct vty *vty, json_object_string_add(jpbrm, "installedReason", pbrms->reason ? rbuf : "Valid"); - if (nhg_name) { + switch (pbrms->forwarding_type) { + case PBR_FT_UNSPEC: + break; + case PBR_FT_VRF_UNCHANGED: + break; + case PBR_FT_SETVRF: + assert(pbrms->vrf_name); + json_object_string_add(jpbrm, "vrfName", pbrms->vrf_name); + break; + case PBR_FT_NEXTHOP_GROUP: + case PBR_FT_NEXTHOP_SINGLE: + assert(nhg_name); nexthop_group = json_object_new_object(); json_object_int_add(nexthop_group, "tableId", @@ -1190,24 +1414,74 @@ static void vty_json_pbrms(json_object *j, struct vty *vty, pbrms->nhs_installed); json_object_object_add(jpbrm, "nexthopGroup", nexthop_group); + break; } - if (pbrms->vrf_lookup) - json_object_string_add(jpbrm, "vrfName", pbrms->vrf_name); - if (pbrms->src) + /* + * Match clauses + */ + + /* IP Header */ + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_IP_PROTOCOL)) + json_object_int_add(jpbrm, "matchIpProtocol", pbrms->ip_proto); + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_IP)) json_object_string_addf(jpbrm, "matchSrc", "%pFX", pbrms->src); - if (pbrms->dst) + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DST_IP)) json_object_string_addf(jpbrm, "matchDst", "%pFX", pbrms->dst); - if (pbrms->mark) - json_object_int_add(jpbrm, "matchMark", pbrms->mark); - if (pbrms->dsfield & PBR_DSFIELD_DSCP) + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_SRC_PORT)) + json_object_int_add(jpbrm, "matchSrcPort", pbrms->src_prt); + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DST_PORT)) + json_object_int_add(jpbrm, "matchDstPort", pbrms->dst_prt); + + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DSCP)) json_object_int_add(jpbrm, "matchDscp", (pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2); - if (pbrms->dsfield & PBR_DSFIELD_ECN) + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_ECN)) json_object_int_add(jpbrm, "matchEcn", pbrms->dsfield & PBR_DSFIELD_ECN); + /* L2 headers */ + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_PCP)) + json_object_int_add(jpbrm, "matchPcp", pbrms->match_pcp); + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_ID)) + json_object_int_add(jpbrm, "matchVlanId", pbrms->match_vlan_id); + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_FLAGS)) { + const char *p = "?"; + + if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_TAGGED) + p = "tagged"; + if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_UNTAGGED) + p = "untagged"; + if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_UNTAGGED_0) + p = "untagged-or-0"; + + json_object_string_addf(jpbrm, "matchVlanFlags", "%s", p); + } + + /* meta */ + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_FWMARK)) + json_object_int_add(jpbrm, "matchMark", pbrms->mark); + + /* + * action clauses + */ + + + /* 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); } @@ -1473,71 +1747,86 @@ static int pbr_vty_map_config_write_sequence(struct vty *vty, { vty_out(vty, "pbr-map %s seq %u\n", pbrm->name, pbrms->seqno); - if (pbrms->src) - vty_out(vty, " match src-ip %pFX\n", pbrms->src); - - if (pbrms->dst) - vty_out(vty, " match dst-ip %pFX\n", pbrms->dst); - - if (pbrms->src_prt) - vty_out(vty, " match src-port %u\n", pbrms->src_prt); - if (pbrms->dst_prt) - vty_out(vty, " match dst-port %u\n", pbrms->dst_prt); - - if (pbrms->ip_proto) { + 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 (pbrms->dsfield & PBR_DSFIELD_DSCP) + 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 (pbrms->dsfield & PBR_DSFIELD_ECN) + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_ECN)) vty_out(vty, " match ecn %u\n", pbrms->dsfield & PBR_DSFIELD_ECN); - if (pbrms->mark) - vty_out(vty, " match mark %u\n", pbrms->mark); if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_PCP)) vty_out(vty, " match pcp %d\n", pbrms->match_pcp); - if ((pbrms->match_vlan_id) && - (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_NO_WILD)) + /* L2 headers */ + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_ID)) 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 (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_FLAGS)) { + if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_TAGGED) + vty_out(vty, " match vlan tagged\n"); + if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_UNTAGGED) + vty_out(vty, " match vlan untagged\n"); + if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_UNTAGGED_0) + vty_out(vty, " match vlan untagged-or-zero\n"); + } - if (pbrms->action_queue_id != PBR_MAP_UNDEFINED_QUEUE_ID) - vty_out(vty, " set queue-id %d\n", pbrms->action_queue_id); + /* meta */ + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_FWMARK)) + vty_out(vty, " match mark %u\n", pbrms->mark); - if (pbrms->action_pcp) + /* + * action clauses + */ + + /* L2 header fields */ + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_STRIP_INNER_ANY)) + vty_out(vty, " strip vlan any\n"); + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_ID)) + vty_out(vty, " set vlan %u\n", pbrms->action_vlan_id); + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_PCP)) vty_out(vty, " set pcp %d\n", pbrms->action_pcp); - if (pbrms->action_vlan_id) - vty_out(vty, " set vlan %u\n", pbrms->action_vlan_id); + /* meta */ + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_QUEUE_ID)) + vty_out(vty, " set queue-id %d\n", pbrms->action_queue_id); - if (pbrms->action_vlan_flags == PBR_MAP_STRIP_INNER_ANY) - vty_out(vty, " strip vlan any\n"); - - if (pbrms->vrf_unchanged) + switch (pbrms->forwarding_type) { + case PBR_FT_UNSPEC: + break; + case PBR_FT_VRF_UNCHANGED: vty_out(vty, " set vrf unchanged\n"); - - if (pbrms->vrf_lookup) + break; + case PBR_FT_SETVRF: + assert(pbrms->vrf_name); vty_out(vty, " set vrf %s\n", pbrms->vrf_name); - - if (pbrms->nhgrp_name) + break; + case PBR_FT_NEXTHOP_GROUP: + assert(pbrms->nhgrp_name); vty_out(vty, " set nexthop-group %s\n", pbrms->nhgrp_name); - - if (pbrms->nhg) { + break; + case PBR_FT_NEXTHOP_SINGLE: + assert(pbrms->nhg); vty_out(vty, " set "); pbrms_nexthop_group_write_individual_nexthop(vty, pbrms); + break; } vty_out(vty, "exit\n"); @@ -1605,6 +1894,7 @@ void pbr_vty_init(void) install_element(CONFIG_NODE, &pbr_set_table_range_cmd); install_element(CONFIG_NODE, &no_pbr_set_table_range_cmd); install_element(INTERFACE_NODE, &pbr_policy_cmd); + install_element(PBRMAP_NODE, &pbr_map_match_ip_proto_cmd); install_element(PBRMAP_NODE, &pbr_map_match_src_port_cmd); install_element(PBRMAP_NODE, &pbr_map_match_dst_port_cmd); diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c index adcf449cfa..66148c630d 100644 --- a/pbrd/pbr_zebra.c +++ b/pbrd/pbr_zebra.c @@ -564,44 +564,18 @@ static bool pbr_encode_pbr_map_sequence(struct stream *s, r.filter.fwmark = pbrms->mark; r.filter.ip_proto = pbrms->ip_proto; - /* - * Fix up filter flags for now, since PBRD doesn't maintain - * them yet (aside from PBR_FILTER_PCP) - */ - if (!is_default_prefix(&r.filter.src_ip)) - SET_FLAG(r.filter.filter_bm, PBR_FILTER_SRC_IP); - if (!is_default_prefix(&r.filter.dst_ip)) - SET_FLAG(r.filter.filter_bm, PBR_FILTER_DST_IP); - if (r.filter.src_port) - SET_FLAG(r.filter.filter_bm, PBR_FILTER_SRC_PORT); - if (r.filter.dst_port) - SET_FLAG(r.filter.filter_bm, PBR_FILTER_DST_PORT); - if (r.filter.vlan_id) - SET_FLAG(r.filter.filter_bm, PBR_FILTER_VLAN_ID); - if (r.filter.vlan_flags) - SET_FLAG(r.filter.filter_bm, PBR_FILTER_VLAN_FLAGS); - if (r.filter.dsfield) - SET_FLAG(r.filter.filter_bm, PBR_FILTER_DSFIELD); - if (r.filter.fwmark) - SET_FLAG(r.filter.filter_bm, PBR_FILTER_FWMARK); - if (r.filter.ip_proto) - SET_FLAG(r.filter.filter_bm, PBR_FILTER_IP_PROTOCOL); + r.filter.filter_bm = pbrms->filter_bm; /* actions */ + SET_FLAG(r.action.flags, PBR_ACTION_TABLE); /* always valid */ + /* * PBR should maintain its own set of action flags that we * can copy here instead of trying to infer from magic values. */ - SET_FLAG(r.action.flags, PBR_ACTION_TABLE); /* always valid */ - if (pbrms->action_queue_id != PBR_MAP_UNDEFINED_QUEUE_ID) - SET_FLAG(r.action.flags, PBR_ACTION_QUEUE_ID); - if (pbrms->action_pcp != 0) - SET_FLAG(r.action.flags, PBR_ACTION_PCP); - if (pbrms->action_vlan_id != 0) - SET_FLAG(r.action.flags, PBR_ACTION_VLAN_ID); - if (pbrms->action_vlan_flags != 0) - SET_FLAG(r.action.flags, PBR_ACTION_VLAN_FLAGS); + + r.action.flags = pbrms->action_bm; /* * if the user does not use the command "set vrf name unchanged" @@ -619,9 +593,9 @@ static bool pbr_encode_pbr_map_sequence(struct stream *s, } r.action.queue_id = pbrms->action_queue_id; + r.action.pcp = pbrms->action_pcp; r.action.vlan_id = pbrms->action_vlan_id; - r.action.vlan_flags = pbrms->action_vlan_flags; strlcpy(r.ifname, ifp->name, sizeof(r.ifname)); diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index e9c243217a..f6afb006b7 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -3188,10 +3188,32 @@ static inline void zread_rule(ZAPI_HANDLER_ARGS) strlcpy(zpr.ifname, zpr.rule.ifname, sizeof(zpr.ifname)); + if ((zpr.rule.family != AF_INET) && + (zpr.rule.family != AF_INET6)) { + zlog_warn("Unsupported PBR source IP family: %s (%hhu)", + family2str(zpr.rule.family), zpr.rule.family); + return; + } + + /* + * Fixup filter src/dst IP addresses if they are unset + * because the netlink code currently obtains address family + * from them. Address family is used to specify which + * kernel database to use when adding/deleting rule. + * + * TBD: propagate zpr.rule.family into dataplane and + * netlink code so they can stop using filter src/dst addrs. + */ + if (!CHECK_FLAG(zpr.rule.filter.filter_bm, PBR_FILTER_SRC_IP)) + zpr.rule.filter.src_ip.family = zpr.rule.family; + if (!CHECK_FLAG(zpr.rule.filter.filter_bm, PBR_FILTER_DST_IP)) + zpr.rule.filter.dst_ip.family = zpr.rule.family; + + /* TBD delete below block when netlink code gets family from zpr.rule.family */ if (!(zpr.rule.filter.src_ip.family == AF_INET || zpr.rule.filter.src_ip.family == AF_INET6)) { zlog_warn( - "Unsupported PBR source IP family: %s (%hhu)", + "Unsupported PBR source IP family: %s (%u)", family2str(zpr.rule.filter.src_ip.family), zpr.rule.filter.src_ip.family); return; @@ -3199,11 +3221,12 @@ static inline void zread_rule(ZAPI_HANDLER_ARGS) if (!(zpr.rule.filter.dst_ip.family == AF_INET || zpr.rule.filter.dst_ip.family == AF_INET6)) { zlog_warn( - "Unsupported PBR destination IP family: %s (%hhu)", + "Unsupported PBR destination IP family: %s (%u)", family2str(zpr.rule.filter.dst_ip.family), zpr.rule.filter.dst_ip.family); return; } + /* TBD delete above block when netlink code gets family from zpr.rule.family */ zpr.vrf_id = zvrf->vrf->vrf_id; From ba240bcfa3762158eaa548906ba9c306d96fe5d1 Mon Sep 17 00:00:00 2001 From: "G. Paul Ziemba" Date: Tue, 1 Aug 2023 11:00:52 -0700 Subject: [PATCH 7/7] pbrd: add packet mangling actions (src/dst ip-addr/port, dscp, ecn) Signed-off-by: G. Paul Ziemba --- doc/user/pbr.rst | 52 ++++ lib/pbr.h | 16 + lib/zclient.c | 79 +++++ pbrd/pbr_map.c | 7 + pbrd/pbr_map.h | 9 + pbrd/pbr_vty.c | 310 +++++++++++++++++++- pbrd/pbr_zebra.c | 9 + tests/topotests/pbr_topo1/test_pbr_topo1.py | 27 +- zebra/zebra_pbr.c | 25 ++ 9 files changed, 521 insertions(+), 13 deletions(-) diff --git a/doc/user/pbr.rst b/doc/user/pbr.rst index 4d90f5cb6f..d0513018e3 100644 --- a/doc/user/pbr.rst +++ b/doc/user/pbr.rst @@ -215,6 +215,58 @@ specified in the rule are also applied to the packet. so this field will be ignored unless another dataplane provider is used. It is invalid to specify both a `strip` and `set vlan` action. +.. clicmd:: set src-ip [A.B.C.D/M|X:X::X:X/M] + + Action: + Set the source IP address of matched packets, possibly using a mask `M`. + The Linux Kernel dataplane provider does not currently support + packet mangling, + so this field will be ignored unless another dataplane provider is used. + +.. clicmd:: set dst-ip [A.B.C.D/M|X:X::X:X/M] + + Action: + set the destination IP address of matched packets, possibly using a mask + `M`. + The Linux Kernel dataplane provider does not currently support + packet mangling, + so this field will be ignored unless another dataplane provider is used. + +.. clicmd:: set src-port (1-65535) + + Action: + set the source port of matched packets. Note that this action only makes + sense with layer 4 protocols that use ports, such as TCP, UDP, and SCTP. + The Linux Kernel dataplane provider does not currently support + packet mangling, + so this field will be ignored unless another dataplane provider is used. + +.. clicmd:: set dst-port (1-65535) + + Action: + set the destination port of matched packets. Note that this action only + makes sense with layer 4 protocols that use ports, such as TCP, UDP, and + SCTP. + The Linux Kernel dataplane provider does not currently support + packet mangling, + so this field will be ignored unless another dataplane provider is used. + +.. clicmd:: set dscp DSCP + + Action: + set the differentiated services code point (DSCP) of matched packets. + The Linux Kernel dataplane provider does not currently support + this action, + so this field will be ignored unless another dataplane provider is used. + +.. clicmd:: set ecn (0-3) + + Action: + set the explicit congestion notification (ECN) of matched packets. + The Linux Kernel dataplane provider does not currently support + this action, + so this field will be ignored unless another dataplane provider is used. + .. clicmd:: show pbr map [NAME] [detail|json] Display pbr maps either all or by ``NAME``. If ``detail`` is set, it will diff --git a/lib/pbr.h b/lib/pbr.h index ae01ae7978..c514cc2a65 100644 --- a/lib/pbr.h +++ b/lib/pbr.h @@ -93,6 +93,12 @@ struct pbr_action { #define PBR_ACTION_PCP (1 << 2) #define PBR_ACTION_VLAN_ID (1 << 3) #define PBR_ACTION_VLAN_STRIP_INNER_ANY (1 << 4) +#define PBR_ACTION_SRC_IP (1 << 5) +#define PBR_ACTION_DST_IP (1 << 6) +#define PBR_ACTION_SRC_PORT (1 << 7) +#define PBR_ACTION_DST_PORT (1 << 8) +#define PBR_ACTION_DSCP (1 << 9) +#define PBR_ACTION_ECN (1 << 10) uint32_t table; uint32_t queue_id; @@ -101,7 +107,17 @@ struct pbr_action { uint8_t pcp; uint16_t vlan_id; + /* Source and Destination IP addresses */ + union sockunion src_ip; + union sockunion dst_ip; + /* Source and Destination layer 4 (TCP/UDP/etc.) port numbers */ + uint32_t src_port; + uint32_t dst_port; + + /* Differentiated Services field */ + uint8_t dscp; /* stored here already shifted to upper 6 bits */ + uint8_t ecn; /* stored here as lower 2 bits */ }; /* diff --git a/lib/zclient.c b/lib/zclient.c index 97c829c90d..e40725826a 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -1622,6 +1622,47 @@ stream_failure: return false; } +static void zapi_encode_sockunion(struct stream *s, const union sockunion *su) +{ + int family = sockunion_family(su); + size_t addrlen = family2addrsize(family); + + /* + * Must know length to encode + */ + assert(addrlen); + + stream_putc(s, (uint8_t)family); + + stream_write(s, sockunion_get_addr(su), addrlen); +} + +static bool zapi_decode_sockunion(struct stream *s, union sockunion *su) +{ + uint8_t family; + size_t addrlen; + uint8_t buf[sizeof(union sockunion)]; + + memset(su, 0, sizeof(*su)); + + STREAM_GETC(s, family); + sockunion_family(su) = family; + + addrlen = family2addrsize(family); + if (!addrlen) + return false; + + if (addrlen > sizeof(buf)) + return false; + + STREAM_GET(buf, s, addrlen); + sockunion_set(su, family, buf, addrlen); + return true; + +stream_failure: + return false; +} + /* * Encode filter subsection of pbr_rule */ @@ -1719,6 +1760,21 @@ static void zapi_pbr_rule_action_encode(struct stream *s, struct pbr_action *a) if (CHECK_FLAG(a->flags, PBR_ACTION_QUEUE_ID)) stream_putl(s, a->queue_id); + /* L3 */ + if (CHECK_FLAG(a->flags, PBR_ACTION_SRC_IP)) + zapi_encode_sockunion(s, &a->src_ip); + if (CHECK_FLAG(a->flags, PBR_ACTION_DST_IP)) + zapi_encode_sockunion(s, &a->dst_ip); + if (CHECK_FLAG(a->flags, PBR_ACTION_SRC_PORT)) + stream_putw(s, a->src_port); + if (CHECK_FLAG(a->flags, PBR_ACTION_DST_PORT)) + stream_putw(s, a->dst_port); + + if (CHECK_FLAG(a->flags, PBR_ACTION_DSCP)) + stream_putc(s, a->dscp & PBR_DSFIELD_DSCP); + if (CHECK_FLAG(a->flags, PBR_ACTION_ECN)) + stream_putc(s, a->ecn & PBR_DSFIELD_ECN); + /* L2 */ if (CHECK_FLAG(a->flags, PBR_ACTION_PCP)) stream_putc(s, a->pcp); @@ -1735,6 +1791,29 @@ static bool zapi_pbr_rule_action_decode(struct stream *s, struct pbr_action *a) if (CHECK_FLAG(a->flags, PBR_ACTION_QUEUE_ID)) STREAM_GETL(s, a->queue_id); + /* L3 */ + if (CHECK_FLAG(a->flags, PBR_ACTION_SRC_IP)) { + if (!zapi_decode_sockunion(s, &(a->src_ip))) + goto stream_failure; + } + if (CHECK_FLAG(a->flags, PBR_ACTION_DST_IP)) + if (!zapi_decode_sockunion(s, &(a->dst_ip))) + goto stream_failure; + + if (CHECK_FLAG(a->flags, PBR_ACTION_SRC_PORT)) + STREAM_GETW(s, a->src_port); + if (CHECK_FLAG(a->flags, PBR_ACTION_DST_PORT)) + STREAM_GETW(s, a->dst_port); + + if (CHECK_FLAG(a->flags, PBR_ACTION_DSCP)) { + STREAM_GETC(s, a->dscp); + a->dscp &= PBR_DSFIELD_DSCP; + } + if (CHECK_FLAG(a->flags, PBR_ACTION_ECN)) { + STREAM_GETC(s, a->ecn); + a->ecn &= PBR_DSFIELD_ECN; + } + /* L2 */ if (CHECK_FLAG(a->flags, PBR_ACTION_PCP)) STREAM_GETC(s, a->pcp); diff --git a/pbrd/pbr_map.c b/pbrd/pbr_map.c index 30148fe093..6b81e1058e 100644 --- a/pbrd/pbr_map.c +++ b/pbrd/pbr_map.c @@ -612,6 +612,13 @@ static void pbr_map_sequence_check_not_empty(struct pbr_map_sequence *pbrms) PBR_FILTER_VLAN_FLAGS )) && !CHECK_FLAG(pbrms->action_bm, ( + PBR_ACTION_SRC_IP | + PBR_ACTION_DST_IP | + PBR_ACTION_SRC_PORT | + PBR_ACTION_DST_PORT | + + PBR_ACTION_DSCP | + PBR_ACTION_ECN | PBR_ACTION_PCP | PBR_ACTION_VLAN_ID | diff --git a/pbrd/pbr_map.h b/pbrd/pbr_map.h index 700bc93f72..61577e4076 100644 --- a/pbrd/pbr_map.h +++ b/pbrd/pbr_map.h @@ -124,6 +124,15 @@ struct pbr_map_sequence { */ uint32_t action_bm; + union sockunion action_src; + union sockunion action_dst; + + uint16_t action_src_port; + uint16_t action_dst_port; + + uint8_t action_dscp; + uint8_t action_ecn; + uint8_t action_pcp; uint8_t action_vlan_id; diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index 0d8fa6939a..0d6e1afd5b 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -183,6 +183,7 @@ static bool pbr_family_consistent(struct pbr_map_sequence *pbrms, 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)) { @@ -196,6 +197,18 @@ static bool pbr_family_consistent(struct pbr_map_sequence *pbrms, *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; } @@ -206,7 +219,7 @@ DEFPY (pbr_map_match_src, "[no] match src-ip ![$prefix]", NO_STR "Match the rest of the command\n" - "Choose the src ip or ipv6 prefix to use\n" + "Choose the src ipv4 or ipv6 prefix to use\n" "v4 Prefix\n" "v6 Prefix\n") { @@ -254,7 +267,7 @@ DEFPY (pbr_map_match_dst, "[no] match dst-ip ![$prefix]", NO_STR "Match the rest of the command\n" - "Choose the dst ip or ipv6 prefix to use\n" + "Choose the dst ipv4 or ipv6 prefix to use\n" "v4 Prefix\n" "v6 Prefix\n") { @@ -673,6 +686,237 @@ check: 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 ![$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 ![$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 (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 (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 (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; + uint8_t raw_dscp; + + assert(dscp); + ul_dscp = strtol(dscp, &pend, 0); + if (*pend) + raw_dscp = pbr_map_decode_dscp_enum(dscp); + else + raw_dscp = ul_dscp << 2; + + if (raw_dscp > PBR_DSFIELD_DSCP) { + vty_out(vty, "Invalid dscp value: %s%s\n", dscp, + (pend ? "" : " (numeric value must be in range 0-63)")); + return CMD_WARNING_CONFIG_FAILED; + } + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DSCP) && + (pbrms->action_dscp == raw_dscp)) { + return CMD_SUCCESS; + } + SET_FLAG(pbrms->action_bm, PBR_ACTION_DSCP); + pbrms->action_dscp = raw_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 (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 ***********************************************************************/ @@ -1308,10 +1552,22 @@ static void vty_show_pbrms(struct vty *vty, /* 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_QUEUE_ID)) - vty_out(vty, " Set Queue ID: %u\n", - pbrms->action_queue_id); + 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); @@ -1320,6 +1576,10 @@ static void vty_show_pbrms(struct vty *vty, 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: @@ -1467,6 +1727,25 @@ static void vty_json_pbrms(json_object *j, struct vty *vty, * 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)) @@ -1796,6 +2075,19 @@ static int pbr_vty_map_config_write_sequence(struct vty *vty, * 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"); @@ -1906,10 +2198,18 @@ void pbr_vty_init(void) install_element(PBRMAP_NODE, &pbr_map_match_vlan_tag_cmd); install_element(PBRMAP_NODE, &pbr_map_match_pcp_cmd); install_element(PBRMAP_NODE, &pbr_map_match_mark_cmd); + install_element(PBRMAP_NODE, &pbr_map_action_queue_id_cmd); install_element(PBRMAP_NODE, &pbr_map_action_strip_vlan_cmd); install_element(PBRMAP_NODE, &pbr_map_action_vlan_id_cmd); install_element(PBRMAP_NODE, &pbr_map_action_pcp_cmd); + install_element(PBRMAP_NODE, &pbr_map_action_src_cmd); + install_element(PBRMAP_NODE, &pbr_map_action_dst_cmd); + install_element(PBRMAP_NODE, &pbr_map_action_dscp_cmd); + install_element(PBRMAP_NODE, &pbr_map_action_ecn_cmd); + install_element(PBRMAP_NODE, &pbr_map_action_src_port_cmd); + install_element(PBRMAP_NODE, &pbr_map_action_dst_port_cmd); + install_element(PBRMAP_NODE, &pbr_map_nexthop_group_cmd); install_element(PBRMAP_NODE, &no_pbr_map_nexthop_group_cmd); install_element(PBRMAP_NODE, &pbr_map_nexthop_cmd); diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c index 66148c630d..35c771469c 100644 --- a/pbrd/pbr_zebra.c +++ b/pbrd/pbr_zebra.c @@ -594,6 +594,15 @@ static bool pbr_encode_pbr_map_sequence(struct stream *s, r.action.queue_id = pbrms->action_queue_id; + r.action.src_ip = pbrms->action_src; + r.action.dst_ip = pbrms->action_dst; + + r.action.src_port = pbrms->action_src_port; + r.action.dst_port = pbrms->action_dst_port; + + r.action.dscp = pbrms->action_dscp; + r.action.ecn = pbrms->action_ecn; + r.action.pcp = pbrms->action_pcp; r.action.vlan_id = pbrms->action_vlan_id; diff --git a/tests/topotests/pbr_topo1/test_pbr_topo1.py b/tests/topotests/pbr_topo1/test_pbr_topo1.py index ad096fa14e..2b437170ff 100644 --- a/tests/topotests/pbr_topo1/test_pbr_topo1.py +++ b/tests/topotests/pbr_topo1/test_pbr_topo1.py @@ -201,27 +201,38 @@ ftest = [ {"c": "match vlan untagged", "tm": r"VLAN Flags Match: untagged$"}, {"c": "match vlan untagged-or-zero", "tm": r"VLAN Flags Match: untagged-or-zero$"}, {"c": "no match vlan tagged", "tN": r"VLAN Flags Match:"}, - {"c": "match src-ip 37.49.22.0/24", "tm": r"SRC IP Match: 37.49.22.0/24$"}, {"c": "no match src-ip 37.49.22.0/24", "tN": r"SRC IP Match: 37.49.22.0/24$"}, - - {"c": "match dst-ip 38.41.29.0/25", "cDN": "foo", "tm": r"DST IP Match: 38.41.29.0/25$"}, + { + "c": "match dst-ip 38.41.29.0/25", + "cDN": "foo", + "tm": r"DST IP Match: 38.41.29.0/25$", + }, {"c": "no match dst-ip 38.41.29.0/25", "tN": r"DST IP Match: 38.41.29.0/25$"}, - {"c": "match src-port 117", "tm": r"SRC Port Match: 117$"}, {"c": "no match src-port 117", "tN": r"SRC Port Match: 117$"}, - {"c": "match dst-port 119", "tm": r"DST Port Match: 119$"}, {"c": "no match dst-port 119", "tN": r"DST Port Match: 119$"}, - {"c": "match dscp cs3", "tm": r"DSCP Match: 24$"}, {"c": "no match dscp cs3", "tN": r"DSCP Match: 24$"}, - {"c": "match ecn 2", "tm": r"ECN Match: 2$"}, {"c": "no match ecn 2", "tN": r"ECN Match: 2$"}, - {"c": "match mark 337", "tm": r"MARK Match: 337$"}, {"c": "no match mark 337", "tN": r"MARK Match: 337$"}, + {"c": "set src-ip 44.100.1.1", "tm": r"Set SRC IP: 44.100.1.1$"}, + {"c": "no set src-ip 44.100.1.1", "tN": r"Set SRC IP: 44.100.1.1$"}, + {"c": "set dst-ip 44.105.1.1", "tm": r"Set DST IP: 44.105.1.1$"}, + {"c": "no set dst-ip 44.105.1.1", "tN": r"Set DST IP: 44.105.1.1$"}, + {"c": "set src-port 41", "tm": r"Set SRC PORT: 41$"}, + {"c": "no set src-port 41", "tN": r"Set SRC PORT: 41$"}, + {"c": "set dst-port 43", "tm": r"Set DST PORT: 43$"}, + {"c": "no set dst-port 43", "tN": r"Set DST PORT: 43$"}, + {"c": "set dscp 24", "tm": r"Set DSCP: 24$"}, + {"c": "no set dscp 24", "tN": r"Set DSCP: 24$"}, + {"c": "set dscp cs7", "tm": r"Set DSCP: 14$"}, + {"c": "no set dscp cs7", "tN": r"Set DSCP: 14$"}, + {"c": "set ecn 1", "tm": r"Set ECN: 1$"}, + {"c": "no set ecn 1", "tN": r"Set ECN: 1$"}, ] diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c index 7560071e59..5124768a7c 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -507,6 +507,7 @@ void zebra_pbr_show_rule_unit(struct zebra_pbr_rule *rule, struct vty *vty) { struct pbr_rule *prule = &rule->rule; struct zebra_pbr_action *zaction = &rule->action; + struct pbr_action *pa = &prule->action; vty_out(vty, "Rules if %s\n", rule->ifname); vty_out(vty, " Seq %u pri %u\n", prule->seq, prule->priority); @@ -548,6 +549,30 @@ void zebra_pbr_show_rule_unit(struct zebra_pbr_rule *rule, struct vty *vty) vty_out(vty, "\n"); } + if (CHECK_FLAG(pa->flags, PBR_ACTION_ECN)) + vty_out(vty, " Action: Set ECN: %u\n", pa->ecn); + if (CHECK_FLAG(pa->flags, PBR_ACTION_DSCP)) + vty_out(vty, " Action: Set DSCP: %u\n", pa->dscp >> 2); + + if (CHECK_FLAG(pa->flags, PBR_ACTION_SRC_IP)) + vty_out(vty, " Action: Set SRC IP: %pSU\n", &pa->src_ip); + if (CHECK_FLAG(pa->flags, PBR_ACTION_DST_IP)) + vty_out(vty, " Action: Set DST IP: %pSU\n", &pa->dst_ip); + if (CHECK_FLAG(pa->flags, PBR_ACTION_SRC_PORT)) + vty_out(vty, " Action: Set SRC PORT: %u\n", pa->src_port); + if (CHECK_FLAG(pa->flags, PBR_ACTION_DST_PORT)) + vty_out(vty, " Action: Set DST PORT: %u\n", pa->dst_port); + + if (CHECK_FLAG(pa->flags, PBR_ACTION_QUEUE_ID)) + vty_out(vty, " Action: Set Queue ID: %u\n", pa->queue_id); + + if (CHECK_FLAG(pa->flags, PBR_ACTION_PCP)) + vty_out(vty, " Action: Set PCP: %u\n", pa->pcp); + if (CHECK_FLAG(pa->flags, PBR_ACTION_VLAN_ID)) + vty_out(vty, " Action: Set VLAN ID: %u\n", pa->vlan_id); + if (CHECK_FLAG(pa->flags, PBR_ACTION_VLAN_STRIP_INNER_ANY)) + vty_out(vty, " Action: Strip VLAN ID\n"); + vty_out(vty, " Tableid: %u\n", prule->action.table); if (zaction->afi == AFI_IP) vty_out(vty, " Action: nh: %pI4 intf: %s\n",