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 (addr
.s_addr
== INADDR_NONE
)
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
) {
77 char nexthop_str
[PREFIX_STRLEN
];
78 pim_addr_dump("<nexthop?>", &nexthop
->mrib_nexthop_addr
,
79 nexthop_str
, sizeof(nexthop_str
));
81 "%s: Using last lookup for %pPAs at %lld, %" PRId64
" addr %s",
82 __func__
, &addr
, nexthop
->last_lookup_time
,
83 pim
->last_route_change_time
, nexthop_str
);
85 pim
->nexthop_lookups_avoided
++;
88 if (PIM_DEBUG_PIM_NHT
)
90 "%s: Looking up: %pPAs, last lookup time: %lld, %" PRId64
,
91 __func__
, &addr
, nexthop
->last_lookup_time
,
92 pim
->last_route_change_time
);
95 memset(nexthop_tab
, 0,
96 sizeof(struct pim_zlookup_nexthop
) * MULTIPATH_NUM
);
97 num_ifindex
= zclient_lookup_nexthop(pim
, nexthop_tab
, MULTIPATH_NUM
,
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_prefix(
130 ifp
, &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
))
143 if (PIM_DEBUG_ZEBRA
) {
144 char nexthop_str
[PREFIX_STRLEN
];
145 pim_addr_dump("<nexthop?>",
146 &nexthop_tab
[i
].nexthop_addr
, nexthop_str
,
147 sizeof(nexthop_str
));
149 "%s %s: found nexthop %s for address %pPAs: interface %s ifindex=%d metric=%d pref=%d",
150 __FILE__
, __func__
, nexthop_str
, &addr
,
151 ifp
->name
, first_ifindex
,
152 nexthop_tab
[i
].route_metric
,
153 nexthop_tab
[i
].protocol_distance
);
155 /* update nexthop data */
156 nexthop
->interface
= ifp
;
157 nexthop
->mrib_nexthop_addr
= nexthop_tab
[i
].nexthop_addr
;
158 nexthop
->mrib_metric_preference
=
159 nexthop_tab
[i
].protocol_distance
;
160 nexthop
->mrib_route_metric
= nexthop_tab
[i
].route_metric
;
161 nexthop
->last_lookup
= addr
;
162 nexthop
->last_lookup_time
= pim_time_monotonic_usec();
169 static int nexthop_mismatch(const struct pim_nexthop
*nh1
,
170 const struct pim_nexthop
*nh2
)
172 pim_addr nh_addr1
= pim_addr_from_prefix(&nh1
->mrib_nexthop_addr
);
173 pim_addr nh_addr2
= pim_addr_from_prefix(&nh2
->mrib_nexthop_addr
);
175 return (nh1
->interface
!= nh2
->interface
) ||
176 (pim_addr_cmp(nh_addr1
, nh_addr2
)) ||
177 (nh1
->mrib_metric_preference
!= nh2
->mrib_metric_preference
) ||
178 (nh1
->mrib_route_metric
!= nh2
->mrib_route_metric
);
181 static void pim_rpf_cost_change(struct pim_instance
*pim
,
182 struct pim_upstream
*up
, uint32_t old_cost
)
184 struct pim_rpf
*rpf
= &up
->rpf
;
187 new_cost
= pim_up_mlag_local_cost(up
);
190 "%s: Cost_to_rp of upstream-%s changed to:%u, from:%u",
191 __func__
, up
->sg_str
, new_cost
, old_cost
);
193 if (old_cost
== new_cost
)
196 /* Cost changed, it might Impact MLAG DF election, update */
199 "%s: Cost_to_rp of upstream-%s changed to:%u",
200 __func__
, up
->sg_str
,
201 rpf
->source_nexthop
.mrib_route_metric
);
203 if (pim_up_mlag_is_local(up
))
204 pim_mlag_up_local_add(pim
, up
);
207 enum pim_rpf_result
pim_rpf_update(struct pim_instance
*pim
,
208 struct pim_upstream
*up
, struct pim_rpf
*old
,
211 struct pim_rpf
*rpf
= &up
->rpf
;
212 struct pim_rpf saved
;
214 struct prefix src
, grp
;
215 bool neigh_needed
= true;
216 uint32_t saved_mrib_route_metric
;
218 pim_addr saved_rpf_addr
;
220 if (PIM_UPSTREAM_FLAG_TEST_STATIC_IIF(up
->flags
))
223 if (pim_addr_is_any(up
->upstream_addr
)) {
224 zlog_debug("%s(%s): RP is not configured yet for %s",
225 __func__
, caller
, up
->sg_str
);
229 saved
.source_nexthop
= rpf
->source_nexthop
;
230 saved
.rpf_addr
= rpf
->rpf_addr
;
231 saved_mrib_route_metric
= pim_up_mlag_local_cost(up
);
233 old
->source_nexthop
= saved
.source_nexthop
;
234 old
->rpf_addr
= saved
.rpf_addr
;
237 pim_addr_to_prefix(&nht_p
, up
->upstream_addr
);
239 pim_addr_to_prefix(&src
, up
->upstream_addr
); // RP or Src address
240 pim_addr_to_prefix(&grp
, up
->sg
.grp
);
242 if ((pim_addr_is_any(up
->sg
.src
) && I_am_RP(pim
, up
->sg
.grp
)) ||
243 PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
))
244 neigh_needed
= false;
245 pim_find_or_track_nexthop(pim
, &nht_p
, up
, NULL
, NULL
);
246 if (!pim_ecmp_nexthop_lookup(pim
, &rpf
->source_nexthop
, &src
, &grp
,
248 /* Route is Deleted in Zebra, reset the stored NH data */
249 pim_upstream_rpf_clear(pim
, up
);
250 pim_rpf_cost_change(pim
, up
, saved_mrib_route_metric
);
251 return PIM_RPF_FAILURE
;
254 rpf_addr
= pim_rpf_find_rpf_addr(up
);
255 pim_addr_to_prefix(&rpf
->rpf_addr
, rpf_addr
);
257 if (pim_rpf_addr_is_inaddr_any(rpf
) && PIM_DEBUG_ZEBRA
) {
258 /* RPF'(S,G) not found */
259 zlog_debug("%s(%s): RPF'%s not found: won't send join upstream",
260 __func__
, caller
, up
->sg_str
);
264 /* detect change in pim_nexthop */
265 if (nexthop_mismatch(&rpf
->source_nexthop
, &saved
.source_nexthop
)) {
267 if (PIM_DEBUG_ZEBRA
) {
268 char nhaddr_str
[PREFIX_STRLEN
];
269 pim_addr_dump("<addr?>",
270 &rpf
->source_nexthop
.mrib_nexthop_addr
,
271 nhaddr_str
, sizeof(nhaddr_str
));
272 zlog_debug("%s(%s): (S,G)=%s source nexthop now is: interface=%s address=%s pref=%d metric=%d",
275 rpf
->source_nexthop
.interface
? rpf
->source_nexthop
.interface
->name
: "<ifname?>",
277 rpf
->source_nexthop
.mrib_metric_preference
,
278 rpf
->source_nexthop
.mrib_route_metric
);
281 pim_upstream_update_join_desired(pim
, up
);
282 pim_upstream_update_could_assert(up
);
283 pim_upstream_update_my_assert_metric(up
);
286 /* detect change in RPF_interface(S) */
287 if (saved
.source_nexthop
.interface
!= rpf
->source_nexthop
.interface
) {
289 if (PIM_DEBUG_ZEBRA
) {
290 zlog_debug("%s(%s): (S,G)=%s RPF_interface(S) changed from %s to %s",
293 saved
.source_nexthop
.interface
? saved
.source_nexthop
.interface
->name
: "<oldif?>",
294 rpf
->source_nexthop
.interface
? rpf
->source_nexthop
.interface
->name
: "<newif?>");
298 pim_upstream_rpf_interface_changed(
299 up
, saved
.source_nexthop
.interface
);
302 /* detect change in RPF'(S,G) */
304 saved_rpf_addr
= pim_addr_from_prefix(&saved
.rpf_addr
);
306 if (pim_addr_cmp(saved_rpf_addr
, rpf_addr
) ||
307 saved
.source_nexthop
.interface
!= rpf
->source_nexthop
.interface
) {
308 pim_rpf_cost_change(pim
, up
, saved_mrib_route_metric
);
309 return PIM_RPF_CHANGED
;
314 "%s(%s): Cost_to_rp of upstream-%s changed to:%u",
315 __func__
, caller
, up
->sg_str
,
316 rpf
->source_nexthop
.mrib_route_metric
);
318 pim_rpf_cost_change(pim
, up
, saved_mrib_route_metric
);
324 * In the case of RP deletion and RP unreachablity,
325 * uninstall the mroute in the kernel and clear the
326 * rpf information in the pim upstream and pim channel
327 * oil data structure.
329 void pim_upstream_rpf_clear(struct pim_instance
*pim
,
330 struct pim_upstream
*up
)
332 if (up
->rpf
.source_nexthop
.interface
) {
333 pim_upstream_switch(pim
, up
, PIM_UPSTREAM_NOTJOINED
);
334 up
->rpf
.source_nexthop
.interface
= NULL
;
335 pim_addr_to_prefix(&up
->rpf
.source_nexthop
.mrib_nexthop_addr
,
337 up
->rpf
.source_nexthop
.mrib_metric_preference
=
338 router
->infinite_assert_metric
.metric_preference
;
339 up
->rpf
.source_nexthop
.mrib_route_metric
=
340 router
->infinite_assert_metric
.route_metric
;
341 pim_addr_to_prefix(&up
->rpf
.rpf_addr
, PIMADDR_ANY
);
342 pim_upstream_mroute_iif_update(up
->channel_oil
, __func__
);
347 RFC 4601: 4.1.6. State Summarization Macros
350 if ( I_Am_Assert_Loser(S, G, RPF_interface(S) )) {
351 return AssertWinner(S, G, RPF_interface(S) )
353 return NBR( RPF_interface(S), MRIB.next_hop( S ) )
357 RPF'(*,G) and RPF'(S,G) indicate the neighbor from which data
358 packets should be coming and to which joins should be sent on the RP
359 tree and SPT, respectively.
361 static pim_addr
pim_rpf_find_rpf_addr(struct pim_upstream
*up
)
363 struct pim_ifchannel
*rpf_ch
;
364 struct pim_neighbor
*neigh
;
367 if (!up
->rpf
.source_nexthop
.interface
) {
368 zlog_warn("%s: missing RPF interface for upstream (S,G)=%s",
369 __func__
, up
->sg_str
);
374 rpf_ch
= pim_ifchannel_find(up
->rpf
.source_nexthop
.interface
, &up
->sg
);
376 if (rpf_ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
377 return rpf_ch
->ifassert_winner
;
381 /* return NBR( RPF_interface(S), MRIB.next_hop( S ) ) */
386 pim_addr_from_prefix(&up
->rpf
.source_nexthop
.mrib_nexthop_addr
);
387 neigh
= pim_if_find_neighbor(up
->rpf
.source_nexthop
.interface
, nhaddr
);
389 rpf_addr
= neigh
->source_addr
;
391 rpf_addr
= PIMADDR_ANY
;
396 int pim_rpf_addr_is_inaddr_none(struct pim_rpf
*rpf
)
398 switch (rpf
->rpf_addr
.family
) {
400 return rpf
->rpf_addr
.u
.prefix4
.s_addr
== INADDR_NONE
;
402 zlog_warn("%s: v6 Unimplmeneted", __func__
);
409 int pim_rpf_addr_is_inaddr_any(struct pim_rpf
*rpf
)
411 pim_addr rpf_addr
= pim_addr_from_prefix(&rpf
->rpf_addr
);
413 switch (rpf
->rpf_addr
.family
) {
416 return pim_addr_is_any(rpf_addr
);
422 int pim_rpf_is_same(struct pim_rpf
*rpf1
, struct pim_rpf
*rpf2
)
424 if (rpf1
->source_nexthop
.interface
== rpf2
->source_nexthop
.interface
)
430 unsigned int pim_rpf_hash_key(const void *arg
)
432 const struct pim_nexthop_cache
*r
= arg
;
434 #if PIM_IPV == 4 || !defined(PIM_V6_TEMP_BREAK)
435 return jhash_1word(r
->rpf
.rpf_addr
.u
.prefix4
.s_addr
, 0);
437 return jhash2(r
->rpf
.rpf_addr
.u
.prefix6
.s6_addr32
,
438 array_size(r
->rpf
.rpf_addr
.u
.prefix6
.s6_addr32
), 0);
442 bool pim_rpf_equal(const void *arg1
, const void *arg2
)
444 const struct pim_nexthop_cache
*r1
=
445 (const struct pim_nexthop_cache
*)arg1
;
446 const struct pim_nexthop_cache
*r2
=
447 (const struct pim_nexthop_cache
*)arg2
;
449 return prefix_same(&r1
->rpf
.rpf_addr
, &r2
->rpf
.rpf_addr
);