2 * This is an implementation of PIM MLAG Functionality
4 * Module name: PIM MLAG
6 * Author: sathesh Kumar karra <sathk@cumulusnetworks.com>
8 * Copyright (C) 2019 Cumulus Networks http://www.cumulusnetworks.com
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)
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
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
28 #include "pim_upstream.h"
29 #include "pim_vxlan.h"
31 extern struct zclient
*zclient
;
33 #define PIM_MLAG_METADATA_LEN 4
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
)
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
;
44 if (is_df
== old_is_df
) {
47 "%s: Ignoring Role update for %s, since no change",
48 __func__
, up
->sg_str
);
53 zlog_debug("local MLAG mroute %s role changed to %s based on %s",
54 up
->sg_str
, is_df
? "df" : "non-df", reason
);
57 PIM_UPSTREAM_FLAG_UNSET_MLAG_NON_DF(up
->flags
);
59 PIM_UPSTREAM_FLAG_SET_MLAG_NON_DF(up
->flags
);
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.
70 vxlan_ifp
= pim_vxlan_get_term_ifp(pim
);
72 pim_channel_update_oif_mute(c_oil
, vxlan_ifp
);
75 /* If DF role changed on a (*,G) termination mroute update the
76 * associated DF role on the inherited (S,G) entries
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 */);
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
)
94 if (!pim_up_mlag_is_local(up
))
97 /* We are yet to rx a status update from the local MLAG daemon so
98 * we will assume DF status.
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");
104 /* If not connected to peer assume DF role on the MLAG primary
105 * switch (and non-DF on the secondary switch.
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
,
113 /* If MLAG peer session is up but zebra is down on the peer
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");
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.
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");
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).
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");
139 is_df
= (local_cost
< peer_cost
) ? true : false;
140 rv
= pim_mlag_up_df_role_update(pim
, up
, is_df
, "cost");
146 /* Handle upstream entry add from the peer MLAG switch -
147 * - if a local entry doesn't exist one is created with reference
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.
152 static void pim_mlag_up_peer_add(struct mlag_mroute_add
*msg
)
154 struct pim_upstream
*up
;
155 struct pim_instance
*pim
;
159 char sg_str
[PIM_SG_LEN
];
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
);
165 pim_str_sg_set(&sg
, sg_str
);
168 zlog_debug("peer MLAG mroute add %s:%s cost %d",
169 msg
->vrf_name
, sg_str
, msg
->cost_to_rp
);
171 /* XXX - this is not correct. we MUST cache updates to avoid losing
172 * an entry because of race conditions with the peer switch.
174 vrf
= vrf_lookup_by_name(msg
->vrf_name
);
177 zlog_debug("peer MLAG mroute add failed %s:%s; no vrf",
178 msg
->vrf_name
, sg_str
);
183 up
= pim_upstream_find(pim
, &sg
);
185 /* upstream already exists; create peer reference if it
186 * doesn't already exist.
188 if (!PIM_UPSTREAM_FLAG_TEST_MLAG_PEER(up
->flags
))
189 pim_upstream_ref(up
, PIM_UPSTREAM_FLAG_MASK_MLAG_PEER
,
192 PIM_UPSTREAM_FLAG_SET_MLAG_PEER(flags
);
193 up
= pim_upstream_add(pim
, &sg
, NULL
/*iif*/, flags
, __func__
,
198 zlog_debug("peer MLAG mroute add failed %s:%s",
203 up
->mlag
.peer_mrib_metric
= msg
->cost_to_rp
;
204 pim_mlag_up_df_role_elect(pim
, up
);
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).
213 static void pim_mlag_up_peer_deref(struct pim_instance
*pim
,
214 struct pim_upstream
*up
)
216 if (!PIM_UPSTREAM_FLAG_TEST_MLAG_PEER(up
->flags
))
219 PIM_UPSTREAM_FLAG_UNSET_MLAG_PEER(up
->flags
);
220 up
= pim_upstream_del(pim
, up
, __func__
);
222 pim_mlag_up_df_role_elect(pim
, up
);
224 static void pim_mlag_up_peer_del(struct mlag_mroute_del
*msg
)
226 struct pim_upstream
*up
;
227 struct pim_instance
*pim
;
230 char sg_str
[PIM_SG_LEN
];
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
);
236 pim_str_sg_set(&sg
, sg_str
);
239 zlog_debug("peer MLAG mroute del %s:%s", msg
->vrf_name
,
242 vrf
= vrf_lookup_by_name(msg
->vrf_name
);
245 zlog_debug("peer MLAG mroute del skipped %s:%s; no vrf",
246 msg
->vrf_name
, sg_str
);
251 up
= pim_upstream_find(pim
, &sg
);
254 zlog_debug("peer MLAG mroute del skipped %s:%s; no up",
259 pim_mlag_up_peer_deref(pim
, up
);
262 /* When we lose connection to the local MLAG daemon we can drop all peer
265 static void pim_mlag_up_peer_del_all(void)
267 struct list
*temp
= list_new();
268 struct pim_upstream
*up
;
270 struct pim_instance
*pim
;
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
286 RB_FOREACH(vrf
, vrf_name_head
, &vrfs_by_name
) {
288 frr_each (rb_pim_upstream
, &pim
->upstream_head
, up
) {
289 if (!PIM_UPSTREAM_FLAG_TEST_MLAG_PEER(up
->flags
))
291 listnode_add(temp
, up
);
293 * Add a reference since we are adding to this
299 while (temp
->count
) {
300 up
= listnode_head(temp
);
301 listnode_delete(temp
, up
);
303 pim_mlag_up_peer_deref(pim
, up
);
305 * This is the deletion of the reference added
308 pim_upstream_del(pim
, up
, __func__
);
315 /* Send upstream entry to the local MLAG daemon (which will subsequently
316 * send it to the peer MLAG switch).
318 static void pim_mlag_up_local_add_send(struct pim_instance
*pim
,
319 struct pim_upstream
*up
)
321 struct stream
*s
= NULL
;
322 struct vrf
*vrf
= pim
->vrf
;
324 if (!(router
->mlag_flags
& PIM_MLAGF_LOCAL_CONN_UP
))
327 s
= stream_new(sizeof(struct mlag_mroute_add
) + PIM_MLAG_METADATA_LEN
);
332 zlog_debug("local MLAG mroute add %s:%s",
333 vrf
->name
, up
->sg_str
);
335 ++router
->mlag_stats
.msg
.mroute_add_tx
;
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
));
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
);
352 stream_fifo_push_safe(router
->mlag_fifo
, s
);
353 pim_mlag_signal_zpthread();
356 static void pim_mlag_up_local_del_send(struct pim_instance
*pim
,
357 struct pim_upstream
*up
)
359 struct stream
*s
= NULL
;
360 struct vrf
*vrf
= pim
->vrf
;
362 if (!(router
->mlag_flags
& PIM_MLAGF_LOCAL_CONN_UP
))
365 s
= stream_new(sizeof(struct mlag_mroute_del
) + PIM_MLAG_METADATA_LEN
);
370 zlog_debug("local MLAG mroute del %s:%s",
371 vrf
->name
, up
->sg_str
);
373 ++router
->mlag_stats
.msg
.mroute_del_tx
;
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
);
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();
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
)
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
);
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
)
404 pim_mlag_up_df_role_elect(pim
, up
);
405 pim_mlag_up_local_del_send(pim
, up
);
408 /* When connection to local MLAG daemon is established all the local
409 * MLAG upstream entries are replayed to it.
411 static void pim_mlag_up_local_replay(void)
413 struct pim_upstream
*up
;
415 struct pim_instance
*pim
;
417 RB_FOREACH(vrf
, vrf_name_head
, &vrfs_by_name
) {
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
);
426 /* on local/peer mlag connection and role changes the DF status needs
429 static void pim_mlag_up_local_reeval(bool mlagd_send
, const char *reason_code
)
431 struct pim_upstream
*up
;
433 struct pim_instance
*pim
;
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
) {
440 frr_each (rb_pim_upstream
, &pim
->upstream_head
, up
) {
441 if (!pim_up_mlag_is_local(up
))
443 /* if role changes re-send to peer */
444 if (pim_mlag_up_df_role_elect(pim
, up
) &&
446 pim_mlag_up_local_add_send(pim
, up
);
451 /*****************PIM Actions for MLAG state changes**********************/
453 /* notify the anycast VTEP component about state changes */
454 static inline void pim_mlag_vxlan_state_update(void)
456 bool enable
= !!(router
->mlag_flags
& PIM_MLAGF_STATUS_RXED
);
457 bool peer_state
= !!(router
->mlag_flags
& PIM_MLAGF_PEER_CONN_UP
);
459 pim_vxlan_mlag_update(enable
, peer_state
, router
->mlag_role
,
460 router
->peerlink_rif_p
, &router
->local_vtep_ip
);
464 /**************End of PIM Actions for MLAG State changes******************/
467 /********************API to process PIM MLAG Data ************************/
469 static void pim_mlag_process_mlagd_state_change(struct mlag_status msg
)
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
];
478 zlog_debug("%s: msg dump: my_role: %s, peer_state: %s",
480 mlag_role2str(msg
.my_role
, buf
, sizeof(buf
)),
481 (msg
.peer_state
== MLAG_STATE_RUNNING
? "RUNNING"
484 if (!(router
->mlag_flags
& PIM_MLAGF_LOCAL_CONN_UP
)) {
486 zlog_debug("%s: msg ignored mlagd process state down",
490 ++router
->mlag_stats
.msg
.mlag_status_updates
;
492 /* evaluate the changes first */
493 if (router
->mlag_role
!= msg
.my_role
) {
496 router
->mlag_role
= msg
.my_role
;
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.
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
;
509 if (msg
.peer_state
== MLAG_STATE_RUNNING
) {
510 if (!(router
->mlag_flags
& PIM_MLAGF_PEER_CONN_UP
)) {
513 router
->mlag_flags
|= PIM_MLAGF_PEER_CONN_UP
;
515 router
->connected_to_mlag
= true;
517 if (router
->mlag_flags
& PIM_MLAGF_PEER_CONN_UP
) {
518 ++router
->mlag_stats
.peer_session_downs
;
521 router
->mlag_flags
&= ~PIM_MLAGF_PEER_CONN_UP
;
523 router
->connected_to_mlag
= false;
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
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();
541 pim_mlag_vxlan_state_update();
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
548 pim_mlag_up_local_reeval(true /*mlagd_send*/,
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
555 pim_mlag_up_local_reeval(true /*mlagd_send*/,
557 } else if (role_chg
) {
558 /* MLAG role changed without a state change */
559 pim_mlag_up_local_reeval(true /*mlagd_send*/, "role_chg");
563 static void pim_mlag_process_peer_frr_state_change(struct mlag_frr_status msg
)
567 "%s: msg dump: peer_frr_state: %s", __func__
,
568 (msg
.frr_state
== MLAG_FRR_STATE_UP
? "UP" : "DOWN"));
570 if (!(router
->mlag_flags
& PIM_MLAGF_LOCAL_CONN_UP
)) {
572 zlog_debug("%s: msg ignored mlagd process state down",
576 ++router
->mlag_stats
.msg
.peer_zebra_status_updates
;
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
586 pim_mlag_up_local_reeval(true /*mlagd_send*/,
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*/,
600 static void pim_mlag_process_vxlan_update(struct mlag_vxlan
*msg
)
602 char addr_buf1
[INET_ADDRSTRLEN
];
603 char addr_buf2
[INET_ADDRSTRLEN
];
606 if (!(router
->mlag_flags
& PIM_MLAGF_LOCAL_CONN_UP
)) {
608 zlog_debug("%s: msg ignored mlagd process state down",
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();
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
);
627 zlog_debug("%s: msg dump: local-ip:%s, anycast-ip:%s",
628 __func__
, addr_buf1
, addr_buf2
);
632 static void pim_mlag_process_mroute_add(struct mlag_mroute_add msg
)
634 if (PIM_DEBUG_MLAG
) {
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
);
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
);
645 if (!(router
->mlag_flags
& PIM_MLAGF_LOCAL_CONN_UP
)) {
647 zlog_debug("%s: msg ignored mlagd process state down",
652 ++router
->mlag_stats
.msg
.mroute_add_rx
;
654 pim_mlag_up_peer_add(&msg
);
657 static void pim_mlag_process_mroute_del(struct mlag_mroute_del msg
)
659 if (PIM_DEBUG_MLAG
) {
661 "%s: msg dump: vrf_name: %s, s.ip: 0x%x, g.ip: 0x%x ",
662 __func__
, msg
.vrf_name
, msg
.source_ip
,
664 zlog_debug("owner_id: %d, vrf_id: 0x%x intf_name: %s",
665 msg
.owner_id
, msg
.vrf_id
, msg
.intf_name
);
668 if (!(router
->mlag_flags
& PIM_MLAGF_LOCAL_CONN_UP
)) {
670 zlog_debug("%s: msg ignored mlagd process state down",
675 ++router
->mlag_stats
.msg
.mroute_del_rx
;
677 pim_mlag_up_peer_del(&msg
);
680 int pim_zebra_mlag_handle_msg(struct stream
*s
, int len
)
682 struct mlag_msg mlag_msg
;
683 char buf
[ZLOG_FILTER_LENGTH_MAX
];
686 rc
= mlag_lib_decode_mlag_hdr(s
, &mlag_msg
);
691 zlog_debug("%s: Received msg type: %s length: %d, bulk_cnt: %d",
693 mlag_lib_msgid_to_str(mlag_msg
.msg_type
, buf
,
695 mlag_msg
.data_len
, mlag_msg
.msg_cnt
);
697 switch (mlag_msg
.msg_type
) {
698 case MLAG_STATUS_UPDATE
: {
699 struct mlag_status msg
;
701 rc
= mlag_lib_decode_mlag_status(s
, &msg
);
704 pim_mlag_process_mlagd_state_change(msg
);
706 case MLAG_PEER_FRR_STATUS
: {
707 struct mlag_frr_status msg
;
709 rc
= mlag_lib_decode_frr_status(s
, &msg
);
712 pim_mlag_process_peer_frr_state_change(msg
);
714 case MLAG_VXLAN_UPDATE
: {
715 struct mlag_vxlan msg
;
717 rc
= mlag_lib_decode_vxlan_update(s
, &msg
);
720 pim_mlag_process_vxlan_update(&msg
);
722 case MLAG_MROUTE_ADD
: {
723 struct mlag_mroute_add msg
;
725 rc
= mlag_lib_decode_mroute_add(s
, &msg
);
728 pim_mlag_process_mroute_add(msg
);
730 case MLAG_MROUTE_DEL
: {
731 struct mlag_mroute_del msg
;
733 rc
= mlag_lib_decode_mroute_del(s
, &msg
);
736 pim_mlag_process_mroute_del(msg
);
738 case MLAG_MROUTE_ADD_BULK
: {
739 struct mlag_mroute_add msg
;
742 for (i
= 0; i
< mlag_msg
.msg_cnt
; i
++) {
744 rc
= mlag_lib_decode_mroute_add(s
, &msg
);
747 pim_mlag_process_mroute_add(msg
);
750 case MLAG_MROUTE_DEL_BULK
: {
751 struct mlag_mroute_del msg
;
754 for (i
= 0; i
< mlag_msg
.msg_cnt
; i
++) {
756 rc
= mlag_lib_decode_mroute_del(s
, &msg
);
759 pim_mlag_process_mroute_del(msg
);
768 /****************End of PIM Mesasge processing handler********************/
770 int pim_zebra_mlag_process_up(void)
773 zlog_debug("%s: Received Process-Up from Mlag", __func__
);
778 static void pim_mlag_param_reset(void)
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';
792 int pim_zebra_mlag_process_down(void)
795 zlog_debug("%s: Received Process-Down from Mlag", __func__
);
797 /* Local CLAG is down, reset peer data and forward the traffic if
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();
815 static int pim_mlag_register_handler(struct thread
*thread
)
817 uint32_t bit_mask
= 0;
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
));
833 zlog_debug("%s: Posting Client Register to MLAG mask: 0x%x",
836 zclient_send_mlag_register(zclient
, bit_mask
);
840 void pim_mlag_register(void)
842 if (router
->mlag_process_register
)
845 router
->mlag_process_register
= true;
847 thread_add_event(router
->master
, pim_mlag_register_handler
, NULL
, 0,
851 static int pim_mlag_deregister_handler(struct thread
*thread
)
857 zlog_debug("%s: Posting Client De-Register to MLAG from PIM",
859 router
->connected_to_mlag
= false;
860 zclient_send_mlag_deregister(zclient
);
864 void pim_mlag_deregister(void)
866 /* if somebody still interested in the MLAG channel skip de-reg */
867 if (router
->pim_mlag_intf_cnt
|| pim_vxlan_do_mlag_reg())
870 /* not registered; nothing do */
871 if (!router
->mlag_process_register
)
874 router
->mlag_process_register
= false;
876 thread_add_event(router
->master
, pim_mlag_deregister_handler
, NULL
, 0,
880 void pim_if_configure_mlag_dualactive(struct pim_interface
*pim_ifp
)
882 if (!pim_ifp
|| !pim_ifp
->pim
|| pim_ifp
->activeactive
== true)
885 pim_ifp
->activeactive
= true;
887 pim_ifp
->pim
->inst_mlag_intf_cnt
++;
889 router
->pim_mlag_intf_cnt
++;
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
);
896 if (router
->pim_mlag_intf_cnt
== 1) {
898 * atleast one Interface is configured for MLAG, send register
899 * to Zebra for receiving MLAG Updates
905 void pim_if_unconfigure_mlag_dualactive(struct pim_interface
*pim_ifp
)
907 if (!pim_ifp
|| !pim_ifp
->pim
|| pim_ifp
->activeactive
== false)
910 pim_ifp
->activeactive
= false;
911 pim_ifp
->pim
->inst_mlag_intf_cnt
--;
913 router
->pim_mlag_intf_cnt
--;
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
);
920 if (router
->pim_mlag_intf_cnt
== 0) {
922 * all the Interfaces are MLAG un-configured, post MLAG
923 * De-register to Zebra
925 pim_mlag_deregister();
926 pim_mlag_param_reset();
931 void pim_instance_mlag_init(struct pim_instance
*pim
)
936 pim
->inst_mlag_intf_cnt
= 0;
940 void pim_instance_mlag_terminate(struct pim_instance
*pim
)
942 struct interface
*ifp
;
947 FOR_ALL_INTERFACES (pim
->vrf
, ifp
) {
948 struct pim_interface
*pim_ifp
= ifp
->info
;
950 if (!pim_ifp
|| pim_ifp
->activeactive
== false)
953 pim_if_unconfigure_mlag_dualactive(pim_ifp
);
955 pim
->inst_mlag_intf_cnt
= 0;
958 void pim_mlag_init(void)
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
);