3 * Copyright (C) 2008 Everton da Silva Marques
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.
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.
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
30 #include "pim_instance.h"
34 #include "pim_iface.h"
35 #include "pim_neighbor.h"
36 #include "pim_zlookup.h"
37 #include "pim_ifchannel.h"
43 static pim_addr
pim_rpf_find_rpf_addr(struct pim_upstream
*up
);
45 void pim_rpf_set_refresh_time(struct pim_instance
*pim
)
47 pim
->last_route_change_time
= pim_time_monotonic_usec();
48 if (PIM_DEBUG_PIM_TRACE
)
49 zlog_debug("%s: vrf(%s) New last route change time: %" PRId64
,
50 __func__
, pim
->vrf
->name
,
51 pim
->last_route_change_time
);
54 bool pim_nexthop_lookup(struct pim_instance
*pim
, struct pim_nexthop
*nexthop
,
55 pim_addr addr
, int neighbor_needed
)
57 struct pim_zlookup_nexthop nexthop_tab
[router
->multipath
];
58 struct pim_neighbor
*nbr
= NULL
;
60 struct interface
*ifp
= NULL
;
61 ifindex_t first_ifindex
= 0;
67 * We should not attempt to lookup a
68 * 255.255.255.255 address, since
71 if (pim_addr_is_any(addr
))
75 if ((!pim_addr_cmp(nexthop
->last_lookup
, addr
)) &&
76 (nexthop
->last_lookup_time
> pim
->last_route_change_time
)) {
77 if (PIM_DEBUG_PIM_NHT
)
79 "%s: Using last lookup for %pPAs at %lld, %" PRId64
81 __func__
, &addr
, nexthop
->last_lookup_time
,
82 pim
->last_route_change_time
,
83 &nexthop
->mrib_nexthop_addr
);
84 pim
->nexthop_lookups_avoided
++;
87 if (PIM_DEBUG_PIM_NHT
)
89 "%s: Looking up: %pPAs, last lookup time: %lld, %" PRId64
,
90 __func__
, &addr
, nexthop
->last_lookup_time
,
91 pim
->last_route_change_time
);
94 memset(nexthop_tab
, 0,
95 sizeof(struct pim_zlookup_nexthop
) * router
->multipath
);
97 zclient_lookup_nexthop(pim
, nexthop_tab
, router
->multipath
,
98 addr
, PIM_NEXTHOP_LOOKUP_MAX
);
99 if (num_ifindex
< 1) {
100 if (PIM_DEBUG_PIM_NHT
)
102 "%s %s: could not find nexthop ifindex for address %pPAs",
103 __FILE__
, __func__
, &addr
);
107 while (!found
&& (i
< num_ifindex
)) {
108 first_ifindex
= nexthop_tab
[i
].ifindex
;
110 ifp
= if_lookup_by_index(first_ifindex
, pim
->vrf
->vrf_id
);
114 "%s %s: could not find interface for ifindex %d (address %pPAs)",
115 __FILE__
, __func__
, first_ifindex
,
124 "%s: multicast not enabled on input interface %s (ifindex=%d, RPF for source %pPAs)",
125 __func__
, ifp
->name
, first_ifindex
,
128 } else if (neighbor_needed
129 && !pim_if_connected_to_source(ifp
, addr
)) {
130 nbr
= pim_neighbor_find(ifp
,
131 nexthop_tab
[i
].nexthop_addr
);
132 if (PIM_DEBUG_PIM_TRACE_DETAIL
)
133 zlog_debug("ifp name: %s, pim nbr: %p",
135 if (!nbr
&& !if_is_loopback(ifp
))
146 "%s %s: found nexthop %pPAs for address %pPAs: interface %s ifindex=%d metric=%d pref=%d",
148 &nexthop_tab
[i
].nexthop_addr
, &addr
, ifp
->name
,
149 first_ifindex
, nexthop_tab
[i
].route_metric
,
150 nexthop_tab
[i
].protocol_distance
);
152 /* update nexthop data */
153 nexthop
->interface
= ifp
;
154 nexthop
->mrib_nexthop_addr
= nexthop_tab
[i
].nexthop_addr
;
155 nexthop
->mrib_metric_preference
=
156 nexthop_tab
[i
].protocol_distance
;
157 nexthop
->mrib_route_metric
= nexthop_tab
[i
].route_metric
;
158 nexthop
->last_lookup
= addr
;
159 nexthop
->last_lookup_time
= pim_time_monotonic_usec();
166 static int nexthop_mismatch(const struct pim_nexthop
*nh1
,
167 const struct pim_nexthop
*nh2
)
169 return (nh1
->interface
!= nh2
->interface
) ||
170 (pim_addr_cmp(nh1
->mrib_nexthop_addr
, nh2
->mrib_nexthop_addr
)) ||
171 (nh1
->mrib_metric_preference
!= nh2
->mrib_metric_preference
) ||
172 (nh1
->mrib_route_metric
!= nh2
->mrib_route_metric
);
175 static void pim_rpf_cost_change(struct pim_instance
*pim
,
176 struct pim_upstream
*up
, uint32_t old_cost
)
178 struct pim_rpf
*rpf
= &up
->rpf
;
181 new_cost
= pim_up_mlag_local_cost(up
);
184 "%s: Cost_to_rp of upstream-%s changed to:%u, from:%u",
185 __func__
, up
->sg_str
, new_cost
, old_cost
);
187 if (old_cost
== new_cost
)
190 /* Cost changed, it might Impact MLAG DF election, update */
193 "%s: Cost_to_rp of upstream-%s changed to:%u",
194 __func__
, up
->sg_str
,
195 rpf
->source_nexthop
.mrib_route_metric
);
197 if (pim_up_mlag_is_local(up
))
198 pim_mlag_up_local_add(pim
, up
);
201 enum pim_rpf_result
pim_rpf_update(struct pim_instance
*pim
,
202 struct pim_upstream
*up
, struct pim_rpf
*old
,
205 struct pim_rpf
*rpf
= &up
->rpf
;
206 struct pim_rpf saved
;
209 bool neigh_needed
= true;
210 uint32_t saved_mrib_route_metric
;
212 if (PIM_UPSTREAM_FLAG_TEST_STATIC_IIF(up
->flags
))
215 if (pim_addr_is_any(up
->upstream_addr
)) {
216 zlog_debug("%s(%s): RP is not configured yet for %s",
217 __func__
, caller
, up
->sg_str
);
221 saved
.source_nexthop
= rpf
->source_nexthop
;
222 saved
.rpf_addr
= rpf
->rpf_addr
;
223 saved_mrib_route_metric
= pim_up_mlag_local_cost(up
);
225 old
->source_nexthop
= saved
.source_nexthop
;
226 old
->rpf_addr
= saved
.rpf_addr
;
229 src
= up
->upstream_addr
; // RP or Src address
230 pim_addr_to_prefix(&grp
, up
->sg
.grp
);
232 if ((pim_addr_is_any(up
->sg
.src
) && I_am_RP(pim
, up
->sg
.grp
)) ||
233 PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
))
234 neigh_needed
= false;
235 pim_find_or_track_nexthop(pim
, up
->upstream_addr
, up
, NULL
, NULL
);
236 if (!pim_ecmp_nexthop_lookup(pim
, &rpf
->source_nexthop
, src
, &grp
,
238 /* Route is Deleted in Zebra, reset the stored NH data */
239 pim_upstream_rpf_clear(pim
, up
);
240 pim_rpf_cost_change(pim
, up
, saved_mrib_route_metric
);
241 return PIM_RPF_FAILURE
;
244 rpf
->rpf_addr
= pim_rpf_find_rpf_addr(up
);
246 if (pim_rpf_addr_is_inaddr_any(rpf
) && PIM_DEBUG_ZEBRA
) {
247 /* RPF'(S,G) not found */
248 zlog_debug("%s(%s): RPF'%s not found: won't send join upstream",
249 __func__
, caller
, up
->sg_str
);
253 /* detect change in pim_nexthop */
254 if (nexthop_mismatch(&rpf
->source_nexthop
, &saved
.source_nexthop
)) {
257 zlog_debug("%s(%s): (S,G)=%s source nexthop now is: interface=%s address=%pPAs pref=%d metric=%d",
260 rpf
->source_nexthop
.interface
? rpf
->source_nexthop
.interface
->name
: "<ifname?>",
261 &rpf
->source_nexthop
.mrib_nexthop_addr
,
262 rpf
->source_nexthop
.mrib_metric_preference
,
263 rpf
->source_nexthop
.mrib_route_metric
);
265 pim_upstream_update_join_desired(pim
, up
);
266 pim_upstream_update_could_assert(up
);
267 pim_upstream_update_my_assert_metric(up
);
270 /* detect change in RPF_interface(S) */
271 if (saved
.source_nexthop
.interface
!= rpf
->source_nexthop
.interface
) {
273 if (PIM_DEBUG_ZEBRA
) {
274 zlog_debug("%s(%s): (S,G)=%s RPF_interface(S) changed from %s to %s",
277 saved
.source_nexthop
.interface
? saved
.source_nexthop
.interface
->name
: "<oldif?>",
278 rpf
->source_nexthop
.interface
? rpf
->source_nexthop
.interface
->name
: "<newif?>");
282 pim_upstream_rpf_interface_changed(
283 up
, saved
.source_nexthop
.interface
);
286 /* detect change in RPF'(S,G) */
287 if (pim_addr_cmp(saved
.rpf_addr
, rpf
->rpf_addr
) ||
288 saved
.source_nexthop
.interface
!= rpf
->source_nexthop
.interface
) {
289 pim_rpf_cost_change(pim
, up
, saved_mrib_route_metric
);
290 return PIM_RPF_CHANGED
;
295 "%s(%s): Cost_to_rp of upstream-%s changed to:%u",
296 __func__
, caller
, up
->sg_str
,
297 rpf
->source_nexthop
.mrib_route_metric
);
299 pim_rpf_cost_change(pim
, up
, saved_mrib_route_metric
);
305 * In the case of RP deletion and RP unreachablity,
306 * uninstall the mroute in the kernel and clear the
307 * rpf information in the pim upstream and pim channel
308 * oil data structure.
310 void pim_upstream_rpf_clear(struct pim_instance
*pim
,
311 struct pim_upstream
*up
)
313 if (up
->rpf
.source_nexthop
.interface
) {
314 pim_upstream_switch(pim
, up
, PIM_UPSTREAM_NOTJOINED
);
315 up
->rpf
.source_nexthop
.interface
= NULL
;
316 up
->rpf
.source_nexthop
.mrib_nexthop_addr
= PIMADDR_ANY
;
317 up
->rpf
.source_nexthop
.mrib_metric_preference
=
318 router
->infinite_assert_metric
.metric_preference
;
319 up
->rpf
.source_nexthop
.mrib_route_metric
=
320 router
->infinite_assert_metric
.route_metric
;
321 up
->rpf
.rpf_addr
= PIMADDR_ANY
;
322 pim_upstream_mroute_iif_update(up
->channel_oil
, __func__
);
327 RFC 4601: 4.1.6. State Summarization Macros
330 if ( I_Am_Assert_Loser(S, G, RPF_interface(S) )) {
331 return AssertWinner(S, G, RPF_interface(S) )
333 return NBR( RPF_interface(S), MRIB.next_hop( S ) )
337 RPF'(*,G) and RPF'(S,G) indicate the neighbor from which data
338 packets should be coming and to which joins should be sent on the RP
339 tree and SPT, respectively.
341 static pim_addr
pim_rpf_find_rpf_addr(struct pim_upstream
*up
)
343 struct pim_ifchannel
*rpf_ch
;
344 struct pim_neighbor
*neigh
;
347 if (!up
->rpf
.source_nexthop
.interface
) {
348 zlog_warn("%s: missing RPF interface for upstream (S,G)=%s",
349 __func__
, up
->sg_str
);
354 rpf_ch
= pim_ifchannel_find(up
->rpf
.source_nexthop
.interface
, &up
->sg
);
356 if (rpf_ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
357 return rpf_ch
->ifassert_winner
;
361 /* return NBR( RPF_interface(S), MRIB.next_hop( S ) ) */
363 neigh
= pim_if_find_neighbor(up
->rpf
.source_nexthop
.interface
,
364 up
->rpf
.source_nexthop
.mrib_nexthop_addr
);
366 rpf_addr
= neigh
->source_addr
;
368 rpf_addr
= PIMADDR_ANY
;
373 int pim_rpf_addr_is_inaddr_any(struct pim_rpf
*rpf
)
375 return pim_addr_is_any(rpf
->rpf_addr
);
378 int pim_rpf_is_same(struct pim_rpf
*rpf1
, struct pim_rpf
*rpf2
)
380 if (rpf1
->source_nexthop
.interface
== rpf2
->source_nexthop
.interface
)
386 unsigned int pim_rpf_hash_key(const void *arg
)
388 const struct pim_nexthop_cache
*r
= arg
;
391 return jhash_1word(r
->rpf
.rpf_addr
.s_addr
, 0);
393 return jhash2(r
->rpf
.rpf_addr
.s6_addr32
,
394 array_size(r
->rpf
.rpf_addr
.s6_addr32
), 0);
398 bool pim_rpf_equal(const void *arg1
, const void *arg2
)
400 const struct pim_nexthop_cache
*r1
=
401 (const struct pim_nexthop_cache
*)arg1
;
402 const struct pim_nexthop_cache
*r2
=
403 (const struct pim_nexthop_cache
*)arg2
;
405 return (!pim_addr_cmp(r1
->rpf
.rpf_addr
, r2
->rpf
.rpf_addr
));