]>
Commit | Line | Data |
---|---|---|
fb018d25 DS |
1 | /* BGP Nexthop tracking |
2 | * Copyright (C) 2013 Cumulus Networks, Inc. | |
3 | * | |
4 | * This file is part of GNU Zebra. | |
5 | * | |
6 | * GNU Zebra is free software; you can redistribute it and/or modify it | |
7 | * under the terms of the GNU General Public License as published by the | |
8 | * Free Software Foundation; either version 2, or (at your option) any | |
9 | * later version. | |
10 | * | |
11 | * GNU Zebra is distributed in the hope that it will be useful, but | |
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * General Public License for more details. | |
15 | * | |
896014f4 DL |
16 | * You should have received a copy of the GNU General Public License along |
17 | * with this program; see the file COPYING; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
fb018d25 DS |
19 | */ |
20 | ||
21 | #include <zebra.h> | |
22 | ||
23 | #include "command.h" | |
24 | #include "thread.h" | |
25 | #include "prefix.h" | |
26 | #include "zclient.h" | |
27 | #include "stream.h" | |
28 | #include "network.h" | |
29 | #include "log.h" | |
30 | #include "memory.h" | |
31 | #include "nexthop.h" | |
7076bb2f | 32 | #include "vrf.h" |
039f3a34 | 33 | #include "filter.h" |
fb018d25 DS |
34 | |
35 | #include "bgpd/bgpd.h" | |
36 | #include "bgpd/bgp_table.h" | |
37 | #include "bgpd/bgp_route.h" | |
38 | #include "bgpd/bgp_attr.h" | |
39 | #include "bgpd/bgp_nexthop.h" | |
40 | #include "bgpd/bgp_debug.h" | |
41 | #include "bgpd/bgp_nht.h" | |
ffd0c037 | 42 | #include "bgpd/bgp_fsm.h" |
afbb1c59 | 43 | #include "bgpd/bgp_zebra.h" |
fb018d25 DS |
44 | |
45 | extern struct zclient *zclient; | |
fb018d25 | 46 | |
078430f6 DS |
47 | static void register_zebra_rnh(struct bgp_nexthop_cache *bnc, |
48 | int is_bgp_static_route); | |
49 | static void unregister_zebra_rnh(struct bgp_nexthop_cache *bnc, | |
50 | int is_bgp_static_route); | |
fb018d25 DS |
51 | static void evaluate_paths(struct bgp_nexthop_cache *bnc); |
52 | static int make_prefix(int afi, struct bgp_info *ri, struct prefix *p); | |
53 | static void path_nh_map(struct bgp_info *path, struct bgp_nexthop_cache *bnc, | |
54 | int keep); | |
55 | ||
d4d9d757 LB |
56 | static int |
57 | bgp_isvalid_nexthop (struct bgp_nexthop_cache *bnc) | |
58 | { | |
59 | return (bgp_zebra_num_connects() == 0 || | |
60 | (bnc && CHECK_FLAG(bnc->flags, BGP_NEXTHOP_VALID))); | |
61 | } | |
62 | ||
fb018d25 | 63 | int |
fc9a856f | 64 | bgp_find_nexthop (struct bgp_info *path, int connected) |
fb018d25 DS |
65 | { |
66 | struct bgp_nexthop_cache *bnc = path->nexthop; | |
67 | ||
68 | if (!bnc) | |
69 | return 0; | |
70 | ||
3f3971a9 DS |
71 | /* |
72 | * We are cheating here. Views have no associated underlying | |
73 | * ability to detect nexthops. So when we have a view | |
74 | * just tell everyone the nexthop is valid | |
75 | */ | |
76 | if (path->peer && path->peer->bgp->inst_type == BGP_INSTANCE_TYPE_VIEW) | |
77 | return 1; | |
78 | ||
fc9a856f DS |
79 | if (connected && !(CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED))) |
80 | return 0; | |
fb018d25 | 81 | |
d4d9d757 | 82 | return (bgp_isvalid_nexthop(bnc)); |
fb018d25 DS |
83 | } |
84 | ||
f9164b1d PJ |
85 | static void |
86 | bgp_unlink_nexthop_check (struct bgp_nexthop_cache *bnc) | |
fb018d25 | 87 | { |
fc9a856f | 88 | if (LIST_EMPTY(&(bnc->paths)) && !bnc->nht_info) |
fb018d25 DS |
89 | { |
90 | if (BGP_DEBUG(nht, NHT)) | |
91 | { | |
4690c7d7 | 92 | char buf[PREFIX2STR_BUFFER]; |
fb018d25 | 93 | zlog_debug("bgp_unlink_nexthop: freeing bnc %s", |
4690c7d7 | 94 | bnc_str(bnc, buf, PREFIX2STR_BUFFER)); |
fb018d25 | 95 | } |
078430f6 | 96 | unregister_zebra_rnh(bnc, CHECK_FLAG(bnc->flags, BGP_STATIC_ROUTE)); |
fb018d25 DS |
97 | bnc->node->info = NULL; |
98 | bgp_unlock_node(bnc->node); | |
f9164b1d | 99 | bnc->node = NULL; |
fb018d25 DS |
100 | bnc_free(bnc); |
101 | } | |
102 | } | |
103 | ||
f9164b1d PJ |
104 | void |
105 | bgp_unlink_nexthop (struct bgp_info *path) | |
106 | { | |
107 | struct bgp_nexthop_cache *bnc = path->nexthop; | |
108 | ||
109 | if (!bnc) | |
110 | return; | |
111 | ||
112 | path_nh_map(path, NULL, 0); | |
113 | ||
114 | bgp_unlink_nexthop_check (bnc); | |
115 | } | |
116 | ||
117 | void | |
118 | bgp_unlink_nexthop_by_peer (struct peer *peer) | |
119 | { | |
120 | struct prefix p; | |
121 | struct bgp_node *rn; | |
122 | struct bgp_nexthop_cache *bnc; | |
123 | afi_t afi = family2afi(peer->su.sa.sa_family); | |
124 | ||
235022cb | 125 | if (! sockunion2hostprefix (&peer->su, &p)) |
f9164b1d PJ |
126 | return; |
127 | ||
128 | rn = bgp_node_get (peer->bgp->nexthop_cache_table[afi], &p); | |
129 | ||
130 | if (!rn->info) | |
131 | return; | |
132 | ||
133 | bnc = rn->info; | |
134 | ||
135 | /* cleanup the peer reference */ | |
136 | bnc->nht_info = NULL; | |
137 | ||
138 | bgp_unlink_nexthop_check (bnc); | |
139 | } | |
140 | ||
fb018d25 | 141 | int |
75aead62 DS |
142 | bgp_find_or_add_nexthop (struct bgp *bgp, afi_t afi, struct bgp_info *ri, |
143 | struct peer *peer, int connected) | |
fb018d25 DS |
144 | { |
145 | struct bgp_node *rn; | |
146 | struct bgp_nexthop_cache *bnc; | |
147 | struct prefix p; | |
078430f6 | 148 | int is_bgp_static_route = 0; |
fb018d25 | 149 | |
fc9a856f DS |
150 | if (ri) |
151 | { | |
8a92a8a0 DS |
152 | is_bgp_static_route = ((ri->type == ZEBRA_ROUTE_BGP) && |
153 | (ri->sub_type == BGP_ROUTE_STATIC)) ? 1 : 0; | |
154 | ||
155 | /* Since Extended Next-hop Encoding (RFC5549) support, we want to derive | |
156 | address-family from the next-hop. */ | |
157 | if (!is_bgp_static_route) | |
158 | afi = BGP_ATTR_NEXTHOP_AFI_IP6(ri->attr) ? AFI_IP6 : AFI_IP; | |
159 | ||
65740e1b | 160 | /* This will return TRUE if the global IPv6 NH is a link local addr */ |
fc9a856f DS |
161 | if (make_prefix(afi, ri, &p) < 0) |
162 | return 1; | |
163 | } | |
164 | else if (peer) | |
165 | { | |
235022cb RW |
166 | /* Don't register link local NH */ |
167 | if (afi == AFI_IP6 && IN6_IS_ADDR_LINKLOCAL (&peer->su.sin6.sin6_addr)) | |
168 | return 1; | |
65740e1b | 169 | |
235022cb | 170 | if (! sockunion2hostprefix (&peer->su, &p)) |
65740e1b DS |
171 | { |
172 | if (BGP_DEBUG(nht, NHT)) | |
173 | { | |
174 | zlog_debug("%s: Attempting to register with unknown AFI %d (not %d or %d)", | |
175 | __FUNCTION__, afi, AFI_IP, AFI_IP6); | |
176 | } | |
177 | return 0; | |
fc9a856f DS |
178 | } |
179 | } | |
65740e1b DS |
180 | else |
181 | return 0; | |
fc9a856f | 182 | |
078430f6 | 183 | if (is_bgp_static_route) |
6aeb9e78 | 184 | rn = bgp_node_get (bgp->import_check_table[afi], &p); |
078430f6 | 185 | else |
6aeb9e78 | 186 | rn = bgp_node_get (bgp->nexthop_cache_table[afi], &p); |
fb018d25 DS |
187 | |
188 | if (!rn->info) | |
189 | { | |
190 | bnc = bnc_new(); | |
191 | rn->info = bnc; | |
192 | bnc->node = rn; | |
75aead62 | 193 | bnc->bgp = bgp; |
fb018d25 | 194 | bgp_lock_node(rn); |
9a233a02 DS |
195 | if (BGP_DEBUG(nht, NHT)) |
196 | { | |
4690c7d7 | 197 | char buf[PREFIX2STR_BUFFER]; |
9a233a02 | 198 | |
078430f6 | 199 | zlog_debug("Allocated bnc %s peer %p", |
4690c7d7 | 200 | bnc_str(bnc, buf, PREFIX2STR_BUFFER), peer); |
9a233a02 | 201 | } |
fb018d25 | 202 | } |
fc9a856f | 203 | |
fb018d25 DS |
204 | bnc = rn->info; |
205 | bgp_unlock_node (rn); | |
078430f6 DS |
206 | if (is_bgp_static_route) |
207 | { | |
208 | SET_FLAG(bnc->flags, BGP_STATIC_ROUTE); | |
209 | ||
210 | /* If we're toggling the type, re-register */ | |
5623e905 | 211 | if ((bgp_flag_check(bgp, BGP_FLAG_IMPORT_CHECK)) && |
078430f6 DS |
212 | !CHECK_FLAG(bnc->flags, BGP_STATIC_ROUTE_EXACT_MATCH)) |
213 | { | |
214 | SET_FLAG(bnc->flags, BGP_STATIC_ROUTE_EXACT_MATCH); | |
215 | UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED); | |
216 | UNSET_FLAG(bnc->flags, BGP_NEXTHOP_VALID); | |
217 | } | |
5623e905 | 218 | else if ((!bgp_flag_check(bgp, BGP_FLAG_IMPORT_CHECK)) && |
078430f6 DS |
219 | CHECK_FLAG(bnc->flags, BGP_STATIC_ROUTE_EXACT_MATCH)) |
220 | { | |
221 | UNSET_FLAG(bnc->flags, BGP_STATIC_ROUTE_EXACT_MATCH); | |
222 | UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED); | |
223 | UNSET_FLAG(bnc->flags, BGP_NEXTHOP_VALID); | |
224 | } | |
225 | } | |
25c38b24 | 226 | /* When nexthop is already known, but now requires 'connected' resolution, |
227 | * re-register it. The reverse scenario where the nexthop currently requires | |
228 | * 'connected' resolution does not need a re-register (i.e., we treat | |
229 | * 'connected-required' as an override) except in the scenario where this | |
230 | * is actually a case of tracking a peer for connectivity (e.g., after | |
231 | * disable connected-check). | |
232 | * NOTE: We don't track the number of paths separately for 'connected- | |
233 | * required' vs 'connected-not-required' as this change is not a common | |
234 | * scenario. | |
235 | */ | |
078430f6 DS |
236 | else if (connected && ! CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED)) |
237 | { | |
238 | SET_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED); | |
239 | UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED); | |
240 | UNSET_FLAG(bnc->flags, BGP_NEXTHOP_VALID); | |
241 | } | |
25c38b24 | 242 | else if (peer && !connected && CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED)) |
078430f6 DS |
243 | { |
244 | UNSET_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED); | |
245 | UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED); | |
246 | UNSET_FLAG(bnc->flags, BGP_NEXTHOP_VALID); | |
247 | } | |
3f3971a9 DS |
248 | if (bgp->inst_type == BGP_INSTANCE_TYPE_VIEW) |
249 | { | |
250 | bnc->flags |= BGP_NEXTHOP_REGISTERED; | |
251 | bnc->flags |= BGP_NEXTHOP_VALID; | |
252 | } | |
253 | else if (!CHECK_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED)) | |
078430f6 | 254 | register_zebra_rnh(bnc, is_bgp_static_route); |
22a29185 | 255 | if (ri && ri->nexthop != bnc) |
fc9a856f | 256 | { |
22a29185 DS |
257 | /* Unlink from existing nexthop cache, if any. This will also free |
258 | * the nexthop cache entry, if appropriate. | |
259 | */ | |
260 | bgp_unlink_nexthop (ri); | |
261 | ||
f9164b1d | 262 | path_nh_map(ri, bnc, 1); /* updates NHT ri list reference */ |
fb018d25 | 263 | |
fc9a856f DS |
264 | if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_VALID) && bnc->metric) |
265 | (bgp_info_extra_get(ri))->igpmetric = bnc->metric; | |
266 | else if (ri->extra) | |
267 | ri->extra->igpmetric = 0; | |
268 | } | |
269 | else if (peer) | |
f9164b1d | 270 | bnc->nht_info = (void *)peer; /* NHT peer reference */ |
fb018d25 | 271 | |
3f3971a9 DS |
272 | /* |
273 | * We are cheating here. Views have no associated underlying | |
274 | * ability to detect nexthops. So when we have a view | |
275 | * just tell everyone the nexthop is valid | |
276 | */ | |
277 | if (bgp->inst_type == BGP_INSTANCE_TYPE_VIEW) | |
278 | return 1; | |
279 | else | |
280 | return (bgp_isvalid_nexthop(bnc)); | |
fb018d25 DS |
281 | } |
282 | ||
9a233a02 DS |
283 | void |
284 | bgp_delete_connected_nexthop (afi_t afi, struct peer *peer) | |
285 | { | |
286 | struct bgp_node *rn; | |
287 | struct bgp_nexthop_cache *bnc; | |
288 | struct prefix p; | |
289 | ||
65740e1b DS |
290 | if (!peer) |
291 | return; | |
292 | ||
235022cb RW |
293 | /* We don't register link local address for NHT */ |
294 | if (afi == AFI_IP6 && IN6_IS_ADDR_LINKLOCAL (&peer->su.sin6.sin6_addr)) | |
295 | return; | |
65740e1b | 296 | |
235022cb | 297 | if (! sockunion2hostprefix (&peer->su, &p)) |
65740e1b | 298 | return; |
9a233a02 | 299 | |
6aeb9e78 | 300 | rn = bgp_node_lookup(peer->bgp->nexthop_cache_table[family2afi(p.family)], &p); |
9a233a02 DS |
301 | if (!rn || !rn->info) |
302 | { | |
303 | if (BGP_DEBUG(nht, NHT)) | |
304 | zlog_debug("Cannot find connected NHT node for peer %s", peer->host); | |
305 | if (rn) | |
306 | bgp_unlock_node (rn); | |
307 | return; | |
308 | } | |
309 | ||
310 | bnc = rn->info; | |
311 | bgp_unlock_node(rn); | |
312 | ||
313 | if (bnc->nht_info != peer) | |
314 | { | |
315 | if (BGP_DEBUG(nht, NHT)) | |
316 | zlog_debug("Connected NHT %p node for peer %s points to %p", | |
317 | bnc, peer->host, bnc->nht_info); | |
318 | return; | |
319 | } | |
320 | ||
321 | bnc->nht_info = NULL; | |
322 | ||
323 | if (LIST_EMPTY(&(bnc->paths))) | |
324 | { | |
325 | if (BGP_DEBUG(nht, NHT)) | |
326 | zlog_debug("Freeing connected NHT node %p for peer %s", | |
327 | bnc, peer->host); | |
078430f6 | 328 | unregister_zebra_rnh(bnc, 0); |
9a233a02 DS |
329 | bnc->node->info = NULL; |
330 | bgp_unlock_node(bnc->node); | |
331 | bnc_free(bnc); | |
332 | } | |
333 | } | |
334 | ||
fb018d25 | 335 | void |
7076bb2f | 336 | bgp_parse_nexthop_update (int command, vrf_id_t vrf_id) |
fb018d25 DS |
337 | { |
338 | struct stream *s; | |
ffd0c037 | 339 | struct bgp_node *rn = NULL; |
fb018d25 DS |
340 | struct bgp_nexthop_cache *bnc; |
341 | struct nexthop *nexthop; | |
342 | struct nexthop *oldnh; | |
343 | struct nexthop *nhlist_head = NULL; | |
344 | struct nexthop *nhlist_tail = NULL; | |
345 | uint32_t metric; | |
346 | u_char nexthop_num; | |
347 | struct prefix p; | |
348 | int i; | |
6aeb9e78 DS |
349 | struct bgp *bgp; |
350 | ||
351 | bgp = bgp_lookup_by_vrf_id (vrf_id); | |
352 | if (!bgp) | |
353 | { | |
354 | zlog_err("parse nexthop update: instance not found for vrf_id %d", vrf_id); | |
355 | return; | |
356 | } | |
fb018d25 DS |
357 | |
358 | s = zclient->ibuf; | |
359 | ||
360 | memset(&p, 0, sizeof(struct prefix)); | |
361 | p.family = stream_getw(s); | |
362 | p.prefixlen = stream_getc(s); | |
363 | switch (p.family) | |
364 | { | |
365 | case AF_INET: | |
366 | p.u.prefix4.s_addr = stream_get_ipv4 (s); | |
367 | break; | |
368 | case AF_INET6: | |
369 | stream_get(&p.u.prefix6, s, 16); | |
370 | break; | |
371 | default: | |
372 | break; | |
373 | } | |
374 | ||
078430f6 | 375 | if (command == ZEBRA_NEXTHOP_UPDATE) |
6aeb9e78 | 376 | rn = bgp_node_lookup(bgp->nexthop_cache_table[family2afi(p.family)], &p); |
078430f6 | 377 | else if (command == ZEBRA_IMPORT_CHECK_UPDATE) |
6aeb9e78 | 378 | rn = bgp_node_lookup(bgp->import_check_table[family2afi(p.family)], &p); |
078430f6 | 379 | |
fb018d25 DS |
380 | if (!rn || !rn->info) |
381 | { | |
382 | if (BGP_DEBUG(nht, NHT)) | |
383 | { | |
4690c7d7 DS |
384 | char buf[PREFIX2STR_BUFFER]; |
385 | prefix2str(&p, buf, sizeof(buf)); | |
fb018d25 DS |
386 | zlog_debug("parse nexthop update(%s): rn not found", buf); |
387 | } | |
7898cb4f DS |
388 | if (rn) |
389 | bgp_unlock_node (rn); | |
fb018d25 DS |
390 | return; |
391 | } | |
392 | ||
393 | bnc = rn->info; | |
7898cb4f | 394 | bgp_unlock_node (rn); |
fb018d25 DS |
395 | bnc->last_update = bgp_clock(); |
396 | bnc->change_flags = 0; | |
d14b95ce | 397 | stream_getc (s); // Distance but not currently used |
fb018d25 DS |
398 | metric = stream_getl (s); |
399 | nexthop_num = stream_getc (s); | |
400 | ||
401 | /* debug print the input */ | |
402 | if (BGP_DEBUG(nht, NHT)) | |
403 | { | |
4690c7d7 DS |
404 | char buf[PREFIX2STR_BUFFER]; |
405 | prefix2str(&p, buf, sizeof (buf)); | |
cd1964ff DS |
406 | zlog_debug("%d: Rcvd NH update %s - metric %d/%d #nhops %d/%d flags 0x%x", |
407 | vrf_id, buf, metric, bnc->metric, nexthop_num, bnc->nexthop_num, | |
408 | bnc->flags); | |
fb018d25 DS |
409 | } |
410 | ||
411 | if (metric != bnc->metric) | |
412 | bnc->change_flags |= BGP_NEXTHOP_METRIC_CHANGED; | |
413 | ||
414 | if(nexthop_num != bnc->nexthop_num) | |
415 | bnc->change_flags |= BGP_NEXTHOP_CHANGED; | |
416 | ||
417 | if (nexthop_num) | |
418 | { | |
9ff31ad5 DS |
419 | /* notify bgp fsm if nbr ip goes from invalid->valid */ |
420 | if (!bnc->nexthop_num) | |
421 | UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED); | |
422 | ||
fb018d25 DS |
423 | bnc->flags |= BGP_NEXTHOP_VALID; |
424 | bnc->metric = metric; | |
425 | bnc->nexthop_num = nexthop_num; | |
426 | ||
427 | for (i = 0; i < nexthop_num; i++) | |
428 | { | |
429 | nexthop = nexthop_new(); | |
430 | nexthop->type = stream_getc (s); | |
431 | switch (nexthop->type) | |
432 | { | |
5b30316e | 433 | case NEXTHOP_TYPE_IPV4: |
fb018d25 | 434 | nexthop->gate.ipv4.s_addr = stream_get_ipv4 (s); |
a18ed03f | 435 | nexthop->ifindex = stream_getl (s); |
fb018d25 | 436 | break; |
5b30316e | 437 | case NEXTHOP_TYPE_IFINDEX: |
fb018d25 DS |
438 | nexthop->ifindex = stream_getl (s); |
439 | break; | |
5b30316e | 440 | case NEXTHOP_TYPE_IPV4_IFINDEX: |
fb018d25 DS |
441 | nexthop->gate.ipv4.s_addr = stream_get_ipv4 (s); |
442 | nexthop->ifindex = stream_getl (s); | |
443 | break; | |
5b30316e | 444 | case NEXTHOP_TYPE_IPV6: |
fb018d25 | 445 | stream_get (&nexthop->gate.ipv6, s, 16); |
8e581e39 | 446 | nexthop->ifindex = stream_getl (s); |
fb018d25 | 447 | break; |
5b30316e | 448 | case NEXTHOP_TYPE_IPV6_IFINDEX: |
fb018d25 DS |
449 | stream_get (&nexthop->gate.ipv6, s, 16); |
450 | nexthop->ifindex = stream_getl (s); | |
451 | break; | |
fb018d25 DS |
452 | default: |
453 | /* do nothing */ | |
454 | break; | |
455 | } | |
456 | ||
80c2442a | 457 | if (BGP_DEBUG(nht, NHT)) |
458 | { | |
459 | char buf[NEXTHOP_STRLEN]; | |
460 | zlog_debug(" nhop via %s", | |
461 | nexthop2str (nexthop, buf, sizeof (buf))); | |
462 | } | |
463 | ||
fb018d25 DS |
464 | if (nhlist_tail) |
465 | { | |
466 | nhlist_tail->next = nexthop; | |
467 | nhlist_tail = nexthop; | |
468 | } | |
469 | else | |
470 | { | |
471 | nhlist_tail = nexthop; | |
472 | nhlist_head = nexthop; | |
473 | } | |
474 | ||
475 | /* No need to evaluate the nexthop if we have already determined | |
476 | * that there has been a change. | |
477 | */ | |
478 | if (bnc->change_flags & BGP_NEXTHOP_CHANGED) | |
479 | continue; | |
480 | ||
481 | for (oldnh = bnc->nexthop; oldnh; oldnh = oldnh->next) | |
482 | if (nexthop_same_no_recurse(oldnh, nexthop)) | |
483 | break; | |
484 | ||
485 | if (!oldnh) | |
9ff31ad5 | 486 | bnc->change_flags |= BGP_NEXTHOP_CHANGED; |
fb018d25 DS |
487 | } |
488 | bnc_nexthop_free(bnc); | |
489 | bnc->nexthop = nhlist_head; | |
490 | } | |
491 | else | |
492 | { | |
493 | bnc->flags &= ~BGP_NEXTHOP_VALID; | |
9ff31ad5 DS |
494 | bnc->nexthop_num = nexthop_num; |
495 | ||
496 | /* notify bgp fsm if nbr ip goes from valid->invalid */ | |
fc9a856f | 497 | UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED); |
9ff31ad5 | 498 | |
fb018d25 DS |
499 | bnc_nexthop_free(bnc); |
500 | bnc->nexthop = NULL; | |
501 | } | |
502 | ||
503 | evaluate_paths(bnc); | |
504 | } | |
505 | ||
506 | /** | |
507 | * make_prefix - make a prefix structure from the path (essentially | |
508 | * path's node. | |
509 | */ | |
510 | static int | |
511 | make_prefix (int afi, struct bgp_info *ri, struct prefix *p) | |
512 | { | |
078430f6 DS |
513 | |
514 | int is_bgp_static = ((ri->type == ZEBRA_ROUTE_BGP) && | |
515 | (ri->sub_type == BGP_ROUTE_STATIC)) ? 1 : 0; | |
516 | ||
fb018d25 DS |
517 | memset (p, 0, sizeof (struct prefix)); |
518 | switch (afi) | |
519 | { | |
520 | case AFI_IP: | |
521 | p->family = AF_INET; | |
078430f6 DS |
522 | if (is_bgp_static) |
523 | { | |
524 | p->u.prefix4 = ri->net->p.u.prefix4; | |
525 | p->prefixlen = ri->net->p.prefixlen; | |
526 | } | |
527 | else | |
528 | { | |
529 | p->u.prefix4 = ri->attr->nexthop; | |
530 | p->prefixlen = IPV4_MAX_BITLEN; | |
531 | } | |
fb018d25 | 532 | break; |
fb018d25 | 533 | case AFI_IP6: |
65740e1b | 534 | /* We don't register link local NH */ |
801a9bcc | 535 | if (ri->attr->extra->mp_nexthop_len != BGP_ATTR_NHLEN_IPV6_GLOBAL |
fb018d25 DS |
536 | || IN6_IS_ADDR_LINKLOCAL (&ri->attr->extra->mp_nexthop_global)) |
537 | return -1; | |
538 | ||
539 | p->family = AF_INET6; | |
078430f6 DS |
540 | |
541 | if (is_bgp_static) | |
542 | { | |
543 | p->u.prefix6 = ri->net->p.u.prefix6; | |
544 | p->prefixlen = ri->net->p.prefixlen; | |
545 | } | |
546 | else | |
547 | { | |
548 | p->u.prefix6 = ri->attr->extra->mp_nexthop_global; | |
549 | p->prefixlen = IPV6_MAX_BITLEN; | |
550 | } | |
fb018d25 | 551 | break; |
fb018d25 | 552 | default: |
65740e1b DS |
553 | if (BGP_DEBUG(nht, NHT)) |
554 | { | |
555 | zlog_debug("%s: Attempting to make prefix with unknown AFI %d (not %d or %d)", | |
556 | __FUNCTION__, afi, AFI_IP, AFI_IP6); | |
557 | } | |
fb018d25 DS |
558 | break; |
559 | } | |
560 | return 0; | |
561 | } | |
562 | ||
563 | /** | |
078430f6 | 564 | * sendmsg_zebra_rnh -- Format and send a nexthop register/Unregister |
fb018d25 DS |
565 | * command to Zebra. |
566 | * ARGUMENTS: | |
567 | * struct bgp_nexthop_cache *bnc -- the nexthop structure. | |
078430f6 | 568 | * int command -- command to send to zebra |
fb018d25 DS |
569 | * RETURNS: |
570 | * void. | |
571 | */ | |
572 | static void | |
078430f6 | 573 | sendmsg_zebra_rnh (struct bgp_nexthop_cache *bnc, int command) |
fb018d25 DS |
574 | { |
575 | struct stream *s; | |
576 | struct prefix *p; | |
577 | int ret; | |
578 | ||
579 | /* Check socket. */ | |
580 | if (!zclient || zclient->sock < 0) | |
ad4cbda1 | 581 | return; |
582 | ||
583 | /* Don't try to register if Zebra doesn't know of this instance. */ | |
584 | if (!IS_BGP_INST_KNOWN_TO_ZEBRA(bnc->bgp)) | |
585 | return; | |
fb018d25 DS |
586 | |
587 | p = &(bnc->node->p); | |
588 | s = zclient->obuf; | |
589 | stream_reset (s); | |
6aeb9e78 | 590 | zclient_create_header (s, command, bnc->bgp->vrf_id); |
078430f6 DS |
591 | if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED) || |
592 | CHECK_FLAG(bnc->flags, BGP_STATIC_ROUTE_EXACT_MATCH)) | |
fc9a856f DS |
593 | stream_putc(s, 1); |
594 | else | |
595 | stream_putc(s, 0); | |
596 | ||
fb018d25 DS |
597 | stream_putw(s, PREFIX_FAMILY(p)); |
598 | stream_putc(s, p->prefixlen); | |
599 | switch (PREFIX_FAMILY(p)) | |
600 | { | |
601 | case AF_INET: | |
602 | stream_put_in_addr (s, &p->u.prefix4); | |
603 | break; | |
fb018d25 DS |
604 | case AF_INET6: |
605 | stream_put(s, &(p->u.prefix6), 16); | |
606 | break; | |
fb018d25 DS |
607 | default: |
608 | break; | |
609 | } | |
610 | stream_putw_at (s, 0, stream_get_endp (s)); | |
611 | ||
612 | ret = zclient_send_message(zclient); | |
613 | /* TBD: handle the failure */ | |
614 | if (ret < 0) | |
615 | zlog_warn("sendmsg_nexthop: zclient_send_message() failed"); | |
fc9a856f | 616 | |
078430f6 DS |
617 | if ((command == ZEBRA_NEXTHOP_REGISTER) || |
618 | (command == ZEBRA_IMPORT_ROUTE_REGISTER)) | |
fc9a856f | 619 | SET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED); |
078430f6 DS |
620 | else if ((command == ZEBRA_NEXTHOP_UNREGISTER) || |
621 | (command == ZEBRA_IMPORT_ROUTE_UNREGISTER)) | |
fc9a856f | 622 | UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED); |
fb018d25 DS |
623 | return; |
624 | } | |
625 | ||
626 | /** | |
078430f6 DS |
627 | * register_zebra_rnh - register a NH/route with Zebra for notification |
628 | * when the route or the route to the nexthop changes. | |
fb018d25 | 629 | * ARGUMENTS: |
078430f6 | 630 | * struct bgp_nexthop_cache *bnc |
fb018d25 DS |
631 | * RETURNS: |
632 | * void. | |
633 | */ | |
634 | static void | |
078430f6 | 635 | register_zebra_rnh (struct bgp_nexthop_cache *bnc, int is_bgp_import_route) |
fb018d25 DS |
636 | { |
637 | /* Check if we have already registered */ | |
638 | if (bnc->flags & BGP_NEXTHOP_REGISTERED) | |
639 | return; | |
078430f6 DS |
640 | if (is_bgp_import_route) |
641 | sendmsg_zebra_rnh(bnc, ZEBRA_IMPORT_ROUTE_REGISTER); | |
642 | else | |
643 | sendmsg_zebra_rnh(bnc, ZEBRA_NEXTHOP_REGISTER); | |
fb018d25 DS |
644 | } |
645 | ||
646 | /** | |
078430f6 | 647 | * unregister_zebra_rnh -- Unregister the route/nexthop from Zebra. |
fb018d25 | 648 | * ARGUMENTS: |
078430f6 | 649 | * struct bgp_nexthop_cache *bnc |
fb018d25 DS |
650 | * RETURNS: |
651 | * void. | |
652 | */ | |
653 | static void | |
078430f6 | 654 | unregister_zebra_rnh (struct bgp_nexthop_cache *bnc, int is_bgp_import_route) |
fb018d25 DS |
655 | { |
656 | /* Check if we have already registered */ | |
657 | if (!CHECK_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED)) | |
658 | return; | |
659 | ||
078430f6 DS |
660 | if (is_bgp_import_route) |
661 | sendmsg_zebra_rnh(bnc, ZEBRA_IMPORT_ROUTE_UNREGISTER); | |
662 | else | |
663 | sendmsg_zebra_rnh(bnc, ZEBRA_NEXTHOP_UNREGISTER); | |
fb018d25 DS |
664 | } |
665 | ||
666 | /** | |
667 | * evaluate_paths - Evaluate the paths/nets associated with a nexthop. | |
668 | * ARGUMENTS: | |
669 | * struct bgp_nexthop_cache *bnc -- the nexthop structure. | |
670 | * RETURNS: | |
671 | * void. | |
672 | */ | |
673 | static void | |
674 | evaluate_paths (struct bgp_nexthop_cache *bnc) | |
675 | { | |
676 | struct bgp_node *rn; | |
677 | struct bgp_info *path; | |
75aead62 | 678 | struct bgp *bgp = bnc->bgp; |
fb018d25 | 679 | int afi; |
fc9a856f | 680 | struct peer *peer = (struct peer *)bnc->nht_info; |
cd1964ff DS |
681 | struct bgp_table *table; |
682 | safi_t safi; | |
fb018d25 | 683 | |
80c2442a | 684 | if (BGP_DEBUG(nht, NHT)) |
685 | { | |
686 | char buf[PREFIX2STR_BUFFER]; | |
687 | bnc_str(bnc, buf, PREFIX2STR_BUFFER); | |
688 | zlog_debug("NH update for %s - flags 0x%x chgflags 0x%x - evaluate paths", | |
689 | buf, bnc->flags, bnc->change_flags); | |
690 | } | |
691 | ||
fb018d25 DS |
692 | LIST_FOREACH(path, &(bnc->paths), nh_thread) |
693 | { | |
694 | if (!(path->type == ZEBRA_ROUTE_BGP && | |
ffd0c037 DS |
695 | ((path->sub_type == BGP_ROUTE_NORMAL) || |
696 | (path->sub_type == BGP_ROUTE_STATIC)))) | |
fb018d25 DS |
697 | continue; |
698 | ||
699 | rn = path->net; | |
cd1964ff | 700 | assert (rn && bgp_node_table (rn)); |
fb018d25 | 701 | afi = family2afi(rn->p.family); |
cd1964ff DS |
702 | table = bgp_node_table (rn); |
703 | safi = table->safi; | |
fb018d25 DS |
704 | |
705 | /* Path becomes valid/invalid depending on whether the nexthop | |
706 | * reachable/unreachable. | |
707 | */ | |
708 | if ((CHECK_FLAG(path->flags, BGP_INFO_VALID) ? 1 : 0) != | |
d4d9d757 | 709 | (bgp_isvalid_nexthop(bnc) ? 1 : 0)) |
fb018d25 DS |
710 | { |
711 | if (CHECK_FLAG (path->flags, BGP_INFO_VALID)) | |
712 | { | |
cd1964ff | 713 | bgp_aggregate_decrement (bgp, &rn->p, path, afi, safi); |
fb018d25 DS |
714 | bgp_info_unset_flag (rn, path, BGP_INFO_VALID); |
715 | } | |
716 | else | |
717 | { | |
718 | bgp_info_set_flag (rn, path, BGP_INFO_VALID); | |
cd1964ff | 719 | bgp_aggregate_increment (bgp, &rn->p, path, afi, safi); |
fb018d25 DS |
720 | } |
721 | } | |
722 | ||
723 | /* Copy the metric to the path. Will be used for bestpath computation */ | |
d4d9d757 | 724 | if (bgp_isvalid_nexthop(bnc) && bnc->metric) |
fb018d25 DS |
725 | (bgp_info_extra_get(path))->igpmetric = bnc->metric; |
726 | else if (path->extra) | |
727 | path->extra->igpmetric = 0; | |
728 | ||
c52d6050 | 729 | if (CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_METRIC_CHANGED) || |
730 | CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_CHANGED)) | |
fb018d25 DS |
731 | SET_FLAG(path->flags, BGP_INFO_IGP_CHANGED); |
732 | ||
cd1964ff | 733 | bgp_process(bgp, rn, afi, safi); |
fb018d25 | 734 | } |
fc9a856f DS |
735 | |
736 | if (peer && !CHECK_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED)) | |
737 | { | |
738 | if (BGP_DEBUG(nht, NHT)) | |
739 | zlog_debug("%s: Updating peer (%s) status with NHT", __FUNCTION__, peer->host); | |
d4d9d757 | 740 | bgp_fsm_nht_update(peer, bgp_isvalid_nexthop(bnc)); |
fc9a856f DS |
741 | SET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED); |
742 | } | |
743 | ||
fb018d25 DS |
744 | RESET_FLAG(bnc->change_flags); |
745 | } | |
746 | ||
747 | /** | |
748 | * path_nh_map - make or break path-to-nexthop association. | |
749 | * ARGUMENTS: | |
750 | * path - pointer to the path structure | |
751 | * bnc - pointer to the nexthop structure | |
752 | * make - if set, make the association. if unset, just break the existing | |
753 | * association. | |
754 | */ | |
755 | static void | |
756 | path_nh_map (struct bgp_info *path, struct bgp_nexthop_cache *bnc, int make) | |
757 | { | |
758 | if (path->nexthop) | |
759 | { | |
760 | LIST_REMOVE(path, nh_thread); | |
761 | path->nexthop->path_count--; | |
762 | path->nexthop = NULL; | |
763 | } | |
764 | if (make) | |
765 | { | |
766 | LIST_INSERT_HEAD(&(bnc->paths), path, nh_thread); | |
767 | path->nexthop = bnc; | |
768 | path->nexthop->path_count++; | |
769 | } | |
770 | } |