1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * This is an implementation of PIM MLAG Functionality
5 * Module name: PIM MLAG
7 * Author: sathesh Kumar karra <sathk@cumulusnetworks.com>
9 * Copyright (C) 2019 Cumulus Networks http://www.cumulusnetworks.com
15 #include "pim_upstream.h"
16 #include "pim_vxlan.h"
18 extern struct zclient
*zclient
;
20 #define PIM_MLAG_METADATA_LEN 4
22 /*********************ACtual Data processing *****************************/
23 /* TBD: There can be duplicate updates to FIB***/
24 #define PIM_MLAG_ADD_OIF_TO_OIL(ch, ch_oil) \
28 "%s: add Dual-active Interface to %s " \
30 __func__, ch->interface->name, ch->sg_str); \
31 pim_channel_update_oif_mute(ch_oil, ch->interface->info); \
34 #define PIM_MLAG_DEL_OIF_TO_OIL(ch, ch_oil) \
38 "%s: del Dual-active Interface to %s " \
40 __func__, ch->interface->name, ch->sg_str); \
41 pim_channel_update_oif_mute(ch_oil, ch->interface->info); \
45 static void pim_mlag_calculate_df_for_ifchannels(struct pim_upstream
*up
,
48 struct listnode
*chnode
;
49 struct listnode
*chnextnode
;
50 struct pim_ifchannel
*ch
;
51 struct pim_interface
*pim_ifp
= NULL
;
52 struct channel_oil
*ch_oil
= NULL
;
54 ch_oil
= (up
) ? up
->channel_oil
: NULL
;
60 zlog_debug("%s: Calculating DF for Dual active if-channel%s",
61 __func__
, up
->sg_str
);
63 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
64 pim_ifp
= (ch
->interface
) ? ch
->interface
->info
: NULL
;
65 if (!pim_ifp
|| !PIM_I_am_DualActive(pim_ifp
))
69 PIM_MLAG_ADD_OIF_TO_OIL(ch
, ch_oil
);
71 PIM_MLAG_DEL_OIF_TO_OIL(ch
, ch_oil
);
75 static void pim_mlag_inherit_mlag_flags(struct pim_upstream
*up
, bool is_df
)
77 struct listnode
*listnode
;
78 struct pim_upstream
*child
;
79 struct listnode
*chnode
;
80 struct listnode
*chnextnode
;
81 struct pim_ifchannel
*ch
;
82 struct pim_interface
*pim_ifp
= NULL
;
83 struct channel_oil
*ch_oil
= NULL
;
86 zlog_debug("%s: Updating DF for uptream:%s children", __func__
,
90 for (ALL_LIST_ELEMENTS(up
->ifchannels
, chnode
, chnextnode
, ch
)) {
91 pim_ifp
= (ch
->interface
) ? ch
->interface
->info
: NULL
;
92 if (!pim_ifp
|| !PIM_I_am_DualActive(pim_ifp
))
95 for (ALL_LIST_ELEMENTS_RO(up
->sources
, listnode
, child
)) {
97 zlog_debug("%s: Updating DF for child:%s",
98 __func__
, child
->sg_str
);
99 ch_oil
= (child
) ? child
->channel_oil
: NULL
;
105 PIM_MLAG_ADD_OIF_TO_OIL(ch
, ch_oil
);
107 PIM_MLAG_DEL_OIF_TO_OIL(ch
, ch_oil
);
112 /******************************* pim upstream sync **************************/
113 /* Update DF role for the upstream entry and return true on role change */
114 bool pim_mlag_up_df_role_update(struct pim_instance
*pim
,
115 struct pim_upstream
*up
, bool is_df
, const char *reason
)
117 struct channel_oil
*c_oil
= up
->channel_oil
;
118 bool old_is_df
= !PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(up
->flags
);
119 struct pim_interface
*vxlan_ifp
;
121 if (is_df
== old_is_df
) {
124 "%s: Ignoring Role update for %s, since no change",
125 __func__
, up
->sg_str
);
130 zlog_debug("local MLAG mroute %s role changed to %s based on %s",
131 up
->sg_str
, is_df
? "df" : "non-df", reason
);
134 PIM_UPSTREAM_FLAG_UNSET_MLAG_NON_DF(up
->flags
);
136 PIM_UPSTREAM_FLAG_SET_MLAG_NON_DF(up
->flags
);
140 * This Upstream entry synced to peer Because of Dual-active
141 * Interface configuration
143 if (PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(up
->flags
)) {
144 pim_mlag_inherit_mlag_flags(up
, is_df
);
145 pim_mlag_calculate_df_for_ifchannels(up
, is_df
);
148 /* If the DF role has changed check if ipmr-lo needs to be
149 * muted/un-muted. Active-Active devices and vxlan termination
150 * devices (ipmr-lo) are suppressed on the non-DF.
151 * This may leave the mroute with the empty OIL in which case the
152 * the forwarding entry's sole purpose is to just blackhole the flow
153 * headed to the switch.
156 vxlan_ifp
= pim_vxlan_get_term_ifp(pim
);
158 pim_channel_update_oif_mute(c_oil
, vxlan_ifp
);
161 /* If DF role changed on a (*,G) termination mroute update the
162 * associated DF role on the inherited (S,G) entries
164 if (pim_addr_is_any(up
->sg
.src
) &&
165 PIM_UPSTREAM_FLAG_TEST_MLAG_VXLAN(up
->flags
))
166 pim_vxlan_inherit_mlag_flags(pim
, up
, true /* inherit */);
171 /* Run per-upstream entry DF election and return true on role change */
172 static bool pim_mlag_up_df_role_elect(struct pim_instance
*pim
,
173 struct pim_upstream
*up
)
180 if (!pim_up_mlag_is_local(up
))
183 /* We are yet to rx a status update from the local MLAG daemon so
184 * we will assume DF status.
186 if (!(router
->mlag_flags
& PIM_MLAGF_STATUS_RXED
))
187 return pim_mlag_up_df_role_update(pim
, up
,
188 true /*is_df*/, "mlagd-down");
190 /* If not connected to peer assume DF role on the MLAG primary
191 * switch (and non-DF on the secondary switch.
193 if (!(router
->mlag_flags
& PIM_MLAGF_PEER_CONN_UP
)) {
194 is_df
= (router
->mlag_role
== MLAG_ROLE_PRIMARY
) ? true : false;
195 return pim_mlag_up_df_role_update(pim
, up
,
199 /* If MLAG peer session is up but zebra is down on the peer
202 if (!(router
->mlag_flags
& PIM_MLAGF_PEER_ZEBRA_UP
))
203 return pim_mlag_up_df_role_update(pim
, up
,
204 true /*is_df*/, "zebra-down");
206 /* If we are connected to peer switch but don't have a mroute
207 * from it we have to assume non-DF role to avoid duplicates.
208 * Note: When the peer connection comes up we wait for initial
209 * replay to complete before moving "strays" i.e. local-mlag-mroutes
210 * without a peer reference to non-df role.
212 if (!PIM_UPSTREAM_FLAG_TEST_MLAG_PEER(up
->flags
))
213 return pim_mlag_up_df_role_update(pim
, up
,
214 false /*is_df*/, "no-peer-mroute");
216 /* switch with the lowest RPF cost wins. if both switches have the same
217 * cost MLAG role is used as a tie breaker (MLAG primary wins).
219 peer_cost
= up
->mlag
.peer_mrib_metric
;
220 local_cost
= pim_up_mlag_local_cost(up
);
221 if (local_cost
== peer_cost
) {
222 is_df
= (router
->mlag_role
== MLAG_ROLE_PRIMARY
) ? true : false;
223 rv
= pim_mlag_up_df_role_update(pim
, up
, is_df
, "equal-cost");
225 is_df
= (local_cost
< peer_cost
) ? true : false;
226 rv
= pim_mlag_up_df_role_update(pim
, up
, is_df
, "cost");
232 /* Handle upstream entry add from the peer MLAG switch -
233 * - if a local entry doesn't exist one is created with reference
235 * - if a local entry exists and has a MLAG OIF DF election is run.
236 * the non-DF switch stop forwarding traffic to MLAG devices.
238 static void pim_mlag_up_peer_add(struct mlag_mroute_add
*msg
)
240 struct pim_upstream
*up
;
241 struct pim_instance
*pim
;
246 memset(&sg
, 0, sizeof(sg
));
247 sg
.src
.s_addr
= htonl(msg
->source_ip
);
248 sg
.grp
.s_addr
= htonl(msg
->group_ip
);
251 zlog_debug("peer MLAG mroute add %s:%pSG cost %d",
252 msg
->vrf_name
, &sg
, msg
->cost_to_rp
);
254 /* XXX - this is not correct. we MUST cache updates to avoid losing
255 * an entry because of race conditions with the peer switch.
257 vrf
= vrf_lookup_by_name(msg
->vrf_name
);
261 "peer MLAG mroute add failed %s:%pSG; no vrf",
267 up
= pim_upstream_find(pim
, &sg
);
269 /* upstream already exists; create peer reference if it
270 * doesn't already exist.
272 if (!PIM_UPSTREAM_FLAG_TEST_MLAG_PEER(up
->flags
))
273 pim_upstream_ref(up
, PIM_UPSTREAM_FLAG_MASK_MLAG_PEER
,
276 PIM_UPSTREAM_FLAG_SET_MLAG_PEER(flags
);
277 up
= pim_upstream_add(pim
, &sg
, NULL
/*iif*/, flags
, __func__
,
283 "peer MLAG mroute add failed %s:%pSG",
288 up
->mlag
.peer_mrib_metric
= msg
->cost_to_rp
;
289 pim_mlag_up_df_role_elect(pim
, up
);
292 /* Handle upstream entry del from the peer MLAG switch -
293 * - peer reference is removed. this can result in the upstream
294 * being deleted altogether.
295 * - if a local entry continues to exisy and has a MLAG OIF DF election
296 * is re-run (at the end of which the local entry will be the DF).
298 static struct pim_upstream
*pim_mlag_up_peer_deref(struct pim_instance
*pim
,
299 struct pim_upstream
*up
)
301 if (!PIM_UPSTREAM_FLAG_TEST_MLAG_PEER(up
->flags
))
304 PIM_UPSTREAM_FLAG_UNSET_MLAG_PEER(up
->flags
);
305 up
= pim_upstream_del(pim
, up
, __func__
);
307 pim_mlag_up_df_role_elect(pim
, up
);
312 static void pim_mlag_up_peer_del(struct mlag_mroute_del
*msg
)
314 struct pim_upstream
*up
;
315 struct pim_instance
*pim
;
319 memset(&sg
, 0, sizeof(sg
));
320 sg
.src
.s_addr
= htonl(msg
->source_ip
);
321 sg
.grp
.s_addr
= htonl(msg
->group_ip
);
324 zlog_debug("peer MLAG mroute del %s:%pSG", msg
->vrf_name
, &sg
);
326 vrf
= vrf_lookup_by_name(msg
->vrf_name
);
330 "peer MLAG mroute del skipped %s:%pSG; no vrf",
336 up
= pim_upstream_find(pim
, &sg
);
340 "peer MLAG mroute del skipped %s:%pSG; no up",
345 (void)pim_mlag_up_peer_deref(pim
, up
);
348 /* When we lose connection to the local MLAG daemon we can drop all peer
351 static void pim_mlag_up_peer_del_all(void)
353 struct list
*temp
= list_new();
354 struct pim_upstream
*up
;
356 struct pim_instance
*pim
;
359 * So why these gyrations?
360 * pim->upstream_head has the list of *,G and S,G
361 * that are in the system. The problem of course
362 * is that it is an ordered list:
363 * (*,G1) -> (S1,G1) -> (S2,G2) -> (S3, G2) -> (*,G2) -> (S1,G2)
364 * And the *,G1 has pointers to S1,G1 and S2,G1
365 * if we delete *,G1 then we have a situation where
366 * S1,G1 and S2,G2 can be deleted as well. Then a
367 * simple ALL_LIST_ELEMENTS will have the next listnode
368 * pointer become invalid and we crash.
369 * So let's grab the list of MLAG_PEER upstreams
370 * add a refcount put on another list and delete safely
372 RB_FOREACH(vrf
, vrf_name_head
, &vrfs_by_name
) {
374 frr_each (rb_pim_upstream
, &pim
->upstream_head
, up
) {
375 if (!PIM_UPSTREAM_FLAG_TEST_MLAG_PEER(up
->flags
))
377 listnode_add(temp
, up
);
379 * Add a reference since we are adding to this
385 while (temp
->count
) {
386 up
= listnode_head(temp
);
387 listnode_delete(temp
, up
);
389 up
= pim_mlag_up_peer_deref(pim
, up
);
391 * This is the deletion of the reference added
395 pim_upstream_del(pim
, up
, __func__
);
402 /* Send upstream entry to the local MLAG daemon (which will subsequently
403 * send it to the peer MLAG switch).
405 static void pim_mlag_up_local_add_send(struct pim_instance
*pim
,
406 struct pim_upstream
*up
)
408 struct stream
*s
= NULL
;
409 struct vrf
*vrf
= pim
->vrf
;
411 if (!(router
->mlag_flags
& PIM_MLAGF_LOCAL_CONN_UP
))
414 s
= stream_new(sizeof(struct mlag_mroute_add
) + PIM_MLAG_METADATA_LEN
);
419 zlog_debug("local MLAG mroute add %s:%s",
420 vrf
->name
, up
->sg_str
);
422 ++router
->mlag_stats
.msg
.mroute_add_tx
;
424 stream_putl(s
, MLAG_MROUTE_ADD
);
425 stream_put(s
, vrf
->name
, VRF_NAMSIZ
);
426 stream_putl(s
, ntohl(up
->sg
.src
.s_addr
));
427 stream_putl(s
, ntohl(up
->sg
.grp
.s_addr
));
429 stream_putl(s
, pim_up_mlag_local_cost(up
));
430 /* XXX - who is addding*/
431 stream_putl(s
, MLAG_OWNER_VXLAN
);
432 /* XXX - am_i_DR field should be removed */
433 stream_putc(s
, false);
434 stream_putc(s
, !(PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(up
->flags
)));
435 stream_putl(s
, vrf
->vrf_id
);
436 /* XXX - this field is a No-op for VXLAN*/
437 stream_put(s
, NULL
, INTERFACE_NAMSIZ
);
439 stream_fifo_push_safe(router
->mlag_fifo
, s
);
440 pim_mlag_signal_zpthread();
443 static void pim_mlag_up_local_del_send(struct pim_instance
*pim
,
444 struct pim_upstream
*up
)
446 struct stream
*s
= NULL
;
447 struct vrf
*vrf
= pim
->vrf
;
449 if (!(router
->mlag_flags
& PIM_MLAGF_LOCAL_CONN_UP
))
452 s
= stream_new(sizeof(struct mlag_mroute_del
) + PIM_MLAG_METADATA_LEN
);
457 zlog_debug("local MLAG mroute del %s:%s",
458 vrf
->name
, up
->sg_str
);
460 ++router
->mlag_stats
.msg
.mroute_del_tx
;
462 stream_putl(s
, MLAG_MROUTE_DEL
);
463 stream_put(s
, vrf
->name
, VRF_NAMSIZ
);
464 stream_putl(s
, ntohl(up
->sg
.src
.s_addr
));
465 stream_putl(s
, ntohl(up
->sg
.grp
.s_addr
));
466 /* XXX - who is adding */
467 stream_putl(s
, MLAG_OWNER_VXLAN
);
468 stream_putl(s
, vrf
->vrf_id
);
469 /* XXX - this field is a No-op for VXLAN */
470 stream_put(s
, NULL
, INTERFACE_NAMSIZ
);
472 /* XXX - is this the the most optimal way to do things */
473 stream_fifo_push_safe(router
->mlag_fifo
, s
);
474 pim_mlag_signal_zpthread();
478 /* Called when a local upstream entry is created or if it's cost changes */
479 void pim_mlag_up_local_add(struct pim_instance
*pim
,
480 struct pim_upstream
*up
)
482 pim_mlag_up_df_role_elect(pim
, up
);
483 /* XXX - need to add some dup checks here */
484 pim_mlag_up_local_add_send(pim
, up
);
487 /* Called when local MLAG reference is removed from an upstream entry */
488 void pim_mlag_up_local_del(struct pim_instance
*pim
,
489 struct pim_upstream
*up
)
491 pim_mlag_up_df_role_elect(pim
, up
);
492 pim_mlag_up_local_del_send(pim
, up
);
495 /* When connection to local MLAG daemon is established all the local
496 * MLAG upstream entries are replayed to it.
498 static void pim_mlag_up_local_replay(void)
500 struct pim_upstream
*up
;
502 struct pim_instance
*pim
;
504 RB_FOREACH(vrf
, vrf_name_head
, &vrfs_by_name
) {
506 frr_each (rb_pim_upstream
, &pim
->upstream_head
, up
) {
507 if (pim_up_mlag_is_local(up
))
508 pim_mlag_up_local_add_send(pim
, up
);
513 /* on local/peer mlag connection and role changes the DF status needs
516 static void pim_mlag_up_local_reeval(bool mlagd_send
, const char *reason_code
)
518 struct pim_upstream
*up
;
520 struct pim_instance
*pim
;
523 zlog_debug("%s re-run DF election because of %s",
524 __func__
, reason_code
);
525 RB_FOREACH(vrf
, vrf_name_head
, &vrfs_by_name
) {
527 frr_each (rb_pim_upstream
, &pim
->upstream_head
, up
) {
528 if (!pim_up_mlag_is_local(up
))
530 /* if role changes re-send to peer */
531 if (pim_mlag_up_df_role_elect(pim
, up
) &&
533 pim_mlag_up_local_add_send(pim
, up
);
538 /*****************PIM Actions for MLAG state changes**********************/
540 /* notify the anycast VTEP component about state changes */
541 static inline void pim_mlag_vxlan_state_update(void)
543 bool enable
= !!(router
->mlag_flags
& PIM_MLAGF_STATUS_RXED
);
544 bool peer_state
= !!(router
->mlag_flags
& PIM_MLAGF_PEER_CONN_UP
);
546 pim_vxlan_mlag_update(enable
, peer_state
, router
->mlag_role
,
547 router
->peerlink_rif_p
, &router
->local_vtep_ip
);
551 /**************End of PIM Actions for MLAG State changes******************/
554 /********************API to process PIM MLAG Data ************************/
556 static void pim_mlag_process_mlagd_state_change(struct mlag_status msg
)
558 bool role_chg
= false;
559 bool state_chg
= false;
560 bool notify_vxlan
= false;
561 struct interface
*peerlink_rif_p
;
562 char buf
[MLAG_ROLE_STRSIZE
];
565 zlog_debug("%s: msg dump: my_role: %s, peer_state: %s",
567 mlag_role2str(msg
.my_role
, buf
, sizeof(buf
)),
568 (msg
.peer_state
== MLAG_STATE_RUNNING
? "RUNNING"
571 if (!(router
->mlag_flags
& PIM_MLAGF_LOCAL_CONN_UP
)) {
573 zlog_debug("%s: msg ignored mlagd process state down",
577 ++router
->mlag_stats
.msg
.mlag_status_updates
;
579 /* evaluate the changes first */
580 if (router
->mlag_role
!= msg
.my_role
) {
583 router
->mlag_role
= msg
.my_role
;
586 strlcpy(router
->peerlink_rif
, msg
.peerlink_rif
,
587 sizeof(router
->peerlink_rif
));
589 /* XXX - handle the case where we may rx the interface name from the
590 * MLAG daemon before we get the interface from zebra.
592 peerlink_rif_p
= if_lookup_by_name(router
->peerlink_rif
, VRF_DEFAULT
);
593 if (router
->peerlink_rif_p
!= peerlink_rif_p
) {
594 router
->peerlink_rif_p
= peerlink_rif_p
;
598 if (msg
.peer_state
== MLAG_STATE_RUNNING
) {
599 if (!(router
->mlag_flags
& PIM_MLAGF_PEER_CONN_UP
)) {
602 router
->mlag_flags
|= PIM_MLAGF_PEER_CONN_UP
;
604 router
->connected_to_mlag
= true;
606 if (router
->mlag_flags
& PIM_MLAGF_PEER_CONN_UP
) {
607 ++router
->mlag_stats
.peer_session_downs
;
610 router
->mlag_flags
&= ~PIM_MLAGF_PEER_CONN_UP
;
612 router
->connected_to_mlag
= false;
615 /* apply the changes */
616 /* when connection to mlagd comes up we hold send mroutes till we have
617 * rxed the status and had a chance to re-valuate DF state
619 if (!(router
->mlag_flags
& PIM_MLAGF_STATUS_RXED
)) {
620 router
->mlag_flags
|= PIM_MLAGF_STATUS_RXED
;
621 pim_mlag_vxlan_state_update();
622 /* on session up re-eval DF status */
623 pim_mlag_up_local_reeval(false /*mlagd_send*/, "mlagd_up");
624 /* replay all the upstream entries to the local MLAG daemon */
625 pim_mlag_up_local_replay();
630 pim_mlag_vxlan_state_update();
633 if (!(router
->mlag_flags
& PIM_MLAGF_PEER_CONN_UP
))
634 /* when a connection goes down the primary takes over
635 * DF role for all entries
637 pim_mlag_up_local_reeval(true /*mlagd_send*/,
640 /* XXX - when session comes up we need to wait for
641 * PEER_REPLAY_DONE before running re-election on
642 * local-mlag entries that are missing peer reference
644 pim_mlag_up_local_reeval(true /*mlagd_send*/,
646 } else if (role_chg
) {
647 /* MLAG role changed without a state change */
648 pim_mlag_up_local_reeval(true /*mlagd_send*/, "role_chg");
652 static void pim_mlag_process_peer_frr_state_change(struct mlag_frr_status msg
)
656 "%s: msg dump: peer_frr_state: %s", __func__
,
657 (msg
.frr_state
== MLAG_FRR_STATE_UP
? "UP" : "DOWN"));
659 if (!(router
->mlag_flags
& PIM_MLAGF_LOCAL_CONN_UP
)) {
661 zlog_debug("%s: msg ignored mlagd process state down",
665 ++router
->mlag_stats
.msg
.peer_zebra_status_updates
;
667 /* evaluate the changes first */
668 if (msg
.frr_state
== MLAG_FRR_STATE_UP
) {
669 if (!(router
->mlag_flags
& PIM_MLAGF_PEER_ZEBRA_UP
)) {
670 router
->mlag_flags
|= PIM_MLAGF_PEER_ZEBRA_UP
;
671 /* XXX - when peer zebra comes up we need to wait for
672 * for some time to let the peer setup MDTs before
673 * before relinquishing DF status
675 pim_mlag_up_local_reeval(true /*mlagd_send*/,
679 if (router
->mlag_flags
& PIM_MLAGF_PEER_ZEBRA_UP
) {
680 ++router
->mlag_stats
.peer_zebra_downs
;
681 router
->mlag_flags
&= ~PIM_MLAGF_PEER_ZEBRA_UP
;
682 /* when a peer zebra goes down we assume DF role */
683 pim_mlag_up_local_reeval(true /*mlagd_send*/,
689 static void pim_mlag_process_vxlan_update(struct mlag_vxlan
*msg
)
691 char addr_buf1
[INET_ADDRSTRLEN
];
692 char addr_buf2
[INET_ADDRSTRLEN
];
695 if (!(router
->mlag_flags
& PIM_MLAGF_LOCAL_CONN_UP
)) {
697 zlog_debug("%s: msg ignored mlagd process state down",
702 ++router
->mlag_stats
.msg
.vxlan_updates
;
703 router
->anycast_vtep_ip
.s_addr
= htonl(msg
->anycast_ip
);
704 local_ip
= htonl(msg
->local_ip
);
705 if (router
->local_vtep_ip
.s_addr
!= local_ip
) {
706 router
->local_vtep_ip
.s_addr
= local_ip
;
707 pim_mlag_vxlan_state_update();
710 if (PIM_DEBUG_MLAG
) {
711 inet_ntop(AF_INET
, &router
->local_vtep_ip
,
712 addr_buf1
, INET_ADDRSTRLEN
);
713 inet_ntop(AF_INET
, &router
->anycast_vtep_ip
,
714 addr_buf2
, INET_ADDRSTRLEN
);
716 zlog_debug("%s: msg dump: local-ip:%s, anycast-ip:%s",
717 __func__
, addr_buf1
, addr_buf2
);
721 static void pim_mlag_process_mroute_add(struct mlag_mroute_add msg
)
723 if (PIM_DEBUG_MLAG
) {
726 sg
.grp
.s_addr
= ntohl(msg
.group_ip
);
727 sg
.src
.s_addr
= ntohl(msg
.source_ip
);
730 "%s: msg dump: vrf_name: %s, s.ip: 0x%x, g.ip: 0x%x (%pSG) cost: %u",
731 __func__
, msg
.vrf_name
, msg
.source_ip
, msg
.group_ip
,
732 &sg
, msg
.cost_to_rp
);
734 "(%pSG)owner_id: %d, DR: %d, Dual active: %d, vrf_id: 0x%x intf_name: %s",
735 &sg
, msg
.owner_id
, msg
.am_i_dr
, msg
.am_i_dual_active
,
736 msg
.vrf_id
, msg
.intf_name
);
739 if (!(router
->mlag_flags
& PIM_MLAGF_LOCAL_CONN_UP
)) {
741 zlog_debug("%s: msg ignored mlagd process state down",
746 ++router
->mlag_stats
.msg
.mroute_add_rx
;
748 pim_mlag_up_peer_add(&msg
);
751 static void pim_mlag_process_mroute_del(struct mlag_mroute_del msg
)
753 if (PIM_DEBUG_MLAG
) {
756 sg
.grp
.s_addr
= ntohl(msg
.group_ip
);
757 sg
.src
.s_addr
= ntohl(msg
.source_ip
);
759 "%s: msg dump: vrf_name: %s, s.ip: 0x%x, g.ip: 0x%x(%pSG)",
760 __func__
, msg
.vrf_name
, msg
.source_ip
, msg
.group_ip
,
762 zlog_debug("(%pSG)owner_id: %d, vrf_id: 0x%x intf_name: %s",
763 &sg
, msg
.owner_id
, msg
.vrf_id
, msg
.intf_name
);
766 if (!(router
->mlag_flags
& PIM_MLAGF_LOCAL_CONN_UP
)) {
768 zlog_debug("%s: msg ignored mlagd process state down",
773 ++router
->mlag_stats
.msg
.mroute_del_rx
;
775 pim_mlag_up_peer_del(&msg
);
778 int pim_zebra_mlag_handle_msg(int cmd
, struct zclient
*zclient
,
779 uint16_t zapi_length
, vrf_id_t vrf_id
)
781 struct stream
*s
= zclient
->ibuf
;
782 struct mlag_msg mlag_msg
;
787 rc
= mlag_lib_decode_mlag_hdr(s
, &mlag_msg
, &length
);
792 zlog_debug("%s: Received msg type: %s length: %d, bulk_cnt: %d",
794 mlag_lib_msgid_to_str(mlag_msg
.msg_type
, buf
,
796 mlag_msg
.data_len
, mlag_msg
.msg_cnt
);
798 switch (mlag_msg
.msg_type
) {
799 case MLAG_STATUS_UPDATE
: {
800 struct mlag_status msg
;
802 rc
= mlag_lib_decode_mlag_status(s
, &msg
);
805 pim_mlag_process_mlagd_state_change(msg
);
807 case MLAG_PEER_FRR_STATUS
: {
808 struct mlag_frr_status msg
;
810 rc
= mlag_lib_decode_frr_status(s
, &msg
);
813 pim_mlag_process_peer_frr_state_change(msg
);
815 case MLAG_VXLAN_UPDATE
: {
816 struct mlag_vxlan msg
;
818 rc
= mlag_lib_decode_vxlan_update(s
, &msg
);
821 pim_mlag_process_vxlan_update(&msg
);
823 case MLAG_MROUTE_ADD
: {
824 struct mlag_mroute_add msg
;
826 rc
= mlag_lib_decode_mroute_add(s
, &msg
, &length
);
829 pim_mlag_process_mroute_add(msg
);
831 case MLAG_MROUTE_DEL
: {
832 struct mlag_mroute_del msg
;
834 rc
= mlag_lib_decode_mroute_del(s
, &msg
, &length
);
837 pim_mlag_process_mroute_del(msg
);
839 case MLAG_MROUTE_ADD_BULK
: {
840 struct mlag_mroute_add msg
;
843 for (i
= 0; i
< mlag_msg
.msg_cnt
; i
++) {
844 rc
= mlag_lib_decode_mroute_add(s
, &msg
, &length
);
847 pim_mlag_process_mroute_add(msg
);
850 case MLAG_MROUTE_DEL_BULK
: {
851 struct mlag_mroute_del msg
;
854 for (i
= 0; i
< mlag_msg
.msg_cnt
; i
++) {
855 rc
= mlag_lib_decode_mroute_del(s
, &msg
, &length
);
858 pim_mlag_process_mroute_del(msg
);
863 case MLAG_DEREGISTER
:
865 case MLAG_PIM_CFG_DUMP
:
871 /****************End of PIM Mesasge processing handler********************/
873 int pim_zebra_mlag_process_up(ZAPI_CALLBACK_ARGS
)
876 zlog_debug("%s: Received Process-Up from Mlag", __func__
);
879 * Incase of local MLAG restart, PIM needs to replay all the data
880 * since MLAG is empty.
882 router
->connected_to_mlag
= true;
883 router
->mlag_flags
|= PIM_MLAGF_LOCAL_CONN_UP
;
887 static void pim_mlag_param_reset(void)
889 /* reset the cached params and stats */
890 router
->mlag_flags
&= ~(PIM_MLAGF_STATUS_RXED
|
891 PIM_MLAGF_LOCAL_CONN_UP
|
892 PIM_MLAGF_PEER_CONN_UP
|
893 PIM_MLAGF_PEER_ZEBRA_UP
);
894 router
->local_vtep_ip
.s_addr
= INADDR_ANY
;
895 router
->anycast_vtep_ip
.s_addr
= INADDR_ANY
;
896 router
->mlag_role
= MLAG_ROLE_NONE
;
897 memset(&router
->mlag_stats
.msg
, 0, sizeof(router
->mlag_stats
.msg
));
898 router
->peerlink_rif
[0] = '\0';
901 int pim_zebra_mlag_process_down(ZAPI_CALLBACK_ARGS
)
904 zlog_debug("%s: Received Process-Down from Mlag", __func__
);
906 /* Local CLAG is down, reset peer data and forward the traffic if
909 if (router
->mlag_flags
& PIM_MLAGF_PEER_CONN_UP
)
910 ++router
->mlag_stats
.peer_session_downs
;
911 if (router
->mlag_flags
& PIM_MLAGF_PEER_ZEBRA_UP
)
912 ++router
->mlag_stats
.peer_zebra_downs
;
913 router
->connected_to_mlag
= false;
914 pim_mlag_param_reset();
915 /* on mlagd session down re-eval DF status */
916 pim_mlag_up_local_reeval(false /*mlagd_send*/, "mlagd_down");
917 /* flush all peer references */
918 pim_mlag_up_peer_del_all();
919 /* notify the vxlan component */
920 pim_mlag_vxlan_state_update();
924 static void pim_mlag_register_handler(struct thread
*thread
)
926 uint32_t bit_mask
= 0;
931 SET_FLAG(bit_mask
, (1 << MLAG_STATUS_UPDATE
));
932 SET_FLAG(bit_mask
, (1 << MLAG_MROUTE_ADD
));
933 SET_FLAG(bit_mask
, (1 << MLAG_MROUTE_DEL
));
934 SET_FLAG(bit_mask
, (1 << MLAG_DUMP
));
935 SET_FLAG(bit_mask
, (1 << MLAG_MROUTE_ADD_BULK
));
936 SET_FLAG(bit_mask
, (1 << MLAG_MROUTE_DEL_BULK
));
937 SET_FLAG(bit_mask
, (1 << MLAG_PIM_CFG_DUMP
));
938 SET_FLAG(bit_mask
, (1 << MLAG_VXLAN_UPDATE
));
939 SET_FLAG(bit_mask
, (1 << MLAG_PEER_FRR_STATUS
));
942 zlog_debug("%s: Posting Client Register to MLAG mask: 0x%x",
945 zclient_send_mlag_register(zclient
, bit_mask
);
948 void pim_mlag_register(void)
950 if (router
->mlag_process_register
)
953 router
->mlag_process_register
= true;
955 thread_add_event(router
->master
, pim_mlag_register_handler
, NULL
, 0,
959 static void pim_mlag_deregister_handler(struct thread
*thread
)
965 zlog_debug("%s: Posting Client De-Register to MLAG from PIM",
967 router
->connected_to_mlag
= false;
968 zclient_send_mlag_deregister(zclient
);
971 void pim_mlag_deregister(void)
973 /* if somebody still interested in the MLAG channel skip de-reg */
974 if (router
->pim_mlag_intf_cnt
|| pim_vxlan_do_mlag_reg())
977 /* not registered; nothing do */
978 if (!router
->mlag_process_register
)
981 router
->mlag_process_register
= false;
983 thread_add_event(router
->master
, pim_mlag_deregister_handler
, NULL
, 0,
987 void pim_if_configure_mlag_dualactive(struct pim_interface
*pim_ifp
)
989 if (!pim_ifp
|| !pim_ifp
->pim
|| pim_ifp
->activeactive
== true)
993 zlog_debug("%s: Configuring active-active on Interface: %s",
996 pim_ifp
->activeactive
= true;
998 pim_ifp
->pim
->inst_mlag_intf_cnt
++;
1000 router
->pim_mlag_intf_cnt
++;
1003 "%s: Total MLAG configured Interfaces on router: %d, Inst: %d",
1004 __func__
, router
->pim_mlag_intf_cnt
,
1005 pim_ifp
->pim
->inst_mlag_intf_cnt
);
1007 if (router
->pim_mlag_intf_cnt
== 1) {
1009 * at least one Interface is configured for MLAG, send register
1010 * to Zebra for receiving MLAG Updates
1012 pim_mlag_register();
1016 void pim_if_unconfigure_mlag_dualactive(struct pim_interface
*pim_ifp
)
1018 if (!pim_ifp
|| !pim_ifp
->pim
|| pim_ifp
->activeactive
== false)
1022 zlog_debug("%s: UnConfiguring active-active on Interface: %s",
1025 pim_ifp
->activeactive
= false;
1026 pim_ifp
->pim
->inst_mlag_intf_cnt
--;
1028 router
->pim_mlag_intf_cnt
--;
1031 "%s: Total MLAG configured Interfaces on router: %d, Inst: %d",
1032 __func__
, router
->pim_mlag_intf_cnt
,
1033 pim_ifp
->pim
->inst_mlag_intf_cnt
);
1035 if (router
->pim_mlag_intf_cnt
== 0) {
1037 * all the Interfaces are MLAG un-configured, post MLAG
1038 * De-register to Zebra
1040 pim_mlag_deregister();
1041 pim_mlag_param_reset();
1046 void pim_instance_mlag_init(struct pim_instance
*pim
)
1051 pim
->inst_mlag_intf_cnt
= 0;
1055 void pim_instance_mlag_terminate(struct pim_instance
*pim
)
1057 struct interface
*ifp
;
1062 FOR_ALL_INTERFACES (pim
->vrf
, ifp
) {
1063 struct pim_interface
*pim_ifp
= ifp
->info
;
1065 if (!pim_ifp
|| pim_ifp
->activeactive
== false)
1068 pim_if_unconfigure_mlag_dualactive(pim_ifp
);
1070 pim
->inst_mlag_intf_cnt
= 0;
1073 void pim_mlag_terminate(void)
1075 stream_free(router
->mlag_stream
);
1076 router
->mlag_stream
= NULL
;
1077 stream_fifo_free(router
->mlag_fifo
);
1078 router
->mlag_fifo
= NULL
;
1081 void pim_mlag_init(void)
1083 pim_mlag_param_reset();
1084 router
->pim_mlag_intf_cnt
= 0;
1085 router
->connected_to_mlag
= false;
1086 router
->mlag_fifo
= stream_fifo_new();
1087 router
->zpthread_mlag_write
= NULL
;
1088 router
->mlag_stream
= stream_new(MLAG_BUF_LIMIT
);