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
33 #include "pim_iface.h"
34 #include "pim_neighbor.h"
35 #include "pim_zlookup.h"
36 #include "pim_ifchannel.h"
42 static pim_addr
pim_rpf_find_rpf_addr(struct pim_upstream
*up
);
44 void pim_rpf_set_refresh_time(struct pim_instance
*pim
)
46 pim
->last_route_change_time
= pim_time_monotonic_usec();
47 if (PIM_DEBUG_PIM_TRACE
)
48 zlog_debug("%s: vrf(%s) New last route change time: %" PRId64
,
49 __func__
, pim
->vrf
->name
,
50 pim
->last_route_change_time
);
53 bool pim_nexthop_lookup(struct pim_instance
*pim
, struct pim_nexthop
*nexthop
,
54 pim_addr addr
, int neighbor_needed
)
56 struct pim_zlookup_nexthop nexthop_tab
[MULTIPATH_NUM
];
57 struct pim_neighbor
*nbr
= NULL
;
59 struct interface
*ifp
= NULL
;
60 ifindex_t first_ifindex
= 0;
66 * We should not attempt to lookup a
67 * 255.255.255.255 address, since
70 if (pim_addr_is_any(addr
))
74 if ((!pim_addr_cmp(nexthop
->last_lookup
, addr
)) &&
75 (nexthop
->last_lookup_time
> pim
->last_route_change_time
)) {
76 if (PIM_DEBUG_PIM_NHT
)
78 "%s: Using last lookup for %pPAs at %lld, %" PRId64
80 __func__
, &addr
, nexthop
->last_lookup_time
,
81 pim
->last_route_change_time
,
82 &nexthop
->mrib_nexthop_addr
);
83 pim
->nexthop_lookups_avoided
++;
86 if (PIM_DEBUG_PIM_NHT
)
88 "%s: Looking up: %pPAs, last lookup time: %lld, %" PRId64
,
89 __func__
, &addr
, nexthop
->last_lookup_time
,
90 pim
->last_route_change_time
);
93 memset(nexthop_tab
, 0,
94 sizeof(struct pim_zlookup_nexthop
) * MULTIPATH_NUM
);
95 num_ifindex
= zclient_lookup_nexthop(pim
, nexthop_tab
, MULTIPATH_NUM
,
96 addr
, PIM_NEXTHOP_LOOKUP_MAX
);
97 if (num_ifindex
< 1) {
99 "%s %s: could not find nexthop ifindex for address %pPAs",
100 __FILE__
, __func__
, &addr
);
104 while (!found
&& (i
< num_ifindex
)) {
105 first_ifindex
= nexthop_tab
[i
].ifindex
;
107 ifp
= if_lookup_by_index(first_ifindex
, pim
->vrf
->vrf_id
);
111 "%s %s: could not find interface for ifindex %d (address %pPAs)",
112 __FILE__
, __func__
, first_ifindex
,
121 "%s: multicast not enabled on input interface %s (ifindex=%d, RPF for source %pPAs)",
122 __func__
, ifp
->name
, first_ifindex
,
125 } else if (neighbor_needed
126 && !pim_if_connected_to_source(ifp
, addr
)) {
127 nbr
= pim_neighbor_find(ifp
,
128 nexthop_tab
[i
].nexthop_addr
);
129 if (PIM_DEBUG_PIM_TRACE_DETAIL
)
130 zlog_debug("ifp name: %s, pim nbr: %p",
132 if (!nbr
&& !if_is_loopback(ifp
))
143 "%s %s: found nexthop %pPAs for address %pPAs: interface %s ifindex=%d metric=%d pref=%d",
145 &nexthop_tab
[i
].nexthop_addr
, &addr
, ifp
->name
,
146 first_ifindex
, nexthop_tab
[i
].route_metric
,
147 nexthop_tab
[i
].protocol_distance
);
149 /* update nexthop data */
150 nexthop
->interface
= ifp
;
151 nexthop
->mrib_nexthop_addr
= nexthop_tab
[i
].nexthop_addr
;
152 nexthop
->mrib_metric_preference
=
153 nexthop_tab
[i
].protocol_distance
;
154 nexthop
->mrib_route_metric
= nexthop_tab
[i
].route_metric
;
155 nexthop
->last_lookup
= addr
;
156 nexthop
->last_lookup_time
= pim_time_monotonic_usec();
163 static int nexthop_mismatch(const struct pim_nexthop
*nh1
,
164 const struct pim_nexthop
*nh2
)
166 return (nh1
->interface
!= nh2
->interface
) ||
167 (pim_addr_cmp(nh1
->mrib_nexthop_addr
, nh2
->mrib_nexthop_addr
)) ||
168 (nh1
->mrib_metric_preference
!= nh2
->mrib_metric_preference
) ||
169 (nh1
->mrib_route_metric
!= nh2
->mrib_route_metric
);
172 static void pim_rpf_cost_change(struct pim_instance
*pim
,
173 struct pim_upstream
*up
, uint32_t old_cost
)
175 struct pim_rpf
*rpf
= &up
->rpf
;
178 new_cost
= pim_up_mlag_local_cost(up
);
181 "%s: Cost_to_rp of upstream-%s changed to:%u, from:%u",
182 __func__
, up
->sg_str
, new_cost
, old_cost
);
184 if (old_cost
== new_cost
)
187 /* Cost changed, it might Impact MLAG DF election, update */
190 "%s: Cost_to_rp of upstream-%s changed to:%u",
191 __func__
, up
->sg_str
,
192 rpf
->source_nexthop
.mrib_route_metric
);
194 if (pim_up_mlag_is_local(up
))
195 pim_mlag_up_local_add(pim
, up
);
198 enum pim_rpf_result
pim_rpf_update(struct pim_instance
*pim
,
199 struct pim_upstream
*up
, struct pim_rpf
*old
,
202 struct pim_rpf
*rpf
= &up
->rpf
;
203 struct pim_rpf saved
;
205 struct prefix src
, grp
;
206 bool neigh_needed
= true;
207 uint32_t saved_mrib_route_metric
;
210 if (PIM_UPSTREAM_FLAG_TEST_STATIC_IIF(up
->flags
))
213 if (pim_addr_is_any(up
->upstream_addr
)) {
214 zlog_debug("%s(%s): RP is not configured yet for %s",
215 __func__
, caller
, up
->sg_str
);
219 saved
.source_nexthop
= rpf
->source_nexthop
;
220 saved
.rpf_addr
= rpf
->rpf_addr
;
221 saved_mrib_route_metric
= pim_up_mlag_local_cost(up
);
223 old
->source_nexthop
= saved
.source_nexthop
;
224 old
->rpf_addr
= saved
.rpf_addr
;
227 pim_addr_to_prefix(&nht_p
, up
->upstream_addr
);
229 pim_addr_to_prefix(&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
, &nht_p
, 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_addr
= pim_rpf_find_rpf_addr(up
);
245 pim_addr_to_prefix(&rpf
->rpf_addr
, rpf_addr
);
247 if (pim_rpf_addr_is_inaddr_any(rpf
) && PIM_DEBUG_ZEBRA
) {
248 /* RPF'(S,G) not found */
249 zlog_debug("%s(%s): RPF'%s not found: won't send join upstream",
250 __func__
, caller
, up
->sg_str
);
254 /* detect change in pim_nexthop */
255 if (nexthop_mismatch(&rpf
->source_nexthop
, &saved
.source_nexthop
)) {
258 zlog_debug("%s(%s): (S,G)=%s source nexthop now is: interface=%s address=%pPAs pref=%d metric=%d",
261 rpf
->source_nexthop
.interface
? rpf
->source_nexthop
.interface
->name
: "<ifname?>",
262 &rpf
->source_nexthop
.mrib_nexthop_addr
,
263 rpf
->source_nexthop
.mrib_metric_preference
,
264 rpf
->source_nexthop
.mrib_route_metric
);
266 pim_upstream_update_join_desired(pim
, up
);
267 pim_upstream_update_could_assert(up
);
268 pim_upstream_update_my_assert_metric(up
);
271 /* detect change in RPF_interface(S) */
272 if (saved
.source_nexthop
.interface
!= rpf
->source_nexthop
.interface
) {
274 if (PIM_DEBUG_ZEBRA
) {
275 zlog_debug("%s(%s): (S,G)=%s RPF_interface(S) changed from %s to %s",
278 saved
.source_nexthop
.interface
? saved
.source_nexthop
.interface
->name
: "<oldif?>",
279 rpf
->source_nexthop
.interface
? rpf
->source_nexthop
.interface
->name
: "<newif?>");
283 pim_upstream_rpf_interface_changed(
284 up
, saved
.source_nexthop
.interface
);
287 /* detect change in RPF'(S,G) */
288 if (!prefix_same(&saved
.rpf_addr
, &rpf
->rpf_addr
) ||
289 saved
.source_nexthop
.interface
!= rpf
->source_nexthop
.interface
) {
290 pim_rpf_cost_change(pim
, up
, saved_mrib_route_metric
);
291 return PIM_RPF_CHANGED
;
296 "%s(%s): Cost_to_rp of upstream-%s changed to:%u",
297 __func__
, caller
, up
->sg_str
,
298 rpf
->source_nexthop
.mrib_route_metric
);
300 pim_rpf_cost_change(pim
, up
, saved_mrib_route_metric
);
306 * In the case of RP deletion and RP unreachablity,
307 * uninstall the mroute in the kernel and clear the
308 * rpf information in the pim upstream and pim channel
309 * oil data structure.
311 void pim_upstream_rpf_clear(struct pim_instance
*pim
,
312 struct pim_upstream
*up
)
314 if (up
->rpf
.source_nexthop
.interface
) {
315 pim_upstream_switch(pim
, up
, PIM_UPSTREAM_NOTJOINED
);
316 up
->rpf
.source_nexthop
.interface
= NULL
;
317 up
->rpf
.source_nexthop
.mrib_nexthop_addr
= PIMADDR_ANY
;
318 up
->rpf
.source_nexthop
.mrib_metric_preference
=
319 router
->infinite_assert_metric
.metric_preference
;
320 up
->rpf
.source_nexthop
.mrib_route_metric
=
321 router
->infinite_assert_metric
.route_metric
;
322 pim_addr_to_prefix(&up
->rpf
.rpf_addr
, PIMADDR_ANY
);
323 pim_upstream_mroute_iif_update(up
->channel_oil
, __func__
);
328 RFC 4601: 4.1.6. State Summarization Macros
331 if ( I_Am_Assert_Loser(S, G, RPF_interface(S) )) {
332 return AssertWinner(S, G, RPF_interface(S) )
334 return NBR( RPF_interface(S), MRIB.next_hop( S ) )
338 RPF'(*,G) and RPF'(S,G) indicate the neighbor from which data
339 packets should be coming and to which joins should be sent on the RP
340 tree and SPT, respectively.
342 static pim_addr
pim_rpf_find_rpf_addr(struct pim_upstream
*up
)
344 struct pim_ifchannel
*rpf_ch
;
345 struct pim_neighbor
*neigh
;
348 if (!up
->rpf
.source_nexthop
.interface
) {
349 zlog_warn("%s: missing RPF interface for upstream (S,G)=%s",
350 __func__
, up
->sg_str
);
355 rpf_ch
= pim_ifchannel_find(up
->rpf
.source_nexthop
.interface
, &up
->sg
);
357 if (rpf_ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
358 return rpf_ch
->ifassert_winner
;
362 /* return NBR( RPF_interface(S), MRIB.next_hop( S ) ) */
364 neigh
= pim_if_find_neighbor(up
->rpf
.source_nexthop
.interface
,
365 up
->rpf
.source_nexthop
.mrib_nexthop_addr
);
367 rpf_addr
= neigh
->source_addr
;
369 rpf_addr
= PIMADDR_ANY
;
374 int pim_rpf_addr_is_inaddr_any(struct pim_rpf
*rpf
)
376 pim_addr rpf_addr
= pim_addr_from_prefix(&rpf
->rpf_addr
);
378 switch (rpf
->rpf_addr
.family
) {
381 return pim_addr_is_any(rpf_addr
);
387 int pim_rpf_is_same(struct pim_rpf
*rpf1
, struct pim_rpf
*rpf2
)
389 if (rpf1
->source_nexthop
.interface
== rpf2
->source_nexthop
.interface
)
395 unsigned int pim_rpf_hash_key(const void *arg
)
397 const struct pim_nexthop_cache
*r
= arg
;
400 return jhash_1word(r
->rpf
.rpf_addr
.u
.prefix4
.s_addr
, 0);
402 return jhash2(r
->rpf
.rpf_addr
.u
.prefix6
.s6_addr32
,
403 array_size(r
->rpf
.rpf_addr
.u
.prefix6
.s6_addr32
), 0);
407 bool pim_rpf_equal(const void *arg1
, const void *arg2
)
409 const struct pim_nexthop_cache
*r1
=
410 (const struct pim_nexthop_cache
*)arg1
;
411 const struct pim_nexthop_cache
*r2
=
412 (const struct pim_nexthop_cache
*)arg2
;
414 return prefix_same(&r1
->rpf
.rpf_addr
, &r2
->rpf
.rpf_addr
);