]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_msdp.c
pim-msdp: part-3: use SA cache for setting up SPTs
[mirror_frr.git] / pimd / pim_msdp.c
1 /*
2 * IP MSDP for Quagga
3 * Copyright (C) 2016 Cumulus Networks, Inc.
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 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; see the file COPYING; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
18 * MA 02110-1301 USA
19 */
20
21 #include <zebra.h>
22
23 #include <lib/hash.h>
24 #include <lib/jhash.h>
25 #include <lib/log.h>
26 #include <lib/prefix.h>
27 #include <lib/sockunion.h>
28 #include <lib/stream.h>
29 #include <lib/thread.h>
30 #include <lib/vty.h>
31 #include <lib/plist.h>
32
33 #include "pimd.h"
34 #include "pim_cmd.h"
35 #include "pim_memory.h"
36 #include "pim_rp.h"
37 #include "pim_str.h"
38 #include "pim_time.h"
39
40 #include "pim_msdp.h"
41 #include "pim_msdp_packet.h"
42 #include "pim_msdp_socket.h"
43
44 struct pim_msdp pim_msdp, *msdp = &pim_msdp;
45
46 static void pim_msdp_peer_listen(struct pim_msdp_peer *mp);
47 static void pim_msdp_peer_cr_timer_setup(struct pim_msdp_peer *mp, bool start);
48 static void pim_msdp_peer_ka_timer_setup(struct pim_msdp_peer *mp, bool start);
49 static void pim_msdp_peer_hold_timer_setup(struct pim_msdp_peer *mp, bool start);
50 static void pim_msdp_peer_free(struct pim_msdp_peer *mp);
51 static void pim_msdp_enable(void);
52 static void pim_msdp_sa_adv_timer_setup(bool start);
53 static void pim_msdp_sa_deref(struct pim_msdp_sa *sa, enum pim_msdp_sa_flags flags);
54
55 /************************ SA cache management ******************************/
56 char *
57 pim_msdp_sa_key_dump(struct pim_msdp_sa *sa, char *buf, int buf_size, bool long_format)
58 {
59 char rp_str[INET_ADDRSTRLEN];
60
61 if (long_format && (sa->flags & PIM_MSDP_SAF_PEER)) {
62 pim_inet4_dump("<rp?>", sa->rp, rp_str, sizeof(rp_str));
63 snprintf(buf, buf_size, "MSDP SA %s rp %s",
64 pim_str_sg_dump(&sa->sg), rp_str);
65 } else {
66 snprintf(buf, buf_size, "MSDP SA %s", pim_str_sg_dump(&sa->sg));
67 }
68
69 return buf;
70 }
71
72 static void
73 pim_msdp_sa_timer_expiry_log(struct pim_msdp_sa *sa, const char *timer_str)
74 {
75 char key_str[PIM_MSDP_SA_KEY_STRLEN];
76
77 pim_msdp_sa_key_dump(sa, key_str, sizeof(key_str), false);
78 zlog_debug("%s %s timer expired", key_str, timer_str);
79 }
80
81 /* RFC-3618:Sec-5.1 - global active source advertisement timer */
82 static int
83 pim_msdp_sa_adv_timer_cb(struct thread *t)
84 {
85 msdp->sa_adv_timer = NULL;
86 if (PIM_DEBUG_MSDP_INTERNAL) {
87 zlog_debug("MSDP SA advertisment timer expired");
88 }
89
90 pim_msdp_pkt_sa_tx();
91 pim_msdp_sa_adv_timer_setup(true /* start */);
92 return 0;
93 }
94 static void
95 pim_msdp_sa_adv_timer_setup(bool start)
96 {
97 THREAD_OFF(msdp->sa_adv_timer);
98 if (start) {
99 THREAD_TIMER_ON(msdp->master, msdp->sa_adv_timer,
100 pim_msdp_sa_adv_timer_cb, NULL, PIM_MSDP_SA_ADVERTISMENT_TIME);
101 }
102 }
103
104 /* RFC-3618:Sec-5.3 - SA cache state timer */
105 static int
106 pim_msdp_sa_state_timer_cb(struct thread *t)
107 {
108 struct pim_msdp_sa *sa;
109
110 sa = THREAD_ARG(t);
111 sa->sa_state_timer = NULL;
112
113 if (PIM_DEBUG_MSDP_EVENTS) {
114 pim_msdp_sa_timer_expiry_log(sa, "state");
115 }
116
117 pim_msdp_sa_deref(sa, PIM_MSDP_SAF_PEER);
118 return 0;
119 }
120 static void
121 pim_msdp_sa_state_timer_setup(struct pim_msdp_sa *sa, bool start)
122 {
123 THREAD_OFF(sa->sa_state_timer);
124 if (start) {
125 THREAD_TIMER_ON(msdp->master, sa->sa_state_timer,
126 pim_msdp_sa_state_timer_cb, sa, PIM_MSDP_SA_HOLD_TIME);
127 }
128 }
129
130 static void
131 pim_msdp_sa_upstream_del(struct pim_msdp_sa *sa)
132 {
133 struct pim_upstream *up = sa->up;
134 if (!up) {
135 return;
136 }
137
138 sa->up = NULL;
139 /* XXX: we can't pull the plug on an active flow even if the SA entry is
140 * removed. so ideally we want to start the kat in parallel and let the
141 * entry age out; but running the kat has fatal consequences. need to
142 * check with Donald on the best way to go abt this */
143 if (PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up->flags)) {
144 PIM_UPSTREAM_FLAG_UNSET_SRC_MSDP(up->flags);
145 pim_upstream_del(up, __PRETTY_FUNCTION__);
146 }
147
148 if (PIM_DEBUG_MSDP_EVENTS) {
149 char key_str[PIM_MSDP_SA_KEY_STRLEN];
150 pim_msdp_sa_key_dump(sa, key_str, sizeof(key_str), true);
151 zlog_debug("%s de-referenced SPT", key_str);
152 }
153 }
154
155 static bool
156 pim_msdp_sa_upstream_add_ok(struct pim_msdp_sa *sa, struct pim_upstream *xg_up)
157 {
158 if (sa->flags & PIM_MSDP_SAF_LOCAL) {
159 /* if there is a local reference we should NEVER use it for setting up
160 * SPTs otherwise we will get stuck in a simple circular deadlock */
161 return false;
162 }
163
164 if (!(sa->flags & PIM_MSDP_SAF_PEER)) {
165 /* SA should have been rxed from a peer */
166 return false;
167 }
168 /* check if we are RP */
169 if (!I_am_RP(sa->sg.grp)) {
170 return false;
171 }
172
173 /* check if we have a (*, G) with a non-empty immediate OIL */
174 if (!xg_up) {
175 struct prefix_sg sg;
176
177 memset(&sg, 0, sizeof(sg));
178 sg.grp = sa->sg.grp;
179
180 xg_up = pim_upstream_find(&sg);
181 }
182 if (!xg_up || (xg_up->join_state != PIM_UPSTREAM_JOINED)) {
183 /* join desired will be true for such (*, G) entries so we will
184 * just look at join_state and let the PIM state machine do the rest of
185 * the magic */
186 return false;
187 }
188
189 return true;
190 }
191
192 /* Upstream add evaluation needs to happen everytime -
193 * 1. Peer reference is added or removed.
194 * 2. Local reference is added or removed.
195 * 3. The RP for a group changes.
196 * 4. joinDesired for the associated (*, G) changes
197 * 5. associated (*, G) is removed - this seems like a bit redundant
198 * (considering #4); but just in case an entry gets nuked without
199 * upstream state transition
200 * */
201 static void
202 pim_msdp_sa_upstream_update(struct pim_msdp_sa *sa,
203 struct pim_upstream *xg_up, const char *ctx)
204 {
205 struct pim_upstream *up;
206 char key_str[PIM_MSDP_SA_KEY_STRLEN];
207
208 if (PIM_DEBUG_MSDP_EVENTS || PIM_DEBUG_MSDP_INTERNAL) {
209 pim_msdp_sa_key_dump(sa, key_str, sizeof(key_str), true);
210 }
211
212 if (PIM_DEBUG_MSDP_INTERNAL) {
213 zlog_debug("%s upstream update on %s", key_str, ctx);
214 }
215
216 if (!pim_msdp_sa_upstream_add_ok(sa, xg_up)) {
217 pim_msdp_sa_upstream_del(sa);
218 return;
219 }
220
221 if (sa->up) {
222 /* nothing to do */
223 return;
224 }
225
226 up = pim_upstream_find(&sa->sg);
227 if (up && (PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up->flags))) {
228 /* somehow we lost track of the upstream ptr? best log it */
229 sa->up = up;
230 if (PIM_DEBUG_MSDP_EVENTS) {
231 zlog_debug("%s SPT reference missing", key_str);
232 }
233 return;
234 }
235
236 /* RFC3618: "RP triggers a (S, G) join event towards the data source
237 * as if a JP message was rxed addressed to the RP itself." */
238 up = pim_upstream_add(&sa->sg, NULL /* iif */,
239 PIM_UPSTREAM_FLAG_MASK_SRC_MSDP,
240 __PRETTY_FUNCTION__);
241
242 sa->up = up;
243 if (up) {
244 /* update inherited oil */
245 pim_upstream_inherited_olist(up);
246 /* should we also start the kat in parallel? we will need it when the
247 * SA ages out */
248 if (PIM_DEBUG_MSDP_EVENTS) {
249 zlog_debug("%s referenced SPT", key_str);
250 }
251 } else {
252 if (PIM_DEBUG_MSDP_EVENTS) {
253 zlog_debug("%s SPT reference failed", key_str);
254 }
255 }
256 }
257
258 /* release all mem associated with a sa */
259 static void
260 pim_msdp_sa_free(struct pim_msdp_sa *sa)
261 {
262 XFREE(MTYPE_PIM_MSDP_SA, sa);
263 }
264
265 static struct pim_msdp_sa *
266 pim_msdp_sa_new(struct prefix_sg *sg, struct in_addr rp)
267 {
268 struct pim_msdp_sa *sa;
269
270 sa = XCALLOC(MTYPE_PIM_MSDP_SA, sizeof(*sa));
271 if (!sa) {
272 zlog_err("%s: PIM XCALLOC(%zu) failure",
273 __PRETTY_FUNCTION__, sizeof(*sa));
274 return NULL;
275 }
276
277 sa->sg = *sg;
278 sa->rp = rp;
279 sa->uptime = pim_time_monotonic_sec();
280
281 /* insert into misc tables for easy access */
282 sa = hash_get(msdp->sa_hash, sa, hash_alloc_intern);
283 if (!sa) {
284 zlog_err("%s: PIM hash get failure", __PRETTY_FUNCTION__);
285 pim_msdp_sa_free(sa);
286 return NULL;
287 }
288 listnode_add_sort(msdp->sa_list, sa);
289
290 if (PIM_DEBUG_MSDP_EVENTS) {
291 char key_str[PIM_MSDP_SA_KEY_STRLEN];
292
293 pim_msdp_sa_key_dump(sa, key_str, sizeof(key_str), true);
294 zlog_debug("%s created", key_str);
295 }
296
297 return sa;
298 }
299
300 static struct pim_msdp_sa *
301 pim_msdp_sa_find(struct prefix_sg *sg)
302 {
303 struct pim_msdp_sa lookup;
304
305 lookup.sg = *sg;
306 return hash_lookup(msdp->sa_hash, &lookup);
307 }
308
309 static struct pim_msdp_sa *
310 pim_msdp_sa_add(struct prefix_sg *sg, struct in_addr rp)
311 {
312 struct pim_msdp_sa *sa;
313
314 sa = pim_msdp_sa_find(sg);
315 if (sa) {
316 return sa;
317 }
318
319 return pim_msdp_sa_new(sg, rp);
320 }
321
322 static void
323 pim_msdp_sa_del(struct pim_msdp_sa * sa)
324 {
325 /* this is somewhat redundant - still want to be careful not to leave
326 * stale upstream references */
327 pim_msdp_sa_upstream_del(sa);
328
329 /* stop timers */
330 pim_msdp_sa_state_timer_setup(sa, false /* start */);
331
332 /* remove the entry from various tables */
333 listnode_delete(msdp->sa_list, sa);
334 hash_release(msdp->sa_hash, sa);
335
336 if (PIM_DEBUG_MSDP_EVENTS) {
337 char key_str[PIM_MSDP_SA_KEY_STRLEN];
338
339 pim_msdp_sa_key_dump(sa, key_str, sizeof(key_str), true /* long */);
340 zlog_debug("%s deleted", key_str);
341 }
342
343 /* free up any associated memory */
344 pim_msdp_sa_free(sa);
345 }
346
347 /* When a local active-source is removed there is no way to withdraw the
348 * source from peers. We will simply remove it from the SA cache so it will
349 * not be sent in supsequent SA updates. Peers will consequently timeout the
350 * SA.
351 * Similarly a "peer-added" SA is never explicitly deleted. It is simply
352 * aged out overtime if not seen in the SA updates from the peers.
353 * XXX: should we provide a knob to drop entries learnt from a peer when the
354 * peer goes down? */
355 static void
356 pim_msdp_sa_deref(struct pim_msdp_sa *sa, enum pim_msdp_sa_flags flags)
357 {
358 char key_str[PIM_MSDP_SA_KEY_STRLEN];
359
360 pim_msdp_sa_key_dump(sa, key_str, sizeof(key_str), true);
361
362 if ((sa->flags &PIM_MSDP_SAF_LOCAL)) {
363 if (flags & PIM_MSDP_SAF_LOCAL) {
364 zlog_debug("%s local reference removed", key_str);
365 if (msdp->local_cnt)
366 --msdp->local_cnt;
367 }
368 }
369
370 if ((sa->flags &PIM_MSDP_SAF_PEER)) {
371 if (flags & PIM_MSDP_SAF_PEER) {
372 zlog_debug("%s peer reference removed", key_str);
373 pim_msdp_sa_state_timer_setup(sa, false /* start */);
374 }
375 }
376
377 sa->flags &= ~flags;
378 pim_msdp_sa_upstream_update(sa, NULL /* xg_up */, "sa-deref");
379 if (!(sa->flags & PIM_MSDP_SAF_REF)) {
380 pim_msdp_sa_del(sa);
381 }
382 }
383
384 void
385 pim_msdp_sa_ref(struct pim_msdp_peer *mp, struct prefix_sg *sg,
386 struct in_addr rp)
387 {
388 struct pim_msdp_sa *sa;
389 char key_str[PIM_MSDP_SA_KEY_STRLEN];
390
391 sa = pim_msdp_sa_add(sg, rp);
392 if (!sa) {
393 return;
394 }
395
396 if (PIM_DEBUG_MSDP_EVENTS) {
397 pim_msdp_sa_key_dump(sa, key_str, sizeof(key_str), true);
398 }
399
400 /* reference it */
401 if (mp) {
402 if (!(sa->flags & PIM_MSDP_SAF_PEER)) {
403 sa->flags |= PIM_MSDP_SAF_PEER;
404 if (PIM_DEBUG_MSDP_EVENTS) {
405 zlog_debug("%s added by peer", key_str);
406 }
407 }
408 sa->peer = mp->peer;
409 /* start/re-start the state timer to prevent cache expiry */
410 pim_msdp_sa_state_timer_setup(sa, true /* start */);
411 /* We re-evaluate SA "SPT-trigger" everytime we hear abt it from a
412 * peer. XXX: If this becomes too much of a periodic overhead we
413 * can make it event based */
414 pim_msdp_sa_upstream_update(sa, NULL /* xg_up */, "peer-ref");
415 } else {
416 if (!(sa->flags & PIM_MSDP_SAF_LOCAL)) {
417 sa->flags |= PIM_MSDP_SAF_LOCAL;
418 ++msdp->local_cnt;
419 if (PIM_DEBUG_MSDP_EVENTS) {
420 zlog_debug("%s added locally", key_str);
421 }
422 /* send an immediate SA update to peers */
423 pim_msdp_pkt_sa_tx_one(sa);
424 pim_msdp_sa_upstream_update(sa, NULL /* xg_up */, "local-ref");
425 }
426 sa->flags &= ~PIM_MSDP_SAF_STALE;
427 }
428 }
429
430 void
431 pim_msdp_sa_local_add(struct prefix_sg *sg)
432 {
433 struct in_addr rp;
434
435 if (!(msdp->flags & PIM_MSDPF_ENABLE)) {
436 /* if the feature is not enabled do nothing; we will collect all local
437 * sources whenever it is */
438 return;
439 }
440
441 /* check if I am RP for this group. XXX: is this check really needed? */
442 if (!I_am_RP(sg->grp)) {
443 return;
444 }
445 rp.s_addr = 0;
446 pim_msdp_sa_ref(NULL /* mp */, sg, rp);
447 }
448
449 void
450 pim_msdp_sa_local_del(struct prefix_sg *sg)
451 {
452 struct pim_msdp_sa *sa;
453
454 if (!(msdp->flags & PIM_MSDPF_ENABLE)) {
455 /* if the feature is not enabled do nothing; we will collect all local
456 * sources whenever it is */
457 return;
458 }
459
460 sa = pim_msdp_sa_find(sg);
461 if (sa) {
462 pim_msdp_sa_deref(sa, PIM_MSDP_SAF_LOCAL);
463 }
464 }
465
466 static void
467 pim_msdp_sa_local_setup(void)
468 {
469 struct pim_upstream *up;
470 struct listnode *up_node;
471
472 for (ALL_LIST_ELEMENTS_RO(pim_upstream_list, up_node, up)) {
473 if (PIM_UPSTREAM_FLAG_TEST_CREATED_BY_UPSTREAM(up->flags)) {
474 pim_msdp_sa_local_add(&up->sg);
475 }
476 }
477 }
478
479 /* whenever the RP changes we need to re-evaluate the "local" SA-cache */
480 /* XXX: needs to be tested */
481 void
482 pim_msdp_i_am_rp_changed(void)
483 {
484 struct listnode *sanode;
485 struct pim_msdp_sa *sa;
486
487 if (!(msdp->flags & PIM_MSDPF_ENABLE)) {
488 /* if the feature is not enabled do nothing */
489 return;
490 }
491
492 if (PIM_DEBUG_MSDP_INTERNAL) {
493 zlog_debug("MSDP i_am_rp changed");
494 }
495
496 /* mark all local entries as stale */
497 for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) {
498 if (sa->flags & PIM_MSDP_SAF_LOCAL) {
499 sa->flags |= PIM_MSDP_SAF_STALE;
500 }
501 }
502
503 /* re-setup local SA entries */
504 pim_msdp_sa_local_setup();
505
506 for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) {
507 /* purge stale SA entries */
508 if (sa->flags & PIM_MSDP_SAF_STALE) {
509 /* clear the stale flag; the entry may be kept even after
510 * "local-deref" */
511 sa->flags &= ~PIM_MSDP_SAF_STALE;
512 pim_msdp_sa_deref(sa, PIM_MSDP_SAF_LOCAL);
513 }
514 /* also check if we can still influence SPT */
515 pim_msdp_sa_upstream_update(sa, NULL /* xg_up */, "rp-change");
516 }
517 }
518
519 /* We track the join state of (*, G) entries. If G has sources in the SA-cache
520 * we need to setup or teardown SPT when the JoinDesired status changes for
521 * (*, G) */
522 void
523 pim_msdp_up_join_state_changed(struct pim_upstream *xg_up)
524 {
525 struct listnode *sanode;
526 struct pim_msdp_sa *sa;
527
528 if (PIM_DEBUG_MSDP_INTERNAL) {
529 zlog_debug("MSDP join state changed for %s", pim_str_sg_dump(&xg_up->sg));
530 }
531
532 /* If this is not really an XG entry just move on */
533 if ((xg_up->sg.src.s_addr != INADDR_ANY) ||
534 (xg_up->sg.grp.s_addr == INADDR_ANY)) {
535 return;
536 }
537
538 /* XXX: Need to maintain SAs per-group to avoid all this unnecessary
539 * walking */
540 for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) {
541 if (sa->sg.grp.s_addr != xg_up->sg.grp.s_addr) {
542 continue;
543 }
544 pim_msdp_sa_upstream_update(sa, xg_up, "up-jp-change");
545 }
546 }
547
548 /* XXX: Need to maintain SAs per-group to avoid all this unnecessary
549 * walking */
550 void
551 pim_msdp_up_xg_del(struct prefix_sg *sg)
552 {
553 struct listnode *sanode;
554 struct pim_msdp_sa *sa;
555
556 if (PIM_DEBUG_MSDP_INTERNAL) {
557 zlog_debug("MSDP %s del", pim_str_sg_dump(sg));
558 }
559
560 /* If this is not really an XG entry just move on */
561 if ((sg->src.s_addr != INADDR_ANY) ||
562 (sg->grp.s_addr == INADDR_ANY)) {
563 return;
564 }
565
566 /* XXX: Need to maintain SAs per-group to avoid all this unnecessary
567 * walking */
568 for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) {
569 if (sa->sg.grp.s_addr != sg->grp.s_addr) {
570 continue;
571 }
572 pim_msdp_sa_upstream_update(sa, NULL /* xg */, "up-jp-change");
573 }
574 }
575
576 /* sa hash and peer list helpers */
577 static unsigned int
578 pim_msdp_sa_hash_key_make(void *p)
579 {
580 struct pim_msdp_sa *sa = p;
581
582 return (jhash_2words(sa->sg.src.s_addr, sa->sg.grp.s_addr, 0));
583 }
584
585 static int
586 pim_msdp_sa_hash_eq(const void *p1, const void *p2)
587 {
588 const struct pim_msdp_sa *sa1 = p1;
589 const struct pim_msdp_sa *sa2 = p2;
590
591 return ((sa1->sg.src.s_addr == sa2->sg.src.s_addr) &&
592 (sa1->sg.grp.s_addr == sa2->sg.grp.s_addr));
593 }
594
595 static int
596 pim_msdp_sa_comp(const void *p1, const void *p2)
597 {
598 const struct pim_msdp_sa *sa1 = p1;
599 const struct pim_msdp_sa *sa2 = p2;
600
601 if (ntohl(sa1->sg.grp.s_addr) < ntohl(sa2->sg.grp.s_addr))
602 return -1;
603
604 if (ntohl(sa1->sg.grp.s_addr) > ntohl(sa2->sg.grp.s_addr))
605 return 1;
606
607 if (ntohl(sa1->sg.src.s_addr) < ntohl(sa2->sg.src.s_addr))
608 return -1;
609
610 if (ntohl(sa1->sg.src.s_addr) > ntohl(sa2->sg.src.s_addr))
611 return 1;
612
613 return 0;
614 }
615
616 /* RFC-3618:Sec-10.1.3 - Peer-RPF forwarding */
617 /* XXX: this can use a bit of refining and extensions */
618 bool
619 pim_msdp_peer_rpf_check(struct pim_msdp_peer *mp, struct in_addr rp)
620 {
621 if (mp->peer.s_addr == rp.s_addr) {
622 return true;
623 }
624
625 return false;
626 }
627
628 /************************ Peer session management **************************/
629 char *
630 pim_msdp_state_dump(enum pim_msdp_peer_state state, char *buf, int buf_size)
631 {
632 switch (state) {
633 case PIM_MSDP_DISABLED:
634 snprintf(buf, buf_size, "%s", "disabled");
635 break;
636 case PIM_MSDP_INACTIVE:
637 snprintf(buf, buf_size, "%s", "inactive");
638 break;
639 case PIM_MSDP_LISTEN:
640 snprintf(buf, buf_size, "%s", "listen");
641 break;
642 case PIM_MSDP_CONNECTING:
643 snprintf(buf, buf_size, "%s", "connecting");
644 break;
645 case PIM_MSDP_ESTABLISHED:
646 snprintf(buf, buf_size, "%s", "established");
647 break;
648 default:
649 snprintf(buf, buf_size, "unk-%d", state);
650 }
651 return buf;
652 }
653
654 char *
655 pim_msdp_peer_key_dump(struct pim_msdp_peer *mp, char *buf, int buf_size, bool long_format)
656 {
657 char peer_str[INET_ADDRSTRLEN];
658 char local_str[INET_ADDRSTRLEN];
659
660 pim_inet4_dump("<peer?>", mp->peer, peer_str, sizeof(peer_str));
661 if (long_format) {
662 pim_inet4_dump("<local?>", mp->local, local_str, sizeof(local_str));
663 snprintf(buf, buf_size, "MSDP peer %s local %s mg %s",
664 peer_str, local_str, mp->mesh_group_name);
665 } else {
666 snprintf(buf, buf_size, "MSDP peer %s", peer_str);
667 }
668
669 return buf;
670 }
671
672 static void
673 pim_msdp_peer_state_chg_log(struct pim_msdp_peer *mp)
674 {
675 char state_str[PIM_MSDP_STATE_STRLEN];
676 char key_str[PIM_MSDP_PEER_KEY_STRLEN];
677
678 pim_msdp_state_dump(mp->state, state_str, sizeof(state_str));
679 pim_msdp_peer_key_dump(mp, key_str, sizeof(key_str), false);
680 zlog_debug("%s state chg to %s", key_str, state_str);
681 }
682
683 /* MSDP Connection State Machine actions (defined in RFC-3618:Sec-11.2) */
684 /* 11.2.A2: active peer - start connect retry timer; when the timer fires
685 * a tcp connection will be made */
686 static void
687 pim_msdp_peer_connect(struct pim_msdp_peer *mp)
688 {
689 mp->state = PIM_MSDP_CONNECTING;
690 if (PIM_DEBUG_MSDP_EVENTS) {
691 pim_msdp_peer_state_chg_log(mp);
692 }
693
694 pim_msdp_peer_cr_timer_setup(mp, true /* start */);
695 }
696
697 /* 11.2.A3: passive peer - just listen for connections */
698 static void
699 pim_msdp_peer_listen(struct pim_msdp_peer *mp)
700 {
701 mp->state = PIM_MSDP_LISTEN;
702 if (PIM_DEBUG_MSDP_EVENTS) {
703 pim_msdp_peer_state_chg_log(mp);
704 }
705
706 /* this is interntionally asymmetric i.e. we set up listen-socket when the
707 * first listening peer is configured; but don't bother tearing it down when
708 * all the peers go down */
709 pim_msdp_sock_listen();
710 }
711
712 /* 11.2.A4 and 11.2.A5: transition active or passive peer to
713 * established state */
714 void
715 pim_msdp_peer_established(struct pim_msdp_peer *mp)
716 {
717 mp->state = PIM_MSDP_ESTABLISHED;
718 mp->uptime = pim_time_monotonic_sec();
719
720 if (PIM_DEBUG_MSDP_EVENTS) {
721 pim_msdp_peer_state_chg_log(mp);
722 }
723
724 /* stop retry timer on active peers */
725 pim_msdp_peer_cr_timer_setup(mp, false /* start */);
726
727 /* send KA; start KA and hold timers */
728 pim_msdp_pkt_ka_tx(mp);
729 pim_msdp_peer_ka_timer_setup(mp, true /* start */);
730 pim_msdp_peer_hold_timer_setup(mp, true /* start */);
731
732 pim_msdp_pkt_sa_tx_to_one_peer(mp);
733
734 PIM_MSDP_PEER_WRITE_ON(mp);
735 PIM_MSDP_PEER_READ_ON(mp);
736 }
737
738 /* 11.2.A6, 11.2.A7 and 11.2.A8: shutdown the peer tcp connection */
739 void
740 pim_msdp_peer_stop_tcp_conn(struct pim_msdp_peer *mp, bool chg_state)
741 {
742 if (chg_state) {
743 mp->state = PIM_MSDP_INACTIVE;
744 if (PIM_DEBUG_MSDP_EVENTS) {
745 pim_msdp_peer_state_chg_log(mp);
746 }
747 }
748
749 if (PIM_DEBUG_MSDP_INTERNAL) {
750 char key_str[PIM_MSDP_PEER_KEY_STRLEN];
751
752 pim_msdp_peer_key_dump(mp, key_str, sizeof(key_str), false);
753 zlog_debug("%s pim_msdp_peer_stop_tcp_conn", key_str);
754 }
755 /* stop read and write threads */
756 PIM_MSDP_PEER_READ_OFF(mp);
757 PIM_MSDP_PEER_WRITE_OFF(mp);
758
759 /* reset buffers */
760 if (mp->ibuf)
761 stream_reset(mp->ibuf);
762 if (mp->obuf)
763 stream_fifo_clean(mp->obuf);
764
765 /* stop all peer timers */
766 pim_msdp_peer_ka_timer_setup(mp, false /* start */);
767 pim_msdp_peer_cr_timer_setup(mp, false /* start */);
768 pim_msdp_peer_hold_timer_setup(mp, false /* start */);
769
770 /* close connection */
771 if (mp->fd >= 0) {
772 close(mp->fd);
773 mp->fd = -1;
774 }
775 }
776
777 /* RFC-3618:Sec-5.6 - stop the peer tcp connection and startover */
778 void
779 pim_msdp_peer_reset_tcp_conn(struct pim_msdp_peer *mp, const char *rc_str)
780 {
781 if (PIM_DEBUG_EVENTS) {
782 char key_str[PIM_MSDP_PEER_KEY_STRLEN];
783
784 pim_msdp_peer_key_dump(mp, key_str, sizeof(key_str), false);
785 zlog_debug("%s tcp reset %s", key_str, rc_str);
786 }
787
788 /* close the connection and transition to listening or connecting */
789 pim_msdp_peer_stop_tcp_conn(mp, true /* chg_state */);
790 if (PIM_MSDP_PEER_IS_LISTENER(mp)) {
791 pim_msdp_peer_listen(mp);
792 } else {
793 pim_msdp_peer_connect(mp);
794 }
795 }
796
797 static void
798 pim_msdp_peer_timer_expiry_log(struct pim_msdp_peer *mp, const char *timer_str)
799 {
800 char key_str[PIM_MSDP_PEER_KEY_STRLEN];
801
802 pim_msdp_peer_key_dump(mp, key_str, sizeof(key_str), false);
803 zlog_debug("%s %s timer expired", key_str, timer_str);
804 }
805
806 /* RFC-3618:Sec-5.4 - peer hold timer */
807 static int
808 pim_msdp_peer_hold_timer_cb(struct thread *t)
809 {
810 struct pim_msdp_peer *mp;
811
812 mp = THREAD_ARG(t);
813 mp->hold_timer = NULL;
814
815 if (PIM_DEBUG_MSDP_EVENTS) {
816 pim_msdp_peer_timer_expiry_log(mp, "hold");
817 }
818
819 if (mp->state != PIM_MSDP_ESTABLISHED) {
820 return 0;
821 }
822
823 if (PIM_DEBUG_MSDP_EVENTS) {
824 pim_msdp_peer_state_chg_log(mp);
825 }
826 pim_msdp_peer_reset_tcp_conn(mp, "ht-expired");
827 return 0;
828 }
829 static void
830 pim_msdp_peer_hold_timer_setup(struct pim_msdp_peer *mp, bool start)
831 {
832 THREAD_OFF(mp->hold_timer);
833 if (start) {
834 THREAD_TIMER_ON(msdp->master, mp->hold_timer,
835 pim_msdp_peer_hold_timer_cb, mp, PIM_MSDP_PEER_HOLD_TIME);
836 }
837 }
838
839
840 /* RFC-3618:Sec-5.5 - peer keepalive timer */
841 static int
842 pim_msdp_peer_ka_timer_cb(struct thread *t)
843 {
844 struct pim_msdp_peer *mp;
845
846 mp = THREAD_ARG(t);
847 mp->ka_timer = NULL;
848
849 if (PIM_DEBUG_MSDP_EVENTS) {
850 pim_msdp_peer_timer_expiry_log(mp, "ka");
851 }
852
853 pim_msdp_pkt_ka_tx(mp);
854 pim_msdp_peer_ka_timer_setup(mp, true /* start */);
855 return 0;
856 }
857 static void
858 pim_msdp_peer_ka_timer_setup(struct pim_msdp_peer *mp, bool start)
859 {
860 THREAD_OFF(mp->ka_timer);
861 if (start) {
862 THREAD_TIMER_ON(msdp->master, mp->ka_timer,
863 pim_msdp_peer_ka_timer_cb, mp, PIM_MSDP_PEER_KA_TIME);
864 }
865 }
866
867 static void
868 pim_msdp_peer_active_connect(struct pim_msdp_peer *mp)
869 {
870 int rc;
871 rc = pim_msdp_sock_connect(mp);
872
873 if (PIM_DEBUG_MSDP_INTERNAL) {
874 char key_str[PIM_MSDP_PEER_KEY_STRLEN];
875
876 pim_msdp_peer_key_dump(mp, key_str, sizeof(key_str), false);
877 zlog_debug("%s pim_msdp_peer_active_connect: %d", key_str, rc);
878 }
879
880 switch (rc) {
881 case connect_error:
882 /* connect failed restart the connect-retry timer */
883 pim_msdp_peer_cr_timer_setup(mp, true /* start */);
884 break;
885
886 case connect_success:
887 /* connect was sucessful move to established */
888 pim_msdp_peer_established(mp);
889 break;
890
891 case connect_in_progress:
892 /* for NB content we need to wait till sock is readable or
893 * writeable */
894 PIM_MSDP_PEER_WRITE_ON(mp);
895 PIM_MSDP_PEER_READ_ON(mp);
896 /* also restart connect-retry timer to reset the socket if connect is
897 * not sucessful */
898 pim_msdp_peer_cr_timer_setup(mp, true /* start */);
899 break;
900 }
901 }
902
903 /* RFC-3618:Sec-5.6 - connection retry on active peer */
904 static int
905 pim_msdp_peer_cr_timer_cb(struct thread *t)
906 {
907 struct pim_msdp_peer *mp;
908
909 mp = THREAD_ARG(t);
910 mp->cr_timer = NULL;
911
912 if (PIM_DEBUG_MSDP_EVENTS) {
913 pim_msdp_peer_timer_expiry_log(mp, "connect-retry");
914 }
915
916 if (mp->state != PIM_MSDP_CONNECTING || PIM_MSDP_PEER_IS_LISTENER(mp)) {
917 return 0;
918 }
919
920 pim_msdp_peer_active_connect(mp);
921 return 0;
922 }
923 static void
924 pim_msdp_peer_cr_timer_setup(struct pim_msdp_peer *mp, bool start)
925 {
926 THREAD_OFF(mp->cr_timer);
927 if (start) {
928 THREAD_TIMER_ON(msdp->master, mp->cr_timer,
929 pim_msdp_peer_cr_timer_cb, mp, PIM_MSDP_PEER_CONNECT_RETRY_TIME);
930 }
931 }
932
933 /* if a valid packet is rxed from the peer we can restart hold timer */
934 void
935 pim_msdp_peer_pkt_rxed(struct pim_msdp_peer *mp)
936 {
937 if (mp->state == PIM_MSDP_ESTABLISHED) {
938 pim_msdp_peer_hold_timer_setup(mp, true /* start */);
939 }
940 }
941
942 /* if a valid packet is txed to the peer we can restart ka timer and avoid
943 * unnecessary ka noise in the network */
944 void
945 pim_msdp_peer_pkt_txed(struct pim_msdp_peer *mp)
946 {
947 if (mp->state == PIM_MSDP_ESTABLISHED) {
948 pim_msdp_peer_ka_timer_setup(mp, true /* start */);
949 }
950 }
951
952 static void pim_msdp_addr2su(union sockunion *su, struct in_addr addr)
953 {
954 sockunion_init(su);
955 su->sin.sin_addr = addr;
956 su->sin.sin_family = AF_INET;
957 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
958 su->sin.sin_len = sizeof(struct sockaddr_in);
959 #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
960 }
961
962 /* 11.2.A1: create a new peer and transition state to listen or connecting */
963 static enum pim_msdp_err
964 pim_msdp_peer_new(struct in_addr peer_addr, struct in_addr local_addr,
965 const char *mesh_group_name)
966 {
967 struct pim_msdp_peer *mp;
968
969 pim_msdp_enable();
970
971 mp = XCALLOC(MTYPE_PIM_MSDP_PEER, sizeof(*mp));
972 if (!mp) {
973 zlog_err("%s: PIM XCALLOC(%zu) failure",
974 __PRETTY_FUNCTION__, sizeof(*mp));
975 return PIM_MSDP_ERR_OOM;
976 }
977
978 mp->peer = peer_addr;
979 pim_msdp_addr2su(&mp->su_peer, mp->peer);
980 mp->local = local_addr;
981 /* XXX: originator_id setting needs to move to the mesh group */
982 msdp->originator_id = local_addr;
983 pim_msdp_addr2su(&mp->su_local, mp->local);
984 mp->mesh_group_name = XSTRDUP(MTYPE_PIM_MSDP_PEER_MG_NAME, mesh_group_name);
985 mp->state = PIM_MSDP_INACTIVE;
986 mp->fd = -1;
987 /* higher IP address is listener */
988 if (ntohl(mp->local.s_addr) > ntohl(mp->peer.s_addr)) {
989 mp->flags |= PIM_MSDP_PEERF_LISTENER;
990 }
991
992 /* setup packet buffers */
993 mp->ibuf = stream_new(PIM_MSDP_MAX_PACKET_SIZE);
994 mp->obuf = stream_fifo_new();
995
996 /* insert into misc tables for easy access */
997 mp = hash_get(msdp->peer_hash, mp, hash_alloc_intern);
998 if (!mp) {
999 zlog_err("%s: PIM hash get failure", __PRETTY_FUNCTION__);
1000 pim_msdp_peer_free(mp);
1001 return PIM_MSDP_ERR_OOM;
1002 }
1003 listnode_add_sort(msdp->peer_list, mp);
1004
1005 if (PIM_DEBUG_MSDP_EVENTS) {
1006 char key_str[PIM_MSDP_PEER_KEY_STRLEN];
1007
1008 pim_msdp_peer_key_dump(mp, key_str, sizeof(key_str), true);
1009 zlog_debug("%s created", key_str);
1010
1011 pim_msdp_peer_state_chg_log(mp);
1012 }
1013
1014 /* fireup the connect state machine */
1015 if (PIM_MSDP_PEER_IS_LISTENER(mp)) {
1016 pim_msdp_peer_listen(mp);
1017 } else {
1018 pim_msdp_peer_connect(mp);
1019 }
1020 return PIM_MSDP_ERR_NONE;
1021 }
1022
1023 struct pim_msdp_peer *
1024 pim_msdp_peer_find(struct in_addr peer_addr)
1025 {
1026 struct pim_msdp_peer lookup;
1027
1028 lookup.peer = peer_addr;
1029 return hash_lookup(msdp->peer_hash, &lookup);
1030 }
1031
1032 /* add peer configuration if it doesn't already exist */
1033 enum pim_msdp_err
1034 pim_msdp_peer_add(struct in_addr peer_addr, struct in_addr local_addr,
1035 const char *mesh_group_name)
1036 {
1037 struct pim_msdp_peer *mp;
1038
1039 mp = pim_msdp_peer_find(peer_addr);
1040 if (mp) {
1041 return PIM_MSDP_ERR_PEER_EXISTS;
1042 }
1043
1044 return pim_msdp_peer_new(peer_addr, local_addr, mesh_group_name);
1045 }
1046
1047 /* release all mem associated with a peer */
1048 static void
1049 pim_msdp_peer_free(struct pim_msdp_peer *mp)
1050 {
1051 if (mp->ibuf) {
1052 stream_free(mp->ibuf);
1053 }
1054
1055 if (mp->obuf) {
1056 stream_fifo_free(mp->obuf);
1057 }
1058
1059 if (mp->mesh_group_name) {
1060 XFREE(MTYPE_PIM_MSDP_PEER_MG_NAME, mp->mesh_group_name);
1061 }
1062 XFREE(MTYPE_PIM_MSDP_PEER, mp);
1063 }
1064
1065 /* delete the peer config */
1066 enum pim_msdp_err
1067 pim_msdp_peer_del(struct in_addr peer_addr)
1068 {
1069 struct pim_msdp_peer *mp;
1070
1071 mp = pim_msdp_peer_find(peer_addr);
1072 if (!mp) {
1073 return PIM_MSDP_ERR_NO_PEER;
1074 }
1075
1076 /* stop the tcp connection and shutdown all timers */
1077 pim_msdp_peer_stop_tcp_conn(mp, true /* chg_state */);
1078
1079 /* remove the session from various tables */
1080 listnode_delete(msdp->peer_list, mp);
1081 hash_release(msdp->peer_hash, mp);
1082
1083 if (PIM_DEBUG_MSDP_EVENTS) {
1084 char key_str[PIM_MSDP_PEER_KEY_STRLEN];
1085
1086 pim_msdp_peer_key_dump(mp, key_str, sizeof(key_str), true);
1087 zlog_debug("%s deleted", key_str);
1088 }
1089
1090 /* free up any associated memory */
1091 pim_msdp_peer_free(mp);
1092
1093 return PIM_MSDP_ERR_NONE;
1094 }
1095
1096 /* peer hash and peer list helpers */
1097 static unsigned int
1098 pim_msdp_peer_hash_key_make(void *p)
1099 {
1100 struct pim_msdp_peer *mp = p;
1101 return (jhash_1word(mp->peer.s_addr, 0));
1102 }
1103
1104 static int
1105 pim_msdp_peer_hash_eq(const void *p1, const void *p2)
1106 {
1107 const struct pim_msdp_peer *mp1 = p1;
1108 const struct pim_msdp_peer *mp2 = p2;
1109
1110 return (mp1->peer.s_addr == mp2->peer.s_addr);
1111 }
1112
1113 static int
1114 pim_msdp_peer_comp(const void *p1, const void *p2)
1115 {
1116 const struct pim_msdp_peer *mp1 = p1;
1117 const struct pim_msdp_peer *mp2 = p2;
1118
1119 if (ntohl(mp1->peer.s_addr) < ntohl(mp2->peer.s_addr))
1120 return -1;
1121
1122 if (ntohl(mp1->peer.s_addr) > ntohl(mp2->peer.s_addr))
1123 return 1;
1124
1125 return 0;
1126 }
1127
1128 /*********************** MSDP feature APIs *********************************/
1129 int
1130 pim_msdp_config_write(struct vty *vty)
1131 {
1132 struct listnode *mpnode;
1133 struct pim_msdp_peer *mp;
1134 char peer_str[INET_ADDRSTRLEN];
1135 char local_str[INET_ADDRSTRLEN];
1136 int count = 0;
1137
1138 for (ALL_LIST_ELEMENTS_RO(msdp->peer_list, mpnode, mp)) {
1139 pim_inet4_dump("<peer?>", mp->peer, peer_str, sizeof(peer_str));
1140 pim_inet4_dump("<local?>", mp->local, local_str, sizeof(local_str));
1141 vty_out(vty, "ip msdp peer %s source %s%s",
1142 peer_str, local_str, VTY_NEWLINE);
1143 ++count;
1144 }
1145 return count;
1146 }
1147
1148 /* Enable feature including active/periodic timers etc. on the first peer
1149 * config. Till then MSDP should just stay quiet. */
1150 static void
1151 pim_msdp_enable(void)
1152 {
1153 if (msdp->flags & PIM_MSDPF_ENABLE) {
1154 /* feature is already enabled */
1155 return;
1156 }
1157 msdp->flags |= PIM_MSDPF_ENABLE;
1158 msdp->work_obuf = stream_new(PIM_MSDP_MAX_PACKET_SIZE);
1159 pim_msdp_sa_adv_timer_setup(true /* start */);
1160 /* setup sa cache based on local sources */
1161 pim_msdp_sa_local_setup();
1162 }
1163
1164 /* MSDP init */
1165 void
1166 pim_msdp_init(struct thread_master *master)
1167 {
1168 /* XXX: temporarily enable noisy logs; will be disabled once dev is
1169 * complete */
1170 PIM_DO_DEBUG_MSDP_INTERNAL;
1171
1172 msdp->master = master;
1173
1174 msdp->peer_hash = hash_create(pim_msdp_peer_hash_key_make,
1175 pim_msdp_peer_hash_eq);
1176 msdp->peer_list = list_new();
1177 msdp->peer_list->del = (void (*)(void *))pim_msdp_peer_free;
1178 msdp->peer_list->cmp = (int (*)(void *, void *))pim_msdp_peer_comp;
1179
1180 msdp->sa_hash = hash_create(pim_msdp_sa_hash_key_make,
1181 pim_msdp_sa_hash_eq);
1182 msdp->sa_list = list_new();
1183 msdp->sa_list->del = (void (*)(void *))pim_msdp_sa_free;
1184 msdp->sa_list->cmp = (int (*)(void *, void *))pim_msdp_sa_comp;
1185 }
1186
1187 /* counterpart to MSDP init; XXX: unused currently */
1188 void
1189 pim_msdp_exit(void)
1190 {
1191 /* XXX: stop listener and delete all peer sessions */
1192
1193 if (msdp->peer_hash) {
1194 hash_free(msdp->peer_hash);
1195 msdp->peer_hash = NULL;
1196 }
1197
1198 if (msdp->peer_list) {
1199 list_free(msdp->peer_list);
1200 msdp->peer_list = NULL;
1201 }
1202 }