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"
39 static long long last_route_change_time
= -1;
40 long long nexthop_lookups_avoided
= 0;
42 static struct in_addr
pim_rpf_find_rpf_addr(struct pim_upstream
*up
);
44 void pim_rpf_set_refresh_time(void)
46 last_route_change_time
= pim_time_monotonic_usec();
48 zlog_debug("%s: New last route change time: %lld",
49 __PRETTY_FUNCTION__
, last_route_change_time
);
52 int pim_nexthop_lookup(struct pim_instance
*pim
, struct pim_nexthop
*nexthop
,
53 struct in_addr addr
, int neighbor_needed
)
55 struct pim_zlookup_nexthop nexthop_tab
[MULTIPATH_NUM
];
56 struct pim_neighbor
*nbr
= NULL
;
58 struct interface
*ifp
= NULL
;
59 ifindex_t first_ifindex
= 0;
63 if ((nexthop
->last_lookup
.s_addr
== addr
.s_addr
)
64 && (nexthop
->last_lookup_time
> last_route_change_time
)) {
65 if (PIM_DEBUG_TRACE
) {
66 char addr_str
[INET_ADDRSTRLEN
];
67 pim_inet4_dump("<addr?>", addr
, addr_str
,
69 char nexthop_str
[PREFIX_STRLEN
];
70 pim_addr_dump("<nexthop?>", &nexthop
->mrib_nexthop_addr
,
71 nexthop_str
, sizeof(nexthop_str
));
73 "%s: Using last lookup for %s at %lld, %lld addr%s",
74 __PRETTY_FUNCTION__
, addr_str
,
75 nexthop
->last_lookup_time
,
76 last_route_change_time
, nexthop_str
);
78 nexthop_lookups_avoided
++;
81 if (PIM_DEBUG_TRACE
) {
82 char addr_str
[INET_ADDRSTRLEN
];
83 pim_inet4_dump("<addr?>", addr
, addr_str
,
86 "%s: Looking up: %s, last lookup time: %lld, %lld",
87 __PRETTY_FUNCTION__
, addr_str
,
88 nexthop
->last_lookup_time
,
89 last_route_change_time
);
93 memset(nexthop_tab
, 0,
94 sizeof(struct pim_zlookup_nexthop
) * MULTIPATH_NUM
);
95 num_ifindex
= zclient_lookup_nexthop(nexthop_tab
, MULTIPATH_NUM
, addr
,
96 PIM_NEXTHOP_LOOKUP_MAX
);
97 if (num_ifindex
< 1) {
98 char addr_str
[INET_ADDRSTRLEN
];
99 pim_inet4_dump("<addr?>", addr
, addr_str
, sizeof(addr_str
));
101 "%s %s: could not find nexthop ifindex for address %s",
102 __FILE__
, __PRETTY_FUNCTION__
, addr_str
);
106 while (!found
&& (i
< num_ifindex
)) {
107 first_ifindex
= nexthop_tab
[i
].ifindex
;
109 ifp
= if_lookup_by_index(first_ifindex
, pimg
->vrf_id
);
111 if (PIM_DEBUG_ZEBRA
) {
112 char addr_str
[INET_ADDRSTRLEN
];
113 pim_inet4_dump("<addr?>", addr
, addr_str
,
116 "%s %s: could not find interface for ifindex %d (address %s)",
117 __FILE__
, __PRETTY_FUNCTION__
,
118 first_ifindex
, addr_str
);
125 if (PIM_DEBUG_ZEBRA
) {
126 char addr_str
[INET_ADDRSTRLEN
];
127 pim_inet4_dump("<addr?>", addr
, addr_str
,
130 "%s: multicast not enabled on input interface %s (ifindex=%d, RPF for source %s)",
131 __PRETTY_FUNCTION__
, ifp
->name
,
132 first_ifindex
, addr_str
);
135 } else if (neighbor_needed
136 && !pim_if_connected_to_source(ifp
, addr
)) {
137 nbr
= pim_neighbor_find(
138 ifp
, nexthop_tab
[i
].nexthop_addr
.u
.prefix4
);
139 if (PIM_DEBUG_PIM_TRACE_DETAIL
)
140 zlog_debug("ifp name: %s, pim nbr: %p",
142 if (!nbr
&& !if_is_loopback(ifp
))
151 if (PIM_DEBUG_ZEBRA
) {
152 char nexthop_str
[PREFIX_STRLEN
];
153 char addr_str
[INET_ADDRSTRLEN
];
154 pim_addr_dump("<nexthop?>",
155 &nexthop_tab
[i
].nexthop_addr
, nexthop_str
,
156 sizeof(nexthop_str
));
157 pim_inet4_dump("<addr?>", addr
, addr_str
,
160 "%s %s: found nexthop %s for address %s: interface %s ifindex=%d metric=%d pref=%d",
161 __FILE__
, __PRETTY_FUNCTION__
, nexthop_str
,
162 addr_str
, ifp
->name
, first_ifindex
,
163 nexthop_tab
[i
].route_metric
,
164 nexthop_tab
[i
].protocol_distance
);
166 /* update nextop data */
167 nexthop
->interface
= ifp
;
168 nexthop
->mrib_nexthop_addr
= nexthop_tab
[i
].nexthop_addr
;
169 nexthop
->mrib_metric_preference
=
170 nexthop_tab
[i
].protocol_distance
;
171 nexthop
->mrib_route_metric
= nexthop_tab
[i
].route_metric
;
172 nexthop
->last_lookup
= addr
;
173 nexthop
->last_lookup_time
= pim_time_monotonic_usec();
180 static int nexthop_mismatch(const struct pim_nexthop
*nh1
,
181 const struct pim_nexthop
*nh2
)
183 return (nh1
->interface
!= nh2
->interface
)
184 || (nh1
->mrib_nexthop_addr
.u
.prefix4
.s_addr
185 != nh2
->mrib_nexthop_addr
.u
.prefix4
.s_addr
)
186 || (nh1
->mrib_metric_preference
!= nh2
->mrib_metric_preference
)
187 || (nh1
->mrib_route_metric
!= nh2
->mrib_route_metric
);
190 enum pim_rpf_result
pim_rpf_update(struct pim_upstream
*up
, struct pim_rpf
*old
,
193 struct pim_rpf
*rpf
= &up
->rpf
;
194 struct pim_rpf saved
;
196 struct pim_nexthop_cache pnc
;
197 struct prefix src
, grp
;
199 saved
.source_nexthop
= rpf
->source_nexthop
;
200 saved
.rpf_addr
= rpf
->rpf_addr
;
202 if (is_new
&& PIM_DEBUG_ZEBRA
) {
203 char source_str
[INET_ADDRSTRLEN
];
204 pim_inet4_dump("<source?>", up
->upstream_addr
, source_str
,
206 zlog_debug("%s: NHT Register upstream %s addr %s with Zebra.",
207 __PRETTY_FUNCTION__
, up
->sg_str
, source_str
);
209 /* Register addr with Zebra NHT */
210 nht_p
.family
= AF_INET
;
211 nht_p
.prefixlen
= IPV4_MAX_BITLEN
;
212 nht_p
.u
.prefix4
.s_addr
= up
->upstream_addr
.s_addr
;
214 src
.family
= AF_INET
;
215 src
.prefixlen
= IPV4_MAX_BITLEN
;
216 src
.u
.prefix4
= up
->upstream_addr
; // RP or Src address
217 grp
.family
= AF_INET
;
218 grp
.prefixlen
= IPV4_MAX_BITLEN
;
219 grp
.u
.prefix4
= up
->sg
.grp
;
220 memset(&pnc
, 0, sizeof(struct pim_nexthop_cache
));
221 if (pim_find_or_track_nexthop(pimg
, &nht_p
, up
, NULL
, &pnc
)) {
222 if (pnc
.nexthop_num
) {
223 if (!pim_ecmp_nexthop_search(
224 pimg
, &pnc
, &up
->rpf
.source_nexthop
, &src
,
226 !PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
)
227 && !PIM_UPSTREAM_FLAG_TEST_SRC_IGMP(
229 return PIM_RPF_FAILURE
;
232 if (!pim_ecmp_nexthop_lookup(
233 pimg
, &rpf
->source_nexthop
, up
->upstream_addr
, &src
,
234 &grp
, !PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
)
235 && !PIM_UPSTREAM_FLAG_TEST_SRC_IGMP(
237 return PIM_RPF_FAILURE
;
240 rpf
->rpf_addr
.family
= AF_INET
;
241 rpf
->rpf_addr
.u
.prefix4
= pim_rpf_find_rpf_addr(up
);
242 if (pim_rpf_addr_is_inaddr_any(rpf
) && PIM_DEBUG_ZEBRA
) {
243 /* RPF'(S,G) not found */
244 zlog_debug("%s %s: RPF'%s not found: won't send join upstream",
245 __FILE__
, __PRETTY_FUNCTION__
, up
->sg_str
);
249 /* detect change in pim_nexthop */
250 if (nexthop_mismatch(&rpf
->source_nexthop
, &saved
.source_nexthop
)) {
252 if (PIM_DEBUG_ZEBRA
) {
253 char nhaddr_str
[PREFIX_STRLEN
];
254 pim_addr_dump("<addr?>",
255 &rpf
->source_nexthop
.mrib_nexthop_addr
,
256 nhaddr_str
, sizeof(nhaddr_str
));
257 zlog_debug("%s %s: (S,G)=%s source nexthop now is: interface=%s address=%s pref=%d metric=%d",
258 __FILE__
, __PRETTY_FUNCTION__
,
260 rpf
->source_nexthop
.interface
? rpf
->source_nexthop
.interface
->name
: "<ifname?>",
262 rpf
->source_nexthop
.mrib_metric_preference
,
263 rpf
->source_nexthop
.mrib_route_metric
);
266 pim_upstream_update_join_desired(pimg
, 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",
276 __FILE__
, __PRETTY_FUNCTION__
,
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 (saved
.rpf_addr
.u
.prefix4
.s_addr
!= rpf
->rpf_addr
.u
.prefix4
.s_addr
289 || saved
.source_nexthop
290 .interface
!= rpf
->source_nexthop
.interface
) {
292 /* return old rpf to caller ? */
294 old
->source_nexthop
= saved
.source_nexthop
;
295 old
->rpf_addr
= saved
.rpf_addr
;
297 return PIM_RPF_CHANGED
;
304 RFC 4601: 4.1.6. State Summarization Macros
307 if ( I_Am_Assert_Loser(S, G, RPF_interface(S) )) {
308 return AssertWinner(S, G, RPF_interface(S) )
310 return NBR( RPF_interface(S), MRIB.next_hop( S ) )
314 RPF'(*,G) and RPF'(S,G) indicate the neighbor from which data
315 packets should be coming and to which joins should be sent on the RP
316 tree and SPT, respectively.
318 static struct in_addr
pim_rpf_find_rpf_addr(struct pim_upstream
*up
)
320 struct pim_ifchannel
*rpf_ch
;
321 struct pim_neighbor
*neigh
;
322 struct in_addr rpf_addr
;
324 if (!up
->rpf
.source_nexthop
.interface
) {
325 zlog_warn("%s: missing RPF interface for upstream (S,G)=%s",
326 __PRETTY_FUNCTION__
, up
->sg_str
);
328 rpf_addr
.s_addr
= PIM_NET_INADDR_ANY
;
332 rpf_ch
= pim_ifchannel_find(up
->rpf
.source_nexthop
.interface
, &up
->sg
);
334 if (rpf_ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
335 return rpf_ch
->ifassert_winner
;
339 /* return NBR( RPF_interface(S), MRIB.next_hop( S ) ) */
341 neigh
= pim_if_find_neighbor(
342 up
->rpf
.source_nexthop
.interface
,
343 up
->rpf
.source_nexthop
.mrib_nexthop_addr
.u
.prefix4
);
345 rpf_addr
= neigh
->source_addr
;
347 rpf_addr
.s_addr
= PIM_NET_INADDR_ANY
;
352 int pim_rpf_addr_is_inaddr_none(struct pim_rpf
*rpf
)
354 switch (rpf
->rpf_addr
.family
) {
356 return rpf
->rpf_addr
.u
.prefix4
.s_addr
== INADDR_NONE
;
359 zlog_warn("%s: v6 Unimplmeneted", __PRETTY_FUNCTION__
);
370 int pim_rpf_addr_is_inaddr_any(struct pim_rpf
*rpf
)
372 switch (rpf
->rpf_addr
.family
) {
374 return rpf
->rpf_addr
.u
.prefix4
.s_addr
== INADDR_ANY
;
377 zlog_warn("%s: v6 Unimplmented", __PRETTY_FUNCTION__
);
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(void *arg
)
398 struct pim_nexthop_cache
*r
= (struct pim_nexthop_cache
*)arg
;
400 return jhash_1word(r
->rpf
.rpf_addr
.u
.prefix4
.s_addr
, 0);
403 int pim_rpf_equal(const void *arg1
, const void *arg2
)
405 const struct pim_nexthop_cache
*r1
=
406 (const struct pim_nexthop_cache
*)arg1
;
407 const struct pim_nexthop_cache
*r2
=
408 (const struct pim_nexthop_cache
*)arg2
;
410 return prefix_same(&r1
->rpf
.rpf_addr
, &r2
->rpf
.rpf_addr
);