diff options
35 files changed, 2047 insertions, 967 deletions
diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 35f2438929..3442eee1e1 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -3757,8 +3757,11 @@ static int install_uninstall_routes_for_vrf(struct bgp *bgp_vrf, int install) pi = pi->next) { ret = bgp_evpn_route_entry_install_if_vrf_match( bgp_vrf, pi, install); - if (ret) + if (ret) { + bgp_dest_unlock_node(rd_dest); + bgp_dest_unlock_node(dest); return ret; + } } } } diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 8b4f4509ae..2f158ab1be 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -2081,12 +2081,12 @@ DEFUN(evpnrt5_network, int idx_ethtag = 5; int idx_routermac = 13; - return bgp_static_set_safi( - AFI_L2VPN, SAFI_EVPN, vty, argv[idx_ipv4_prefixlen]->arg, - argv[idx_route_distinguisher]->arg, argv[idx_label]->arg, NULL, - BGP_EVPN_IP_PREFIX_ROUTE, argv[idx_esi]->arg, - argv[idx_gwip]->arg, argv[idx_ethtag]->arg, - argv[idx_routermac]->arg); + return bgp_static_set(vty, false, argv[idx_ipv4_prefixlen]->arg, + argv[idx_route_distinguisher]->arg, + argv[idx_label]->arg, AFI_L2VPN, SAFI_EVPN, NULL, + 0, 0, BGP_EVPN_IP_PREFIX_ROUTE, + argv[idx_esi]->arg, argv[idx_gwip]->arg, + argv[idx_ethtag]->arg, argv[idx_routermac]->arg); } /* For testing purpose, static route of EVPN RT-5. */ @@ -2113,11 +2113,12 @@ DEFUN(no_evpnrt5_network, int idx_ethtag = 6; int idx_esi = 10; int idx_gwip = 12; - return bgp_static_unset_safi( - AFI_L2VPN, SAFI_EVPN, vty, argv[idx_ipv4_prefixlen]->arg, - argv[idx_ext_community]->arg, argv[idx_label]->arg, - BGP_EVPN_IP_PREFIX_ROUTE, argv[idx_esi]->arg, - argv[idx_gwip]->arg, argv[idx_ethtag]->arg); + + return bgp_static_set(vty, true, argv[idx_ipv4_prefixlen]->arg, + argv[idx_ext_community]->arg, + argv[idx_label]->arg, AFI_L2VPN, SAFI_EVPN, NULL, + 0, 0, BGP_EVPN_IP_PREFIX_ROUTE, argv[idx_esi]->arg, + argv[idx_gwip]->arg, argv[idx_ethtag]->arg, NULL); } static void evpn_import_rt_delete_auto(struct bgp *bgp, struct bgpevpn *vpn) diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index ad66720512..3ecb72b4ef 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -3011,10 +3011,11 @@ DEFUN (vpnv4_network, int idx_ipv4_prefixlen = 1; int idx_ext_community = 3; int idx_label = 5; - return bgp_static_set_safi( - AFI_IP, SAFI_MPLS_VPN, vty, argv[idx_ipv4_prefixlen]->arg, - argv[idx_ext_community]->arg, argv[idx_label]->arg, NULL, 0, - NULL, NULL, NULL, NULL); + + return bgp_static_set(vty, false, argv[idx_ipv4_prefixlen]->arg, + argv[idx_ext_community]->arg, + argv[idx_label]->arg, AFI_IP, SAFI_MPLS_VPN, NULL, + 0, 0, 0, NULL, NULL, NULL, NULL); } DEFUN (vpnv4_network_route_map, @@ -3033,11 +3034,12 @@ DEFUN (vpnv4_network_route_map, int idx_ipv4_prefixlen = 1; int idx_ext_community = 3; int idx_label = 5; - int idx_word_2 = 7; - return bgp_static_set_safi( - AFI_IP, SAFI_MPLS_VPN, vty, argv[idx_ipv4_prefixlen]->arg, - argv[idx_ext_community]->arg, argv[idx_label]->arg, - argv[idx_word_2]->arg, 0, NULL, NULL, NULL, NULL); + int idx_rmap = 7; + + return bgp_static_set(vty, false, argv[idx_ipv4_prefixlen]->arg, + argv[idx_ext_community]->arg, argv[idx_label]->arg, + AFI_IP, SAFI_MPLS_VPN, argv[idx_rmap]->arg, 0, 0, + 0, NULL, NULL, NULL, NULL); } /* For testing purpose, static route of MPLS-VPN. */ @@ -3056,10 +3058,11 @@ DEFUN (no_vpnv4_network, int idx_ipv4_prefixlen = 2; int idx_ext_community = 4; int idx_label = 6; - return bgp_static_unset_safi(AFI_IP, SAFI_MPLS_VPN, vty, - argv[idx_ipv4_prefixlen]->arg, - argv[idx_ext_community]->arg, - argv[idx_label]->arg, 0, NULL, NULL, NULL); + + return bgp_static_set(vty, true, argv[idx_ipv4_prefixlen]->arg, + argv[idx_ext_community]->arg, + argv[idx_label]->arg, AFI_IP, SAFI_MPLS_VPN, NULL, + 0, 0, 0, NULL, NULL, NULL, NULL); } DEFUN (vpnv6_network, @@ -3078,17 +3081,20 @@ DEFUN (vpnv6_network, int idx_ipv6_prefix = 1; int idx_ext_community = 3; int idx_label = 5; - int idx_word_2 = 7; + int idx_rmap = 7; + if (argc == 8) - return bgp_static_set_safi( - AFI_IP6, SAFI_MPLS_VPN, vty, argv[idx_ipv6_prefix]->arg, - argv[idx_ext_community]->arg, argv[idx_label]->arg, - argv[idx_word_2]->arg, 0, NULL, NULL, NULL, NULL); + return bgp_static_set(vty, false, argv[idx_ipv6_prefix]->arg, + argv[idx_ext_community]->arg, + argv[idx_label]->arg, AFI_IP6, + SAFI_MPLS_VPN, argv[idx_rmap]->arg, 0, 0, + 0, NULL, NULL, NULL, NULL); else - return bgp_static_set_safi( - AFI_IP6, SAFI_MPLS_VPN, vty, argv[idx_ipv6_prefix]->arg, - argv[idx_ext_community]->arg, argv[idx_label]->arg, - NULL, 0, NULL, NULL, NULL, NULL); + return bgp_static_set(vty, false, argv[idx_ipv6_prefix]->arg, + argv[idx_ext_community]->arg, + argv[idx_label]->arg, AFI_IP6, + SAFI_MPLS_VPN, NULL, 0, 0, 0, NULL, NULL, + NULL, NULL); } /* For testing purpose, static route of MPLS-VPN. */ @@ -3107,10 +3113,11 @@ DEFUN (no_vpnv6_network, int idx_ipv6_prefix = 2; int idx_ext_community = 4; int idx_label = 6; - return bgp_static_unset_safi(AFI_IP6, SAFI_MPLS_VPN, vty, - argv[idx_ipv6_prefix]->arg, - argv[idx_ext_community]->arg, - argv[idx_label]->arg, 0, NULL, NULL, NULL); + + return bgp_static_set(vty, true, argv[idx_ipv6_prefix]->arg, + argv[idx_ext_community]->arg, + argv[idx_label]->arg, AFI_IP6, SAFI_MPLS_VPN, + NULL, 0, 0, 0, NULL, NULL, NULL, NULL); } int bgp_show_mpls_vpn(struct vty *vty, afi_t afi, struct prefix_rd *prd, diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index e104aac870..0cb32ff94c 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -6571,16 +6571,23 @@ void bgp_static_withdraw(struct bgp *bgp, const struct prefix *p, afi_t afi, /* Configure static BGP network. When user don't run zebra, static route should be installed as valid. */ -static int bgp_static_set(struct vty *vty, const char *negate, - const char *ip_str, afi_t afi, safi_t safi, - const char *rmap, int backdoor, uint32_t label_index) +int bgp_static_set(struct vty *vty, bool negate, const char *ip_str, + const char *rd_str, const char *label_str, afi_t afi, + safi_t safi, const char *rmap, int backdoor, + uint32_t label_index, int evpn_type, const char *esi, + const char *gwip, const char *ethtag, const char *routermac) { VTY_DECLVAR_CONTEXT(bgp, bgp); int ret; struct prefix p; struct bgp_static *bgp_static; + struct prefix_rd prd; + struct bgp_dest *pdest; struct bgp_dest *dest; + struct bgp_table *table; uint8_t need_update = 0; + mpls_label_t label = MPLS_INVALID_LABEL; + struct prefix gw_ip; /* Convert IP prefix string to struct prefix. */ ret = str2prefix(ip_str, &p); @@ -6595,8 +6602,69 @@ static int bgp_static_set(struct vty *vty, const char *negate, apply_mask(&p); - if (negate) { + if (afi == AFI_L2VPN && + (bgp_build_evpn_prefix(evpn_type, ethtag != NULL ? atol(ethtag) : 0, + &p))) { + vty_out(vty, "%% L2VPN prefix could not be forged\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (safi == SAFI_MPLS_VPN || safi == SAFI_EVPN) { + ret = str2prefix_rd(rd_str, &prd); + if (!ret) { + vty_out(vty, "%% Malformed rd\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (label_str) { + unsigned long label_val; + + label_val = strtoul(label_str, NULL, 10); + encode_label(label_val, &label); + } + } + + if (safi == SAFI_EVPN) { + if (esi && str2esi(esi, NULL) == 0) { + vty_out(vty, "%% Malformed ESI\n"); + return CMD_WARNING_CONFIG_FAILED; + } + if (routermac && prefix_str2mac(routermac, NULL) == 0) { + vty_out(vty, "%% Malformed Router MAC\n"); + return CMD_WARNING_CONFIG_FAILED; + } + if (gwip) { + memset(&gw_ip, 0, sizeof(gw_ip)); + ret = str2prefix(gwip, &gw_ip); + if (!ret) { + vty_out(vty, "%% Malformed GatewayIp\n"); + return CMD_WARNING_CONFIG_FAILED; + } + if ((gw_ip.family == AF_INET && + is_evpn_prefix_ipaddr_v6((struct prefix_evpn *)&p)) || + (gw_ip.family == AF_INET6 && + is_evpn_prefix_ipaddr_v4( + (struct prefix_evpn *)&p))) { + vty_out(vty, + "%% GatewayIp family differs with IP prefix\n"); + return CMD_WARNING_CONFIG_FAILED; + } + } + } + if (safi == SAFI_MPLS_VPN || safi == SAFI_EVPN) { + pdest = bgp_node_get(bgp->route[afi][safi], + (struct prefix *)&prd); + if (!bgp_dest_has_bgp_path_info_data(pdest)) + bgp_dest_set_bgp_table_info(pdest, + bgp_table_init(bgp, afi, + safi)); + table = bgp_dest_get_bgp_table_info(pdest); + } else { + table = bgp->route[afi][safi]; + } + + if (negate) { /* Set BGP static route configuration. */ dest = bgp_node_lookup(bgp->route[afi][safi], &p); @@ -6606,36 +6674,37 @@ static int bgp_static_set(struct vty *vty, const char *negate, } bgp_static = bgp_dest_get_bgp_static_info(dest); + if (bgp_static) { + if ((label_index != BGP_INVALID_LABEL_INDEX) && + (label_index != bgp_static->label_index)) { + vty_out(vty, + "%% label-index doesn't match static route\n"); + bgp_dest_unlock_node(dest); + return CMD_WARNING_CONFIG_FAILED; + } - if ((label_index != BGP_INVALID_LABEL_INDEX) - && (label_index != bgp_static->label_index)) { - vty_out(vty, - "%% label-index doesn't match static route\n"); - bgp_dest_unlock_node(dest); - return CMD_WARNING_CONFIG_FAILED; - } + if ((rmap && bgp_static->rmap.name) && + strcmp(rmap, bgp_static->rmap.name)) { + vty_out(vty, + "%% route-map name doesn't match static route\n"); + bgp_dest_unlock_node(dest); + return CMD_WARNING_CONFIG_FAILED; + } - if ((rmap && bgp_static->rmap.name) - && strcmp(rmap, bgp_static->rmap.name)) { - vty_out(vty, - "%% route-map name doesn't match static route\n"); - bgp_dest_unlock_node(dest); - return CMD_WARNING_CONFIG_FAILED; - } + /* Update BGP RIB. */ + if (!bgp_static->backdoor) + bgp_static_withdraw(bgp, &p, afi, safi, NULL); - /* Update BGP RIB. */ - if (!bgp_static->backdoor) - bgp_static_withdraw(bgp, &p, afi, safi, NULL); + /* Clear configuration. */ + bgp_static_free(bgp_static); + } - /* Clear configuration. */ - bgp_static_free(bgp_static); bgp_dest_set_bgp_static_info(dest, NULL); bgp_dest_unlock_node(dest); bgp_dest_unlock_node(dest); } else { + dest = bgp_node_get(table, &p); - /* Set BGP static route configuration. */ - dest = bgp_node_get(bgp->route[afi][safi], &p); bgp_static = bgp_dest_get_bgp_static_info(dest); if (bgp_static) { /* Configuration change. */ @@ -6681,6 +6750,8 @@ static int bgp_static_set(struct vty *vty, const char *negate, bgp_static->igpmetric = 0; bgp_static->igpnexthop.s_addr = INADDR_ANY; bgp_static->label_index = label_index; + bgp_static->label = label; + bgp_static->prd = prd; if (rmap) { XFREE(MTYPE_ROUTE_MAP_NAME, @@ -6694,6 +6765,26 @@ static int bgp_static_set(struct vty *vty, const char *negate, route_map_counter_increment( bgp_static->rmap.map); } + + if (safi == SAFI_EVPN) { + if (esi) { + bgp_static->eth_s_id = + XCALLOC(MTYPE_ATTR, + sizeof(esi_t)); + str2esi(esi, bgp_static->eth_s_id); + } + if (routermac) { + bgp_static->router_mac = + XCALLOC(MTYPE_ATTR, + ETH_ALEN + 1); + (void)prefix_str2mac(routermac, + bgp_static->router_mac); + } + if (gwip) + prefix_copy(&bgp_static->gatewayIp, + &gw_ip); + } + bgp_dest_set_bgp_static_info(dest, bgp_static); } @@ -6888,205 +6979,6 @@ void bgp_purge_static_redist_routes(struct bgp *bgp) bgp_purge_af_static_redist_routes(bgp, afi, safi); } -/* - * gpz 110624 - * Currently this is used to set static routes for VPN and ENCAP. - * I think it can probably be factored with bgp_static_set. - */ -int bgp_static_set_safi(afi_t afi, safi_t safi, struct vty *vty, - const char *ip_str, const char *rd_str, - const char *label_str, const char *rmap_str, - int evpn_type, const char *esi, const char *gwip, - const char *ethtag, const char *routermac) -{ - VTY_DECLVAR_CONTEXT(bgp, bgp); - int ret; - struct prefix p; - struct prefix_rd prd; - struct bgp_dest *pdest; - struct bgp_dest *dest; - struct bgp_table *table; - struct bgp_static *bgp_static; - mpls_label_t label = MPLS_INVALID_LABEL; - struct prefix gw_ip; - - /* validate ip prefix */ - ret = str2prefix(ip_str, &p); - if (!ret) { - vty_out(vty, "%% Malformed prefix\n"); - return CMD_WARNING_CONFIG_FAILED; - } - apply_mask(&p); - if ((afi == AFI_L2VPN) - && (bgp_build_evpn_prefix(evpn_type, - ethtag != NULL ? atol(ethtag) : 0, &p))) { - vty_out(vty, "%% L2VPN prefix could not be forged\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - ret = str2prefix_rd(rd_str, &prd); - if (!ret) { - vty_out(vty, "%% Malformed rd\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - if (label_str) { - unsigned long label_val; - label_val = strtoul(label_str, NULL, 10); - encode_label(label_val, &label); - } - - if (safi == SAFI_EVPN) { - if (esi && str2esi(esi, NULL) == 0) { - vty_out(vty, "%% Malformed ESI\n"); - return CMD_WARNING_CONFIG_FAILED; - } - if (routermac && prefix_str2mac(routermac, NULL) == 0) { - vty_out(vty, "%% Malformed Router MAC\n"); - return CMD_WARNING_CONFIG_FAILED; - } - if (gwip) { - memset(&gw_ip, 0, sizeof(gw_ip)); - ret = str2prefix(gwip, &gw_ip); - if (!ret) { - vty_out(vty, "%% Malformed GatewayIp\n"); - return CMD_WARNING_CONFIG_FAILED; - } - if ((gw_ip.family == AF_INET - && is_evpn_prefix_ipaddr_v6( - (struct prefix_evpn *)&p)) - || (gw_ip.family == AF_INET6 - && is_evpn_prefix_ipaddr_v4( - (struct prefix_evpn *)&p))) { - vty_out(vty, - "%% GatewayIp family differs with IP prefix\n"); - return CMD_WARNING_CONFIG_FAILED; - } - } - } - pdest = bgp_node_get(bgp->route[afi][safi], (struct prefix *)&prd); - if (!bgp_dest_has_bgp_path_info_data(pdest)) - bgp_dest_set_bgp_table_info(pdest, - bgp_table_init(bgp, afi, safi)); - table = bgp_dest_get_bgp_table_info(pdest); - - dest = bgp_node_get(table, &p); - - if (bgp_dest_has_bgp_path_info_data(dest)) { - vty_out(vty, "%% Same network configuration exists\n"); - bgp_dest_unlock_node(dest); - } else { - /* New configuration. */ - bgp_static = bgp_static_new(); - bgp_static->backdoor = 0; - bgp_static->valid = 0; - bgp_static->igpmetric = 0; - bgp_static->igpnexthop.s_addr = INADDR_ANY; - bgp_static->label = label; - bgp_static->prd = prd; - - bgp_static->prd_pretty = XSTRDUP(MTYPE_BGP, rd_str); - - if (rmap_str) { - XFREE(MTYPE_ROUTE_MAP_NAME, bgp_static->rmap.name); - route_map_counter_decrement(bgp_static->rmap.map); - bgp_static->rmap.name = - XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap_str); - bgp_static->rmap.map = - route_map_lookup_by_name(rmap_str); - route_map_counter_increment(bgp_static->rmap.map); - } - - if (safi == SAFI_EVPN) { - if (esi) { - bgp_static->eth_s_id = - XCALLOC(MTYPE_ATTR, - sizeof(esi_t)); - str2esi(esi, bgp_static->eth_s_id); - } - if (routermac) { - bgp_static->router_mac = - XCALLOC(MTYPE_ATTR, ETH_ALEN + 1); - (void)prefix_str2mac(routermac, - bgp_static->router_mac); - } - if (gwip) - prefix_copy(&bgp_static->gatewayIp, &gw_ip); - } - bgp_dest_set_bgp_static_info(dest, bgp_static); - - bgp_static->valid = 1; - bgp_static_update(bgp, &p, bgp_static, afi, safi); - } - - return CMD_SUCCESS; -} - -/* Configure static BGP network. */ -int bgp_static_unset_safi(afi_t afi, safi_t safi, struct vty *vty, - const char *ip_str, const char *rd_str, - const char *label_str, int evpn_type, const char *esi, - const char *gwip, const char *ethtag) -{ - VTY_DECLVAR_CONTEXT(bgp, bgp); - int ret; - struct prefix p; - struct prefix_rd prd; - struct bgp_dest *pdest; - struct bgp_dest *dest; - struct bgp_table *table; - struct bgp_static *bgp_static; - mpls_label_t label = MPLS_INVALID_LABEL; - - /* Convert IP prefix string to struct prefix. */ - ret = str2prefix(ip_str, &p); - if (!ret) { - vty_out(vty, "%% Malformed prefix\n"); - return CMD_WARNING_CONFIG_FAILED; - } - apply_mask(&p); - if ((afi == AFI_L2VPN) - && (bgp_build_evpn_prefix(evpn_type, - ethtag != NULL ? atol(ethtag) : 0, &p))) { - vty_out(vty, "%% L2VPN prefix could not be forged\n"); - return CMD_WARNING_CONFIG_FAILED; - } - ret = str2prefix_rd(rd_str, &prd); - if (!ret) { - vty_out(vty, "%% Malformed rd\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - if (label_str) { - unsigned long label_val; - label_val = strtoul(label_str, NULL, 10); - encode_label(label_val, &label); - } - - pdest = bgp_node_get(bgp->route[afi][safi], (struct prefix *)&prd); - if (!bgp_dest_has_bgp_path_info_data(pdest)) - bgp_dest_set_bgp_table_info(pdest, - bgp_table_init(bgp, afi, safi)); - else - bgp_dest_unlock_node(pdest); - table = bgp_dest_get_bgp_table_info(pdest); - - dest = bgp_node_lookup(table, &p); - - if (dest) { - bgp_static_withdraw(bgp, &p, afi, safi, &prd); - - bgp_static = bgp_dest_get_bgp_static_info(dest); - bgp_static_free(bgp_static); - bgp_dest_set_bgp_static_info(dest, NULL); - bgp_dest_unlock_node(dest); - bgp_dest_unlock_node(dest); - } else - vty_out(vty, "%% Can't find the route\n"); - - return CMD_SUCCESS; -} - static int bgp_table_map_set(struct vty *vty, afi_t afi, safi_t safi, const char *rmap_name) { @@ -7192,10 +7084,13 @@ DEFPY(bgp_network, } } - return bgp_static_set( - vty, no, address_str ? addr_prefix_str : prefix_str, AFI_IP, - bgp_node_safi(vty), map_name, backdoor ? 1 : 0, - label_index ? (uint32_t)label_index : BGP_INVALID_LABEL_INDEX); + return bgp_static_set(vty, no, + address_str ? addr_prefix_str : prefix_str, NULL, + NULL, AFI_IP, bgp_node_safi(vty), map_name, + backdoor ? 1 : 0, + label_index ? (uint32_t)label_index + : BGP_INVALID_LABEL_INDEX, + 0, NULL, NULL, NULL, NULL); } DEFPY(ipv6_bgp_network, @@ -7210,9 +7105,11 @@ DEFPY(ipv6_bgp_network, "Label index to associate with the prefix\n" "Label index value\n") { - return bgp_static_set( - vty, no, prefix_str, AFI_IP6, bgp_node_safi(vty), map_name, 0, - label_index ? (uint32_t)label_index : BGP_INVALID_LABEL_INDEX); + return bgp_static_set(vty, no, prefix_str, NULL, NULL, AFI_IP6, + bgp_node_safi(vty), map_name, 0, + label_index ? (uint32_t)label_index + : BGP_INVALID_LABEL_INDEX, + 0, NULL, NULL, NULL, NULL); } static struct bgp_aggregate *bgp_aggregate_new(void) diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 0a6f535b22..f424e6d286 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -762,14 +762,12 @@ extern void bgp_static_update(struct bgp *bgp, const struct prefix *p, extern void bgp_static_withdraw(struct bgp *bgp, const struct prefix *p, afi_t afi, safi_t safi, struct prefix_rd *prd); -extern int bgp_static_set_safi(afi_t afi, safi_t safi, struct vty *vty, - const char *, const char *, const char *, - const char *, int, const char *, const char *, - const char *, const char *); - -extern int bgp_static_unset_safi(afi_t afi, safi_t safi, struct vty *, - const char *, const char *, const char *, int, - const char *, const char *, const char *); +extern int bgp_static_set(struct vty *vty, bool negate, const char *ip_str, + const char *rd_str, const char *label_str, afi_t afi, + safi_t safi, const char *rmap, int backdoor, + uint32_t label_index, int evpn_type, const char *esi, + const char *gwip, const char *ethtag, + const char *routermac); /* this is primarily for MPLS-VPN */ extern void bgp_update(struct peer *peer, const struct prefix *p, diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index ea7168f749..21ccbebe67 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -2760,6 +2760,7 @@ static void bgp_encode_pbr_rule_action(struct stream *s, else r.unique = pbra->unique; + r.family = fam; /* filter */ diff --git a/doc/developer/workflow.rst b/doc/developer/workflow.rst index 6efe5f42f6..5199d42db3 100644 --- a/doc/developer/workflow.rst +++ b/doc/developer/workflow.rst @@ -761,6 +761,13 @@ following requirements have achieved consensus: tools can catch uninitialized value use that would otherwise be suppressed by the (incorrect) zero initialization. +- Usage of ``system()`` or other c library routines that cause signals to + possibly be ignored are not allowed. This includes the ``fork()`` and + ``execXX`` call patterns, which is actually what system() does underneath + the covers. This pattern causes the system shutdown to never work properly + as the SIGINT sent is never received. It is better to just prohibit code + that does this instead of having to debug shutdown issues again. + Other than these specific rules, coding practices from the Linux kernel as well as CERT or MISRA C guidelines may provide useful input on safe C code. However, these rules are not applied as-is; some of them expressly collide diff --git a/doc/user/pbr.rst b/doc/user/pbr.rst index 17f3504898..d0513018e3 100644 --- a/doc/user/pbr.rst +++ b/doc/user/pbr.rst @@ -161,6 +161,26 @@ specified in the rule are also applied to the packet. VLAN-matching facilities, so this field will be ignored unless other dataplane providers are used. +.. clicmd:: set nexthop-group NAME + + Action: + forward the packet using nexthop-group NAME. + +.. clicmd:: set nexthop [A.B.C.D|X:X::X:XX|blackhole] [interface] [nexthop-vrf NAME] + + Action: + forward the packet using the specified single nexthop. + If `blackhole`, packets will be sent to a blackhole route and dropped. + +.. clicmd:: set vrf unchanged|NAME + + Action: + If set to ``unchanged``, the rule will use the vrf table the interface + is in as its lookup. + If set to NAME, the rule will use that vrf table as its lookup. + + Not supported with NETNS VRF backend. + .. clicmd:: set queue-id (1-65535) Action: @@ -195,24 +215,57 @@ specified in the rule are also applied to the packet. so this field will be ignored unless another dataplane provider is used. It is invalid to specify both a `strip` and `set vlan` action. -.. clicmd:: set nexthop-group NAME +.. clicmd:: set src-ip [A.B.C.D/M|X:X::X:X/M] Action: - forward the packet using nexthop-group NAME. + Set the source IP address of matched packets, possibly using a mask `M`. + The Linux Kernel dataplane provider does not currently support + packet mangling, + so this field will be ignored unless another dataplane provider is used. -.. clicmd:: set nexthop [A.B.C.D|X:X::X:XX] [interface] [nexthop-vrf NAME] +.. clicmd:: set dst-ip [A.B.C.D/M|X:X::X:X/M] Action: - forward the packet using the specified single nexthop. + set the destination IP address of matched packets, possibly using a mask + `M`. + The Linux Kernel dataplane provider does not currently support + packet mangling, + so this field will be ignored unless another dataplane provider is used. -.. clicmd:: set vrf unchanged|NAME +.. clicmd:: set src-port (1-65535) Action: - If set to ``unchanged``, the rule will use the vrf table the interface - is in as its lookup. - If set to NAME, the rule will use that vrf table as its lookup. + set the source port of matched packets. Note that this action only makes + sense with layer 4 protocols that use ports, such as TCP, UDP, and SCTP. + The Linux Kernel dataplane provider does not currently support + packet mangling, + so this field will be ignored unless another dataplane provider is used. - Not supported with NETNS VRF backend. +.. clicmd:: set dst-port (1-65535) + + Action: + set the destination port of matched packets. Note that this action only + makes sense with layer 4 protocols that use ports, such as TCP, UDP, and + SCTP. + The Linux Kernel dataplane provider does not currently support + packet mangling, + so this field will be ignored unless another dataplane provider is used. + +.. clicmd:: set dscp DSCP + + Action: + set the differentiated services code point (DSCP) of matched packets. + The Linux Kernel dataplane provider does not currently support + this action, + so this field will be ignored unless another dataplane provider is used. + +.. clicmd:: set ecn (0-3) + + Action: + set the explicit congestion notification (ECN) of matched packets. + The Linux Kernel dataplane provider does not currently support + this action, + so this field will be ignored unless another dataplane provider is used. .. clicmd:: show pbr map [NAME] [detail|json] diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst index 32de3e908f..39add2235f 100644 --- a/doc/user/zebra.rst +++ b/doc/user/zebra.rst @@ -163,12 +163,13 @@ Standard Commands Set description for the interface. -.. clicmd:: mpls enable +.. clicmd:: mpls <enable|disable> - Enable or disable mpls kernel processing on the interface, for linux. Interfaces + Choose mpls kernel processing value on the interface, for linux. Interfaces configured with mpls will not automatically turn on if mpls kernel modules do not - happen to be loaded. This command will fail on 3.X linux kernels and does not - work on non-linux systems at all. + happen to be loaded. This command will fail on 3.X linux kernels and does not + work on non-linux systems at all. 'enable' and 'disable' will respectively turn + on and off mpls on the given interface. .. clicmd:: multicast diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c index 0613fc6736..25370eba48 100644 --- a/lib/nexthop_group.c +++ b/lib/nexthop_group.c @@ -1047,6 +1047,7 @@ void nexthop_group_write_nexthop_simple(struct vty *vty, vty_out(vty, "%pI6 %s", &nh->gate.ipv6, ifname); break; case NEXTHOP_TYPE_BLACKHOLE: + vty_out(vty, "%s", "drop"); break; } } @@ -34,17 +34,18 @@ struct pbr_filter { #define PBR_FILTER_SRC_PORT (1 << 2) #define PBR_FILTER_DST_PORT (1 << 3) #define PBR_FILTER_FWMARK (1 << 4) -#define PBR_FILTER_PROTO (1 << 5) +#define PBR_FILTER_IP_PROTOCOL (1 << 5) #define PBR_FILTER_SRC_PORT_RANGE (1 << 6) #define PBR_FILTER_DST_PORT_RANGE (1 << 7) -#define PBR_FILTER_DSFIELD (1 << 8) -#define PBR_FILTER_IP_PROTOCOL (1 << 9) +#define PBR_FILTER_DSCP (1 << 8) +#define PBR_FILTER_ECN (1 << 9) #define PBR_FILTER_PCP (1 << 10) #define PBR_FILTER_VLAN_FLAGS (1 << 11) #define PBR_FILTER_VLAN_ID (1 << 12) #define PBR_DSFIELD_DSCP (0xfc) /* Upper 6 bits of DS field: DSCP */ #define PBR_DSFIELD_ECN (0x03) /* Lower 2 bits of DS field: BCN */ + #define PBR_PCP (0x07) /* 3-bit value 0..7 for prioritization*/ #define PBR_VLAN_FLAGS_NO_WILD 0 @@ -56,7 +57,7 @@ struct pbr_filter { struct prefix src_ip; struct prefix dst_ip; - /* Source and Destination higher-layer (TCP/UDP) port numbers */ + /* Source and Destination layer 4 (TCP/UDP/etc.) port numbers */ uint16_t src_port; uint16_t dst_port; @@ -87,11 +88,17 @@ struct pbr_filter { struct pbr_action { uint32_t flags; -#define PBR_ACTION_TABLE (1 << 0) -#define PBR_ACTION_QUEUE_ID (1 << 1) -#define PBR_ACTION_PCP (1 << 2) -#define PBR_ACTION_VLAN_ID (1 << 3) -#define PBR_ACTION_VLAN_FLAGS (1 << 4) +#define PBR_ACTION_TABLE (1 << 0) +#define PBR_ACTION_QUEUE_ID (1 << 1) +#define PBR_ACTION_PCP (1 << 2) +#define PBR_ACTION_VLAN_ID (1 << 3) +#define PBR_ACTION_VLAN_STRIP_INNER_ANY (1 << 4) +#define PBR_ACTION_SRC_IP (1 << 5) +#define PBR_ACTION_DST_IP (1 << 6) +#define PBR_ACTION_SRC_PORT (1 << 7) +#define PBR_ACTION_DST_PORT (1 << 8) +#define PBR_ACTION_DSCP (1 << 9) +#define PBR_ACTION_ECN (1 << 10) uint32_t table; uint32_t queue_id; @@ -99,9 +106,18 @@ struct pbr_action { /* VLAN */ uint8_t pcp; uint16_t vlan_id; - uint16_t vlan_flags; + /* Source and Destination IP addresses */ + union sockunion src_ip; + union sockunion dst_ip; + + /* Source and Destination layer 4 (TCP/UDP/etc.) port numbers */ + uint32_t src_port; + uint32_t dst_port; + /* Differentiated Services field */ + uint8_t dscp; /* stored here already shifted to upper 6 bits */ + uint8_t ecn; /* stored here as lower 2 bits */ }; /* @@ -113,6 +129,7 @@ struct pbr_action { */ struct pbr_rule { vrf_id_t vrf_id; + uint8_t family; /* netlink: select which rule database */ uint32_t seq; uint32_t priority; diff --git a/lib/zclient.c b/lib/zclient.c index 294a78feb0..e40725826a 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -1622,6 +1622,47 @@ stream_failure: return false; } +static void zapi_encode_sockunion(struct stream *s, const union sockunion *su) +{ + int family = sockunion_family(su); + size_t addrlen = family2addrsize(family); + + /* + * Must know length to encode + */ + assert(addrlen); + + stream_putc(s, (uint8_t)family); + + stream_write(s, sockunion_get_addr(su), addrlen); +} + +static bool zapi_decode_sockunion(struct stream *s, union sockunion *su) +{ + uint8_t family; + size_t addrlen; + uint8_t buf[sizeof(union sockunion)]; + + memset(su, 0, sizeof(*su)); + + STREAM_GETC(s, family); + sockunion_family(su) = family; + + addrlen = family2addrsize(family); + if (!addrlen) + return false; + + if (addrlen > sizeof(buf)) + return false; + + STREAM_GET(buf, s, addrlen); + sockunion_set(su, family, buf, addrlen); + return true; + +stream_failure: + return false; +} + /* * Encode filter subsection of pbr_rule */ @@ -1631,40 +1672,79 @@ static void zapi_pbr_rule_filter_encode(struct stream *s, struct pbr_filter *f) assert((f->src_ip.family == AF_INET) || (f->src_ip.family == AF_INET6)); stream_putl(s, f->filter_bm); - stream_putc(s, f->ip_proto); + + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_IP_PROTOCOL)) + stream_putc(s, f->ip_proto); /* addresses */ - zapi_encode_prefix(s, &f->src_ip, f->src_ip.family); - zapi_encode_prefix(s, &f->dst_ip, f->dst_ip.family); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_SRC_IP)) + zapi_encode_prefix(s, &f->src_ip, f->src_ip.family); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_DST_IP)) + zapi_encode_prefix(s, &f->dst_ip, f->dst_ip.family); /* port numbers */ - stream_putw(s, f->src_port); - stream_putw(s, f->dst_port); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_SRC_PORT)) + stream_putw(s, f->src_port); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_DST_PORT)) + stream_putw(s, f->dst_port); + + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_DSCP)) + stream_putc(s, f->dsfield & PBR_DSFIELD_DSCP); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_ECN)) + stream_putc(s, f->dsfield & PBR_DSFIELD_ECN); /* vlan */ - stream_putc(s, f->pcp); - stream_putw(s, f->vlan_id); - stream_putw(s, f->vlan_flags); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_PCP)) + stream_putc(s, f->pcp); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_VLAN_ID)) + stream_putw(s, f->vlan_id); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_VLAN_FLAGS)) + stream_putw(s, f->vlan_flags); + - stream_putc(s, f->dsfield); - stream_putl(s, f->fwmark); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_FWMARK)) + stream_putl(s, f->fwmark); } static bool zapi_pbr_rule_filter_decode(struct stream *s, struct pbr_filter *f) { + uint8_t dscp = 0; + uint8_t ecn = 0; + STREAM_GETL(s, f->filter_bm); - STREAM_GETC(s, f->ip_proto); - if (!zapi_decode_prefix(s, &(f->src_ip))) - goto stream_failure; - if (!zapi_decode_prefix(s, &(f->dst_ip))) - goto stream_failure; - STREAM_GETW(s, f->src_port); - STREAM_GETW(s, f->dst_port); - STREAM_GETC(s, f->pcp); - STREAM_GETW(s, f->vlan_id); - STREAM_GETW(s, f->vlan_flags); - STREAM_GETC(s, f->dsfield); - STREAM_GETL(s, f->fwmark); + + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_IP_PROTOCOL)) + STREAM_GETC(s, f->ip_proto); + + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_SRC_IP)) + if (!zapi_decode_prefix(s, &(f->src_ip))) + goto stream_failure; + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_DST_IP)) + if (!zapi_decode_prefix(s, &(f->dst_ip))) + goto stream_failure; + + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_SRC_PORT)) + STREAM_GETW(s, f->src_port); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_DST_PORT)) + STREAM_GETW(s, f->dst_port); + + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_DSCP)) + STREAM_GETC(s, dscp); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_ECN)) + STREAM_GETC(s, ecn); + f->dsfield = (dscp & PBR_DSFIELD_DSCP) | (ecn & PBR_DSFIELD_ECN); + + /* vlan */ + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_PCP)) + STREAM_GETC(s, f->pcp); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_VLAN_ID)) + STREAM_GETW(s, f->vlan_id); + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_VLAN_FLAGS)) + STREAM_GETW(s, f->vlan_flags); + + if (CHECK_FLAG(f->filter_bm, PBR_FILTER_FWMARK)) + STREAM_GETL(s, f->fwmark); + return true; stream_failure: @@ -1674,21 +1754,72 @@ stream_failure: static void zapi_pbr_rule_action_encode(struct stream *s, struct pbr_action *a) { stream_putl(s, a->flags); - stream_putl(s, a->table); - stream_putl(s, a->queue_id); - stream_putc(s, a->pcp); - stream_putw(s, a->vlan_id); - stream_putw(s, a->vlan_flags); + + if (CHECK_FLAG(a->flags, PBR_ACTION_TABLE)) + stream_putl(s, a->table); + if (CHECK_FLAG(a->flags, PBR_ACTION_QUEUE_ID)) + stream_putl(s, a->queue_id); + + /* L3 */ + if (CHECK_FLAG(a->flags, PBR_ACTION_SRC_IP)) + zapi_encode_sockunion(s, &a->src_ip); + if (CHECK_FLAG(a->flags, PBR_ACTION_DST_IP)) + zapi_encode_sockunion(s, &a->dst_ip); + if (CHECK_FLAG(a->flags, PBR_ACTION_SRC_PORT)) + stream_putw(s, a->src_port); + if (CHECK_FLAG(a->flags, PBR_ACTION_DST_PORT)) + stream_putw(s, a->dst_port); + + if (CHECK_FLAG(a->flags, PBR_ACTION_DSCP)) + stream_putc(s, a->dscp & PBR_DSFIELD_DSCP); + if (CHECK_FLAG(a->flags, PBR_ACTION_ECN)) + stream_putc(s, a->ecn & PBR_DSFIELD_ECN); + + /* L2 */ + if (CHECK_FLAG(a->flags, PBR_ACTION_PCP)) + stream_putc(s, a->pcp); + if (CHECK_FLAG(a->flags, PBR_ACTION_VLAN_ID)) + stream_putw(s, a->vlan_id); } static bool zapi_pbr_rule_action_decode(struct stream *s, struct pbr_action *a) { STREAM_GETL(s, a->flags); - STREAM_GETL(s, a->table); - STREAM_GETL(s, a->queue_id); - STREAM_GETC(s, a->pcp); - STREAM_GETW(s, a->vlan_id); - STREAM_GETW(s, a->vlan_flags); + + if (CHECK_FLAG(a->flags, PBR_ACTION_TABLE)) + STREAM_GETL(s, a->table); + if (CHECK_FLAG(a->flags, PBR_ACTION_QUEUE_ID)) + STREAM_GETL(s, a->queue_id); + + /* L3 */ + if (CHECK_FLAG(a->flags, PBR_ACTION_SRC_IP)) { + if (!zapi_decode_sockunion(s, &(a->src_ip))) + goto stream_failure; + } + if (CHECK_FLAG(a->flags, PBR_ACTION_DST_IP)) + if (!zapi_decode_sockunion(s, &(a->dst_ip))) + goto stream_failure; + + if (CHECK_FLAG(a->flags, PBR_ACTION_SRC_PORT)) + STREAM_GETW(s, a->src_port); + if (CHECK_FLAG(a->flags, PBR_ACTION_DST_PORT)) + STREAM_GETW(s, a->dst_port); + + if (CHECK_FLAG(a->flags, PBR_ACTION_DSCP)) { + STREAM_GETC(s, a->dscp); + a->dscp &= PBR_DSFIELD_DSCP; + } + if (CHECK_FLAG(a->flags, PBR_ACTION_ECN)) { + STREAM_GETC(s, a->ecn); + a->ecn &= PBR_DSFIELD_ECN; + } + + /* L2 */ + if (CHECK_FLAG(a->flags, PBR_ACTION_PCP)) + STREAM_GETC(s, a->pcp); + if (CHECK_FLAG(a->flags, PBR_ACTION_VLAN_ID)) + STREAM_GETW(s, a->vlan_id); + return true; stream_failure: @@ -1702,6 +1833,7 @@ int zapi_pbr_rule_encode(struct stream *s, struct pbr_rule *r) */ stream_putl(s, 1); + stream_putc(s, r->family); stream_putl(s, r->seq); stream_putl(s, r->priority); stream_putl(s, r->unique); @@ -1723,6 +1855,7 @@ bool zapi_pbr_rule_decode(struct stream *s, struct pbr_rule *r) memset(r, 0, sizeof(*r)); + STREAM_GETC(s, r->family); STREAM_GETL(s, r->seq); STREAM_GETL(s, r->priority); STREAM_GETL(s, r->unique); diff --git a/pbrd/pbr_map.c b/pbrd/pbr_map.c index 758e08b565..6b81e1058e 100644 --- a/pbrd/pbr_map.c +++ b/pbrd/pbr_map.c @@ -105,38 +105,6 @@ static bool pbrms_is_installed(const struct pbr_map_sequence *pbrms, return false; } -void pbr_set_match_clause_for_pcp(struct pbr_map_sequence *pbrms, bool set, - uint8_t pcp) -{ - bool changed = false; - - if (set) { - if (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_PCP) || - (pcp != pbrms->match_pcp)) { - SET_FLAG(pbrms->filter_bm, PBR_FILTER_PCP); - pbrms->match_pcp = pcp; - changed = true; - } - } else { - if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_PCP)) { - UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_PCP); - changed = true; - } - } - if (changed) - pbr_map_check(pbrms, true); -} - -void pbr_set_match_clause_for_vlan(struct pbr_map_sequence *pbrms, - uint16_t vlan_id, uint16_t vlan_flags) -{ - if (pbrms) { - pbrms->match_vlan_id = vlan_id; - pbrms->match_vlan_flags = vlan_flags; - pbr_map_check(pbrms, true); - } -} - /* If any sequence is installed on the interface, assume installed */ static bool pbr_map_interface_is_installed(const struct pbr_map *pbrm, @@ -562,14 +530,6 @@ struct pbr_map_sequence *pbrms_get(const char *name, uint32_t seqno) pbrms->ruleno = pbr_nht_get_next_rule(seqno); pbrms->parent = pbrm; - pbrms->match_vlan_id = 0; - pbrms->match_vlan_flags = 0; - pbrms->match_pcp = 0; - - pbrms->action_vlan_id = 0; - pbrms->action_vlan_flags = 0; - pbrms->action_pcp = 0; - pbrms->action_queue_id = PBR_MAP_UNDEFINED_QUEUE_ID; pbrms->reason = @@ -634,13 +594,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 && - !CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_PCP) && - !pbrms->action_pcp && !pbrms->match_vlan_id && - !pbrms->match_vlan_flags && !pbrms->action_vlan_id && - !pbrms->action_vlan_flags && - pbrms->action_queue_id == PBR_MAP_UNDEFINED_QUEUE_ID) + /* clang-format off */ + if ( + !CHECK_FLAG(pbrms->filter_bm, ( + PBR_FILTER_SRC_IP | + PBR_FILTER_DST_IP | + PBR_FILTER_SRC_PORT | + PBR_FILTER_DST_PORT | + + PBR_FILTER_IP_PROTOCOL | + PBR_FILTER_DSCP | + PBR_FILTER_ECN | + + PBR_FILTER_FWMARK | + PBR_FILTER_PCP | + PBR_FILTER_VLAN_ID | + PBR_FILTER_VLAN_FLAGS + )) && + !CHECK_FLAG(pbrms->action_bm, ( + PBR_ACTION_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) @@ -653,7 +642,8 @@ static void pbr_map_sequence_check_vlan_actions(struct pbr_map_sequence *pbrms) * The strip vlan action removes any inner tag, so it is invalid to * specify both a set and strip action. */ - if ((pbrms->action_vlan_id != 0) && (pbrms->action_vlan_flags != 0)) + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_ID) && + (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_VLAN_STRIP_INNER_ANY))) pbrms->reason |= PBR_MAP_INVALID_SET_STRIP_VLAN; } diff --git a/pbrd/pbr_map.h b/pbrd/pbr_map.h index 3afb199565..61577e4076 100644 --- a/pbrd/pbr_map.h +++ b/pbrd/pbr_map.h @@ -3,7 +3,9 @@ * PBR-map Header * Copyright (C) 2018 Cumulus Networks, Inc. * Donald Sharp - * Copyright (c) 2023 LabN Consulting, L.L.C. + * Portions: + * Copyright (c) 2023 LabN Consulting, L.L.C. + * Copyright (c) 2021 The MITRE Corporation */ #ifndef __PBR_MAP_H__ #define __PBR_MAP_H__ @@ -54,6 +56,14 @@ struct pbr_map_interface { bool delete; }; +enum pbr_forwarding_type { + PBR_FT_UNSPEC = 0, + PBR_FT_VRF_UNCHANGED, + PBR_FT_SETVRF, + PBR_FT_NEXTHOP_GROUP, + PBR_FT_NEXTHOP_SINGLE, +}; + struct pbr_map_sequence { struct pbr_map *parent; @@ -109,14 +119,28 @@ struct pbr_map_sequence { * Action fields *****************************************************************/ + /* + * 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; + enum pbr_forwarding_type forwarding_type; + /* * Use interface's vrf. */ @@ -233,9 +257,4 @@ extern void pbr_map_check_vrf_nh_group_change(const char *nh_group, extern void pbr_map_check_interface_nh_group_change(const char *nh_group, struct interface *ifp, ifindex_t oldifindex); -extern void pbr_set_match_clause_for_vlan(struct pbr_map_sequence *pbrms, - uint16_t vlan_id, - uint16_t vlan_flags); -extern void pbr_set_match_clause_for_pcp(struct pbr_map_sequence *pbrms, - bool set, uint8_t pcp); #endif diff --git a/pbrd/pbr_nht.c b/pbrd/pbr_nht.c index 405a2d6588..4f7882fb22 100644 --- a/pbrd/pbr_nht.c +++ b/pbrd/pbr_nht.c @@ -549,6 +549,7 @@ void pbr_nht_set_seq_nhg(struct pbr_map_sequence *pbrms, const char *name) return; pbrms->nhgrp_name = XSTRDUP(MTYPE_TMP, name); + pbrms->forwarding_type = PBR_FT_NEXTHOP_GROUP; nhgc = nhgc_find(name); if (!nhgc) @@ -572,6 +573,7 @@ void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms, MTYPE_TMP, pbr_nht_nexthop_make_name(pbrms->parent->name, PBR_NHC_NAMELEN, pbrms->seqno, buf)); + pbrms->forwarding_type = PBR_FT_NEXTHOP_SINGLE; nh = nexthop_new(); memcpy(nh, nhop, sizeof(*nh)); diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index 8e9673482e..0d6e1afd5b 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -29,89 +29,60 @@ #include "pbrd/pbr_vty_clippy.c" /* clang-format off */ -DEFPY(pbr_map_match_pcp, pbr_map_match_pcp_cmd, "[no] match pcp <(0-7)$pcp>", - NO_STR - "Match spec follows\n" - "Match based on 802.1p Priority Code Point (PCP) value\n" - "PCP value to match\n") +DEFPY (pbr_set_table_range, + pbr_set_table_range_cmd, + "pbr table range (10000-4294966272)$lb (10000-4294966272)$ub", + PBR_STR + "Set table ID range\n" + "Set table ID range\n" + "Lower bound for table ID range\n" + "Upper bound for table ID range\n") { /* clang-format on */ - struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + /* upper bound is 2^32 - 2^10 */ + int ret = CMD_WARNING; + const int minrange = 1000; - if (pbrms) - pbr_set_match_clause_for_pcp(pbrms, !no, pcp); + /* validate given bounds */ + if (lb > ub) + vty_out(vty, "%% Lower bound must be less than upper bound\n"); + else if (ub - lb < minrange) + vty_out(vty, "%% Range breadth must be at least %d\n", minrange); + else { + ret = CMD_SUCCESS; + pbr_nht_set_tableid_range((uint32_t)lb, (uint32_t)ub); + } - return CMD_SUCCESS; + return ret; } /* 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") +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 */ - struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); - - if (pbrms) { - if (!no) { - pbr_set_match_clause_for_vlan(pbrms, vlan_id, 0); - } else { - /* if the user previously set a vlan_id value */ - if (pbrms->match_vlan_id != 0) { - if (vlan_id == pbrms->match_vlan_id) { - pbr_set_match_clause_for_vlan(pbrms, 0, - 0); - } - } - } - } + pbr_nht_set_tableid_range(PBR_NHT_DEFAULT_LOW_TABLEID, + PBR_NHT_DEFAULT_HIGH_TABLEID); return CMD_SUCCESS; } /* clang-format off */ -DEFPY(pbr_map_match_vlan_tag, pbr_map_match_vlan_tag_cmd, - "[no] match vlan ![<tagged|untagged|untagged-or-zero>$tag_type]", - NO_STR - "Match the rest of the command\n" - "Match based on VLAN tagging\n" - "Match all tagged frames\n" - "Match all untagged frames\n" - "Match untagged frames, or tagged frames with id zero\n") -{ - /* clang-format on */ - struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); - - if (!pbrms) - return CMD_WARNING; - - if (!no) { - assert(tag_type); - if (strmatch(tag_type, "tagged")) { - pbr_set_match_clause_for_vlan(pbrms, 0, - PBR_VLAN_FLAGS_TAGGED); - } else if (strmatch(tag_type, "untagged")) { - pbr_set_match_clause_for_vlan(pbrms, 0, - PBR_VLAN_FLAGS_UNTAGGED); - } else if (strmatch(tag_type, "untagged-or-zero")) { - pbr_set_match_clause_for_vlan(pbrms, 0, - PBR_VLAN_FLAGS_UNTAGGED_0); - } - } else { - pbr_set_match_clause_for_vlan(pbrms, 0, PBR_VLAN_FLAGS_NO_WILD); - } - - return CMD_SUCCESS; -} - -DEFUN_NOSH(pbr_map, pbr_map_cmd, "pbr-map PBRMAP seq (1-700)", +DEFUN_NOSH(pbr_map, + pbr_map_cmd, + "pbr-map PBRMAP seq (1-700)", "Create pbr-map or enter pbr-map command mode\n" "The name of the PBR MAP\n" "Sequence to insert in existing pbr-map entry\n" "Sequence number\n") { + /* clang-format on */ const char *pbrm_name = argv[1]->arg; uint32_t seqno = atoi(argv[3]->arg); struct pbr_map_sequence *pbrms; @@ -122,13 +93,17 @@ DEFUN_NOSH(pbr_map, pbr_map_cmd, "pbr-map PBRMAP seq (1-700)", return CMD_SUCCESS; } -DEFUN_NOSH(no_pbr_map, no_pbr_map_cmd, "no pbr-map PBRMAP [seq (1-700)]", +/* clang-format off */ +DEFUN_NOSH(no_pbr_map, + no_pbr_map_cmd, + "no pbr-map PBRMAP [seq (1-700)]", NO_STR "Delete pbr-map\n" "The name of the PBR MAP\n" "Sequence to delete from existing pbr-map entry\n" "Sequence number\n") { + /* clang-format on */ const char *pbrm_name = argv[2]->arg; uint32_t seqno = 0; struct pbr_map *pbrm = pbrm_find(pbrm_name); @@ -153,301 +128,533 @@ DEFUN_NOSH(no_pbr_map, no_pbr_map_cmd, "no pbr-map PBRMAP [seq (1-700)]", return CMD_SUCCESS; } -DEFPY(pbr_set_table_range, - pbr_set_table_range_cmd, - "pbr table range (10000-4294966272)$lb (10000-4294966272)$ub", - PBR_STR - "Set table ID range\n" - "Set table ID range\n" - "Lower bound for table ID range\n" - "Upper bound for table ID range\n") -{ - /* upper bound is 2^32 - 2^10 */ - int ret = CMD_WARNING; - const int minrange = 1000; +/*********************************************************************** + * pbrms/rule Match L3 Fields + ***********************************************************************/ - /* validate given bounds */ - if (lb > ub) - vty_out(vty, "%% Lower bound must be less than upper bound\n"); - else if (ub - lb < minrange) - vty_out(vty, "%% Range breadth must be at least %d\n", minrange); - else { - ret = CMD_SUCCESS; - pbr_nht_set_tableid_range((uint32_t) lb, (uint32_t) ub); +/* + * 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 PROTO$ip_proto", - NO_STR - "Match the rest of the command\n" - "Choose an ip-protocol\n" - "Protocol name\n") +/* clang-format off */ +DEFPY (pbr_map_match_ip_proto, + pbr_map_match_ip_proto_cmd, + "[no] match ip-protocol ![PROTO$ip_proto]", + NO_STR + "Match the rest of the command\n" + "Choose an ip-protocol\n" + "Protocol name\n") { + /* clang-format on */ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); - struct protoent *p; + struct protoent *p = NULL; if (!pbrms) return CMD_WARNING_CONFIG_FAILED; - if (!no) { - if (!ip_proto) { - vty_out(vty, "Unable to convert (null) to proto id\n"); - return CMD_WARNING; - } + if (no) { + if (!CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_IP_PROTOCOL)) + return CMD_SUCCESS; + UNSET_FLAG(pbrms->filter_bm, PBR_FILTER_IP_PROTOCOL); + goto check; + } + if (ip_proto) p = getprotobyname(ip_proto); - if (!p) { - vty_out(vty, "Unable to convert %s to proto id\n", - ip_proto); - return CMD_WARNING; - } - pbrms->ip_proto = p->p_proto; - } else - pbrms->ip_proto = 0; + if (!ip_proto || !p) { + vty_out(vty, "Unable to convert %s to proto id\n", + (ip_proto ? ip_proto : "(null)")); + return CMD_WARNING_CONFIG_FAILED; + } - pbr_map_check(pbrms, true); + 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; - - /* Lowercase the dscp enum (if needed) */ - if (isupper(dscp[i])) - dscpname[i] = tolower(dscp[i]); - else - dscpname[i] = dscp[i]; + 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; } - dscpname[strlen(dscp)] = '\0'; - if (isANumber) { - /* dscp passed is a regular number */ - long dscpAsNum = strtol(dscp, NULL, 0); + unsigned long ul_dscp; + char *pend; + uint8_t raw_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; - } + assert(dscp); + ul_dscp = strtol(dscp, &pend, 0); + if (*pend) + raw_dscp = pbr_map_decode_dscp_enum(dscp); + else + raw_dscp = ul_dscp << 2; + if (raw_dscp > PBR_DSFIELD_DSCP) { + vty_out(vty, "Invalid dscp value: %s%s\n", dscp, + (pend ? "" : " (numeric value must be in range 0-63)")); + return CMD_WARNING_CONFIG_FAILED; } - if (!no) { - if (((pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2) == rawDscp) + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_DSCP) && + (((pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2) == raw_dscp)) { + return CMD_SUCCESS; + } + + /* Set the DSCP bits of the DSField */ + pbrms->dsfield = (pbrms->dsfield & ~PBR_DSFIELD_DSCP) | (raw_dscp << 2); + SET_FLAG(pbrms->filter_bm, PBR_FILTER_DSCP); + +check: + pbr_map_check(pbrms, true); + return CMD_SUCCESS; +} + +/* clang-format off */ +DEFPY (pbr_map_match_ecn, + pbr_map_match_ecn_cmd, + "[no] match ecn ![(0-3)$ecn]", + NO_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) @@ -458,139 +665,450 @@ 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 (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 (no) { + if (!CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DST_PORT)) + return CMD_SUCCESS; + UNSET_FLAG(pbrms->action_bm, PBR_ACTION_DST_PORT); + goto check; + } + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DST_PORT) && + (pbrms->action_dst_port == port)) + return CMD_SUCCESS; + + SET_FLAG(pbrms->action_bm, PBR_ACTION_DST_PORT); + pbrms->action_dst_port = port; + +check: + pbr_map_check(pbrms, true); + return CMD_SUCCESS; } +/* clang-format off */ +DEFPY (pbr_map_action_dscp, + pbr_map_action_dscp_cmd, + "[no] set dscp ![DSCP$dscp]", + NO_STR + "Set command\n" + "Set IP DSCP field\n" + "DSCP numeric value (0-63) or standard codepoint name\n") +{ + /* clang-format on */ + struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); -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") + if (no) { + if (!CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DSCP)) + return CMD_SUCCESS; + UNSET_FLAG(pbrms->action_bm, PBR_ACTION_DSCP); + goto check; + } + + unsigned long ul_dscp; + char *pend; + uint8_t raw_dscp; + + assert(dscp); + ul_dscp = strtol(dscp, &pend, 0); + if (*pend) + raw_dscp = pbr_map_decode_dscp_enum(dscp); + else + raw_dscp = ul_dscp << 2; + + if (raw_dscp > PBR_DSFIELD_DSCP) { + vty_out(vty, "Invalid dscp value: %s%s\n", dscp, + (pend ? "" : " (numeric value must be in range 0-63)")); + return CMD_WARNING_CONFIG_FAILED; + } + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_DSCP) && + (pbrms->action_dscp == raw_dscp)) { + return CMD_SUCCESS; + } + SET_FLAG(pbrms->action_bm, PBR_ACTION_DSCP); + pbrms->action_dscp = raw_dscp; + +check: + pbr_map_check(pbrms, true); + return CMD_SUCCESS; +} + +/* clang-format off */ +DEFPY (pbr_map_action_ecn, + pbr_map_action_ecn_cmd, + "[no] set ecn ![(0-3)$ecn]", + NO_STR + "Set command\n" + "Set IP ECN field\n" + "Explicit Congestion Notification value\n") +{ + /* clang-format on */ + struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + + if (no) { + if (!CHECK_FLAG(pbrms->action_bm, PBR_ACTION_ECN)) + return CMD_SUCCESS; + UNSET_FLAG(pbrms->action_bm, PBR_ACTION_ECN); + goto check; + } + if (CHECK_FLAG(pbrms->action_bm, PBR_ACTION_ECN) && + (pbrms->action_ecn == ecn)) { + return CMD_SUCCESS; + } + SET_FLAG(pbrms->action_bm, PBR_ACTION_ECN); + pbrms->action_ecn = ecn; + +check: + pbr_map_check(pbrms, true); + return CMD_SUCCESS; +} + + +/*********************************************************************** + * pbrms/rule Action Set Meta + ***********************************************************************/ + +/* clang-format off */ +DEFPY (pbr_map_action_queue_id, + pbr_map_action_queue_id_cmd, + "[no] set queue-id ![(1-65535)$queue_id]", + NO_STR + "Set the rest of the command\n" + "Set based on egress port queue id\n" + "A valid value in range 1..65535 \n") { + /* clang-format on */ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); if (!pbrms) return CMD_WARNING_CONFIG_FAILED; - if (!no) - pbrms->action_queue_id = queue_id; - else if ((uint32_t)queue_id == pbrms->action_queue_id) - pbrms->action_queue_id = PBR_MAP_UNDEFINED_QUEUE_ID; + 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; + } - pbr_map_check(pbrms, true); + 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; } -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") + +/*********************************************************************** + * pbrms/rule Action Set L2 Fields + ***********************************************************************/ + +/* clang-format off */ +DEFPY (pbr_map_action_pcp, + pbr_map_action_pcp_cmd, + "[no] set pcp ![(0-7)$pcp]", + NO_STR + "Set the rest of the command\n" + "Set based on 802.1p Priority Code Point (PCP) value\n" + "A valid value in range 0..7\n") { + /* clang-format on */ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); if (!pbrms) return CMD_WARNING_CONFIG_FAILED; - if (!no) - pbrms->action_pcp = pcp; - else if (pcp == pbrms->action_pcp) - pbrms->action_pcp = 0; + 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; + } - pbr_map_check(pbrms, true); + 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_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 off */ +DEFPY (pbr_map_action_vlan_id, + pbr_map_action_vlan_id_cmd, + "[no] set vlan ![(1-4094)$vlan_id]", + NO_STR + "Set the rest of the command\n" + "Set action for VLAN tagging\n" + "A valid value in range 1..4094\n") { + /* clang-format on */ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); if (!pbrms) return CMD_WARNING_CONFIG_FAILED; - if (!no) - pbrms->action_vlan_id = vlan_id; - else if (pbrms->action_vlan_id == vlan_id) - pbrms->action_vlan_id = 0; + 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; + } - pbr_map_check(pbrms, true); + 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; } -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_strip_vlan, + pbr_map_action_strip_vlan_cmd, + "[no] strip vlan", + NO_STR + "Strip the vlan tags from frame\n" + "Strip any inner vlan tag\n") { + /* clang-format on */ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); if (!pbrms) return CMD_WARNING_CONFIG_FAILED; - if (!no) - pbrms->action_vlan_flags = PBR_MAP_STRIP_INNER_ANY; - else - pbrms->action_vlan_flags = 0; + 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; - pbr_map_check(pbrms, true); + /* + * 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; +} + + + DEFPY(pbr_map_nexthop_group, pbr_map_nexthop_group_cmd, "set nexthop-group NHGNAME$name", "Set for the PBR-MAP\n" @@ -641,22 +1159,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; @@ -738,8 +1261,11 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd, nhop.type = NEXTHOP_TYPE_IPV6; } } - } else + } else if (bh) { + nhop.type = NEXTHOP_TYPE_BLACKHOLE; + } else { nhop.type = NEXTHOP_TYPE_IFINDEX; + } if (pbrms->nhg) nh = nexthop_exists(pbrms->nhg, &nhop); @@ -768,23 +1294,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) @@ -810,10 +1341,11 @@ DEFPY(pbr_map_vrf, pbr_map_vrf_cmd, /* * If an equivalent set vrf * exists, just return success. */ - if (vrf_name && pbrms->vrf_lookup - && strncmp(pbrms->vrf_name, vrf_name, sizeof(pbrms->vrf_name)) == 0) + if ((pbrms->forwarding_type == PBR_FT_SETVRF) && + strncmp(pbrms->vrf_name, vrf_name, sizeof(pbrms->vrf_name)) == 0) return CMD_SUCCESS; - else if (!vrf_name && pbrms->vrf_unchanged) /* Unchanged already set */ + else if (!vrf_name && (pbrms->forwarding_type == PBR_FT_VRF_UNCHANGED)) + /* Unchanged already set */ return CMD_SUCCESS; if (vrf_name && !pbr_vrf_lookup_by_name(vrf_name)) { @@ -826,9 +1358,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); @@ -853,13 +1388,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; @@ -966,56 +1507,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 (pbrms->match_vlan_id != 0) + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_ID)) vty_out(vty, " Match VLAN ID: %u\n", pbrms->match_vlan_id); - if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_TAGGED) - vty_out(vty, " Match VLAN tagged frames\n"); - if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_UNTAGGED) - vty_out(vty, " Match VLAN untagged frames\n"); - if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_UNTAGGED_0) - vty_out(vty, " Match VLAN untagged or ID 0\n"); - - if (pbrms->action_queue_id != PBR_MAP_UNDEFINED_QUEUE_ID) - vty_out(vty, " Set Queue ID: %u\n", - pbrms->action_queue_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"); + } + + /* 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) @@ -1029,8 +1607,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) @@ -1045,13 +1624,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; } } @@ -1078,7 +1651,18 @@ static void vty_json_pbrms(json_object *j, struct vty *vty, json_object_string_add(jpbrm, "installedReason", pbrms->reason ? rbuf : "Valid"); - if (nhg_name) { + switch (pbrms->forwarding_type) { + case PBR_FT_UNSPEC: + break; + case PBR_FT_VRF_UNCHANGED: + break; + case PBR_FT_SETVRF: + assert(pbrms->vrf_name); + json_object_string_add(jpbrm, "vrfName", pbrms->vrf_name); + break; + case PBR_FT_NEXTHOP_GROUP: + case PBR_FT_NEXTHOP_SINGLE: + assert(nhg_name); nexthop_group = json_object_new_object(); json_object_int_add(nexthop_group, "tableId", @@ -1090,24 +1674,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); } @@ -1373,71 +2026,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); - if ((pbrms->match_vlan_id) && - (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_NO_WILD)) + /* L2 headers */ + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_VLAN_ID)) vty_out(vty, " match vlan %u\n", pbrms->match_vlan_id); - if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_TAGGED) - vty_out(vty, " match vlan tagged\n"); - if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_UNTAGGED) - vty_out(vty, " match vlan untagged\n"); - if (pbrms->match_vlan_flags == PBR_VLAN_FLAGS_UNTAGGED_0) - vty_out(vty, " match vlan untagged-or-zero\n"); - - if (pbrms->action_queue_id != PBR_MAP_UNDEFINED_QUEUE_ID) - vty_out(vty, " set queue-id %d\n", pbrms->action_queue_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_pcp) - vty_out(vty, " set pcp %d\n", pbrms->action_pcp); + /* meta */ + if (CHECK_FLAG(pbrms->filter_bm, PBR_FILTER_FWMARK)) + vty_out(vty, " match mark %u\n", pbrms->mark); - if (pbrms->action_vlan_id) - vty_out(vty, " set vlan %u\n", pbrms->action_vlan_id); + /* + * action clauses + */ - if (pbrms->action_vlan_flags == PBR_MAP_STRIP_INNER_ANY) + 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->vrf_unchanged) - vty_out(vty, " set vrf unchanged\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_lookup) + switch (pbrms->forwarding_type) { + case PBR_FT_UNSPEC: + break; + case PBR_FT_VRF_UNCHANGED: + vty_out(vty, " set vrf unchanged\n"); + 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"); @@ -1505,6 +2186,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); @@ -1516,10 +2198,18 @@ void pbr_vty_init(void) install_element(PBRMAP_NODE, &pbr_map_match_vlan_tag_cmd); install_element(PBRMAP_NODE, &pbr_map_match_pcp_cmd); install_element(PBRMAP_NODE, &pbr_map_match_mark_cmd); + install_element(PBRMAP_NODE, &pbr_map_action_queue_id_cmd); install_element(PBRMAP_NODE, &pbr_map_action_strip_vlan_cmd); install_element(PBRMAP_NODE, &pbr_map_action_vlan_id_cmd); install_element(PBRMAP_NODE, &pbr_map_action_pcp_cmd); + install_element(PBRMAP_NODE, &pbr_map_action_src_cmd); + install_element(PBRMAP_NODE, &pbr_map_action_dst_cmd); + install_element(PBRMAP_NODE, &pbr_map_action_dscp_cmd); + install_element(PBRMAP_NODE, &pbr_map_action_ecn_cmd); + install_element(PBRMAP_NODE, &pbr_map_action_src_port_cmd); + install_element(PBRMAP_NODE, &pbr_map_action_dst_port_cmd); + install_element(PBRMAP_NODE, &pbr_map_nexthop_group_cmd); install_element(PBRMAP_NODE, &no_pbr_map_nexthop_group_cmd); install_element(PBRMAP_NODE, &pbr_map_nexthop_cmd); diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c index 030c4c1114..35c771469c 100644 --- a/pbrd/pbr_zebra.c +++ b/pbrd/pbr_zebra.c @@ -517,10 +517,14 @@ static bool pbr_encode_pbr_map_sequence(struct stream *s, uint8_t family; /* - * There seems to be some effort in pbr_vty.c to keep the three - * copies of "family" equal. Not sure if the reason goes beyond - * ensuring consistency in ZAPI encoding. In any case, it might - * be handled better as an internal matter for the encoder (TBD). + * Opportunistic address family field is set when any of the IP + * address match/set fields is set, or when a NH/NHG is resolved. + * The value is needed by zebra for the underlying netlink + * messaging, particularly in delete operations, because it + * selects the rule database (IPv4 vs. IPv6). + * + * Historically the value has been encoded into any unused + * "match src/dst address" fields and picked off in zebra. */ family = AF_INET; if (pbrms->family) @@ -539,6 +543,8 @@ static bool pbr_encode_pbr_map_sequence(struct stream *s, r.priority = pbrms->ruleno; r.unique = pbrms->unique; + r.family = pbrms->family; + /* filter */ r.filter.filter_bm = pbrms->filter_bm; if (pbrms->src) @@ -558,44 +564,18 @@ static bool pbr_encode_pbr_map_sequence(struct stream *s, r.filter.fwmark = pbrms->mark; r.filter.ip_proto = pbrms->ip_proto; - /* - * Fix up filter flags for now, since PBRD doesn't maintain - * them yet (aside from PBR_FILTER_PCP) - */ - if (!is_default_prefix(&r.filter.src_ip)) - SET_FLAG(r.filter.filter_bm, PBR_FILTER_SRC_IP); - if (!is_default_prefix(&r.filter.dst_ip)) - SET_FLAG(r.filter.filter_bm, PBR_FILTER_DST_IP); - if (r.filter.src_port) - SET_FLAG(r.filter.filter_bm, PBR_FILTER_SRC_PORT); - if (r.filter.dst_port) - SET_FLAG(r.filter.filter_bm, PBR_FILTER_DST_PORT); - if (r.filter.vlan_id) - SET_FLAG(r.filter.filter_bm, PBR_FILTER_VLAN_ID); - if (r.filter.vlan_flags) - SET_FLAG(r.filter.filter_bm, PBR_FILTER_VLAN_FLAGS); - if (r.filter.dsfield) - SET_FLAG(r.filter.filter_bm, PBR_FILTER_DSFIELD); - if (r.filter.fwmark) - SET_FLAG(r.filter.filter_bm, PBR_FILTER_FWMARK); - if (r.filter.ip_proto) - SET_FLAG(r.filter.filter_bm, PBR_FILTER_IP_PROTOCOL); + r.filter.filter_bm = pbrms->filter_bm; /* actions */ + SET_FLAG(r.action.flags, PBR_ACTION_TABLE); /* always valid */ + /* * PBR should maintain its own set of action flags that we * can copy here instead of trying to infer from magic values. */ - SET_FLAG(r.action.flags, PBR_ACTION_TABLE); /* always valid */ - if (pbrms->action_queue_id != PBR_MAP_UNDEFINED_QUEUE_ID) - SET_FLAG(r.action.flags, PBR_ACTION_QUEUE_ID); - if (pbrms->action_pcp != 0) - SET_FLAG(r.action.flags, PBR_ACTION_PCP); - if (pbrms->action_vlan_id != 0) - SET_FLAG(r.action.flags, PBR_ACTION_VLAN_ID); - if (pbrms->action_vlan_flags != 0) - SET_FLAG(r.action.flags, PBR_ACTION_VLAN_FLAGS); + + r.action.flags = pbrms->action_bm; /* * if the user does not use the command "set vrf name unchanged" @@ -613,9 +593,18 @@ static bool pbr_encode_pbr_map_sequence(struct stream *s, } r.action.queue_id = pbrms->action_queue_id; + + r.action.src_ip = pbrms->action_src; + r.action.dst_ip = pbrms->action_dst; + + r.action.src_port = pbrms->action_src_port; + r.action.dst_port = pbrms->action_dst_port; + + r.action.dscp = pbrms->action_dscp; + r.action.ecn = pbrms->action_ecn; + r.action.pcp = pbrms->action_pcp; r.action.vlan_id = pbrms->action_vlan_id; - r.action.vlan_flags = pbrms->action_vlan_flags; strlcpy(r.ifname, ifp->name, sizeof(r.ifname)); diff --git a/tests/topotests/pbr_topo1/test_pbr_topo1.py b/tests/topotests/pbr_topo1/test_pbr_topo1.py index ad096fa14e..2b437170ff 100644 --- a/tests/topotests/pbr_topo1/test_pbr_topo1.py +++ b/tests/topotests/pbr_topo1/test_pbr_topo1.py @@ -201,27 +201,38 @@ ftest = [ {"c": "match vlan untagged", "tm": r"VLAN Flags Match: untagged$"}, {"c": "match vlan untagged-or-zero", "tm": r"VLAN Flags Match: untagged-or-zero$"}, {"c": "no match vlan tagged", "tN": r"VLAN Flags Match:"}, - {"c": "match src-ip 37.49.22.0/24", "tm": r"SRC IP Match: 37.49.22.0/24$"}, {"c": "no match src-ip 37.49.22.0/24", "tN": r"SRC IP Match: 37.49.22.0/24$"}, - - {"c": "match dst-ip 38.41.29.0/25", "cDN": "foo", "tm": r"DST IP Match: 38.41.29.0/25$"}, + { + "c": "match dst-ip 38.41.29.0/25", + "cDN": "foo", + "tm": r"DST IP Match: 38.41.29.0/25$", + }, {"c": "no match dst-ip 38.41.29.0/25", "tN": r"DST IP Match: 38.41.29.0/25$"}, - {"c": "match src-port 117", "tm": r"SRC Port Match: 117$"}, {"c": "no match src-port 117", "tN": r"SRC Port Match: 117$"}, - {"c": "match dst-port 119", "tm": r"DST Port Match: 119$"}, {"c": "no match dst-port 119", "tN": r"DST Port Match: 119$"}, - {"c": "match dscp cs3", "tm": r"DSCP Match: 24$"}, {"c": "no match dscp cs3", "tN": r"DSCP Match: 24$"}, - {"c": "match ecn 2", "tm": r"ECN Match: 2$"}, {"c": "no match ecn 2", "tN": r"ECN Match: 2$"}, - {"c": "match mark 337", "tm": r"MARK Match: 337$"}, {"c": "no match mark 337", "tN": r"MARK Match: 337$"}, + {"c": "set src-ip 44.100.1.1", "tm": r"Set SRC IP: 44.100.1.1$"}, + {"c": "no set src-ip 44.100.1.1", "tN": r"Set SRC IP: 44.100.1.1$"}, + {"c": "set dst-ip 44.105.1.1", "tm": r"Set DST IP: 44.105.1.1$"}, + {"c": "no set dst-ip 44.105.1.1", "tN": r"Set DST IP: 44.105.1.1$"}, + {"c": "set src-port 41", "tm": r"Set SRC PORT: 41$"}, + {"c": "no set src-port 41", "tN": r"Set SRC PORT: 41$"}, + {"c": "set dst-port 43", "tm": r"Set DST PORT: 43$"}, + {"c": "no set dst-port 43", "tN": r"Set DST PORT: 43$"}, + {"c": "set dscp 24", "tm": r"Set DSCP: 24$"}, + {"c": "no set dscp 24", "tN": r"Set DSCP: 24$"}, + {"c": "set dscp cs7", "tm": r"Set DSCP: 14$"}, + {"c": "no set dscp cs7", "tN": r"Set DSCP: 14$"}, + {"c": "set ecn 1", "tm": r"Set ECN: 1$"}, + {"c": "no set ecn 1", "tN": r"Set ECN: 1$"}, ] diff --git a/tests/topotests/static_routing_mpls/r1/zebra.conf b/tests/topotests/static_routing_mpls/r1/zebra.conf new file mode 100644 index 0000000000..4e5d29af98 --- /dev/null +++ b/tests/topotests/static_routing_mpls/r1/zebra.conf @@ -0,0 +1,16 @@ +hostname r1 +ip forwarding +ipv6 forwarding + +int r1-eth0 + ip addr 192.168.1.1/24 +! +int r1-eth1 + ip addr 192.168.2.1/24 +! +int lo + ip addr 192.0.2.1/32 +! +int r1-eth1 + ip addr 192.168.2.1/24 +! diff --git a/tests/topotests/static_routing_mpls/r2/zebra.conf b/tests/topotests/static_routing_mpls/r2/zebra.conf new file mode 100644 index 0000000000..4e06ae5202 --- /dev/null +++ b/tests/topotests/static_routing_mpls/r2/zebra.conf @@ -0,0 +1,18 @@ +hostname r2 +ip forwarding +ipv6 forwarding + +int r2-eth0 + ip addr 192.168.2.2/24 +! +int r2-eth1 + mpls enable + ip addr 192.168.3.2/24 +! +int r2-eth2 + mpls disable + ip addr 172.31.3.2/24 +! +int lo + ip addr 192.0.2.2/32 +! diff --git a/tests/topotests/static_routing_mpls/test_static_routing_mpls.py b/tests/topotests/static_routing_mpls/test_static_routing_mpls.py new file mode 100644 index 0000000000..c1e249cc8f --- /dev/null +++ b/tests/topotests/static_routing_mpls/test_static_routing_mpls.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_static_routing_mlpls.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2023 by 6WIND +# + +""" +test_static_routing_mpls.py: Testing MPLS configuration with mpls interface settings + +""" + +import os +import re +import sys +import pytest +import json +from functools import partial +import functools + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + +##################################################### +## +## Network Topology Definition +## +##################################################### + + +def build_topo(tgen): + "Build function" + + tgen.add_router("r1") + tgen.add_router("r2") + + switch = tgen.add_switch("sw1") + switch.add_link(tgen.gears["r1"]) + + switch = tgen.add_switch("sw2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("sw3") + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("sw4") + switch.add_link(tgen.gears["r2"]) + + +##################################################### +## +## Tests starting +## +##################################################### +def _populate_mpls_labels(): + tgen = get_topogen() + cmds_list = ["echo 100000 > /proc/sys/net/mpls/platform_labels"] + for cmd in cmds_list: + for host in ("r1", "r2"): + logger.info("input: " + cmd) + output = tgen.net[host].cmd(cmd) + logger.info("output: " + output) + + +def setup_module(module): + "Setup topology" + tgen = Topogen(build_topo, module.__name__) + tgen.start_topology() + + _populate_mpls_labels() + + # This is a sample of configuration loading. + router_list = tgen.routers() + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def _check_mpls_state_interface(router, interface, up=True): + output = router.vtysh_cmd("show interface {}".format(interface)) + if up and "MPLS enabled" in output: + return None + elif not up and "MPLS enabled" not in output: + return None + return "not good" + + +def _check_mpls_state(router, interface, configured=True): + test_func = functools.partial( + _check_mpls_state_interface, router, interface, up=configured + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + return success + + +def test_mpls_configured_on_interface(): + "Test 'mpls' state is correctly configured on an unconfigured interfaces" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Checking that MPLS state is on on r2-eth1") + assertmsg = "r2, interface r2-eth1, mpls operational state is off, not expected" + assert _check_mpls_state(tgen.gears["r2"], "r2-eth1"), assertmsg + + logger.info("Checking that MPLS state is off on r2-eth2") + assertmsg = "r2, interface r2-eth2, mpls operational state is on, not expected" + assert _check_mpls_state(tgen.gears["r2"], "r2-eth2", False), assertmsg + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/yang/frr-zebra.yang b/yang/frr-zebra.yang index 7f4254cd41..3c6e45126a 100644 --- a/yang/frr-zebra.yang +++ b/yang/frr-zebra.yang @@ -1982,6 +1982,12 @@ module frr-zebra { "Interface admin status."; } + leaf mpls { + type boolean; + description + "Interface MPLS status."; + } + leaf bandwidth { type uint32 { range "1..100000"; diff --git a/zebra/interface.c b/zebra/interface.c index 4006f9c574..ab2b7d2446 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -3777,23 +3777,20 @@ DEFUN (multicast, DEFPY (mpls, mpls_cmd, - "[no] mpls enable", + "[no] mpls <enable$on|disable$off>", NO_STR MPLS_STR - "Set mpls to be on for the interface\n") + "Set mpls to be on for the interface\n" + "Set mpls to be off for the interface\n") { - VTY_DECLVAR_CONTEXT(interface, ifp); - struct zebra_if *if_data = ifp->info; - - if (no) { - dplane_intf_mpls_modify_state(ifp, false); - if_data->mpls_config = IF_ZEBRA_DATA_UNSPEC; - } else { - dplane_intf_mpls_modify_state(ifp, true); - if_data->mpls_config = IF_ZEBRA_DATA_ON; - } + if (!no) + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/mpls", + NB_OP_CREATE, on ? "true" : "false"); + else + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/mpls", + NB_OP_DESTROY, NULL); - return CMD_SUCCESS; + return nb_cli_apply_changes(vty, NULL); } int if_multicast_unset(struct interface *ifp) @@ -5633,6 +5630,9 @@ static int if_config_write(struct vty *vty) if (if_data->mpls_config == IF_ZEBRA_DATA_ON) vty_out(vty, " mpls enable\n"); + else if (if_data->mpls_config == + IF_ZEBRA_DATA_OFF) + vty_out(vty, " mpls disable\n"); } hook_call(zebra_if_config_wr, vty, ifp); diff --git a/zebra/redistribute.c b/zebra/redistribute.c index 9ca9c7a55a..8c8cbfc8d1 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -647,10 +647,9 @@ int zebra_add_import_table_entry(struct zebra_vrf *zvrf, struct route_node *rn, afi = family2afi(rn->p.family); if (rmap_name) - ret = zebra_import_table_route_map_check( - afi, re->type, re->instance, &rn->p, - re->nhe->nhg.nexthop, - zvrf->vrf->vrf_id, re->tag, rmap_name); + ret = zebra_import_table_route_map_check(afi, re, &rn->p, + re->nhe->nhg.nexthop, + rmap_name); if (ret != RMAP_PERMITMATCH) { UNSET_FLAG(re->flags, ZEBRA_FLAG_SELECTED); diff --git a/zebra/rule_netlink.c b/zebra/rule_netlink.c index 3bb4936b81..bc96e12902 100644 --- a/zebra/rule_netlink.c +++ b/zebra/rule_netlink.c @@ -117,8 +117,8 @@ static ssize_t netlink_rule_msg_encode( } /* dsfield, if specified; mask off the ECN bits */ - if (filter_bm & PBR_FILTER_DSFIELD) - req->frh.tos = dsfield & 0xfc; + if (filter_bm & PBR_FILTER_DSCP) + req->frh.tos = dsfield & PBR_DSFIELD_DSCP; /* protocol to match on */ if (filter_bm & PBR_FILTER_IP_PROTOCOL) diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index e9c243217a..f6afb006b7 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -3188,10 +3188,32 @@ static inline void zread_rule(ZAPI_HANDLER_ARGS) strlcpy(zpr.ifname, zpr.rule.ifname, sizeof(zpr.ifname)); + if ((zpr.rule.family != AF_INET) && + (zpr.rule.family != AF_INET6)) { + zlog_warn("Unsupported PBR source IP family: %s (%hhu)", + family2str(zpr.rule.family), zpr.rule.family); + return; + } + + /* + * Fixup filter src/dst IP addresses if they are unset + * because the netlink code currently obtains address family + * from them. Address family is used to specify which + * kernel database to use when adding/deleting rule. + * + * TBD: propagate zpr.rule.family into dataplane and + * netlink code so they can stop using filter src/dst addrs. + */ + if (!CHECK_FLAG(zpr.rule.filter.filter_bm, PBR_FILTER_SRC_IP)) + zpr.rule.filter.src_ip.family = zpr.rule.family; + if (!CHECK_FLAG(zpr.rule.filter.filter_bm, PBR_FILTER_DST_IP)) + zpr.rule.filter.dst_ip.family = zpr.rule.family; + + /* TBD delete below block when netlink code gets family from zpr.rule.family */ if (!(zpr.rule.filter.src_ip.family == AF_INET || zpr.rule.filter.src_ip.family == AF_INET6)) { zlog_warn( - "Unsupported PBR source IP family: %s (%hhu)", + "Unsupported PBR source IP family: %s (%u)", family2str(zpr.rule.filter.src_ip.family), zpr.rule.filter.src_ip.family); return; @@ -3199,11 +3221,12 @@ static inline void zread_rule(ZAPI_HANDLER_ARGS) if (!(zpr.rule.filter.dst_ip.family == AF_INET || zpr.rule.filter.dst_ip.family == AF_INET6)) { zlog_warn( - "Unsupported PBR destination IP family: %s (%hhu)", + "Unsupported PBR destination IP family: %s (%u)", family2str(zpr.rule.filter.dst_ip.family), zpr.rule.filter.dst_ip.family); return; } + /* TBD delete above block when netlink code gets family from zpr.rule.family */ zpr.vrf_id = zvrf->vrf->vrf_id; diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c index ba34951e76..1279762232 100644 --- a/zebra/zebra_fpm_netlink.c +++ b/zebra/zebra_fpm_netlink.c @@ -287,6 +287,8 @@ static int netlink_route_info_fill(struct netlink_route_info *ri, int cmd, if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) continue; + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE)) + continue; if (nexthop->type == NEXTHOP_TYPE_BLACKHOLE) { switch (nexthop->bh_type) { diff --git a/zebra/zebra_nb.c b/zebra/zebra_nb.c index d94547cffc..a0ef08273c 100644 --- a/zebra/zebra_nb.c +++ b/zebra/zebra_nb.c @@ -339,6 +339,13 @@ const struct frr_yang_module_info frr_zebra_info = { } }, { + .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/mpls", + .cbs = { + .modify = lib_interface_zebra_mpls_modify, + .destroy = lib_interface_zebra_mpls_destroy, + } + }, + { .xpath = "/frr-interface:lib/interface/frr-zebra:zebra/bandwidth", .cbs = { .modify = lib_interface_zebra_bandwidth_modify, diff --git a/zebra/zebra_nb.h b/zebra/zebra_nb.h index fa576ec3f4..a066a7f9dc 100644 --- a/zebra/zebra_nb.h +++ b/zebra/zebra_nb.h @@ -96,6 +96,8 @@ int lib_interface_zebra_shutdown_modify(struct nb_cb_modify_args *args); int lib_interface_zebra_shutdown_destroy(struct nb_cb_destroy_args *args); int lib_interface_zebra_bandwidth_modify(struct nb_cb_modify_args *args); int lib_interface_zebra_bandwidth_destroy(struct nb_cb_destroy_args *args); +int lib_interface_zebra_mpls_modify(struct nb_cb_modify_args *args); +int lib_interface_zebra_mpls_destroy(struct nb_cb_destroy_args *args); int lib_interface_zebra_legacy_admin_group_modify( struct nb_cb_modify_args *args); int lib_interface_zebra_legacy_admin_group_destroy( diff --git a/zebra/zebra_nb_config.c b/zebra/zebra_nb_config.c index 336669a49b..5ea03112bc 100644 --- a/zebra/zebra_nb_config.c +++ b/zebra/zebra_nb_config.c @@ -1088,6 +1088,50 @@ int lib_interface_zebra_shutdown_destroy(struct nb_cb_destroy_args *args) } /* + * XPath: /frr-interface:lib/interface/frr-zebra:zebra/mpls + */ +int lib_interface_zebra_mpls_modify(struct nb_cb_modify_args *args) +{ + struct interface *ifp; + bool mpls; + struct zebra_if *zif; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + zif = ifp->info; + mpls = yang_dnode_get_bool(args->dnode, NULL); + + if (mpls) + zif->mpls_config = IF_ZEBRA_DATA_ON; + else + zif->mpls_config = IF_ZEBRA_DATA_OFF; + + dplane_intf_mpls_modify_state(ifp, mpls); + + return NB_OK; +} + +int lib_interface_zebra_mpls_destroy(struct nb_cb_destroy_args *args) +{ + struct interface *ifp; + struct zebra_if *zif; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ifp = nb_running_get_entry(args->dnode, NULL, true); + zif = ifp->info; + + zif->mpls_config = IF_ZEBRA_DATA_UNSPEC; + + /* keep the state as it is */ + + return NB_OK; +} + +/* * XPath: /frr-interface:lib/interface/frr-zebra:zebra/bandwidth */ int lib_interface_zebra_bandwidth_modify(struct nb_cb_modify_args *args) diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index a701b582ce..d3c86e2a37 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -2703,8 +2703,7 @@ skip_check: } /* It'll get set if required inside */ - ret = zebra_route_map_check(family, re->type, re->instance, p, nexthop, - zvrf, re->tag); + ret = zebra_route_map_check(family, re, p, nexthop, zvrf); if (ret == RMAP_DENYMATCH) { if (IS_ZEBRA_DEBUG_RIB) { zlog_debug( diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c index eac93dca41..5124768a7c 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -507,6 +507,7 @@ void zebra_pbr_show_rule_unit(struct zebra_pbr_rule *rule, struct vty *vty) { struct pbr_rule *prule = &rule->rule; struct zebra_pbr_action *zaction = &rule->action; + struct pbr_action *pa = &prule->action; vty_out(vty, "Rules if %s\n", rule->ifname); vty_out(vty, " Seq %u pri %u\n", prule->seq, prule->priority); @@ -522,12 +523,12 @@ void zebra_pbr_show_rule_unit(struct zebra_pbr_rule *rule, struct vty *vty) if (prule->filter.filter_bm & PBR_FILTER_DST_PORT) vty_out(vty, " DST Port Match: %u\n", prule->filter.dst_port); - if (prule->filter.filter_bm & PBR_FILTER_DSFIELD) { + if (prule->filter.filter_bm & PBR_FILTER_DSCP) vty_out(vty, " DSCP Match: %u\n", (prule->filter.dsfield & PBR_DSFIELD_DSCP) >> 2); + if (prule->filter.filter_bm & PBR_FILTER_ECN) vty_out(vty, " ECN Match: %u\n", prule->filter.dsfield & PBR_DSFIELD_ECN); - } if (prule->filter.filter_bm & PBR_FILTER_FWMARK) vty_out(vty, " MARK Match: %u\n", prule->filter.fwmark); @@ -548,6 +549,30 @@ void zebra_pbr_show_rule_unit(struct zebra_pbr_rule *rule, struct vty *vty) vty_out(vty, "\n"); } + if (CHECK_FLAG(pa->flags, PBR_ACTION_ECN)) + vty_out(vty, " Action: Set ECN: %u\n", pa->ecn); + if (CHECK_FLAG(pa->flags, PBR_ACTION_DSCP)) + vty_out(vty, " Action: Set DSCP: %u\n", pa->dscp >> 2); + + if (CHECK_FLAG(pa->flags, PBR_ACTION_SRC_IP)) + vty_out(vty, " Action: Set SRC IP: %pSU\n", &pa->src_ip); + if (CHECK_FLAG(pa->flags, PBR_ACTION_DST_IP)) + vty_out(vty, " Action: Set DST IP: %pSU\n", &pa->dst_ip); + if (CHECK_FLAG(pa->flags, PBR_ACTION_SRC_PORT)) + vty_out(vty, " Action: Set SRC PORT: %u\n", pa->src_port); + if (CHECK_FLAG(pa->flags, PBR_ACTION_DST_PORT)) + vty_out(vty, " Action: Set DST PORT: %u\n", pa->dst_port); + + if (CHECK_FLAG(pa->flags, PBR_ACTION_QUEUE_ID)) + vty_out(vty, " Action: Set Queue ID: %u\n", pa->queue_id); + + if (CHECK_FLAG(pa->flags, PBR_ACTION_PCP)) + vty_out(vty, " Action: Set PCP: %u\n", pa->pcp); + if (CHECK_FLAG(pa->flags, PBR_ACTION_VLAN_ID)) + vty_out(vty, " Action: Set VLAN ID: %u\n", pa->vlan_id); + if (CHECK_FLAG(pa->flags, PBR_ACTION_VLAN_STRIP_INNER_ANY)) + vty_out(vty, " Action: Strip VLAN ID\n"); + vty_out(vty, " Tableid: %u\n", prule->action.table); if (zaction->afi == AFI_IP) vty_out(vty, " Action: nh: %pI4 intf: %s\n", diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h index 15ad4e35cf..ddc1460d40 100644 --- a/zebra/zebra_pbr.h +++ b/zebra/zebra_pbr.h @@ -61,8 +61,6 @@ struct zebra_pbr_rule { (r->rule.filter.filter_bm & PBR_FILTER_SRC_PORT) #define IS_RULE_FILTERING_ON_DST_PORT(r) \ (r->rule.filter.filter_bm & PBR_FILTER_DST_PORT) -#define IS_RULE_FILTERING_ON_DSFIELD(r) \ - (r->rule.filter.filter_bm & PBR_FILTER_DSFIELD) #define IS_RULE_FILTERING_ON_FWMARK(r) \ (r->rule.filter.filter_bm & PBR_FILTER_FWMARK) diff --git a/zebra/zebra_routemap.c b/zebra/zebra_routemap.c index eb94e26c30..21aaf1d066 100644 --- a/zebra/zebra_routemap.c +++ b/zebra/zebra_routemap.c @@ -31,13 +31,9 @@ static uint32_t zebra_rmap_update_timer = ZEBRA_RMAP_DEFAULT_UPDATE_TIMER; static struct event *zebra_t_rmap_update = NULL; char *zebra_import_table_routemap[AFI_MAX][ZEBRA_KERNEL_TABLE_MAX]; -struct nh_rmap_obj { +struct zebra_rmap_obj { struct nexthop *nexthop; - vrf_id_t vrf_id; - uint32_t source_protocol; - uint8_t instance; - int metric; - route_tag_t tag; + struct route_entry *re; }; static void zebra_route_map_set_delay_timer(uint32_t value); @@ -49,12 +45,12 @@ static enum route_map_cmd_result_t route_match_tag(void *rule, const struct prefix *prefix, void *object) { route_tag_t *tag; - struct nh_rmap_obj *nh_data; + struct zebra_rmap_obj *rm_data; tag = rule; - nh_data = object; + rm_data = object; - if (nh_data->tag == *tag) + if (rm_data->re->tag == *tag) return RMAP_MATCH; return RMAP_NOMATCH; @@ -74,19 +70,19 @@ static const struct route_map_rule_cmd route_match_tag_cmd = { static enum route_map_cmd_result_t route_match_interface(void *rule, const struct prefix *prefix, void *object) { - struct nh_rmap_obj *nh_data; + struct zebra_rmap_obj *rm_data; char *ifname = rule; ifindex_t ifindex; if (strcasecmp(ifname, "any") == 0) return RMAP_MATCH; - nh_data = object; - if (!nh_data || !nh_data->nexthop) + rm_data = object; + if (!rm_data || !rm_data->nexthop) return RMAP_NOMATCH; - ifindex = ifname2ifindex(ifname, nh_data->vrf_id); + ifindex = ifname2ifindex(ifname, rm_data->nexthop->vrf_id); if (ifindex == 0) return RMAP_NOMATCH; - if (nh_data->nexthop->ifindex == ifindex) + if (rm_data->nexthop->ifindex == ifindex) return RMAP_MATCH; return RMAP_NOMATCH; @@ -1017,21 +1013,21 @@ static enum route_map_cmd_result_t route_match_ip_next_hop(void *rule, const struct prefix *prefix, void *object) { struct access_list *alist; - struct nh_rmap_obj *nh_data; + struct zebra_rmap_obj *rm_data; struct prefix_ipv4 p; - nh_data = object; - if (!nh_data) + rm_data = object; + if (!rm_data) return RMAP_NOMATCH; - switch (nh_data->nexthop->type) { + switch (rm_data->nexthop->type) { case NEXTHOP_TYPE_IFINDEX: /* Interface routes can't match ip next-hop */ return RMAP_NOMATCH; case NEXTHOP_TYPE_IPV4_IFINDEX: case NEXTHOP_TYPE_IPV4: p.family = AF_INET; - p.prefix = nh_data->nexthop->gate.ipv4; + p.prefix = rm_data->nexthop->gate.ipv4; p.prefixlen = IPV4_MAX_BITLEN; break; case NEXTHOP_TYPE_IPV6: @@ -1080,21 +1076,21 @@ route_match_ip_next_hop_prefix_list(void *rule, const struct prefix *prefix, void *object) { struct prefix_list *plist; - struct nh_rmap_obj *nh_data; + struct zebra_rmap_obj *rm_data; struct prefix_ipv4 p; - nh_data = (struct nh_rmap_obj *)object; - if (!nh_data) + rm_data = (struct zebra_rmap_obj *)object; + if (!rm_data) return RMAP_NOMATCH; - switch (nh_data->nexthop->type) { + switch (rm_data->nexthop->type) { case NEXTHOP_TYPE_IFINDEX: /* Interface routes can't match ip next-hop */ return RMAP_NOMATCH; case NEXTHOP_TYPE_IPV4_IFINDEX: case NEXTHOP_TYPE_IPV4: p.family = AF_INET; - p.prefix = nh_data->nexthop->gate.ipv4; + p.prefix = rm_data->nexthop->gate.ipv4; p.prefixlen = IPV4_MAX_BITLEN; break; case NEXTHOP_TYPE_IPV6: @@ -1264,14 +1260,14 @@ static enum route_map_cmd_result_t route_match_ipv6_next_hop_type(void *rule, const struct prefix *prefix, void *object) { - struct nh_rmap_obj *nh_data; + struct zebra_rmap_obj *rm_data; if (prefix->family == AF_INET6) { - nh_data = (struct nh_rmap_obj *)object; - if (!nh_data) + rm_data = (struct zebra_rmap_obj *)object; + if (!rm_data) return RMAP_NOMATCH; - if (nh_data->nexthop->type == NEXTHOP_TYPE_BLACKHOLE) + if (rm_data->nexthop->type == NEXTHOP_TYPE_BLACKHOLE) return RMAP_MATCH; } @@ -1356,21 +1352,21 @@ route_match_ip_nexthop_prefix_len(void *rule, const struct prefix *prefix, void *object) { uint32_t *prefixlen = (uint32_t *)rule; - struct nh_rmap_obj *nh_data; + struct zebra_rmap_obj *rm_data; struct prefix_ipv4 p; - nh_data = (struct nh_rmap_obj *)object; - if (!nh_data || !nh_data->nexthop) + rm_data = (struct zebra_rmap_obj *)object; + if (!rm_data || !rm_data->nexthop) return RMAP_NOMATCH; - switch (nh_data->nexthop->type) { + switch (rm_data->nexthop->type) { case NEXTHOP_TYPE_IFINDEX: /* Interface routes can't match ip next-hop */ return RMAP_NOMATCH; case NEXTHOP_TYPE_IPV4_IFINDEX: case NEXTHOP_TYPE_IPV4: p.family = AF_INET; - p.prefix = nh_data->nexthop->gate.ipv4; + p.prefix = rm_data->nexthop->gate.ipv4; p.prefixlen = IPV4_MAX_BITLEN; break; case NEXTHOP_TYPE_IPV6: @@ -1395,14 +1391,14 @@ static enum route_map_cmd_result_t route_match_ip_next_hop_type(void *rule, const struct prefix *prefix, void *object) { - struct nh_rmap_obj *nh_data; + struct zebra_rmap_obj *rm_data; if (prefix->family == AF_INET) { - nh_data = (struct nh_rmap_obj *)object; - if (!nh_data) + rm_data = (struct zebra_rmap_obj *)object; + if (!rm_data) return RMAP_NOMATCH; - if (nh_data->nexthop->type == NEXTHOP_TYPE_BLACKHOLE) + if (rm_data->nexthop->type == NEXTHOP_TYPE_BLACKHOLE) return RMAP_MATCH; } @@ -1432,15 +1428,14 @@ static const struct route_map_rule_cmd static enum route_map_cmd_result_t route_match_source_protocol(void *rule, const struct prefix *p, void *object) { - uint32_t *rib_type = (uint32_t *)rule; - struct nh_rmap_obj *nh_data; + int32_t *rib_type = (int32_t *)rule; + struct zebra_rmap_obj *rm_data; - nh_data = (struct nh_rmap_obj *)object; - if (!nh_data) + rm_data = (struct zebra_rmap_obj *)object; + if (!rm_data) return RMAP_NOMATCH; - return ((nh_data->source_protocol == *rib_type) ? RMAP_MATCH - : RMAP_NOMATCH); + return ((rm_data->re->type == *rib_type) ? RMAP_MATCH : RMAP_NOMATCH); } static void *route_match_source_protocol_compile(const char *arg) @@ -1473,13 +1468,13 @@ static enum route_map_cmd_result_t route_match_source_instance(void *rule, const struct prefix *p, void *object) { uint8_t *instance = (uint8_t *)rule; - struct nh_rmap_obj *nh_data; + struct zebra_rmap_obj *rm_data; - nh_data = (struct nh_rmap_obj *)object; - if (!nh_data) + rm_data = (struct zebra_rmap_obj *)object; + if (!rm_data) return RMAP_NOMATCH; - return (nh_data->instance == *instance) ? RMAP_MATCH : RMAP_NOMATCH; + return (rm_data->re->instance == *instance) ? RMAP_MATCH : RMAP_NOMATCH; } static void *route_match_source_instance_compile(const char *arg) @@ -1513,10 +1508,10 @@ static const struct route_map_rule_cmd route_match_source_instance_cmd = { static enum route_map_cmd_result_t route_set_src(void *rule, const struct prefix *prefix, void *object) { - struct nh_rmap_obj *nh_data; + struct zebra_rmap_obj *rm_data; - nh_data = (struct nh_rmap_obj *)object; - nh_data->nexthop->rmap_src = *(union g_addr *)rule; + rm_data = (struct zebra_rmap_obj *)object; + rm_data->nexthop->rmap_src = *(union g_addr *)rule; return RMAP_OKAY; } @@ -1761,26 +1756,22 @@ void zebra_routemap_finish(void) route_map_finish(); } -route_map_result_t -zebra_route_map_check(afi_t family, int rib_type, uint8_t instance, - const struct prefix *p, struct nexthop *nexthop, - struct zebra_vrf *zvrf, route_tag_t tag) +route_map_result_t zebra_route_map_check(afi_t family, struct route_entry *re, + const struct prefix *p, + struct nexthop *nexthop, + struct zebra_vrf *zvrf) { struct route_map *rmap = NULL; char *rm_name; route_map_result_t ret = RMAP_PERMITMATCH; - struct nh_rmap_obj nh_obj; + struct zebra_rmap_obj rm_obj; - nh_obj.nexthop = nexthop; - nh_obj.vrf_id = nexthop->vrf_id; - nh_obj.source_protocol = rib_type; - nh_obj.instance = instance; - nh_obj.metric = 0; - nh_obj.tag = tag; + rm_obj.nexthop = nexthop; + rm_obj.re = re; - if (rib_type >= 0 && rib_type < ZEBRA_ROUTE_MAX) { - rm_name = PROTO_RM_NAME(zvrf, family, rib_type); - rmap = PROTO_RM_MAP(zvrf, family, rib_type); + if (re->type >= 0 && re->type < ZEBRA_ROUTE_MAX) { + rm_name = PROTO_RM_NAME(zvrf, family, re->type); + rmap = PROTO_RM_MAP(zvrf, family, re->type); if (rm_name && !rmap) return RMAP_DENYMATCH; @@ -1793,7 +1784,7 @@ zebra_route_map_check(afi_t family, int rib_type, uint8_t instance, return RMAP_DENYMATCH; } if (rmap) { - ret = route_map_apply(rmap, p, &nh_obj); + ret = route_map_apply(rmap, p, &rm_obj); } return (ret); @@ -1816,28 +1807,23 @@ void zebra_del_import_table_route_map(afi_t afi, uint32_t table) XFREE(MTYPE_ROUTE_MAP_NAME, zebra_import_table_routemap[afi][table]); } -route_map_result_t -zebra_import_table_route_map_check(int family, int re_type, uint8_t instance, - const struct prefix *p, - struct nexthop *nexthop, - vrf_id_t vrf_id, route_tag_t tag, - const char *rmap_name) +route_map_result_t zebra_import_table_route_map_check(int family, + struct route_entry *re, + const struct prefix *p, + struct nexthop *nexthop, + const char *rmap_name) { struct route_map *rmap = NULL; route_map_result_t ret = RMAP_DENYMATCH; - struct nh_rmap_obj nh_obj; + struct zebra_rmap_obj rm_obj; - nh_obj.nexthop = nexthop; - nh_obj.vrf_id = vrf_id; - nh_obj.source_protocol = re_type; - nh_obj.instance = instance; - nh_obj.metric = 0; - nh_obj.tag = tag; + rm_obj.nexthop = nexthop; + rm_obj.re = re; - if (re_type >= 0 && re_type < ZEBRA_ROUTE_MAX) + if (re->type >= 0 && re->type < ZEBRA_ROUTE_MAX) rmap = route_map_lookup_by_name(rmap_name); if (rmap) { - ret = route_map_apply(rmap, p, &nh_obj); + ret = route_map_apply(rmap, p, &rm_obj); } return (ret); @@ -1851,21 +1837,17 @@ route_map_result_t zebra_nht_route_map_check(afi_t afi, int client_proto, { struct route_map *rmap = NULL; route_map_result_t ret = RMAP_PERMITMATCH; - struct nh_rmap_obj nh_obj; + struct zebra_rmap_obj rm_obj; - nh_obj.nexthop = nexthop; - nh_obj.vrf_id = nexthop->vrf_id; - nh_obj.source_protocol = re->type; - nh_obj.instance = re->instance; - nh_obj.metric = re->metric; - nh_obj.tag = re->tag; + rm_obj.nexthop = nexthop; + rm_obj.re = re; if (client_proto >= 0 && client_proto < ZEBRA_ROUTE_MAX) rmap = NHT_RM_MAP(zvrf, afi, client_proto); if (!rmap && NHT_RM_MAP(zvrf, afi, ZEBRA_ROUTE_MAX)) rmap = NHT_RM_MAP(zvrf, afi, ZEBRA_ROUTE_MAX); if (rmap) - ret = route_map_apply(rmap, p, &nh_obj); + ret = route_map_apply(rmap, p, &rm_obj); return ret; } diff --git a/zebra/zebra_routemap.h b/zebra/zebra_routemap.h index f77735edc2..fceb53c841 100644 --- a/zebra/zebra_routemap.h +++ b/zebra/zebra_routemap.h @@ -21,19 +21,19 @@ extern void zebra_add_import_table_route_map(afi_t afi, const char *rmap_name, uint32_t table); extern void zebra_del_import_table_route_map(afi_t afi, uint32_t table); -extern route_map_result_t -zebra_import_table_route_map_check(int family, int rib_type, uint8_t instance, - const struct prefix *p, - struct nexthop *nexthop, vrf_id_t vrf_id, - route_tag_t tag, const char *rmap_name); -extern route_map_result_t -zebra_route_map_check(afi_t family, int rib_type, uint8_t instance, - const struct prefix *p, struct nexthop *nexthop, - struct zebra_vrf *zvrf, route_tag_t tag); -extern route_map_result_t -zebra_nht_route_map_check(afi_t afi, int client_proto, const struct prefix *p, - struct zebra_vrf *zvrf, struct route_entry *, - struct nexthop *nexthop); +extern route_map_result_t zebra_import_table_route_map_check( + int family, struct route_entry *re, const struct prefix *p, + struct nexthop *nexthop, const char *rmap_name); +extern route_map_result_t zebra_route_map_check(afi_t family, + struct route_entry *re, + const struct prefix *p, + struct nexthop *nexthop, + struct zebra_vrf *zvrf); +extern route_map_result_t zebra_nht_route_map_check(afi_t afi, int client_proto, + const struct prefix *p, + struct zebra_vrf *zvrf, + struct route_entry *re, + struct nexthop *nexthop); extern void zebra_routemap_vrf_delete(struct zebra_vrf *zvrf); |
