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
16 along with this program; see the file COPYING; if not, write to the
17 Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
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
);
45 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_nexthop
*nexthop
, 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
))
68 char addr_str
[INET_ADDRSTRLEN
];
69 pim_inet4_dump("<addr?>", addr
, addr_str
, sizeof(addr_str
));
70 char nexthop_str
[PREFIX_STRLEN
];
71 pim_addr_dump("<nexthop?>", &nexthop
->mrib_nexthop_addr
,
72 nexthop_str
, sizeof(nexthop_str
));
73 zlog_debug ("%s: Using last lookup for %s at %lld, %lld addr%s",
76 nexthop
->last_lookup_time
,
77 last_route_change_time
, nexthop_str
);
79 nexthop_lookups_avoided
++;
86 char addr_str
[INET_ADDRSTRLEN
];
87 pim_inet4_dump("<addr?>", addr
, addr_str
, sizeof(addr_str
));
88 zlog_debug ("%s: Looking up: %s, last lookup time: %lld, %lld",
91 nexthop
->last_lookup_time
,
92 last_route_change_time
);
96 memset (nexthop_tab
, 0, sizeof (struct pim_zlookup_nexthop
) * MULTIPATH_NUM
);
97 num_ifindex
= zclient_lookup_nexthop(nexthop_tab
,
99 addr
, PIM_NEXTHOP_LOOKUP_MAX
);
100 if (num_ifindex
< 1) {
101 char addr_str
[INET_ADDRSTRLEN
];
102 pim_inet4_dump("<addr?>", addr
, addr_str
, sizeof(addr_str
));
103 zlog_warn("%s %s: could not find nexthop ifindex for address %s",
104 __FILE__
, __PRETTY_FUNCTION__
,
109 while (!found
&& (i
< num_ifindex
))
111 first_ifindex
= nexthop_tab
[i
].ifindex
;
113 ifp
= if_lookup_by_index(first_ifindex
, VRF_DEFAULT
);
118 char addr_str
[INET_ADDRSTRLEN
];
119 pim_inet4_dump("<addr?>", addr
, addr_str
, sizeof(addr_str
));
120 zlog_debug("%s %s: could not find interface for ifindex %d (address %s)",
121 __FILE__
, __PRETTY_FUNCTION__
,
122 first_ifindex
, addr_str
);
132 char addr_str
[INET_ADDRSTRLEN
];
133 pim_inet4_dump("<addr?>", addr
, addr_str
, sizeof(addr_str
));
134 zlog_debug("%s: multicast not enabled on input interface %s (ifindex=%d, RPF for source %s)",
136 ifp
->name
, first_ifindex
, addr_str
);
140 else if (neighbor_needed
&& !pim_if_connected_to_source (ifp
, addr
))
142 nbr
= pim_neighbor_find (ifp
, nexthop_tab
[i
].nexthop_addr
.u
.prefix4
);
143 if (PIM_DEBUG_PIM_TRACE_DETAIL
)
144 zlog_debug ("ifp name: %s, pim nbr: %p", ifp
->name
, nbr
);
145 if (!nbr
&& !if_is_loopback (ifp
))
156 if (PIM_DEBUG_ZEBRA
) {
157 char nexthop_str
[PREFIX_STRLEN
];
158 char addr_str
[INET_ADDRSTRLEN
];
159 pim_addr_dump("<nexthop?>", &nexthop_tab
[i
].nexthop_addr
, nexthop_str
, sizeof(nexthop_str
));
160 pim_inet4_dump("<addr?>", addr
, addr_str
, sizeof(addr_str
));
161 zlog_debug("%s %s: found nexthop %s for address %s: interface %s ifindex=%d metric=%d pref=%d",
162 __FILE__
, __PRETTY_FUNCTION__
,
163 nexthop_str
, addr_str
,
164 ifp
->name
, first_ifindex
,
165 nexthop_tab
[i
].route_metric
,
166 nexthop_tab
[i
].protocol_distance
);
168 /* update nextop data */
169 nexthop
->interface
= ifp
;
170 nexthop
->mrib_nexthop_addr
= nexthop_tab
[i
].nexthop_addr
;
171 nexthop
->mrib_metric_preference
= 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();
182 static int nexthop_mismatch(const struct pim_nexthop
*nh1
,
183 const struct pim_nexthop
*nh2
)
185 return (nh1
->interface
!= nh2
->interface
) ||
186 (nh1
->mrib_nexthop_addr
.u
.prefix4
.s_addr
!= 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_upstream
*up
, struct pim_rpf
*old
, uint8_t is_new
)
193 struct pim_rpf
*rpf
= &up
->rpf
;
194 struct pim_rpf saved
;
196 struct pim_nexthop_cache pnc
;
198 struct prefix src
, grp
;
200 saved
.source_nexthop
= rpf
->source_nexthop
;
201 saved
.rpf_addr
= rpf
->rpf_addr
;
203 if (is_new
&& PIM_DEBUG_ZEBRA
)
205 char source_str
[INET_ADDRSTRLEN
];
206 pim_inet4_dump ("<source?>", up
->upstream_addr
, source_str
,
207 sizeof (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 ((ret
= pim_find_or_track_nexthop (&nht_p
, up
, NULL
, &pnc
)) == 1)
227 //Compute PIM RPF using Cached nexthop
228 if (pim_ecmp_nexthop_search (&pnc
, &up
->rpf
.source_nexthop
,
230 !PIM_UPSTREAM_FLAG_TEST_FHR (up
->flags
) &&
231 !PIM_UPSTREAM_FLAG_TEST_SRC_IGMP (up
->flags
)))
234 return PIM_RPF_FAILURE
;
240 if (pim_ecmp_nexthop_lookup (&rpf
->source_nexthop
,
241 up
->upstream_addr
, &src
, &grp
,
242 !PIM_UPSTREAM_FLAG_TEST_FHR (up
->flags
) &&
243 !PIM_UPSTREAM_FLAG_TEST_SRC_IGMP (up
->
246 return PIM_RPF_FAILURE
;
250 rpf
->rpf_addr
.family
= AF_INET
;
251 rpf
->rpf_addr
.u
.prefix4
= pim_rpf_find_rpf_addr(up
);
252 if (pim_rpf_addr_is_inaddr_any(rpf
) && PIM_DEBUG_ZEBRA
) {
253 /* RPF'(S,G) not found */
254 zlog_debug("%s %s: RPF'%s not found: won't send join upstream",
255 __FILE__
, __PRETTY_FUNCTION__
,
260 /* detect change in pim_nexthop */
261 if (nexthop_mismatch(&rpf
->source_nexthop
, &saved
.source_nexthop
)) {
263 if (PIM_DEBUG_ZEBRA
) {
264 char nhaddr_str
[PREFIX_STRLEN
];
265 pim_addr_dump("<addr?>", &rpf
->source_nexthop
.mrib_nexthop_addr
, nhaddr_str
, sizeof(nhaddr_str
));
266 zlog_debug("%s %s: (S,G)=%s source nexthop now is: interface=%s address=%s pref=%d metric=%d",
267 __FILE__
, __PRETTY_FUNCTION__
,
269 rpf
->source_nexthop
.interface
? rpf
->source_nexthop
.interface
->name
: "<ifname?>",
271 rpf
->source_nexthop
.mrib_metric_preference
,
272 rpf
->source_nexthop
.mrib_route_metric
);
275 pim_upstream_update_join_desired(up
);
276 pim_upstream_update_could_assert(up
);
277 pim_upstream_update_my_assert_metric(up
);
280 /* detect change in RPF_interface(S) */
281 if (saved
.source_nexthop
.interface
!= rpf
->source_nexthop
.interface
) {
283 if (PIM_DEBUG_ZEBRA
) {
284 zlog_debug("%s %s: (S,G)=%s RPF_interface(S) changed from %s to %s",
285 __FILE__
, __PRETTY_FUNCTION__
,
287 saved
.source_nexthop
.interface
? saved
.source_nexthop
.interface
->name
: "<oldif?>",
288 rpf
->source_nexthop
.interface
? rpf
->source_nexthop
.interface
->name
: "<newif?>");
292 pim_upstream_rpf_interface_changed(up
, saved
.source_nexthop
.interface
);
295 /* detect change in RPF'(S,G) */
296 if (saved
.rpf_addr
.u
.prefix4
.s_addr
!= rpf
->rpf_addr
.u
.prefix4
.s_addr
||
297 saved
.source_nexthop
.interface
!= rpf
->source_nexthop
.interface
)
300 /* return old rpf to caller ? */
303 old
->source_nexthop
= saved
.source_nexthop
;
304 old
->rpf_addr
= saved
.rpf_addr
;
306 return PIM_RPF_CHANGED
;
313 RFC 4601: 4.1.6. State Summarization Macros
316 if ( I_Am_Assert_Loser(S, G, RPF_interface(S) )) {
317 return AssertWinner(S, G, RPF_interface(S) )
319 return NBR( RPF_interface(S), MRIB.next_hop( S ) )
323 RPF'(*,G) and RPF'(S,G) indicate the neighbor from which data
324 packets should be coming and to which joins should be sent on the RP
325 tree and SPT, respectively.
327 static struct in_addr
pim_rpf_find_rpf_addr(struct pim_upstream
*up
)
329 struct pim_ifchannel
*rpf_ch
;
330 struct pim_neighbor
*neigh
;
331 struct in_addr rpf_addr
;
333 if (!up
->rpf
.source_nexthop
.interface
) {
334 zlog_warn("%s: missing RPF interface for upstream (S,G)=%s",
338 rpf_addr
.s_addr
= PIM_NET_INADDR_ANY
;
342 rpf_ch
= pim_ifchannel_find(up
->rpf
.source_nexthop
.interface
,
345 if (rpf_ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
346 return rpf_ch
->ifassert_winner
;
350 /* return NBR( RPF_interface(S), MRIB.next_hop( S ) ) */
352 neigh
= pim_if_find_neighbor(up
->rpf
.source_nexthop
.interface
,
353 up
->rpf
.source_nexthop
.mrib_nexthop_addr
.u
.prefix4
);
355 rpf_addr
= neigh
->source_addr
;
357 rpf_addr
.s_addr
= PIM_NET_INADDR_ANY
;
363 pim_rpf_addr_is_inaddr_none (struct pim_rpf
*rpf
)
365 switch (rpf
->rpf_addr
.family
)
368 return rpf
->rpf_addr
.u
.prefix4
.s_addr
== INADDR_NONE
;
371 zlog_warn ("%s: v6 Unimplmeneted", __PRETTY_FUNCTION__
);
383 pim_rpf_addr_is_inaddr_any (struct pim_rpf
*rpf
)
385 switch (rpf
->rpf_addr
.family
)
388 return rpf
->rpf_addr
.u
.prefix4
.s_addr
== INADDR_ANY
;
391 zlog_warn ("%s: v6 Unimplmented", __PRETTY_FUNCTION__
);
403 pim_rpf_is_same (struct pim_rpf
*rpf1
, struct pim_rpf
*rpf2
)
405 if (rpf1
->source_nexthop
.interface
== rpf2
->source_nexthop
.interface
)