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
[MULTIPATH_NUM
];
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
) * MULTIPATH_NUM
);
96 num_ifindex
= zclient_lookup_nexthop(pim
, nexthop_tab
, MULTIPATH_NUM
,
97 addr
, PIM_NEXTHOP_LOOKUP_MAX
);
98 if (num_ifindex
< 1) {
100 "%s %s: could not find nexthop ifindex for address %pPAs",
101 __FILE__
, __func__
, &addr
);
105 while (!found
&& (i
< num_ifindex
)) {
106 first_ifindex
= nexthop_tab
[i
].ifindex
;
108 ifp
= if_lookup_by_index(first_ifindex
, pim
->vrf
->vrf_id
);
112 "%s %s: could not find interface for ifindex %d (address %pPAs)",
113 __FILE__
, __func__
, first_ifindex
,
122 "%s: multicast not enabled on input interface %s (ifindex=%d, RPF for source %pPAs)",
123 __func__
, ifp
->name
, first_ifindex
,
126 } else if (neighbor_needed
127 && !pim_if_connected_to_source(ifp
, addr
)) {
128 nbr
= pim_neighbor_find(ifp
,
129 nexthop_tab
[i
].nexthop_addr
);
130 if (PIM_DEBUG_PIM_TRACE_DETAIL
)
131 zlog_debug("ifp name: %s, pim nbr: %p",
133 if (!nbr
&& !if_is_loopback(ifp
))
144 "%s %s: found nexthop %pPAs for address %pPAs: interface %s ifindex=%d metric=%d pref=%d",
146 &nexthop_tab
[i
].nexthop_addr
, &addr
, ifp
->name
,
147 first_ifindex
, nexthop_tab
[i
].route_metric
,
148 nexthop_tab
[i
].protocol_distance
);
150 /* update nexthop data */
151 nexthop
->interface
= ifp
;
152 nexthop
->mrib_nexthop_addr
= nexthop_tab
[i
].nexthop_addr
;
153 nexthop
->mrib_metric_preference
=
154 nexthop_tab
[i
].protocol_distance
;
155 nexthop
->mrib_route_metric
= nexthop_tab
[i
].route_metric
;
156 nexthop
->last_lookup
= addr
;
157 nexthop
->last_lookup_time
= pim_time_monotonic_usec();
164 static int nexthop_mismatch(const struct pim_nexthop
*nh1
,
165 const struct pim_nexthop
*nh2
)
167 return (nh1
->interface
!= nh2
->interface
) ||
168 (pim_addr_cmp(nh1
->mrib_nexthop_addr
, nh2
->mrib_nexthop_addr
)) ||
169 (nh1
->mrib_metric_preference
!= nh2
->mrib_metric_preference
) ||
170 (nh1
->mrib_route_metric
!= nh2
->mrib_route_metric
);
173 static void pim_rpf_cost_change(struct pim_instance
*pim
,
174 struct pim_upstream
*up
, uint32_t old_cost
)
176 struct pim_rpf
*rpf
= &up
->rpf
;
179 new_cost
= pim_up_mlag_local_cost(up
);
182 "%s: Cost_to_rp of upstream-%s changed to:%u, from:%u",
183 __func__
, up
->sg_str
, new_cost
, old_cost
);
185 if (old_cost
== new_cost
)
188 /* Cost changed, it might Impact MLAG DF election, update */
191 "%s: Cost_to_rp of upstream-%s changed to:%u",
192 __func__
, up
->sg_str
,
193 rpf
->source_nexthop
.mrib_route_metric
);
195 if (pim_up_mlag_is_local(up
))
196 pim_mlag_up_local_add(pim
, up
);
199 enum pim_rpf_result
pim_rpf_update(struct pim_instance
*pim
,
200 struct pim_upstream
*up
, struct pim_rpf
*old
,
203 struct pim_rpf
*rpf
= &up
->rpf
;
204 struct pim_rpf saved
;
206 struct prefix src
, grp
;
207 bool neigh_needed
= true;
208 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 pim_addr_to_prefix(&nht_p
, up
->upstream_addr
);
230 pim_addr_to_prefix(&src
, up
->upstream_addr
); // RP or Src address
231 pim_addr_to_prefix(&grp
, up
->sg
.grp
);
233 if ((pim_addr_is_any(up
->sg
.src
) && I_am_RP(pim
, up
->sg
.grp
)) ||
234 PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
))
235 neigh_needed
= false;
236 pim_find_or_track_nexthop(pim
, &nht_p
, up
, NULL
, NULL
);
237 if (!pim_ecmp_nexthop_lookup(pim
, &rpf
->source_nexthop
, &src
, &grp
,
239 /* Route is Deleted in Zebra, reset the stored NH data */
240 pim_upstream_rpf_clear(pim
, up
);
241 pim_rpf_cost_change(pim
, up
, saved_mrib_route_metric
);
242 return PIM_RPF_FAILURE
;
245 rpf_addr
= pim_rpf_find_rpf_addr(up
);
246 pim_addr_to_prefix(&rpf
->rpf_addr
, rpf_addr
);
248 if (pim_rpf_addr_is_inaddr_any(rpf
) && PIM_DEBUG_ZEBRA
) {
249 /* RPF'(S,G) not found */
250 zlog_debug("%s(%s): RPF'%s not found: won't send join upstream",
251 __func__
, caller
, up
->sg_str
);
255 /* detect change in pim_nexthop */
256 if (nexthop_mismatch(&rpf
->source_nexthop
, &saved
.source_nexthop
)) {
259 zlog_debug("%s(%s): (S,G)=%s source nexthop now is: interface=%s address=%pPAs pref=%d metric=%d",
262 rpf
->source_nexthop
.interface
? rpf
->source_nexthop
.interface
->name
: "<ifname?>",
263 &rpf
->source_nexthop
.mrib_nexthop_addr
,
264 rpf
->source_nexthop
.mrib_metric_preference
,
265 rpf
->source_nexthop
.mrib_route_metric
);
267 pim_upstream_update_join_desired(pim
, up
);
268 pim_upstream_update_could_assert(up
);
269 pim_upstream_update_my_assert_metric(up
);
272 /* detect change in RPF_interface(S) */
273 if (saved
.source_nexthop
.interface
!= rpf
->source_nexthop
.interface
) {
275 if (PIM_DEBUG_ZEBRA
) {
276 zlog_debug("%s(%s): (S,G)=%s RPF_interface(S) changed from %s to %s",
279 saved
.source_nexthop
.interface
? saved
.source_nexthop
.interface
->name
: "<oldif?>",
280 rpf
->source_nexthop
.interface
? rpf
->source_nexthop
.interface
->name
: "<newif?>");
284 pim_upstream_rpf_interface_changed(
285 up
, saved
.source_nexthop
.interface
);
288 /* detect change in RPF'(S,G) */
289 if (!prefix_same(&saved
.rpf_addr
, &rpf
->rpf_addr
) ||
290 saved
.source_nexthop
.interface
!= rpf
->source_nexthop
.interface
) {
291 pim_rpf_cost_change(pim
, up
, saved_mrib_route_metric
);
292 return PIM_RPF_CHANGED
;
297 "%s(%s): Cost_to_rp of upstream-%s changed to:%u",
298 __func__
, caller
, up
->sg_str
,
299 rpf
->source_nexthop
.mrib_route_metric
);
301 pim_rpf_cost_change(pim
, up
, saved_mrib_route_metric
);
307 * In the case of RP deletion and RP unreachablity,
308 * uninstall the mroute in the kernel and clear the
309 * rpf information in the pim upstream and pim channel
310 * oil data structure.
312 void pim_upstream_rpf_clear(struct pim_instance
*pim
,
313 struct pim_upstream
*up
)
315 if (up
->rpf
.source_nexthop
.interface
) {
316 pim_upstream_switch(pim
, up
, PIM_UPSTREAM_NOTJOINED
);
317 up
->rpf
.source_nexthop
.interface
= NULL
;
318 up
->rpf
.source_nexthop
.mrib_nexthop_addr
= PIMADDR_ANY
;
319 up
->rpf
.source_nexthop
.mrib_metric_preference
=
320 router
->infinite_assert_metric
.metric_preference
;
321 up
->rpf
.source_nexthop
.mrib_route_metric
=
322 router
->infinite_assert_metric
.route_metric
;
323 pim_addr_to_prefix(&up
->rpf
.rpf_addr
, PIMADDR_ANY
);
324 pim_upstream_mroute_iif_update(up
->channel_oil
, __func__
);
329 RFC 4601: 4.1.6. State Summarization Macros
332 if ( I_Am_Assert_Loser(S, G, RPF_interface(S) )) {
333 return AssertWinner(S, G, RPF_interface(S) )
335 return NBR( RPF_interface(S), MRIB.next_hop( S ) )
339 RPF'(*,G) and RPF'(S,G) indicate the neighbor from which data
340 packets should be coming and to which joins should be sent on the RP
341 tree and SPT, respectively.
343 static pim_addr
pim_rpf_find_rpf_addr(struct pim_upstream
*up
)
345 struct pim_ifchannel
*rpf_ch
;
346 struct pim_neighbor
*neigh
;
349 if (!up
->rpf
.source_nexthop
.interface
) {
350 zlog_warn("%s: missing RPF interface for upstream (S,G)=%s",
351 __func__
, up
->sg_str
);
356 rpf_ch
= pim_ifchannel_find(up
->rpf
.source_nexthop
.interface
, &up
->sg
);
358 if (rpf_ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
359 return rpf_ch
->ifassert_winner
;
363 /* return NBR( RPF_interface(S), MRIB.next_hop( S ) ) */
365 neigh
= pim_if_find_neighbor(up
->rpf
.source_nexthop
.interface
,
366 up
->rpf
.source_nexthop
.mrib_nexthop_addr
);
368 rpf_addr
= neigh
->source_addr
;
370 rpf_addr
= PIMADDR_ANY
;
375 int pim_rpf_addr_is_inaddr_any(struct pim_rpf
*rpf
)
377 pim_addr rpf_addr
= pim_addr_from_prefix(&rpf
->rpf_addr
);
379 switch (rpf
->rpf_addr
.family
) {
382 return pim_addr_is_any(rpf_addr
);
388 int pim_rpf_is_same(struct pim_rpf
*rpf1
, struct pim_rpf
*rpf2
)
390 if (rpf1
->source_nexthop
.interface
== rpf2
->source_nexthop
.interface
)
396 unsigned int pim_rpf_hash_key(const void *arg
)
398 const struct pim_nexthop_cache
*r
= arg
;
401 return jhash_1word(r
->rpf
.rpf_addr
.u
.prefix4
.s_addr
, 0);
403 return jhash2(r
->rpf
.rpf_addr
.u
.prefix6
.s6_addr32
,
404 array_size(r
->rpf
.rpf_addr
.u
.prefix6
.s6_addr32
), 0);
408 bool pim_rpf_equal(const void *arg1
, const void *arg2
)
410 const struct pim_nexthop_cache
*r1
=
411 (const struct pim_nexthop_cache
*)arg1
;
412 const struct pim_nexthop_cache
*r2
=
413 (const struct pim_nexthop_cache
*)arg2
;
415 return prefix_same(&r1
->rpf
.rpf_addr
, &r2
->rpf
.rpf_addr
);