]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_nht.c
pimd: Nexthop tracking support
[mirror_frr.git] / pimd / pim_nht.c
CommitLineData
1bc98276
CS
1/*
2 * PIM for Quagga
3 * Copyright (C) 2017 Cumulus Networks, Inc.
4 * Chirag Shah
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; see the file COPYING; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
19 * MA 02110-1301 USA
20 */
21#include <zebra.h>
22#include "network.h"
23#include "zclient.h"
24#include "stream.h"
25#include "nexthop.h"
26#include "if.h"
27#include "hash.h"
28#include "jhash.h"
29
30#include "pimd.h"
31#include "pimd/pim_nht.h"
32#include "log.h"
33#include "pim_time.h"
34#include "pim_oil.h"
35#include "pim_ifchannel.h"
36#include "pim_mroute.h"
37#include "pim_zebra.h"
38#include "pim_upstream.h"
39#include "pim_join.h"
40#include "pim_jp_agg.h"
41#include "pim_zebra.h"
42
43/**
44 * pim_sendmsg_zebra_rnh -- Format and send a nexthop register/Unregister
45 * command to Zebra.
46 */
47static void
48pim_sendmsg_zebra_rnh (struct zclient *zclient, struct pim_nexthop_cache *pnc,
49 int command)
50{
51 struct stream *s;
52 struct prefix *p;
53 int ret;
54
55 /* Check socket. */
56 if (!zclient || zclient->sock < 0)
57 return;
58
59 p = &(pnc->rpf.rpf_addr);
60 s = zclient->obuf;
61 stream_reset (s);
62 zclient_create_header (s, command, VRF_DEFAULT);
63 /* get update for all routes for a prefix */
64 stream_putc (s, 0);
65
66 stream_putw (s, PREFIX_FAMILY (p));
67 stream_putc (s, p->prefixlen);
68 switch (PREFIX_FAMILY (p))
69 {
70 case AF_INET:
71 stream_put_in_addr (s, &p->u.prefix4);
72 break;
73 case AF_INET6:
74 stream_put (s, &(p->u.prefix6), 16);
75 break;
76 default:
77 break;
78 }
79 stream_putw_at (s, 0, stream_get_endp (s));
80
81 ret = zclient_send_message (zclient);
82 if (ret < 0)
83 zlog_warn ("sendmsg_nexthop: zclient_send_message() failed");
84
85
86 if (PIM_DEBUG_TRACE)
87 {
88 char buf[PREFIX2STR_BUFFER];
89 prefix2str (p, buf, sizeof (buf));
90 zlog_debug ("%s: NHT Addr %s %sregistered with Zebra ret:%d ",
91 __PRETTY_FUNCTION__, buf,
92 (command == ZEBRA_NEXTHOP_REGISTER) ? " " : "de", ret);
93 }
94
95 return;
96}
97
98struct pim_nexthop_cache *
99pim_nexthop_cache_find (struct pim_rpf *rpf)
100{
101 struct pim_nexthop_cache *pnc = NULL;
102 struct pim_nexthop_cache lookup;
103
104 lookup.rpf.rpf_addr.family = rpf->rpf_addr.family;
105 lookup.rpf.rpf_addr.prefixlen = rpf->rpf_addr.prefixlen;
106 lookup.rpf.rpf_addr.u.prefix4.s_addr = rpf->rpf_addr.u.prefix4.s_addr;
107
108 pnc = hash_lookup (pimg->rpf_hash, &lookup);
109
110 return pnc;
111
112}
113
114static int
115pim_rp_list_cmp (void *v1, void *v2)
116{
117 struct rp_info *rp1 = (struct rp_info *) v1;
118 struct rp_info *rp2 = (struct rp_info *) v2;
119
120 if (rp1 == rp2)
121 return 0;
122
123 if (!rp1 && rp2)
124 return -1;
125
126 if (rp1 && !rp2)
127 return 1;
128
129 /*
130 * Sort by RP IP address
131 */
132 if (rp1->rp.rpf_addr.u.prefix4.s_addr < rp2->rp.rpf_addr.u.prefix4.s_addr)
133 return -1;
134
135 if (rp1->rp.rpf_addr.u.prefix4.s_addr > rp2->rp.rpf_addr.u.prefix4.s_addr)
136 return 1;
137
138 /*
139 * Sort by group IP address
140 */
141 if (rp1->group.u.prefix4.s_addr < rp2->group.u.prefix4.s_addr)
142 return -1;
143
144 if (rp1->group.u.prefix4.s_addr > rp2->group.u.prefix4.s_addr)
145 return 1;
146
147 return -1;
148}
149
150struct pim_nexthop_cache *
151pim_nexthop_cache_add (struct pim_rpf *rpf_addr)
152{
153 struct pim_nexthop_cache *pnc;
154
155 pnc = XCALLOC (MTYPE_PIM_NEXTHOP_CACHE, sizeof (struct pim_nexthop_cache));
156 if (!pnc)
157 {
158 zlog_err ("%s: NHT PIM XCALLOC failure ", __PRETTY_FUNCTION__);
159 return NULL;
160 }
161 pnc->rpf.rpf_addr.family = rpf_addr->rpf_addr.family;
162 pnc->rpf.rpf_addr.prefixlen = rpf_addr->rpf_addr.prefixlen;
163 pnc->rpf.rpf_addr.u.prefix4.s_addr = rpf_addr->rpf_addr.u.prefix4.s_addr;
164
165 pnc = hash_get (pimg->rpf_hash, pnc, hash_alloc_intern);
166
167 pnc->rp_list = list_new ();
168 pnc->rp_list->cmp = pim_rp_list_cmp;
169
170 pnc->upstream_list = list_new ();
171 pnc->upstream_list->cmp = pim_upstream_compare;
172
173 if (PIM_DEBUG_ZEBRA)
174 {
175 char rpf_str[PREFIX_STRLEN];
176 pim_addr_dump ("<nht?>", &rpf_addr->rpf_addr, rpf_str,
177 sizeof (rpf_str));
178 zlog_debug ("%s: NHT hash node, RP and UP lists allocated for %s ",
179 __PRETTY_FUNCTION__, rpf_str);
180 }
181
182 return pnc;
183}
184
185/* This API is used to Register an address with Zebra */
186int
187pim_find_or_track_nexthop (struct prefix *addr, struct pim_upstream *up,
188 struct rp_info *rp)
189{
190 struct pim_nexthop_cache *pnc = NULL;
191 struct pim_rpf rpf;
192 struct listnode *ch_node = NULL;
193 struct zclient *zclient = NULL;
194
195 zclient = pim_zebra_zclient_get ();
196 memset (&rpf, 0, sizeof (struct pim_rpf));
197 rpf.rpf_addr.family = addr->family;
198 rpf.rpf_addr.prefixlen = addr->prefixlen;
199 rpf.rpf_addr.u.prefix4 = addr->u.prefix4;
200
201 pnc = pim_nexthop_cache_find (&rpf);
202 if (!pnc)
203 {
204 if (PIM_DEBUG_ZEBRA)
205 {
206 char buf[PREFIX2STR_BUFFER];
207 prefix2str (&rpf.rpf_addr, buf, sizeof (buf));
208 zlog_debug ("%s: NHT New PNC allocated for addr %s ",
209 __PRETTY_FUNCTION__, buf);
210 }
211 pnc = pim_nexthop_cache_add (&rpf);
212 if (pnc)
213 pim_sendmsg_zebra_rnh (zclient, pnc,
214 ZEBRA_NEXTHOP_REGISTER);
215 else
216 {
217 zlog_warn ("%s: pnc node allocation failed. ", __PRETTY_FUNCTION__);
218 }
219 }
220
221 if (rp != NULL)
222 {
223 ch_node = listnode_lookup (pnc->rp_list, rp);
224 if (ch_node == NULL)
225 {
226 if (PIM_DEBUG_ZEBRA)
227 {
228 char rp_str[PREFIX_STRLEN];
229 pim_addr_dump ("<rp?>", &rp->rp.rpf_addr, rp_str,
230 sizeof (rp_str));
231 zlog_debug ("%s: NHT add RP %s node to cached list",
232 __PRETTY_FUNCTION__, rp_str);
233 }
234 listnode_add_sort (pnc->rp_list, rp);
235 }
236 }
237
238 if (up != NULL)
239 {
240 ch_node = listnode_lookup (pnc->upstream_list, up);
241 if (ch_node == NULL)
242 {
243 if (PIM_DEBUG_ZEBRA)
244 {
245 char buf[PREFIX2STR_BUFFER];
246 prefix2str (addr, buf, sizeof (buf));
247 zlog_debug
248 ("%s: NHT add upstream %s node to cached list, rpf %s",
249 __PRETTY_FUNCTION__, up->sg_str, buf);
250 }
251 listnode_add_sort (pnc->upstream_list, up);
252 }
253 }
254
255 if (CHECK_FLAG (pnc->flags, PIM_NEXTHOP_VALID))
256 return 1;
257
258 return 0;
259}
260
261void
262pim_delete_tracked_nexthop (struct prefix *addr, struct pim_upstream *up,
263 struct rp_info *rp)
264{
265 struct pim_nexthop_cache *pnc = NULL;
266 struct pim_nexthop_cache lookup;
267 struct zclient *zclient = NULL;
268
269 zclient = pim_zebra_zclient_get ();
270
271 /* Remove from RPF hash if it is the last entry */
272 lookup.rpf.rpf_addr = *addr;
273 pnc = hash_lookup (pimg->rpf_hash, &lookup);
274 if (pnc)
275 {
276 if (rp)
277 listnode_delete (pnc->rp_list, rp);
278 if (up)
279 listnode_delete (pnc->upstream_list, up);
280
281 if (PIM_DEBUG_ZEBRA)
282 zlog_debug ("%s: NHT rp_list count:%d upstream_list count:%d ",
283 __PRETTY_FUNCTION__, pnc->rp_list->count,
284 pnc->upstream_list->count);
285
286 if (pnc->rp_list->count == 0 && pnc->upstream_list->count == 0)
287 {
288 pim_sendmsg_zebra_rnh (zclient, pnc,
289 ZEBRA_NEXTHOP_UNREGISTER);
290
291 list_delete (pnc->rp_list);
292 list_delete (pnc->upstream_list);
293
294 hash_release (pimg->rpf_hash, pnc);
295 if (pnc->nexthop)
296 nexthops_free (pnc->nexthop);
297 XFREE (MTYPE_PIM_NEXTHOP_CACHE, pnc);
298 }
299 }
300}
301
302/* Update RP nexthop info based on Nexthop update received from Zebra.*/
303static int
304pim_update_rp_nh (struct pim_nexthop_cache *pnc)
305{
306 struct listnode *node = NULL;
307 struct rp_info *rp_info = NULL;
308 int ret = 0;
309
310 /*Traverse RP list and update each RP Nexthop info */
311 for (ALL_LIST_ELEMENTS_RO (pnc->rp_list, node, rp_info))
312 {
313 if (rp_info->rp.rpf_addr.u.prefix4.s_addr == INADDR_NONE)
314 continue;
315
316 if (pim_nexthop_lookup (&rp_info->rp.source_nexthop,
317 rp_info->rp.rpf_addr.u.prefix4, 1) != 0)
318 {
319 if (PIM_DEBUG_PIM_TRACE)
320 zlog_debug ("Unable to lookup nexthop for rp specified");
321 ret++;
322 continue;
323 }
324
325 if (PIM_DEBUG_TRACE)
326 {
327 char rp_str[PREFIX_STRLEN];
328 pim_addr_dump ("<rp?>", &rp_info->rp.rpf_addr, rp_str,
329 sizeof (rp_str));
330 zlog_debug ("%s: NHT update nexthop for RP %s to interface %s ",
331 __PRETTY_FUNCTION__, rp_str,
332 rp_info->rp.source_nexthop.interface->name);
333 }
334 }
335
336 if (ret)
337 return 0;
338
339 return 1;
340
341}
342
343/* Update Upstream nexthop info based on Nexthop update received from Zebra.*/
344static int
345pim_update_upstream_nh (struct pim_nexthop_cache *pnc)
346{
347 struct listnode *up_node;
348 struct listnode *ifnode;
349 struct listnode *up_nextnode;
350 struct listnode *node;
351 struct pim_upstream *up;
352 struct interface *ifp;
353 int vif_index = 0;
354
355 for (ALL_LIST_ELEMENTS (pnc->upstream_list, up_node, up_nextnode, up))
356 {
357 enum pim_rpf_result rpf_result;
358 struct pim_rpf old;
359
360 if (up == NULL)
361 {
362 zlog_debug ("%s: Upstream node is NULL ", __PRETTY_FUNCTION__);
363 continue;
364 }
365
366 old.source_nexthop.interface = up->rpf.source_nexthop.interface;
367 rpf_result = pim_rpf_update (up, &old, 0);
368 if (rpf_result == PIM_RPF_FAILURE)
369 continue;
370
371 if (rpf_result == PIM_RPF_CHANGED)
372 {
373
374 /*
375 * We have detected a case where we might need to rescan
376 * the inherited o_list so do it.
377 */
378 if (up->channel_oil && up->channel_oil->oil_inherited_rescan)
379 {
380 pim_upstream_inherited_olist_decide (up);
381 up->channel_oil->oil_inherited_rescan = 0;
382 }
383
384 if (up->join_state == PIM_UPSTREAM_JOINED)
385 {
386 /*
387 * If we come up real fast we can be here
388 * where the mroute has not been installed
389 * so install it.
390 */
391 if (up->channel_oil && !up->channel_oil->installed)
392 pim_mroute_add (up->channel_oil, __PRETTY_FUNCTION__);
393
394 /*
395 RFC 4601: 4.5.7. Sending (S,G) Join/Prune Messages
396
397 Transitions from Joined State
398
399 RPF'(S,G) changes not due to an Assert
400
401 The upstream (S,G) state machine remains in Joined
402 state. Send Join(S,G) to the new upstream neighbor, which is
403 the new value of RPF'(S,G). Send Prune(S,G) to the old
404 upstream neighbor, which is the old value of RPF'(S,G). Set
405 the Join Timer (JT) to expire after t_periodic seconds.
406 */
407 pim_jp_agg_switch_interface (&old, &up->rpf, up);
408
409 pim_upstream_join_timer_restart (up, &old);
410 } /* up->join_state == PIM_UPSTREAM_JOINED */
411
412 /* FIXME can join_desired actually be changed by pim_rpf_update()
413 returning PIM_RPF_CHANGED ? */
414 pim_upstream_update_join_desired (up);
415
416 } /* PIM_RPF_CHANGED */
417
418 if (PIM_DEBUG_TRACE)
419 {
420 zlog_debug ("%s: NHT upstream %s old ifp %s new ifp %s",
421 __PRETTY_FUNCTION__, up->sg_str,
422 old.source_nexthop.interface->name,
423 up->rpf.source_nexthop.interface->name);
424 }
425 /* update kernel multicast forwarding cache (MFC) */
426 if (up->channel_oil)
427 {
428 vif_index =
429 pim_if_find_vifindex_by_ifindex (up->rpf.
430 source_nexthop.interface->
431 ifindex);
432 /* Pass Current selected NH vif index to mroute download */
433 if (vif_index)
434 pim_scan_individual_oil (up->channel_oil, vif_index);
435 else
436 {
437 if (PIM_DEBUG_ZEBRA)
438 zlog_debug ("%s: NHT upstream %s channel_oil IIF %s vif_index is not valid",
439 __PRETTY_FUNCTION__, up->sg_str,
440 up->rpf.source_nexthop.interface->name);
441 }
442 }
443
444 } /* for (pnc->upstream_list) */
445
446 for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp))
447 if (ifp->info)
448 {
449 struct pim_interface *pim_ifp = ifp->info;
450 struct pim_iface_upstream_switch *us;
451
452 for (ALL_LIST_ELEMENTS_RO (pim_ifp->upstream_switch_list, node, us))
453 {
454 struct pim_rpf rpf;
455 rpf.source_nexthop.interface = ifp;
456 rpf.rpf_addr.u.prefix4 = us->address;
457 pim_joinprune_send (&rpf, us->us);
458 pim_jp_agg_clear_group (us->us);
459 }
460 }
461
462 return 0;
463}
464
465/* This API is used to parse Registered address nexthop update coming from Zebra */
466void
467pim_parse_nexthop_update (struct zclient *zclient, int command,
468 vrf_id_t vrf_id)
469{
470 struct stream *s;
471 struct prefix p;
472 struct nexthop *nexthop;
473 struct nexthop *oldnh;
474 struct nexthop *nhlist_head = NULL;
475 struct nexthop *nhlist_tail = NULL;
476 uint32_t metric, distance;
477 u_char nexthop_num = 0;
478 int i;
479 struct pim_rpf rpf;
480 struct pim_nexthop_cache *pnc = NULL;
481 struct pim_neighbor *nbr = NULL;
482 struct interface *ifp = NULL;
483
484 s = zclient->ibuf;
485 memset (&p, 0, sizeof (struct prefix));
486 p.family = stream_getw (s);
487 p.prefixlen = stream_getc (s);
488 switch (p.family)
489 {
490 case AF_INET:
491 p.u.prefix4.s_addr = stream_get_ipv4 (s);
492 break;
493 case AF_INET6:
494 stream_get (&p.u.prefix6, s, 16);
495 break;
496 default:
497 break;
498 }
499
500 if (command == ZEBRA_NEXTHOP_UPDATE)
501 {
502 rpf.rpf_addr.family = p.family;
503 rpf.rpf_addr.prefixlen = p.prefixlen;
504 rpf.rpf_addr.u.prefix4.s_addr = p.u.prefix4.s_addr;
505 pnc = pim_nexthop_cache_find (&rpf);
506 if (!pnc)
507 {
508 if (PIM_DEBUG_TRACE)
509 {
510 char buf[PREFIX2STR_BUFFER];
511 prefix2str (&rpf.rpf_addr, buf, sizeof (buf));
512 zlog_debug ("%s: NHT addr %s is not in local cached DB.",
513 __PRETTY_FUNCTION__, buf);
514 }
515 return;
516 }
517 }
518
519 pnc->last_update = pim_time_monotonic_sec ();
520 distance = stream_getc (s);
521 metric = stream_getl (s);
522 nexthop_num = stream_getc (s);
523
524 if (PIM_DEBUG_TRACE)
525 {
526 char buf[PREFIX2STR_BUFFER];
527 prefix2str (&p, buf, sizeof (buf));
528 zlog_debug ("%s: NHT Update for %s nexthop_num %d vrf:%d upcount %d rpcount %d",
529 __PRETTY_FUNCTION__, buf, nexthop_num, vrf_id,
530 listcount (pnc->upstream_list), listcount (pnc->rp_list));
531 }
532
533 if (nexthop_num)
534 {
535 pnc->flags |= PIM_NEXTHOP_VALID;
536 pnc->distance = distance;
537 pnc->metric = metric;
538 pnc->nexthop_num = nexthop_num;
539
540 for (i = 0; i < nexthop_num; i++)
541 {
542 nexthop = nexthop_new ();
543 nexthop->type = stream_getc (s);
544 switch (nexthop->type)
545 {
546 case NEXTHOP_TYPE_IPV4:
547 nexthop->gate.ipv4.s_addr = stream_get_ipv4 (s);
548 nexthop->ifindex = stream_getl (s);
549 break;
550 case NEXTHOP_TYPE_IFINDEX:
551 nexthop->ifindex = stream_getl (s);
552 break;
553 case NEXTHOP_TYPE_IPV4_IFINDEX:
554 nexthop->gate.ipv4.s_addr = stream_get_ipv4 (s);
555 nexthop->ifindex = stream_getl (s);
556 break;
557 case NEXTHOP_TYPE_IPV6:
558 stream_get (&nexthop->gate.ipv6, s, 16);
559 break;
560 case NEXTHOP_TYPE_IPV6_IFINDEX:
561 stream_get (&nexthop->gate.ipv6, s, 16);
562 nexthop->ifindex = stream_getl (s);
563 nbr =
564 pim_neighbor_find_if (if_lookup_by_index_vrf
565 (nexthop->ifindex, VRF_DEFAULT));
566 /* Overwrite with Nbr address as NH addr */
567 if (nbr)
568 nexthop->gate.ipv4 = nbr->source_addr;
569
570 break;
571 default:
572 /* do nothing */
573 break;
574 }
575
576 if (PIM_DEBUG_TRACE)
577 {
578 char p_str[PREFIX2STR_BUFFER];
579 prefix2str (&p, p_str, sizeof (p_str));
580 zlog_debug ("%s: NHT addr %s %d-nhop via %s type %d",
581 __PRETTY_FUNCTION__, p_str, i + 1,
582 inet_ntoa (nexthop->gate.ipv4), nexthop->type);
583 }
584
585 ifp = if_lookup_by_index (nexthop->ifindex);
586 if (!ifp)
587 {
588 if (PIM_DEBUG_ZEBRA)
589 {
590 char buf[NEXTHOP_STRLEN];
591 zlog_debug("%s: could not find interface for ifindex %d (addr %s)",
592 __PRETTY_FUNCTION__,
593 nexthop->ifindex, nexthop2str (nexthop, buf, sizeof (buf)));
594 }
595 nexthop_free (nexthop);
596 continue;
597 }
598
599 if (!ifp->info)
600 {
601 if (PIM_DEBUG_ZEBRA)
602 {
603 char buf[NEXTHOP_STRLEN];
604 zlog_debug
605 ("%s: multicast not enabled on input interface %s (ifindex=%d, addr %s)",
606 __PRETTY_FUNCTION__, ifp->name, nexthop->ifindex,
607 nexthop2str (nexthop, buf, sizeof (buf)));
608 }
609 nexthop_free (nexthop);
610 continue;
611 }
612
613 if (nhlist_tail)
614 {
615 nhlist_tail->next = nexthop;
616 nhlist_tail = nexthop;
617 }
618 else
619 {
620 nhlist_tail = nexthop;
621 nhlist_head = nexthop;
622 }
623
624 for (oldnh = pnc->nexthop; oldnh; oldnh = oldnh->next)
625 if (nexthop_same_no_recurse (oldnh, nexthop))
626 break;
627 }
628 /* Reset existing pnc->nexthop before assigning new list */
629 nexthops_free (pnc->nexthop);
630 pnc->nexthop = nhlist_head;
631 }
632 else
633 {
634 pnc->flags &= ~PIM_NEXTHOP_VALID;
635 pnc->nexthop_num = nexthop_num;
636 nexthops_free (pnc->nexthop);
637 pnc->nexthop = NULL;
638 }
639
640 pim_rpf_set_refresh_time ();
641
642 if (listcount (pnc->rp_list))
643 pim_update_rp_nh (pnc);
644 if (listcount (pnc->upstream_list))
645 pim_update_upstream_nh (pnc);
646
647}