From d5167496339ba462e0b6e8d69363b393f6c6ffa0 Mon Sep 17 00:00:00 2001 From: Satheesh Kumar K Date: Mon, 26 Aug 2019 21:24:19 -0700 Subject: [PATCH] Zebra: ADD Protobuf Encoding & Decoding for MLAG Messages 1. add the Mlag ProtoBuf Lib to Zebra Compilation 2. Encode the messages with protobuf before writing to MLAG 3. Decode the MLAG Messages using protobuf and write to clients based on their subscrption. Signed-off-by: Satheesh Kumar K --- Makefile.am | 2 +- zebra/subdir.am | 4 + zebra/zebra_mlag.c | 514 ++++++++++++++++++++++++++++++++++++++++++++- zebra/zebra_mlag.h | 4 + 4 files changed, 522 insertions(+), 2 deletions(-) diff --git a/Makefile.am b/Makefile.am index ada715dbca..34f112bf01 100644 --- a/Makefile.am +++ b/Makefile.am @@ -125,11 +125,11 @@ include doc/manpages/subdir.am include doc/developer/subdir.am include include/subdir.am include lib/subdir.am +include mlag/subdir.am include zebra/subdir.am include watchfrr/subdir.am include qpb/subdir.am include fpm/subdir.am -include mlag/subdir.am include grpc/subdir.am include tools/subdir.am include solaris/subdir.am diff --git a/zebra/subdir.am b/zebra/subdir.am index 5016ca1f28..859c8c0836 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -38,6 +38,9 @@ man8 += $(MANBUILD)/zebra.8 endif zebra_zebra_LDADD = lib/libfrr.la $(LIBCAP) +if HAVE_PROTOBUF +zebra_zebra_LDADD += mlag/libmlag_pb.la $(PROTOBUF_C_LIBS) +endif zebra_zebra_SOURCES = \ zebra/connected.c \ zebra/debug.c \ @@ -131,6 +134,7 @@ noinst_HEADERS += \ zebra/rtadv.h \ zebra/rule_netlink.h \ zebra/zebra_mlag.h \ + zebra/zebra_mlag_private.h \ zebra/zebra_fpm_private.h \ zebra/zebra_l2.h \ zebra/zebra_dplane.h \ diff --git a/zebra/zebra_mlag.c b/zebra/zebra_mlag.c index 90af959d27..4d5145f570 100644 --- a/zebra/zebra_mlag.c +++ b/zebra/zebra_mlag.c @@ -380,7 +380,7 @@ static void zebra_mlag_spawn_pthread(void) zrouter.mlag_info.th_master = zrouter.mlag_info.zebra_pth_mlag->master; - /* Enqueue an initial event for the dataplane pthread */ + /* Enqueue an initial event to the Newly spawn MLAG pthread */ zebra_mlag_signal_write_thread(); frr_pthread_run(zrouter.mlag_info.zebra_pth_mlag, NULL); @@ -908,6 +908,517 @@ void zebra_mlag_terminate(void) * ProtoBuf Encoding APIs */ +#ifdef HAVE_PROTOBUF + +DEFINE_MTYPE_STATIC(ZEBRA, MLAG_PBUF, "ZEBRA MLAG PROTOBUF") + +int zebra_mlag_protobuf_encode_client_data(struct stream *s, uint32_t *msg_type) +{ + ZebraMlagHeader hdr = ZEBRA_MLAG__HEADER__INIT; + struct mlag_msg mlag_msg; + uint8_t tmp_buf[ZEBRA_MLAG_BUF_LIMIT]; + int len = 0; + int n_len = 0; + int rc = 0; + char buf[80]; + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("%s: Entering..", __func__); + + rc = zebra_mlag_lib_decode_mlag_hdr(s, &mlag_msg); + if (rc) + return rc; + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("%s: Decoded msg length:%d..", __func__, + mlag_msg.data_len); + + memset(tmp_buf, 0, ZEBRA_MLAG_BUF_LIMIT); + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("%s: Mlag ProtoBuf encoding of message:%s", __func__, + zebra_mlag_lib_msgid_to_str(mlag_msg.msg_type, buf, + 80)); + *msg_type = mlag_msg.msg_type; + switch (mlag_msg.msg_type) { + case MLAG_MROUTE_ADD: { + struct mlag_mroute_add msg; + ZebraMlagMrouteAdd pay_load = ZEBRA_MLAG_MROUTE_ADD__INIT; + uint32_t vrf_name_len = 0; + + rc = zebra_mlag_lib_decode_mroute_add(s, &msg); + if (rc) + return rc; + vrf_name_len = strlen(msg.vrf_name) + 1; + pay_load.vrf_name = XMALLOC(MTYPE_MLAG_PBUF, vrf_name_len); + strlcpy(pay_load.vrf_name, msg.vrf_name, vrf_name_len); + pay_load.source_ip = msg.source_ip; + pay_load.group_ip = msg.group_ip; + pay_load.cost_to_rp = msg.cost_to_rp; + pay_load.owner_id = msg.owner_id; + pay_load.am_i_dr = msg.am_i_dr; + pay_load.am_i_dual_active = msg.am_i_dual_active; + pay_load.vrf_id = msg.vrf_id; + + if (msg.owner_id == MLAG_OWNER_INTERFACE) { + vrf_name_len = strlen(msg.intf_name) + 1; + pay_load.intf_name = + XMALLOC(MTYPE_MLAG_PBUF, vrf_name_len); + strlcpy(pay_load.intf_name, msg.intf_name, + vrf_name_len); + } + + len = zebra_mlag_mroute_add__pack(&pay_load, tmp_buf); + XFREE(MTYPE_MLAG_PBUF, pay_load.vrf_name); + if (msg.owner_id == MLAG_OWNER_INTERFACE) + XFREE(MTYPE_MLAG_PBUF, pay_load.intf_name); + } break; + case MLAG_MROUTE_DEL: { + struct mlag_mroute_del msg; + ZebraMlagMrouteDel pay_load = ZEBRA_MLAG_MROUTE_DEL__INIT; + uint32_t vrf_name_len = 0; + + rc = zebra_mlag_lib_decode_mroute_del(s, &msg); + if (rc) + return rc; + vrf_name_len = strlen(msg.vrf_name) + 1; + pay_load.vrf_name = XMALLOC(MTYPE_MLAG_PBUF, vrf_name_len); + strlcpy(pay_load.vrf_name, msg.vrf_name, vrf_name_len); + pay_load.source_ip = msg.source_ip; + pay_load.group_ip = msg.group_ip; + pay_load.owner_id = msg.owner_id; + pay_load.vrf_id = msg.vrf_id; + + if (msg.owner_id == MLAG_OWNER_INTERFACE) { + vrf_name_len = strlen(msg.intf_name) + 1; + pay_load.intf_name = + XMALLOC(MTYPE_MLAG_PBUF, vrf_name_len); + strlcpy(pay_load.intf_name, msg.intf_name, + vrf_name_len); + } + + len = zebra_mlag_mroute_del__pack(&pay_load, tmp_buf); + XFREE(MTYPE_MLAG_PBUF, pay_load.vrf_name); + if (msg.owner_id == MLAG_OWNER_INTERFACE) + XFREE(MTYPE_MLAG_PBUF, pay_load.intf_name); + } break; + case MLAG_MROUTE_ADD_BULK: { + struct mlag_mroute_add msg; + ZebraMlagMrouteAddBulk Bulk_msg = + ZEBRA_MLAG_MROUTE_ADD_BULK__INIT; + ZebraMlagMrouteAdd **pay_load = NULL; + int i = 0; + bool cleanup = false; + + Bulk_msg.n_mroute_add = mlag_msg.msg_cnt; + pay_load = XMALLOC(MTYPE_MLAG_PBUF, + sizeof(ZebraMlagMrouteAdd *) + * Bulk_msg.n_mroute_add); + + for (i = 0; i < mlag_msg.msg_cnt; i++) { + + uint32_t vrf_name_len = 0; + + rc = zebra_mlag_lib_decode_mroute_add(s, &msg); + if (rc) { + cleanup = true; + break; + } + pay_load[i] = XMALLOC(MTYPE_MLAG_PBUF, + sizeof(ZebraMlagMrouteAdd)); + zebra_mlag_mroute_add__init(pay_load[i]); + + vrf_name_len = strlen(msg.vrf_name) + 1; + pay_load[i]->vrf_name = + XMALLOC(MTYPE_MLAG_PBUF, vrf_name_len); + strlcpy(pay_load[i]->vrf_name, msg.vrf_name, + vrf_name_len); + pay_load[i]->source_ip = msg.source_ip; + pay_load[i]->group_ip = msg.group_ip; + pay_load[i]->cost_to_rp = msg.cost_to_rp; + pay_load[i]->owner_id = msg.owner_id; + pay_load[i]->am_i_dr = msg.am_i_dr; + pay_load[i]->am_i_dual_active = msg.am_i_dual_active; + pay_load[i]->vrf_id = msg.vrf_id; + if (msg.owner_id == MLAG_OWNER_INTERFACE) { + vrf_name_len = strlen(msg.intf_name) + 1; + pay_load[i]->intf_name = + XMALLOC(MTYPE_MLAG_PBUF, vrf_name_len); + + strlcpy(pay_load[i]->intf_name, msg.intf_name, + vrf_name_len); + } + } + if (cleanup == false) { + Bulk_msg.mroute_add = pay_load; + len = zebra_mlag_mroute_add_bulk__pack(&Bulk_msg, + tmp_buf); + } + + for (i = 0; i < mlag_msg.msg_cnt; i++) { + if (pay_load[i]->vrf_name) + XFREE(MTYPE_MLAG_PBUF, pay_load[i]->vrf_name); + if (pay_load[i]->owner_id == MLAG_OWNER_INTERFACE + && pay_load[i]->intf_name) + XFREE(MTYPE_MLAG_PBUF, pay_load[i]->intf_name); + if (pay_load[i]) + XFREE(MTYPE_MLAG_PBUF, pay_load[i]); + } + XFREE(MTYPE_MLAG_PBUF, pay_load); + if (cleanup == true) + return -1; + } break; + case MLAG_MROUTE_DEL_BULK: { + struct mlag_mroute_del msg; + ZebraMlagMrouteDelBulk Bulk_msg = + ZEBRA_MLAG_MROUTE_DEL_BULK__INIT; + ZebraMlagMrouteDel **pay_load = NULL; + int i = 0; + bool cleanup = false; + + Bulk_msg.n_mroute_del = mlag_msg.msg_cnt; + pay_load = XMALLOC(MTYPE_MLAG_PBUF, + sizeof(ZebraMlagMrouteDel *) + * Bulk_msg.n_mroute_del); + + for (i = 0; i < mlag_msg.msg_cnt; i++) { + + uint32_t vrf_name_len = 0; + + rc = zebra_mlag_lib_decode_mroute_del(s, &msg); + if (rc) { + cleanup = true; + break; + } + + pay_load[i] = XMALLOC(MTYPE_MLAG_PBUF, + sizeof(ZebraMlagMrouteDel)); + zebra_mlag_mroute_del__init(pay_load[i]); + + vrf_name_len = strlen(msg.vrf_name) + 1; + pay_load[i]->vrf_name = + XMALLOC(MTYPE_MLAG_PBUF, vrf_name_len); + + strlcpy(pay_load[i]->vrf_name, msg.vrf_name, + vrf_name_len); + pay_load[i]->source_ip = msg.source_ip; + pay_load[i]->group_ip = msg.group_ip; + pay_load[i]->owner_id = msg.owner_id; + pay_load[i]->vrf_id = msg.vrf_id; + if (msg.owner_id == MLAG_OWNER_INTERFACE) { + vrf_name_len = strlen(msg.intf_name) + 1; + pay_load[i]->intf_name = + XMALLOC(MTYPE_MLAG_PBUF, vrf_name_len); + + strlcpy(pay_load[i]->intf_name, msg.intf_name, + vrf_name_len); + } + } + if (cleanup == false) { + Bulk_msg.mroute_del = pay_load; + len = zebra_mlag_mroute_del_bulk__pack(&Bulk_msg, + tmp_buf); + } + + for (i = 0; i < mlag_msg.msg_cnt; i++) { + if (pay_load[i]->vrf_name) + XFREE(MTYPE_MLAG_PBUF, pay_load[i]->vrf_name); + if (pay_load[i]->owner_id == MLAG_OWNER_INTERFACE + && pay_load[i]->intf_name) + XFREE(MTYPE_MLAG_PBUF, pay_load[i]->intf_name); + if (pay_load[i]) + XFREE(MTYPE_MLAG_PBUF, pay_load[i]); + } + XFREE(MTYPE_MLAG_PBUF, pay_load); + if (cleanup == true) + return -1; + } break; + default: + break; + } + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug( + "%s: length of Mlag ProtoBuf encoded message:%s, %d", + __func__, + zebra_mlag_lib_msgid_to_str(mlag_msg.msg_type, buf, 80), + len); + hdr.type = (ZebraMlagHeader__MessageType)mlag_msg.msg_type; + if (len != 0) { + hdr.data.len = len; + hdr.data.data = XMALLOC(MTYPE_MLAG_PBUF, len); + if (hdr.data.data == NULL) + return -1; + memcpy(hdr.data.data, tmp_buf, len); + } + + /* + * ProtoBuf Infra will not support to demarc the pointers whem multiple + * mesasges are posted inside a single Buffer. + * 2 -sloutions exist to solve this + * 1. add Unenoced length at the beginning of every message, this will + * be used to point to next mesasge in the buffer + * 2. another solution is defining all messages insides another message + * But this will permit only 32 messages. this can be extended with + * multiple levels. + * for simplicity we are going with solution-1. + */ + len = zebra_mlag__header__pack(&hdr, + (mlag_wr_buffer + ZEBRA_MLAG_LEN_SIZE)); + n_len = htonl(len); + memcpy(mlag_wr_buffer, &n_len, ZEBRA_MLAG_LEN_SIZE); + len += ZEBRA_MLAG_LEN_SIZE; + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug( + "%s: length of Mlag ProtoBuf message:%s with Header %d", + __func__, + zebra_mlag_lib_msgid_to_str(mlag_msg.msg_type, buf, 80), + len); + if (hdr.data.data) + XFREE(MTYPE_MLAG_PBUF, hdr.data.data); + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("%s: Exiting..", __func__); + return len; +} + +int zebra_mlag_protobuf_decode_message(struct stream **s, uint8_t *data, + uint32_t len) +{ + uint32_t msg_type; + ZebraMlagHeader *hdr = NULL; + char buf[80]; + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("%s: Entering..", __func__); + hdr = zebra_mlag__header__unpack(NULL, len, data); + if (hdr == NULL) + return -1; + + /* + * ADD The MLAG Header + */ + zclient_create_header(*s, ZEBRA_MLAG_FORWARD_MSG, VRF_DEFAULT); + + msg_type = hdr->type; + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("%s: Mlag ProtoBuf decoding of message:%s", __func__, + zebra_mlag_lib_msgid_to_str(msg_type, buf, 80)); + + /* + * Internal MLAG Message-types & MLAG.proto message types should + * always match, otherwise there can be decoding errors + * To avoid exposing clients with Protobuf flags, using internal + * message-types + */ + stream_putl(*s, hdr->type); + + if (hdr->data.len == 0) { + /* NULL Payload */ + stream_putw(*s, MLAG_MSG_NULL_PAYLOAD); + /* No Batching */ + stream_putw(*s, MLAG_MSG_NO_BATCH); + } else { + switch (msg_type) { + case ZEBRA_MLAG__HEADER__MESSAGE_TYPE__ZEBRA_MLAG_STATUS_UPDATE: { + ZebraMlagStatusUpdate *msg = NULL; + + msg = zebra_mlag_status_update__unpack( + NULL, hdr->data.len, hdr->data.data); + if (msg == NULL) { + zebra_mlag__header__free_unpacked(hdr, NULL); + return -1; + } + /* Payload len */ + stream_putw(*s, sizeof(struct mlag_status)); + /* No Batching */ + stream_putw(*s, MLAG_MSG_NO_BATCH); + /* Actual Data */ + stream_put(*s, msg->peerlink, INTERFACE_NAMSIZ); + stream_putl(*s, msg->my_role); + stream_putl(*s, msg->peer_state); + zebra_mlag_status_update__free_unpacked(msg, NULL); + } break; + case ZEBRA_MLAG__HEADER__MESSAGE_TYPE__ZEBRA_MLAG_VXLAN_UPDATE: { + ZebraMlagVxlanUpdate *msg = NULL; + + msg = zebra_mlag_vxlan_update__unpack( + NULL, hdr->data.len, hdr->data.data); + if (msg == NULL) { + zebra_mlag__header__free_unpacked(hdr, NULL); + return -1; + } + /* Payload len */ + stream_putw(*s, sizeof(struct mlag_vxlan)); + /* No Batching */ + stream_putw(*s, MLAG_MSG_NO_BATCH); + /* Actual Data */ + stream_putl(*s, msg->anycast_ip); + stream_putl(*s, msg->local_ip); + zebra_mlag_vxlan_update__free_unpacked(msg, NULL); + } break; + case ZEBRA_MLAG__HEADER__MESSAGE_TYPE__ZEBRA_MLAG_MROUTE_ADD: { + ZebraMlagMrouteAdd *msg = NULL; + + msg = zebra_mlag_mroute_add__unpack(NULL, hdr->data.len, + hdr->data.data); + if (msg == NULL) { + zebra_mlag__header__free_unpacked(hdr, NULL); + return -1; + } + /* Payload len */ + stream_putw(*s, sizeof(struct mlag_mroute_add)); + /* No Batching */ + stream_putw(*s, MLAG_MSG_NO_BATCH); + /* Actual Data */ + stream_put(*s, msg->vrf_name, VRF_NAMSIZ); + + stream_putl(*s, msg->source_ip); + stream_putl(*s, msg->group_ip); + stream_putl(*s, msg->cost_to_rp); + stream_putl(*s, msg->owner_id); + stream_putc(*s, msg->am_i_dr); + stream_putc(*s, msg->am_i_dual_active); + stream_putl(*s, msg->vrf_id); + if (msg->owner_id == MLAG_OWNER_INTERFACE) + stream_put(*s, msg->intf_name, + INTERFACE_NAMSIZ); + else + stream_put(*s, NULL, INTERFACE_NAMSIZ); + zebra_mlag_mroute_add__free_unpacked(msg, NULL); + } break; + case ZEBRA_MLAG__HEADER__MESSAGE_TYPE__ZEBRA_MLAG_MROUTE_DEL: { + ZebraMlagMrouteDel *msg = NULL; + + msg = zebra_mlag_mroute_del__unpack(NULL, hdr->data.len, + hdr->data.data); + if (msg == NULL) { + zebra_mlag__header__free_unpacked(hdr, NULL); + return -1; + } + /* Payload len */ + stream_putw(*s, sizeof(struct mlag_mroute_del)); + /* No Batching */ + stream_putw(*s, MLAG_MSG_NO_BATCH); + /* Actual Data */ + stream_put(*s, msg->vrf_name, VRF_NAMSIZ); + + stream_putl(*s, msg->source_ip); + stream_putl(*s, msg->group_ip); + stream_putl(*s, msg->group_ip); + stream_putl(*s, msg->owner_id); + stream_putl(*s, msg->vrf_id); + if (msg->owner_id == MLAG_OWNER_INTERFACE) + stream_put(*s, msg->intf_name, + INTERFACE_NAMSIZ); + else + stream_put(*s, NULL, INTERFACE_NAMSIZ); + zebra_mlag_mroute_del__free_unpacked(msg, NULL); + } break; + case ZEBRA_MLAG__HEADER__MESSAGE_TYPE__ZEBRA_MLAG_MROUTE_ADD_BULK: { + ZebraMlagMrouteAddBulk *Bulk_msg = NULL; + ZebraMlagMrouteAdd *msg = NULL; + size_t i = 0; + + Bulk_msg = zebra_mlag_mroute_add_bulk__unpack( + NULL, hdr->data.len, hdr->data.data); + if (Bulk_msg == NULL) { + zebra_mlag__header__free_unpacked(hdr, NULL); + return -1; + } + /* Payload len */ + stream_putw(*s, (Bulk_msg->n_mroute_add + * sizeof(struct mlag_mroute_add))); + /* No. of msgs in Batch */ + stream_putw(*s, Bulk_msg->n_mroute_add); + + /* Actual Data */ + for (i = 0; i < Bulk_msg->n_mroute_add; i++) { + + msg = Bulk_msg->mroute_add[i]; + + stream_put(*s, msg->vrf_name, VRF_NAMSIZ); + stream_putl(*s, msg->source_ip); + stream_putl(*s, msg->group_ip); + stream_putl(*s, msg->cost_to_rp); + stream_putl(*s, msg->owner_id); + stream_putc(*s, msg->am_i_dr); + stream_putc(*s, msg->am_i_dual_active); + stream_putl(*s, msg->vrf_id); + if (msg->owner_id == MLAG_OWNER_INTERFACE) + stream_put(*s, msg->intf_name, + INTERFACE_NAMSIZ); + else + stream_put(*s, NULL, INTERFACE_NAMSIZ); + } + zebra_mlag_mroute_add_bulk__free_unpacked(Bulk_msg, + NULL); + } break; + case ZEBRA_MLAG__HEADER__MESSAGE_TYPE__ZEBRA_MLAG_MROUTE_DEL_BULK: { + ZebraMlagMrouteDelBulk *Bulk_msg = NULL; + ZebraMlagMrouteDel *msg = NULL; + size_t i = 0; + + Bulk_msg = zebra_mlag_mroute_del_bulk__unpack( + NULL, hdr->data.len, hdr->data.data); + if (Bulk_msg == NULL) { + zebra_mlag__header__free_unpacked(hdr, NULL); + return -1; + } + /* Payload len */ + stream_putw(*s, (Bulk_msg->n_mroute_del + * sizeof(struct mlag_mroute_del))); + /* No. of msgs in Batch */ + stream_putw(*s, Bulk_msg->n_mroute_del); + + /* Actual Data */ + for (i = 0; i < Bulk_msg->n_mroute_del; i++) { + + msg = Bulk_msg->mroute_del[i]; + + stream_put(*s, msg->vrf_name, VRF_NAMSIZ); + stream_putl(*s, msg->source_ip); + stream_putl(*s, msg->group_ip); + stream_putl(*s, msg->owner_id); + stream_putl(*s, msg->vrf_id); + if (msg->owner_id == MLAG_OWNER_INTERFACE) + stream_put(*s, msg->intf_name, + INTERFACE_NAMSIZ); + else + stream_put(*s, NULL, INTERFACE_NAMSIZ); + } + zebra_mlag_mroute_del_bulk__free_unpacked(Bulk_msg, + NULL); + } break; + case ZEBRA_MLAG__HEADER__MESSAGE_TYPE__ZEBRA_MLAG_ZEBRA_STATUS_UPDATE: { + ZebraMlagZebraStatusUpdate *msg = NULL; + + msg = zebra_mlag_zebra_status_update__unpack( + NULL, hdr->data.len, hdr->data.data); + if (msg == NULL) { + zebra_mlag__header__free_unpacked(hdr, NULL); + return -1; + } + /* Payload len */ + stream_putw(*s, sizeof(struct mlag_frr_status)); + /* No Batching */ + stream_putw(*s, MLAG_MSG_NO_BATCH); + /* Actual Data */ + stream_putl(*s, msg->peer_frrstate); + zebra_mlag_zebra_status_update__free_unpacked(msg, + NULL); + } break; + default: + break; + } + } + zebra_mlag__header__free_unpacked(hdr, NULL); + return msg_type; +} + +#else int zebra_mlag_protobuf_encode_client_data(struct stream *s, uint32_t *msg_type) { return 0; @@ -918,3 +1429,4 @@ int zebra_mlag_protobuf_decode_message(struct stream **s, uint8_t *data, { return 0; } +#endif diff --git a/zebra/zebra_mlag.h b/zebra/zebra_mlag.h index a145753d88..01459cd29c 100644 --- a/zebra/zebra_mlag.h +++ b/zebra/zebra_mlag.h @@ -26,6 +26,10 @@ #include "zclient.h" #include "zebra/zserv.h" +#ifdef HAVE_PROTOBUF +#include "mlag/mlag.pb-c.h" +#endif + #define ZEBRA_MLAG_BUF_LIMIT 2048 #define ZEBRA_MLAG_LEN_SIZE 4 -- 2.39.5