]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_mlag.c
pimd: Add accidently missed code during upstreaming process
[mirror_frr.git] / pimd / pim_mlag.c
1 /*
2 * This is an implementation of PIM MLAG Functionality
3 *
4 * Module name: PIM MLAG
5 *
6 * Author: sathesh Kumar karra <sathk@cumulusnetworks.com>
7 *
8 * Copyright (C) 2019 Cumulus Networks http://www.cumulusnetworks.com
9 *
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the Free
12 * Software Foundation; either version 2 of the License, or (at your option)
13 * any later version.
14 *
15 * This program is distributed in the hope that it will be useful, but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
18 * more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; see the file COPYING; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24 #include <zebra.h>
25
26 #include "pimd.h"
27 #include "pim_mlag.h"
28 #include "pim_upstream.h"
29 #include "pim_vxlan.h"
30
31 extern struct zclient *zclient;
32
33 #define PIM_MLAG_METADATA_LEN 4
34
35 /******************************* pim upstream sync **************************/
36 /* Update DF role for the upstream entry and return true on role change */
37 bool pim_mlag_up_df_role_update(struct pim_instance *pim,
38 struct pim_upstream *up, bool is_df, const char *reason)
39 {
40 struct channel_oil *c_oil = up->channel_oil;
41 bool old_is_df = !PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(up->flags);
42 struct pim_interface *vxlan_ifp;
43
44 if (is_df == old_is_df) {
45 if (PIM_DEBUG_MLAG)
46 zlog_debug(
47 "%s: Ignoring Role update for %s, since no change",
48 __func__, up->sg_str);
49 return false;
50 }
51
52 if (PIM_DEBUG_MLAG)
53 zlog_debug("local MLAG mroute %s role changed to %s based on %s",
54 up->sg_str, is_df ? "df" : "non-df", reason);
55
56 if (is_df)
57 PIM_UPSTREAM_FLAG_UNSET_MLAG_NON_DF(up->flags);
58 else
59 PIM_UPSTREAM_FLAG_SET_MLAG_NON_DF(up->flags);
60
61
62 /* If the DF role has changed check if ipmr-lo needs to be
63 * muted/un-muted. Active-Active devices and vxlan termination
64 * devices (ipmr-lo) are suppressed on the non-DF.
65 * This may leave the mroute with the empty OIL in which case the
66 * the forwarding entry's sole purpose is to just blackhole the flow
67 * headed to the switch.
68 */
69 if (c_oil) {
70 vxlan_ifp = pim_vxlan_get_term_ifp(pim);
71 if (vxlan_ifp)
72 pim_channel_update_oif_mute(c_oil, vxlan_ifp);
73 }
74
75 /* If DF role changed on a (*,G) termination mroute update the
76 * associated DF role on the inherited (S,G) entries
77 */
78 if ((up->sg.src.s_addr == INADDR_ANY) &&
79 PIM_UPSTREAM_FLAG_TEST_MLAG_VXLAN(up->flags))
80 pim_vxlan_inherit_mlag_flags(pim, up, true /* inherit */);
81
82 return true;
83 }
84
85 /* Run per-upstream entry DF election and return true on role change */
86 static bool pim_mlag_up_df_role_elect(struct pim_instance *pim,
87 struct pim_upstream *up)
88 {
89 bool is_df;
90 uint32_t peer_cost;
91 uint32_t local_cost;
92 bool rv;
93
94 if (!pim_up_mlag_is_local(up))
95 return false;
96
97 /* We are yet to rx a status update from the local MLAG daemon so
98 * we will assume DF status.
99 */
100 if (!(router->mlag_flags & PIM_MLAGF_STATUS_RXED))
101 return pim_mlag_up_df_role_update(pim, up,
102 true /*is_df*/, "mlagd-down");
103
104 /* If not connected to peer assume DF role on the MLAG primary
105 * switch (and non-DF on the secondary switch.
106 */
107 if (!(router->mlag_flags & PIM_MLAGF_PEER_CONN_UP)) {
108 is_df = (router->mlag_role == MLAG_ROLE_PRIMARY) ? true : false;
109 return pim_mlag_up_df_role_update(pim, up,
110 is_df, "peer-down");
111 }
112
113 /* If MLAG peer session is up but zebra is down on the peer
114 * assume DF role.
115 */
116 if (!(router->mlag_flags & PIM_MLAGF_PEER_ZEBRA_UP))
117 return pim_mlag_up_df_role_update(pim, up,
118 true /*is_df*/, "zebra-down");
119
120 /* If we are connected to peer switch but don't have a mroute
121 * from it we have to assume non-DF role to avoid duplicates.
122 * Note: When the peer connection comes up we wait for initial
123 * replay to complete before moving "strays" i.e. local-mlag-mroutes
124 * without a peer reference to non-df role.
125 */
126 if (!PIM_UPSTREAM_FLAG_TEST_MLAG_PEER(up->flags))
127 return pim_mlag_up_df_role_update(pim, up,
128 false /*is_df*/, "no-peer-mroute");
129
130 /* switch with the lowest RPF cost wins. if both switches have the same
131 * cost MLAG role is used as a tie breaker (MLAG primary wins).
132 */
133 peer_cost = up->mlag.peer_mrib_metric;
134 local_cost = pim_up_mlag_local_cost(up);
135 if (local_cost == peer_cost) {
136 is_df = (router->mlag_role == MLAG_ROLE_PRIMARY) ? true : false;
137 rv = pim_mlag_up_df_role_update(pim, up, is_df, "equal-cost");
138 } else {
139 is_df = (local_cost < peer_cost) ? true : false;
140 rv = pim_mlag_up_df_role_update(pim, up, is_df, "cost");
141 }
142
143 return rv;
144 }
145
146 /* Handle upstream entry add from the peer MLAG switch -
147 * - if a local entry doesn't exist one is created with reference
148 * _MLAG_PEER
149 * - if a local entry exists and has a MLAG OIF DF election is run.
150 * the non-DF switch stop forwarding traffic to MLAG devices.
151 */
152 static void pim_mlag_up_peer_add(struct mlag_mroute_add *msg)
153 {
154 struct pim_upstream *up;
155 struct pim_instance *pim;
156 int flags = 0;
157 struct prefix_sg sg;
158 struct vrf *vrf;
159 char sg_str[PIM_SG_LEN];
160
161 memset(&sg, 0, sizeof(struct prefix_sg));
162 sg.src.s_addr = htonl(msg->source_ip);
163 sg.grp.s_addr = htonl(msg->group_ip);
164 if (PIM_DEBUG_MLAG)
165 pim_str_sg_set(&sg, sg_str);
166
167 if (PIM_DEBUG_MLAG)
168 zlog_debug("peer MLAG mroute add %s:%s cost %d",
169 msg->vrf_name, sg_str, msg->cost_to_rp);
170
171 /* XXX - this is not correct. we MUST cache updates to avoid losing
172 * an entry because of race conditions with the peer switch.
173 */
174 vrf = vrf_lookup_by_name(msg->vrf_name);
175 if (!vrf) {
176 if (PIM_DEBUG_MLAG)
177 zlog_debug("peer MLAG mroute add failed %s:%s; no vrf",
178 msg->vrf_name, sg_str);
179 return;
180 }
181 pim = vrf->info;
182
183 up = pim_upstream_find(pim, &sg);
184 if (up) {
185 /* upstream already exists; create peer reference if it
186 * doesn't already exist.
187 */
188 if (!PIM_UPSTREAM_FLAG_TEST_MLAG_PEER(up->flags))
189 pim_upstream_ref(up, PIM_UPSTREAM_FLAG_MASK_MLAG_PEER,
190 __func__);
191 } else {
192 PIM_UPSTREAM_FLAG_SET_MLAG_PEER(flags);
193 up = pim_upstream_add(pim, &sg, NULL /*iif*/, flags, __func__,
194 NULL /*if_ch*/);
195
196 if (!up) {
197 if (PIM_DEBUG_MLAG)
198 zlog_debug("peer MLAG mroute add failed %s:%s",
199 vrf->name, sg_str);
200 return;
201 }
202 }
203 up->mlag.peer_mrib_metric = msg->cost_to_rp;
204 pim_mlag_up_df_role_elect(pim, up);
205 }
206
207 /* Handle upstream entry del from the peer MLAG switch -
208 * - peer reference is removed. this can result in the upstream
209 * being deleted altogether.
210 * - if a local entry continues to exisy and has a MLAG OIF DF election
211 * is re-run (at the end of which the local entry will be the DF).
212 */
213 static void pim_mlag_up_peer_deref(struct pim_instance *pim,
214 struct pim_upstream *up)
215 {
216 if (!PIM_UPSTREAM_FLAG_TEST_MLAG_PEER(up->flags))
217 return;
218
219 PIM_UPSTREAM_FLAG_UNSET_MLAG_PEER(up->flags);
220 up = pim_upstream_del(pim, up, __func__);
221 if (up)
222 pim_mlag_up_df_role_elect(pim, up);
223 }
224 static void pim_mlag_up_peer_del(struct mlag_mroute_del *msg)
225 {
226 struct pim_upstream *up;
227 struct pim_instance *pim;
228 struct prefix_sg sg;
229 struct vrf *vrf;
230 char sg_str[PIM_SG_LEN];
231
232 memset(&sg, 0, sizeof(struct prefix_sg));
233 sg.src.s_addr = htonl(msg->source_ip);
234 sg.grp.s_addr = htonl(msg->group_ip);
235 if (PIM_DEBUG_MLAG)
236 pim_str_sg_set(&sg, sg_str);
237
238 if (PIM_DEBUG_MLAG)
239 zlog_debug("peer MLAG mroute del %s:%s", msg->vrf_name,
240 sg_str);
241
242 vrf = vrf_lookup_by_name(msg->vrf_name);
243 if (!vrf) {
244 if (PIM_DEBUG_MLAG)
245 zlog_debug("peer MLAG mroute del skipped %s:%s; no vrf",
246 msg->vrf_name, sg_str);
247 return;
248 }
249 pim = vrf->info;
250
251 up = pim_upstream_find(pim, &sg);
252 if (!up) {
253 if (PIM_DEBUG_MLAG)
254 zlog_debug("peer MLAG mroute del skipped %s:%s; no up",
255 vrf->name, sg_str);
256 return;
257 }
258
259 pim_mlag_up_peer_deref(pim, up);
260 }
261
262 /* When we lose connection to the local MLAG daemon we can drop all peer
263 * references.
264 */
265 static void pim_mlag_up_peer_del_all(void)
266 {
267 struct list *temp = list_new();
268 struct pim_upstream *up;
269 struct vrf *vrf;
270 struct pim_instance *pim;
271
272 /*
273 * So why these gyrations?
274 * pim->upstream_head has the list of *,G and S,G
275 * that are in the system. The problem of course
276 * is that it is an ordered list:
277 * (*,G1) -> (S1,G1) -> (S2,G2) -> (S3, G2) -> (*,G2) -> (S1,G2)
278 * And the *,G1 has pointers to S1,G1 and S2,G1
279 * if we delete *,G1 then we have a situation where
280 * S1,G1 and S2,G2 can be deleted as well. Then a
281 * simple ALL_LIST_ELEMENTS will have the next listnode
282 * pointer become invalid and we crash.
283 * So let's grab the list of MLAG_PEER upstreams
284 * add a refcount put on another list and delete safely
285 */
286 RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) {
287 pim = vrf->info;
288 frr_each (rb_pim_upstream, &pim->upstream_head, up) {
289 if (!PIM_UPSTREAM_FLAG_TEST_MLAG_PEER(up->flags))
290 continue;
291 listnode_add(temp, up);
292 /*
293 * Add a reference since we are adding to this
294 * list for deletion
295 */
296 up->ref_count++;
297 }
298
299 while (temp->count) {
300 up = listnode_head(temp);
301 listnode_delete(temp, up);
302
303 pim_mlag_up_peer_deref(pim, up);
304 /*
305 * This is the deletion of the reference added
306 * above
307 */
308 pim_upstream_del(pim, up, __func__);
309 }
310 }
311
312 list_delete(&temp);
313 }
314
315 /* Send upstream entry to the local MLAG daemon (which will subsequently
316 * send it to the peer MLAG switch).
317 */
318 static void pim_mlag_up_local_add_send(struct pim_instance *pim,
319 struct pim_upstream *up)
320 {
321 struct stream *s = NULL;
322 struct vrf *vrf = pim->vrf;
323
324 if (!(router->mlag_flags & PIM_MLAGF_LOCAL_CONN_UP))
325 return;
326
327 s = stream_new(sizeof(struct mlag_mroute_add) + PIM_MLAG_METADATA_LEN);
328 if (!s)
329 return;
330
331 if (PIM_DEBUG_MLAG)
332 zlog_debug("local MLAG mroute add %s:%s",
333 vrf->name, up->sg_str);
334
335 ++router->mlag_stats.msg.mroute_add_tx;
336
337 stream_putl(s, MLAG_MROUTE_ADD);
338 stream_put(s, vrf->name, VRF_NAMSIZ);
339 stream_putl(s, ntohl(up->sg.src.s_addr));
340 stream_putl(s, ntohl(up->sg.grp.s_addr));
341
342 stream_putl(s, pim_up_mlag_local_cost(up));
343 /* XXX - who is addding*/
344 stream_putl(s, MLAG_OWNER_VXLAN);
345 /* XXX - am_i_DR field should be removed */
346 stream_putc(s, false);
347 stream_putc(s, !(PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(up->flags)));
348 stream_putl(s, vrf->vrf_id);
349 /* XXX - this field is a No-op for VXLAN*/
350 stream_put(s, NULL, INTERFACE_NAMSIZ);
351
352 stream_fifo_push_safe(router->mlag_fifo, s);
353 pim_mlag_signal_zpthread();
354 }
355
356 static void pim_mlag_up_local_del_send(struct pim_instance *pim,
357 struct pim_upstream *up)
358 {
359 struct stream *s = NULL;
360 struct vrf *vrf = pim->vrf;
361
362 if (!(router->mlag_flags & PIM_MLAGF_LOCAL_CONN_UP))
363 return;
364
365 s = stream_new(sizeof(struct mlag_mroute_del) + PIM_MLAG_METADATA_LEN);
366 if (!s)
367 return;
368
369 if (PIM_DEBUG_MLAG)
370 zlog_debug("local MLAG mroute del %s:%s",
371 vrf->name, up->sg_str);
372
373 ++router->mlag_stats.msg.mroute_del_tx;
374
375 stream_putl(s, MLAG_MROUTE_DEL);
376 stream_put(s, vrf->name, VRF_NAMSIZ);
377 stream_putl(s, ntohl(up->sg.src.s_addr));
378 stream_putl(s, ntohl(up->sg.grp.s_addr));
379 /* XXX - who is adding */
380 stream_putl(s, MLAG_OWNER_VXLAN);
381 stream_putl(s, vrf->vrf_id);
382 /* XXX - this field is a No-op for VXLAN */
383 stream_put(s, NULL, INTERFACE_NAMSIZ);
384
385 /* XXX - is this the the most optimal way to do things */
386 stream_fifo_push_safe(router->mlag_fifo, s);
387 pim_mlag_signal_zpthread();
388 }
389
390
391 /* Called when a local upstream entry is created or if it's cost changes */
392 void pim_mlag_up_local_add(struct pim_instance *pim,
393 struct pim_upstream *up)
394 {
395 pim_mlag_up_df_role_elect(pim, up);
396 /* XXX - need to add some dup checks here */
397 pim_mlag_up_local_add_send(pim, up);
398 }
399
400 /* Called when local MLAG reference is removed from an upstream entry */
401 void pim_mlag_up_local_del(struct pim_instance *pim,
402 struct pim_upstream *up)
403 {
404 pim_mlag_up_df_role_elect(pim, up);
405 pim_mlag_up_local_del_send(pim, up);
406 }
407
408 /* When connection to local MLAG daemon is established all the local
409 * MLAG upstream entries are replayed to it.
410 */
411 static void pim_mlag_up_local_replay(void)
412 {
413 struct pim_upstream *up;
414 struct vrf *vrf;
415 struct pim_instance *pim;
416
417 RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) {
418 pim = vrf->info;
419 frr_each (rb_pim_upstream, &pim->upstream_head, up) {
420 if (pim_up_mlag_is_local(up))
421 pim_mlag_up_local_add_send(pim, up);
422 }
423 }
424 }
425
426 /* on local/peer mlag connection and role changes the DF status needs
427 * to be re-evaluated
428 */
429 static void pim_mlag_up_local_reeval(bool mlagd_send, const char *reason_code)
430 {
431 struct pim_upstream *up;
432 struct vrf *vrf;
433 struct pim_instance *pim;
434
435 if (PIM_DEBUG_MLAG)
436 zlog_debug("%s re-run DF election because of %s",
437 __func__, reason_code);
438 RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) {
439 pim = vrf->info;
440 frr_each (rb_pim_upstream, &pim->upstream_head, up) {
441 if (!pim_up_mlag_is_local(up))
442 continue;
443 /* if role changes re-send to peer */
444 if (pim_mlag_up_df_role_elect(pim, up) &&
445 mlagd_send)
446 pim_mlag_up_local_add_send(pim, up);
447 }
448 }
449 }
450
451 /*****************PIM Actions for MLAG state changes**********************/
452
453 /* notify the anycast VTEP component about state changes */
454 static inline void pim_mlag_vxlan_state_update(void)
455 {
456 bool enable = !!(router->mlag_flags & PIM_MLAGF_STATUS_RXED);
457 bool peer_state = !!(router->mlag_flags & PIM_MLAGF_PEER_CONN_UP);
458
459 pim_vxlan_mlag_update(enable, peer_state, router->mlag_role,
460 router->peerlink_rif_p, &router->local_vtep_ip);
461
462 }
463
464 /**************End of PIM Actions for MLAG State changes******************/
465
466
467 /********************API to process PIM MLAG Data ************************/
468
469 static void pim_mlag_process_mlagd_state_change(struct mlag_status msg)
470 {
471 bool role_chg = false;
472 bool state_chg = false;
473 bool notify_vxlan = false;
474 struct interface *peerlink_rif_p;
475 char buf[MLAG_ROLE_STRSIZE];
476
477 if (PIM_DEBUG_MLAG)
478 zlog_debug("%s: msg dump: my_role: %s, peer_state: %s",
479 __func__,
480 mlag_role2str(msg.my_role, buf, sizeof(buf)),
481 (msg.peer_state == MLAG_STATE_RUNNING ? "RUNNING"
482 : "DOWN"));
483
484 if (!(router->mlag_flags & PIM_MLAGF_LOCAL_CONN_UP)) {
485 if (PIM_DEBUG_MLAG)
486 zlog_debug("%s: msg ignored mlagd process state down",
487 __func__);
488 return;
489 }
490 ++router->mlag_stats.msg.mlag_status_updates;
491
492 /* evaluate the changes first */
493 if (router->mlag_role != msg.my_role) {
494 role_chg = true;
495 notify_vxlan = true;
496 router->mlag_role = msg.my_role;
497 }
498
499 strcpy(router->peerlink_rif, msg.peerlink_rif);
500 /* XXX - handle the case where we may rx the interface name from the
501 * MLAG daemon before we get the interface from zebra.
502 */
503 peerlink_rif_p = if_lookup_by_name(router->peerlink_rif, VRF_DEFAULT);
504 if (router->peerlink_rif_p != peerlink_rif_p) {
505 router->peerlink_rif_p = peerlink_rif_p;
506 notify_vxlan = true;
507 }
508
509 if (msg.peer_state == MLAG_STATE_RUNNING) {
510 if (!(router->mlag_flags & PIM_MLAGF_PEER_CONN_UP)) {
511 state_chg = true;
512 notify_vxlan = true;
513 router->mlag_flags |= PIM_MLAGF_PEER_CONN_UP;
514 }
515 router->connected_to_mlag = true;
516 } else {
517 if (router->mlag_flags & PIM_MLAGF_PEER_CONN_UP) {
518 ++router->mlag_stats.peer_session_downs;
519 state_chg = true;
520 notify_vxlan = true;
521 router->mlag_flags &= ~PIM_MLAGF_PEER_CONN_UP;
522 }
523 router->connected_to_mlag = false;
524 }
525
526 /* apply the changes */
527 /* when connection to mlagd comes up we hold send mroutes till we have
528 * rxed the status and had a chance to re-valuate DF state
529 */
530 if (!(router->mlag_flags & PIM_MLAGF_STATUS_RXED)) {
531 router->mlag_flags |= PIM_MLAGF_STATUS_RXED;
532 pim_mlag_vxlan_state_update();
533 /* on session up re-eval DF status */
534 pim_mlag_up_local_reeval(false /*mlagd_send*/, "mlagd_up");
535 /* replay all the upstream entries to the local MLAG daemon */
536 pim_mlag_up_local_replay();
537 return;
538 }
539
540 if (notify_vxlan)
541 pim_mlag_vxlan_state_update();
542
543 if (state_chg) {
544 if (!(router->mlag_flags & PIM_MLAGF_PEER_CONN_UP))
545 /* when a connection goes down the primary takes over
546 * DF role for all entries
547 */
548 pim_mlag_up_local_reeval(true /*mlagd_send*/,
549 "peer_down");
550 else
551 /* XXX - when session comes up we need to wait for
552 * PEER_REPLAY_DONE before running re-election on
553 * local-mlag entries that are missing peer reference
554 */
555 pim_mlag_up_local_reeval(true /*mlagd_send*/,
556 "peer_up");
557 } else if (role_chg) {
558 /* MLAG role changed without a state change */
559 pim_mlag_up_local_reeval(true /*mlagd_send*/, "role_chg");
560 }
561 }
562
563 static void pim_mlag_process_peer_frr_state_change(struct mlag_frr_status msg)
564 {
565 if (PIM_DEBUG_MLAG)
566 zlog_debug(
567 "%s: msg dump: peer_frr_state: %s", __func__,
568 (msg.frr_state == MLAG_FRR_STATE_UP ? "UP" : "DOWN"));
569
570 if (!(router->mlag_flags & PIM_MLAGF_LOCAL_CONN_UP)) {
571 if (PIM_DEBUG_MLAG)
572 zlog_debug("%s: msg ignored mlagd process state down",
573 __func__);
574 return;
575 }
576 ++router->mlag_stats.msg.peer_zebra_status_updates;
577
578 /* evaluate the changes first */
579 if (msg.frr_state == MLAG_FRR_STATE_UP) {
580 if (!(router->mlag_flags & PIM_MLAGF_PEER_ZEBRA_UP)) {
581 router->mlag_flags |= PIM_MLAGF_PEER_ZEBRA_UP;
582 /* XXX - when peer zebra comes up we need to wait for
583 * for some time to let the peer setup MDTs before
584 * before relinquishing DF status
585 */
586 pim_mlag_up_local_reeval(true /*mlagd_send*/,
587 "zebra_up");
588 }
589 } else {
590 if (router->mlag_flags & PIM_MLAGF_PEER_ZEBRA_UP) {
591 ++router->mlag_stats.peer_zebra_downs;
592 router->mlag_flags &= ~PIM_MLAGF_PEER_ZEBRA_UP;
593 /* when a peer zebra goes down we assume DF role */
594 pim_mlag_up_local_reeval(true /*mlagd_send*/,
595 "zebra_down");
596 }
597 }
598 }
599
600 static void pim_mlag_process_vxlan_update(struct mlag_vxlan *msg)
601 {
602 char addr_buf1[INET_ADDRSTRLEN];
603 char addr_buf2[INET_ADDRSTRLEN];
604 uint32_t local_ip;
605
606 if (!(router->mlag_flags & PIM_MLAGF_LOCAL_CONN_UP)) {
607 if (PIM_DEBUG_MLAG)
608 zlog_debug("%s: msg ignored mlagd process state down",
609 __func__);
610 return;
611 }
612
613 ++router->mlag_stats.msg.vxlan_updates;
614 router->anycast_vtep_ip.s_addr = htonl(msg->anycast_ip);
615 local_ip = htonl(msg->local_ip);
616 if (router->local_vtep_ip.s_addr != local_ip) {
617 router->local_vtep_ip.s_addr = local_ip;
618 pim_mlag_vxlan_state_update();
619 }
620
621 if (PIM_DEBUG_MLAG) {
622 inet_ntop(AF_INET, &router->local_vtep_ip,
623 addr_buf1, INET_ADDRSTRLEN);
624 inet_ntop(AF_INET, &router->anycast_vtep_ip,
625 addr_buf2, INET_ADDRSTRLEN);
626
627 zlog_debug("%s: msg dump: local-ip:%s, anycast-ip:%s",
628 __func__, addr_buf1, addr_buf2);
629 }
630 }
631
632 static void pim_mlag_process_mroute_add(struct mlag_mroute_add msg)
633 {
634 if (PIM_DEBUG_MLAG) {
635 zlog_debug(
636 "%s: msg dump: vrf_name: %s, s.ip: 0x%x, g.ip: 0x%x cost: %u",
637 __func__, msg.vrf_name, msg.source_ip,
638 msg.group_ip, msg.cost_to_rp);
639 zlog_debug(
640 "owner_id: %d, DR: %d, Dual active: %d, vrf_id: 0x%x intf_name: %s",
641 msg.owner_id, msg.am_i_dr, msg.am_i_dual_active,
642 msg.vrf_id, msg.intf_name);
643 }
644
645 if (!(router->mlag_flags & PIM_MLAGF_LOCAL_CONN_UP)) {
646 if (PIM_DEBUG_MLAG)
647 zlog_debug("%s: msg ignored mlagd process state down",
648 __func__);
649 return;
650 }
651
652 ++router->mlag_stats.msg.mroute_add_rx;
653
654 pim_mlag_up_peer_add(&msg);
655 }
656
657 static void pim_mlag_process_mroute_del(struct mlag_mroute_del msg)
658 {
659 if (PIM_DEBUG_MLAG) {
660 zlog_debug(
661 "%s: msg dump: vrf_name: %s, s.ip: 0x%x, g.ip: 0x%x ",
662 __func__, msg.vrf_name, msg.source_ip,
663 msg.group_ip);
664 zlog_debug("owner_id: %d, vrf_id: 0x%x intf_name: %s",
665 msg.owner_id, msg.vrf_id, msg.intf_name);
666 }
667
668 if (!(router->mlag_flags & PIM_MLAGF_LOCAL_CONN_UP)) {
669 if (PIM_DEBUG_MLAG)
670 zlog_debug("%s: msg ignored mlagd process state down",
671 __func__);
672 return;
673 }
674
675 ++router->mlag_stats.msg.mroute_del_rx;
676
677 pim_mlag_up_peer_del(&msg);
678 }
679
680 int pim_zebra_mlag_handle_msg(struct stream *s, int len)
681 {
682 struct mlag_msg mlag_msg;
683 char buf[ZLOG_FILTER_LENGTH_MAX];
684 int rc = 0;
685
686 rc = mlag_lib_decode_mlag_hdr(s, &mlag_msg);
687 if (rc)
688 return (rc);
689
690 if (PIM_DEBUG_MLAG)
691 zlog_debug("%s: Received msg type: %s length: %d, bulk_cnt: %d",
692 __func__,
693 mlag_lib_msgid_to_str(mlag_msg.msg_type, buf,
694 sizeof(buf)),
695 mlag_msg.data_len, mlag_msg.msg_cnt);
696
697 switch (mlag_msg.msg_type) {
698 case MLAG_STATUS_UPDATE: {
699 struct mlag_status msg;
700
701 rc = mlag_lib_decode_mlag_status(s, &msg);
702 if (rc)
703 return (rc);
704 pim_mlag_process_mlagd_state_change(msg);
705 } break;
706 case MLAG_PEER_FRR_STATUS: {
707 struct mlag_frr_status msg;
708
709 rc = mlag_lib_decode_frr_status(s, &msg);
710 if (rc)
711 return (rc);
712 pim_mlag_process_peer_frr_state_change(msg);
713 } break;
714 case MLAG_VXLAN_UPDATE: {
715 struct mlag_vxlan msg;
716
717 rc = mlag_lib_decode_vxlan_update(s, &msg);
718 if (rc)
719 return rc;
720 pim_mlag_process_vxlan_update(&msg);
721 } break;
722 case MLAG_MROUTE_ADD: {
723 struct mlag_mroute_add msg;
724
725 rc = mlag_lib_decode_mroute_add(s, &msg);
726 if (rc)
727 return (rc);
728 pim_mlag_process_mroute_add(msg);
729 } break;
730 case MLAG_MROUTE_DEL: {
731 struct mlag_mroute_del msg;
732
733 rc = mlag_lib_decode_mroute_del(s, &msg);
734 if (rc)
735 return (rc);
736 pim_mlag_process_mroute_del(msg);
737 } break;
738 case MLAG_MROUTE_ADD_BULK: {
739 struct mlag_mroute_add msg;
740 int i;
741
742 for (i = 0; i < mlag_msg.msg_cnt; i++) {
743
744 rc = mlag_lib_decode_mroute_add(s, &msg);
745 if (rc)
746 return (rc);
747 pim_mlag_process_mroute_add(msg);
748 }
749 } break;
750 case MLAG_MROUTE_DEL_BULK: {
751 struct mlag_mroute_del msg;
752 int i;
753
754 for (i = 0; i < mlag_msg.msg_cnt; i++) {
755
756 rc = mlag_lib_decode_mroute_del(s, &msg);
757 if (rc)
758 return (rc);
759 pim_mlag_process_mroute_del(msg);
760 }
761 } break;
762 default:
763 break;
764 }
765 return 0;
766 }
767
768 /****************End of PIM Mesasge processing handler********************/
769
770 int pim_zebra_mlag_process_up(void)
771 {
772 if (PIM_DEBUG_MLAG)
773 zlog_debug("%s: Received Process-Up from Mlag", __func__);
774
775 return 0;
776 }
777
778 static void pim_mlag_param_reset(void)
779 {
780 /* reset the cached params and stats */
781 router->mlag_flags &= ~(PIM_MLAGF_STATUS_RXED |
782 PIM_MLAGF_LOCAL_CONN_UP |
783 PIM_MLAGF_PEER_CONN_UP |
784 PIM_MLAGF_PEER_ZEBRA_UP);
785 router->local_vtep_ip.s_addr = INADDR_ANY;
786 router->anycast_vtep_ip.s_addr = INADDR_ANY;
787 router->mlag_role = MLAG_ROLE_NONE;
788 memset(&router->mlag_stats.msg, 0, sizeof(router->mlag_stats.msg));
789 router->peerlink_rif[0] = '\0';
790 }
791
792 int pim_zebra_mlag_process_down(void)
793 {
794 if (PIM_DEBUG_MLAG)
795 zlog_debug("%s: Received Process-Down from Mlag", __func__);
796
797 /* Local CLAG is down, reset peer data and forward the traffic if
798 * we are DR
799 */
800 if (router->mlag_flags & PIM_MLAGF_PEER_CONN_UP)
801 ++router->mlag_stats.peer_session_downs;
802 if (router->mlag_flags & PIM_MLAGF_PEER_ZEBRA_UP)
803 ++router->mlag_stats.peer_zebra_downs;
804 router->connected_to_mlag = false;
805 pim_mlag_param_reset();
806 /* on mlagd session down re-eval DF status */
807 pim_mlag_up_local_reeval(false /*mlagd_send*/, "mlagd_down");
808 /* flush all peer references */
809 pim_mlag_up_peer_del_all();
810 /* notify the vxlan component */
811 pim_mlag_vxlan_state_update();
812 return 0;
813 }
814
815 static int pim_mlag_register_handler(struct thread *thread)
816 {
817 uint32_t bit_mask = 0;
818
819 if (!zclient)
820 return -1;
821
822 SET_FLAG(bit_mask, (1 << MLAG_STATUS_UPDATE));
823 SET_FLAG(bit_mask, (1 << MLAG_MROUTE_ADD));
824 SET_FLAG(bit_mask, (1 << MLAG_MROUTE_DEL));
825 SET_FLAG(bit_mask, (1 << MLAG_DUMP));
826 SET_FLAG(bit_mask, (1 << MLAG_MROUTE_ADD_BULK));
827 SET_FLAG(bit_mask, (1 << MLAG_MROUTE_DEL_BULK));
828 SET_FLAG(bit_mask, (1 << MLAG_PIM_CFG_DUMP));
829 SET_FLAG(bit_mask, (1 << MLAG_VXLAN_UPDATE));
830 SET_FLAG(bit_mask, (1 << MLAG_PEER_FRR_STATUS));
831
832 if (PIM_DEBUG_MLAG)
833 zlog_debug("%s: Posting Client Register to MLAG mask: 0x%x",
834 __func__, bit_mask);
835
836 zclient_send_mlag_register(zclient, bit_mask);
837 return 0;
838 }
839
840 void pim_mlag_register(void)
841 {
842 if (router->mlag_process_register)
843 return;
844
845 router->mlag_process_register = true;
846
847 thread_add_event(router->master, pim_mlag_register_handler, NULL, 0,
848 NULL);
849 }
850
851 static int pim_mlag_deregister_handler(struct thread *thread)
852 {
853 if (!zclient)
854 return -1;
855
856 if (PIM_DEBUG_MLAG)
857 zlog_debug("%s: Posting Client De-Register to MLAG from PIM",
858 __func__);
859 router->connected_to_mlag = false;
860 zclient_send_mlag_deregister(zclient);
861 return 0;
862 }
863
864 void pim_mlag_deregister(void)
865 {
866 /* if somebody still interested in the MLAG channel skip de-reg */
867 if (router->pim_mlag_intf_cnt || pim_vxlan_do_mlag_reg())
868 return;
869
870 /* not registered; nothing do */
871 if (!router->mlag_process_register)
872 return;
873
874 router->mlag_process_register = false;
875
876 thread_add_event(router->master, pim_mlag_deregister_handler, NULL, 0,
877 NULL);
878 }
879
880 void pim_if_configure_mlag_dualactive(struct pim_interface *pim_ifp)
881 {
882 if (!pim_ifp || !pim_ifp->pim || pim_ifp->activeactive == true)
883 return;
884
885 pim_ifp->activeactive = true;
886 if (pim_ifp->pim)
887 pim_ifp->pim->inst_mlag_intf_cnt++;
888
889 router->pim_mlag_intf_cnt++;
890 if (PIM_DEBUG_MLAG)
891 zlog_debug(
892 "%s: Total MLAG configured Interfaces on router: %d, Inst: %d",
893 __func__, router->pim_mlag_intf_cnt,
894 pim_ifp->pim->inst_mlag_intf_cnt);
895
896 if (router->pim_mlag_intf_cnt == 1) {
897 /*
898 * atleast one Interface is configured for MLAG, send register
899 * to Zebra for receiving MLAG Updates
900 */
901 pim_mlag_register();
902 }
903 }
904
905 void pim_if_unconfigure_mlag_dualactive(struct pim_interface *pim_ifp)
906 {
907 if (!pim_ifp || !pim_ifp->pim || pim_ifp->activeactive == false)
908 return;
909
910 pim_ifp->activeactive = false;
911 pim_ifp->pim->inst_mlag_intf_cnt--;
912
913 router->pim_mlag_intf_cnt--;
914 if (PIM_DEBUG_MLAG)
915 zlog_debug(
916 "%s: Total MLAG configured Interfaces on router: %d, Inst: %d",
917 __func__, router->pim_mlag_intf_cnt,
918 pim_ifp->pim->inst_mlag_intf_cnt);
919
920 if (router->pim_mlag_intf_cnt == 0) {
921 /*
922 * all the Interfaces are MLAG un-configured, post MLAG
923 * De-register to Zebra
924 */
925 pim_mlag_deregister();
926 pim_mlag_param_reset();
927 }
928 }
929
930
931 void pim_instance_mlag_init(struct pim_instance *pim)
932 {
933 if (!pim)
934 return;
935
936 pim->inst_mlag_intf_cnt = 0;
937 }
938
939
940 void pim_instance_mlag_terminate(struct pim_instance *pim)
941 {
942 struct interface *ifp;
943
944 if (!pim)
945 return;
946
947 FOR_ALL_INTERFACES (pim->vrf, ifp) {
948 struct pim_interface *pim_ifp = ifp->info;
949
950 if (!pim_ifp || pim_ifp->activeactive == false)
951 continue;
952
953 pim_if_unconfigure_mlag_dualactive(pim_ifp);
954 }
955 pim->inst_mlag_intf_cnt = 0;
956 }
957
958 void pim_mlag_init(void)
959 {
960 pim_mlag_param_reset();
961 router->pim_mlag_intf_cnt = 0;
962 router->connected_to_mlag = false;
963 router->mlag_fifo = stream_fifo_new();
964 router->zpthread_mlag_write = NULL;
965 router->mlag_stream = stream_new(MLAG_BUF_LIMIT);
966 }