]> git.proxmox.com Git - mirror_frr.git/blame - zebra/zebra_mlag.c
Merge pull request #5452 from mjstapp/fix_notify_nhg
[mirror_frr.git] / zebra / zebra_mlag.c
CommitLineData
df395600 1/* Zebra Mlag Code.
6a597223 2 * Copyright (C) 2018 Cumulus Networks, Inc.
df395600
DS
3 * Donald Sharp
4 *
5 * This file is part of FRR.
6 *
7 * FRR is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2, or (at your option) any
10 * later version.
11 *
12 * FRR is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with FRR; see the file COPYING. If not, write to the Free
19 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20 * 02111-1307, USA.
21 */
22#include "zebra.h"
23
763ec244 24#include "command.h"
df395600 25#include "hook.h"
ee235396
SK
26#include "frr_pthread.h"
27#include "mlag.h"
df395600
DS
28
29#include "zebra/zebra_mlag.h"
e96ba9da 30#include "zebra/zebra_router.h"
ee235396 31#include "zebra/zebra_memory.h"
b120fe3b
DS
32#include "zebra/zapi_msg.h"
33#include "zebra/debug.h"
df395600 34
763ec244
DS
35#ifndef VTYSH_EXTRACT_PL
36#include "zebra/zebra_mlag_clippy.c"
37#endif
38
d621815a
DL
39DEFINE_HOOK(zebra_mlag_private_write_data,
40 (uint8_t *data, uint32_t len), (data, len))
41DEFINE_HOOK(zebra_mlag_private_monitor_state, (), ())
42DEFINE_HOOK(zebra_mlag_private_open_channel, (), ())
43DEFINE_HOOK(zebra_mlag_private_close_channel, (), ())
44DEFINE_HOOK(zebra_mlag_private_cleanup_data, (), ())
45
ee235396
SK
46#define ZEBRA_MLAG_METADATA_LEN 4
47#define ZEBRA_MLAG_MSG_BCAST 0xFFFFFFFF
48
49uint8_t mlag_wr_buffer[ZEBRA_MLAG_BUF_LIMIT];
50uint8_t mlag_rd_buffer[ZEBRA_MLAG_BUF_LIMIT];
51uint32_t mlag_rd_buf_offset;
52
53static bool test_mlag_in_progress;
54
55static int zebra_mlag_signal_write_thread(void);
56static int zebra_mlag_terminate_pthread(struct thread *event);
57static int zebra_mlag_post_data_from_main_thread(struct thread *thread);
58static void zebra_mlag_publish_process_state(struct zserv *client,
59 zebra_message_types_t msg_type);
60
61/**********************MLAG Interaction***************************************/
62
63/*
64 * API to post the Registration to MLAGD
65 * MLAG will not process any messages with out the registration
66 */
67void zebra_mlag_send_register(void)
68{
69 struct stream *s = NULL;
70
71 s = stream_new(sizeof(struct mlag_msg));
72
73 stream_putl(s, MLAG_REGISTER);
74 stream_putw(s, MLAG_MSG_NULL_PAYLOAD);
75 stream_putw(s, MLAG_MSG_NO_BATCH);
76 stream_fifo_push_safe(zrouter.mlag_info.mlag_fifo, s);
77 zebra_mlag_signal_write_thread();
78
79 if (IS_ZEBRA_DEBUG_MLAG)
80 zlog_debug("%s: Enqueued MLAG Register to MLAG Thread ",
81 __func__);
82}
83
84/*
85 * API to post the De-Registration to MLAGD
86 * MLAG will not process any messages after the de-registration
87 */
88void zebra_mlag_send_deregister(void)
89{
90 struct stream *s = NULL;
91
92 s = stream_new(sizeof(struct mlag_msg));
93
94 stream_putl(s, MLAG_DEREGISTER);
95 stream_putw(s, MLAG_MSG_NULL_PAYLOAD);
96 stream_putw(s, MLAG_MSG_NO_BATCH);
97 stream_fifo_push_safe(zrouter.mlag_info.mlag_fifo, s);
98 zebra_mlag_signal_write_thread();
99
100 if (IS_ZEBRA_DEBUG_MLAG)
101 zlog_debug("%s: Enqueued MLAG De-Register to MLAG Thread ",
102 __func__);
103}
104
105/*
106 * API To handle MLAG Received data
107 * Decodes the data using protobuf and enqueue to main thread
108 * main thread publish this to clients based on client subscription
109 */
110void zebra_mlag_process_mlag_data(uint8_t *data, uint32_t len)
111{
112 struct stream *s = NULL;
113 struct stream *s1 = NULL;
114 int msg_type = 0;
115
116 s = stream_new(ZEBRA_MAX_PACKET_SIZ);
67fa73f2 117 msg_type = zebra_mlag_protobuf_decode_message(s, data, len);
ee235396
SK
118
119 if (msg_type <= 0) {
120 /* Something went wrong in decoding */
121 stream_free(s);
122 zlog_err("%s: failed to process mlag data-%d, %u", __func__,
123 msg_type, len);
124 return;
125 }
126
127 /*
128 * additional four bytes are for message type
129 */
130 s1 = stream_new(stream_get_endp(s) + ZEBRA_MLAG_METADATA_LEN);
131 stream_putl(s1, msg_type);
132 stream_put(s1, s->data, stream_get_endp(s));
133 thread_add_event(zrouter.master, zebra_mlag_post_data_from_main_thread,
134 s1, 0, NULL);
135 stream_free(s);
136}
137
138/**********************End of MLAG Interaction********************************/
139
140/************************MLAG Thread Processing*******************************/
141
142/*
143 * after posting every 'ZEBRA_MLAG_POST_LIMIT' packets, MLAG Thread will be
144 * yielded to give CPU for other threads
145 */
146#define ZEBRA_MLAG_POST_LIMIT 100
147
148/*
149 * This thread reads the clients data from the Global queue and encodes with
150 * protobuf and pass on to the MLAG socket.
151 */
152static int zebra_mlag_client_msg_handler(struct thread *event)
153{
154 struct stream *s;
155 uint32_t wr_count = 0;
156 uint32_t msg_type = 0;
67fa73f2 157 uint32_t max_count = 0;
ee235396
SK
158 int len = 0;
159
160 wr_count = stream_fifo_count_safe(zrouter.mlag_info.mlag_fifo);
161 if (IS_ZEBRA_DEBUG_MLAG)
1e76492b 162 zlog_debug(":%s: Processing MLAG write, %u messages in queue",
ee235396
SK
163 __func__, wr_count);
164
67fa73f2 165 max_count = MIN(wr_count, ZEBRA_MLAG_POST_LIMIT);
ee235396 166
67fa73f2 167 for (wr_count = 0; wr_count < max_count; wr_count++) {
ee235396
SK
168 s = stream_fifo_pop_safe(zrouter.mlag_info.mlag_fifo);
169 if (!s) {
170 zlog_debug(":%s: Got a NULL Messages, some thing wrong",
171 __func__);
172 break;
173 }
174
ee235396
SK
175 /*
176 * Encode the data now
177 */
178 len = zebra_mlag_protobuf_encode_client_data(s, &msg_type);
179
180 /*
181 * write to MCLAGD
182 */
67fa73f2 183 if (len > 0) {
d621815a
DL
184 hook_call(zebra_mlag_private_write_data,
185 mlag_wr_buffer, len);
ee235396 186
67fa73f2
SK
187 /*
188 * If message type is De-register, send a signal to main
189 * thread, so that necessary cleanup will be done by
190 * main thread.
191 */
192 if (msg_type == MLAG_DEREGISTER) {
193 thread_add_event(zrouter.master,
194 zebra_mlag_terminate_pthread,
195 NULL, 0, NULL);
196 }
ee235396
SK
197 }
198
199 stream_free(s);
200 }
201
202 if (IS_ZEBRA_DEBUG_MLAG)
203 zlog_debug(":%s: Posted %d messages to MLAGD", __func__,
204 wr_count);
205 /*
206 * Currently there is only message write task is enqueued to this
207 * thread, yielding was added for future purpose, so that this thread
208 * can server other tasks also and in case FIFO is empty, this task will
209 * be schedule when main thread adds some messages
210 */
211 if (wr_count >= ZEBRA_MLAG_POST_LIMIT)
212 zebra_mlag_signal_write_thread();
213 return 0;
214}
215
216/*
217 * API to handle the process state.
218 * In case of Down, Zebra keep monitoring the MLAG state.
219 * all the state Notifications will be published to clients
220 */
221void zebra_mlag_handle_process_state(enum zebra_mlag_state state)
222{
223 if (state == MLAG_UP) {
224 zrouter.mlag_info.connected = true;
225 zebra_mlag_publish_process_state(NULL, ZEBRA_MLAG_PROCESS_UP);
226 zebra_mlag_send_register();
227 } else if (state == MLAG_DOWN) {
228 zrouter.mlag_info.connected = false;
229 zebra_mlag_publish_process_state(NULL, ZEBRA_MLAG_PROCESS_DOWN);
d621815a 230 hook_call(zebra_mlag_private_monitor_state);
ee235396
SK
231 }
232}
233
234/***********************End of MLAG Thread processing*************************/
235
236/*************************Multi-entratnt Api's********************************/
237
238/*
239 * Provider api to signal that work/events are available
240 * for the Zebra MLAG Write pthread.
241 * This API is called from 2 pthreads..
242 * 1) by main thread when client posts a MLAG Message
243 * 2) by MLAG Thread, in case of yield
244 * though this api, is called from two threads we don't need any locking
245 * because Thread task enqueue is thread safe means internally it had
246 * necessary protection
247 */
248static int zebra_mlag_signal_write_thread(void)
249{
1e76492b
SK
250 if (IS_ZEBRA_DEBUG_MLAG)
251 zlog_debug(":%s: Scheduling MLAG write", __func__);
252 /*
253 * This api will be called from Both main & MLAG Threads.
254 * main thread writes, "zrouter.mlag_info.th_master" only
255 * during Zebra Init/after MLAG thread is destroyed.
256 * so it is safe to use without any locking
257 */
258 thread_add_event(zrouter.mlag_info.th_master,
259 zebra_mlag_client_msg_handler, NULL, 0,
260 &zrouter.mlag_info.t_write);
ee235396
SK
261 return 0;
262}
263
264/*
265 * API will be used to publish the MLAG state to interested clients
266 * In case client is passed, state is posted only for that client,
267 * otherwise to all interested clients
268 * this api can be called from two threads.
269 * 1) from main thread: when client is passed
270 * 2) from MLAG Thread: when client is NULL
271 *
272 * In second case, to avoid global data access data will be post to Main
273 * thread, so that actual posting to clients will happen from Main thread.
274 */
275static void zebra_mlag_publish_process_state(struct zserv *client,
276 zebra_message_types_t msg_type)
277{
278 struct stream *s;
279
280 if (IS_ZEBRA_DEBUG_MLAG)
281 zlog_debug("%s: Publishing MLAG process state:%s to %s Client",
282 __func__,
283 (msg_type == ZEBRA_MLAG_PROCESS_UP) ? "UP" : "DOWN",
284 (client) ? "one" : "all");
285
286 if (client) {
287 s = stream_new(ZEBRA_HEADER_SIZE);
288 zclient_create_header(s, msg_type, VRF_DEFAULT);
289 zserv_send_message(client, s);
290 return;
291 }
292
293
294 /*
295 * additional four bytes are for mesasge type
296 */
297 s = stream_new(ZEBRA_HEADER_SIZE + ZEBRA_MLAG_METADATA_LEN);
298 stream_putl(s, ZEBRA_MLAG_MSG_BCAST);
299 zclient_create_header(s, msg_type, VRF_DEFAULT);
300 thread_add_event(zrouter.master, zebra_mlag_post_data_from_main_thread,
301 s, 0, NULL);
302}
303
304/**************************End of Multi-entrant Apis**************************/
305
306/***********************Zebra Main thread processing**************************/
307
308/*
309 * To avoid data corruption, messages will be post to clients only from
310 * main thread, because for that access was needed for clients list.
311 * so instead of forcing the locks, messages will be posted from main thread.
312 */
313static int zebra_mlag_post_data_from_main_thread(struct thread *thread)
314{
315 struct stream *s = THREAD_ARG(thread);
316 struct stream *zebra_s = NULL;
317 struct listnode *node;
318 struct zserv *client;
319 uint32_t msg_type = 0;
320 uint32_t msg_len = 0;
321
322 if (!s)
323 return -1;
324
325 STREAM_GETL(s, msg_type);
326 if (IS_ZEBRA_DEBUG_MLAG)
327 zlog_debug(
328 "%s: Posting MLAG data for msg_type:0x%x to interested cleints",
329 __func__, msg_type);
330
331 msg_len = s->endp - ZEBRA_MLAG_METADATA_LEN;
332 for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client)) {
333 if (client->mlag_updates_interested == true) {
334 if (msg_type != ZEBRA_MLAG_MSG_BCAST
335 && !CHECK_FLAG(client->mlag_reg_mask1,
336 (1 << msg_type))) {
337 continue;
338 }
339
340 if (IS_ZEBRA_DEBUG_MLAG)
341 zlog_debug(
342 "%s: Posting MLAG data of length-%d to client:%d ",
343 __func__, msg_len, client->proto);
344
345 zebra_s = stream_new(msg_len);
346 STREAM_GET(zebra_s->data, s, msg_len);
347 zebra_s->endp = msg_len;
348 stream_putw_at(zebra_s, 0, msg_len);
349
350 /*
351 * This stream will be enqueued to client_obuf, it will
352 * be freed after posting to client socket.
353 */
354 zserv_send_message(client, zebra_s);
355 zebra_s = NULL;
356 }
357 }
358
359 stream_free(s);
360 return 0;
361stream_failure:
362 stream_free(s);
363 if (zebra_s)
364 stream_free(zebra_s);
365 return 0;
366}
367
368/*
369 * Start the MLAG Thread, this will be used to write client data on to
370 * MLAG Process and to read the data from MLAG and post to cleints.
371 * when all clients are un-registered, this Thread will be
372 * suspended.
373 */
374static void zebra_mlag_spawn_pthread(void)
375{
376 /* Start MLAG write pthread */
377
378 struct frr_pthread_attr pattr = {.start =
379 frr_pthread_attr_default.start,
380 .stop = frr_pthread_attr_default.stop};
381
382 zrouter.mlag_info.zebra_pth_mlag =
383 frr_pthread_new(&pattr, "Zebra MLAG thread", "Zebra MLAG");
384
385 zrouter.mlag_info.th_master = zrouter.mlag_info.zebra_pth_mlag->master;
386
387
67fa73f2 388 /* Enqueue an initial event to the Newly spawn MLAG pthread */
ee235396
SK
389 zebra_mlag_signal_write_thread();
390
391 frr_pthread_run(zrouter.mlag_info.zebra_pth_mlag, NULL);
392}
393
394/*
395 * all clients are un-registered for MLAG Updates, terminate the
396 * MLAG write thread
397 */
398static int zebra_mlag_terminate_pthread(struct thread *event)
399{
400 if (IS_ZEBRA_DEBUG_MLAG)
401 zlog_debug("Zebra MLAG write thread terminate called");
402
403 if (zrouter.mlag_info.clients_interested_cnt) {
404 if (IS_ZEBRA_DEBUG_MLAG)
405 zlog_debug(
406 "Zebra MLAG: still some clients are interested");
407 return 0;
408 }
409
410 frr_pthread_stop(zrouter.mlag_info.zebra_pth_mlag, NULL);
411
412 /* Destroy pthread */
413 frr_pthread_destroy(zrouter.mlag_info.zebra_pth_mlag);
414 zrouter.mlag_info.zebra_pth_mlag = NULL;
415 zrouter.mlag_info.th_master = NULL;
416 zrouter.mlag_info.t_read = NULL;
417 zrouter.mlag_info.t_write = NULL;
418
419 /*
420 * Send Notification to clean private data
421 */
d621815a 422 hook_call(zebra_mlag_private_cleanup_data);
ee235396
SK
423 return 0;
424}
425
426/*
427 * API to register zebra client for MLAG Updates
428 */
429void zebra_mlag_client_register(ZAPI_HANDLER_ARGS)
430{
431 struct stream *s;
432 uint32_t reg_mask = 0;
433 int rc = 0;
434
435 if (IS_ZEBRA_DEBUG_MLAG)
436 zlog_debug("Received MLAG Registration from client-proto:%d",
437 client->proto);
438
439
440 /* Get input stream. */
441 s = msg;
442
443 /* Get data. */
444 STREAM_GETL(s, reg_mask);
445
446 if (client->mlag_updates_interested == true) {
447
448 if (IS_ZEBRA_DEBUG_MLAG)
449 zlog_debug(
450 "Client is registered, existing mask: 0x%x, new mask: 0x%x",
451 client->mlag_reg_mask1, reg_mask);
452 if (client->mlag_reg_mask1 != reg_mask)
453 client->mlag_reg_mask1 = reg_mask;
454 /*
455 * Client might missed MLAG-UP Notification, post-it again
456 */
457 zebra_mlag_publish_process_state(client, ZEBRA_MLAG_PROCESS_UP);
458 return;
459 }
460
461
462 client->mlag_updates_interested = true;
463 client->mlag_reg_mask1 = reg_mask;
464 if (IS_ZEBRA_DEBUG_MLAG)
465 zlog_debug("Registering for MLAG Updates with mask: 0x%x, ",
466 client->mlag_reg_mask1);
467
468 zrouter.mlag_info.clients_interested_cnt++;
469
470 if (zrouter.mlag_info.clients_interested_cnt == 1) {
471 /*
472 * First-client for MLAG Updates,open the communication channel
473 * with MLAG
474 */
475 if (IS_ZEBRA_DEBUG_MLAG)
476 zlog_debug(
477 "First client, opening the channel with MLAG");
478
479 zebra_mlag_spawn_pthread();
d621815a 480 rc = hook_call(zebra_mlag_private_open_channel);
ee235396
SK
481 if (rc < 0) {
482 /*
483 * For some reason, zebra not able to open the
484 * comm-channel with MLAG, so post MLAG-DOWN to client.
485 * later when the channel is open, zebra will send
486 * MLAG-UP
487 */
488 if (IS_ZEBRA_DEBUG_MLAG)
489 zlog_debug(
490 "Fail to open channel with MLAG,rc:%d, post Proto-down",
491 rc);
492 zebra_mlag_publish_process_state(
493 client, ZEBRA_MLAG_PROCESS_DOWN);
494 }
495 }
496
497 if (IS_ZEBRA_DEBUG_MLAG)
498 zlog_debug("Client Registered successfully for MLAG Updates");
499
500 if (zrouter.mlag_info.connected == true)
501 zebra_mlag_publish_process_state(client, ZEBRA_MLAG_PROCESS_UP);
502stream_failure:
503 return;
504}
505
506/*
507 * API to un-register for MLAG Updates
508 */
509void zebra_mlag_client_unregister(ZAPI_HANDLER_ARGS)
510{
511 if (IS_ZEBRA_DEBUG_MLAG)
512 zlog_debug("Received MLAG De-Registration from client-proto:%d",
513 client->proto);
514
515 if (client->mlag_updates_interested == false)
516 /* Unexpected */
517 return;
518
519 client->mlag_updates_interested = false;
520 client->mlag_reg_mask1 = 0;
521 zrouter.mlag_info.clients_interested_cnt--;
522
523 if (zrouter.mlag_info.clients_interested_cnt == 0) {
524 /*
525 * No-client is interested for MLAG Updates,close the
526 * communication channel with MLAG
527 */
528 if (IS_ZEBRA_DEBUG_MLAG)
529 zlog_debug("Last client for MLAG, close the channel ");
530
531 /*
532 * Clean up flow:
533 * =============
534 * 1) main thread calls socket close which posts De-register
535 * to MLAG write thread
536 * 2) after MLAG write thread posts De-register it sends a
537 * signal back to main thread to do the thread cleanup
538 * this was mainly to make sure De-register is posted to MCLAGD.
539 */
d621815a 540 hook_call(zebra_mlag_private_close_channel);
ee235396
SK
541 }
542
543 if (IS_ZEBRA_DEBUG_MLAG)
544 zlog_debug(
545 "Client De-Registered successfully for MLAG Updates");
546}
547
548/*
549 * Does following things.
550 * 1) allocated new local stream, and copies the client data and enqueue
551 * to MLAG Thread
552 * 2) MLAG Thread after dequeing, encode the client data using protobuf
553 * and write on to MLAG
554 */
555void zebra_mlag_forward_client_msg(ZAPI_HANDLER_ARGS)
556{
557 struct stream *zebra_s;
558 struct stream *mlag_s;
559
560 if (IS_ZEBRA_DEBUG_MLAG)
561 zlog_debug("Received Client MLAG Data from client-proto:%d",
562 client->proto);
563
564 /* Get input stream. */
565 zebra_s = msg;
566 mlag_s = stream_new(zebra_s->endp);
567
568 /*
569 * Client data is | Zebra Header + MLAG Data |
570 * we need to enqueue only the MLAG data, skipping Zebra Header
571 */
572 stream_put(mlag_s, zebra_s->data + zebra_s->getp,
573 STREAM_READABLE(zebra_s));
574 stream_fifo_push_safe(zrouter.mlag_info.mlag_fifo, mlag_s);
575 zebra_mlag_signal_write_thread();
576
577 if (IS_ZEBRA_DEBUG_MLAG)
578 zlog_debug("%s: Enqueued Client:%d data to MLAG Thread ",
579 __func__, client->proto);
580}
581
582/***********************End of Zebra Main thread processing*************/
583
ff1fb8d5
DS
584enum mlag_role zebra_mlag_get_role(void)
585{
e96ba9da 586 return zrouter.mlag_info.role;
ff1fb8d5
DS
587}
588
763ec244
DS
589DEFUN_HIDDEN (show_mlag,
590 show_mlag_cmd,
591 "show zebra mlag",
592 SHOW_STR
593 ZEBRA_STR
594 "The mlag role on this machine\n")
595{
67fa73f2 596 char buf[MLAG_ROLE_STRSIZE];
763ec244
DS
597
598 vty_out(vty, "MLag is configured to: %s\n",
e96ba9da 599 mlag_role2str(zrouter.mlag_info.role, buf, sizeof(buf)));
763ec244
DS
600
601 return CMD_SUCCESS;
602}
603
1e76492b
SK
604DEFPY_HIDDEN(test_mlag, test_mlag_cmd,
605 "test zebra mlag <none$none|primary$primary|secondary$secondary>",
606 "Test code\n"
607 ZEBRA_STR
608 "Modify the Mlag state\n"
609 "Mlag is not setup on the machine\n"
610 "Mlag is setup to be primary\n"
611 "Mlag is setup to be the secondary\n")
763ec244 612{
b120fe3b 613 enum mlag_role orig = zrouter.mlag_info.role;
67fa73f2 614 char buf1[MLAG_ROLE_STRSIZE], buf2[MLAG_ROLE_STRSIZE];
b120fe3b 615
763ec244 616 if (none)
e96ba9da 617 zrouter.mlag_info.role = MLAG_ROLE_NONE;
763ec244 618 if (primary)
e96ba9da 619 zrouter.mlag_info.role = MLAG_ROLE_PRIMARY;
763ec244 620 if (secondary)
e96ba9da 621 zrouter.mlag_info.role = MLAG_ROLE_SECONDARY;
763ec244 622
b120fe3b
DS
623 if (IS_ZEBRA_DEBUG_MLAG)
624 zlog_debug("Test: Changing role from %s to %s",
625 mlag_role2str(orig, buf1, sizeof(buf1)),
626 mlag_role2str(orig, buf2, sizeof(buf2)));
627
ee235396 628 if (orig != zrouter.mlag_info.role) {
b120fe3b 629 zsend_capabilities_all_clients();
ee235396
SK
630 if (zrouter.mlag_info.role != MLAG_ROLE_NONE) {
631 if (zrouter.mlag_info.clients_interested_cnt == 0
632 && test_mlag_in_progress == false) {
1e76492b
SK
633 if (zrouter.mlag_info.zebra_pth_mlag == NULL)
634 zebra_mlag_spawn_pthread();
ee235396
SK
635 zrouter.mlag_info.clients_interested_cnt++;
636 test_mlag_in_progress = true;
d621815a 637 hook_call(zebra_mlag_private_open_channel);
ee235396
SK
638 }
639 } else {
640 if (test_mlag_in_progress == true) {
641 test_mlag_in_progress = false;
642 zrouter.mlag_info.clients_interested_cnt--;
d621815a 643 hook_call(zebra_mlag_private_close_channel);
ee235396
SK
644 }
645 }
646 }
b120fe3b 647
763ec244
DS
648 return CMD_SUCCESS;
649}
650
df395600
DS
651void zebra_mlag_init(void)
652{
763ec244
DS
653 install_element(VIEW_NODE, &show_mlag_cmd);
654 install_element(ENABLE_NODE, &test_mlag_cmd);
ee235396
SK
655
656 /*
1e76492b
SK
657 * Intialiaze the MLAG Global variables
658 * write thread will be created during actual registration with MCLAG
ee235396
SK
659 */
660 zrouter.mlag_info.clients_interested_cnt = 0;
661 zrouter.mlag_info.connected = false;
662 zrouter.mlag_info.timer_running = false;
663 zrouter.mlag_info.mlag_fifo = stream_fifo_new();
664 zrouter.mlag_info.zebra_pth_mlag = NULL;
665 zrouter.mlag_info.th_master = NULL;
666 zrouter.mlag_info.t_read = NULL;
667 zrouter.mlag_info.t_write = NULL;
668 test_mlag_in_progress = false;
ee235396 669 zebra_mlag_reset_read_buffer();
df395600
DS
670}
671
672void zebra_mlag_terminate(void)
673{
674}
ee235396
SK
675
676
677/*
678 *
679 * ProtoBuf Encoding APIs
680 */
681
67fa73f2
SK
682#ifdef HAVE_PROTOBUF
683
684DEFINE_MTYPE_STATIC(ZEBRA, MLAG_PBUF, "ZEBRA MLAG PROTOBUF")
685
686int zebra_mlag_protobuf_encode_client_data(struct stream *s, uint32_t *msg_type)
687{
688 ZebraMlagHeader hdr = ZEBRA_MLAG__HEADER__INIT;
689 struct mlag_msg mlag_msg;
690 uint8_t tmp_buf[ZEBRA_MLAG_BUF_LIMIT];
691 int len = 0;
692 int n_len = 0;
693 int rc = 0;
694 char buf[ZLOG_FILTER_LENGTH_MAX];
695
696 if (IS_ZEBRA_DEBUG_MLAG)
697 zlog_debug("%s: Entering..", __func__);
698
1e76492b 699 rc = mlag_lib_decode_mlag_hdr(s, &mlag_msg);
67fa73f2
SK
700 if (rc)
701 return rc;
702
703 if (IS_ZEBRA_DEBUG_MLAG)
1e76492b
SK
704 zlog_debug("%s: Mlag ProtoBuf encoding of message:%s, len:%d",
705 __func__,
706 mlag_lib_msgid_to_str(mlag_msg.msg_type, buf,
707 sizeof(buf)),
67fa73f2 708 mlag_msg.data_len);
67fa73f2
SK
709 *msg_type = mlag_msg.msg_type;
710 switch (mlag_msg.msg_type) {
711 case MLAG_MROUTE_ADD: {
712 struct mlag_mroute_add msg;
713 ZebraMlagMrouteAdd pay_load = ZEBRA_MLAG_MROUTE_ADD__INIT;
714 uint32_t vrf_name_len = 0;
715
1e76492b 716 rc = mlag_lib_decode_mroute_add(s, &msg);
67fa73f2
SK
717 if (rc)
718 return rc;
719 vrf_name_len = strlen(msg.vrf_name) + 1;
720 pay_load.vrf_name = XMALLOC(MTYPE_MLAG_PBUF, vrf_name_len);
721 strlcpy(pay_load.vrf_name, msg.vrf_name, vrf_name_len);
722 pay_load.source_ip = msg.source_ip;
723 pay_load.group_ip = msg.group_ip;
724 pay_load.cost_to_rp = msg.cost_to_rp;
725 pay_load.owner_id = msg.owner_id;
726 pay_load.am_i_dr = msg.am_i_dr;
727 pay_load.am_i_dual_active = msg.am_i_dual_active;
728 pay_load.vrf_id = msg.vrf_id;
729
730 if (msg.owner_id == MLAG_OWNER_INTERFACE) {
731 vrf_name_len = strlen(msg.intf_name) + 1;
732 pay_load.intf_name =
733 XMALLOC(MTYPE_MLAG_PBUF, vrf_name_len);
734 strlcpy(pay_load.intf_name, msg.intf_name,
735 vrf_name_len);
736 }
737
738 len = zebra_mlag_mroute_add__pack(&pay_load, tmp_buf);
739 XFREE(MTYPE_MLAG_PBUF, pay_load.vrf_name);
740 if (msg.owner_id == MLAG_OWNER_INTERFACE)
741 XFREE(MTYPE_MLAG_PBUF, pay_load.intf_name);
742 } break;
743 case MLAG_MROUTE_DEL: {
744 struct mlag_mroute_del msg;
745 ZebraMlagMrouteDel pay_load = ZEBRA_MLAG_MROUTE_DEL__INIT;
746 uint32_t vrf_name_len = 0;
747
1e76492b 748 rc = mlag_lib_decode_mroute_del(s, &msg);
67fa73f2
SK
749 if (rc)
750 return rc;
751 vrf_name_len = strlen(msg.vrf_name) + 1;
752 pay_load.vrf_name = XMALLOC(MTYPE_MLAG_PBUF, vrf_name_len);
753 strlcpy(pay_load.vrf_name, msg.vrf_name, vrf_name_len);
754 pay_load.source_ip = msg.source_ip;
755 pay_load.group_ip = msg.group_ip;
756 pay_load.owner_id = msg.owner_id;
757 pay_load.vrf_id = msg.vrf_id;
758
759 if (msg.owner_id == MLAG_OWNER_INTERFACE) {
760 vrf_name_len = strlen(msg.intf_name) + 1;
761 pay_load.intf_name =
762 XMALLOC(MTYPE_MLAG_PBUF, vrf_name_len);
763 strlcpy(pay_load.intf_name, msg.intf_name,
764 vrf_name_len);
765 }
766
767 len = zebra_mlag_mroute_del__pack(&pay_load, tmp_buf);
768 XFREE(MTYPE_MLAG_PBUF, pay_load.vrf_name);
769 if (msg.owner_id == MLAG_OWNER_INTERFACE)
770 XFREE(MTYPE_MLAG_PBUF, pay_load.intf_name);
771 } break;
772 case MLAG_MROUTE_ADD_BULK: {
773 struct mlag_mroute_add msg;
774 ZebraMlagMrouteAddBulk Bulk_msg =
775 ZEBRA_MLAG_MROUTE_ADD_BULK__INIT;
776 ZebraMlagMrouteAdd **pay_load = NULL;
777 int i;
778 bool cleanup = false;
779
780 Bulk_msg.n_mroute_add = mlag_msg.msg_cnt;
781 pay_load = XMALLOC(MTYPE_MLAG_PBUF, sizeof(ZebraMlagMrouteAdd *)
782 * mlag_msg.msg_cnt);
783
784 for (i = 0; i < mlag_msg.msg_cnt; i++) {
785
786 uint32_t vrf_name_len = 0;
787
1e76492b 788 rc = mlag_lib_decode_mroute_add(s, &msg);
67fa73f2
SK
789 if (rc) {
790 cleanup = true;
791 break;
792 }
793 pay_load[i] = XMALLOC(MTYPE_MLAG_PBUF,
794 sizeof(ZebraMlagMrouteAdd));
795 zebra_mlag_mroute_add__init(pay_load[i]);
796
797 vrf_name_len = strlen(msg.vrf_name) + 1;
798 pay_load[i]->vrf_name =
799 XMALLOC(MTYPE_MLAG_PBUF, vrf_name_len);
800 strlcpy(pay_load[i]->vrf_name, msg.vrf_name,
801 vrf_name_len);
802 pay_load[i]->source_ip = msg.source_ip;
803 pay_load[i]->group_ip = msg.group_ip;
804 pay_load[i]->cost_to_rp = msg.cost_to_rp;
805 pay_load[i]->owner_id = msg.owner_id;
806 pay_load[i]->am_i_dr = msg.am_i_dr;
807 pay_load[i]->am_i_dual_active = msg.am_i_dual_active;
808 pay_load[i]->vrf_id = msg.vrf_id;
809 if (msg.owner_id == MLAG_OWNER_INTERFACE) {
810 vrf_name_len = strlen(msg.intf_name) + 1;
811 pay_load[i]->intf_name =
812 XMALLOC(MTYPE_MLAG_PBUF, vrf_name_len);
813
814 strlcpy(pay_load[i]->intf_name, msg.intf_name,
815 vrf_name_len);
816 }
817 }
818 if (cleanup == false) {
819 Bulk_msg.mroute_add = pay_load;
820 len = zebra_mlag_mroute_add_bulk__pack(&Bulk_msg,
821 tmp_buf);
822 }
823
824 for (i = 0; i < mlag_msg.msg_cnt; i++) {
825 if (pay_load[i]->vrf_name)
826 XFREE(MTYPE_MLAG_PBUF, pay_load[i]->vrf_name);
827 if (pay_load[i]->owner_id == MLAG_OWNER_INTERFACE
828 && pay_load[i]->intf_name)
829 XFREE(MTYPE_MLAG_PBUF, pay_load[i]->intf_name);
830 if (pay_load[i])
831 XFREE(MTYPE_MLAG_PBUF, pay_load[i]);
832 }
833 XFREE(MTYPE_MLAG_PBUF, pay_load);
834 if (cleanup == true)
835 return -1;
836 } break;
837 case MLAG_MROUTE_DEL_BULK: {
838 struct mlag_mroute_del msg;
839 ZebraMlagMrouteDelBulk Bulk_msg =
840 ZEBRA_MLAG_MROUTE_DEL_BULK__INIT;
841 ZebraMlagMrouteDel **pay_load = NULL;
842 int i;
843 bool cleanup = false;
844
845 Bulk_msg.n_mroute_del = mlag_msg.msg_cnt;
846 pay_load = XMALLOC(MTYPE_MLAG_PBUF, sizeof(ZebraMlagMrouteDel *)
847 * mlag_msg.msg_cnt);
848
849 for (i = 0; i < mlag_msg.msg_cnt; i++) {
850
851 uint32_t vrf_name_len = 0;
852
1e76492b 853 rc = mlag_lib_decode_mroute_del(s, &msg);
67fa73f2
SK
854 if (rc) {
855 cleanup = true;
856 break;
857 }
858
859 pay_load[i] = XMALLOC(MTYPE_MLAG_PBUF,
860 sizeof(ZebraMlagMrouteDel));
861 zebra_mlag_mroute_del__init(pay_load[i]);
862
863 vrf_name_len = strlen(msg.vrf_name) + 1;
864 pay_load[i]->vrf_name =
865 XMALLOC(MTYPE_MLAG_PBUF, vrf_name_len);
866
867 strlcpy(pay_load[i]->vrf_name, msg.vrf_name,
868 vrf_name_len);
869 pay_load[i]->source_ip = msg.source_ip;
870 pay_load[i]->group_ip = msg.group_ip;
871 pay_load[i]->owner_id = msg.owner_id;
872 pay_load[i]->vrf_id = msg.vrf_id;
873 if (msg.owner_id == MLAG_OWNER_INTERFACE) {
874 vrf_name_len = strlen(msg.intf_name) + 1;
875 pay_load[i]->intf_name =
876 XMALLOC(MTYPE_MLAG_PBUF, vrf_name_len);
877
878 strlcpy(pay_load[i]->intf_name, msg.intf_name,
879 vrf_name_len);
880 }
881 }
882 if (!cleanup) {
883 Bulk_msg.mroute_del = pay_load;
884 len = zebra_mlag_mroute_del_bulk__pack(&Bulk_msg,
885 tmp_buf);
886 }
887
888 for (i = 0; i < mlag_msg.msg_cnt; i++) {
889 if (pay_load[i]->vrf_name)
890 XFREE(MTYPE_MLAG_PBUF, pay_load[i]->vrf_name);
891 if (pay_load[i]->owner_id == MLAG_OWNER_INTERFACE
892 && pay_load[i]->intf_name)
893 XFREE(MTYPE_MLAG_PBUF, pay_load[i]->intf_name);
894 if (pay_load[i])
895 XFREE(MTYPE_MLAG_PBUF, pay_load[i]);
896 }
897 XFREE(MTYPE_MLAG_PBUF, pay_load);
898 if (cleanup)
899 return -1;
900 } break;
901 default:
902 break;
903 }
904
905 if (IS_ZEBRA_DEBUG_MLAG)
906 zlog_debug("%s: length of Mlag ProtoBuf encoded message:%s, %d",
907 __func__,
1e76492b
SK
908 mlag_lib_msgid_to_str(mlag_msg.msg_type, buf,
909 sizeof(buf)),
67fa73f2
SK
910 len);
911 hdr.type = (ZebraMlagHeader__MessageType)mlag_msg.msg_type;
912 if (len != 0) {
913 hdr.data.len = len;
914 hdr.data.data = XMALLOC(MTYPE_MLAG_PBUF, len);
915 memcpy(hdr.data.data, tmp_buf, len);
916 }
917
918 /*
919 * ProtoBuf Infra will not support to demarc the pointers whem multiple
920 * messages are posted inside a single Buffer.
921 * 2 -solutions exist to solve this
922 * 1. add Unenoced length at the beginning of every message, this will
923 * be used to point to next message in the buffer
924 * 2. another solution is defining all messages insides another message
925 * But this will permit only 32 messages. this can be extended with
926 * multiple levels.
927 * for simplicity we are going with solution-1.
928 */
929 len = zebra_mlag__header__pack(&hdr,
930 (mlag_wr_buffer + ZEBRA_MLAG_LEN_SIZE));
931 n_len = htonl(len);
932 memcpy(mlag_wr_buffer, &n_len, ZEBRA_MLAG_LEN_SIZE);
933 len += ZEBRA_MLAG_LEN_SIZE;
934
935 if (IS_ZEBRA_DEBUG_MLAG)
936 zlog_debug(
937 "%s: length of Mlag ProtoBuf message:%s with Header %d",
938 __func__,
1e76492b
SK
939 mlag_lib_msgid_to_str(mlag_msg.msg_type, buf,
940 sizeof(buf)),
67fa73f2
SK
941 len);
942 if (hdr.data.data)
943 XFREE(MTYPE_MLAG_PBUF, hdr.data.data);
944
945 return len;
946}
947
948int zebra_mlag_protobuf_decode_message(struct stream *s, uint8_t *data,
949 uint32_t len)
950{
951 uint32_t msg_type;
952 ZebraMlagHeader *hdr;
953 char buf[80];
954
955 hdr = zebra_mlag__header__unpack(NULL, len, data);
956 if (hdr == NULL)
957 return -1;
958
959 /*
960 * ADD The MLAG Header
961 */
962 zclient_create_header(s, ZEBRA_MLAG_FORWARD_MSG, VRF_DEFAULT);
963
964 msg_type = hdr->type;
965
966 if (IS_ZEBRA_DEBUG_MLAG)
967 zlog_debug("%s: Mlag ProtoBuf decoding of message:%s", __func__,
1e76492b 968 mlag_lib_msgid_to_str(msg_type, buf, 80));
67fa73f2
SK
969
970 /*
971 * Internal MLAG Message-types & MLAG.proto message types should
972 * always match, otherwise there can be decoding errors
973 * To avoid exposing clients with Protobuf flags, using internal
974 * message-types
975 */
976 stream_putl(s, hdr->type);
977
978 if (hdr->data.len == 0) {
979 /* NULL Payload */
980 stream_putw(s, MLAG_MSG_NULL_PAYLOAD);
981 /* No Batching */
982 stream_putw(s, MLAG_MSG_NO_BATCH);
983 } else {
984 switch (msg_type) {
985 case ZEBRA_MLAG__HEADER__MESSAGE_TYPE__ZEBRA_MLAG_STATUS_UPDATE: {
986 ZebraMlagStatusUpdate *msg = NULL;
987
988 msg = zebra_mlag_status_update__unpack(
989 NULL, hdr->data.len, hdr->data.data);
990 if (msg == NULL) {
991 zebra_mlag__header__free_unpacked(hdr, NULL);
992 return -1;
993 }
994 /* Payload len */
995 stream_putw(s, sizeof(struct mlag_status));
996 /* No Batching */
997 stream_putw(s, MLAG_MSG_NO_BATCH);
998 /* Actual Data */
999 stream_put(s, msg->peerlink, INTERFACE_NAMSIZ);
1000 stream_putl(s, msg->my_role);
1001 stream_putl(s, msg->peer_state);
1002 zebra_mlag_status_update__free_unpacked(msg, NULL);
1003 } break;
1004 case ZEBRA_MLAG__HEADER__MESSAGE_TYPE__ZEBRA_MLAG_VXLAN_UPDATE: {
1005 ZebraMlagVxlanUpdate *msg = NULL;
1006
1007 msg = zebra_mlag_vxlan_update__unpack(
1008 NULL, hdr->data.len, hdr->data.data);
1009 if (msg == NULL) {
1010 zebra_mlag__header__free_unpacked(hdr, NULL);
1011 return -1;
1012 }
1013 /* Payload len */
1014 stream_putw(s, sizeof(struct mlag_vxlan));
1015 /* No Batching */
1016 stream_putw(s, MLAG_MSG_NO_BATCH);
1017 /* Actual Data */
1018 stream_putl(s, msg->anycast_ip);
1019 stream_putl(s, msg->local_ip);
1020 zebra_mlag_vxlan_update__free_unpacked(msg, NULL);
1021 } break;
1022 case ZEBRA_MLAG__HEADER__MESSAGE_TYPE__ZEBRA_MLAG_MROUTE_ADD: {
1023 ZebraMlagMrouteAdd *msg = NULL;
1024
1025 msg = zebra_mlag_mroute_add__unpack(NULL, hdr->data.len,
1026 hdr->data.data);
1027 if (msg == NULL) {
1028 zebra_mlag__header__free_unpacked(hdr, NULL);
1029 return -1;
1030 }
1031 /* Payload len */
1032 stream_putw(s, sizeof(struct mlag_mroute_add));
1033 /* No Batching */
1034 stream_putw(s, MLAG_MSG_NO_BATCH);
1035 /* Actual Data */
1036 stream_put(s, msg->vrf_name, VRF_NAMSIZ);
1037
1038 stream_putl(s, msg->source_ip);
1039 stream_putl(s, msg->group_ip);
1040 stream_putl(s, msg->cost_to_rp);
1041 stream_putl(s, msg->owner_id);
1042 stream_putc(s, msg->am_i_dr);
1043 stream_putc(s, msg->am_i_dual_active);
1044 stream_putl(s, msg->vrf_id);
1045 if (msg->owner_id == MLAG_OWNER_INTERFACE)
1046 stream_put(s, msg->intf_name, INTERFACE_NAMSIZ);
1047 else
1048 stream_put(s, NULL, INTERFACE_NAMSIZ);
1049 zebra_mlag_mroute_add__free_unpacked(msg, NULL);
1050 } break;
1051 case ZEBRA_MLAG__HEADER__MESSAGE_TYPE__ZEBRA_MLAG_MROUTE_DEL: {
1052 ZebraMlagMrouteDel *msg = NULL;
1053
1054 msg = zebra_mlag_mroute_del__unpack(NULL, hdr->data.len,
1055 hdr->data.data);
1056 if (msg == NULL) {
1057 zebra_mlag__header__free_unpacked(hdr, NULL);
1058 return -1;
1059 }
1060 /* Payload len */
1061 stream_putw(s, sizeof(struct mlag_mroute_del));
1062 /* No Batching */
1063 stream_putw(s, MLAG_MSG_NO_BATCH);
1064 /* Actual Data */
1065 stream_put(s, msg->vrf_name, VRF_NAMSIZ);
1066
1067 stream_putl(s, msg->source_ip);
1068 stream_putl(s, msg->group_ip);
1069 stream_putl(s, msg->group_ip);
1070 stream_putl(s, msg->owner_id);
1071 stream_putl(s, msg->vrf_id);
1072 if (msg->owner_id == MLAG_OWNER_INTERFACE)
1073 stream_put(s, msg->intf_name, INTERFACE_NAMSIZ);
1074 else
1075 stream_put(s, NULL, INTERFACE_NAMSIZ);
1076 zebra_mlag_mroute_del__free_unpacked(msg, NULL);
1077 } break;
1078 case ZEBRA_MLAG__HEADER__MESSAGE_TYPE__ZEBRA_MLAG_MROUTE_ADD_BULK: {
1079 ZebraMlagMrouteAddBulk *Bulk_msg = NULL;
1080 ZebraMlagMrouteAdd *msg = NULL;
1081 size_t i;
1082
1083 Bulk_msg = zebra_mlag_mroute_add_bulk__unpack(
1084 NULL, hdr->data.len, hdr->data.data);
1085 if (Bulk_msg == NULL) {
1086 zebra_mlag__header__free_unpacked(hdr, NULL);
1087 return -1;
1088 }
1089 /* Payload len */
1090 stream_putw(s, (Bulk_msg->n_mroute_add
1091 * sizeof(struct mlag_mroute_add)));
1092 /* No. of msgs in Batch */
1093 stream_putw(s, Bulk_msg->n_mroute_add);
1094
1095 /* Actual Data */
1096 for (i = 0; i < Bulk_msg->n_mroute_add; i++) {
1097
1098 msg = Bulk_msg->mroute_add[i];
1099
1100 stream_put(s, msg->vrf_name, VRF_NAMSIZ);
1101 stream_putl(s, msg->source_ip);
1102 stream_putl(s, msg->group_ip);
1103 stream_putl(s, msg->cost_to_rp);
1104 stream_putl(s, msg->owner_id);
1105 stream_putc(s, msg->am_i_dr);
1106 stream_putc(s, msg->am_i_dual_active);
1107 stream_putl(s, msg->vrf_id);
1108 if (msg->owner_id == MLAG_OWNER_INTERFACE)
1109 stream_put(s, msg->intf_name,
1110 INTERFACE_NAMSIZ);
1111 else
1112 stream_put(s, NULL, INTERFACE_NAMSIZ);
1113 }
1114 zebra_mlag_mroute_add_bulk__free_unpacked(Bulk_msg,
1115 NULL);
1116 } break;
1117 case ZEBRA_MLAG__HEADER__MESSAGE_TYPE__ZEBRA_MLAG_MROUTE_DEL_BULK: {
1118 ZebraMlagMrouteDelBulk *Bulk_msg = NULL;
1119 ZebraMlagMrouteDel *msg = NULL;
1120 size_t i;
1121
1122 Bulk_msg = zebra_mlag_mroute_del_bulk__unpack(
1123 NULL, hdr->data.len, hdr->data.data);
1124 if (Bulk_msg == NULL) {
1125 zebra_mlag__header__free_unpacked(hdr, NULL);
1126 return -1;
1127 }
1128 /* Payload len */
1129 stream_putw(s, (Bulk_msg->n_mroute_del
1130 * sizeof(struct mlag_mroute_del)));
1131 /* No. of msgs in Batch */
1132 stream_putw(s, Bulk_msg->n_mroute_del);
1133
1134 /* Actual Data */
1135 for (i = 0; i < Bulk_msg->n_mroute_del; i++) {
1136
1137 msg = Bulk_msg->mroute_del[i];
1138
1139 stream_put(s, msg->vrf_name, VRF_NAMSIZ);
1140 stream_putl(s, msg->source_ip);
1141 stream_putl(s, msg->group_ip);
1142 stream_putl(s, msg->owner_id);
1143 stream_putl(s, msg->vrf_id);
1144 if (msg->owner_id == MLAG_OWNER_INTERFACE)
1145 stream_put(s, msg->intf_name,
1146 INTERFACE_NAMSIZ);
1147 else
1148 stream_put(s, NULL, INTERFACE_NAMSIZ);
1149 }
1150 zebra_mlag_mroute_del_bulk__free_unpacked(Bulk_msg,
1151 NULL);
1152 } break;
1153 case ZEBRA_MLAG__HEADER__MESSAGE_TYPE__ZEBRA_MLAG_ZEBRA_STATUS_UPDATE: {
1154 ZebraMlagZebraStatusUpdate *msg = NULL;
1155
1156 msg = zebra_mlag_zebra_status_update__unpack(
1157 NULL, hdr->data.len, hdr->data.data);
1158 if (msg == NULL) {
1159 zebra_mlag__header__free_unpacked(hdr, NULL);
1160 return -1;
1161 }
1162 /* Payload len */
1163 stream_putw(s, sizeof(struct mlag_frr_status));
1164 /* No Batching */
1165 stream_putw(s, MLAG_MSG_NO_BATCH);
1166 /* Actual Data */
1167 stream_putl(s, msg->peer_frrstate);
1168 zebra_mlag_zebra_status_update__free_unpacked(msg,
1169 NULL);
1170 } break;
1171 default:
1172 break;
1173 }
1174 }
1175 zebra_mlag__header__free_unpacked(hdr, NULL);
1176 return msg_type;
1177}
1178
1179#else
ee235396
SK
1180int zebra_mlag_protobuf_encode_client_data(struct stream *s, uint32_t *msg_type)
1181{
1182 return 0;
1183}
1184
67fa73f2 1185int zebra_mlag_protobuf_decode_message(struct stream *s, uint8_t *data,
ee235396
SK
1186 uint32_t len)
1187{
1188 return 0;
1189}
67fa73f2 1190#endif