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_prefix(
128 ifp
, &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 %pFX 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
);
148 /* update nexthop data */
149 nexthop
->interface
= ifp
;
150 nexthop
->mrib_nexthop_addr
= nexthop_tab
[i
].nexthop_addr
;
151 nexthop
->mrib_metric_preference
=
152 nexthop_tab
[i
].protocol_distance
;
153 nexthop
->mrib_route_metric
= nexthop_tab
[i
].route_metric
;
154 nexthop
->last_lookup
= addr
;
155 nexthop
->last_lookup_time
= pim_time_monotonic_usec();
162 static int nexthop_mismatch(const struct pim_nexthop
*nh1
,
163 const struct pim_nexthop
*nh2
)
165 pim_addr nh_addr1
= pim_addr_from_prefix(&nh1
->mrib_nexthop_addr
);
166 pim_addr nh_addr2
= pim_addr_from_prefix(&nh2
->mrib_nexthop_addr
);
168 return (nh1
->interface
!= nh2
->interface
) ||
169 (pim_addr_cmp(nh_addr1
, nh_addr2
)) ||
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
;
207 struct prefix src
, grp
;
208 bool neigh_needed
= true;
209 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 pim_addr_to_prefix(&nht_p
, up
->upstream_addr
);
231 pim_addr_to_prefix(&src
, up
->upstream_addr
); // RP or Src address
232 pim_addr_to_prefix(&grp
, up
->sg
.grp
);
234 if ((pim_addr_is_any(up
->sg
.src
) && I_am_RP(pim
, up
->sg
.grp
)) ||
235 PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
))
236 neigh_needed
= false;
237 pim_find_or_track_nexthop(pim
, &nht_p
, up
, NULL
, NULL
);
238 if (!pim_ecmp_nexthop_lookup(pim
, &rpf
->source_nexthop
, &src
, &grp
,
240 /* Route is Deleted in Zebra, reset the stored NH data */
241 pim_upstream_rpf_clear(pim
, up
);
242 pim_rpf_cost_change(pim
, up
, saved_mrib_route_metric
);
243 return PIM_RPF_FAILURE
;
246 rpf_addr
= pim_rpf_find_rpf_addr(up
);
247 pim_addr_to_prefix(&rpf
->rpf_addr
, rpf_addr
);
249 if (pim_rpf_addr_is_inaddr_any(rpf
) && PIM_DEBUG_ZEBRA
) {
250 /* RPF'(S,G) not found */
251 zlog_debug("%s(%s): RPF'%s not found: won't send join upstream",
252 __func__
, caller
, up
->sg_str
);
256 /* detect change in pim_nexthop */
257 if (nexthop_mismatch(&rpf
->source_nexthop
, &saved
.source_nexthop
)) {
260 zlog_debug("%s(%s): (S,G)=%s source nexthop now is: interface=%s address=%pFX pref=%d metric=%d",
263 rpf
->source_nexthop
.interface
? rpf
->source_nexthop
.interface
->name
: "<ifname?>",
264 &rpf
->source_nexthop
.mrib_nexthop_addr
,
265 rpf
->source_nexthop
.mrib_metric_preference
,
266 rpf
->source_nexthop
.mrib_route_metric
);
268 pim_upstream_update_join_desired(pim
, up
);
269 pim_upstream_update_could_assert(up
);
270 pim_upstream_update_my_assert_metric(up
);
273 /* detect change in RPF_interface(S) */
274 if (saved
.source_nexthop
.interface
!= rpf
->source_nexthop
.interface
) {
276 if (PIM_DEBUG_ZEBRA
) {
277 zlog_debug("%s(%s): (S,G)=%s RPF_interface(S) changed from %s to %s",
280 saved
.source_nexthop
.interface
? saved
.source_nexthop
.interface
->name
: "<oldif?>",
281 rpf
->source_nexthop
.interface
? rpf
->source_nexthop
.interface
->name
: "<newif?>");
285 pim_upstream_rpf_interface_changed(
286 up
, saved
.source_nexthop
.interface
);
289 /* detect change in RPF'(S,G) */
290 if (!prefix_same(&saved
.rpf_addr
, &rpf
->rpf_addr
) ||
291 saved
.source_nexthop
.interface
!= rpf
->source_nexthop
.interface
) {
292 pim_rpf_cost_change(pim
, up
, saved_mrib_route_metric
);
293 return PIM_RPF_CHANGED
;
298 "%s(%s): Cost_to_rp of upstream-%s changed to:%u",
299 __func__
, caller
, up
->sg_str
,
300 rpf
->source_nexthop
.mrib_route_metric
);
302 pim_rpf_cost_change(pim
, up
, saved_mrib_route_metric
);
308 * In the case of RP deletion and RP unreachablity,
309 * uninstall the mroute in the kernel and clear the
310 * rpf information in the pim upstream and pim channel
311 * oil data structure.
313 void pim_upstream_rpf_clear(struct pim_instance
*pim
,
314 struct pim_upstream
*up
)
316 if (up
->rpf
.source_nexthop
.interface
) {
317 pim_upstream_switch(pim
, up
, PIM_UPSTREAM_NOTJOINED
);
318 up
->rpf
.source_nexthop
.interface
= NULL
;
319 pim_addr_to_prefix(&up
->rpf
.source_nexthop
.mrib_nexthop_addr
,
321 up
->rpf
.source_nexthop
.mrib_metric_preference
=
322 router
->infinite_assert_metric
.metric_preference
;
323 up
->rpf
.source_nexthop
.mrib_route_metric
=
324 router
->infinite_assert_metric
.route_metric
;
325 pim_addr_to_prefix(&up
->rpf
.rpf_addr
, PIMADDR_ANY
);
326 pim_upstream_mroute_iif_update(up
->channel_oil
, __func__
);
331 RFC 4601: 4.1.6. State Summarization Macros
334 if ( I_Am_Assert_Loser(S, G, RPF_interface(S) )) {
335 return AssertWinner(S, G, RPF_interface(S) )
337 return NBR( RPF_interface(S), MRIB.next_hop( S ) )
341 RPF'(*,G) and RPF'(S,G) indicate the neighbor from which data
342 packets should be coming and to which joins should be sent on the RP
343 tree and SPT, respectively.
345 static pim_addr
pim_rpf_find_rpf_addr(struct pim_upstream
*up
)
347 struct pim_ifchannel
*rpf_ch
;
348 struct pim_neighbor
*neigh
;
351 if (!up
->rpf
.source_nexthop
.interface
) {
352 zlog_warn("%s: missing RPF interface for upstream (S,G)=%s",
353 __func__
, up
->sg_str
);
358 rpf_ch
= pim_ifchannel_find(up
->rpf
.source_nexthop
.interface
, &up
->sg
);
360 if (rpf_ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
361 return rpf_ch
->ifassert_winner
;
365 /* return NBR( RPF_interface(S), MRIB.next_hop( S ) ) */
370 pim_addr_from_prefix(&up
->rpf
.source_nexthop
.mrib_nexthop_addr
);
371 neigh
= pim_if_find_neighbor(up
->rpf
.source_nexthop
.interface
, nhaddr
);
373 rpf_addr
= neigh
->source_addr
;
375 rpf_addr
= PIMADDR_ANY
;
380 int pim_rpf_addr_is_inaddr_any(struct pim_rpf
*rpf
)
382 pim_addr rpf_addr
= pim_addr_from_prefix(&rpf
->rpf_addr
);
384 switch (rpf
->rpf_addr
.family
) {
387 return pim_addr_is_any(rpf_addr
);
393 int pim_rpf_is_same(struct pim_rpf
*rpf1
, struct pim_rpf
*rpf2
)
395 if (rpf1
->source_nexthop
.interface
== rpf2
->source_nexthop
.interface
)
401 unsigned int pim_rpf_hash_key(const void *arg
)
403 const struct pim_nexthop_cache
*r
= arg
;
406 return jhash_1word(r
->rpf
.rpf_addr
.u
.prefix4
.s_addr
, 0);
408 return jhash2(r
->rpf
.rpf_addr
.u
.prefix6
.s6_addr32
,
409 array_size(r
->rpf
.rpf_addr
.u
.prefix6
.s6_addr32
), 0);
413 bool pim_rpf_equal(const void *arg1
, const void *arg2
)
415 const struct pim_nexthop_cache
*r1
=
416 (const struct pim_nexthop_cache
*)arg1
;
417 const struct pim_nexthop_cache
*r2
=
418 (const struct pim_nexthop_cache
*)arg2
;
420 return prefix_same(&r1
->rpf
.rpf_addr
, &r2
->rpf
.rpf_addr
);