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) {
101 "%s %s: could not find nexthop ifindex for address %pPAs",
102 __FILE__
, __func__
, &addr
);
106 while (!found
&& (i
< num_ifindex
)) {
107 first_ifindex
= nexthop_tab
[i
].ifindex
;
109 ifp
= if_lookup_by_index(first_ifindex
, pim
->vrf
->vrf_id
);
113 "%s %s: could not find interface for ifindex %d (address %pPAs)",
114 __FILE__
, __func__
, first_ifindex
,
123 "%s: multicast not enabled on input interface %s (ifindex=%d, RPF for source %pPAs)",
124 __func__
, ifp
->name
, first_ifindex
,
127 } else if (neighbor_needed
128 && !pim_if_connected_to_source(ifp
, addr
)) {
129 nbr
= pim_neighbor_find(ifp
,
130 nexthop_tab
[i
].nexthop_addr
);
131 if (PIM_DEBUG_PIM_TRACE_DETAIL
)
132 zlog_debug("ifp name: %s, pim nbr: %p",
134 if (!nbr
&& !if_is_loopback(ifp
))
145 "%s %s: found nexthop %pPAs for address %pPAs: interface %s ifindex=%d metric=%d pref=%d",
147 &nexthop_tab
[i
].nexthop_addr
, &addr
, ifp
->name
,
148 first_ifindex
, nexthop_tab
[i
].route_metric
,
149 nexthop_tab
[i
].protocol_distance
);
151 /* update nexthop data */
152 nexthop
->interface
= ifp
;
153 nexthop
->mrib_nexthop_addr
= nexthop_tab
[i
].nexthop_addr
;
154 nexthop
->mrib_metric_preference
=
155 nexthop_tab
[i
].protocol_distance
;
156 nexthop
->mrib_route_metric
= nexthop_tab
[i
].route_metric
;
157 nexthop
->last_lookup
= addr
;
158 nexthop
->last_lookup_time
= pim_time_monotonic_usec();
165 static int nexthop_mismatch(const struct pim_nexthop
*nh1
,
166 const struct pim_nexthop
*nh2
)
168 return (nh1
->interface
!= nh2
->interface
) ||
169 (pim_addr_cmp(nh1
->mrib_nexthop_addr
, nh2
->mrib_nexthop_addr
)) ||
170 (nh1
->mrib_metric_preference
!= nh2
->mrib_metric_preference
) ||
171 (nh1
->mrib_route_metric
!= nh2
->mrib_route_metric
);
174 static void pim_rpf_cost_change(struct pim_instance
*pim
,
175 struct pim_upstream
*up
, uint32_t old_cost
)
177 struct pim_rpf
*rpf
= &up
->rpf
;
180 new_cost
= pim_up_mlag_local_cost(up
);
183 "%s: Cost_to_rp of upstream-%s changed to:%u, from:%u",
184 __func__
, up
->sg_str
, new_cost
, old_cost
);
186 if (old_cost
== new_cost
)
189 /* Cost changed, it might Impact MLAG DF election, update */
192 "%s: Cost_to_rp of upstream-%s changed to:%u",
193 __func__
, up
->sg_str
,
194 rpf
->source_nexthop
.mrib_route_metric
);
196 if (pim_up_mlag_is_local(up
))
197 pim_mlag_up_local_add(pim
, up
);
200 enum pim_rpf_result
pim_rpf_update(struct pim_instance
*pim
,
201 struct pim_upstream
*up
, struct pim_rpf
*old
,
204 struct pim_rpf
*rpf
= &up
->rpf
;
205 struct pim_rpf saved
;
208 bool neigh_needed
= true;
209 uint32_t saved_mrib_route_metric
;
211 if (PIM_UPSTREAM_FLAG_TEST_STATIC_IIF(up
->flags
))
214 if (pim_addr_is_any(up
->upstream_addr
)) {
215 zlog_debug("%s(%s): RP is not configured yet for %s",
216 __func__
, caller
, up
->sg_str
);
220 saved
.source_nexthop
= rpf
->source_nexthop
;
221 saved
.rpf_addr
= rpf
->rpf_addr
;
222 saved_mrib_route_metric
= pim_up_mlag_local_cost(up
);
224 old
->source_nexthop
= saved
.source_nexthop
;
225 old
->rpf_addr
= saved
.rpf_addr
;
228 src
= up
->upstream_addr
; // RP or Src address
229 pim_addr_to_prefix(&grp
, up
->sg
.grp
);
231 if ((pim_addr_is_any(up
->sg
.src
) && I_am_RP(pim
, up
->sg
.grp
)) ||
232 PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
))
233 neigh_needed
= false;
234 pim_find_or_track_nexthop(pim
, up
->upstream_addr
, up
, NULL
, NULL
);
235 if (!pim_ecmp_nexthop_lookup(pim
, &rpf
->source_nexthop
, src
, &grp
,
237 /* Route is Deleted in Zebra, reset the stored NH data */
238 pim_upstream_rpf_clear(pim
, up
);
239 pim_rpf_cost_change(pim
, up
, saved_mrib_route_metric
);
240 return PIM_RPF_FAILURE
;
243 rpf
->rpf_addr
= pim_rpf_find_rpf_addr(up
);
245 if (pim_rpf_addr_is_inaddr_any(rpf
) && PIM_DEBUG_ZEBRA
) {
246 /* RPF'(S,G) not found */
247 zlog_debug("%s(%s): RPF'%s not found: won't send join upstream",
248 __func__
, caller
, up
->sg_str
);
252 /* detect change in pim_nexthop */
253 if (nexthop_mismatch(&rpf
->source_nexthop
, &saved
.source_nexthop
)) {
256 zlog_debug("%s(%s): (S,G)=%s source nexthop now is: interface=%s address=%pPAs pref=%d metric=%d",
259 rpf
->source_nexthop
.interface
? rpf
->source_nexthop
.interface
->name
: "<ifname?>",
260 &rpf
->source_nexthop
.mrib_nexthop_addr
,
261 rpf
->source_nexthop
.mrib_metric_preference
,
262 rpf
->source_nexthop
.mrib_route_metric
);
264 pim_upstream_update_join_desired(pim
, up
);
265 pim_upstream_update_could_assert(up
);
266 pim_upstream_update_my_assert_metric(up
);
269 /* detect change in RPF_interface(S) */
270 if (saved
.source_nexthop
.interface
!= rpf
->source_nexthop
.interface
) {
272 if (PIM_DEBUG_ZEBRA
) {
273 zlog_debug("%s(%s): (S,G)=%s RPF_interface(S) changed from %s to %s",
276 saved
.source_nexthop
.interface
? saved
.source_nexthop
.interface
->name
: "<oldif?>",
277 rpf
->source_nexthop
.interface
? rpf
->source_nexthop
.interface
->name
: "<newif?>");
281 pim_upstream_rpf_interface_changed(
282 up
, saved
.source_nexthop
.interface
);
285 /* detect change in RPF'(S,G) */
286 if (pim_addr_cmp(saved
.rpf_addr
, rpf
->rpf_addr
) ||
287 saved
.source_nexthop
.interface
!= rpf
->source_nexthop
.interface
) {
288 pim_rpf_cost_change(pim
, up
, saved_mrib_route_metric
);
289 return PIM_RPF_CHANGED
;
294 "%s(%s): Cost_to_rp of upstream-%s changed to:%u",
295 __func__
, caller
, up
->sg_str
,
296 rpf
->source_nexthop
.mrib_route_metric
);
298 pim_rpf_cost_change(pim
, up
, saved_mrib_route_metric
);
304 * In the case of RP deletion and RP unreachablity,
305 * uninstall the mroute in the kernel and clear the
306 * rpf information in the pim upstream and pim channel
307 * oil data structure.
309 void pim_upstream_rpf_clear(struct pim_instance
*pim
,
310 struct pim_upstream
*up
)
312 if (up
->rpf
.source_nexthop
.interface
) {
313 pim_upstream_switch(pim
, up
, PIM_UPSTREAM_NOTJOINED
);
314 up
->rpf
.source_nexthop
.interface
= NULL
;
315 up
->rpf
.source_nexthop
.mrib_nexthop_addr
= PIMADDR_ANY
;
316 up
->rpf
.source_nexthop
.mrib_metric_preference
=
317 router
->infinite_assert_metric
.metric_preference
;
318 up
->rpf
.source_nexthop
.mrib_route_metric
=
319 router
->infinite_assert_metric
.route_metric
;
320 up
->rpf
.rpf_addr
= PIMADDR_ANY
;
321 pim_upstream_mroute_iif_update(up
->channel_oil
, __func__
);
326 RFC 4601: 4.1.6. State Summarization Macros
329 if ( I_Am_Assert_Loser(S, G, RPF_interface(S) )) {
330 return AssertWinner(S, G, RPF_interface(S) )
332 return NBR( RPF_interface(S), MRIB.next_hop( S ) )
336 RPF'(*,G) and RPF'(S,G) indicate the neighbor from which data
337 packets should be coming and to which joins should be sent on the RP
338 tree and SPT, respectively.
340 static pim_addr
pim_rpf_find_rpf_addr(struct pim_upstream
*up
)
342 struct pim_ifchannel
*rpf_ch
;
343 struct pim_neighbor
*neigh
;
346 if (!up
->rpf
.source_nexthop
.interface
) {
347 zlog_warn("%s: missing RPF interface for upstream (S,G)=%s",
348 __func__
, up
->sg_str
);
353 rpf_ch
= pim_ifchannel_find(up
->rpf
.source_nexthop
.interface
, &up
->sg
);
355 if (rpf_ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
356 return rpf_ch
->ifassert_winner
;
360 /* return NBR( RPF_interface(S), MRIB.next_hop( S ) ) */
362 neigh
= pim_if_find_neighbor(up
->rpf
.source_nexthop
.interface
,
363 up
->rpf
.source_nexthop
.mrib_nexthop_addr
);
365 rpf_addr
= neigh
->source_addr
;
367 rpf_addr
= PIMADDR_ANY
;
372 int pim_rpf_addr_is_inaddr_any(struct pim_rpf
*rpf
)
374 return pim_addr_is_any(rpf
->rpf_addr
);
377 int pim_rpf_is_same(struct pim_rpf
*rpf1
, struct pim_rpf
*rpf2
)
379 if (rpf1
->source_nexthop
.interface
== rpf2
->source_nexthop
.interface
)
385 unsigned int pim_rpf_hash_key(const void *arg
)
387 const struct pim_nexthop_cache
*r
= arg
;
390 return jhash_1word(r
->rpf
.rpf_addr
.s_addr
, 0);
392 return jhash2(r
->rpf
.rpf_addr
.s6_addr32
,
393 array_size(r
->rpf
.rpf_addr
.s6_addr32
), 0);
397 bool pim_rpf_equal(const void *arg1
, const void *arg2
)
399 const struct pim_nexthop_cache
*r1
=
400 (const struct pim_nexthop_cache
*)arg1
;
401 const struct pim_nexthop_cache
*r2
=
402 (const struct pim_nexthop_cache
*)arg2
;
404 return (!pim_addr_cmp(r1
->rpf
.rpf_addr
, r2
->rpf
.rpf_addr
));