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