diff options
Diffstat (limited to 'pbrd/pbr_vty.c')
| -rw-r--r-- | pbrd/pbr_vty.c | 1474 |
1 files changed, 1149 insertions, 325 deletions
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); |
