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 long long last_route_change_time
= -1;
41 long long nexthop_lookups_avoided
= 0;
43 static struct in_addr
pim_rpf_find_rpf_addr(struct pim_upstream
*up
);
45 void pim_rpf_set_refresh_time(void)
47 last_route_change_time
= pim_time_monotonic_usec();
49 zlog_debug("%s: New last route change time: %lld",
50 __PRETTY_FUNCTION__
, last_route_change_time
);
53 int pim_nexthop_lookup(struct pim_instance
*pim
, struct pim_nexthop
*nexthop
,
54 struct in_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;
64 if ((nexthop
->last_lookup
.s_addr
== addr
.s_addr
)
65 && (nexthop
->last_lookup_time
> last_route_change_time
)) {
66 if (PIM_DEBUG_TRACE
) {
67 char addr_str
[INET_ADDRSTRLEN
];
68 pim_inet4_dump("<addr?>", addr
, addr_str
,
70 char nexthop_str
[PREFIX_STRLEN
];
71 pim_addr_dump("<nexthop?>", &nexthop
->mrib_nexthop_addr
,
72 nexthop_str
, sizeof(nexthop_str
));
74 "%s: Using last lookup for %s at %lld, %lld addr%s",
75 __PRETTY_FUNCTION__
, addr_str
,
76 nexthop
->last_lookup_time
,
77 last_route_change_time
, nexthop_str
);
79 nexthop_lookups_avoided
++;
82 if (PIM_DEBUG_TRACE
) {
83 char addr_str
[INET_ADDRSTRLEN
];
84 pim_inet4_dump("<addr?>", addr
, addr_str
,
87 "%s: Looking up: %s, last lookup time: %lld, %lld",
88 __PRETTY_FUNCTION__
, addr_str
,
89 nexthop
->last_lookup_time
,
90 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) {
99 char addr_str
[INET_ADDRSTRLEN
];
100 pim_inet4_dump("<addr?>", addr
, addr_str
, sizeof(addr_str
));
102 "%s %s: could not find nexthop ifindex for address %s",
103 __FILE__
, __PRETTY_FUNCTION__
, addr_str
);
107 while (!found
&& (i
< num_ifindex
)) {
108 first_ifindex
= nexthop_tab
[i
].ifindex
;
110 ifp
= if_lookup_by_index(first_ifindex
, pim
->vrf_id
);
112 if (PIM_DEBUG_ZEBRA
) {
113 char addr_str
[INET_ADDRSTRLEN
];
114 pim_inet4_dump("<addr?>", addr
, addr_str
,
117 "%s %s: could not find interface for ifindex %d (address %s)",
118 __FILE__
, __PRETTY_FUNCTION__
,
119 first_ifindex
, addr_str
);
126 if (PIM_DEBUG_ZEBRA
) {
127 char addr_str
[INET_ADDRSTRLEN
];
128 pim_inet4_dump("<addr?>", addr
, addr_str
,
131 "%s: multicast not enabled on input interface %s (ifindex=%d, RPF for source %s)",
132 __PRETTY_FUNCTION__
, ifp
->name
,
133 first_ifindex
, addr_str
);
136 } else if (neighbor_needed
137 && !pim_if_connected_to_source(ifp
, addr
)) {
138 nbr
= pim_neighbor_find(
139 ifp
, nexthop_tab
[i
].nexthop_addr
.u
.prefix4
);
140 if (PIM_DEBUG_PIM_TRACE_DETAIL
)
141 zlog_debug("ifp name: %s, pim nbr: %p",
143 if (!nbr
&& !if_is_loopback(ifp
))
152 if (PIM_DEBUG_ZEBRA
) {
153 char nexthop_str
[PREFIX_STRLEN
];
154 char addr_str
[INET_ADDRSTRLEN
];
155 pim_addr_dump("<nexthop?>",
156 &nexthop_tab
[i
].nexthop_addr
, nexthop_str
,
157 sizeof(nexthop_str
));
158 pim_inet4_dump("<addr?>", addr
, addr_str
,
161 "%s %s: found nexthop %s for address %s: interface %s ifindex=%d metric=%d pref=%d",
162 __FILE__
, __PRETTY_FUNCTION__
, nexthop_str
,
163 addr_str
, ifp
->name
, first_ifindex
,
164 nexthop_tab
[i
].route_metric
,
165 nexthop_tab
[i
].protocol_distance
);
167 /* update nextop data */
168 nexthop
->interface
= ifp
;
169 nexthop
->mrib_nexthop_addr
= nexthop_tab
[i
].nexthop_addr
;
170 nexthop
->mrib_metric_preference
=
171 nexthop_tab
[i
].protocol_distance
;
172 nexthop
->mrib_route_metric
= nexthop_tab
[i
].route_metric
;
173 nexthop
->last_lookup
= addr
;
174 nexthop
->last_lookup_time
= pim_time_monotonic_usec();
181 static int nexthop_mismatch(const struct pim_nexthop
*nh1
,
182 const struct pim_nexthop
*nh2
)
184 return (nh1
->interface
!= nh2
->interface
)
185 || (nh1
->mrib_nexthop_addr
.u
.prefix4
.s_addr
186 != nh2
->mrib_nexthop_addr
.u
.prefix4
.s_addr
)
187 || (nh1
->mrib_metric_preference
!= nh2
->mrib_metric_preference
)
188 || (nh1
->mrib_route_metric
!= nh2
->mrib_route_metric
);
191 enum pim_rpf_result
pim_rpf_update(struct pim_instance
*pim
,
192 struct pim_upstream
*up
, struct pim_rpf
*old
,
195 struct pim_rpf
*rpf
= &up
->rpf
;
196 struct pim_rpf saved
;
198 struct pim_nexthop_cache pnc
;
199 struct prefix src
, grp
;
201 saved
.source_nexthop
= rpf
->source_nexthop
;
202 saved
.rpf_addr
= rpf
->rpf_addr
;
204 if (is_new
&& PIM_DEBUG_ZEBRA
) {
205 char source_str
[INET_ADDRSTRLEN
];
206 pim_inet4_dump("<source?>", up
->upstream_addr
, source_str
,
208 zlog_debug("%s: NHT Register upstream %s addr %s with Zebra.",
209 __PRETTY_FUNCTION__
, up
->sg_str
, source_str
);
211 /* Register addr with Zebra NHT */
212 nht_p
.family
= AF_INET
;
213 nht_p
.prefixlen
= IPV4_MAX_BITLEN
;
214 nht_p
.u
.prefix4
.s_addr
= up
->upstream_addr
.s_addr
;
216 src
.family
= AF_INET
;
217 src
.prefixlen
= IPV4_MAX_BITLEN
;
218 src
.u
.prefix4
= up
->upstream_addr
; // RP or Src address
219 grp
.family
= AF_INET
;
220 grp
.prefixlen
= IPV4_MAX_BITLEN
;
221 grp
.u
.prefix4
= up
->sg
.grp
;
222 memset(&pnc
, 0, sizeof(struct pim_nexthop_cache
));
223 if (pim_find_or_track_nexthop(pim
, &nht_p
, up
, NULL
, &pnc
)) {
224 if (pnc
.nexthop_num
) {
225 if (!pim_ecmp_nexthop_search(
226 pim
, &pnc
, &up
->rpf
.source_nexthop
, &src
,
228 !PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
)
229 && !PIM_UPSTREAM_FLAG_TEST_SRC_IGMP(
231 return PIM_RPF_FAILURE
;
234 if (!pim_ecmp_nexthop_lookup(
235 pim
, &rpf
->source_nexthop
, up
->upstream_addr
, &src
,
236 &grp
, !PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
)
237 && !PIM_UPSTREAM_FLAG_TEST_SRC_IGMP(
239 return PIM_RPF_FAILURE
;
242 rpf
->rpf_addr
.family
= AF_INET
;
243 rpf
->rpf_addr
.u
.prefix4
= pim_rpf_find_rpf_addr(up
);
244 if (pim_rpf_addr_is_inaddr_any(rpf
) && PIM_DEBUG_ZEBRA
) {
245 /* RPF'(S,G) not found */
246 zlog_debug("%s %s: RPF'%s not found: won't send join upstream",
247 __FILE__
, __PRETTY_FUNCTION__
, up
->sg_str
);
251 /* detect change in pim_nexthop */
252 if (nexthop_mismatch(&rpf
->source_nexthop
, &saved
.source_nexthop
)) {
254 if (PIM_DEBUG_ZEBRA
) {
255 char nhaddr_str
[PREFIX_STRLEN
];
256 pim_addr_dump("<addr?>",
257 &rpf
->source_nexthop
.mrib_nexthop_addr
,
258 nhaddr_str
, sizeof(nhaddr_str
));
259 zlog_debug("%s %s: (S,G)=%s source nexthop now is: interface=%s address=%s pref=%d metric=%d",
260 __FILE__
, __PRETTY_FUNCTION__
,
262 rpf
->source_nexthop
.interface
? rpf
->source_nexthop
.interface
->name
: "<ifname?>",
264 rpf
->source_nexthop
.mrib_metric_preference
,
265 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",
278 __FILE__
, __PRETTY_FUNCTION__
,
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 (saved
.rpf_addr
.u
.prefix4
.s_addr
!= rpf
->rpf_addr
.u
.prefix4
.s_addr
291 || saved
.source_nexthop
292 .interface
!= rpf
->source_nexthop
.interface
) {
294 /* return old rpf to caller ? */
296 old
->source_nexthop
= saved
.source_nexthop
;
297 old
->rpf_addr
= saved
.rpf_addr
;
299 return PIM_RPF_CHANGED
;
306 RFC 4601: 4.1.6. State Summarization Macros
309 if ( I_Am_Assert_Loser(S, G, RPF_interface(S) )) {
310 return AssertWinner(S, G, RPF_interface(S) )
312 return NBR( RPF_interface(S), MRIB.next_hop( S ) )
316 RPF'(*,G) and RPF'(S,G) indicate the neighbor from which data
317 packets should be coming and to which joins should be sent on the RP
318 tree and SPT, respectively.
320 static struct in_addr
pim_rpf_find_rpf_addr(struct pim_upstream
*up
)
322 struct pim_ifchannel
*rpf_ch
;
323 struct pim_neighbor
*neigh
;
324 struct in_addr rpf_addr
;
326 if (!up
->rpf
.source_nexthop
.interface
) {
327 zlog_warn("%s: missing RPF interface for upstream (S,G)=%s",
328 __PRETTY_FUNCTION__
, up
->sg_str
);
330 rpf_addr
.s_addr
= PIM_NET_INADDR_ANY
;
334 rpf_ch
= pim_ifchannel_find(up
->rpf
.source_nexthop
.interface
, &up
->sg
);
336 if (rpf_ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
337 return rpf_ch
->ifassert_winner
;
341 /* return NBR( RPF_interface(S), MRIB.next_hop( S ) ) */
343 neigh
= pim_if_find_neighbor(
344 up
->rpf
.source_nexthop
.interface
,
345 up
->rpf
.source_nexthop
.mrib_nexthop_addr
.u
.prefix4
);
347 rpf_addr
= neigh
->source_addr
;
349 rpf_addr
.s_addr
= PIM_NET_INADDR_ANY
;
354 int pim_rpf_addr_is_inaddr_none(struct pim_rpf
*rpf
)
356 switch (rpf
->rpf_addr
.family
) {
358 return rpf
->rpf_addr
.u
.prefix4
.s_addr
== INADDR_NONE
;
361 zlog_warn("%s: v6 Unimplmeneted", __PRETTY_FUNCTION__
);
372 int pim_rpf_addr_is_inaddr_any(struct pim_rpf
*rpf
)
374 switch (rpf
->rpf_addr
.family
) {
376 return rpf
->rpf_addr
.u
.prefix4
.s_addr
== INADDR_ANY
;
379 zlog_warn("%s: v6 Unimplmented", __PRETTY_FUNCTION__
);
390 int pim_rpf_is_same(struct pim_rpf
*rpf1
, struct pim_rpf
*rpf2
)
392 if (rpf1
->source_nexthop
.interface
== rpf2
->source_nexthop
.interface
)
398 unsigned int pim_rpf_hash_key(void *arg
)
400 struct pim_nexthop_cache
*r
= (struct pim_nexthop_cache
*)arg
;
402 return jhash_1word(r
->rpf
.rpf_addr
.u
.prefix4
.s_addr
, 0);
405 int pim_rpf_equal(const void *arg1
, const void *arg2
)
407 const struct pim_nexthop_cache
*r1
=
408 (const struct pim_nexthop_cache
*)arg1
;
409 const struct pim_nexthop_cache
*r2
=
410 (const struct pim_nexthop_cache
*)arg2
;
412 return prefix_same(&r1
->rpf
.rpf_addr
, &r2
->rpf
.rpf_addr
);