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"
38 static long long last_route_change_time
= -1;
39 long long nexthop_lookups_avoided
= 0;
41 static struct in_addr
pim_rpf_find_rpf_addr(struct pim_upstream
*up
);
44 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_nexthop
*nexthop
, 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;
62 if ((nexthop
->last_lookup
.s_addr
== addr
.s_addr
) &&
63 (nexthop
->last_lookup_time
> last_route_change_time
))
67 char addr_str
[INET_ADDRSTRLEN
];
68 pim_inet4_dump("<addr?>", addr
, addr_str
, sizeof(addr_str
));
69 zlog_debug ("%s: Using last lookup for %s at %lld, %lld",
72 nexthop
->last_lookup_time
,
73 last_route_change_time
);
75 nexthop_lookups_avoided
++;
82 char addr_str
[INET_ADDRSTRLEN
];
83 pim_inet4_dump("<addr?>", addr
, addr_str
, sizeof(addr_str
));
84 zlog_debug ("%s: Looking up: %s, last lookup time: %lld, %lld",
87 nexthop
->last_lookup_time
,
88 last_route_change_time
);
92 memset (nexthop_tab
, 0, sizeof (struct pim_zlookup_nexthop
) * MULTIPATH_NUM
);
93 num_ifindex
= zclient_lookup_nexthop(nexthop_tab
,
95 addr
, PIM_NEXTHOP_LOOKUP_MAX
);
96 if (num_ifindex
< 1) {
97 char addr_str
[INET_ADDRSTRLEN
];
98 pim_inet4_dump("<addr?>", addr
, addr_str
, sizeof(addr_str
));
99 zlog_warn("%s %s: could not find nexthop ifindex for address %s",
100 __FILE__
, __PRETTY_FUNCTION__
,
105 while (!found
&& (i
< num_ifindex
))
107 first_ifindex
= nexthop_tab
[i
].ifindex
;
109 ifp
= if_lookup_by_index(first_ifindex
);
114 char addr_str
[INET_ADDRSTRLEN
];
115 pim_inet4_dump("<addr?>", addr
, addr_str
, sizeof(addr_str
));
116 zlog_debug("%s %s: could not find interface for ifindex %d (address %s)",
117 __FILE__
, __PRETTY_FUNCTION__
,
118 first_ifindex
, addr_str
);
128 char addr_str
[INET_ADDRSTRLEN
];
129 pim_inet4_dump("<addr?>", addr
, addr_str
, sizeof(addr_str
));
130 zlog_debug("%s: multicast not enabled on input interface %s (ifindex=%d, RPF for source %s)",
132 ifp
->name
, first_ifindex
, addr_str
);
136 else if (neighbor_needed
&& !pim_if_connected_to_source (ifp
, addr
))
138 nbr
= pim_neighbor_find (ifp
, nexthop_tab
[i
].nexthop_addr
.u
.prefix4
);
139 if (PIM_DEBUG_PIM_TRACE_DETAIL
)
140 zlog_debug ("ifp name: %s, pim nbr: %p", ifp
->name
, nbr
);
141 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?>", &nexthop_tab
[i
].nexthop_addr
, nexthop_str
, sizeof(nexthop_str
));
156 pim_inet4_dump("<addr?>", addr
, addr_str
, sizeof(addr_str
));
157 zlog_debug("%s %s: found nexthop %s for address %s: interface %s ifindex=%d metric=%d pref=%d",
158 __FILE__
, __PRETTY_FUNCTION__
,
159 nexthop_str
, addr_str
,
160 ifp
->name
, first_ifindex
,
161 nexthop_tab
[i
].route_metric
,
162 nexthop_tab
[i
].protocol_distance
);
164 /* update nextop data */
165 nexthop
->interface
= ifp
;
166 nexthop
->mrib_nexthop_addr
= nexthop_tab
[i
].nexthop_addr
;
167 nexthop
->mrib_metric_preference
= nexthop_tab
[i
].protocol_distance
;
168 nexthop
->mrib_route_metric
= nexthop_tab
[i
].route_metric
;
169 nexthop
->last_lookup
= addr
;
170 nexthop
->last_lookup_time
= pim_time_monotonic_usec();
178 static int nexthop_mismatch(const struct pim_nexthop
*nh1
,
179 const struct pim_nexthop
*nh2
)
181 return (nh1
->interface
!= nh2
->interface
) ||
182 (nh1
->mrib_nexthop_addr
.u
.prefix4
.s_addr
!= nh2
->mrib_nexthop_addr
.u
.prefix4
.s_addr
) ||
183 (nh1
->mrib_metric_preference
!= nh2
->mrib_metric_preference
) ||
184 (nh1
->mrib_route_metric
!= nh2
->mrib_route_metric
);
187 enum pim_rpf_result
pim_rpf_update(struct pim_upstream
*up
, struct pim_rpf
*old
)
189 struct pim_rpf
*rpf
= &up
->rpf
;
190 struct pim_rpf saved
;
192 saved
.source_nexthop
= rpf
->source_nexthop
;
193 saved
.rpf_addr
= rpf
->rpf_addr
;
195 if (pim_nexthop_lookup(&rpf
->source_nexthop
,
197 !PIM_UPSTREAM_FLAG_TEST_FHR (up
->flags
) &&
198 !PIM_UPSTREAM_FLAG_TEST_SRC_IGMP (up
->flags
))) {
199 return PIM_RPF_FAILURE
;
202 rpf
->rpf_addr
.family
= AF_INET
;
203 rpf
->rpf_addr
.u
.prefix4
= pim_rpf_find_rpf_addr(up
);
204 if (pim_rpf_addr_is_inaddr_any(rpf
) && PIM_DEBUG_ZEBRA
) {
205 /* RPF'(S,G) not found */
206 zlog_debug("%s %s: RPF'%s not found: won't send join upstream",
207 __FILE__
, __PRETTY_FUNCTION__
,
212 /* detect change in pim_nexthop */
213 if (nexthop_mismatch(&rpf
->source_nexthop
, &saved
.source_nexthop
)) {
215 if (PIM_DEBUG_ZEBRA
) {
216 char nhaddr_str
[PREFIX_STRLEN
];
217 pim_addr_dump("<addr?>", &rpf
->source_nexthop
.mrib_nexthop_addr
, nhaddr_str
, sizeof(nhaddr_str
));
218 zlog_debug("%s %s: (S,G)=%s source nexthop now is: interface=%s address=%s pref=%d metric=%d",
219 __FILE__
, __PRETTY_FUNCTION__
,
221 rpf
->source_nexthop
.interface
? rpf
->source_nexthop
.interface
->name
: "<ifname?>",
223 rpf
->source_nexthop
.mrib_metric_preference
,
224 rpf
->source_nexthop
.mrib_route_metric
);
227 pim_upstream_update_join_desired(up
);
228 pim_upstream_update_could_assert(up
);
229 pim_upstream_update_my_assert_metric(up
);
232 /* detect change in RPF_interface(S) */
233 if (saved
.source_nexthop
.interface
!= rpf
->source_nexthop
.interface
) {
235 if (PIM_DEBUG_ZEBRA
) {
236 zlog_debug("%s %s: (S,G)=%s RPF_interface(S) changed from %s to %s",
237 __FILE__
, __PRETTY_FUNCTION__
,
239 saved
.source_nexthop
.interface
? saved
.source_nexthop
.interface
->name
: "<oldif?>",
240 rpf
->source_nexthop
.interface
? rpf
->source_nexthop
.interface
->name
: "<newif?>");
244 pim_upstream_rpf_interface_changed(up
, saved
.source_nexthop
.interface
);
247 /* detect change in RPF'(S,G) */
248 if (saved
.rpf_addr
.u
.prefix4
.s_addr
!= rpf
->rpf_addr
.u
.prefix4
.s_addr
) {
250 /* return old rpf to caller ? */
253 old
->source_nexthop
= saved
.source_nexthop
;
254 old
->rpf_addr
= saved
.rpf_addr
;
256 return PIM_RPF_CHANGED
;
263 RFC 4601: 4.1.6. State Summarization Macros
266 if ( I_Am_Assert_Loser(S, G, RPF_interface(S) )) {
267 return AssertWinner(S, G, RPF_interface(S) )
269 return NBR( RPF_interface(S), MRIB.next_hop( S ) )
273 RPF'(*,G) and RPF'(S,G) indicate the neighbor from which data
274 packets should be coming and to which joins should be sent on the RP
275 tree and SPT, respectively.
277 static struct in_addr
pim_rpf_find_rpf_addr(struct pim_upstream
*up
)
279 struct pim_ifchannel
*rpf_ch
;
280 struct pim_neighbor
*neigh
;
281 struct in_addr rpf_addr
;
283 if (!up
->rpf
.source_nexthop
.interface
) {
284 zlog_warn("%s: missing RPF interface for upstream (S,G)=%s",
288 rpf_addr
.s_addr
= PIM_NET_INADDR_ANY
;
292 rpf_ch
= pim_ifchannel_find(up
->rpf
.source_nexthop
.interface
,
295 if (rpf_ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
296 return rpf_ch
->ifassert_winner
;
300 /* return NBR( RPF_interface(S), MRIB.next_hop( S ) ) */
302 neigh
= pim_if_find_neighbor(up
->rpf
.source_nexthop
.interface
,
303 up
->rpf
.source_nexthop
.mrib_nexthop_addr
.u
.prefix4
);
305 rpf_addr
= neigh
->source_addr
;
307 rpf_addr
.s_addr
= PIM_NET_INADDR_ANY
;
313 pim_rpf_addr_is_inaddr_none (struct pim_rpf
*rpf
)
315 switch (rpf
->rpf_addr
.family
)
318 return rpf
->rpf_addr
.u
.prefix4
.s_addr
== INADDR_NONE
;
321 zlog_warn ("%s: v6 Unimplmeneted", __PRETTY_FUNCTION__
);
333 pim_rpf_addr_is_inaddr_any (struct pim_rpf
*rpf
)
335 switch (rpf
->rpf_addr
.family
)
338 return rpf
->rpf_addr
.u
.prefix4
.s_addr
== INADDR_ANY
;
341 zlog_warn ("%s: v6 Unimplmented", __PRETTY_FUNCTION__
);
353 pim_rpf_is_same (struct pim_rpf
*rpf1
, struct pim_rpf
*rpf2
)
355 if (rpf1
->source_nexthop
.interface
== rpf2
->source_nexthop
.interface
)