diff --git a/Makefile.am b/Makefile.am index 851cefc85c..34f112bf01 100644 --- a/Makefile.am +++ b/Makefile.am @@ -125,6 +125,7 @@ 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 diff --git a/lib/mlag.c b/lib/mlag.c index acdc662924..7aac571da6 100644 --- a/lib/mlag.c +++ b/lib/mlag.c @@ -39,3 +39,133 @@ char *mlag_role2str(enum mlag_role role, char *buf, size_t size) return buf; } + +char *zebra_mlag_lib_msgid_to_str(enum mlag_msg_type msg_type, char *buf, + size_t size) +{ + switch (msg_type) { + case MLAG_REGISTER: + snprintf(buf, size, "Register"); + break; + case MLAG_DEREGISTER: + snprintf(buf, size, "De-Register"); + break; + case MLAG_MROUTE_ADD: + snprintf(buf, size, "Mroute add"); + break; + case MLAG_MROUTE_DEL: + snprintf(buf, size, "Mroute del"); + break; + case MLAG_DUMP: + snprintf(buf, size, "Mlag Replay"); + break; + case MLAG_MROUTE_ADD_BULK: + snprintf(buf, size, "Mroute Add Batch"); + break; + case MLAG_MROUTE_DEL_BULK: + snprintf(buf, size, "Mroute Del Batch"); + break; + case MLAG_STATUS_UPDATE: + snprintf(buf, size, "Mlag Status"); + break; + case MLAG_VXLAN_UPDATE: + snprintf(buf, size, "Mlag vxlan update"); + break; + case MLAG_PEER_FRR_STATUS: + snprintf(buf, size, "Mlag Peer FRR Status"); + break; + default: + snprintf(buf, size, "Unknown %d", msg_type); + break; + } + return buf; +} + + +int zebra_mlag_lib_decode_mlag_hdr(struct stream *s, struct mlag_msg *msg) +{ + if (s == NULL || msg == NULL) + return -1; + + STREAM_GETL(s, msg->msg_type); + STREAM_GETW(s, msg->data_len); + STREAM_GETW(s, msg->msg_cnt); + return 0; +stream_failure: + return -1; +} + +int zebra_mlag_lib_decode_mroute_add(struct stream *s, + struct mlag_mroute_add *msg) +{ + if (s == NULL || msg == NULL) + return -1; + + STREAM_GET(msg->vrf_name, s, VRF_NAMSIZ); + STREAM_GETL(s, msg->source_ip); + STREAM_GETL(s, msg->group_ip); + STREAM_GETL(s, msg->cost_to_rp); + STREAM_GETL(s, msg->owner_id); + STREAM_GETC(s, msg->am_i_dr); + STREAM_GETC(s, msg->am_i_dual_active); + STREAM_GETL(s, msg->vrf_id); + STREAM_GET(msg->intf_name, s, INTERFACE_NAMSIZ); + return 0; +stream_failure: + return -1; +} + +int zebra_mlag_lib_decode_mroute_del(struct stream *s, + struct mlag_mroute_del *msg) +{ + if (s == NULL || msg == NULL) + return -1; + + STREAM_GET(msg->vrf_name, s, VRF_NAMSIZ); + STREAM_GETL(s, msg->source_ip); + STREAM_GETL(s, msg->group_ip); + STREAM_GETL(s, msg->owner_id); + STREAM_GETL(s, msg->vrf_id); + STREAM_GET(msg->intf_name, s, INTERFACE_NAMSIZ); + return 0; +stream_failure: + return -1; +} + +int zebra_mlag_lib_decode_mlag_status(struct stream *s, struct mlag_status *msg) +{ + if (s == NULL || msg == NULL) + return -1; + + STREAM_GET(msg->peerlink_rif, s, INTERFACE_NAMSIZ); + STREAM_GETL(s, msg->my_role); + STREAM_GETL(s, msg->peer_state); + return 0; +stream_failure: + return -1; +} + +int zebra_mlag_lib_decode_vxlan_update(struct stream *s, struct mlag_vxlan *msg) +{ + if (s == NULL || msg == NULL) + return -1; + + STREAM_GETL(s, msg->anycast_ip); + STREAM_GETL(s, msg->local_ip); + return 0; + +stream_failure: + return -1; +} + +int zebra_mlag_lib_decode_frr_status(struct stream *s, + struct mlag_frr_status *msg) +{ + if (s == NULL || msg == NULL) + return -1; + + STREAM_GETL(s, msg->frr_state); + return 0; +stream_failure: + return -1; +} diff --git a/lib/mlag.h b/lib/mlag.h index 2b904d44f4..a88db8b35e 100644 --- a/lib/mlag.h +++ b/lib/mlag.h @@ -26,13 +26,118 @@ extern "C" { #endif +#include "lib/if.h" +#include "lib/vrf.h" +#include "lib/stream.h" + +#define MLAG_MSG_NULL_PAYLOAD 0 +#define MLAG_MSG_NO_BATCH 1 +#define MLAG_BUF_LIMIT 2048 + enum mlag_role { MLAG_ROLE_NONE, MLAG_ROLE_PRIMARY, MLAG_ROLE_SECONDARY }; +enum mlag_state { + MLAG_STATE_DOWN, + MLAG_STATE_RUNNING, +}; + +enum mlag_frr_state { + MLAG_FRR_STATE_NONE, + MLAG_FRR_STATE_DOWN, + MLAG_FRR_STATE_UP, +}; + +enum mlag_owner { + MLAG_OWNER_NONE, + MLAG_OWNER_INTERFACE, + MLAG_OWNER_VXLAN, +}; + +/* + * This message definition should match mlag.proto + * Because message registration is based on this + */ +enum mlag_msg_type { + MLAG_MSG_NONE = 0, + MLAG_REGISTER = 1, + MLAG_DEREGISTER = 2, + MLAG_STATUS_UPDATE = 3, + MLAG_MROUTE_ADD = 4, + MLAG_MROUTE_DEL = 5, + MLAG_DUMP = 6, + MLAG_MROUTE_ADD_BULK = 7, + MLAG_MROUTE_DEL_BULK = 8, + MLAG_PIM_CFG_DUMP = 10, + MLAG_VXLAN_UPDATE = 11, + MLAG_PEER_FRR_STATUS = 12, +}; + +struct mlag_frr_status { + enum mlag_frr_state frr_state; +}; + +struct mlag_status { + char peerlink_rif[INTERFACE_NAMSIZ]; + enum mlag_role my_role; + enum mlag_state peer_state; +}; + +#define MLAG_ROLE_STRSIZE 16 + +struct mlag_vxlan { + uint32_t anycast_ip; + uint32_t local_ip; +}; + +struct mlag_mroute_add { + char vrf_name[VRF_NAMSIZ]; + uint32_t source_ip; + uint32_t group_ip; + uint32_t cost_to_rp; + enum mlag_owner owner_id; + uint8_t am_i_dr; + uint8_t am_i_dual_active; + uint32_t vrf_id; + char intf_name[INTERFACE_NAMSIZ]; +}; + +struct mlag_mroute_del { + char vrf_name[VRF_NAMSIZ]; + uint32_t source_ip; + uint32_t group_ip; + enum mlag_owner owner_id; + uint32_t vrf_id; + char intf_name[INTERFACE_NAMSIZ]; +}; + +struct mlag_msg { + enum mlag_msg_type msg_type; + uint16_t data_len; + uint16_t msg_cnt; + uint8_t data[0]; +}; + + extern char *mlag_role2str(enum mlag_role role, char *buf, size_t size); +extern char *zebra_mlag_lib_msgid_to_str(enum mlag_msg_type msg_type, char *buf, + size_t size); +extern int zebra_mlag_lib_decode_mlag_hdr(struct stream *s, + struct mlag_msg *msg); +extern int zebra_mlag_lib_decode_mroute_add(struct stream *s, + struct mlag_mroute_add *msg); +extern int zebra_mlag_lib_decode_mroute_del(struct stream *s, + struct mlag_mroute_del *msg); +extern int zebra_mlag_lib_decode_mlag_status(struct stream *s, + struct mlag_status *msg); +extern int zebra_mlag_lib_decode_vxlan_update(struct stream *s, + struct mlag_vxlan *msg); + +extern int zebra_mlag_lib_decode_frr_status(struct stream *s, + struct mlag_frr_status *msg); #ifdef __cplusplus } diff --git a/lib/zclient.c b/lib/zclient.c index 91dbe30a09..5e23a5cc33 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -2717,6 +2717,63 @@ stream_failure: return; } +void zclient_send_mlag_register(struct zclient *client, uint32_t bit_map) +{ + struct stream *s; + + s = client->obuf; + stream_reset(s); + + zclient_create_header(s, ZEBRA_MLAG_CLIENT_REGISTER, VRF_DEFAULT); + stream_putl(s, bit_map); + + stream_putw_at(s, 0, stream_get_endp(s)); + zclient_send_message(client); +} + +void zclient_send_mlag_deregister(struct zclient *client) +{ + zebra_message_send(client, ZEBRA_MLAG_CLIENT_UNREGISTER, VRF_DEFAULT); +} + +void zclient_send_mlag_data(struct zclient *client, struct stream *client_s) +{ + struct stream *s; + + s = client->obuf; + stream_reset(s); + + zclient_create_header(s, ZEBRA_MLAG_FORWARD_MSG, VRF_DEFAULT); + stream_put(s, client_s->data, client_s->endp); + + stream_putw_at(s, 0, stream_get_endp(s)); + zclient_send_message(client); +} + +static void zclient_mlag_process_up(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + if (zclient->mlag_process_up) + (*zclient->mlag_process_up)(); + +} + +static void zclient_mlag_process_down(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + if (zclient->mlag_process_down) + (*zclient->mlag_process_down)(); + +} + +static void zclient_mlag_handle_msg(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + if (zclient->mlag_handle_msg) + (*zclient->mlag_handle_msg)(zclient->ibuf, length); + +} + /* Zebra client message read function. */ static int zclient_read(struct thread *thread) { @@ -3011,6 +3068,15 @@ static int zclient_read(struct thread *thread) (*zclient->vxlan_sg_del)(command, zclient, length, vrf_id); break; + case ZEBRA_MLAG_PROCESS_UP: + zclient_mlag_process_up(command, zclient, length, vrf_id); + break; + case ZEBRA_MLAG_PROCESS_DOWN: + zclient_mlag_process_down(command, zclient, length, vrf_id); + break; + case ZEBRA_MLAG_FORWARD_MSG: + zclient_mlag_handle_msg(command, zclient, length, vrf_id); + break; default: break; } diff --git a/lib/zclient.h b/lib/zclient.h index 5f9edc36ff..71ebabbf75 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -178,6 +178,11 @@ typedef enum { ZEBRA_VXLAN_SG_ADD, ZEBRA_VXLAN_SG_DEL, ZEBRA_VXLAN_SG_REPLAY, + ZEBRA_MLAG_PROCESS_UP, + ZEBRA_MLAG_PROCESS_DOWN, + ZEBRA_MLAG_CLIENT_REGISTER, + ZEBRA_MLAG_CLIENT_UNREGISTER, + ZEBRA_MLAG_FORWARD_MSG, } zebra_message_types_t; struct redist_proto { @@ -272,6 +277,9 @@ struct zclient { int (*iptable_notify_owner)(ZAPI_CALLBACK_ARGS); int (*vxlan_sg_add)(ZAPI_CALLBACK_ARGS); int (*vxlan_sg_del)(ZAPI_CALLBACK_ARGS); + int (*mlag_process_up)(void); + int (*mlag_process_down)(void); + int (*mlag_handle_msg)(struct stream *msg, int len); }; /* Zebra API message flag. */ @@ -693,5 +701,11 @@ static inline void zapi_route_set_blackhole(struct zapi_route *api, SET_FLAG(api->message, ZAPI_MESSAGE_NEXTHOP); }; +extern void zclient_send_mlag_register(struct zclient *client, + uint32_t bit_map); +extern void zclient_send_mlag_deregister(struct zclient *client); + +extern void zclient_send_mlag_data(struct zclient *client, + struct stream *client_s); #endif /* _ZEBRA_ZCLIENT_H */ diff --git a/mlag/mlag.proto b/mlag/mlag.proto new file mode 100644 index 0000000000..6991015a3d --- /dev/null +++ b/mlag/mlag.proto @@ -0,0 +1,186 @@ +// See README.txt for information and build instructions. +// +// Note: START and END tags are used in comments to define sections used in +// tutorials. They are not part of the syntax for Protocol Buffers. +// +// To get an in-depth walkthrough of this file and the related examples, see: +// https://developers.google.com/protocol-buffers/docs/tutorials + +// [START declaration] +syntax = "proto3"; +//package tutorial; + +/* + * This Contains the Mesadge structurtes used for PIM MLAG Active-Active support. + * Mainly there were two types of messages + * + * 1. Messages sent from PIM (Node-1) to PIM (Node-2) + * 2. Messages sent from MCLAGD to PIM (status Messages) + * + * ProtoBuf supports maximum 32 fileds, so to make it more generic message + * encoding is like below. + * __________________________________________ + * | | | + * | Header | bytes | + * ___________________________________________ + * + * + * Header carries Information about + * 1) what Message it is carrying + * 2) Bytes carries teh actual payload encoded with protobuf + * + * + * Limitations + *============= + * Since message-type is 32-bit, there were no real limitations on number of + * messages Infra can support, but each message can carry only 32 fileds. + * + */ + + +// [START messages] +message ZebraMlag_Header { + enum MessageType { + ZEBRA_MLAG_NONE = 0; //Invalid message-type + ZEBRA_MLAG_REGISTER = 1; + ZEBRA_MLAG_DEREGISTER = 2; + ZEBRA_MLAG_STATUS_UPDATE = 3; + ZEBRA_MLAG_MROUTE_ADD = 4; + ZEBRA_MLAG_MROUTE_DEL = 5; + ZEBRA_MLAG_DUMP = 6; + ZEBRA_MLAG_MROUTE_ADD_BULK = 7; + ZEBRA_MLAG_MROUTE_DEL_BULK = 8; + ZEBRA_MLAG_PIM_CFG_DUMP = 10; + ZEBRA_MLAG_VXLAN_UPDATE = 11; + ZEBRA_MLAG_ZEBRA_STATUS_UPDATE = 12; + } + + /* + * tells what type of message this payload carries + */ + MessageType type = 1; + + /* + * Length of payload + */ + uint32 len = 2; + + /* + * Actual Encoded payload + */ + bytes data = 3; +} + + +/* + * ZEBRA_MLAG_REGISTER & ZEBRA_MLAG_DEREGISTER + * + * After the MLAGD is up, First Zebra has to register to send any data, + * otherwise MLAGD will not accept any data from the client. + * De-register will be used for the Data cleanup at MLAGD + * These are NULL payload message currently + */ + +/* + * ZEBRA_MLAG_STATUS_UPDATE + * + * This message will be posted by CLAGD(an external control plane manager + * which monitors MCLAG failures) to inform peerlink/CLAG Failure + * to zebra, after the failure Notification Node with primary role will + * forward the Traffic and Node with standby will drop the traffic + */ + +message ZebraMlagStatusUpdate { + enum ClagState { + CLAG_STATE_DOWN = 0; + CLAG_STATE_RUNNING = 1; + } + + enum ClagRole { + CLAG_ROLE_NONE = 0; + CLAG_ROLE_PRIMAY = 1; + CLAG_ROLE_SECONDARY = 2; + } + + string peerlink = 1; + ClagRole my_role = 2; + ClagState peer_state = 3; +} + +/* + * ZEBRA_MLAG_VXLAN_UPDATE + * + * This message will be posted by CLAGD(an external control plane Manager + * which is responsible for MCLAG) to inform zebra obout anycast/local + * ip updates. + */ +message ZebraMlagVxlanUpdate { + uint32 anycast_ip = 1; + uint32 local_ip = 2; +} + +/* + * ZebraMlagZebraStatusUpdate + * + * This message will be posted by CLAGD to advertise FRR state + * Change Information to peer + */ + +message ZebraMlagZebraStatusUpdate{ + enum FrrState { + FRR_STATE_NONE = 0; + FRR_STATE_DOWN = 1; + FRR_STATE_UP = 2; + } + + FrrState peer_frrstate = 1; +} + +/* + * ZEBRA_MLAG_MROUTE_ADD & ZEBRA_MLAG_MROUTE_DEL + * + * These meesages will be sent from PIM (Node-1) to PIM (Node-2) to perform + * DF Election for each Mcast flow. Elected DF will forward the tarffic + * towards the host and loser will keep the OIL as empty, sothat only single + * copy will be sent to host + * This message will be posted with any chnage in the params. + * + * ZEBRA_MLAG_MROUTE_DEL is mainly to delete the record at MLAGD when the + * mcast flow is deleted. + * key for the MLAGD lookup is (vrf_id, source_ip & group_ip) + */ + +message ZebraMlagMrouteAdd { + string vrf_name = 1; + uint32 source_ip = 2; + uint32 group_ip = 3; + /* + * This is the IGP Cost to reach Configured RP in case of (*,G) or + * Cost to the source in case of (S,G) entry + */ + uint32 cost_to_rp = 4; + uint32 owner_id = 5; + bool am_i_DR = 6; + bool am_i_Dual_active = 7; + uint32 vrf_id = 8; + string intf_name = 9; +} + +message ZebraMlagMrouteDel { + string vrf_name = 1; + uint32 source_ip = 2; + uint32 group_ip = 3; + uint32 owner_id = 4; + uint32 vrf_id = 5; + string intf_name = 6; +} + +message ZebraMlagMrouteAddBulk { + repeated ZebraMlagMrouteAdd mroute_add = 1; +} + +message ZebraMlagMrouteDelBulk { + repeated ZebraMlagMrouteDel mroute_del = 1; +} + +// [END messages] diff --git a/mlag/subdir.am b/mlag/subdir.am new file mode 100644 index 0000000000..9fab662860 --- /dev/null +++ b/mlag/subdir.am @@ -0,0 +1,19 @@ +if HAVE_PROTOBUF +lib_LTLIBRARIES += mlag/libmlag_pb.la +endif + +mlag_libmlag_pb_la_LDFLAGS = -version-info 0:0:0 +mlag_libmlag_pb_la_CPPFLAGS = $(AM_CPPFLAGS) $(PROTOBUF_C_CFLAGS) +mlag_libmlag_pb_la_SOURCES = \ + # end + +nodist_mlag_libmlag_pb_la_SOURCES = \ + mlag/mlag.pb-c.c \ + # end + +CLEANFILES += \ + mlag/mlag.pb-c.c \ + mlag/mlag.pb-c.h \ + # end + +EXTRA_DIST += mlag/mlag.proto diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 28b4af9457..2c2b1c9586 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -61,6 +61,7 @@ #include "pim_nht.h" #include "pim_bfd.h" #include "pim_vxlan.h" +#include "pim_mlag.h" #include "bfd.h" #include "pim_bsm.h" @@ -7462,9 +7463,9 @@ DEFPY_HIDDEN (interface_ip_pim_activeactive, pim_ifp = ifp->info; if (no) - pim_ifp->activeactive = false; + pim_if_unconfigure_mlag_dualactive(pim_ifp); else - pim_ifp->activeactive = true; + pim_if_configure_mlag_dualactive(pim_ifp); return CMD_SUCCESS; } @@ -8382,6 +8383,20 @@ DEFUN (no_debug_pim_zebra, return CMD_SUCCESS; } +DEFUN(debug_pim_mlag, debug_pim_mlag_cmd, "debug pim mlag", + DEBUG_STR DEBUG_PIM_STR DEBUG_PIM_MLAG_STR) +{ + PIM_DO_DEBUG_MLAG; + return CMD_SUCCESS; +} + +DEFUN(no_debug_pim_mlag, no_debug_pim_mlag_cmd, "no debug pim mlag", + NO_STR DEBUG_STR DEBUG_PIM_STR DEBUG_PIM_MLAG_STR) +{ + PIM_DONT_DEBUG_MLAG; + return CMD_SUCCESS; +} + DEFUN (debug_pim_vxlan, debug_pim_vxlan_cmd, "debug pim vxlan", @@ -10408,6 +10423,8 @@ void pim_cmd_init(void) install_element(ENABLE_NODE, &no_debug_ssmpingd_cmd); install_element(ENABLE_NODE, &debug_pim_zebra_cmd); install_element(ENABLE_NODE, &no_debug_pim_zebra_cmd); + install_element(ENABLE_NODE, &debug_pim_mlag_cmd); + install_element(ENABLE_NODE, &no_debug_pim_mlag_cmd); install_element(ENABLE_NODE, &debug_pim_vxlan_cmd); install_element(ENABLE_NODE, &no_debug_pim_vxlan_cmd); install_element(ENABLE_NODE, &debug_msdp_cmd); diff --git a/pimd/pim_cmd.h b/pimd/pim_cmd.h index f5bb316a7a..9f05bc74e5 100644 --- a/pimd/pim_cmd.h +++ b/pimd/pim_cmd.h @@ -54,6 +54,7 @@ #define DEBUG_PIM_PACKETDUMP_RECV_STR "Dump received packets\n" #define DEBUG_PIM_TRACE_STR "PIM internal daemon activity\n" #define DEBUG_PIM_ZEBRA_STR "ZEBRA protocol activity\n" +#define DEBUG_PIM_MLAG_STR "PIM Mlag activity\n" #define DEBUG_PIM_VXLAN_STR "PIM VxLAN events\n" #define DEBUG_SSMPINGD_STR "ssmpingd activity\n" #define CLEAR_IP_IGMP_STR "IGMP clear commands\n" diff --git a/pimd/pim_instance.c b/pimd/pim_instance.c index 6848d2dabb..955ad14b01 100644 --- a/pimd/pim_instance.c +++ b/pimd/pim_instance.c @@ -34,6 +34,7 @@ #include "pim_ssmpingd.h" #include "pim_vty.h" #include "pim_bsm.h" +#include "pim_mlag.h" static void pim_instance_terminate(struct pim_instance *pim) { @@ -47,6 +48,8 @@ static void pim_instance_terminate(struct pim_instance *pim) if (pim->static_routes) list_delete(&pim->static_routes); + pim_instance_mlag_terminate(pim); + pim_upstream_terminate(pim); pim_rp_free(pim); @@ -115,6 +118,8 @@ static struct pim_instance *pim_instance_init(struct vrf *vrf) pim_upstream_init(pim); + pim_instance_mlag_init(pim); + pim->last_route_change_time = -1; return pim; } diff --git a/pimd/pim_instance.h b/pimd/pim_instance.h index 06d41c4b53..dd3ac8fcb0 100644 --- a/pimd/pim_instance.h +++ b/pimd/pim_instance.h @@ -64,6 +64,17 @@ struct pim_router { vrf_id_t vrf_id; enum mlag_role role; + uint32_t pim_mlag_intf_cnt; + /* if true we have registered with MLAG */ + bool mlag_process_register; + /* if true local MLAG process reported that it is connected + * with the peer MLAG process + */ + bool connected_to_mlag; + /* Holds the client data(unencoded) that need to be pushed to MCLAGD*/ + struct stream_fifo *mlag_fifo; + struct stream *mlag_stream; + struct thread *zpthread_mlag_write; }; /* Per VRF PIM DB */ @@ -122,6 +133,9 @@ struct pim_instance { bool ecmp_enable; bool ecmp_rebalance_enable; + /* No. of Dual active I/fs in pim_instance */ + uint32_t inst_mlag_intf_cnt; + /* Bsm related */ struct bsm_scope global_scope; uint64_t bsm_rcvd; diff --git a/pimd/pim_main.c b/pimd/pim_main.c index 6a7dbe769f..4090ce7f93 100644 --- a/pimd/pim_main.c +++ b/pimd/pim_main.c @@ -47,6 +47,7 @@ #include "pim_msdp.h" #include "pim_iface.h" #include "pim_bfd.h" +#include "pim_mlag.h" #include "pim_errors.h" extern struct host host; @@ -131,6 +132,7 @@ int main(int argc, char **argv, char **envp) pim_ifp_down, pim_ifp_destroy); pim_zebra_init(); pim_bfd_init(); + pim_mlag_init(); frr_config_fork(); diff --git a/pimd/pim_mlag.c b/pimd/pim_mlag.c new file mode 100644 index 0000000000..1fa5c49bd5 --- /dev/null +++ b/pimd/pim_mlag.c @@ -0,0 +1,344 @@ +/* PIM Mlag Code. + * Copyright (C) 2019 Cumulus Networks, Inc. + * Donald Sharp + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#include + +#include "pimd.h" +#include "pim_mlag.h" + +extern struct zclient *zclient; + + +/********************API to process PIM MLAG Data ************************/ + +static void pim_mlag_process_mlagd_state_change(struct mlag_status msg) +{ + char buf[80]; + + if (PIM_DEBUG_MLAG) + zlog_debug("%s: msg dump: my_role:%s, peer_state:%s", __func__, + mlag_role2str(msg.my_role, buf, sizeof(buf)), + (msg.peer_state == MLAG_STATE_RUNNING ? "RUNNING" + : "DOWN")); +} + +static void pim_mlag_process_peer_frr_state_change(struct mlag_frr_status msg) +{ + if (PIM_DEBUG_MLAG) + zlog_debug( + "%s: msg dump: peer_frr_state:%s", __func__, + (msg.frr_state == MLAG_FRR_STATE_UP ? "UP" : "DOWN")); +} + +static void pim_mlag_process_vxlan_update(struct mlag_vxlan *msg) +{ +} + +static void pim_mlag_process_mroute_add(struct mlag_mroute_add msg) +{ + if (PIM_DEBUG_MLAG) { + zlog_debug( + "%s: msg dump: vrf_name:%s, s.ip:0x%x, g.ip:0x%x cost:%u", + __func__, msg.vrf_name, msg.source_ip, msg.group_ip, + msg.cost_to_rp); + zlog_debug( + "owner_id:%d, DR:%d, Dual active:%d, vrf_id:0x%x intf_name:%s", + msg.owner_id, msg.am_i_dr, msg.am_i_dual_active, + msg.vrf_id, msg.intf_name); + } +} + +static void pim_mlag_process_mroute_del(struct mlag_mroute_del msg) +{ + if (PIM_DEBUG_MLAG) { + zlog_debug("%s: msg dump: vrf_name:%s, s.ip:0x%x, g.ip:0x%x ", + __func__, msg.vrf_name, msg.source_ip, msg.group_ip); + zlog_debug("owner_id:%d, vrf_id:0x%x intf_name:%s", + msg.owner_id, msg.vrf_id, msg.intf_name); + } +} + + +int pim_zebra_mlag_handle_msg(struct stream *s, int len) +{ + struct mlag_msg mlag_msg; + char buf[80]; + int rc = 0; + + rc = zebra_mlag_lib_decode_mlag_hdr(s, &mlag_msg); + if (rc) + return (rc); + + if (PIM_DEBUG_MLAG) + zlog_debug( + "%s: Received msg type:%s length:%d, bulk_cnt:%d", + __func__, + zebra_mlag_lib_msgid_to_str(mlag_msg.msg_type, buf, 80), + mlag_msg.data_len, mlag_msg.msg_cnt); + + switch (mlag_msg.msg_type) { + case MLAG_STATUS_UPDATE: { + struct mlag_status msg; + + rc = zebra_mlag_lib_decode_mlag_status(s, &msg); + if (rc) + return (rc); + pim_mlag_process_mlagd_state_change(msg); + } break; + case MLAG_PEER_FRR_STATUS: { + struct mlag_frr_status msg; + + rc = zebra_mlag_lib_decode_frr_status(s, &msg); + if (rc) + return (rc); + pim_mlag_process_peer_frr_state_change(msg); + } break; + case MLAG_VXLAN_UPDATE: { + struct mlag_vxlan msg; + + rc = zebra_mlag_lib_decode_vxlan_update(s, &msg); + if (rc) + return rc; + pim_mlag_process_vxlan_update(&msg); + } break; + case MLAG_MROUTE_ADD: { + struct mlag_mroute_add msg; + + rc = zebra_mlag_lib_decode_mroute_add(s, &msg); + if (rc) + return (rc); + pim_mlag_process_mroute_add(msg); + } break; + case MLAG_MROUTE_DEL: { + struct mlag_mroute_del msg; + + rc = zebra_mlag_lib_decode_mroute_del(s, &msg); + if (rc) + return (rc); + pim_mlag_process_mroute_del(msg); + } break; + case MLAG_MROUTE_ADD_BULK: { + struct mlag_mroute_add msg; + int i = 0; + + for (i = 0; i < mlag_msg.msg_cnt; i++) { + + rc = zebra_mlag_lib_decode_mroute_add(s, &msg); + if (rc) + return (rc); + pim_mlag_process_mroute_add(msg); + } + } break; + case MLAG_MROUTE_DEL_BULK: { + struct mlag_mroute_del msg; + int i = 0; + + for (i = 0; i < mlag_msg.msg_cnt; i++) { + + rc = zebra_mlag_lib_decode_mroute_del(s, &msg); + if (rc) + return (rc); + pim_mlag_process_mroute_del(msg); + } + } break; + default: + break; + } + return 0; +} + +/****************End of PIM Mesasge processing handler********************/ + +int pim_zebra_mlag_process_up(void) +{ + if (PIM_DEBUG_MLAG) + zlog_debug("%s: Received Process-Up from Mlag", __func__); + + return 0; +} + +int pim_zebra_mlag_process_down(void) +{ + if (PIM_DEBUG_MLAG) + zlog_debug("%s: Received Process-Down from Mlag", __func__); + + return 0; +} + +static int pim_mlag_register_handler(struct thread *thread) +{ + uint32_t bit_mask = 0; + + if (!zclient) + return -1; + + SET_FLAG(bit_mask, (1 << MLAG_STATUS_UPDATE)); + SET_FLAG(bit_mask, (1 << MLAG_MROUTE_ADD)); + SET_FLAG(bit_mask, (1 << MLAG_MROUTE_DEL)); + SET_FLAG(bit_mask, (1 << MLAG_DUMP)); + SET_FLAG(bit_mask, (1 << MLAG_MROUTE_ADD_BULK)); + SET_FLAG(bit_mask, (1 << MLAG_MROUTE_DEL_BULK)); + SET_FLAG(bit_mask, (1 << MLAG_PIM_CFG_DUMP)); + SET_FLAG(bit_mask, (1 << MLAG_VXLAN_UPDATE)); + SET_FLAG(bit_mask, (1 << MLAG_PEER_FRR_STATUS)); + + if (PIM_DEBUG_MLAG) + zlog_debug("%s: Posting Client Register to MLAG mask:0x%x", + __func__, bit_mask); + + zclient_send_mlag_register(zclient, bit_mask); + return 0; +} + +void pim_mlag_register(void) +{ + if (router->mlag_process_register) + return; + + router->mlag_process_register = true; + + thread_add_event(router->master, pim_mlag_register_handler, NULL, 0, + NULL); +} + +static int pim_mlag_deregister_handler(struct thread *thread) +{ + if (!zclient) + return -1; + + if (PIM_DEBUG_MLAG) + zlog_debug("%s: Posting Client De-Register to MLAG from PIM", + __func__); + router->connected_to_mlag = false; + zclient_send_mlag_deregister(zclient); + return 0; +} + +void pim_mlag_deregister(void) +{ + /* if somebody still interested in the MLAG channel skip de-reg */ + if (router->pim_mlag_intf_cnt) + return; + + /* not registered; nothing do */ + if (!router->mlag_process_register) + return; + + router->mlag_process_register = false; + + thread_add_event(router->master, pim_mlag_deregister_handler, NULL, 0, + NULL); +} + +void pim_if_configure_mlag_dualactive(struct pim_interface *pim_ifp) +{ + if (!pim_ifp || !pim_ifp->pim || pim_ifp->activeactive == true) + return; + + if (PIM_DEBUG_MLAG) + zlog_debug("%s: Configuring active-active on Interface: %s", + __func__, "NULL"); + + pim_ifp->activeactive = true; + if (pim_ifp->pim) + pim_ifp->pim->inst_mlag_intf_cnt++; + + router->pim_mlag_intf_cnt++; + if (PIM_DEBUG_MLAG) + zlog_debug( + "%s: Total MLAG configured Interfaces on router: %d, Inst:%d", + __func__, router->pim_mlag_intf_cnt, + pim_ifp->pim->inst_mlag_intf_cnt); + + if (router->pim_mlag_intf_cnt == 1) { + /* + * atleast one Interface is configured for MLAG, send register + * to Zebra for receiving MLAG Updates + */ + pim_mlag_register(); + } +} + +void pim_if_unconfigure_mlag_dualactive(struct pim_interface *pim_ifp) +{ + if (!pim_ifp || !pim_ifp->pim || pim_ifp->activeactive == false) + return; + + if (PIM_DEBUG_MLAG) + zlog_debug("%s: UnConfiguring active-active on Interface: %s", + __func__, "NULL"); + + pim_ifp->activeactive = false; + if (pim_ifp->pim) + pim_ifp->pim->inst_mlag_intf_cnt--; + + router->pim_mlag_intf_cnt--; + if (PIM_DEBUG_MLAG) + zlog_debug( + "%s: Total MLAG configured Interfaces on router: %d, Inst:%d", + __func__, router->pim_mlag_intf_cnt, + pim_ifp->pim->inst_mlag_intf_cnt); + + if (router->pim_mlag_intf_cnt == 0) { + /* + * all the Interfaces are MLAG un-configured, post MLAG + * De-register to Zebra + */ + pim_mlag_deregister(); + } +} + + +void pim_instance_mlag_init(struct pim_instance *pim) +{ + if (!pim) + return; + + pim->inst_mlag_intf_cnt = 0; +} + + +void pim_instance_mlag_terminate(struct pim_instance *pim) +{ + struct interface *ifp; + + if (!pim) + return; + + FOR_ALL_INTERFACES (pim->vrf, ifp) { + struct pim_interface *pim_ifp = ifp->info; + + if (!pim_ifp || pim_ifp->activeactive == false) + continue; + + pim_if_unconfigure_mlag_dualactive(pim_ifp); + } + pim->inst_mlag_intf_cnt = 0; +} + +void pim_mlag_init(void) +{ + router->pim_mlag_intf_cnt = 0; + router->connected_to_mlag = false; + router->mlag_fifo = stream_fifo_new(); + router->zpthread_mlag_write = NULL; + router->mlag_stream = stream_new(MLAG_BUF_LIMIT); +} diff --git a/pimd/pim_mlag.h b/pimd/pim_mlag.h new file mode 100644 index 0000000000..87a57ca29a --- /dev/null +++ b/pimd/pim_mlag.h @@ -0,0 +1,48 @@ +/* PIM mlag header. + * Copyright (C) 2019 Cumulus Networks, Inc. + * Donald Sharp + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#ifndef __PIM_MLAG_H__ +#define __PIM_MLAG_H__ + +#include "mlag.h" +#include "pim_iface.h" + +extern void pim_mlag_init(void); + +extern void pim_instance_mlag_init(struct pim_instance *pim); + +extern void pim_instance_mlag_terminate(struct pim_instance *pim); + +extern void pim_if_configure_mlag_dualactive(struct pim_interface *pim_ifp); + +extern void pim_if_unconfigure_mlag_dualactive(struct pim_interface *pim_ifp); + +extern void pim_mlag_register(void); + +extern void pim_mlag_deregister(void); + +extern int pim_zebra_mlag_process_up(void); + +extern int pim_zebra_mlag_process_down(void); + +extern int pim_zebra_mlag_handle_msg(struct stream *msg, int len); + +#endif diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index dadcbbe65d..b517b998e3 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -46,11 +46,12 @@ #include "pim_nht.h" #include "pim_ssm.h" #include "pim_vxlan.h" +#include "pim_mlag.h" #undef PIM_DEBUG_IFADDR_DUMP #define PIM_DEBUG_IFADDR_DUMP -static struct zclient *zclient = NULL; +struct zclient *zclient; /* Router-id update message from zebra. */ @@ -587,6 +588,9 @@ void pim_zebra_init(void) zclient->nexthop_update = pim_parse_nexthop_update; zclient->vxlan_sg_add = pim_zebra_vxlan_sg_proc; zclient->vxlan_sg_del = pim_zebra_vxlan_sg_proc; + zclient->mlag_process_up = pim_zebra_mlag_process_up; + zclient->mlag_process_down = pim_zebra_mlag_process_down; + zclient->mlag_handle_msg = pim_zebra_mlag_handle_msg; zclient_init(zclient, ZEBRA_ROUTE_PIM, 0, &pimd_privs); if (PIM_DEBUG_PIM_TRACE) { diff --git a/pimd/pimd.h b/pimd/pimd.h index 3b83d3b6c7..b7e6d916a8 100644 --- a/pimd/pimd.h +++ b/pimd/pimd.h @@ -115,6 +115,7 @@ #define PIM_MASK_MTRACE (1 << 25) #define PIM_MASK_VXLAN (1 << 26) #define PIM_MASK_BSM_PROC (1 << 27) +#define PIM_MASK_MLAG (1 << 28) /* Remember 32 bits!!! */ /* PIM error codes */ @@ -171,6 +172,7 @@ extern uint8_t qpim_ecmp_rebalance_enable; #define PIM_DEBUG_IGMP_TRACE_DETAIL \ (router->debugs & (PIM_MASK_IGMP_TRACE_DETAIL | PIM_MASK_IGMP_TRACE)) #define PIM_DEBUG_ZEBRA (router->debugs & PIM_MASK_ZEBRA) +#define PIM_DEBUG_MLAG (router->debugs & PIM_MASK_MLAG) #define PIM_DEBUG_SSMPINGD (router->debugs & PIM_MASK_SSMPINGD) #define PIM_DEBUG_MROUTE (router->debugs & PIM_MASK_MROUTE) #define PIM_DEBUG_MROUTE_DETAIL \ @@ -217,6 +219,7 @@ extern uint8_t qpim_ecmp_rebalance_enable; #define PIM_DO_DEBUG_IGMP_TRACE_DETAIL \ (router->debugs |= PIM_MASK_IGMP_TRACE_DETAIL) #define PIM_DO_DEBUG_ZEBRA (router->debugs |= PIM_MASK_ZEBRA) +#define PIM_DO_DEBUG_MLAG (router->debugs |= PIM_MASK_MLAG) #define PIM_DO_DEBUG_SSMPINGD (router->debugs |= PIM_MASK_SSMPINGD) #define PIM_DO_DEBUG_MROUTE (router->debugs |= PIM_MASK_MROUTE) #define PIM_DO_DEBUG_MROUTE_DETAIL (router->debugs |= PIM_MASK_MROUTE_DETAIL) @@ -248,6 +251,7 @@ extern uint8_t qpim_ecmp_rebalance_enable; #define PIM_DONT_DEBUG_IGMP_TRACE_DETAIL \ (router->debugs &= ~PIM_MASK_IGMP_TRACE_DETAIL) #define PIM_DONT_DEBUG_ZEBRA (router->debugs &= ~PIM_MASK_ZEBRA) +#define PIM_DONT_DEBUG_MLAG (router->debugs &= ~PIM_MASK_MLAG) #define PIM_DONT_DEBUG_SSMPINGD (router->debugs &= ~PIM_MASK_SSMPINGD) #define PIM_DONT_DEBUG_MROUTE (router->debugs &= ~PIM_MASK_MROUTE) #define PIM_DONT_DEBUG_MROUTE_DETAIL (router->debugs &= ~PIM_MASK_MROUTE_DETAIL) diff --git a/pimd/subdir.am b/pimd/subdir.am index 240b62804f..5407e566a5 100644 --- a/pimd/subdir.am +++ b/pimd/subdir.am @@ -62,6 +62,7 @@ pimd_libpim_a_SOURCES = \ pimd/pim_zebra.c \ pimd/pim_zlookup.c \ pimd/pim_vxlan.c \ + pimd/pim_mlag.c \ pimd/pimd.c \ # end @@ -114,6 +115,7 @@ noinst_HEADERS += \ pimd/pim_zebra.h \ pimd/pim_zlookup.h \ pimd/pim_vxlan.h \ + pimd/pim_mlag.h \ pimd/pim_vxlan_instance.h \ pimd/pimd.h \ pimd/mtracebis_netlink.h \ diff --git a/zebra/subdir.am b/zebra/subdir.am index 25040a2717..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 \ @@ -66,6 +69,7 @@ zebra_zebra_SOURCES = \ zebra/rule_netlink.c \ zebra/rule_socket.c \ zebra/zebra_mlag.c \ + zebra/zebra_mlag_private.c \ zebra/zebra_l2.c \ zebra/zebra_memory.c \ zebra/zebra_dplane.c \ @@ -130,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/zapi_msg.c b/zebra/zapi_msg.c index b0488b7559..5820032902 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -2551,6 +2551,9 @@ void (*zserv_handlers[])(ZAPI_HANDLER_ARGS) = { [ZEBRA_IPTABLE_DELETE] = zread_iptable, [ZEBRA_VXLAN_FLOOD_CONTROL] = zebra_vxlan_flood_control, [ZEBRA_VXLAN_SG_REPLAY] = zebra_vxlan_sg_replay, + [ZEBRA_MLAG_CLIENT_REGISTER] = zebra_mlag_client_register, + [ZEBRA_MLAG_CLIENT_UNREGISTER] = zebra_mlag_client_unregister, + [ZEBRA_MLAG_FORWARD_MSG] = zebra_mlag_forward_client_msg, }; #if defined(HANDLE_ZAPI_FUZZING) diff --git a/zebra/zebra_mlag.c b/zebra/zebra_mlag.c index 5012cc2a49..633ebe95cb 100644 --- a/zebra/zebra_mlag.c +++ b/zebra/zebra_mlag.c @@ -1,5 +1,5 @@ /* Zebra Mlag Code. - * Copyright (C) 2018 Cumulus Networks, Inc. + * Copyright (C) 2019 Cumulus Networks, Inc. * Donald Sharp * * This file is part of FRR. @@ -23,9 +23,13 @@ #include "command.h" #include "hook.h" +#include "frr_pthread.h" +#include "mlag.h" #include "zebra/zebra_mlag.h" +#include "zebra/zebra_mlag_private.h" #include "zebra/zebra_router.h" +#include "zebra/zebra_memory.h" #include "zebra/zapi_msg.h" #include "zebra/debug.h" @@ -33,6 +37,551 @@ #include "zebra/zebra_mlag_clippy.c" #endif +#define ZEBRA_MLAG_METADATA_LEN 4 +#define ZEBRA_MLAG_MSG_BCAST 0xFFFFFFFF +#define MAXCH_LEN 80 + +uint8_t mlag_wr_buffer[ZEBRA_MLAG_BUF_LIMIT]; +uint8_t mlag_rd_buffer[ZEBRA_MLAG_BUF_LIMIT]; +uint32_t mlag_rd_buf_offset; + +static bool test_mlag_in_progress; + +static int zebra_mlag_signal_write_thread(void); +static int zebra_mlag_terminate_pthread(struct thread *event); +static int zebra_mlag_post_data_from_main_thread(struct thread *thread); +static void zebra_mlag_publish_process_state(struct zserv *client, + zebra_message_types_t msg_type); + +/**********************MLAG Interaction***************************************/ + +/* + * API to post the Registartion to MLAGD + * MLAG will not process any messages with out the registration + */ +void zebra_mlag_send_register(void) +{ + struct stream *s = NULL; + + s = stream_new(sizeof(struct mlag_msg)); + if (!s) + return; + + stream_putl(s, MLAG_REGISTER); + stream_putw(s, MLAG_MSG_NULL_PAYLOAD); + stream_putw(s, MLAG_MSG_NO_BATCH); + stream_fifo_push_safe(zrouter.mlag_info.mlag_fifo, s); + zebra_mlag_signal_write_thread(); + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("%s: Enqueued MLAG Register to MLAG Thread ", + __func__); +} + +/* + * API to post the De-Registartion to MLAGD + * MLAG will not process any messages after the de-registration + */ +void zebra_mlag_send_deregister(void) +{ + struct stream *s = NULL; + + s = stream_new(sizeof(struct mlag_msg)); + if (!s) + return; + + stream_putl(s, MLAG_DEREGISTER); + stream_putw(s, MLAG_MSG_NULL_PAYLOAD); + stream_putw(s, MLAG_MSG_NO_BATCH); + stream_fifo_push_safe(zrouter.mlag_info.mlag_fifo, s); + zebra_mlag_signal_write_thread(); + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("%s: Enqueued MLAG De-Register to MLAG Thread ", + __func__); +} + +/* + * API To handle MLAG Received data + * Decodes teh data using protobuf and enqueue to main thread + * main thread publish this to clients based on client subscrption + */ +void zebra_mlag_process_mlag_data(uint8_t *data, uint32_t len) +{ + struct stream *s = NULL; + struct stream *s1 = NULL; + int msg_type = 0; + + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + if (s) + msg_type = zebra_mlag_protobuf_decode_message(&s, data, len); + + if (msg_type <= 0) { + /* Something went wrong in decoding */ + stream_free(s); + zlog_err("%s: failed to process mlag data-%d, %u", __func__, + msg_type, len); + return; + } + + /* + * additional four bytes are for mesasge type + */ + s1 = stream_new(stream_get_endp(s) + ZEBRA_MLAG_METADATA_LEN); + stream_putl(s1, msg_type); + stream_put(s1, s->data, stream_get_endp(s)); + thread_add_event(zrouter.master, zebra_mlag_post_data_from_main_thread, + s1, 0, NULL); + stream_free(s); +} + +/**********************End of MLAG Interaction********************************/ + +/************************MLAG Thread Processing*******************************/ + +/* + * after posting every 1000 packets, MLAG Thread wll be yielded to give CPU + * for other threads + */ +#define ZEBRA_MLAG_POST_LIMIT 100 + +/* + * Thsi thread reads the clients data from the Gloabl queue and encodes with + * protobuf and pass on to the MLAG socket. + */ +static int zebra_mlag_thread_handler(struct thread *event) +{ + struct stream *s; + uint32_t wr_count = 0; + uint32_t msg_type = 0; + uint32_t max_count = 0; + int len = 0; + + wr_count = stream_fifo_count_safe(zrouter.mlag_info.mlag_fifo); + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug(":%s: Processing MLAG write, %d messages in queue", + __func__, wr_count); + + pthread_mutex_lock(&zrouter.mlag_info.mlag_th_mtx); + zrouter.mlag_info.t_write = NULL; + pthread_mutex_unlock(&zrouter.mlag_info.mlag_th_mtx); + + max_count = MIN(wr_count, ZEBRA_MLAG_POST_LIMIT); + + for (wr_count = 0; wr_count < max_count; wr_count++) { + s = stream_fifo_pop_safe(zrouter.mlag_info.mlag_fifo); + if (!s) { + zlog_debug(":%s: Got a NULL Messages, some thing wrong", + __func__); + break; + } + + /* + * Encode the data now + */ + len = zebra_mlag_protobuf_encode_client_data(s, &msg_type); + + /* + * write to MCLAGD + */ + if (len > 0) { + zebra_mlag_private_write_data(mlag_wr_buffer, len); + + /* + * If mesasge type is De-register, send a signal to main + * thread, sothat necessary cleanup will be done by main + * thread. + */ + if (msg_type == MLAG_DEREGISTER) { + thread_add_event(zrouter.master, + zebra_mlag_terminate_pthread, + NULL, 0, NULL); + } + } + + stream_free(s); + } + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug(":%s: Posted %d messages to MLAGD", __func__, + wr_count); + /* + * Currently there is only message write task is enqueued to this + * thread, yielding was added for future purpose, sothat this thread can + * server other tasks also and in case FIFO is empty, this task will be + * schedule when main thread adds some messages + */ + if (wr_count >= ZEBRA_MLAG_POST_LIMIT) + zebra_mlag_signal_write_thread(); + return 0; +} + +/* + * API to handle teh process state. + * In case of Down, Zebra keep monitoring the MLAG state. + * all the state Notifications will be published to clients + */ +void zebra_mlag_handle_process_state(enum zebra_mlag_state state) +{ + if (state == MLAG_UP) { + zrouter.mlag_info.connected = true; + zebra_mlag_publish_process_state(NULL, ZEBRA_MLAG_PROCESS_UP); + zebra_mlag_send_register(); + } else if (state == MLAG_DOWN) { + zrouter.mlag_info.connected = false; + zebra_mlag_publish_process_state(NULL, ZEBRA_MLAG_PROCESS_DOWN); + zebra_mlag_private_monitor_state(); + } +} + +/***********************End of MLAG Thread processing*************************/ + +/*************************Multi-entratnt Api's********************************/ + +/* + * Provider api to signal that work/events are available + * for the Zebra MLAG Write pthread. + * This API is called from 2 pthreads.. + * 1) by main thread when client posts a MLAG Message + * 2) by MLAG Thread, in case of yield + * though this api, is called from two threads we don't need any locking + * because Thread task enqueue is thread safe means internally it had + * necessary protection + */ +static int zebra_mlag_signal_write_thread(void) +{ + pthread_mutex_lock(&zrouter.mlag_info.mlag_th_mtx); + if (zrouter.mlag_info.zebra_pth_mlag) { + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug(":%s: Scheduling MLAG write", __func__); + thread_add_event(zrouter.mlag_info.th_master, + zebra_mlag_thread_handler, NULL, 0, + &zrouter.mlag_info.t_write); + } + pthread_mutex_unlock(&zrouter.mlag_info.mlag_th_mtx); + return 0; +} + +/* + * API will be used to publish the MLAG state to interested clients + * In case client is passed, state is posted only for that client, + * otherwise to all interested clients + * this api can be called from two threads. + * 1) from main thread: when client is passed + * 2) from MLAG Thread: when client is NULL + * + * In second case, to avoid global data access data will be post to Main + * thread, so that actual posting to cleints will happen from Main thread. + */ +static void zebra_mlag_publish_process_state(struct zserv *client, + zebra_message_types_t msg_type) +{ + struct stream *s = NULL; + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("%s: Publishing MLAG process state:%s to %s Client", + __func__, + (msg_type == ZEBRA_MLAG_PROCESS_UP) ? "UP" : "DOWN", + (client) ? "one" : "all"); + + if (client) { + s = stream_new(ZEBRA_HEADER_SIZE); + zclient_create_header(s, msg_type, VRF_DEFAULT); + zserv_send_message(client, s); + return; + } + + + /* + * additional four bytes are for mesasge type + */ + s = stream_new(ZEBRA_HEADER_SIZE + ZEBRA_MLAG_METADATA_LEN); + stream_putl(s, ZEBRA_MLAG_MSG_BCAST); + zclient_create_header(s, msg_type, VRF_DEFAULT); + thread_add_event(zrouter.master, zebra_mlag_post_data_from_main_thread, + s, 0, NULL); +} + +/**************************End of Multi-entrant Apis**************************/ + +/***********************Zebra Main thread processing**************************/ + +/* + * To avoid data corruption, messages will be post to clients only from + * main thread, beacuse for that access was needed for clients list. + * so instaed of forcing the locks, messages will be posted from main thread. + */ +static int zebra_mlag_post_data_from_main_thread(struct thread *thread) +{ + struct stream *s = THREAD_ARG(thread); + struct stream *zebra_s = NULL; + struct listnode *node; + struct zserv *client; + uint32_t msg_type = 0; + uint32_t msg_len = 0; + + if (!s) + return -1; + + STREAM_GETL(s, msg_type); + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug( + "%s: Posting MLAG data for msg_type:0x%x to interested cleints", + __func__, msg_type); + + msg_len = s->endp - ZEBRA_MLAG_METADATA_LEN; + for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client)) { + if (client->mlag_updates_interested == true) { + if (msg_type != ZEBRA_MLAG_MSG_BCAST + && !CHECK_FLAG(client->mlag_reg_mask1, + (1 << msg_type))) { + continue; + } + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug( + "%s: Posting MLAG data of length-%d to client:%d ", + __func__, msg_len, client->proto); + + zebra_s = stream_new(msg_len); + STREAM_GET(zebra_s->data, s, msg_len); + zebra_s->endp = msg_len; + stream_putw_at(zebra_s, 0, msg_len); + + /* + * This stream will be enqueued to client_obuf, it will + * be freed after posting to client socket. + */ + zserv_send_message(client, zebra_s); + zebra_s = NULL; + } + } + + stream_free(s); + return 0; +stream_failure: + stream_free(s); + if (zebra_s) + stream_free(zebra_s); + return 0; +} + +/* + * Start the MLAG Thread, this will be used to write client data on to + * MLAG Process and to read the data from MLAG and post to cleints. + * when all clients are un-registered, this Thread will be + * suspended. + */ +static void zebra_mlag_spawn_pthread(void) +{ + /* Start MLAG write pthread */ + + struct frr_pthread_attr pattr = {.start = + frr_pthread_attr_default.start, + .stop = frr_pthread_attr_default.stop}; + + zrouter.mlag_info.zebra_pth_mlag = + frr_pthread_new(&pattr, "Zebra MLAG thread", "Zebra MLAG"); + + zrouter.mlag_info.th_master = zrouter.mlag_info.zebra_pth_mlag->master; + + + /* 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); +} + +/* + * all clients are un-registered for MLAG Updates, terminate the + * MLAG write thread + */ +static int zebra_mlag_terminate_pthread(struct thread *event) +{ + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("Zebra MLAG write thread terminate calleid"); + + if (zrouter.mlag_info.clients_interested_cnt) { + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug( + "Zebra MLAG: still some clients are interested"); + return 0; + } + + frr_pthread_stop(zrouter.mlag_info.zebra_pth_mlag, NULL); + + /* Destroy pthread */ + frr_pthread_destroy(zrouter.mlag_info.zebra_pth_mlag); + zrouter.mlag_info.zebra_pth_mlag = NULL; + zrouter.mlag_info.th_master = NULL; + zrouter.mlag_info.t_read = NULL; + zrouter.mlag_info.t_write = NULL; + + /* + * Send Notification to clean private data + */ + zebra_mlag_private_cleanup_data(); + return 0; +} + +/* + * API to register zebra client for MLAG Updates + */ +void zebra_mlag_client_register(ZAPI_HANDLER_ARGS) +{ + struct stream *s; + uint32_t reg_mask = 0; + int rc = 0; + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("Received MLAG Registration from client-proto:%d", + client->proto); + + + /* Get input stream. */ + s = msg; + + /* Get data. */ + STREAM_GETL(s, reg_mask); + + if (client->mlag_updates_interested == true) { + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug( + "Client is registered, existing mask: 0x%x, new mask: 0x%x", + client->mlag_reg_mask1, reg_mask); + if (client->mlag_reg_mask1 != reg_mask) + client->mlag_reg_mask1 = reg_mask; + /* + * Client might missed MLAG-UP Notification, post-it again + */ + zebra_mlag_publish_process_state(client, ZEBRA_MLAG_PROCESS_UP); + return; + } + + + client->mlag_updates_interested = true; + client->mlag_reg_mask1 = reg_mask; + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("Registering for MLAG Upadtes with mask: 0x%x, ", + client->mlag_reg_mask1); + + zrouter.mlag_info.clients_interested_cnt++; + + if (zrouter.mlag_info.clients_interested_cnt == 1) { + /* + * First-client for MLAG Updates,open the communication channel + * with MLAG + */ + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug( + "First client, opening the channel with MLAG"); + + zebra_mlag_spawn_pthread(); + rc = zebra_mlag_private_open_channel(); + if (rc < 0) { + /* + * For some reason, zebra not able to open the + * comm-channel with MLAG, so post MLAG-DOWN to client. + * later when the channel is open, zebra will send + * MLAG-UP + */ + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug( + "Fail to open channel with MLAG,rc:%d, post Proto-down", + rc); + } + } + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("Client Registered successfully for MLAG Updates"); + + if (zrouter.mlag_info.connected == true) + zebra_mlag_publish_process_state(client, ZEBRA_MLAG_PROCESS_UP); +stream_failure: + return; +} + +/* + * API to un-register for MLAG Updates + */ +void zebra_mlag_client_unregister(ZAPI_HANDLER_ARGS) +{ + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("Received MLAG De-Registration from client-proto:%d", + client->proto); + + if (client->mlag_updates_interested == false) + /* Unexpected */ + return; + + client->mlag_updates_interested = false; + client->mlag_reg_mask1 = 0; + zrouter.mlag_info.clients_interested_cnt--; + + if (zrouter.mlag_info.clients_interested_cnt == 0) { + /* + * No-client is interested for MLAG Updates,close the + * communication channel with MLAG + */ + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("Last client for MLAG, close the channel "); + + /* + * Clean up flow: + * ============= + * 1) main thread calls socket close which posts De-register + * to MLAG write thread + * 2) after MLAG write thread posts De-register it sends a + * signal back to main thread to do the thread cleanup + * this was mainly to make sure De-register is posted to MCLAGD. + */ + zebra_mlag_private_close_channel(); + } + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug( + "Client De-Registered successfully for MLAG Updates"); +} + +/* + * Does following things. + * 1) allocated new local stream, and copies teh client data and enqueue + * to MLAG Thread + * 2) MLAG Thread after dequeing, encode the client data using protobuf + * and write on to MLAG + */ +void zebra_mlag_forward_client_msg(ZAPI_HANDLER_ARGS) +{ + struct stream *zebra_s; + struct stream *mlag_s; + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("Received Client MLAG Data from client-proto:%d", + client->proto); + + /* Get input stream. */ + zebra_s = msg; + mlag_s = stream_new(zebra_s->endp); + if (!mlag_s) + return; + + /* + * Client data is | Zebra Header + MLAG Data | + * we need to enqueue only the MLAG data, skipping Zebra Header + */ + stream_put(mlag_s, zebra_s->data + zebra_s->getp, + zebra_s->endp - zebra_s->getp); + stream_fifo_push_safe(zrouter.mlag_info.mlag_fifo, mlag_s); + zebra_mlag_signal_write_thread(); + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("%s: Enqueued Client:%d data to MLAG Thread ", + __func__, client->proto); +} + +/***********************End of Zebra Main thread processing*************/ + enum mlag_role zebra_mlag_get_role(void) { return zrouter.mlag_info.role; @@ -45,7 +594,7 @@ DEFUN_HIDDEN (show_mlag, ZEBRA_STR "The mlag role on this machine\n") { - char buf[80]; + char buf[MAXCH_LEN]; vty_out(vty, "MLag is configured to: %s\n", mlag_role2str(zrouter.mlag_info.role, buf, sizeof(buf))); @@ -53,18 +602,201 @@ DEFUN_HIDDEN (show_mlag, return CMD_SUCCESS; } -DEFPY_HIDDEN (test_mlag, - test_mlag_cmd, - "test zebra mlag ", - "Test code\n" - ZEBRA_STR - "Modify the Mlag state\n" - "Mlag is not setup on the machine\n" - "Mlag is setup to be primary\n" - "Mlag is setup to be the secondary\n") +static void test_mlag_post_mroute_add(void) +{ + struct stream *s = NULL; + char vrf_temp[20]; + char intf_temp[20]; + + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + if (!s) + return; + + memset(vrf_temp, 0, 20); + memset(intf_temp, 0, 20); + + strlcpy(vrf_temp, "test", 20); + strlcpy(intf_temp, "br0.11", 20); + + stream_putl(s, MLAG_MROUTE_ADD); + stream_putw(s, sizeof(struct mlag_mroute_add)); + stream_putw(s, MLAG_MSG_NO_BATCH); + + /* payload*/ + stream_put(s, vrf_temp, VRF_NAMSIZ); + stream_putl(s, 0x01010101); /*source_ip*/ + stream_putl(s, 0xE4000001); /*group_ip*/ + stream_putl(s, 10); /*cost_to_rp*/ + stream_putl(s, 5); /*vni_id */ + stream_putc(s, 1); /*am_i_dr */ + stream_putc(s, 1); /*dual_active */ + stream_putl(s, 0x1004); /*vrf_id*/ + stream_put(s, intf_temp, INTERFACE_NAMSIZ); + stream_fifo_push_safe(zrouter.mlag_info.mlag_fifo, s); + zebra_mlag_signal_write_thread(); + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("%s: Enqueued MLAG Mroute-add to MLAG Thread ", + __func__); +} + +static void test_mlag_post_mroute_del(void) +{ + struct stream *s = NULL; + char vrf_temp[20]; + char intf_temp[20]; + + + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + if (!s) + return; + + memset(vrf_temp, 0, 20); + memset(intf_temp, 0, 20); + + strlcpy(vrf_temp, "test", 20); + strlcpy(intf_temp, "br0.11", 20); + + stream_putl(s, MLAG_MROUTE_DEL); + stream_putw(s, sizeof(struct mlag_mroute_del)); + stream_putw(s, MLAG_MSG_NO_BATCH); + + /* payload*/ + stream_put(s, vrf_temp, VRF_NAMSIZ); + stream_putl(s, 0x01010101); /*source_ip*/ + stream_putl(s, 0xE4000001); /*group_ip*/ + stream_putl(s, 5); /*vni_id */ + stream_putl(s, 0x1004); /*vrf_id*/ + stream_put(s, intf_temp, INTERFACE_NAMSIZ); + stream_fifo_push_safe(zrouter.mlag_info.mlag_fifo, s); + zebra_mlag_signal_write_thread(); + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("%s: Enqueued MLAG Mroute-Del to MLAG Thread ", + __func__); +} + +static void test_mlag_post_mroute_bulk_add(void) +{ + struct stream *s = NULL; + char vrf_temp[20]; + char intf_temp[20]; + + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + if (!s) + return; + + memset(vrf_temp, 0, 20); + memset(intf_temp, 0, 20); + + strlcpy(vrf_temp, "test", 20); + strlcpy(intf_temp, "br0.11", 20); + + stream_putl(s, MLAG_MROUTE_ADD_BULK); + stream_putw(s, 3 * sizeof(struct mlag_mroute_add)); + stream_putw(s, 3); + + /* payload-1*/ + stream_put(s, vrf_temp, VRF_NAMSIZ); + stream_putl(s, 0x01010101); /*source_ip*/ + stream_putl(s, 0xE4000001); /*group_ip*/ + stream_putl(s, 10); /*cost_to_rp*/ + stream_putl(s, 5); /*vni_id */ + stream_putc(s, 1); /*am_i_dr */ + stream_putc(s, 1); /*dual_active */ + stream_putl(s, 0x1004); /*vrf_id*/ + stream_put(s, intf_temp, INTERFACE_NAMSIZ); + + /* payload-2*/ + stream_put(s, vrf_temp, VRF_NAMSIZ); + stream_putl(s, 0x0); /*source_ip*/ + stream_putl(s, 0xE9000001); /*group_ip*/ + stream_putl(s, 10); /*cost_to_rp*/ + stream_putl(s, 5); /*vni_id */ + stream_putc(s, 1); /*am_i_dr */ + stream_putc(s, 1); /*dual_active */ + stream_putl(s, 0x1004); /*vrf_id*/ + stream_put(s, intf_temp, INTERFACE_NAMSIZ); + + /* payload-3*/ + stream_put(s, vrf_temp, VRF_NAMSIZ); + stream_putl(s, 0x01010101); /*source_ip*/ + stream_putl(s, 0xE5000001); /*group_ip*/ + stream_putl(s, 10); /*cost_to_rp*/ + stream_putl(s, 5); /*vni_id */ + stream_putc(s, 1); /*am_i_dr */ + stream_putc(s, 1); /*dual_active */ + stream_putl(s, 0x1004); /*vrf_id*/ + stream_put(s, intf_temp, INTERFACE_NAMSIZ); + stream_fifo_push_safe(zrouter.mlag_info.mlag_fifo, s); + zebra_mlag_signal_write_thread(); + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("%s: Enqueued MLAG Mroute-Bulk to MLAG Thread ", + __func__); +} + +static void test_mlag_post_mroute_bulk_del(void) +{ + struct stream *s = NULL; + char vrf_temp[20]; + char intf_temp[20]; + + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + if (!s) + return; + + memset(vrf_temp, 0, 20); + memset(intf_temp, 0, 20); + + strlcpy(vrf_temp, "test", 20); + strlcpy(intf_temp, "br0.11", 20); + + stream_putl(s, MLAG_MROUTE_DEL_BULK); + stream_putw(s, 2 * sizeof(struct mlag_mroute_del)); + stream_putw(s, 2); + + /* payload-1*/ + stream_put(s, vrf_temp, VRF_NAMSIZ); + stream_putl(s, 0x01010101); /*source_ip*/ + stream_putl(s, 0xE4000001); /*group_ip*/ + stream_putl(s, 5); /*vni_id */ + stream_putl(s, 0x1004); /*vrf_id*/ + stream_put(s, intf_temp, INTERFACE_NAMSIZ); + + /* payload-2*/ + stream_put(s, vrf_temp, VRF_NAMSIZ); + stream_putl(s, 0x0); /*source_ip*/ + stream_putl(s, 0xE9000001); /*group_ip*/ + stream_putl(s, 5); /*vni_id */ + stream_putl(s, 0x1004); /*vrf_id*/ + stream_put(s, intf_temp, INTERFACE_NAMSIZ); + + /* payload-3*/ + stream_put(s, vrf_temp, VRF_NAMSIZ); + stream_putl(s, 0x01010101); /*source_ip*/ + stream_putl(s, 0xE5000001); /*group_ip*/ + stream_putl(s, 5); /*vni_id */ + stream_putl(s, 0x1004); /*vrf_id*/ + stream_put(s, intf_temp, INTERFACE_NAMSIZ); + stream_fifo_push_safe(zrouter.mlag_info.mlag_fifo, s); + zebra_mlag_signal_write_thread(); + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("%s: Enqueued MLAG Mroute-Bulk to MLAG Thread ", + __func__); +} + +DEFPY(test_mlag, test_mlag_cmd, + "test zebra mlag ", + "Test code\n" ZEBRA_STR + "Modify the Mlag state\n" + "Mlag is not setup on the machine\n" + "Mlag is setup to be primary\n" + "Mlag is setup to be the secondary\n") { enum mlag_role orig = zrouter.mlag_info.role; - char buf1[80], buf2[80]; + char buf1[MAXCH_LEN], buf2[MAXCH_LEN]; if (none) zrouter.mlag_info.role = MLAG_ROLE_NONE; @@ -78,8 +810,72 @@ DEFPY_HIDDEN (test_mlag, mlag_role2str(orig, buf1, sizeof(buf1)), mlag_role2str(orig, buf2, sizeof(buf2))); - if (orig != zrouter.mlag_info.role) + if (orig != zrouter.mlag_info.role) { zsend_capabilities_all_clients(); + if (zrouter.mlag_info.role != MLAG_ROLE_NONE) { + if (zrouter.mlag_info.clients_interested_cnt == 0 + && test_mlag_in_progress == false) { + if (zrouter.mlag_info.zebra_pth_mlag == NULL) + zebra_mlag_spawn_pthread(); + zrouter.mlag_info.clients_interested_cnt++; + test_mlag_in_progress = true; + zebra_mlag_private_open_channel(); + } + } else { + if (test_mlag_in_progress == true) { + test_mlag_in_progress = false; + zrouter.mlag_info.clients_interested_cnt--; + zebra_mlag_private_close_channel(); + } + } + } + + return CMD_SUCCESS; +} + +DEFPY(test_mlag_route, test_mlag_route_cmd, + "test zebra mlag route ", + "Test code\n" ZEBRA_STR + "Modify the Mlag state\n" + "Post Route Action to Mlag\n" + "Posting Route-add\n" + "Posting Route-del\n") +{ + + if (zrouter.mlag_info.connected == false) { + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("Test: Not connected to MLAG"); + return CMD_SUCCESS; + } + + if (add) + test_mlag_post_mroute_add(); + if (del) + test_mlag_post_mroute_del(); + + return CMD_SUCCESS; +} + +DEFPY(test_mlag_route_bulk, test_mlag_route_bulk_cmd, + "test zebra mlag route bulk ", + "Test code\n" ZEBRA_STR + "Modify the Mlag state\n" + "Post Route Action to Mlag\n" + "Posting Route-bulk\n" + "Posting Route-add\n" + "Posting Route-del\n") +{ + + if (zrouter.mlag_info.connected == false) { + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("Test: Not connected to MLAG"); + return CMD_SUCCESS; + } + + if (add) + test_mlag_post_mroute_bulk_add(); + if (del) + test_mlag_post_mroute_bulk_del(); return CMD_SUCCESS; } @@ -88,8 +884,553 @@ void zebra_mlag_init(void) { install_element(VIEW_NODE, &show_mlag_cmd); install_element(ENABLE_NODE, &test_mlag_cmd); + install_element(ENABLE_NODE, &test_mlag_route_cmd); + install_element(ENABLE_NODE, &test_mlag_route_bulk_cmd); + + /* + * Intialiaze teh MLAG Global variableis + * write thread will be craeted during actual registration with MCLAG + */ + zrouter.mlag_info.clients_interested_cnt = 0; + zrouter.mlag_info.connected = false; + zrouter.mlag_info.timer_running = false; + zrouter.mlag_info.mlag_fifo = stream_fifo_new(); + zrouter.mlag_info.zebra_pth_mlag = NULL; + zrouter.mlag_info.th_master = NULL; + zrouter.mlag_info.t_read = NULL; + zrouter.mlag_info.t_write = NULL; + test_mlag_in_progress = false; + zebra_mlag_reset_read_buffer(); + pthread_mutex_init(&zrouter.mlag_info.mlag_th_mtx, NULL); } 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[MAXCH_LEN]; + + 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); + + 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, + sizeof(buf))); + *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, + sizeof(buf)), + len); + hdr.type = (ZebraMlagHeader__MessageType)mlag_msg.msg_type; + if (len != 0) { + hdr.data.len = len; + hdr.data.data = XMALLOC(MTYPE_MLAG_PBUF, len); + 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, + sizeof(buf)), + 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[MAXCH_LEN]; + + 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, + sizeof(buf))); + + /* + * 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; +} + +int zebra_mlag_protobuf_decode_message(struct stream **s, uint8_t *data, + uint32_t len) +{ + return 0; +} +#endif diff --git a/zebra/zebra_mlag.h b/zebra/zebra_mlag.h index 90a5a41fa4..015c94bf5c 100644 --- a/zebra/zebra_mlag.h +++ b/zebra/zebra_mlag.h @@ -1,5 +1,5 @@ /* Zebra mlag header. - * Copyright (C) 2018 Cumulus Networks, Inc. + * Copyright (C) 2019 Cumulus Networks, Inc. * Donald Sharp * * This file is part of FRR. @@ -23,18 +23,55 @@ #define __ZEBRA_MLAG_H__ #include "mlag.h" +#include "zclient.h" +#include "zebra/zserv.h" -#ifdef __cplusplus -extern "C" { +#ifdef HAVE_PROTOBUF +#include "mlag/mlag.pb-c.h" #endif +#define ZEBRA_MLAG_BUF_LIMIT 2048 +#define ZEBRA_MLAG_LEN_SIZE 4 + +extern uint8_t mlag_wr_buffer[ZEBRA_MLAG_BUF_LIMIT]; +extern uint8_t mlag_rd_buffer[ZEBRA_MLAG_BUF_LIMIT]; +extern uint32_t mlag_rd_buf_offset; + +static inline void zebra_mlag_reset_read_buffer(void) +{ + mlag_rd_buf_offset = 0; +} + +enum zebra_mlag_state { + MLAG_UP = 1, + MLAG_DOWN = 2, +}; + void zebra_mlag_init(void); void zebra_mlag_terminate(void); enum mlag_role zebra_mlag_get_role(void); -#ifdef __cplusplus -} -#endif +void zebra_mlag_client_register(ZAPI_HANDLER_ARGS); + +void zebra_mlag_client_unregister(ZAPI_HANDLER_ARGS); + +void zebra_mlag_forward_client_msg(ZAPI_HANDLER_ARGS); + +void zebra_mlag_send_register(void); + +void zebra_mlag_send_deregister(void); + +void zebra_mlag_handle_process_state(enum zebra_mlag_state state); + +void zebra_mlag_process_mlag_data(uint8_t *data, uint32_t len); + +/* + * ProtoBuffer Api's + */ +int zebra_mlag_protobuf_encode_client_data(struct stream *s, + uint32_t *msg_type); +int zebra_mlag_protobuf_decode_message(struct stream **s, uint8_t *data, + uint32_t len); #endif diff --git a/zebra/zebra_mlag_private.c b/zebra/zebra_mlag_private.c new file mode 100644 index 0000000000..896e78ca0a --- /dev/null +++ b/zebra/zebra_mlag_private.c @@ -0,0 +1,302 @@ +/* Zebra Mlag Code. + * Copyright (C) 2019 Cumulus Networks, Inc. + * Donald Sharp + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#include "zebra.h" + +#include "hook.h" +#include "module.h" +#include "thread.h" +#include "libfrr.h" +#include "version.h" +#include "network.h" + +#include "lib/stream.h" + +#include "zebra/debug.h" +#include "zebra/zebra_router.h" +#include "zebra/zebra_mlag.h" +#include "zebra/zebra_mlag_private.h" + +#include + + +/* + * This file will have platform specific apis to communicate with MCLAG. + * + */ + +#ifdef HAVE_CUMULUS + +static struct thread_master *zmlag_master; +static int mlag_socket; + +static int zebra_mlag_connect(struct thread *thread); +static int zebra_mlag_read(struct thread *thread); + +/* + * Write teh data to MLAGD + */ +int zebra_mlag_private_write_data(uint8_t *data, uint32_t len) +{ + int rc = 0; + + if (IS_ZEBRA_DEBUG_MLAG) { + zlog_debug("%s: Writing %d length Data to clag", __func__, len); + zlog_hexdump(data, len); + } + rc = write(mlag_socket, data, len); + return rc; +} + +static void zebra_mlag_sched_read(void) +{ + pthread_mutex_lock(&zrouter.mlag_info.mlag_th_mtx); + thread_add_read(zmlag_master, zebra_mlag_read, NULL, mlag_socket, + &zrouter.mlag_info.t_read); + pthread_mutex_unlock(&zrouter.mlag_info.mlag_th_mtx); +} + +static int zebra_mlag_read(struct thread *thread) +{ + uint32_t *msglen; + uint32_t h_msglen; + uint32_t tot_len, curr_len = mlag_rd_buf_offset; + + pthread_mutex_lock(&zrouter.mlag_info.mlag_th_mtx); + zrouter.mlag_info.t_read = NULL; + pthread_mutex_unlock(&zrouter.mlag_info.mlag_th_mtx); + + /* + * Received message in sock_stream looks like below + * | len-1 (4 Bytes) | payload-1 (len-1) | + * len-2 (4 Bytes) | payload-2 (len-2) | .. + * + * Idea is read one message completely, then process, until message is + * read completely, keep on reading from the socket + */ + if (curr_len < ZEBRA_MLAG_LEN_SIZE) { + ssize_t data_len; + + data_len = read(mlag_socket, mlag_rd_buffer + curr_len, + ZEBRA_MLAG_LEN_SIZE - curr_len); + if (data_len == 0 || data_len == -1) { + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("MLAG connection closed socket : %d", + mlag_socket); + close(mlag_socket); + zebra_mlag_handle_process_state(MLAG_DOWN); + return -1; + } + if (data_len != (ssize_t)ZEBRA_MLAG_LEN_SIZE - curr_len) { + /* Try again later */ + zebra_mlag_sched_read(); + return 0; + } + curr_len = ZEBRA_MLAG_LEN_SIZE; + } + + /* Get the actual packet length */ + msglen = (uint32_t *)mlag_rd_buffer; + h_msglen = ntohl(*msglen); + + /* This will be the actual length of the packet */ + tot_len = h_msglen + ZEBRA_MLAG_LEN_SIZE; + + if (curr_len < tot_len) { + ssize_t data_len; + + data_len = read(mlag_socket, mlag_rd_buffer + curr_len, + tot_len - curr_len); + if (data_len == 0 || data_len == -1) { + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("MLAG connection closed socket : %d", + mlag_socket); + close(mlag_socket); + zebra_mlag_handle_process_state(MLAG_DOWN); + return -1; + } + if (data_len != (ssize_t)tot_len - curr_len) { + /* Try again later */ + zebra_mlag_sched_read(); + return 0; + } + } + + if (IS_ZEBRA_DEBUG_MLAG) { + zlog_debug("Received a MLAG Message from socket: %d, len:%u ", + mlag_socket, tot_len); + zlog_hexdump(mlag_rd_buffer, tot_len); + } + + tot_len -= ZEBRA_MLAG_LEN_SIZE; + + /* Process the packet */ + zebra_mlag_process_mlag_data(mlag_rd_buffer + ZEBRA_MLAG_LEN_SIZE, + tot_len); + + /* Register read thread. */ + zebra_mlag_reset_read_buffer(); + zebra_mlag_sched_read(); + return 0; +} + +static int zebra_mlag_connect(struct thread *thread) +{ + struct sockaddr_un svr; + struct ucred ucred; + socklen_t len = 0; + + /* Reset the Timer-running flag */ + zrouter.mlag_info.timer_running = false; + + zrouter.mlag_info.t_read = NULL; + memset(&svr, 0, sizeof(svr)); + svr.sun_family = AF_UNIX; +#define MLAG_SOCK_NAME "/var/run/clag-zebra.socket" + strlcpy(svr.sun_path, MLAG_SOCK_NAME, sizeof(MLAG_SOCK_NAME) + 1); + + mlag_socket = socket(svr.sun_family, SOCK_STREAM, 0); + if (mlag_socket < 0) + return -1; + + if (connect(mlag_socket, (struct sockaddr *)&svr, sizeof(svr)) == -1) { + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug( + "Unable to connect to %s try again in 10 secs", + svr.sun_path); + close(mlag_socket); + zrouter.mlag_info.timer_running = true; + thread_add_timer(zmlag_master, zebra_mlag_connect, NULL, 10, + &zrouter.mlag_info.t_read); + return 0; + } + len = sizeof(struct ucred); + ucred.pid = getpid(); + + set_nonblocking(mlag_socket); + setsockopt(mlag_socket, SOL_SOCKET, SO_PEERCRED, &ucred, len); + + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("%s: Connection with MLAG is established ", + __func__); + + thread_add_read(zmlag_master, zebra_mlag_read, NULL, mlag_socket, + &zrouter.mlag_info.t_read); + /* + * Connection is established with MLAGD, post to clients + */ + zebra_mlag_handle_process_state(MLAG_UP); + return 0; +} + +/* + * Currently we are doing polling later we will look for better options + */ +void zebra_mlag_private_monitor_state(void) +{ + thread_add_event(zmlag_master, zebra_mlag_connect, NULL, 0, + &zrouter.mlag_info.t_read); +} + +int zebra_mlag_private_open_channel(void) +{ + zmlag_master = zrouter.mlag_info.th_master; + + if (zrouter.mlag_info.connected == true) { + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("%s: Zebra already connected to MLAGD", + __func__); + return 0; + } + + if (zrouter.mlag_info.timer_running == true) { + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug( + "%s: Connection retry is in progress for MLAGD", + __func__); + return 0; + } + + if (zrouter.mlag_info.clients_interested_cnt) { + /* + * Connect only if any clients are showing interest + */ + thread_add_event(zmlag_master, zebra_mlag_connect, NULL, 0, + &zrouter.mlag_info.t_read); + } + return 0; +} + +int zebra_mlag_private_close_channel(void) +{ + if (zmlag_master == NULL) + return -1; + + if (zrouter.mlag_info.clients_interested_cnt) { + if (IS_ZEBRA_DEBUG_MLAG) + zlog_debug("%s: still %d clients are connected, skip", + __func__, + zrouter.mlag_info.clients_interested_cnt); + return -1; + } + + /* + * Post the De-register to MLAG, so that it can do necesasry cleanup + */ + zebra_mlag_send_deregister(); + + return 0; +} + +void zebra_mlag_private_cleanup_data(void) +{ + zmlag_master = NULL; + zrouter.mlag_info.connected = false; + zrouter.mlag_info.timer_running = false; + + close(mlag_socket); +} + +#else /*HAVE_CUMULUS */ + +int zebra_mlag_private_write_data(uint8_t *data, uint32_t len) +{ + return 0; +} + +void zebra_mlag_private_monitor_state(void) +{ +} + +int zebra_mlag_private_open_channel(void) +{ + return 0; +} + +int zebra_mlag_private_close_channel(void) +{ + return 0; +} + +void zebra_mlag_private_cleanup_data(void) +{ +} +#endif /*HAVE_CUMULUS*/ diff --git a/zebra/zebra_mlag_private.h b/zebra/zebra_mlag_private.h new file mode 100644 index 0000000000..2ae7e39325 --- /dev/null +++ b/zebra/zebra_mlag_private.h @@ -0,0 +1,40 @@ +/* Zebra mlag header. + * Copyright (C) 2019 Cumulus Networks, Inc. + * Donald Sharp + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#ifndef __ZEBRA_MLAG_PRIVATE_H__ +#define __ZEBRA_MLAG_PRIVATE_H__ + + +/* + * all the platform specific API's + */ + +int zebra_mlag_private_open_channel(void); + +int zebra_mlag_private_close_channel(void); + +void zebra_mlag_private_monitor_state(void); + +int zebra_mlag_private_write_data(uint8_t *data, uint32_t len); + +void zebra_mlag_private_cleanup_data(void); + +#endif diff --git a/zebra/zebra_router.h b/zebra/zebra_router.h index 25a7adac11..1f80561986 100644 --- a/zebra/zebra_router.h +++ b/zebra/zebra_router.h @@ -71,6 +71,36 @@ struct zebra_mlag_info { /* The system mac being used */ struct ethaddr mac; + /* + * Zebra will open the communication channel with MLAGD only if any + * clients are interested and it is controlled dynamically based on + * client registers & un-registers. + */ + uint32_t clients_interested_cnt; + + /* coomunication channel with MLAGD is established */ + bool connected; + + /* connection retry timer is running */ + bool timer_running; + + /* Holds the client data(unencoded) that need to be pushed to MCLAGD*/ + struct stream_fifo *mlag_fifo; + + /* + * A new Kernel thread will be created to post the data to MCLAGD. + * where as, read will be performed from the zebra main thread, because + * read involves accessing client registartion data structures. + */ + struct frr_pthread *zebra_pth_mlag; + + /* MLAG Thread context 'master' */ + struct thread_master *th_master; + + /* Threads for read/write. */ + struct thread *t_read; + struct thread *t_write; + pthread_mutex_t mlag_th_mtx; }; struct zebra_router { diff --git a/zebra/zserv.h b/zebra/zserv.h index 708ff1e226..ccc8d92aa2 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -99,6 +99,13 @@ struct zserv { uint8_t proto; uint16_t instance; + /* + * Interested for MLAG Updates, and also stores the client + * interested message mask + */ + bool mlag_updates_interested; + uint32_t mlag_reg_mask1; + /* Statistics */ uint32_t redist_v4_add_cnt; uint32_t redist_v4_del_cnt;