diff --git a/pbrd/pbr_map.c b/pbrd/pbr_map.c index e45e629649..7928b8e2e7 100644 --- a/pbrd/pbr_map.c +++ b/pbrd/pbr_map.c @@ -145,7 +145,7 @@ static bool pbr_map_interface_is_valid(const struct pbr_map_interface *pmi) } static void pbr_map_pbrms_update_common(struct pbr_map_sequence *pbrms, - bool install) + bool install, bool changed) { struct pbr_map *pbrm; struct listnode *node; @@ -161,19 +161,19 @@ static void pbr_map_pbrms_update_common(struct pbr_map_sequence *pbrms, if (install && !pbr_map_interface_is_valid(pmi)) continue; - pbr_send_pbr_map(pbrms, pmi, install); + pbr_send_pbr_map(pbrms, pmi, install, changed); } } } -static void pbr_map_pbrms_install(struct pbr_map_sequence *pbrms) +static void pbr_map_pbrms_install(struct pbr_map_sequence *pbrms, bool changed) { - pbr_map_pbrms_update_common(pbrms, true); + pbr_map_pbrms_update_common(pbrms, true, changed); } static void pbr_map_pbrms_uninstall(struct pbr_map_sequence *pbrms) { - pbr_map_pbrms_update_common(pbrms, false); + pbr_map_pbrms_update_common(pbrms, false, false); } static const char *const pbr_map_reason_str[] = { @@ -292,7 +292,7 @@ void pbr_map_policy_interface_update(const struct interface *ifp, bool state_up) for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) if (pmi->ifp == ifp && pbr_map_interface_is_valid(pmi)) - pbr_send_pbr_map(pbrms, pmi, state_up); + pbr_send_pbr_map(pbrms, pmi, state_up, false); } static void pbrms_vrf_update(struct pbr_map_sequence *pbrms, @@ -306,7 +306,7 @@ static void pbrms_vrf_update(struct pbr_map_sequence *pbrms, DEBUGD(&pbr_dbg_map, "\tSeq %u uses vrf %s (%u), updating map", pbrms->seqno, vrf_name, pbr_vrf_id(pbr_vrf)); - pbr_map_check(pbrms); + pbr_map_check(pbrms, false); } } @@ -360,7 +360,7 @@ extern void pbr_map_delete(struct pbr_map_sequence *pbrms) pbrm = pbrms->parent; for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) - pbr_send_pbr_map(pbrms, pmi, false); + pbr_send_pbr_map(pbrms, pmi, false, false); if (pbrms->nhg) pbr_nht_delete_individual_nexthop(pbrms); @@ -384,7 +384,7 @@ static void pbr_map_delete_common(struct pbr_map_sequence *pbrms) pbrm->valid = false; pbrms->nhs_installed = false; pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS; - pbrms->nhgrp_name = NULL; + XFREE(MTYPE_TMP, pbrms->nhgrp_name); } void pbr_map_delete_nexthops(struct pbr_map_sequence *pbrms) @@ -619,7 +619,7 @@ void pbr_map_schedule_policy_from_nhg(const char *nh_group) && (strcmp(nh_group, pbrms->nhgrp_name) == 0)) { pbrms->nhs_installed = true; - pbr_map_check(pbrms); + pbr_map_check(pbrms, false); } if (pbrms->nhg @@ -627,7 +627,7 @@ void pbr_map_schedule_policy_from_nhg(const char *nh_group) == 0)) { pbrms->nhs_installed = true; - pbr_map_check(pbrms); + pbr_map_check(pbrms, false); } } } @@ -656,7 +656,8 @@ void pbr_map_policy_install(const char *name) pbrms->seqno); for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi)) if (pbr_map_interface_is_valid(pmi)) - pbr_send_pbr_map(pbrms, pmi, true); + pbr_send_pbr_map(pbrms, pmi, true, + false); } } } @@ -668,7 +669,7 @@ void pbr_map_policy_delete(struct pbr_map *pbrm, struct pbr_map_interface *pmi) for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) - pbr_send_pbr_map(pbrms, pmi, false); + pbr_send_pbr_map(pbrms, pmi, false, false); pmi->delete = true; } @@ -710,13 +711,13 @@ void pbr_map_check_nh_group_change(const char *nh_group) pbrm->incoming, inode, pmi)) pbr_send_pbr_map(pbrms, pmi, - false); + false, false); } } } } -void pbr_map_check(struct pbr_map_sequence *pbrms) +void pbr_map_check(struct pbr_map_sequence *pbrms, bool changed) { struct pbr_map *pbrm; bool install; @@ -741,7 +742,7 @@ void pbr_map_check(struct pbr_map_sequence *pbrms) } if (install) - pbr_map_pbrms_install(pbrms); + pbr_map_pbrms_install(pbrms, changed); else pbr_map_pbrms_uninstall(pbrms); } @@ -755,7 +756,7 @@ void pbr_map_install(struct pbr_map *pbrm) return; for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) - pbr_map_pbrms_install(pbrms); + pbr_map_pbrms_install(pbrms, false); } void pbr_map_init(void) diff --git a/pbrd/pbr_map.h b/pbrd/pbr_map.h index 8bd22cbf2a..41f1703954 100644 --- a/pbrd/pbr_map.h +++ b/pbrd/pbr_map.h @@ -182,7 +182,15 @@ extern void pbr_map_init(void); extern bool pbr_map_check_valid(const char *name); -extern void pbr_map_check(struct pbr_map_sequence *pbrms); +/** + * Re-check the pbr map for validity. + * + * Install if valid, remove if not. + * + * If changed is set, the config on the on the map has changed somewhere + * and the rules need to be replaced if valid. + */ +extern void pbr_map_check(struct pbr_map_sequence *pbrms, bool changed); extern void pbr_map_check_nh_group_change(const char *nh_group); extern void pbr_map_reason_string(unsigned int reason, char *buf, int size); diff --git a/pbrd/pbr_nht.c b/pbrd/pbr_nht.c index ecd375333c..2f3591ac8d 100644 --- a/pbrd/pbr_nht.c +++ b/pbrd/pbr_nht.c @@ -510,12 +510,26 @@ char *pbr_nht_nexthop_make_name(char *name, size_t l, return buffer; } -void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms) +void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms, + const struct nexthop *nhop) { struct pbr_nexthop_group_cache *pnhgc; struct pbr_nexthop_group_cache find; struct pbr_nexthop_cache *pnhc; struct pbr_nexthop_cache lookup; + struct nexthop *nh; + char buf[PBR_NHC_NAMELEN]; + + pbrms->nhg = nexthop_group_new(); + pbrms->internal_nhg_name = XSTRDUP( + MTYPE_TMP, + pbr_nht_nexthop_make_name(pbrms->parent->name, PBR_NHC_NAMELEN, + pbrms->seqno, buf)); + + nh = nexthop_new(); + memcpy(nh, nhop, sizeof(*nh)); + + nexthop_group_add_sorted(pbrms->nhg, nh); memset(&find, 0, sizeof(find)); pbr_nht_nexthop_make_name(pbrms->parent->name, PBR_NHC_NAMELEN, @@ -539,7 +553,7 @@ void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms) pbr_nht_install_nexthop_group(pnhgc, *pbrms->nhg); } -void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence *pbrms) +static void pbr_nht_release_individual_nexthop(struct pbr_map_sequence *pbrms) { struct pbr_nexthop_group_cache *pnhgc; struct pbr_nexthop_group_cache find; @@ -548,8 +562,6 @@ void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence *pbrms) struct nexthop *nh; enum nexthop_types_t nh_type = 0; - pbr_map_delete_nexthops(pbrms); - memset(&find, 0, sizeof(find)); snprintf(find.name, sizeof(find.name), "%s", pbrms->internal_nhg_name); pnhgc = hash_lookup(pbr_nhg_hash, &find); @@ -564,11 +576,19 @@ void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence *pbrms) pbr_nht_uninstall_nexthop_group(pnhgc, *pbrms->nhg, nh_type); hash_release(pbr_nhg_hash, pnhgc); + pbr_nhgc_delete(pnhgc); nexthop_group_delete(&pbrms->nhg); XFREE(MTYPE_TMP, pbrms->internal_nhg_name); } +void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence *pbrms) +{ + pbr_map_delete_nexthops(pbrms); + + pbr_nht_release_individual_nexthop(pbrms); +} + struct pbr_nexthop_group_cache *pbr_nht_add_group(const char *name) { struct nexthop *nhop; diff --git a/pbrd/pbr_nht.h b/pbrd/pbr_nht.h index 4ef41cede7..2533942547 100644 --- a/pbrd/pbr_nht.h +++ b/pbrd/pbr_nht.h @@ -88,7 +88,8 @@ extern struct pbr_nexthop_group_cache *pbr_nht_add_group(const char *name); extern void pbr_nht_change_group(const char *name); extern void pbr_nht_delete_group(const char *name); -extern void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms); +extern void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms, + const struct nexthop *nhop); extern void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence *pbrms); /* * Given the tableid of the installed default diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index dfc8bec1bc..4a3a9ca382 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -142,18 +142,14 @@ DEFPY(pbr_map_match_src, pbr_map_match_src_cmd, if (pbrms->src) { if (prefix_same(pbrms->src, prefix)) return CMD_SUCCESS; + } else + pbrms->src = prefix_new(); - vty_out(vty, - "A `match src-ip XX` command already exists, please remove that first\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - pbrms->src = prefix_new(); prefix_copy(pbrms->src, prefix); } else prefix_free(&pbrms->src); - pbr_map_check(pbrms); + pbr_map_check(pbrms, true); return CMD_SUCCESS; } @@ -174,18 +170,14 @@ DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd, if (pbrms->dst) { if (prefix_same(pbrms->dst, prefix)) return CMD_SUCCESS; + } else + pbrms->dst = prefix_new(); - vty_out(vty, - "A `match dst-ip XX` command already exists, please remove that first\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - pbrms->dst = prefix_new(); prefix_copy(pbrms->dst, prefix); } else prefix_free(&pbrms->dst); - pbr_map_check(pbrms); + pbr_map_check(pbrms, true); return CMD_SUCCESS; } @@ -205,30 +197,52 @@ DEFPY(pbr_map_match_mark, pbr_map_match_mark_cmd, #endif if (!no) { - if (pbrms->mark) { + if (pbrms->mark) if (pbrms->mark == (uint32_t)mark) return CMD_SUCCESS; - vty_out(vty, - "A `match mark XX` command already exists, please remove that first\n"); - return CMD_WARNING_CONFIG_FAILED; - } - pbrms->mark = (uint32_t)mark; } else pbrms->mark = 0; - pbr_map_check(pbrms); + pbr_map_check(pbrms, true); return CMD_SUCCESS; } -#define SET_VRF_EXISTS_STR \ - "A `set vrf XX` command already exists, please remove that first\n" +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; +} DEFPY(pbr_map_nexthop_group, pbr_map_nexthop_group_cmd, - "[no] set nexthop-group NHGNAME$name", - NO_STR + "set nexthop-group NHGNAME$name", "Set for the PBR-MAP\n" "nexthop-group to use\n" "The name of the nexthop-group\n") @@ -236,17 +250,6 @@ DEFPY(pbr_map_nexthop_group, pbr_map_nexthop_group_cmd, struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); struct nexthop_group_cmd *nhgc; - if (pbrms->nhg) { - vty_out(vty, - "A `set nexthop XX` command already exists, please remove that first\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - if (pbrms->vrf_lookup || pbrms->vrf_unchanged) { - vty_out(vty, SET_VRF_EXISTS_STR); - return CMD_WARNING_CONFIG_FAILED; - } - nhgc = nhgc_find(name); if (!nhgc) { vty_out(vty, "Specified nexthop-group %s does not exist\n", @@ -255,40 +258,39 @@ DEFPY(pbr_map_nexthop_group, pbr_map_nexthop_group_cmd, "PBR-MAP will not be applied until it is created\n"); } - if (no) { - if (pbrms->nhgrp_name && strcmp(name, pbrms->nhgrp_name) == 0) - pbr_map_delete_nexthops(pbrms); - else { - vty_out(vty, - "Nexthop Group specified: %s does not exist to remove\n", - name); - return CMD_WARNING_CONFIG_FAILED; - } - } else { - if (pbrms->nhgrp_name) { - if (strcmp(name, pbrms->nhgrp_name) != 0) { - vty_out(vty, - "Please delete current nexthop group before modifying current one\n"); - return CMD_WARNING_CONFIG_FAILED; - } + if (pbrms->nhgrp_name && strcmp(name, pbrms->nhgrp_name) == 0) + return CMD_SUCCESS; - return CMD_SUCCESS; - } - pbrms->nhgrp_name = XSTRDUP(MTYPE_TMP, name); - pbr_map_check(pbrms); - } + /* This is new/replacement config */ + pbrms_clear_set_config(pbrms); + + pbrms->nhgrp_name = XSTRDUP(MTYPE_TMP, name); + pbr_map_check(pbrms, true); + + return CMD_SUCCESS; +} + +DEFPY(no_pbr_map_nexthop_group, no_pbr_map_nexthop_group_cmd, + "no set nexthop-group [NHGNAME$name]", + NO_STR + "Set for the PBR-MAP\n" + "nexthop-group to use\n" + "The name of the nexthop-group\n") +{ + struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + + pbrms_clear_set_config(pbrms); return CMD_SUCCESS; } DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd, - "[no] set nexthop\ + "set nexthop\ <\ $addr [INTERFACE$intf]\ |INTERFACE$intf\ >\ [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" @@ -301,18 +303,7 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd, struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); struct vrf *vrf; struct nexthop nhop; - struct nexthop *nh; - - if (pbrms->nhgrp_name) { - vty_out(vty, - "Please unconfigure the nexthop group before adding an individual nexthop\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - if (pbrms->vrf_lookup || pbrms->vrf_unchanged) { - vty_out(vty, SET_VRF_EXISTS_STR); - return CMD_WARNING_CONFIG_FAILED; - } + struct nexthop *nh = NULL; if (vrf_name) vrf = vrf_lookup_by_name(vrf_name); @@ -362,45 +353,18 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd, if (pbrms->nhg) nh = nexthop_exists(pbrms->nhg, &nhop); - else { - char buf[PBR_NHC_NAMELEN]; - if (no) { - vty_out(vty, "No nexthops to delete\n"); - return CMD_WARNING_CONFIG_FAILED; - } + if (nh) /* Same config re-entered */ + goto done; - pbrms->nhg = nexthop_group_new(); - pbrms->internal_nhg_name = - XSTRDUP(MTYPE_TMP, - pbr_nht_nexthop_make_name(pbrms->parent->name, - PBR_NHC_NAMELEN, - pbrms->seqno, - buf)); - nh = NULL; - } + /* This is new/replacement config */ + pbrms_clear_set_config(pbrms); - if (no) { - if (nh) - pbr_nht_delete_individual_nexthop(pbrms); - } else if (!nh) { + pbr_nht_add_individual_nexthop(pbrms, &nhop); - if (pbrms->nhg->nexthop) { - vty_out(vty, - "If you would like more than one nexthop please use nexthop-groups\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - /* must be adding new nexthop since !no and !nexthop_exists */ - nh = nexthop_new(); - - memcpy(nh, &nhop, sizeof(nhop)); - _nexthop_add(&pbrms->nhg->nexthop, nh); - - pbr_nht_add_individual_nexthop(pbrms); - pbr_map_check(pbrms); - } + pbr_map_check(pbrms, true); +done: if (nhop.type == NEXTHOP_TYPE_IFINDEX || (nhop.type == NEXTHOP_TYPE_IPV6_IFINDEX && IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6))) { @@ -414,8 +378,69 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd, return CMD_SUCCESS; } +DEFPY(no_pbr_map_nexthop, no_pbr_map_nexthop_cmd, + "no set nexthop\ + [<\ + $addr [INTERFACE$intf]\ + |INTERFACE$intf\ + >\ + [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") +{ + struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + + pbrms_clear_set_config(pbrms); + + return CMD_SUCCESS; +} + DEFPY(pbr_map_vrf, pbr_map_vrf_cmd, - "[no] set vrf ", + "set vrf ", + "Set for the PBR-MAP\n" + "Specify the VRF for this map\n" + "The VRF Name\n" + "Use the interface's VRF for lookup\n") +{ + struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + + /* + * 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) + return CMD_SUCCESS; + else if (!vrf_name && pbrms->vrf_unchanged) /* Unchanged already set */ + return CMD_SUCCESS; + + if (vrf_name && !pbr_vrf_lookup_by_name(vrf_name)) { + vty_out(vty, "Specified: %s is non-existent\n", vrf_name); + return CMD_WARNING_CONFIG_FAILED; + } + + /* This is new/replacement config */ + pbrms_clear_set_config(pbrms); + + if (vrf_name) { + pbrms->vrf_lookup = true; + strlcpy(pbrms->vrf_name, vrf_name, sizeof(pbrms->vrf_name)); + } else + pbrms->vrf_unchanged = true; + + pbr_map_check(pbrms, true); + + return CMD_SUCCESS; +} + +DEFPY(no_pbr_map_vrf, no_pbr_map_vrf_cmd, + "no set vrf []", NO_STR "Set for the PBR-MAP\n" "Specify the VRF for this map\n" @@ -424,74 +449,9 @@ DEFPY(pbr_map_vrf, pbr_map_vrf_cmd, { struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); - if (no) { - pbr_map_delete_vrf(pbrms); - - /* Reset all data */ - pbrms->nhs_installed = false; - pbrms->vrf_name[0] = '\0'; - pbrms->vrf_lookup = false; - pbrms->vrf_unchanged = false; - - return CMD_SUCCESS; - } - - if (pbrms->nhgrp_name || pbrms->nhg) { - vty_out(vty, - "A `set nexthop/nexthop-group XX` command already exits, please remove that first\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - /* - * Determine if a set vrf * command already exists. - * - * If its equivalent, just return success. - * - * Else, return failure, we don't allow atomic swaps yet. - */ - if (vrf_name && pbrms->vrf_lookup) { - /* New vrf specified and one already exists */ - - /* Is this vrf different from one already configured? */ - if (strncmp(pbrms->vrf_name, vrf_name, sizeof(pbrms->vrf_name)) - != 0) - goto vrf_exists; - - return CMD_SUCCESS; - - } else if (!vrf_name && pbrms->vrf_unchanged) { - /* Unchanged specified and unchanged already exists */ - return CMD_SUCCESS; - - } else if (vrf_name && pbrms->vrf_unchanged) { - /* New vrf specified and unchanged is already set */ - goto vrf_exists; - - } else if (!vrf_name && pbrms->vrf_lookup) { - /* Unchanged specified and vrf to lookup already exists */ - goto vrf_exists; - } - - /* Create new lookup VRF or Unchanged */ - if (vrf_name) { - if (!pbr_vrf_lookup_by_name(vrf_name)) { - vty_out(vty, "Specified: %s is non-existent\n", - vrf_name); - return CMD_WARNING_CONFIG_FAILED; - } - - pbrms->vrf_lookup = true; - strlcpy(pbrms->vrf_name, vrf_name, sizeof(pbrms->vrf_name)); - } else - pbrms->vrf_unchanged = true; - - pbr_map_check(pbrms); + pbrms_clear_set_config(pbrms); return CMD_SUCCESS; - -vrf_exists: - vty_out(vty, SET_VRF_EXISTS_STR); - return CMD_WARNING_CONFIG_FAILED; } DEFPY (pbr_policy, @@ -897,8 +857,11 @@ void pbr_vty_init(void) install_element(PBRMAP_NODE, &pbr_map_match_dst_cmd); install_element(PBRMAP_NODE, &pbr_map_match_mark_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); + install_element(PBRMAP_NODE, &no_pbr_map_nexthop_cmd); install_element(PBRMAP_NODE, &pbr_map_vrf_cmd); + install_element(PBRMAP_NODE, &no_pbr_map_vrf_cmd); install_element(VIEW_NODE, &show_pbr_cmd); install_element(VIEW_NODE, &show_pbr_map_cmd); install_element(VIEW_NODE, &show_pbr_interface_cmd); diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c index c2d93a405e..de2a99e269 100644 --- a/pbrd/pbr_zebra.c +++ b/pbrd/pbr_zebra.c @@ -548,7 +548,7 @@ static void pbr_encode_pbr_map_sequence(struct stream *s, } void pbr_send_pbr_map(struct pbr_map_sequence *pbrms, - struct pbr_map_interface *pmi, bool install) + struct pbr_map_interface *pmi, bool install, bool changed) { struct pbr_map *pbrm = pbrms->parent; struct stream *s; @@ -560,11 +560,13 @@ void pbr_send_pbr_map(struct pbr_map_sequence *pbrms, pbrm->name, install, is_installed); /* - * If we are installed and asked to do so again - * just return. If we are not installed and asked - * and asked to delete just return; + * If we are installed and asked to do so again and the config + * has not changed, just return. + * + * If we are not installed and asked + * to delete just return. */ - if (install && is_installed) + if (install && is_installed && !changed) return; if (!install && !is_installed) @@ -582,9 +584,9 @@ void pbr_send_pbr_map(struct pbr_map_sequence *pbrms, */ stream_putl(s, 1); - DEBUGD(&pbr_dbg_zebra, "%s: \t%s %s %d %s %u", __func__, - install ? "Installing" : "Deleting", pbrm->name, install, - pmi->ifp->name, pmi->delete); + DEBUGD(&pbr_dbg_zebra, "%s: \t%s %s seq %u %d %s %u", __func__, + install ? "Installing" : "Deleting", pbrm->name, pbrms->seqno, + install, pmi->ifp->name, pmi->delete); pbr_encode_pbr_map_sequence(s, pbrms, pmi->ifp); diff --git a/pbrd/pbr_zebra.h b/pbrd/pbr_zebra.h index d5d938021a..cc42e21abe 100644 --- a/pbrd/pbr_zebra.h +++ b/pbrd/pbr_zebra.h @@ -36,7 +36,8 @@ extern void route_delete(struct pbr_nexthop_group_cache *pnhgc, extern void pbr_send_rnh(struct nexthop *nhop, bool reg); extern void pbr_send_pbr_map(struct pbr_map_sequence *pbrms, - struct pbr_map_interface *pmi, bool install); + struct pbr_map_interface *pmi, bool install, + bool changed); extern struct pbr_interface *pbr_if_new(struct interface *ifp); diff --git a/zebra/rule_netlink.c b/zebra/rule_netlink.c index c9699c7d95..07e1902f65 100644 --- a/zebra/rule_netlink.c +++ b/zebra/rule_netlink.c @@ -173,12 +173,41 @@ enum zebra_dplane_result kernel_del_pbr_rule(struct zebra_pbr_rule *rule) return ZEBRA_DPLANE_REQUEST_SUCCESS; } +/* + * Update specified rule for a specific interface. + */ +enum zebra_dplane_result kernel_update_pbr_rule(struct zebra_pbr_rule *old_rule, + struct zebra_pbr_rule *new_rule) +{ + int ret = 0; + + /* Add the new, updated one */ + ret = netlink_rule_update(RTM_NEWRULE, new_rule); + + /** + * Delete the old one. + * + * Don't care about this result right? + */ + netlink_rule_update(RTM_DELRULE, old_rule); + + kernel_pbr_rule_add_del_status(new_rule, + (!ret) ? ZEBRA_DPLANE_INSTALL_SUCCESS + : ZEBRA_DPLANE_INSTALL_FAILURE); + + return ZEBRA_DPLANE_REQUEST_SUCCESS; +} + /* * Handle netlink notification informing a rule add or delete. * Handling of an ADD is TBD. * DELs are notified up, if other attributes indicate it may be a * notification of interest. The expectation is that if this corresponds * to a PBR rule added by FRR, it will be readded. + * + * If startup and we see a rule we created, delete it as its leftover + * from a previous instance and should have been removed on shutdown. + * */ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) { @@ -190,15 +219,12 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) struct zebra_pbr_rule rule = {}; char buf1[PREFIX_STRLEN]; char buf2[PREFIX_STRLEN]; + uint8_t proto = 0; /* Basic validation followed by extracting attributes. */ if (h->nlmsg_type != RTM_NEWRULE && h->nlmsg_type != RTM_DELRULE) return 0; - /* TBD */ - if (h->nlmsg_type == RTM_NEWRULE) - return 0; - len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct fib_rule_hdr)); if (len < 0) { zlog_err( @@ -222,19 +248,6 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) memset(tb, 0, sizeof(tb)); netlink_parse_rtattr(tb, FRA_MAX, RTM_RTA(frh), len); - /* TBD: We don't care about rules not specifying an IIF. */ - if (tb[FRA_IFNAME] == NULL) - return 0; - - ifname = (char *)RTA_DATA(tb[FRA_IFNAME]); - zns = zebra_ns_lookup(ns_id); - - /* If we don't know the interface, we don't care. */ - if (!if_lookup_by_name_per_ns(zns, ifname)) - return 0; - - strlcpy(rule.ifname, ifname, sizeof(rule.ifname)); - if (tb[FRA_PRIORITY]) rule.rule.priority = *(uint32_t *)RTA_DATA(tb[FRA_PRIORITY]); @@ -246,6 +259,7 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) memcpy(&rule.rule.filter.src_ip.u.prefix6, RTA_DATA(tb[FRA_SRC]), 16); rule.rule.filter.src_ip.prefixlen = frh->src_len; + rule.rule.filter.src_ip.family = frh->family; rule.rule.filter.filter_bm |= PBR_FILTER_SRC_IP; } @@ -257,6 +271,7 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) memcpy(&rule.rule.filter.dst_ip.u.prefix6, RTA_DATA(tb[FRA_DST]), 16); rule.rule.filter.dst_ip.prefixlen = frh->dst_len; + rule.rule.filter.dst_ip.family = frh->family; rule.rule.filter.filter_bm |= PBR_FILTER_DST_IP; } @@ -265,6 +280,49 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) else rule.rule.action.table = frh->table; + /* TBD: We don't care about rules not specifying an IIF. */ + if (tb[FRA_IFNAME] == NULL) + return 0; + + if (tb[FRA_PROTOCOL]) + proto = *(uint8_t *)RTA_DATA(tb[FRA_PROTOCOL]); + + ifname = (char *)RTA_DATA(tb[FRA_IFNAME]); + strlcpy(rule.ifname, ifname, sizeof(rule.ifname)); + + if (h->nlmsg_type == RTM_NEWRULE) { + /* + * If we see a rule at startup we created, delete it now. + * It should have been flushed on a previous shutdown. + */ + if (startup && proto == RTPROT_ZEBRA) { + int ret; + + ret = netlink_rule_update(RTM_DELRULE, &rule); + + zlog_debug( + "%s: %s leftover rule: family %s IF %s(%u) Pref %u Src %s Dst %s Table %u", + __func__, + ((ret == 0) ? "Removed" : "Failed to remove"), + nl_family_to_str(frh->family), rule.ifname, + rule.rule.ifindex, rule.rule.priority, + prefix2str(&rule.rule.filter.src_ip, buf1, + sizeof(buf1)), + prefix2str(&rule.rule.filter.dst_ip, buf2, + sizeof(buf2)), + rule.rule.action.table); + } + + /* TBD */ + return 0; + } + + zns = zebra_ns_lookup(ns_id); + + /* If we don't know the interface, we don't care. */ + if (!if_lookup_by_name_per_ns(zns, ifname)) + return 0; + if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( "Rx %s family %s IF %s(%u) Pref %u Src %s Dst %s Table %u", @@ -280,13 +338,52 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) return kernel_pbr_rule_del(&rule); } +/* + * Request rules from the kernel + */ +static int netlink_request_rules(struct zebra_ns *zns, int family, int type) +{ + struct { + struct nlmsghdr n; + struct fib_rule_hdr frh; + char buf[NL_PKT_BUF_SIZE]; + } req; + + memset(&req, 0, sizeof(req)); + req.n.nlmsg_type = type; + req.n.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct fib_rule_hdr)); + req.frh.family = family; + + return netlink_request(&zns->netlink_cmd, &req.n); +} + /* * Get to know existing PBR rules in the kernel - typically called at startup. - * TBD. */ int netlink_rules_read(struct zebra_ns *zns) { - return 0; + int ret; + struct zebra_dplane_info dp_info; + + zebra_dplane_info_from_zns(&dp_info, zns, true); + + ret = netlink_request_rules(zns, AF_INET, RTM_GETRULE); + if (ret < 0) + return ret; + + ret = netlink_parse_info(netlink_rule_change, &zns->netlink_cmd, + &dp_info, 0, 1); + if (ret < 0) + return ret; + + ret = netlink_request_rules(zns, AF_INET6, RTM_GETRULE); + if (ret < 0) + return ret; + + ret = netlink_parse_info(netlink_rule_change, &zns->netlink_cmd, + &dp_info, 0, 1); + return ret; } #endif /* HAVE_NETLINK */ diff --git a/zebra/rule_socket.c b/zebra/rule_socket.c index e2c650b4ad..219fa7de6f 100644 --- a/zebra/rule_socket.c +++ b/zebra/rule_socket.c @@ -57,4 +57,12 @@ enum zebra_dplane_result kernel_del_pbr_rule(struct zebra_pbr_rule *rule) return ZEBRA_DPLANE_REQUEST_FAILURE; } +enum zebra_dplane_result kernel_update_pbr_rule(struct zebra_pbr_rule *old_rule, + struct zebra_pbr_rule *new_rule) +{ + flog_err(EC_LIB_UNAVAILABLE, "%s not Implemented for this platform", + __PRETTY_FUNCTION__); + return ZEBRA_DPLANE_REQUEST_FAILURE; +} + #endif diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 3287176ef5..4e51437337 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -126,6 +126,7 @@ int zebra_ns_enable(ns_id_t ns_id, void **info) kernel_init(zns); interface_list(zns); route_read(zns); + kernel_read_pbr_rules(zns); /* Initiate Table Manager per ZNS */ table_manager_enable(ns_id); diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c index 6728567e6e..c049aa14f6 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -431,32 +431,51 @@ static void *pbr_rule_alloc_intern(void *arg) return new; } -void zebra_pbr_add_rule(struct zebra_pbr_rule *rule) -{ - struct zebra_pbr_rule *unique = - pbr_rule_lookup_unique(rule); - - (void)hash_get(zrouter.rules_hash, rule, pbr_rule_alloc_intern); - (void)kernel_add_pbr_rule(rule); - /* - * Rule Replace semantics, if we have an old, install the - * new rule, look above, and then delete the old - */ - if (unique) - zebra_pbr_del_rule(unique); -} - -void zebra_pbr_del_rule(struct zebra_pbr_rule *rule) +static int pbr_rule_release(struct zebra_pbr_rule *rule) { struct zebra_pbr_rule *lookup; lookup = hash_lookup(zrouter.rules_hash, rule); + + if (!lookup) + return -ENOENT; + + hash_release(zrouter.rules_hash, lookup); + XFREE(MTYPE_TMP, lookup); + + return 0; +} + +void zebra_pbr_add_rule(struct zebra_pbr_rule *rule) +{ + struct zebra_pbr_rule *found; + + /** + * Check if we already have it (this checks via a unique ID, walking + * over the hash table, not via a hash operation). + */ + found = pbr_rule_lookup_unique(rule); + + (void)hash_get(zrouter.rules_hash, rule, pbr_rule_alloc_intern); + + /* If found, this is an update */ + if (found) { + (void)kernel_update_pbr_rule(found, rule); + + if (pbr_rule_release(found)) + zlog_debug( + "%s: Rule being updated we know nothing about", + __PRETTY_FUNCTION__); + + } else + (void)kernel_add_pbr_rule(rule); +} + +void zebra_pbr_del_rule(struct zebra_pbr_rule *rule) +{ (void)kernel_del_pbr_rule(rule); - if (lookup) { - hash_release(zrouter.rules_hash, lookup); - XFREE(MTYPE_TMP, lookup); - } else + if (pbr_rule_release(rule)) zlog_debug("%s: Rule being deleted we know nothing about", __func__); } diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h index b7fbc9b7d5..83797b9521 100644 --- a/zebra/zebra_pbr.h +++ b/zebra/zebra_pbr.h @@ -182,6 +182,13 @@ extern enum zebra_dplane_result kernel_add_pbr_rule(struct zebra_pbr_rule *rule) */ extern enum zebra_dplane_result kernel_del_pbr_rule(struct zebra_pbr_rule *rule); +/* + * Update specified rule for a specific interface. + */ +extern enum zebra_dplane_result +kernel_update_pbr_rule(struct zebra_pbr_rule *old_rule, + struct zebra_pbr_rule *new_rule); + /* * Get to know existing PBR rules in the kernel - typically called at startup. */