zebra: convert interface evpn mh es-id commands to NB

Signed-off-by: Igor Ryzhov <iryzhov@nfware.com>
This commit is contained in:
Igor Ryzhov 2024-01-22 21:34:35 +02:00
parent be712fc697
commit a6db1c14a4
9 changed files with 361 additions and 159 deletions

View File

@ -1019,6 +1019,13 @@ void yang_str2mac(const char *value, struct ethaddr *mac)
(void)prefix_str2mac(value, mac);
}
void yang_dnode_get_mac(struct ethaddr *mac, const struct lyd_node *dnode,
const char *xpath_fmt, ...)
{
const char *canon = YANG_DNODE_XPATH_GET_CANON(dnode, xpath_fmt);
(void)prefix_str2mac(canon, mac);
}
struct yang_data *yang_data_new_date_and_time(const char *xpath, time_t time)
{
struct tm tm;

View File

@ -195,6 +195,8 @@ extern void yang_get_default_ip(struct ipaddr *var, const char *xpath_fmt, ...)
extern struct yang_data *yang_data_new_mac(const char *xpath,
const struct ethaddr *mac);
extern void yang_str2mac(const char *value, struct ethaddr *mac);
extern void yang_dnode_get_mac(struct ethaddr *mac, const struct lyd_node *dnode,
const char *xpath_fmt, ...) PRINTFRR(3, 4);
/*data-and-time */
extern struct yang_data *yang_data_new_date_and_time(const char *xpath,

View File

@ -2217,6 +2217,35 @@ module frr-zebra {
// TODO -- other link-params options
// for (experimental/partial TE use in IGP extensions)
}
container evpn-mh {
description "EVPN multihoming configuration";
choice esi-choice {
description "ESI type";
container type-0 {
leaf esi {
type yang:hex-string {
length "29";
}
description
"10-octet ESI.";
}
}
container type-3 {
leaf system-mac {
type yang:mac-address;
description
"System MAC address.";
}
leaf local-discriminator {
type uint32 {
range "1..16777215";
}
description
"Local discriminator.";
}
}
}
}
container state {
config false;
description

View File

@ -28,7 +28,7 @@ man8 += $(MANBUILD)/frr-zebra.8
## endif ZEBRA
endif
zebra_zebra_LDADD = lib/libfrr.la $(LIBCAP) $(UST_LIBS)
zebra_zebra_LDADD = lib/libfrr.la $(LIBCAP) $(LIBYANG_LIBS) $(UST_LIBS)
if HAVE_PROTOBUF3
zebra_zebra_LDADD += mlag/libmlag_pb.la $(PROTOBUF_C_LIBS)
zebra/zebra_mlag.$(OBJEXT): mlag/mlag.pb-c.h

View File

@ -20,6 +20,7 @@
#include "table.h"
#include "vlan.h"
#include "vxlan.h"
#include "northbound_cli.h"
#include "zebra/zebra_router.h"
#include "zebra/debug.h"
@ -52,7 +53,7 @@ static void zebra_evpn_es_get_one_base_evpn(void);
static int zebra_evpn_es_evi_send_to_client(struct zebra_evpn_es *es,
struct zebra_evpn *zevpn, bool add);
static void zebra_evpn_local_es_del(struct zebra_evpn_es **esp);
static int zebra_evpn_local_es_update(struct zebra_if *zif, esi_t *esi);
static void zebra_evpn_local_es_update(struct zebra_if *zif);
static bool zebra_evpn_es_br_port_dplane_update(struct zebra_evpn_es *es,
const char *caller);
static void zebra_evpn_mh_uplink_cfg_update(struct zebra_if *zif, bool set);
@ -1139,7 +1140,7 @@ void zebra_evpn_if_init(struct zebra_if *zif)
/* if an es_id and sysmac are already present against the interface
* activate it
*/
zebra_evpn_local_es_update(zif, &zif->es_info.esi);
zebra_evpn_local_es_update(zif);
}
/* handle deletion of an access port by removing it from all associated
@ -2402,73 +2403,63 @@ static void zebra_evpn_es_remote_info_re_eval(struct zebra_evpn_es **esp)
}
}
void zebra_build_type3_esi(uint32_t lid, struct ethaddr *mac, esi_t *esi)
{
int offset = 0;
int field_bytes = 0;
/* build 10-byte type-3-ESI -
* Type(1-byte), MAC(6-bytes), ES-LID (3-bytes)
*/
field_bytes = 1;
esi->val[offset] = ESI_TYPE_MAC;
offset += field_bytes;
field_bytes = ETH_ALEN;
memcpy(&esi->val[offset], (uint8_t *)mac, field_bytes);
offset += field_bytes;
esi->val[offset++] = (uint8_t)(lid >> 16);
esi->val[offset++] = (uint8_t)(lid >> 8);
esi->val[offset++] = (uint8_t)lid;
}
/* A new local es is created when a local-es-id and sysmac is configured
* against an interface.
*/
static int zebra_evpn_local_es_update(struct zebra_if *zif, esi_t *esi)
static void zebra_evpn_local_es_update(struct zebra_if *zif)
{
struct zebra_evpn_es *old_es = zif->es_info.es;
struct zebra_evpn_es *es;
esi_t _esi, *esi;
if (!zebra_evpn_is_if_es_capable(zif))
return;
if (memcmp(&zif->es_info.esi, zero_esi, sizeof(*zero_esi))) {
esi = &zif->es_info.esi;
} else if (zif->es_info.lid && !is_zero_mac(&zif->es_info.sysmac)) {
zebra_build_type3_esi(zif->es_info.lid, &zif->es_info.sysmac,
&_esi);
esi = &_esi;
} else {
esi = zero_esi;
}
if (old_es && !memcmp(&old_es->esi, esi, sizeof(*esi)))
/* dup - nothing to be done */
return 0;
return;
/* release the old_es against the zif */
if (old_es)
zebra_evpn_local_es_del(&old_es);
es = zebra_evpn_es_find(esi);
if (es) {
/* if it exists against another interface flag an error */
if (es->zif && es->zif != zif)
return -1;
} else {
/* create new es */
if (!es)
es = zebra_evpn_es_new(esi);
}
memcpy(&zif->es_info.esi, esi, sizeof(*esi));
if (es)
zebra_evpn_es_local_info_set(es, zif);
return 0;
}
static int zebra_evpn_type3_esi_update(struct zebra_if *zif, uint32_t lid,
struct ethaddr *sysmac)
{
struct zebra_evpn_es *old_es = zif->es_info.es;
esi_t esi;
int offset = 0;
int field_bytes = 0;
/* Complete config of the ES-ID bootstraps the ES */
if (!lid || is_zero_mac(sysmac)) {
/* clear old esi */
memset(&zif->es_info.esi, 0, sizeof(zif->es_info.esi));
/* if in ES is attached to zif delete it */
if (old_es)
zebra_evpn_local_es_del(&old_es);
return 0;
}
/* build 10-byte type-3-ESI -
* Type(1-byte), MAC(6-bytes), ES-LID (3-bytes)
*/
field_bytes = 1;
esi.val[offset] = ESI_TYPE_MAC;
offset += field_bytes;
field_bytes = ETH_ALEN;
memcpy(&esi.val[offset], (uint8_t *)sysmac, field_bytes);
offset += field_bytes;
esi.val[offset++] = (uint8_t)(lid >> 16);
esi.val[offset++] = (uint8_t)(lid >> 8);
esi.val[offset++] = (uint8_t)lid;
return zebra_evpn_local_es_update(zif, &esi);
}
int zebra_evpn_remote_es_del(const esi_t *esi, struct in_addr vtep_ip)
@ -2673,44 +2664,33 @@ static int zebra_evpn_es_evi_send_to_client(struct zebra_evpn_es *es,
}
/* sysmac part of a local ESI has changed */
static int zebra_evpn_es_sys_mac_update(struct zebra_if *zif,
struct ethaddr *sysmac)
void zebra_evpn_es_sys_mac_update(struct zebra_if *zif, struct ethaddr *sysmac)
{
int rv;
rv = zebra_evpn_type3_esi_update(zif, zif->es_info.lid, sysmac);
if (!rv)
if (sysmac)
memcpy(&zif->es_info.sysmac, sysmac, sizeof(struct ethaddr));
else
memset(&zif->es_info.sysmac, 0, sizeof(struct ethaddr));
return rv;
zebra_evpn_local_es_update(zif);
}
/* local-ID part of ESI has changed */
static int zebra_evpn_es_lid_update(struct zebra_if *zif, uint32_t lid)
void zebra_evpn_es_lid_update(struct zebra_if *zif, uint32_t lid)
{
int rv;
zif->es_info.lid = lid;
rv = zebra_evpn_type3_esi_update(zif, lid, &zif->es_info.sysmac);
if (!rv)
zif->es_info.lid = lid;
return rv;
zebra_evpn_local_es_update(zif);
}
/* type-0 esi has changed */
static int zebra_evpn_es_type0_esi_update(struct zebra_if *zif, esi_t *esi)
void zebra_evpn_es_type0_esi_update(struct zebra_if *zif, esi_t *esi)
{
int rv;
if (esi)
memcpy(&zif->es_info.esi, esi, sizeof(*esi));
else
memset(&zif->es_info.esi, 0, sizeof(*esi));
rv = zebra_evpn_local_es_update(zif, esi);
/* clear the old es_lid, es_sysmac - type-0 is being set so old
* type-3 params need to be flushed
*/
memset(&zif->es_info.sysmac, 0, sizeof(struct ethaddr));
zif->es_info.lid = 0;
return rv;
zebra_evpn_local_es_update(zif);
}
void zebra_evpn_es_cleanup(void)
@ -3409,9 +3389,9 @@ DEFPY(zebra_evpn_es_pref, zebra_evpn_es_pref_cmd,
}
/* CLI for setting up sysmac part of ESI on an access port */
DEFPY(zebra_evpn_es_sys_mac,
DEFPY_YANG (zebra_evpn_es_sys_mac,
zebra_evpn_es_sys_mac_cmd,
"[no$no] evpn mh es-sys-mac [X:X:X:X:X:X$mac]",
"[no$no] evpn mh es-sys-mac ![X:X:X:X:X:X$mac]",
NO_STR
"EVPN\n"
EVPN_MH_VTY_STR
@ -3419,47 +3399,21 @@ DEFPY(zebra_evpn_es_sys_mac,
MAC_STR
)
{
VTY_DECLVAR_CONTEXT(interface, ifp);
struct zebra_if *zif;
int ret = 0;
zif = ifp->info;
if (no) {
static struct ethaddr zero_mac;
ret = zebra_evpn_es_sys_mac_update(zif, &zero_mac);
if (ret == -1) {
vty_out(vty, "%% Failed to clear ES sysmac\n");
return CMD_WARNING;
}
} else {
if (!zebra_evpn_is_if_es_capable(zif)) {
vty_out(vty,
"%% ESI cannot be associated with this interface type\n");
return CMD_WARNING;
}
if (!mac || is_zero_mac(&mac->eth_addr)) {
vty_out(vty, "%% ES sysmac value is invalid\n");
return CMD_WARNING;
}
ret = zebra_evpn_es_sys_mac_update(zif, &mac->eth_addr);
if (ret == -1) {
vty_out(vty,
"%% ESI already exists on a different interface\n");
return CMD_WARNING;
}
}
return CMD_SUCCESS;
if (!no)
nb_cli_enqueue_change(vty,
"./frr-zebra:zebra/evpn-mh/type-3/system-mac",
NB_OP_MODIFY, mac_str);
else
nb_cli_enqueue_change(vty,
"./frr-zebra:zebra/evpn-mh/type-3/system-mac",
NB_OP_DESTROY, NULL);
return nb_cli_apply_changes(vty, NULL);
}
/* CLI for setting up local-ID part of ESI on an access port */
DEFPY(zebra_evpn_es_id,
DEFPY_YANG (zebra_evpn_es_id,
zebra_evpn_es_id_cmd,
"[no$no] evpn mh es-id [(1-16777215)$es_lid | NAME$esi_str]",
"[no$no] evpn mh es-id ![(1-16777215)$es_lid | NAME$esi_str]",
NO_STR
"EVPN\n"
EVPN_MH_VTY_STR
@ -3468,53 +3422,25 @@ DEFPY(zebra_evpn_es_id,
"10-byte ID - 00:AA:BB:CC:DD:EE:FF:GG:HH:II\n"
)
{
VTY_DECLVAR_CONTEXT(interface, ifp);
struct zebra_if *zif;
int ret = 0;
esi_t esi;
zif = ifp->info;
if (no) {
if (zif->es_info.lid)
ret = zebra_evpn_es_lid_update(zif, 0);
else if (memcmp(&zif->es_info.esi, zero_esi, sizeof(*zero_esi)))
ret = zebra_evpn_es_type0_esi_update(zif, zero_esi);
if (ret == -1) {
vty_out(vty,
"%% Failed to clear ES local id or ESI name\n");
return CMD_WARNING;
}
/* We don't know which one is configured, so detroy both types. */
nb_cli_enqueue_change(vty,
"./frr-zebra:zebra/evpn-mh/type-0/esi",
NB_OP_DESTROY, NULL);
nb_cli_enqueue_change(vty,
"./frr-zebra:zebra/evpn-mh/type-3/local-discriminator",
NB_OP_DESTROY, NULL);
} else {
if (!zebra_evpn_is_if_es_capable(zif)) {
vty_out(vty,
"%% ESI cannot be associated with this interface type\n");
return CMD_WARNING;
}
if (esi_str) {
if (!str_to_esi(esi_str, &esi)) {
vty_out(vty, "%% Malformed ESI name\n");
return CMD_WARNING;
}
ret = zebra_evpn_es_type0_esi_update(zif, &esi);
} else {
if (!es_lid) {
vty_out(vty,
"%% Specify ES local id or ESI name\n");
return CMD_WARNING;
}
ret = zebra_evpn_es_lid_update(zif, es_lid);
}
if (ret == -1) {
vty_out(vty,
"%% ESI already exists on a different interface\n");
return CMD_WARNING;
}
if (esi_str)
nb_cli_enqueue_change(vty,
"./frr-zebra:zebra/evpn-mh/type-0/esi",
NB_OP_MODIFY, esi_str);
else
nb_cli_enqueue_change(vty,
"./frr-zebra:zebra/evpn-mh/type-3/local-discriminator",
NB_OP_MODIFY, es_lid_str);
}
return CMD_SUCCESS;
return nb_cli_apply_changes(vty, NULL);
}
/* CLI for tagging an interface as an uplink */

View File

@ -382,4 +382,10 @@ extern void zebra_evpn_proc_remote_nh(ZAPI_HANDLER_ARGS);
extern struct zebra_evpn_es_evi *
zebra_evpn_es_evi_find(struct zebra_evpn_es *es, struct zebra_evpn *zevpn);
void zebra_build_type3_esi(uint32_t lid, struct ethaddr *mac, esi_t *esi);
void zebra_evpn_es_sys_mac_update(struct zebra_if *zif, struct ethaddr *sysmac);
void zebra_evpn_es_lid_update(struct zebra_if *zif, uint32_t lid);
void zebra_evpn_es_type0_esi_update(struct zebra_if *zif, esi_t *esi);
#endif /* _ZEBRA_EVPN_MH_H */

View File

@ -507,6 +507,27 @@ const struct frr_yang_module_info frr_zebra_info = {
.destroy = lib_interface_zebra_link_params_packet_loss_destroy,
}
},
{
.xpath = "/frr-interface:lib/interface/frr-zebra:zebra/evpn-mh/type-0/esi",
.cbs = {
.modify = lib_interface_zebra_evpn_mh_type_0_esi_modify,
.destroy = lib_interface_zebra_evpn_mh_type_0_esi_destroy,
}
},
{
.xpath = "/frr-interface:lib/interface/frr-zebra:zebra/evpn-mh/type-3/system-mac",
.cbs = {
.modify = lib_interface_zebra_evpn_mh_type_3_system_mac_modify,
.destroy = lib_interface_zebra_evpn_mh_type_3_system_mac_destroy,
}
},
{
.xpath = "/frr-interface:lib/interface/frr-zebra:zebra/evpn-mh/type-3/local-discriminator",
.cbs = {
.modify = lib_interface_zebra_evpn_mh_type_3_local_discriminator_modify,
.destroy = lib_interface_zebra_evpn_mh_type_3_local_discriminator_destroy,
}
},
{
.xpath = "/frr-interface:lib/interface/frr-zebra:zebra/state/up-count",
.cbs = {

View File

@ -166,6 +166,17 @@ int lib_interface_zebra_link_params_packet_loss_modify(
struct nb_cb_modify_args *args);
int lib_interface_zebra_link_params_packet_loss_destroy(
struct nb_cb_destroy_args *args);
int lib_interface_zebra_evpn_mh_type_0_esi_modify(struct nb_cb_modify_args *args);
int lib_interface_zebra_evpn_mh_type_0_esi_destroy(
struct nb_cb_destroy_args *args);
int lib_interface_zebra_evpn_mh_type_3_system_mac_modify(
struct nb_cb_modify_args *args);
int lib_interface_zebra_evpn_mh_type_3_system_mac_destroy(
struct nb_cb_destroy_args *args);
int lib_interface_zebra_evpn_mh_type_3_local_discriminator_modify(
struct nb_cb_modify_args *args);
int lib_interface_zebra_evpn_mh_type_3_local_discriminator_destroy(
struct nb_cb_destroy_args *args);
struct yang_data *
lib_interface_zebra_state_up_count_get_elem(struct nb_cb_get_elem_args *args);
struct yang_data *

View File

@ -23,6 +23,7 @@
#include "zebra/debug.h"
#include "zebra/zebra_vxlan_private.h"
#include "zebra/zebra_vxlan.h"
#include "zebra/zebra_evpn_mh.h"
/*
* XPath: /frr-zebra:zebra/mcast-rpf-lookup
@ -2200,6 +2201,205 @@ int lib_interface_zebra_link_params_packet_loss_destroy(
return NB_OK;
}
static bool evpn_mh_dnode_to_esi(const struct lyd_node *dnode, esi_t *esi)
{
if (yang_dnode_exists(dnode, "type-0/esi")) {
str_to_esi(yang_dnode_get_string(dnode, "type-0/esi"), esi);
} else if (yang_dnode_exists(dnode, "type-3/system-mac") &&
yang_dnode_exists(dnode, "type-3/local-discriminator")) {
struct ethaddr mac;
uint32_t lid;
yang_dnode_get_mac(&mac, dnode, "type-3/system-mac");
lid = yang_dnode_get_uint32(dnode, "type-3/local-discriminator");
zebra_build_type3_esi(lid, &mac, esi);
} else {
return false;
}
return true;
}
struct esi_cmp_iter_arg {
struct lyd_node *dnode;
esi_t esi;
bool exists;
};
static int esi_cmp_iter_cb(const struct lyd_node *dnode, void *arg)
{
struct esi_cmp_iter_arg *iter = arg;
esi_t esi;
if (dnode == iter->dnode)
return YANG_ITER_CONTINUE;
if (!evpn_mh_dnode_to_esi(dnode, &esi))
return YANG_ITER_CONTINUE;
if (!memcmp(&esi, &iter->esi, ESI_BYTES)) {
iter->exists = true;
return YANG_ITER_STOP;
}
return YANG_ITER_CONTINUE;
}
/* evpn-mh should be passed to this function */
static bool esi_unique(struct lyd_node *dnode)
{
struct esi_cmp_iter_arg iter;
iter.dnode = dnode;
evpn_mh_dnode_to_esi(dnode, &iter.esi);
iter.exists = false;
yang_dnode_iterate(esi_cmp_iter_cb, &iter, dnode,
"/frr-interface:lib/interface/frr-zebra:zebra/evpn-mh");
if (iter.exists)
return false;
return true;
}
/*
* XPath: /frr-interface:lib/interface/frr-zebra:zebra/evpn-mh/type-0/esi
*/
int lib_interface_zebra_evpn_mh_type_0_esi_modify(struct nb_cb_modify_args *args)
{
struct interface *ifp;
esi_t esi;
switch (args->event) {
case NB_EV_VALIDATE:
if (!esi_unique(lyd_parent(lyd_parent(args->dnode)))) {
snprintfrr(args->errmsg, args->errmsg_len,
"ESI already exists on a different interface");
return NB_ERR_VALIDATION;
}
break;
case NB_EV_PREPARE:
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
ifp = nb_running_get_entry(args->dnode, NULL, true);
str_to_esi(yang_dnode_get_string(args->dnode, NULL), &esi);
zebra_evpn_es_type0_esi_update(ifp->info, &esi);
break;
}
return NB_OK;
}
int lib_interface_zebra_evpn_mh_type_0_esi_destroy(struct nb_cb_destroy_args *args)
{
struct interface *ifp;
if (args->event != NB_EV_APPLY)
return NB_OK;
ifp = nb_running_get_entry(args->dnode, NULL, true);
zebra_evpn_es_type0_esi_update(ifp->info, NULL);
return NB_OK;
}
/*
* XPath: /frr-interface:lib/interface/frr-zebra:zebra/evpn-mh/type-3/system-mac
*/
int lib_interface_zebra_evpn_mh_type_3_system_mac_modify(
struct nb_cb_modify_args *args)
{
struct interface *ifp;
struct ethaddr mac;
yang_dnode_get_mac(&mac, args->dnode, NULL);
switch (args->event) {
case NB_EV_VALIDATE:
if (is_zero_mac(&mac)) {
snprintfrr(args->errmsg, args->errmsg_len,
"MAC cannot be all-zeroes");
return NB_ERR_VALIDATION;
}
if (!esi_unique(lyd_parent(lyd_parent(args->dnode)))) {
snprintfrr(args->errmsg, args->errmsg_len,
"ESI already exists on a different interface");
return NB_ERR_VALIDATION;
}
break;
case NB_EV_PREPARE:
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
ifp = nb_running_get_entry(args->dnode, NULL, true);
zebra_evpn_es_sys_mac_update(ifp->info, &mac);
break;
}
return NB_OK;
}
int lib_interface_zebra_evpn_mh_type_3_system_mac_destroy(
struct nb_cb_destroy_args *args)
{
struct interface *ifp;
if (args->event != NB_EV_APPLY)
return NB_OK;
ifp = nb_running_get_entry(args->dnode, NULL, true);
zebra_evpn_es_sys_mac_update(ifp->info, NULL);
return NB_OK;
}
/*
* XPath: /frr-interface:lib/interface/frr-zebra:zebra/evpn-mh/type-3/local-discriminator
*/
int lib_interface_zebra_evpn_mh_type_3_local_discriminator_modify(
struct nb_cb_modify_args *args)
{
struct interface *ifp;
uint32_t lid;
switch (args->event) {
case NB_EV_VALIDATE:
if (!esi_unique(lyd_parent(lyd_parent(args->dnode)))) {
snprintfrr(args->errmsg, args->errmsg_len,
"ESI already exists on a different interface");
return NB_ERR_VALIDATION;
}
break;
case NB_EV_PREPARE:
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
ifp = nb_running_get_entry(args->dnode, NULL, true);
lid = yang_dnode_get_uint32(args->dnode, NULL);
zebra_evpn_es_lid_update(ifp->info, lid);
break;
}
return NB_OK;
}
int lib_interface_zebra_evpn_mh_type_3_local_discriminator_destroy(
struct nb_cb_destroy_args *args)
{
struct interface *ifp;
if (args->event != NB_EV_APPLY)
return NB_OK;
ifp = nb_running_get_entry(args->dnode, NULL, true);
zebra_evpn_es_lid_update(ifp->info, 0);
return NB_OK;
}
/*
* XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/l3vni-id
*/