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