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_zlookup.h"
35 #include "pim_ifchannel.h"
40 static struct in_addr
pim_rpf_find_rpf_addr(struct pim_upstream
*up
);
42 void pim_rpf_set_refresh_time(struct pim_instance
*pim
)
44 pim
->last_route_change_time
= pim_time_monotonic_usec();
46 zlog_debug("%s: vrf(%s) New last route change time: %" PRId64
,
47 __PRETTY_FUNCTION__
, pim
->vrf
->name
,
48 pim
->last_route_change_time
);
51 bool pim_nexthop_lookup(struct pim_instance
*pim
, struct pim_nexthop
*nexthop
,
52 struct in_addr addr
, int neighbor_needed
)
54 struct pim_zlookup_nexthop nexthop_tab
[MULTIPATH_NUM
];
55 struct pim_neighbor
*nbr
= NULL
;
57 struct interface
*ifp
= NULL
;
58 ifindex_t first_ifindex
= 0;
63 * We should not attempt to lookup a
64 * 255.255.255.255 address, since
67 if (addr
.s_addr
== INADDR_NONE
)
70 if ((nexthop
->last_lookup
.s_addr
== addr
.s_addr
)
71 && (nexthop
->last_lookup_time
> pim
->last_route_change_time
)) {
72 if (PIM_DEBUG_TRACE
) {
73 char addr_str
[INET_ADDRSTRLEN
];
74 pim_inet4_dump("<addr?>", addr
, addr_str
,
76 char nexthop_str
[PREFIX_STRLEN
];
77 pim_addr_dump("<nexthop?>", &nexthop
->mrib_nexthop_addr
,
78 nexthop_str
, sizeof(nexthop_str
));
80 "%s: Using last lookup for %s at %lld, %" PRId64
" addr %s",
81 __PRETTY_FUNCTION__
, addr_str
,
82 nexthop
->last_lookup_time
,
83 pim
->last_route_change_time
, nexthop_str
);
85 pim
->nexthop_lookups_avoided
++;
88 if (PIM_DEBUG_TRACE
) {
89 char addr_str
[INET_ADDRSTRLEN
];
90 pim_inet4_dump("<addr?>", addr
, addr_str
,
93 "%s: Looking up: %s, last lookup time: %lld, %" PRId64
,
94 __PRETTY_FUNCTION__
, addr_str
,
95 nexthop
->last_lookup_time
,
96 pim
->last_route_change_time
);
100 memset(nexthop_tab
, 0,
101 sizeof(struct pim_zlookup_nexthop
) * MULTIPATH_NUM
);
102 num_ifindex
= zclient_lookup_nexthop(pim
, nexthop_tab
, MULTIPATH_NUM
,
103 addr
, PIM_NEXTHOP_LOOKUP_MAX
);
104 if (num_ifindex
< 1) {
105 char addr_str
[INET_ADDRSTRLEN
];
106 pim_inet4_dump("<addr?>", addr
, addr_str
, sizeof(addr_str
));
108 "%s %s: could not find nexthop ifindex for address %s",
109 __FILE__
, __PRETTY_FUNCTION__
, addr_str
);
113 while (!found
&& (i
< num_ifindex
)) {
114 first_ifindex
= nexthop_tab
[i
].ifindex
;
116 ifp
= if_lookup_by_index(first_ifindex
, pim
->vrf_id
);
118 if (PIM_DEBUG_ZEBRA
) {
119 char addr_str
[INET_ADDRSTRLEN
];
120 pim_inet4_dump("<addr?>", addr
, addr_str
,
123 "%s %s: could not find interface for ifindex %d (address %s)",
124 __FILE__
, __PRETTY_FUNCTION__
,
125 first_ifindex
, addr_str
);
132 if (PIM_DEBUG_ZEBRA
) {
133 char addr_str
[INET_ADDRSTRLEN
];
134 pim_inet4_dump("<addr?>", addr
, addr_str
,
137 "%s: multicast not enabled on input interface %s (ifindex=%d, RPF for source %s)",
138 __PRETTY_FUNCTION__
, ifp
->name
,
139 first_ifindex
, addr_str
);
142 } else if (neighbor_needed
143 && !pim_if_connected_to_source(ifp
, addr
)) {
144 nbr
= pim_neighbor_find(
145 ifp
, nexthop_tab
[i
].nexthop_addr
.u
.prefix4
);
146 if (PIM_DEBUG_PIM_TRACE_DETAIL
)
147 zlog_debug("ifp name: %s, pim nbr: %p",
149 if (!nbr
&& !if_is_loopback(ifp
))
158 if (PIM_DEBUG_ZEBRA
) {
159 char nexthop_str
[PREFIX_STRLEN
];
160 char addr_str
[INET_ADDRSTRLEN
];
161 pim_addr_dump("<nexthop?>",
162 &nexthop_tab
[i
].nexthop_addr
, nexthop_str
,
163 sizeof(nexthop_str
));
164 pim_inet4_dump("<addr?>", addr
, addr_str
,
167 "%s %s: found nexthop %s for address %s: interface %s ifindex=%d metric=%d pref=%d",
168 __FILE__
, __PRETTY_FUNCTION__
, nexthop_str
,
169 addr_str
, ifp
->name
, first_ifindex
,
170 nexthop_tab
[i
].route_metric
,
171 nexthop_tab
[i
].protocol_distance
);
173 /* update nexthop data */
174 nexthop
->interface
= ifp
;
175 nexthop
->mrib_nexthop_addr
= nexthop_tab
[i
].nexthop_addr
;
176 nexthop
->mrib_metric_preference
=
177 nexthop_tab
[i
].protocol_distance
;
178 nexthop
->mrib_route_metric
= nexthop_tab
[i
].route_metric
;
179 nexthop
->last_lookup
= addr
;
180 nexthop
->last_lookup_time
= pim_time_monotonic_usec();
187 static int nexthop_mismatch(const struct pim_nexthop
*nh1
,
188 const struct pim_nexthop
*nh2
)
190 return (nh1
->interface
!= nh2
->interface
)
191 || (nh1
->mrib_nexthop_addr
.u
.prefix4
.s_addr
192 != nh2
->mrib_nexthop_addr
.u
.prefix4
.s_addr
)
193 || (nh1
->mrib_metric_preference
!= nh2
->mrib_metric_preference
)
194 || (nh1
->mrib_route_metric
!= nh2
->mrib_route_metric
);
197 enum pim_rpf_result
pim_rpf_update(struct pim_instance
*pim
,
198 struct pim_upstream
*up
, struct pim_rpf
*old
,
201 struct pim_rpf
*rpf
= &up
->rpf
;
202 struct pim_rpf saved
;
204 struct prefix src
, grp
;
205 bool neigh_needed
= true;
207 if (PIM_UPSTREAM_FLAG_TEST_STATIC_IIF(up
->flags
))
210 if (up
->upstream_addr
.s_addr
== INADDR_ANY
) {
211 zlog_debug("%s: RP is not configured yet for %s",
212 __PRETTY_FUNCTION__
, up
->sg_str
);
216 saved
.source_nexthop
= rpf
->source_nexthop
;
217 saved
.rpf_addr
= rpf
->rpf_addr
;
219 if (is_new
&& PIM_DEBUG_ZEBRA
) {
220 char source_str
[INET_ADDRSTRLEN
];
221 pim_inet4_dump("<source?>", up
->upstream_addr
, source_str
,
223 zlog_debug("%s: NHT Register upstream %s addr %s with Zebra.",
224 __PRETTY_FUNCTION__
, up
->sg_str
, source_str
);
226 /* Register addr with Zebra NHT */
227 nht_p
.family
= AF_INET
;
228 nht_p
.prefixlen
= IPV4_MAX_BITLEN
;
229 nht_p
.u
.prefix4
.s_addr
= up
->upstream_addr
.s_addr
;
231 src
.family
= AF_INET
;
232 src
.prefixlen
= IPV4_MAX_BITLEN
;
233 src
.u
.prefix4
= up
->upstream_addr
; // RP or Src address
234 grp
.family
= AF_INET
;
235 grp
.prefixlen
= IPV4_MAX_BITLEN
;
236 grp
.u
.prefix4
= up
->sg
.grp
;
238 if ((up
->sg
.src
.s_addr
== INADDR_ANY
&& I_am_RP(pim
, up
->sg
.grp
)) ||
239 PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
))
240 neigh_needed
= FALSE
;
241 pim_find_or_track_nexthop(pim
, &nht_p
, up
, NULL
, NULL
);
242 if (!pim_ecmp_nexthop_lookup(pim
, &rpf
->source_nexthop
, &src
, &grp
,
244 return PIM_RPF_FAILURE
;
246 rpf
->rpf_addr
.family
= AF_INET
;
247 rpf
->rpf_addr
.u
.prefix4
= pim_rpf_find_rpf_addr(up
);
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 __FILE__
, __PRETTY_FUNCTION__
, up
->sg_str
);
255 /* detect change in pim_nexthop */
256 if (nexthop_mismatch(&rpf
->source_nexthop
, &saved
.source_nexthop
)) {
258 if (PIM_DEBUG_ZEBRA
) {
259 char nhaddr_str
[PREFIX_STRLEN
];
260 pim_addr_dump("<addr?>",
261 &rpf
->source_nexthop
.mrib_nexthop_addr
,
262 nhaddr_str
, sizeof(nhaddr_str
));
263 zlog_debug("%s %s: (S,G)=%s source nexthop now is: interface=%s address=%s pref=%d metric=%d",
264 __FILE__
, __PRETTY_FUNCTION__
,
266 rpf
->source_nexthop
.interface
? rpf
->source_nexthop
.interface
->name
: "<ifname?>",
268 rpf
->source_nexthop
.mrib_metric_preference
,
269 rpf
->source_nexthop
.mrib_route_metric
);
272 pim_upstream_update_join_desired(pim
, up
);
273 pim_upstream_update_could_assert(up
);
274 pim_upstream_update_my_assert_metric(up
);
277 /* detect change in RPF_interface(S) */
278 if (saved
.source_nexthop
.interface
!= rpf
->source_nexthop
.interface
) {
280 if (PIM_DEBUG_ZEBRA
) {
281 zlog_debug("%s %s: (S,G)=%s RPF_interface(S) changed from %s to %s",
282 __FILE__
, __PRETTY_FUNCTION__
,
284 saved
.source_nexthop
.interface
? saved
.source_nexthop
.interface
->name
: "<oldif?>",
285 rpf
->source_nexthop
.interface
? rpf
->source_nexthop
.interface
->name
: "<newif?>");
289 pim_upstream_rpf_interface_changed(
290 up
, saved
.source_nexthop
.interface
);
293 /* detect change in RPF'(S,G) */
294 if (saved
.rpf_addr
.u
.prefix4
.s_addr
!= rpf
->rpf_addr
.u
.prefix4
.s_addr
295 || saved
.source_nexthop
296 .interface
!= rpf
->source_nexthop
.interface
) {
298 /* return old rpf to caller ? */
300 old
->source_nexthop
= saved
.source_nexthop
;
301 old
->rpf_addr
= saved
.rpf_addr
;
303 return PIM_RPF_CHANGED
;
310 * In the case of RP deletion and RP unreachablity,
311 * uninstall the mroute in the kernel and clear the
312 * rpf information in the pim upstream and pim channel
313 * oil data structure.
315 void pim_upstream_rpf_clear(struct pim_instance
*pim
,
316 struct pim_upstream
*up
)
318 if (up
->rpf
.source_nexthop
.interface
) {
319 if (up
->channel_oil
) {
320 up
->channel_oil
->oil
.mfcc_parent
= MAXVIFS
;
321 pim_mroute_del(up
->channel_oil
, __PRETTY_FUNCTION__
);
324 pim_upstream_switch(pim
, up
, PIM_UPSTREAM_NOTJOINED
);
325 up
->rpf
.source_nexthop
.interface
= NULL
;
326 up
->rpf
.source_nexthop
.mrib_nexthop_addr
.u
.prefix4
.s_addr
=
328 up
->rpf
.source_nexthop
.mrib_metric_preference
=
329 router
->infinite_assert_metric
.metric_preference
;
330 up
->rpf
.source_nexthop
.mrib_route_metric
=
331 router
->infinite_assert_metric
.route_metric
;
332 up
->rpf
.rpf_addr
.u
.prefix4
.s_addr
= PIM_NET_INADDR_ANY
;
337 RFC 4601: 4.1.6. State Summarization Macros
340 if ( I_Am_Assert_Loser(S, G, RPF_interface(S) )) {
341 return AssertWinner(S, G, RPF_interface(S) )
343 return NBR( RPF_interface(S), MRIB.next_hop( S ) )
347 RPF'(*,G) and RPF'(S,G) indicate the neighbor from which data
348 packets should be coming and to which joins should be sent on the RP
349 tree and SPT, respectively.
351 static struct in_addr
pim_rpf_find_rpf_addr(struct pim_upstream
*up
)
353 struct pim_ifchannel
*rpf_ch
;
354 struct pim_neighbor
*neigh
;
355 struct in_addr rpf_addr
;
357 if (!up
->rpf
.source_nexthop
.interface
) {
358 zlog_warn("%s: missing RPF interface for upstream (S,G)=%s",
359 __PRETTY_FUNCTION__
, up
->sg_str
);
361 rpf_addr
.s_addr
= PIM_NET_INADDR_ANY
;
365 rpf_ch
= pim_ifchannel_find(up
->rpf
.source_nexthop
.interface
, &up
->sg
);
367 if (rpf_ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
368 return rpf_ch
->ifassert_winner
;
372 /* return NBR( RPF_interface(S), MRIB.next_hop( S ) ) */
374 neigh
= pim_if_find_neighbor(
375 up
->rpf
.source_nexthop
.interface
,
376 up
->rpf
.source_nexthop
.mrib_nexthop_addr
.u
.prefix4
);
378 rpf_addr
= neigh
->source_addr
;
380 rpf_addr
.s_addr
= PIM_NET_INADDR_ANY
;
385 int pim_rpf_addr_is_inaddr_none(struct pim_rpf
*rpf
)
387 switch (rpf
->rpf_addr
.family
) {
389 return rpf
->rpf_addr
.u
.prefix4
.s_addr
== INADDR_NONE
;
392 zlog_warn("%s: v6 Unimplmeneted", __PRETTY_FUNCTION__
);
403 int pim_rpf_addr_is_inaddr_any(struct pim_rpf
*rpf
)
405 switch (rpf
->rpf_addr
.family
) {
407 return rpf
->rpf_addr
.u
.prefix4
.s_addr
== INADDR_ANY
;
410 zlog_warn("%s: v6 Unimplmented", __PRETTY_FUNCTION__
);
421 int pim_rpf_is_same(struct pim_rpf
*rpf1
, struct pim_rpf
*rpf2
)
423 if (rpf1
->source_nexthop
.interface
== rpf2
->source_nexthop
.interface
)
429 unsigned int pim_rpf_hash_key(const void *arg
)
431 const struct pim_nexthop_cache
*r
= arg
;
433 return jhash_1word(r
->rpf
.rpf_addr
.u
.prefix4
.s_addr
, 0);
436 bool pim_rpf_equal(const void *arg1
, const void *arg2
)
438 const struct pim_nexthop_cache
*r1
=
439 (const struct pim_nexthop_cache
*)arg1
;
440 const struct pim_nexthop_cache
*r2
=
441 (const struct pim_nexthop_cache
*)arg2
;
443 return prefix_same(&r1
->rpf
.rpf_addr
, &r2
->rpf
.rpf_addr
);