]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_msdp.c
Merge pull request #561 from donaldsharp/static_config2
[mirror_frr.git] / pimd / pim_msdp.c
CommitLineData
11128587 1/*
2a333e0f 2 * IP MSDP for Quagga
11128587 3 * Copyright (C) 2016 Cumulus Networks, Inc.
11128587
DS
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
896014f4
DL
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
11128587
DS
18 */
19
20#include <zebra.h>
21
2a333e0f 22#include <lib/hash.h>
23#include <lib/jhash.h>
24#include <lib/log.h>
3c72d654 25#include <lib/prefix.h>
2a333e0f 26#include <lib/sockunion.h>
27#include <lib/stream.h>
28#include <lib/thread.h>
3c72d654 29#include <lib/vty.h>
30#include <lib/plist.h>
11128587 31
2a333e0f 32#include "pimd.h"
33#include "pim_cmd.h"
34#include "pim_memory.h"
7176984f 35#include "pim_iface.h"
3c72d654 36#include "pim_rp.h"
2a333e0f 37#include "pim_str.h"
38#include "pim_time.h"
1bf16443 39#include "pim_upstream.h"
11128587 40
2a333e0f 41#include "pim_msdp.h"
42#include "pim_msdp_packet.h"
43#include "pim_msdp_socket.h"
44
45struct pim_msdp pim_msdp, *msdp = &pim_msdp;
46
47static void pim_msdp_peer_listen(struct pim_msdp_peer *mp);
48static void pim_msdp_peer_cr_timer_setup(struct pim_msdp_peer *mp, bool start);
49static void pim_msdp_peer_ka_timer_setup(struct pim_msdp_peer *mp, bool start);
50static void pim_msdp_peer_hold_timer_setup(struct pim_msdp_peer *mp, bool start);
51static void pim_msdp_peer_free(struct pim_msdp_peer *mp);
3c72d654 52static void pim_msdp_enable(void);
53static void pim_msdp_sa_adv_timer_setup(bool start);
54static void pim_msdp_sa_deref(struct pim_msdp_sa *sa, enum pim_msdp_sa_flags flags);
977d71cc 55static int pim_msdp_mg_mbr_comp(const void *p1, const void *p2);
56static void pim_msdp_mg_mbr_free(struct pim_msdp_mg_mbr *mbr);
57static void pim_msdp_mg_mbr_do_del(struct pim_msdp_mg *mg, struct pim_msdp_mg_mbr *mbr);
2a333e0f 58
3c72d654 59/************************ SA cache management ******************************/
3c72d654 60static void
61pim_msdp_sa_timer_expiry_log(struct pim_msdp_sa *sa, const char *timer_str)
62{
15ad0c71 63 zlog_debug("MSDP SA %s %s timer expired", sa->sg_str, timer_str);
3c72d654 64}
65
66/* RFC-3618:Sec-5.1 - global active source advertisement timer */
67static int
68pim_msdp_sa_adv_timer_cb(struct thread *t)
69{
7667c556 70 msdp->sa_adv_timer = NULL;
15ad0c71 71 if (PIM_DEBUG_MSDP_EVENTS) {
3c72d654 72 zlog_debug("MSDP SA advertisment timer expired");
73 }
74
3c72d654 75 pim_msdp_sa_adv_timer_setup(true /* start */);
69053fb4 76 pim_msdp_pkt_sa_tx();
3c72d654 77 return 0;
78}
79static void
80pim_msdp_sa_adv_timer_setup(bool start)
81{
82 THREAD_OFF(msdp->sa_adv_timer);
83 if (start) {
ffa2c898
QY
84 thread_add_timer(msdp->master, pim_msdp_sa_adv_timer_cb, NULL,
85 PIM_MSDP_SA_ADVERTISMENT_TIME, &msdp->sa_adv_timer);
3c72d654 86 }
87}
88
89/* RFC-3618:Sec-5.3 - SA cache state timer */
90static int
91pim_msdp_sa_state_timer_cb(struct thread *t)
92{
93 struct pim_msdp_sa *sa;
94
3c72d654 95 sa = THREAD_ARG(t);
7667c556 96 sa->sa_state_timer = NULL;
3c72d654 97
98 if (PIM_DEBUG_MSDP_EVENTS) {
99 pim_msdp_sa_timer_expiry_log(sa, "state");
100 }
101
102 pim_msdp_sa_deref(sa, PIM_MSDP_SAF_PEER);
103 return 0;
104}
105static void
106pim_msdp_sa_state_timer_setup(struct pim_msdp_sa *sa, bool start)
107{
108 THREAD_OFF(sa->sa_state_timer);
109 if (start) {
ffa2c898
QY
110 thread_add_timer(msdp->master, pim_msdp_sa_state_timer_cb, sa,
111 PIM_MSDP_SA_HOLD_TIME, &sa->sa_state_timer);
3c72d654 112 }
113}
114
7667c556 115static void
116pim_msdp_sa_upstream_del(struct pim_msdp_sa *sa)
117{
118 struct pim_upstream *up = sa->up;
119 if (!up) {
120 return;
121 }
122
123 sa->up = NULL;
7667c556 124 if (PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up->flags)) {
125 PIM_UPSTREAM_FLAG_UNSET_SRC_MSDP(up->flags);
36e466fe 126 sa->flags |= PIM_MSDP_SAF_UP_DEL_IN_PROG;
7667c556 127 pim_upstream_del(up, __PRETTY_FUNCTION__);
36e466fe 128 sa->flags &= ~PIM_MSDP_SAF_UP_DEL_IN_PROG;
7667c556 129 }
130
131 if (PIM_DEBUG_MSDP_EVENTS) {
15ad0c71 132 zlog_debug("MSDP SA %s de-referenced SPT", sa->sg_str);
7667c556 133 }
134}
135
136static bool
137pim_msdp_sa_upstream_add_ok(struct pim_msdp_sa *sa, struct pim_upstream *xg_up)
138{
7667c556 139 if (!(sa->flags & PIM_MSDP_SAF_PEER)) {
140 /* SA should have been rxed from a peer */
141 return false;
142 }
143 /* check if we are RP */
144 if (!I_am_RP(sa->sg.grp)) {
145 return false;
146 }
147
148 /* check if we have a (*, G) with a non-empty immediate OIL */
149 if (!xg_up) {
150 struct prefix_sg sg;
151
152 memset(&sg, 0, sizeof(sg));
153 sg.grp = sa->sg.grp;
154
155 xg_up = pim_upstream_find(&sg);
156 }
157 if (!xg_up || (xg_up->join_state != PIM_UPSTREAM_JOINED)) {
158 /* join desired will be true for such (*, G) entries so we will
159 * just look at join_state and let the PIM state machine do the rest of
160 * the magic */
161 return false;
162 }
163
164 return true;
165}
166
167/* Upstream add evaluation needs to happen everytime -
168 * 1. Peer reference is added or removed.
1bf16443 169 * 2. The RP for a group changes.
170 * 3. joinDesired for the associated (*, G) changes
171 * 4. associated (*, G) is removed - this seems like a bit redundant
7667c556 172 * (considering #4); but just in case an entry gets nuked without
173 * upstream state transition
174 * */
175static void
176pim_msdp_sa_upstream_update(struct pim_msdp_sa *sa,
177 struct pim_upstream *xg_up, const char *ctx)
178{
179 struct pim_upstream *up;
7667c556 180
181 if (!pim_msdp_sa_upstream_add_ok(sa, xg_up)) {
182 pim_msdp_sa_upstream_del(sa);
183 return;
184 }
185
186 if (sa->up) {
187 /* nothing to do */
188 return;
189 }
190
191 up = pim_upstream_find(&sa->sg);
192 if (up && (PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up->flags))) {
193 /* somehow we lost track of the upstream ptr? best log it */
194 sa->up = up;
195 if (PIM_DEBUG_MSDP_EVENTS) {
15ad0c71 196 zlog_debug("MSDP SA %s SPT reference missing", sa->sg_str);
7667c556 197 }
198 return;
199 }
200
201 /* RFC3618: "RP triggers a (S, G) join event towards the data source
202 * as if a JP message was rxed addressed to the RP itself." */
203 up = pim_upstream_add(&sa->sg, NULL /* iif */,
204 PIM_UPSTREAM_FLAG_MASK_SRC_MSDP,
205 __PRETTY_FUNCTION__);
206
207 sa->up = up;
208 if (up) {
209 /* update inherited oil */
210 pim_upstream_inherited_olist(up);
211 /* should we also start the kat in parallel? we will need it when the
212 * SA ages out */
213 if (PIM_DEBUG_MSDP_EVENTS) {
15ad0c71 214 zlog_debug("MSDP SA %s referenced SPT", sa->sg_str);
7667c556 215 }
216 } else {
217 if (PIM_DEBUG_MSDP_EVENTS) {
15ad0c71 218 zlog_debug("MSDP SA %s SPT reference failed", sa->sg_str);
7667c556 219 }
220 }
221}
222
3c72d654 223/* release all mem associated with a sa */
224static void
225pim_msdp_sa_free(struct pim_msdp_sa *sa)
226{
227 XFREE(MTYPE_PIM_MSDP_SA, sa);
228}
229
230static struct pim_msdp_sa *
231pim_msdp_sa_new(struct prefix_sg *sg, struct in_addr rp)
232{
233 struct pim_msdp_sa *sa;
234
3c72d654 235 sa = XCALLOC(MTYPE_PIM_MSDP_SA, sizeof(*sa));
236 if (!sa) {
237 zlog_err("%s: PIM XCALLOC(%zu) failure",
36e466fe 238 __PRETTY_FUNCTION__, sizeof(*sa));
3c72d654 239 return NULL;
240 }
241
242 sa->sg = *sg;
15ad0c71 243 pim_str_sg_set(sg, sa->sg_str);
3c72d654 244 sa->rp = rp;
245 sa->uptime = pim_time_monotonic_sec();
246
247 /* insert into misc tables for easy access */
248 sa = hash_get(msdp->sa_hash, sa, hash_alloc_intern);
249 if (!sa) {
250 zlog_err("%s: PIM hash get failure", __PRETTY_FUNCTION__);
251 pim_msdp_sa_free(sa);
252 return NULL;
253 }
254 listnode_add_sort(msdp->sa_list, sa);
255
256 if (PIM_DEBUG_MSDP_EVENTS) {
15ad0c71 257 zlog_debug("MSDP SA %s created", sa->sg_str);
3c72d654 258 }
259
260 return sa;
261}
262
263static struct pim_msdp_sa *
264pim_msdp_sa_find(struct prefix_sg *sg)
265{
266 struct pim_msdp_sa lookup;
267
268 lookup.sg = *sg;
269 return hash_lookup(msdp->sa_hash, &lookup);
270}
271
272static struct pim_msdp_sa *
273pim_msdp_sa_add(struct prefix_sg *sg, struct in_addr rp)
274{
275 struct pim_msdp_sa *sa;
276
277 sa = pim_msdp_sa_find(sg);
278 if (sa) {
279 return sa;
280 }
281
282 return pim_msdp_sa_new(sg, rp);
283}
284
285static void
286pim_msdp_sa_del(struct pim_msdp_sa * sa)
287{
7667c556 288 /* this is somewhat redundant - still want to be careful not to leave
289 * stale upstream references */
290 pim_msdp_sa_upstream_del(sa);
291
3c72d654 292 /* stop timers */
293 pim_msdp_sa_state_timer_setup(sa, false /* start */);
294
295 /* remove the entry from various tables */
296 listnode_delete(msdp->sa_list, sa);
297 hash_release(msdp->sa_hash, sa);
298
299 if (PIM_DEBUG_MSDP_EVENTS) {
15ad0c71 300 zlog_debug("MSDP SA %s deleted", sa->sg_str);
3c72d654 301 }
302
303 /* free up any associated memory */
304 pim_msdp_sa_free(sa);
305}
306
15ad0c71 307static void
11b7753a 308pim_msdp_sa_peer_ip_set(struct pim_msdp_sa *sa, struct pim_msdp_peer *mp, struct in_addr rp)
15ad0c71 309{
310 struct pim_msdp_peer *old_mp;
311
312 /* optimize the "no change" case as it will happen
313 * frequently/periodically */
314 if (mp && (sa->peer.s_addr == mp->peer.s_addr)) {
315 return;
316 }
317
11b7753a 318 /* any time the peer ip changes also update the rp address */
15ad0c71 319 if (PIM_INADDR_ISNOT_ANY(sa->peer)) {
320 old_mp = pim_msdp_peer_find(sa->peer);
321 if (old_mp && old_mp->sa_cnt) {
322 --old_mp->sa_cnt;
323 }
324 }
325
326 if (mp) {
327 ++mp->sa_cnt;
328 sa->peer = mp->peer;
329 } else {
330 sa->peer.s_addr = PIM_NET_INADDR_ANY;
331 }
11b7753a 332 sa->rp = rp;
15ad0c71 333}
334
3c72d654 335/* When a local active-source is removed there is no way to withdraw the
336 * source from peers. We will simply remove it from the SA cache so it will
337 * not be sent in supsequent SA updates. Peers will consequently timeout the
338 * SA.
339 * Similarly a "peer-added" SA is never explicitly deleted. It is simply
340 * aged out overtime if not seen in the SA updates from the peers.
341 * XXX: should we provide a knob to drop entries learnt from a peer when the
342 * peer goes down? */
343static void
344pim_msdp_sa_deref(struct pim_msdp_sa *sa, enum pim_msdp_sa_flags flags)
345{
1bf16443 346 bool update_up = false;
3c72d654 347
3c72d654 348 if ((sa->flags &PIM_MSDP_SAF_LOCAL)) {
349 if (flags & PIM_MSDP_SAF_LOCAL) {
1bf16443 350 if (PIM_DEBUG_MSDP_EVENTS) {
15ad0c71 351 zlog_debug("MSDP SA %s local reference removed", sa->sg_str);
1bf16443 352 }
3c72d654 353 if (msdp->local_cnt)
354 --msdp->local_cnt;
355 }
356 }
357
358 if ((sa->flags &PIM_MSDP_SAF_PEER)) {
359 if (flags & PIM_MSDP_SAF_PEER) {
11b7753a 360 struct in_addr rp;
361
1bf16443 362 if (PIM_DEBUG_MSDP_EVENTS) {
15ad0c71 363 zlog_debug("MSDP SA %s peer reference removed", sa->sg_str);
1bf16443 364 }
3c72d654 365 pim_msdp_sa_state_timer_setup(sa, false /* start */);
11b7753a 366 rp.s_addr = INADDR_ANY;
367 pim_msdp_sa_peer_ip_set(sa, NULL /* mp */, rp);
1bf16443 368 /* if peer ref was removed we need to remove the msdp reference on the
369 * msdp entry */
370 update_up = true;
3c72d654 371 }
372 }
373
374 sa->flags &= ~flags;
1bf16443 375 if (update_up) {
376 pim_msdp_sa_upstream_update(sa, NULL /* xg_up */, "sa-deref");
377 }
378
3c72d654 379 if (!(sa->flags & PIM_MSDP_SAF_REF)) {
380 pim_msdp_sa_del(sa);
381 }
382}
383
384void
385pim_msdp_sa_ref(struct pim_msdp_peer *mp, struct prefix_sg *sg,
386 struct in_addr rp)
387{
388 struct pim_msdp_sa *sa;
3c72d654 389
390 sa = pim_msdp_sa_add(sg, rp);
391 if (!sa) {
392 return;
393 }
394
3c72d654 395 /* reference it */
396 if (mp) {
397 if (!(sa->flags & PIM_MSDP_SAF_PEER)) {
398 sa->flags |= PIM_MSDP_SAF_PEER;
399 if (PIM_DEBUG_MSDP_EVENTS) {
15ad0c71 400 zlog_debug("MSDP SA %s added by peer", sa->sg_str);
3c72d654 401 }
402 }
11b7753a 403 pim_msdp_sa_peer_ip_set(sa, mp, rp);
3c72d654 404 /* start/re-start the state timer to prevent cache expiry */
405 pim_msdp_sa_state_timer_setup(sa, true /* start */);
7667c556 406 /* We re-evaluate SA "SPT-trigger" everytime we hear abt it from a
407 * peer. XXX: If this becomes too much of a periodic overhead we
408 * can make it event based */
409 pim_msdp_sa_upstream_update(sa, NULL /* xg_up */, "peer-ref");
3c72d654 410 } else {
411 if (!(sa->flags & PIM_MSDP_SAF_LOCAL)) {
412 sa->flags |= PIM_MSDP_SAF_LOCAL;
413 ++msdp->local_cnt;
414 if (PIM_DEBUG_MSDP_EVENTS) {
15ad0c71 415 zlog_debug("MSDP SA %s added locally", sa->sg_str);
3c72d654 416 }
7667c556 417 /* send an immediate SA update to peers */
3c72d654 418 pim_msdp_pkt_sa_tx_one(sa);
419 }
420 sa->flags &= ~PIM_MSDP_SAF_STALE;
421 }
422}
423
1bf16443 424/* The following criteria must be met to originate an SA from the MSDP
425 * speaker -
426 * 1. KAT must be running i.e. source is active.
427 * 2. We must be RP for the group.
428 * 3. Source must be registrable to the RP (this is where the RFC is vague
429 * and especially ambiguous in CLOS networks; with anycast RP all sources
430 * are potentially registrable to all RPs in the domain). We assume #3 is
431 * satisfied if -
432 * a. We are also the FHR-DR for the source (OR)
433 * b. We rxed a pim register (null or data encapsulated) within the last
434 * (3 * (1.5 * register_suppression_timer))).
435 */
436static bool
437pim_msdp_sa_local_add_ok(struct pim_upstream *up)
3c72d654 438{
3c72d654 439 if (!(msdp->flags & PIM_MSDPF_ENABLE)) {
1bf16443 440 return false;
3c72d654 441 }
442
1bf16443 443 if (!up->t_ka_timer) {
444 /* stream is not active */
445 return false;
446 }
447
448 if (!I_am_RP(up->sg.grp)) {
449 /* we are not RP for the group */
450 return false;
451 }
452
453 /* we are the FHR-DR for this stream or we are RP and have seen registers
454 * from a FHR for this source */
455 if (PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) || up->t_msdp_reg_timer) {
456 return true;
3c72d654 457 }
1bf16443 458
459 return false;
460}
461
462static void
463pim_msdp_sa_local_add(struct prefix_sg *sg)
464{
465 struct in_addr rp;
3c72d654 466 rp.s_addr = 0;
467 pim_msdp_sa_ref(NULL /* mp */, sg, rp);
468}
469
470void
471pim_msdp_sa_local_del(struct prefix_sg *sg)
472{
473 struct pim_msdp_sa *sa;
474
3c72d654 475 sa = pim_msdp_sa_find(sg);
476 if (sa) {
477 pim_msdp_sa_deref(sa, PIM_MSDP_SAF_LOCAL);
478 }
479}
480
36e466fe 481/* we need to be very cautious with this API as SA del too can trigger an
482 * upstream del and we will get stuck in a simple loop */
483static void
484pim_msdp_sa_local_del_on_up_del(struct prefix_sg *sg)
485{
486 struct pim_msdp_sa *sa;
487
488 sa = pim_msdp_sa_find(sg);
489 if (sa) {
490 if (PIM_DEBUG_MSDP_INTERNAL) {
491 zlog_debug("MSDP local sa %s del on up del", sa->sg_str);
492 }
493
494 /* if there is no local reference escape */
495 if (!(sa->flags & PIM_MSDP_SAF_LOCAL)) {
496 if (PIM_DEBUG_MSDP_INTERNAL) {
497 zlog_debug("MSDP local sa %s del; no local ref", sa->sg_str);
498 }
499 return;
500 }
501
502 if (sa->flags & PIM_MSDP_SAF_UP_DEL_IN_PROG) {
503 /* MSDP is the one that triggered the upstream del. if this happens
504 * we most certainly have a bug in the PIM upstream state machine. We
505 * will not have a local reference unless the KAT is running. And if the
506 * KAT is running there MUST be an additional source-stream reference to
507 * the flow. Accounting for such cases requires lot of changes; perhaps
508 * address this in the next release? - XXX */
509 zlog_err("MSDP sa %s SPT teardown is causing the local entry to be removed", sa->sg_str);
510 return;
511 }
512
513 /* we are dropping the sa on upstream del we should not have an
514 * upstream reference */
515 if (sa->up) {
516 if (PIM_DEBUG_MSDP_INTERNAL) {
517 zlog_debug("MSDP local sa %s del; up non-NULL", sa->sg_str);
518 }
519 sa->up = NULL;
520 }
521 pim_msdp_sa_deref(sa, PIM_MSDP_SAF_LOCAL);
522 }
523}
524
1bf16443 525/* Local SA qualification needs to be re-evaluated when -
526 * 1. KAT is started or stopped
527 * 2. on RP changes
528 * 3. Whenever FHR status changes for a (S,G) - XXX - currently there
529 * is no clear path to transition an entry out of "MASK_FHR" need
530 * to discuss this with Donald. May result in some strangeness if the
531 * FHR is also the RP.
532 * 4. When msdp_reg timer is started or stopped
533 */
534void
535pim_msdp_sa_local_update(struct pim_upstream *up)
536{
537 if (pim_msdp_sa_local_add_ok(up)) {
538 pim_msdp_sa_local_add(&up->sg);
539 } else {
540 pim_msdp_sa_local_del(&up->sg);
541 }
542}
543
3c72d654 544static void
545pim_msdp_sa_local_setup(void)
546{
547 struct pim_upstream *up;
548 struct listnode *up_node;
549
550 for (ALL_LIST_ELEMENTS_RO(pim_upstream_list, up_node, up)) {
1bf16443 551 pim_msdp_sa_local_update(up);
3c72d654 552 }
553}
554
7667c556 555/* whenever the RP changes we need to re-evaluate the "local" SA-cache */
556/* XXX: needs to be tested */
3c72d654 557void
558pim_msdp_i_am_rp_changed(void)
559{
560 struct listnode *sanode;
7176984f 561 struct listnode *nextnode;
3c72d654 562 struct pim_msdp_sa *sa;
563
7667c556 564 if (!(msdp->flags & PIM_MSDPF_ENABLE)) {
565 /* if the feature is not enabled do nothing */
566 return;
567 }
568
569 if (PIM_DEBUG_MSDP_INTERNAL) {
570 zlog_debug("MSDP i_am_rp changed");
571 }
572
3c72d654 573 /* mark all local entries as stale */
574 for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) {
7667c556 575 if (sa->flags & PIM_MSDP_SAF_LOCAL) {
576 sa->flags |= PIM_MSDP_SAF_STALE;
577 }
3c72d654 578 }
579
580 /* re-setup local SA entries */
581 pim_msdp_sa_local_setup();
582
7176984f 583 for (ALL_LIST_ELEMENTS(msdp->sa_list, sanode, nextnode, sa)) {
7667c556 584 /* purge stale SA entries */
3c72d654 585 if (sa->flags & PIM_MSDP_SAF_STALE) {
7667c556 586 /* clear the stale flag; the entry may be kept even after
587 * "local-deref" */
588 sa->flags &= ~PIM_MSDP_SAF_STALE;
7176984f 589 /* sa_deref can end up freeing the sa; so don't access contents after */
3c72d654 590 pim_msdp_sa_deref(sa, PIM_MSDP_SAF_LOCAL);
7176984f 591 } else {
592 /* if the souce is still active check if we can influence SPT */
593 pim_msdp_sa_upstream_update(sa, NULL /* xg_up */, "rp-change");
3c72d654 594 }
7667c556 595 }
596}
597
598/* We track the join state of (*, G) entries. If G has sources in the SA-cache
599 * we need to setup or teardown SPT when the JoinDesired status changes for
600 * (*, G) */
601void
602pim_msdp_up_join_state_changed(struct pim_upstream *xg_up)
603{
604 struct listnode *sanode;
605 struct pim_msdp_sa *sa;
606
607 if (PIM_DEBUG_MSDP_INTERNAL) {
8bfb8b67 608 zlog_debug("MSDP join state changed for %s", xg_up->sg_str);
7667c556 609 }
610
611 /* If this is not really an XG entry just move on */
612 if ((xg_up->sg.src.s_addr != INADDR_ANY) ||
613 (xg_up->sg.grp.s_addr == INADDR_ANY)) {
614 return;
615 }
616
617 /* XXX: Need to maintain SAs per-group to avoid all this unnecessary
618 * walking */
619 for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) {
620 if (sa->sg.grp.s_addr != xg_up->sg.grp.s_addr) {
621 continue;
622 }
623 pim_msdp_sa_upstream_update(sa, xg_up, "up-jp-change");
624 }
625}
626
36e466fe 627static void
7667c556 628pim_msdp_up_xg_del(struct prefix_sg *sg)
629{
630 struct listnode *sanode;
631 struct pim_msdp_sa *sa;
632
633 if (PIM_DEBUG_MSDP_INTERNAL) {
634 zlog_debug("MSDP %s del", pim_str_sg_dump(sg));
635 }
636
637 /* If this is not really an XG entry just move on */
638 if ((sg->src.s_addr != INADDR_ANY) ||
639 (sg->grp.s_addr == INADDR_ANY)) {
640 return;
641 }
642
643 /* XXX: Need to maintain SAs per-group to avoid all this unnecessary
644 * walking */
645 for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) {
646 if (sa->sg.grp.s_addr != sg->grp.s_addr) {
647 continue;
648 }
649 pim_msdp_sa_upstream_update(sa, NULL /* xg */, "up-jp-change");
3c72d654 650 }
651}
652
36e466fe 653void
654pim_msdp_up_del(struct prefix_sg *sg)
655{
656 if (PIM_DEBUG_MSDP_INTERNAL) {
657 zlog_debug("MSDP up %s del", pim_str_sg_dump(sg));
658 }
659 if (sg->src.s_addr == INADDR_ANY) {
660 pim_msdp_up_xg_del(sg);
661 } else {
662 pim_msdp_sa_local_del_on_up_del(sg);
663 }
664}
665
3c72d654 666/* sa hash and peer list helpers */
667static unsigned int
668pim_msdp_sa_hash_key_make(void *p)
669{
670 struct pim_msdp_sa *sa = p;
671
672 return (jhash_2words(sa->sg.src.s_addr, sa->sg.grp.s_addr, 0));
673}
674
675static int
676pim_msdp_sa_hash_eq(const void *p1, const void *p2)
677{
678 const struct pim_msdp_sa *sa1 = p1;
679 const struct pim_msdp_sa *sa2 = p2;
680
681 return ((sa1->sg.src.s_addr == sa2->sg.src.s_addr) &&
682 (sa1->sg.grp.s_addr == sa2->sg.grp.s_addr));
683}
684
685static int
686pim_msdp_sa_comp(const void *p1, const void *p2)
687{
688 const struct pim_msdp_sa *sa1 = p1;
689 const struct pim_msdp_sa *sa2 = p2;
690
691 if (ntohl(sa1->sg.grp.s_addr) < ntohl(sa2->sg.grp.s_addr))
692 return -1;
693
694 if (ntohl(sa1->sg.grp.s_addr) > ntohl(sa2->sg.grp.s_addr))
695 return 1;
696
697 if (ntohl(sa1->sg.src.s_addr) < ntohl(sa2->sg.src.s_addr))
698 return -1;
699
700 if (ntohl(sa1->sg.src.s_addr) > ntohl(sa2->sg.src.s_addr))
701 return 1;
702
703 return 0;
704}
705
706/* RFC-3618:Sec-10.1.3 - Peer-RPF forwarding */
707/* XXX: this can use a bit of refining and extensions */
708bool
709pim_msdp_peer_rpf_check(struct pim_msdp_peer *mp, struct in_addr rp)
710{
711 if (mp->peer.s_addr == rp.s_addr) {
712 return true;
713 }
714
715 return false;
716}
717
718/************************ Peer session management **************************/
2a333e0f 719char *
720pim_msdp_state_dump(enum pim_msdp_peer_state state, char *buf, int buf_size)
721{
722 switch (state) {
723 case PIM_MSDP_DISABLED:
724 snprintf(buf, buf_size, "%s", "disabled");
725 break;
726 case PIM_MSDP_INACTIVE:
727 snprintf(buf, buf_size, "%s", "inactive");
728 break;
729 case PIM_MSDP_LISTEN:
730 snprintf(buf, buf_size, "%s", "listen");
731 break;
732 case PIM_MSDP_CONNECTING:
733 snprintf(buf, buf_size, "%s", "connecting");
734 break;
735 case PIM_MSDP_ESTABLISHED:
736 snprintf(buf, buf_size, "%s", "established");
737 break;
738 default:
739 snprintf(buf, buf_size, "unk-%d", state);
740 }
741 return buf;
742}
743
744char *
745pim_msdp_peer_key_dump(struct pim_msdp_peer *mp, char *buf, int buf_size, bool long_format)
746{
747 char peer_str[INET_ADDRSTRLEN];
748 char local_str[INET_ADDRSTRLEN];
749
750 pim_inet4_dump("<peer?>", mp->peer, peer_str, sizeof(peer_str));
751 if (long_format) {
752 pim_inet4_dump("<local?>", mp->local, local_str, sizeof(local_str));
753 snprintf(buf, buf_size, "MSDP peer %s local %s mg %s",
754 peer_str, local_str, mp->mesh_group_name);
755 } else {
756 snprintf(buf, buf_size, "MSDP peer %s", peer_str);
757 }
758
759 return buf;
760}
761
762static void
763pim_msdp_peer_state_chg_log(struct pim_msdp_peer *mp)
764{
765 char state_str[PIM_MSDP_STATE_STRLEN];
2a333e0f 766
767 pim_msdp_state_dump(mp->state, state_str, sizeof(state_str));
15ad0c71 768 zlog_debug("MSDP peer %s state chg to %s", mp->key_str, state_str);
2a333e0f 769}
770
771/* MSDP Connection State Machine actions (defined in RFC-3618:Sec-11.2) */
772/* 11.2.A2: active peer - start connect retry timer; when the timer fires
773 * a tcp connection will be made */
774static void
775pim_msdp_peer_connect(struct pim_msdp_peer *mp)
776{
777 mp->state = PIM_MSDP_CONNECTING;
778 if (PIM_DEBUG_MSDP_EVENTS) {
779 pim_msdp_peer_state_chg_log(mp);
780 }
781
782 pim_msdp_peer_cr_timer_setup(mp, true /* start */);
783}
784
785/* 11.2.A3: passive peer - just listen for connections */
786static void
787pim_msdp_peer_listen(struct pim_msdp_peer *mp)
788{
789 mp->state = PIM_MSDP_LISTEN;
790 if (PIM_DEBUG_MSDP_EVENTS) {
791 pim_msdp_peer_state_chg_log(mp);
792 }
793
794 /* this is interntionally asymmetric i.e. we set up listen-socket when the
795 * first listening peer is configured; but don't bother tearing it down when
796 * all the peers go down */
797 pim_msdp_sock_listen();
798}
799
800/* 11.2.A4 and 11.2.A5: transition active or passive peer to
801 * established state */
802void
803pim_msdp_peer_established(struct pim_msdp_peer *mp)
804{
977d71cc 805 if (mp->state != PIM_MSDP_ESTABLISHED) {
806 ++mp->est_flaps;
807 }
808
2a333e0f 809 mp->state = PIM_MSDP_ESTABLISHED;
810 mp->uptime = pim_time_monotonic_sec();
811
812 if (PIM_DEBUG_MSDP_EVENTS) {
813 pim_msdp_peer_state_chg_log(mp);
814 }
815
816 /* stop retry timer on active peers */
817 pim_msdp_peer_cr_timer_setup(mp, false /* start */);
818
819 /* send KA; start KA and hold timers */
820 pim_msdp_pkt_ka_tx(mp);
821 pim_msdp_peer_ka_timer_setup(mp, true /* start */);
822 pim_msdp_peer_hold_timer_setup(mp, true /* start */);
823
3c72d654 824 pim_msdp_pkt_sa_tx_to_one_peer(mp);
825
2a333e0f 826 PIM_MSDP_PEER_WRITE_ON(mp);
827 PIM_MSDP_PEER_READ_ON(mp);
828}
829
830/* 11.2.A6, 11.2.A7 and 11.2.A8: shutdown the peer tcp connection */
831void
832pim_msdp_peer_stop_tcp_conn(struct pim_msdp_peer *mp, bool chg_state)
833{
834 if (chg_state) {
977d71cc 835 if (mp->state == PIM_MSDP_ESTABLISHED) {
836 ++mp->est_flaps;
837 }
2a333e0f 838 mp->state = PIM_MSDP_INACTIVE;
839 if (PIM_DEBUG_MSDP_EVENTS) {
840 pim_msdp_peer_state_chg_log(mp);
841 }
842 }
843
7667c556 844 if (PIM_DEBUG_MSDP_INTERNAL) {
15ad0c71 845 zlog_debug("MSDP peer %s pim_msdp_peer_stop_tcp_conn", mp->key_str);
7667c556 846 }
2a333e0f 847 /* stop read and write threads */
848 PIM_MSDP_PEER_READ_OFF(mp);
849 PIM_MSDP_PEER_WRITE_OFF(mp);
850
851 /* reset buffers */
3e5e4225 852 mp->packet_size = 0;
2a333e0f 853 if (mp->ibuf)
854 stream_reset(mp->ibuf);
855 if (mp->obuf)
856 stream_fifo_clean(mp->obuf);
857
858 /* stop all peer timers */
859 pim_msdp_peer_ka_timer_setup(mp, false /* start */);
860 pim_msdp_peer_cr_timer_setup(mp, false /* start */);
861 pim_msdp_peer_hold_timer_setup(mp, false /* start */);
862
863 /* close connection */
864 if (mp->fd >= 0) {
865 close(mp->fd);
866 mp->fd = -1;
867 }
868}
869
870/* RFC-3618:Sec-5.6 - stop the peer tcp connection and startover */
871void
872pim_msdp_peer_reset_tcp_conn(struct pim_msdp_peer *mp, const char *rc_str)
873{
874 if (PIM_DEBUG_EVENTS) {
15ad0c71 875 zlog_debug("MSDP peer %s tcp reset %s", mp->key_str, rc_str);
977d71cc 876 snprintf(mp->last_reset, sizeof(mp->last_reset), "%s", rc_str);
2a333e0f 877 }
878
879 /* close the connection and transition to listening or connecting */
880 pim_msdp_peer_stop_tcp_conn(mp, true /* chg_state */);
881 if (PIM_MSDP_PEER_IS_LISTENER(mp)) {
882 pim_msdp_peer_listen(mp);
883 } else {
884 pim_msdp_peer_connect(mp);
885 }
886}
887
888static void
889pim_msdp_peer_timer_expiry_log(struct pim_msdp_peer *mp, const char *timer_str)
890{
15ad0c71 891 zlog_debug("MSDP peer %s %s timer expired", mp->key_str, timer_str);
2a333e0f 892}
893
894/* RFC-3618:Sec-5.4 - peer hold timer */
895static int
896pim_msdp_peer_hold_timer_cb(struct thread *t)
897{
898 struct pim_msdp_peer *mp;
899
2a333e0f 900 mp = THREAD_ARG(t);
7667c556 901 mp->hold_timer = NULL;
2a333e0f 902
903 if (PIM_DEBUG_MSDP_EVENTS) {
904 pim_msdp_peer_timer_expiry_log(mp, "hold");
905 }
906
907 if (mp->state != PIM_MSDP_ESTABLISHED) {
908 return 0;
909 }
910
911 if (PIM_DEBUG_MSDP_EVENTS) {
912 pim_msdp_peer_state_chg_log(mp);
913 }
914 pim_msdp_peer_reset_tcp_conn(mp, "ht-expired");
915 return 0;
916}
917static void
918pim_msdp_peer_hold_timer_setup(struct pim_msdp_peer *mp, bool start)
919{
920 THREAD_OFF(mp->hold_timer);
921 if (start) {
ffa2c898
QY
922 thread_add_timer(msdp->master, pim_msdp_peer_hold_timer_cb, mp,
923 PIM_MSDP_PEER_HOLD_TIME, &mp->hold_timer);
2a333e0f 924 }
925}
926
927
928/* RFC-3618:Sec-5.5 - peer keepalive timer */
929static int
930pim_msdp_peer_ka_timer_cb(struct thread *t)
931{
932 struct pim_msdp_peer *mp;
933
2a333e0f 934 mp = THREAD_ARG(t);
7667c556 935 mp->ka_timer = NULL;
2a333e0f 936
937 if (PIM_DEBUG_MSDP_EVENTS) {
938 pim_msdp_peer_timer_expiry_log(mp, "ka");
939 }
940
2a333e0f 941 pim_msdp_pkt_ka_tx(mp);
942 pim_msdp_peer_ka_timer_setup(mp, true /* start */);
943 return 0;
944}
2a333e0f 945static void
946pim_msdp_peer_ka_timer_setup(struct pim_msdp_peer *mp, bool start)
947{
948 THREAD_OFF(mp->ka_timer);
949 if (start) {
ffa2c898
QY
950 thread_add_timer(msdp->master, pim_msdp_peer_ka_timer_cb, mp,
951 PIM_MSDP_PEER_KA_TIME, &mp->ka_timer);
2a333e0f 952 }
953}
954
955static void
956pim_msdp_peer_active_connect(struct pim_msdp_peer *mp)
957{
958 int rc;
977d71cc 959 ++mp->conn_attempts;
2a333e0f 960 rc = pim_msdp_sock_connect(mp);
961
962 if (PIM_DEBUG_MSDP_INTERNAL) {
15ad0c71 963 zlog_debug("MSDP peer %s pim_msdp_peer_active_connect: %d", mp->key_str, rc);
2a333e0f 964 }
965
966 switch (rc) {
967 case connect_error:
977d71cc 968 case -1:
2a333e0f 969 /* connect failed restart the connect-retry timer */
970 pim_msdp_peer_cr_timer_setup(mp, true /* start */);
971 break;
972
973 case connect_success:
974 /* connect was sucessful move to established */
975 pim_msdp_peer_established(mp);
976 break;
977
978 case connect_in_progress:
979 /* for NB content we need to wait till sock is readable or
980 * writeable */
981 PIM_MSDP_PEER_WRITE_ON(mp);
982 PIM_MSDP_PEER_READ_ON(mp);
983 /* also restart connect-retry timer to reset the socket if connect is
984 * not sucessful */
985 pim_msdp_peer_cr_timer_setup(mp, true /* start */);
986 break;
987 }
988}
989
990/* RFC-3618:Sec-5.6 - connection retry on active peer */
991static int
992pim_msdp_peer_cr_timer_cb(struct thread *t)
993{
994 struct pim_msdp_peer *mp;
995
2a333e0f 996 mp = THREAD_ARG(t);
7667c556 997 mp->cr_timer = NULL;
2a333e0f 998
999 if (PIM_DEBUG_MSDP_EVENTS) {
1000 pim_msdp_peer_timer_expiry_log(mp, "connect-retry");
1001 }
1002
1003 if (mp->state != PIM_MSDP_CONNECTING || PIM_MSDP_PEER_IS_LISTENER(mp)) {
1004 return 0;
1005 }
1006
1007 pim_msdp_peer_active_connect(mp);
1008 return 0;
1009}
1010static void
1011pim_msdp_peer_cr_timer_setup(struct pim_msdp_peer *mp, bool start)
1012{
1013 THREAD_OFF(mp->cr_timer);
1014 if (start) {
ffa2c898
QY
1015 thread_add_timer(msdp->master, pim_msdp_peer_cr_timer_cb, mp,
1016 PIM_MSDP_PEER_CONNECT_RETRY_TIME, &mp->cr_timer);
2a333e0f 1017 }
1018}
1019
1020/* if a valid packet is rxed from the peer we can restart hold timer */
11128587 1021void
2a333e0f 1022pim_msdp_peer_pkt_rxed(struct pim_msdp_peer *mp)
1023{
1024 if (mp->state == PIM_MSDP_ESTABLISHED) {
1025 pim_msdp_peer_hold_timer_setup(mp, true /* start */);
1026 }
1027}
1028
3c72d654 1029/* if a valid packet is txed to the peer we can restart ka timer and avoid
1030 * unnecessary ka noise in the network */
1031void
1032pim_msdp_peer_pkt_txed(struct pim_msdp_peer *mp)
1033{
1034 if (mp->state == PIM_MSDP_ESTABLISHED) {
1035 pim_msdp_peer_ka_timer_setup(mp, true /* start */);
69053fb4 1036 if (PIM_DEBUG_MSDP_INTERNAL) {
1037 zlog_debug("MSDP ka timer restart on pkt tx to %s", mp->key_str);
1038 }
3c72d654 1039 }
1040}
1041
2a333e0f 1042static void pim_msdp_addr2su(union sockunion *su, struct in_addr addr)
1043{
1044 sockunion_init(su);
1045 su->sin.sin_addr = addr;
1046 su->sin.sin_family = AF_INET;
1047#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
1048 su->sin.sin_len = sizeof(struct sockaddr_in);
1049#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
1050}
1051
1052/* 11.2.A1: create a new peer and transition state to listen or connecting */
1053static enum pim_msdp_err
1054pim_msdp_peer_new(struct in_addr peer_addr, struct in_addr local_addr,
977d71cc 1055 const char *mesh_group_name, struct pim_msdp_peer **mp_p)
2a333e0f 1056{
1057 struct pim_msdp_peer *mp;
1058
3c72d654 1059 pim_msdp_enable();
1060
2a333e0f 1061 mp = XCALLOC(MTYPE_PIM_MSDP_PEER, sizeof(*mp));
1062 if (!mp) {
1063 zlog_err("%s: PIM XCALLOC(%zu) failure",
1064 __PRETTY_FUNCTION__, sizeof(*mp));
1065 return PIM_MSDP_ERR_OOM;
1066 }
1067
1068 mp->peer = peer_addr;
15ad0c71 1069 pim_inet4_dump("<peer?>", mp->peer, mp->key_str, sizeof(mp->key_str));
2a333e0f 1070 pim_msdp_addr2su(&mp->su_peer, mp->peer);
1071 mp->local = local_addr;
3c72d654 1072 /* XXX: originator_id setting needs to move to the mesh group */
1073 msdp->originator_id = local_addr;
2a333e0f 1074 pim_msdp_addr2su(&mp->su_local, mp->local);
977d71cc 1075 mp->mesh_group_name = XSTRDUP(MTYPE_PIM_MSDP_MG_NAME, mesh_group_name);
2a333e0f 1076 mp->state = PIM_MSDP_INACTIVE;
1077 mp->fd = -1;
977d71cc 1078 strcpy(mp->last_reset, "-");
2a333e0f 1079 /* higher IP address is listener */
1080 if (ntohl(mp->local.s_addr) > ntohl(mp->peer.s_addr)) {
1081 mp->flags |= PIM_MSDP_PEERF_LISTENER;
1082 }
1083
2a333e0f 1084 /* setup packet buffers */
1085 mp->ibuf = stream_new(PIM_MSDP_MAX_PACKET_SIZE);
1086 mp->obuf = stream_fifo_new();
1087
1088 /* insert into misc tables for easy access */
1089 mp = hash_get(msdp->peer_hash, mp, hash_alloc_intern);
2a333e0f 1090 listnode_add_sort(msdp->peer_list, mp);
1091
1092 if (PIM_DEBUG_MSDP_EVENTS) {
15ad0c71 1093 zlog_debug("MSDP peer %s created", mp->key_str);
3c72d654 1094
2a333e0f 1095 pim_msdp_peer_state_chg_log(mp);
1096 }
3c72d654 1097
2a333e0f 1098 /* fireup the connect state machine */
1099 if (PIM_MSDP_PEER_IS_LISTENER(mp)) {
1100 pim_msdp_peer_listen(mp);
1101 } else {
1102 pim_msdp_peer_connect(mp);
1103 }
977d71cc 1104 if (mp_p) {
1105 *mp_p = mp;
1106 }
2a333e0f 1107 return PIM_MSDP_ERR_NONE;
1108}
1109
1110struct pim_msdp_peer *
1111pim_msdp_peer_find(struct in_addr peer_addr)
1112{
1113 struct pim_msdp_peer lookup;
1114
1115 lookup.peer = peer_addr;
1116 return hash_lookup(msdp->peer_hash, &lookup);
1117}
1118
1119/* add peer configuration if it doesn't already exist */
1120enum pim_msdp_err
1121pim_msdp_peer_add(struct in_addr peer_addr, struct in_addr local_addr,
977d71cc 1122 const char *mesh_group_name, struct pim_msdp_peer **mp_p)
2a333e0f 1123{
1124 struct pim_msdp_peer *mp;
1125
977d71cc 1126 if (mp_p) {
1127 *mp_p = NULL;
1128 }
1129
1130 if (peer_addr.s_addr == local_addr.s_addr) {
1131 /* skip session setup if config is invalid */
1132 if (PIM_DEBUG_MSDP_EVENTS) {
1133 char peer_str[INET_ADDRSTRLEN];
1134
1135 pim_inet4_dump("<peer?>", peer_addr, peer_str, sizeof(peer_str));
1136 zlog_debug("%s add skipped as DIP=SIP", peer_str);
1137 }
1138 return PIM_MSDP_ERR_SIP_EQ_DIP;
1139 }
1140
2a333e0f 1141 mp = pim_msdp_peer_find(peer_addr);
1142 if (mp) {
977d71cc 1143 if (mp_p) {
1144 *mp_p = mp;
1145 }
2a333e0f 1146 return PIM_MSDP_ERR_PEER_EXISTS;
1147 }
1148
977d71cc 1149 return pim_msdp_peer_new(peer_addr, local_addr, mesh_group_name, mp_p);
2a333e0f 1150}
1151
1152/* release all mem associated with a peer */
1153static void
1154pim_msdp_peer_free(struct pim_msdp_peer *mp)
11128587 1155{
2a333e0f 1156 if (mp->ibuf) {
1157 stream_free(mp->ibuf);
1158 }
1159
1160 if (mp->obuf) {
1161 stream_fifo_free(mp->obuf);
1162 }
1163
1164 if (mp->mesh_group_name) {
977d71cc 1165 XFREE(MTYPE_PIM_MSDP_MG_NAME, mp->mesh_group_name);
2a333e0f 1166 }
1167 XFREE(MTYPE_PIM_MSDP_PEER, mp);
1168}
1169
1170/* delete the peer config */
977d71cc 1171static enum pim_msdp_err
1172pim_msdp_peer_do_del(struct pim_msdp_peer *mp)
2a333e0f 1173{
2a333e0f 1174 /* stop the tcp connection and shutdown all timers */
1175 pim_msdp_peer_stop_tcp_conn(mp, true /* chg_state */);
1176
1177 /* remove the session from various tables */
1178 listnode_delete(msdp->peer_list, mp);
1179 hash_release(msdp->peer_hash, mp);
1180
1181 if (PIM_DEBUG_MSDP_EVENTS) {
15ad0c71 1182 zlog_debug("MSDP peer %s deleted", mp->key_str);
2a333e0f 1183 }
1184
1185 /* free up any associated memory */
1186 pim_msdp_peer_free(mp);
1187
1188 return PIM_MSDP_ERR_NONE;
1189}
1190
977d71cc 1191enum pim_msdp_err
1192pim_msdp_peer_del(struct in_addr peer_addr)
1193{
1194 struct pim_msdp_peer *mp;
1195
1196 mp = pim_msdp_peer_find(peer_addr);
1197 if (!mp) {
1198 return PIM_MSDP_ERR_NO_PEER;
1199 }
1200
1201 return pim_msdp_peer_do_del(mp);
1202}
1203
2a333e0f 1204/* peer hash and peer list helpers */
1205static unsigned int
1206pim_msdp_peer_hash_key_make(void *p)
1207{
1208 struct pim_msdp_peer *mp = p;
1209 return (jhash_1word(mp->peer.s_addr, 0));
1210}
1211
1212static int
1213pim_msdp_peer_hash_eq(const void *p1, const void *p2)
1214{
1215 const struct pim_msdp_peer *mp1 = p1;
1216 const struct pim_msdp_peer *mp2 = p2;
1217
1218 return (mp1->peer.s_addr == mp2->peer.s_addr);
1219}
1220
1221static int
1222pim_msdp_peer_comp(const void *p1, const void *p2)
1223{
1224 const struct pim_msdp_peer *mp1 = p1;
1225 const struct pim_msdp_peer *mp2 = p2;
1226
1227 if (ntohl(mp1->peer.s_addr) < ntohl(mp2->peer.s_addr))
1228 return -1;
1229
1230 if (ntohl(mp1->peer.s_addr) > ntohl(mp2->peer.s_addr))
1231 return 1;
1232
1233 return 0;
1234}
1235
977d71cc 1236/************************** Mesh group management **************************/
1237static void
1238pim_msdp_mg_free(struct pim_msdp_mg *mg)
1239{
1240 /* If the mesh-group has valid member or src_ip don't delete it */
1241 if (!mg || mg->mbr_cnt || (mg->src_ip.s_addr != INADDR_ANY)) {
1242 return;
1243 }
1244
1245 if (PIM_DEBUG_MSDP_EVENTS) {
1246 zlog_debug("MSDP mesh-group %s deleted", mg->mesh_group_name);
1247 }
1248 if (mg->mesh_group_name)
1249 XFREE(MTYPE_PIM_MSDP_MG_NAME, mg->mesh_group_name);
977d71cc 1250
7176984f 1251 if (mg->mbr_list)
1252 list_free(mg->mbr_list);
b9b1e1f2 1253
1254 XFREE(MTYPE_PIM_MSDP_MG, mg);
977d71cc 1255 msdp->mg = NULL;
1256}
1257
1258static struct pim_msdp_mg *
1259pim_msdp_mg_new(const char *mesh_group_name)
1260{
1261 struct pim_msdp_mg *mg;
1262
1263 mg = XCALLOC(MTYPE_PIM_MSDP_MG, sizeof(*mg));
1264 if (!mg) {
1265 zlog_err("%s: PIM XCALLOC(%zu) failure",
1266 __PRETTY_FUNCTION__, sizeof(*mg));
1267 return NULL;
1268 }
1269
1270 mg->mesh_group_name = XSTRDUP(MTYPE_PIM_MSDP_MG_NAME, mesh_group_name);
1271 mg->mbr_list = list_new();
1272 mg->mbr_list->del = (void (*)(void *))pim_msdp_mg_mbr_free;
1273 mg->mbr_list->cmp = (int (*)(void *, void *))pim_msdp_mg_mbr_comp;
1274
1275 if (PIM_DEBUG_MSDP_EVENTS) {
1276 zlog_debug("MSDP mesh-group %s created", mg->mesh_group_name);
1277 }
1278 return mg;
1279}
1280
1281enum pim_msdp_err
1282pim_msdp_mg_del(const char *mesh_group_name)
1283{
1284 struct pim_msdp_mg *mg = msdp->mg;
1285 struct pim_msdp_mg_mbr *mbr;
1286
1287 if (!mg || strcmp(mg->mesh_group_name, mesh_group_name)) {
1288 return PIM_MSDP_ERR_NO_MG;
1289 }
1290
1291 /* delete all the mesh-group members */
1292 while (!list_isempty(mg->mbr_list)) {
1293 mbr = listnode_head(mg->mbr_list);
1294 pim_msdp_mg_mbr_do_del(mg, mbr);
1295 }
1296
1297 /* clear src ip */
1298 mg->src_ip.s_addr = INADDR_ANY;
1299
1300 /* free up the mesh-group */
1301 pim_msdp_mg_free(mg);
1302 return PIM_MSDP_ERR_NONE;
1303}
1304
1305static enum pim_msdp_err
1306pim_msdp_mg_add(const char *mesh_group_name)
1307{
1308 if (msdp->mg) {
1309 if (!strcmp(msdp->mg->mesh_group_name, mesh_group_name)) {
1310 return PIM_MSDP_ERR_NONE;
1311 }
1312 /* currently only one mesh-group can exist at a time */
1313 return PIM_MSDP_ERR_MAX_MESH_GROUPS;
1314 }
1315
1316 msdp->mg = pim_msdp_mg_new(mesh_group_name);
1317 if (!msdp->mg) {
1318 return PIM_MSDP_ERR_OOM;
1319 }
1320
1321 return PIM_MSDP_ERR_NONE;
1322}
1323
1324static int
1325pim_msdp_mg_mbr_comp(const void *p1, const void *p2)
1326{
1327 const struct pim_msdp_mg_mbr *mbr1 = p1;
1328 const struct pim_msdp_mg_mbr *mbr2 = p2;
1329
1330 if (ntohl(mbr1->mbr_ip.s_addr) < ntohl(mbr2->mbr_ip.s_addr))
1331 return -1;
1332
1333 if (ntohl(mbr1->mbr_ip.s_addr) > ntohl(mbr2->mbr_ip.s_addr))
1334 return 1;
1335
1336 return 0;
1337}
1338
1339static void
1340pim_msdp_mg_mbr_free(struct pim_msdp_mg_mbr *mbr)
1341{
1342 XFREE(MTYPE_PIM_MSDP_MG_MBR, mbr);
1343}
1344
1345static struct pim_msdp_mg_mbr *
1346pim_msdp_mg_mbr_find(struct in_addr mbr_ip)
1347{
1348 struct pim_msdp_mg_mbr *mbr;
1349 struct listnode *mbr_node;
1350
1351 if (!msdp->mg) {
1352 return NULL;
1353 }
1354 /* we can move this to a hash but considering that number of peers in
1355 * a mesh-group that seems like bit of an overkill */
1356 for (ALL_LIST_ELEMENTS_RO(msdp->mg->mbr_list, mbr_node, mbr)) {
1357 if (mbr->mbr_ip.s_addr == mbr_ip.s_addr) {
1358 return mbr;
1359 }
1360 }
1361 return mbr;
1362}
1363
1364enum pim_msdp_err
1365pim_msdp_mg_mbr_add(const char *mesh_group_name, struct in_addr mbr_ip)
1366{
1367 int rc;
1368 struct pim_msdp_mg_mbr *mbr;
1369 struct pim_msdp_mg *mg;
1370
1371 rc = pim_msdp_mg_add(mesh_group_name);
1372 if (rc != PIM_MSDP_ERR_NONE) {
1373 return rc;
1374 }
1375
1376 mg = msdp->mg;
1377 mbr = pim_msdp_mg_mbr_find(mbr_ip);
1378 if (mbr) {
1379 return PIM_MSDP_ERR_MG_MBR_EXISTS;
1380 }
1381
1382 mbr = XCALLOC(MTYPE_PIM_MSDP_MG_MBR, sizeof(*mbr));
1383 if (!mbr) {
1384 zlog_err("%s: PIM XCALLOC(%zu) failure",
1385 __PRETTY_FUNCTION__, sizeof(*mbr));
1386 /* if there are no references to the mg free it */
1387 pim_msdp_mg_free(mg);
1388 return PIM_MSDP_ERR_OOM;
1389 }
1390 mbr->mbr_ip = mbr_ip;
1391 listnode_add_sort(mg->mbr_list, mbr);
1392
1393 /* if valid SIP has been configured add peer session */
1394 if (mg->src_ip.s_addr != INADDR_ANY) {
1395 pim_msdp_peer_add(mbr_ip, mg->src_ip, mesh_group_name,
1396 &mbr->mp);
1397 }
1398
1399 if (PIM_DEBUG_MSDP_EVENTS) {
1400 char ip_str[INET_ADDRSTRLEN];
1401 pim_inet4_dump("<mbr?>", mbr->mbr_ip, ip_str, sizeof(ip_str));
1402 zlog_debug("MSDP mesh-group %s mbr %s created", mg->mesh_group_name, ip_str);
1403 }
1404 ++mg->mbr_cnt;
1405 return PIM_MSDP_ERR_NONE;
1406}
1407
1408static void
1409pim_msdp_mg_mbr_do_del(struct pim_msdp_mg *mg, struct pim_msdp_mg_mbr *mbr)
1410{
1411 /* Delete active peer session if any */
1412 if (mbr->mp) {
1413 pim_msdp_peer_do_del(mbr->mp);
1414 }
1415
1416 listnode_delete(mg->mbr_list, mbr);
1417 if (PIM_DEBUG_MSDP_EVENTS) {
1418 char ip_str[INET_ADDRSTRLEN];
1419 pim_inet4_dump("<mbr?>", mbr->mbr_ip, ip_str, sizeof(ip_str));
1420 zlog_debug("MSDP mesh-group %s mbr %s deleted", mg->mesh_group_name, ip_str);
1421 }
1422 pim_msdp_mg_mbr_free(mbr);
1423 if (mg->mbr_cnt) {
1424 --mg->mbr_cnt;
1425 }
1426}
1427
1428enum pim_msdp_err
1429pim_msdp_mg_mbr_del(const char *mesh_group_name, struct in_addr mbr_ip)
1430{
1431 struct pim_msdp_mg_mbr *mbr;
1432 struct pim_msdp_mg *mg = msdp->mg;
1433
1434 if (!mg || strcmp(mg->mesh_group_name, mesh_group_name)) {
1435 return PIM_MSDP_ERR_NO_MG;
1436 }
1437
1438 mbr = pim_msdp_mg_mbr_find(mbr_ip);
1439 if (!mbr) {
1440 return PIM_MSDP_ERR_NO_MG_MBR;
1441 }
1442
1443 pim_msdp_mg_mbr_do_del(mg, mbr);
1444 /* if there are no references to the mg free it */
1445 pim_msdp_mg_free(mg);
1446
1447 return PIM_MSDP_ERR_NONE;
1448}
1449
1450static void
1451pim_msdp_mg_src_do_del(void)
1452{
1453 struct pim_msdp_mg_mbr *mbr;
1454 struct listnode *mbr_node;
1455 struct pim_msdp_mg *mg = msdp->mg;
1456
1457 /* SIP is being removed - tear down all active peer sessions */
1458 for (ALL_LIST_ELEMENTS_RO(mg->mbr_list, mbr_node, mbr)) {
1459 if (mbr->mp) {
1460 pim_msdp_peer_do_del(mbr->mp);
1461 mbr->mp = NULL;
1462 }
1463 }
1464 if (PIM_DEBUG_MSDP_EVENTS) {
1465 zlog_debug("MSDP mesh-group %s src cleared", mg->mesh_group_name);
1466 }
1467}
1468
1469enum pim_msdp_err
1470pim_msdp_mg_src_del(const char *mesh_group_name)
1471{
1472 struct pim_msdp_mg *mg = msdp->mg;
1473
1474 if (!mg || strcmp(mg->mesh_group_name, mesh_group_name)) {
1475 return PIM_MSDP_ERR_NO_MG;
1476 }
1477
1478 if (mg->src_ip.s_addr != INADDR_ANY) {
1479 mg->src_ip.s_addr = INADDR_ANY;
1480 pim_msdp_mg_src_do_del();
1481 /* if there are no references to the mg free it */
1482 pim_msdp_mg_free(mg);
1483 }
1484 return PIM_MSDP_ERR_NONE;
1485}
1486
1487enum pim_msdp_err
1488pim_msdp_mg_src_add(const char *mesh_group_name, struct in_addr src_ip)
1489{
1490 int rc;
1491 struct pim_msdp_mg_mbr *mbr;
1492 struct listnode *mbr_node;
1493 struct pim_msdp_mg *mg;
1494
1495 if (src_ip.s_addr == INADDR_ANY) {
1496 pim_msdp_mg_src_del(mesh_group_name);
1497 return PIM_MSDP_ERR_NONE;
1498 }
1499
1500 rc = pim_msdp_mg_add(mesh_group_name);
1501 if (rc != PIM_MSDP_ERR_NONE) {
1502 return rc;
1503 }
1504
1505 mg = msdp->mg;
1506 if (mg->src_ip.s_addr != INADDR_ANY) {
1507 pim_msdp_mg_src_do_del();
1508 }
1509 mg->src_ip = src_ip;
1510
1511 for (ALL_LIST_ELEMENTS_RO(mg->mbr_list, mbr_node, mbr)) {
1512 pim_msdp_peer_add(mbr->mbr_ip, mg->src_ip, mesh_group_name,
1513 &mbr->mp);
1514 }
1515
1516 if (PIM_DEBUG_MSDP_EVENTS) {
1517 char ip_str[INET_ADDRSTRLEN];
1518 pim_inet4_dump("<src?>", mg->src_ip, ip_str, sizeof(ip_str));
1519 zlog_debug("MSDP mesh-group %s src %s set", mg->mesh_group_name, ip_str);
1520 }
1521 return PIM_MSDP_ERR_NONE;
1522}
1523
3c72d654 1524/*********************** MSDP feature APIs *********************************/
1525int
1526pim_msdp_config_write(struct vty *vty)
1527{
977d71cc 1528 struct listnode *mbrnode;
1529 struct pim_msdp_mg_mbr *mbr;
1530 struct pim_msdp_mg *mg = msdp->mg;
1531 char mbr_str[INET_ADDRSTRLEN];
1532 char src_str[INET_ADDRSTRLEN];
3c72d654 1533 int count = 0;
1534
977d71cc 1535 if (!mg) {
1536 return count;
1537 }
1538
1539 if (mg->src_ip.s_addr != INADDR_ANY) {
1540 pim_inet4_dump("<src?>", mg->src_ip, src_str, sizeof(src_str));
1541 vty_out(vty, "ip msdp mesh-group %s source %s%s",
1542 mg->mesh_group_name, src_str, VTY_NEWLINE);
1543 ++count;
1544 }
1545
1546 for (ALL_LIST_ELEMENTS_RO(mg->mbr_list, mbrnode, mbr)) {
1547 pim_inet4_dump("<mbr?>", mbr->mbr_ip, mbr_str, sizeof(mbr_str));
1548 vty_out(vty, "ip msdp mesh-group %s member %s%s",
1549 mg->mesh_group_name, mbr_str, VTY_NEWLINE);
3c72d654 1550 ++count;
1551 }
1552 return count;
1553}
1554
1555/* Enable feature including active/periodic timers etc. on the first peer
1556 * config. Till then MSDP should just stay quiet. */
1557static void
1558pim_msdp_enable(void)
1559{
1560 if (msdp->flags & PIM_MSDPF_ENABLE) {
1561 /* feature is already enabled */
1562 return;
1563 }
1564 msdp->flags |= PIM_MSDPF_ENABLE;
1565 msdp->work_obuf = stream_new(PIM_MSDP_MAX_PACKET_SIZE);
1566 pim_msdp_sa_adv_timer_setup(true /* start */);
1567 /* setup sa cache based on local sources */
1568 pim_msdp_sa_local_setup();
1569}
1570
2a333e0f 1571/* MSDP init */
1572void
1573pim_msdp_init(struct thread_master *master)
1574{
3c72d654 1575 msdp->master = master;
1576
2a333e0f 1577 msdp->peer_hash = hash_create(pim_msdp_peer_hash_key_make,
1578 pim_msdp_peer_hash_eq);
1579 msdp->peer_list = list_new();
1580 msdp->peer_list->del = (void (*)(void *))pim_msdp_peer_free;
1581 msdp->peer_list->cmp = (int (*)(void *, void *))pim_msdp_peer_comp;
3c72d654 1582
1583 msdp->sa_hash = hash_create(pim_msdp_sa_hash_key_make,
1584 pim_msdp_sa_hash_eq);
1585 msdp->sa_list = list_new();
1586 msdp->sa_list->del = (void (*)(void *))pim_msdp_sa_free;
1587 msdp->sa_list->cmp = (int (*)(void *, void *))pim_msdp_sa_comp;
2a333e0f 1588}
1589
1590/* counterpart to MSDP init; XXX: unused currently */
1591void
1592pim_msdp_exit(void)
1593{
1594 /* XXX: stop listener and delete all peer sessions */
1595
1596 if (msdp->peer_hash) {
1597 hash_free(msdp->peer_hash);
1598 msdp->peer_hash = NULL;
1599 }
1600
1601 if (msdp->peer_list) {
1602 list_free(msdp->peer_list);
1603 msdp->peer_list = NULL;
1604 }
11128587 1605}