1 // SPDX-License-Identifier: GPL-2.0-or-later
4 * Copyright (C) 2008 Everton da Silva Marques
17 #include "pim_instance.h"
21 #include "pim_iface.h"
22 #include "pim_neighbor.h"
23 #include "pim_zlookup.h"
24 #include "pim_ifchannel.h"
30 static pim_addr
pim_rpf_find_rpf_addr(struct pim_upstream
*up
);
32 void pim_rpf_set_refresh_time(struct pim_instance
*pim
)
34 pim
->last_route_change_time
= pim_time_monotonic_usec();
35 if (PIM_DEBUG_PIM_TRACE
)
36 zlog_debug("%s: vrf(%s) New last route change time: %" PRId64
,
37 __func__
, pim
->vrf
->name
,
38 pim
->last_route_change_time
);
41 bool pim_nexthop_lookup(struct pim_instance
*pim
, struct pim_nexthop
*nexthop
,
42 pim_addr addr
, int neighbor_needed
)
44 struct pim_zlookup_nexthop nexthop_tab
[router
->multipath
];
45 struct pim_neighbor
*nbr
= NULL
;
47 struct interface
*ifp
= NULL
;
48 ifindex_t first_ifindex
= 0;
51 struct pim_interface
*pim_ifp
;
55 * We should not attempt to lookup a
56 * 255.255.255.255 address, since
59 if (pim_addr_is_any(addr
))
63 if ((!pim_addr_cmp(nexthop
->last_lookup
, addr
)) &&
64 (nexthop
->last_lookup_time
> pim
->last_route_change_time
)) {
65 if (PIM_DEBUG_PIM_NHT
)
67 "%s: Using last lookup for %pPAs at %lld, %" PRId64
69 __func__
, &addr
, nexthop
->last_lookup_time
,
70 pim
->last_route_change_time
,
71 &nexthop
->mrib_nexthop_addr
);
72 pim
->nexthop_lookups_avoided
++;
75 if (PIM_DEBUG_PIM_NHT
)
77 "%s: Looking up: %pPAs, last lookup time: %lld, %" PRId64
,
78 __func__
, &addr
, nexthop
->last_lookup_time
,
79 pim
->last_route_change_time
);
82 memset(nexthop_tab
, 0,
83 sizeof(struct pim_zlookup_nexthop
) * router
->multipath
);
85 zclient_lookup_nexthop(pim
, nexthop_tab
, router
->multipath
,
86 addr
, PIM_NEXTHOP_LOOKUP_MAX
);
87 if (num_ifindex
< 1) {
88 if (PIM_DEBUG_PIM_NHT
)
90 "%s %s: could not find nexthop ifindex for address %pPAs",
91 __FILE__
, __func__
, &addr
);
95 while (!found
&& (i
< num_ifindex
)) {
96 first_ifindex
= nexthop_tab
[i
].ifindex
;
98 ifp
= if_lookup_by_index(first_ifindex
, pim
->vrf
->vrf_id
);
102 "%s %s: could not find interface for ifindex %d (address %pPAs)",
103 __FILE__
, __func__
, first_ifindex
,
110 if (!pim_ifp
|| !pim_ifp
->pim_enable
) {
113 "%s: pim not enabled on input interface %s (ifindex=%d, RPF for source %pPAs)",
114 __func__
, ifp
->name
, first_ifindex
,
117 } else if (neighbor_needed
&&
118 !pim_if_connected_to_source(ifp
, addr
)) {
119 nbr
= pim_neighbor_find(
120 ifp
, nexthop_tab
[i
].nexthop_addr
, true);
121 if (PIM_DEBUG_PIM_TRACE_DETAIL
)
122 zlog_debug("ifp name: %s, pim nbr: %p",
124 if (!nbr
&& !if_is_loopback(ifp
))
135 "%s %s: found nexthop %pPAs for address %pPAs: interface %s ifindex=%d metric=%d pref=%d",
137 &nexthop_tab
[i
].nexthop_addr
, &addr
, ifp
->name
,
138 first_ifindex
, nexthop_tab
[i
].route_metric
,
139 nexthop_tab
[i
].protocol_distance
);
141 /* update nexthop data */
142 nexthop
->interface
= ifp
;
143 nexthop
->mrib_nexthop_addr
= nexthop_tab
[i
].nexthop_addr
;
144 nexthop
->mrib_metric_preference
=
145 nexthop_tab
[i
].protocol_distance
;
146 nexthop
->mrib_route_metric
= nexthop_tab
[i
].route_metric
;
147 nexthop
->last_lookup
= addr
;
148 nexthop
->last_lookup_time
= pim_time_monotonic_usec();
155 static int nexthop_mismatch(const struct pim_nexthop
*nh1
,
156 const struct pim_nexthop
*nh2
)
158 return (nh1
->interface
!= nh2
->interface
) ||
159 (pim_addr_cmp(nh1
->mrib_nexthop_addr
, nh2
->mrib_nexthop_addr
)) ||
160 (nh1
->mrib_metric_preference
!= nh2
->mrib_metric_preference
) ||
161 (nh1
->mrib_route_metric
!= nh2
->mrib_route_metric
);
164 static void pim_rpf_cost_change(struct pim_instance
*pim
,
165 struct pim_upstream
*up
, uint32_t old_cost
)
167 struct pim_rpf
*rpf
= &up
->rpf
;
170 new_cost
= pim_up_mlag_local_cost(up
);
173 "%s: Cost_to_rp of upstream-%s changed to:%u, from:%u",
174 __func__
, up
->sg_str
, new_cost
, old_cost
);
176 if (old_cost
== new_cost
)
179 /* Cost changed, it might Impact MLAG DF election, update */
182 "%s: Cost_to_rp of upstream-%s changed to:%u",
183 __func__
, up
->sg_str
,
184 rpf
->source_nexthop
.mrib_route_metric
);
186 if (pim_up_mlag_is_local(up
))
187 pim_mlag_up_local_add(pim
, up
);
190 enum pim_rpf_result
pim_rpf_update(struct pim_instance
*pim
,
191 struct pim_upstream
*up
, struct pim_rpf
*old
,
194 struct pim_rpf
*rpf
= &up
->rpf
;
195 struct pim_rpf saved
;
198 bool neigh_needed
= true;
199 uint32_t saved_mrib_route_metric
;
201 if (PIM_UPSTREAM_FLAG_TEST_STATIC_IIF(up
->flags
))
204 if (pim_addr_is_any(up
->upstream_addr
)) {
205 zlog_debug("%s(%s): RP is not configured yet for %s",
206 __func__
, caller
, up
->sg_str
);
210 saved
.source_nexthop
= rpf
->source_nexthop
;
211 saved
.rpf_addr
= rpf
->rpf_addr
;
212 saved_mrib_route_metric
= pim_up_mlag_local_cost(up
);
214 old
->source_nexthop
= saved
.source_nexthop
;
215 old
->rpf_addr
= saved
.rpf_addr
;
218 src
= up
->upstream_addr
; // RP or Src address
219 pim_addr_to_prefix(&grp
, up
->sg
.grp
);
221 if ((pim_addr_is_any(up
->sg
.src
) && I_am_RP(pim
, up
->sg
.grp
)) ||
222 PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
))
223 neigh_needed
= false;
224 pim_find_or_track_nexthop(pim
, up
->upstream_addr
, up
, NULL
, NULL
);
225 if (!pim_ecmp_nexthop_lookup(pim
, &rpf
->source_nexthop
, src
, &grp
,
227 /* Route is Deleted in Zebra, reset the stored NH data */
228 pim_upstream_rpf_clear(pim
, up
);
229 pim_rpf_cost_change(pim
, up
, saved_mrib_route_metric
);
230 return PIM_RPF_FAILURE
;
233 rpf
->rpf_addr
= pim_rpf_find_rpf_addr(up
);
235 if (pim_rpf_addr_is_inaddr_any(rpf
) && PIM_DEBUG_ZEBRA
) {
236 /* RPF'(S,G) not found */
237 zlog_debug("%s(%s): RPF'%s not found: won't send join upstream",
238 __func__
, caller
, up
->sg_str
);
242 /* detect change in pim_nexthop */
243 if (nexthop_mismatch(&rpf
->source_nexthop
, &saved
.source_nexthop
)) {
246 zlog_debug("%s(%s): (S,G)=%s source nexthop now is: interface=%s address=%pPAs pref=%d metric=%d",
249 rpf
->source_nexthop
.interface
? rpf
->source_nexthop
.interface
->name
: "<ifname?>",
250 &rpf
->source_nexthop
.mrib_nexthop_addr
,
251 rpf
->source_nexthop
.mrib_metric_preference
,
252 rpf
->source_nexthop
.mrib_route_metric
);
254 pim_upstream_update_join_desired(pim
, up
);
255 pim_upstream_update_could_assert(up
);
256 pim_upstream_update_my_assert_metric(up
);
259 /* detect change in RPF_interface(S) */
260 if (saved
.source_nexthop
.interface
!= rpf
->source_nexthop
.interface
) {
262 if (PIM_DEBUG_ZEBRA
) {
263 zlog_debug("%s(%s): (S,G)=%s RPF_interface(S) changed from %s to %s",
266 saved
.source_nexthop
.interface
? saved
.source_nexthop
.interface
->name
: "<oldif?>",
267 rpf
->source_nexthop
.interface
? rpf
->source_nexthop
.interface
->name
: "<newif?>");
271 pim_upstream_rpf_interface_changed(
272 up
, saved
.source_nexthop
.interface
);
275 /* detect change in RPF'(S,G) */
276 if (pim_addr_cmp(saved
.rpf_addr
, rpf
->rpf_addr
) ||
277 saved
.source_nexthop
.interface
!= rpf
->source_nexthop
.interface
) {
278 pim_rpf_cost_change(pim
, up
, saved_mrib_route_metric
);
279 return PIM_RPF_CHANGED
;
284 "%s(%s): Cost_to_rp of upstream-%s changed to:%u",
285 __func__
, caller
, up
->sg_str
,
286 rpf
->source_nexthop
.mrib_route_metric
);
288 pim_rpf_cost_change(pim
, up
, saved_mrib_route_metric
);
294 * In the case of RP deletion and RP unreachablity,
295 * uninstall the mroute in the kernel and clear the
296 * rpf information in the pim upstream and pim channel
297 * oil data structure.
299 void pim_upstream_rpf_clear(struct pim_instance
*pim
,
300 struct pim_upstream
*up
)
302 if (up
->rpf
.source_nexthop
.interface
) {
303 pim_upstream_switch(pim
, up
, PIM_UPSTREAM_NOTJOINED
);
304 up
->rpf
.source_nexthop
.interface
= NULL
;
305 up
->rpf
.source_nexthop
.mrib_nexthop_addr
= PIMADDR_ANY
;
306 up
->rpf
.source_nexthop
.mrib_metric_preference
=
307 router
->infinite_assert_metric
.metric_preference
;
308 up
->rpf
.source_nexthop
.mrib_route_metric
=
309 router
->infinite_assert_metric
.route_metric
;
310 up
->rpf
.rpf_addr
= PIMADDR_ANY
;
311 pim_upstream_mroute_iif_update(up
->channel_oil
, __func__
);
316 RFC 4601: 4.1.6. State Summarization Macros
319 if ( I_Am_Assert_Loser(S, G, RPF_interface(S) )) {
320 return AssertWinner(S, G, RPF_interface(S) )
322 return NBR( RPF_interface(S), MRIB.next_hop( S ) )
326 RPF'(*,G) and RPF'(S,G) indicate the neighbor from which data
327 packets should be coming and to which joins should be sent on the RP
328 tree and SPT, respectively.
330 static pim_addr
pim_rpf_find_rpf_addr(struct pim_upstream
*up
)
332 struct pim_ifchannel
*rpf_ch
;
333 struct pim_neighbor
*neigh
;
336 if (!up
->rpf
.source_nexthop
.interface
) {
337 zlog_warn("%s: missing RPF interface for upstream (S,G)=%s",
338 __func__
, up
->sg_str
);
343 rpf_ch
= pim_ifchannel_find(up
->rpf
.source_nexthop
.interface
, &up
->sg
);
345 if (rpf_ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
346 return rpf_ch
->ifassert_winner
;
350 /* return NBR( RPF_interface(S), MRIB.next_hop( S ) ) */
352 neigh
= pim_if_find_neighbor(up
->rpf
.source_nexthop
.interface
,
353 up
->rpf
.source_nexthop
.mrib_nexthop_addr
);
355 rpf_addr
= neigh
->source_addr
;
357 rpf_addr
= PIMADDR_ANY
;
362 int pim_rpf_addr_is_inaddr_any(struct pim_rpf
*rpf
)
364 return pim_addr_is_any(rpf
->rpf_addr
);
367 int pim_rpf_is_same(struct pim_rpf
*rpf1
, struct pim_rpf
*rpf2
)
369 if (rpf1
->source_nexthop
.interface
== rpf2
->source_nexthop
.interface
)
375 unsigned int pim_rpf_hash_key(const void *arg
)
377 const struct pim_nexthop_cache
*r
= arg
;
380 return jhash_1word(r
->rpf
.rpf_addr
.s_addr
, 0);
382 return jhash2(r
->rpf
.rpf_addr
.s6_addr32
,
383 array_size(r
->rpf
.rpf_addr
.s6_addr32
), 0);
387 bool pim_rpf_equal(const void *arg1
, const void *arg2
)
389 const struct pim_nexthop_cache
*r1
=
390 (const struct pim_nexthop_cache
*)arg1
;
391 const struct pim_nexthop_cache
*r2
=
392 (const struct pim_nexthop_cache
*)arg2
;
394 return (!pim_addr_cmp(r1
->rpf
.rpf_addr
, r2
->rpf
.rpf_addr
));