]>
Commit | Line | Data |
---|---|---|
12e41d03 | 1 | /* |
896014f4 DL |
2 | * PIM for Quagga |
3 | * Copyright (C) 2008 Everton da Silva Marques | |
4 | * | |
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. | |
9 | * | |
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. | |
14 | * | |
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 | |
18 | */ | |
12e41d03 DL |
19 | |
20 | #include <zebra.h> | |
21 | ||
22 | #include "log.h" | |
23 | #include "prefix.h" | |
24 | #include "memory.h" | |
744d91b3 | 25 | #include "if.h" |
e446de6a DS |
26 | #include "vty.h" |
27 | #include "plist.h" | |
3613d898 | 28 | #include "lib_errors.h" |
12e41d03 DL |
29 | |
30 | #include "pimd.h" | |
31 | #include "pim_neighbor.h" | |
32 | #include "pim_time.h" | |
33 | #include "pim_str.h" | |
34 | #include "pim_iface.h" | |
35 | #include "pim_pim.h" | |
36 | #include "pim_upstream.h" | |
37 | #include "pim_ifchannel.h" | |
e446de6a | 38 | #include "pim_rp.h" |
da72c9fd | 39 | #include "pim_zebra.h" |
982bff89 DS |
40 | #include "pim_join.h" |
41 | #include "pim_jp_agg.h" | |
ba4eb1bc | 42 | #include "pim_bfd.h" |
46a9ea8b | 43 | #include "pim_register.h" |
12e41d03 DL |
44 | |
45 | static void dr_election_by_addr(struct interface *ifp) | |
46 | { | |
d62a17ae | 47 | struct pim_interface *pim_ifp; |
48 | struct listnode *node; | |
49 | struct pim_neighbor *neigh; | |
50 | ||
51 | pim_ifp = ifp->info; | |
df5dfb77 | 52 | assert(pim_ifp); |
d62a17ae | 53 | |
54 | pim_ifp->pim_dr_addr = pim_ifp->primary_address; | |
55 | ||
56 | if (PIM_DEBUG_PIM_TRACE) { | |
5e81f5dd | 57 | zlog_debug("%s: on interface %s", __func__, ifp->name); |
d62a17ae | 58 | } |
59 | ||
60 | for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, node, neigh)) { | |
9bb93fa0 | 61 | if (pim_addr_cmp(neigh->source_addr, pim_ifp->pim_dr_addr) > 0) |
d62a17ae | 62 | pim_ifp->pim_dr_addr = neigh->source_addr; |
d62a17ae | 63 | } |
12e41d03 DL |
64 | } |
65 | ||
66 | static void dr_election_by_pri(struct interface *ifp) | |
67 | { | |
d62a17ae | 68 | struct pim_interface *pim_ifp; |
69 | struct listnode *node; | |
70 | struct pim_neighbor *neigh; | |
71 | uint32_t dr_pri; | |
72 | ||
73 | pim_ifp = ifp->info; | |
df5dfb77 | 74 | assert(pim_ifp); |
d62a17ae | 75 | |
76 | pim_ifp->pim_dr_addr = pim_ifp->primary_address; | |
77 | dr_pri = pim_ifp->pim_dr_priority; | |
78 | ||
79 | if (PIM_DEBUG_PIM_TRACE) { | |
5e81f5dd DS |
80 | zlog_debug("%s: dr pri %u on interface %s", __func__, dr_pri, |
81 | ifp->name); | |
d62a17ae | 82 | } |
83 | ||
84 | for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, node, neigh)) { | |
85 | if (PIM_DEBUG_PIM_TRACE) { | |
9bb93fa0 | 86 | zlog_info("%s: neigh pri %u addr %pPA if dr addr %pPA", |
5e81f5dd | 87 | __func__, neigh->dr_priority, |
9bb93fa0 | 88 | &neigh->source_addr, &pim_ifp->pim_dr_addr); |
d62a17ae | 89 | } |
9bb93fa0 DL |
90 | if ((neigh->dr_priority > dr_pri) || |
91 | ((neigh->dr_priority == dr_pri) && | |
92 | (pim_addr_cmp(neigh->source_addr, pim_ifp->pim_dr_addr) > | |
93 | 0))) { | |
d62a17ae | 94 | pim_ifp->pim_dr_addr = neigh->source_addr; |
95 | dr_pri = neigh->dr_priority; | |
96 | } | |
97 | } | |
12e41d03 DL |
98 | } |
99 | ||
100 | /* | |
101 | RFC 4601: 4.3.2. DR Election | |
102 | ||
103 | A router's idea of the current DR on an interface can change when a | |
104 | PIM Hello message is received, when a neighbor times out, or when a | |
105 | router's own DR Priority changes. | |
106 | */ | |
dedccda6 | 107 | int pim_if_dr_election(struct interface *ifp) |
12e41d03 | 108 | { |
d62a17ae | 109 | struct pim_interface *pim_ifp = ifp->info; |
034db86b | 110 | pim_addr old_dr_addr; |
d62a17ae | 111 | |
112 | ++pim_ifp->pim_dr_election_count; | |
113 | ||
114 | old_dr_addr = pim_ifp->pim_dr_addr; | |
115 | ||
116 | if (pim_ifp->pim_dr_num_nondrpri_neighbors) { | |
117 | dr_election_by_addr(ifp); | |
118 | } else { | |
119 | dr_election_by_pri(ifp); | |
120 | } | |
121 | ||
122 | /* DR changed ? */ | |
034db86b DL |
123 | if (pim_addr_cmp(old_dr_addr, pim_ifp->pim_dr_addr)) { |
124 | ||
125 | if (PIM_DEBUG_PIM_EVENTS) | |
126 | zlog_debug( | |
127 | "%s: DR was %pPA now is %pPA on interface %s", | |
128 | __func__, &old_dr_addr, &pim_ifp->pim_dr_addr, | |
129 | ifp->name); | |
d62a17ae | 130 | |
131 | pim_ifp->pim_dr_election_last = | |
132 | pim_time_monotonic_sec(); /* timestamp */ | |
133 | ++pim_ifp->pim_dr_election_changes; | |
134 | pim_if_update_join_desired(pim_ifp); | |
135 | pim_if_update_could_assert(ifp); | |
136 | pim_if_update_assert_tracking_desired(ifp); | |
46a9ea8b | 137 | |
138 | if (PIM_I_am_DR(pim_ifp)) | |
139 | pim_ifp->am_i_dr = true; | |
140 | else { | |
141 | if (pim_ifp->am_i_dr == true) { | |
142 | pim_reg_del_on_couldreg_fail(ifp); | |
143 | pim_ifp->am_i_dr = false; | |
144 | } | |
145 | } | |
146 | ||
d62a17ae | 147 | return 1; |
148 | } | |
149 | ||
150 | return 0; | |
12e41d03 DL |
151 | } |
152 | ||
153 | static void update_dr_priority(struct pim_neighbor *neigh, | |
154 | pim_hello_options hello_options, | |
155 | uint32_t dr_priority) | |
156 | { | |
d62a17ae | 157 | pim_hello_options will_set_pri; /* boolean */ |
158 | pim_hello_options bit_flip; /* boolean */ | |
159 | pim_hello_options pri_change; /* boolean */ | |
160 | ||
161 | will_set_pri = | |
162 | PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_DR_PRIORITY); | |
163 | ||
164 | bit_flip = (will_set_pri | |
165 | != PIM_OPTION_IS_SET(neigh->hello_options, | |
166 | PIM_OPTION_MASK_DR_PRIORITY)); | |
167 | ||
168 | if (bit_flip) { | |
169 | struct pim_interface *pim_ifp = neigh->interface->info; | |
170 | ||
171 | /* update num. of neighbors without dr_pri */ | |
172 | ||
173 | if (will_set_pri) { | |
174 | --pim_ifp->pim_dr_num_nondrpri_neighbors; | |
175 | } else { | |
176 | ++pim_ifp->pim_dr_num_nondrpri_neighbors; | |
177 | } | |
178 | } | |
179 | ||
180 | pri_change = (bit_flip || (neigh->dr_priority != dr_priority)); | |
181 | ||
182 | if (will_set_pri) { | |
183 | neigh->dr_priority = dr_priority; | |
184 | } else { | |
185 | neigh->dr_priority = 0; /* cosmetic unset */ | |
186 | } | |
187 | ||
188 | if (pri_change) { | |
189 | /* | |
190 | RFC 4601: 4.3.2. DR Election | |
191 | ||
192 | A router's idea of the current DR on an interface can change | |
193 | when a | |
194 | PIM Hello message is received, when a neighbor times out, or | |
195 | when a | |
196 | router's own DR Priority changes. | |
197 | */ | |
198 | pim_if_dr_election( | |
199 | neigh->interface); // router's own DR Priority changes | |
200 | } | |
12e41d03 DL |
201 | } |
202 | ||
cc9f21da | 203 | static void on_neighbor_timer(struct thread *t) |
12e41d03 | 204 | { |
d62a17ae | 205 | struct pim_neighbor *neigh; |
206 | struct interface *ifp; | |
207 | char msg[100]; | |
208 | ||
209 | neigh = THREAD_ARG(t); | |
210 | ||
211 | ifp = neigh->interface; | |
212 | ||
9bb93fa0 | 213 | if (PIM_DEBUG_PIM_TRACE) |
d62a17ae | 214 | zlog_debug( |
9bb93fa0 DL |
215 | "Expired %d sec holdtime for neighbor %pPA on interface %s", |
216 | neigh->holdtime, &neigh->source_addr, ifp->name); | |
d62a17ae | 217 | |
218 | snprintf(msg, sizeof(msg), "%d-sec holdtime expired", neigh->holdtime); | |
219 | pim_neighbor_delete(ifp, neigh, msg); | |
220 | ||
221 | /* | |
222 | RFC 4601: 4.3.2. DR Election | |
223 | ||
224 | A router's idea of the current DR on an interface can change when a | |
225 | PIM Hello message is received, when a neighbor times out, or when a | |
226 | router's own DR Priority changes. | |
227 | */ | |
228 | pim_if_dr_election(ifp); // neighbor times out | |
12e41d03 DL |
229 | } |
230 | ||
12e41d03 DL |
231 | void pim_neighbor_timer_reset(struct pim_neighbor *neigh, uint16_t holdtime) |
232 | { | |
d62a17ae | 233 | neigh->holdtime = holdtime; |
234 | ||
235 | THREAD_OFF(neigh->t_expire_timer); | |
236 | ||
237 | /* | |
238 | 0xFFFF is request for no holdtime | |
239 | */ | |
240 | if (neigh->holdtime == 0xFFFF) { | |
241 | return; | |
242 | } | |
243 | ||
9bb93fa0 DL |
244 | if (PIM_DEBUG_PIM_TRACE_DETAIL) |
245 | zlog_debug("%s: starting %u sec timer for neighbor %pPA on %s", | |
246 | __func__, neigh->holdtime, &neigh->source_addr, | |
d62a17ae | 247 | neigh->interface->name); |
d62a17ae | 248 | |
36417fcc DS |
249 | thread_add_timer(router->master, on_neighbor_timer, neigh, |
250 | neigh->holdtime, &neigh->t_expire_timer); | |
12e41d03 DL |
251 | } |
252 | ||
cc9f21da | 253 | static void on_neighbor_jp_timer(struct thread *t) |
982bff89 | 254 | { |
d62a17ae | 255 | struct pim_neighbor *neigh = THREAD_ARG(t); |
256 | struct pim_rpf rpf; | |
257 | ||
9bb93fa0 DL |
258 | if (PIM_DEBUG_PIM_TRACE) |
259 | zlog_debug("%s:Sending JP Agg to %pPA on %s with %d groups", | |
260 | __func__, &neigh->source_addr, | |
261 | neigh->interface->name, | |
d62a17ae | 262 | neigh->upstream_jp_agg->count); |
982bff89 | 263 | |
d62a17ae | 264 | rpf.source_nexthop.interface = neigh->interface; |
9bb93fa0 | 265 | pim_addr_to_prefix(&rpf.rpf_addr, neigh->source_addr); |
d62a17ae | 266 | pim_joinprune_send(&rpf, neigh->upstream_jp_agg); |
982bff89 | 267 | |
36417fcc | 268 | thread_add_timer(router->master, on_neighbor_jp_timer, neigh, |
5b45753e | 269 | router->t_periodic, &neigh->jp_timer); |
982bff89 DS |
270 | } |
271 | ||
d62a17ae | 272 | static void pim_neighbor_start_jp_timer(struct pim_neighbor *neigh) |
982bff89 | 273 | { |
50478845 | 274 | THREAD_OFF(neigh->jp_timer); |
36417fcc | 275 | thread_add_timer(router->master, on_neighbor_jp_timer, neigh, |
5b45753e | 276 | router->t_periodic, &neigh->jp_timer); |
982bff89 DS |
277 | } |
278 | ||
d62a17ae | 279 | static struct pim_neighbor * |
9bb93fa0 | 280 | pim_neighbor_new(struct interface *ifp, pim_addr source_addr, |
d62a17ae | 281 | pim_hello_options hello_options, uint16_t holdtime, |
282 | uint16_t propagation_delay, uint16_t override_interval, | |
283 | uint32_t dr_priority, uint32_t generation_id, | |
284 | struct list *addr_list) | |
12e41d03 | 285 | { |
d62a17ae | 286 | struct pim_interface *pim_ifp; |
287 | struct pim_neighbor *neigh; | |
d62a17ae | 288 | |
df5dfb77 | 289 | assert(ifp); |
d62a17ae | 290 | pim_ifp = ifp->info; |
df5dfb77 | 291 | assert(pim_ifp); |
d62a17ae | 292 | |
293 | neigh = XCALLOC(MTYPE_PIM_NEIGHBOR, sizeof(*neigh)); | |
d62a17ae | 294 | |
295 | neigh->creation = pim_time_monotonic_sec(); | |
296 | neigh->source_addr = source_addr; | |
297 | neigh->hello_options = hello_options; | |
298 | neigh->propagation_delay_msec = propagation_delay; | |
299 | neigh->override_interval_msec = override_interval; | |
300 | neigh->dr_priority = dr_priority; | |
301 | neigh->generation_id = generation_id; | |
302 | neigh->prefix_list = addr_list; | |
303 | neigh->t_expire_timer = NULL; | |
304 | neigh->interface = ifp; | |
305 | ||
306 | neigh->upstream_jp_agg = list_new(); | |
307 | neigh->upstream_jp_agg->cmp = pim_jp_agg_group_list_cmp; | |
308 | neigh->upstream_jp_agg->del = | |
309 | (void (*)(void *))pim_jp_agg_group_list_free; | |
310 | pim_neighbor_start_jp_timer(neigh); | |
311 | ||
312 | pim_neighbor_timer_reset(neigh, holdtime); | |
313 | /* | |
314 | * The pim_ifstat_hello_sent variable is used to decide if | |
315 | * we should expedite a hello out the interface. If we | |
316 | * establish a new neighbor, we unfortunately need to | |
317 | * reset the value so that we can know to hurry up and | |
318 | * hello | |
319 | */ | |
79992e8a | 320 | PIM_IF_FLAG_UNSET_HELLO_SENT(pim_ifp->flags); |
d62a17ae | 321 | |
9bb93fa0 DL |
322 | if (PIM_DEBUG_PIM_EVENTS) |
323 | zlog_debug("%s: creating PIM neighbor %pPA on interface %s", | |
324 | __func__, &source_addr, ifp->name); | |
d62a17ae | 325 | |
9bb93fa0 DL |
326 | zlog_notice("PIM NEIGHBOR UP: neighbor %pPA on interface %s", |
327 | &source_addr, ifp->name); | |
d62a17ae | 328 | |
329 | if (neigh->propagation_delay_msec | |
330 | > pim_ifp->pim_neighbors_highest_propagation_delay_msec) { | |
331 | pim_ifp->pim_neighbors_highest_propagation_delay_msec = | |
332 | neigh->propagation_delay_msec; | |
333 | } | |
334 | if (neigh->override_interval_msec | |
335 | > pim_ifp->pim_neighbors_highest_override_interval_msec) { | |
336 | pim_ifp->pim_neighbors_highest_override_interval_msec = | |
337 | neigh->override_interval_msec; | |
338 | } | |
339 | ||
340 | if (!PIM_OPTION_IS_SET(neigh->hello_options, | |
341 | PIM_OPTION_MASK_LAN_PRUNE_DELAY)) { | |
342 | /* update num. of neighbors without hello option lan_delay */ | |
343 | ++pim_ifp->pim_number_of_nonlandelay_neighbors; | |
344 | } | |
345 | ||
346 | if (!PIM_OPTION_IS_SET(neigh->hello_options, | |
347 | PIM_OPTION_MASK_DR_PRIORITY)) { | |
348 | /* update num. of neighbors without hello option dr_pri */ | |
349 | ++pim_ifp->pim_dr_num_nondrpri_neighbors; | |
350 | } | |
351 | ||
352 | // Register PIM Neighbor with BFD | |
1f3e6bf5 | 353 | pim_bfd_info_nbr_create(pim_ifp, neigh); |
d62a17ae | 354 | |
355 | return neigh; | |
12e41d03 DL |
356 | } |
357 | ||
358 | static void delete_prefix_list(struct pim_neighbor *neigh) | |
359 | { | |
d62a17ae | 360 | if (neigh->prefix_list) { |
12e41d03 DL |
361 | |
362 | #ifdef DUMP_PREFIX_LIST | |
d62a17ae | 363 | struct listnode *p_node; |
364 | struct prefix *p; | |
365 | char addr_str[10]; | |
366 | int list_size = neigh->prefix_list | |
367 | ? (int)listcount(neigh->prefix_list) | |
368 | : -1; | |
369 | int i = 0; | |
370 | for (ALL_LIST_ELEMENTS_RO(neigh->prefix_list, p_node, p)) { | |
371 | pim_inet4_dump("<addr?>", p->u.prefix4, addr_str, | |
372 | sizeof(addr_str)); | |
373 | zlog_debug( | |
374 | "%s: DUMP_PREFIX_LIST neigh=%x prefix_list=%x prefix=%x addr=%s [%d/%d]", | |
5e81f5dd | 375 | __func__, (unsigned)neigh, |
d62a17ae | 376 | (unsigned)neigh->prefix_list, (unsigned)p, |
377 | addr_str, i, list_size); | |
378 | ++i; | |
379 | } | |
12e41d03 DL |
380 | #endif |
381 | ||
6a154c88 | 382 | list_delete(&neigh->prefix_list); |
d62a17ae | 383 | } |
12e41d03 DL |
384 | } |
385 | ||
386 | void pim_neighbor_free(struct pim_neighbor *neigh) | |
387 | { | |
df5dfb77 | 388 | assert(!neigh->t_expire_timer); |
12e41d03 | 389 | |
d62a17ae | 390 | delete_prefix_list(neigh); |
12e41d03 | 391 | |
6a154c88 | 392 | list_delete(&neigh->upstream_jp_agg); |
d62a17ae | 393 | THREAD_OFF(neigh->jp_timer); |
982bff89 | 394 | |
1f3e6bf5 | 395 | bfd_sess_free(&neigh->bfd_session); |
662ba9e6 | 396 | |
d62a17ae | 397 | XFREE(MTYPE_PIM_NEIGHBOR, neigh); |
12e41d03 DL |
398 | } |
399 | ||
d62a17ae | 400 | struct pim_neighbor *pim_neighbor_find_by_secondary(struct interface *ifp, |
401 | struct prefix *src) | |
07b17d59 | 402 | { |
d62a17ae | 403 | struct pim_interface *pim_ifp; |
404 | struct listnode *node, *pnode; | |
405 | struct pim_neighbor *neigh; | |
406 | struct prefix *p; | |
407 | ||
5b4d431d | 408 | if (!ifp || !ifp->info) |
d62a17ae | 409 | return NULL; |
410 | ||
5b4d431d SK |
411 | pim_ifp = ifp->info; |
412 | ||
d62a17ae | 413 | for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, node, neigh)) { |
414 | for (ALL_LIST_ELEMENTS_RO(neigh->prefix_list, pnode, p)) { | |
415 | if (prefix_same(p, src)) | |
416 | return neigh; | |
417 | } | |
418 | } | |
419 | ||
420 | return NULL; | |
07b17d59 DS |
421 | } |
422 | ||
12e41d03 | 423 | struct pim_neighbor *pim_neighbor_find(struct interface *ifp, |
9bb93fa0 | 424 | pim_addr source_addr) |
12e41d03 | 425 | { |
d62a17ae | 426 | struct pim_interface *pim_ifp; |
427 | struct listnode *node; | |
428 | struct pim_neighbor *neigh; | |
12e41d03 | 429 | |
d62a17ae | 430 | if (!ifp) |
431 | return NULL; | |
1131c2eb | 432 | |
d62a17ae | 433 | pim_ifp = ifp->info; |
434 | if (!pim_ifp) | |
435 | return NULL; | |
12e41d03 | 436 | |
d62a17ae | 437 | for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, node, neigh)) { |
9bb93fa0 | 438 | if (!pim_addr_cmp(source_addr, neigh->source_addr)) { |
d62a17ae | 439 | return neigh; |
440 | } | |
441 | } | |
12e41d03 | 442 | |
d62a17ae | 443 | return NULL; |
12e41d03 DL |
444 | } |
445 | ||
9bb93fa0 DL |
446 | struct pim_neighbor *pim_neighbor_find_prefix(struct interface *ifp, |
447 | const struct prefix *src_prefix) | |
448 | { | |
449 | pim_addr addr; | |
450 | ||
451 | addr = pim_addr_from_prefix(src_prefix); | |
452 | return pim_neighbor_find(ifp, addr); | |
453 | } | |
454 | ||
99deb321 DS |
455 | /* |
456 | * Find the *one* interface out | |
457 | * this interface. If more than | |
458 | * one return NULL | |
459 | */ | |
d62a17ae | 460 | struct pim_neighbor *pim_neighbor_find_if(struct interface *ifp) |
99deb321 | 461 | { |
d62a17ae | 462 | struct pim_interface *pim_ifp = ifp->info; |
99deb321 | 463 | |
d62a17ae | 464 | if (!pim_ifp || pim_ifp->pim_neighbor_list->count != 1) |
465 | return NULL; | |
99deb321 | 466 | |
d62a17ae | 467 | return listnode_head(pim_ifp->pim_neighbor_list); |
99deb321 DS |
468 | } |
469 | ||
d62a17ae | 470 | struct pim_neighbor * |
9bb93fa0 | 471 | pim_neighbor_add(struct interface *ifp, pim_addr source_addr, |
d62a17ae | 472 | pim_hello_options hello_options, uint16_t holdtime, |
473 | uint16_t propagation_delay, uint16_t override_interval, | |
474 | uint32_t dr_priority, uint32_t generation_id, | |
475 | struct list *addr_list, int send_hello_now) | |
12e41d03 | 476 | { |
d62a17ae | 477 | struct pim_interface *pim_ifp; |
478 | struct pim_neighbor *neigh; | |
479 | ||
480 | neigh = pim_neighbor_new(ifp, source_addr, hello_options, holdtime, | |
481 | propagation_delay, override_interval, | |
482 | dr_priority, generation_id, addr_list); | |
483 | if (!neigh) { | |
484 | return 0; | |
485 | } | |
12e41d03 | 486 | |
d62a17ae | 487 | pim_ifp = ifp->info; |
df5dfb77 | 488 | assert(pim_ifp); |
12e41d03 | 489 | |
d62a17ae | 490 | listnode_add(pim_ifp->pim_neighbor_list, neigh); |
12e41d03 | 491 | |
9bb93fa0 DL |
492 | if (PIM_DEBUG_PIM_TRACE_DETAIL) |
493 | zlog_debug("%s: neighbor %pPA added ", __func__, &source_addr); | |
d62a17ae | 494 | /* |
495 | RFC 4601: 4.3.2. DR Election | |
496 | ||
497 | A router's idea of the current DR on an interface can change when a | |
498 | PIM Hello message is received, when a neighbor times out, or when a | |
499 | router's own DR Priority changes. | |
500 | */ | |
501 | pim_if_dr_election(neigh->interface); // new neighbor -- should not | |
502 | // trigger dr election... | |
503 | ||
504 | /* | |
505 | RFC 4601: 4.3.1. Sending Hello Messages | |
506 | ||
507 | To allow new or rebooting routers to learn of PIM neighbors quickly, | |
508 | when a Hello message is received from a new neighbor, or a Hello | |
509 | message with a new GenID is received from an existing neighbor, a | |
510 | new Hello message should be sent on this interface after a | |
511 | randomized delay between 0 and Triggered_Hello_Delay. | |
512 | ||
513 | This is a bit silly to do it that way. If I get a new | |
514 | genid we need to send the hello *now* because we've | |
515 | lined up a bunch of join/prune messages to go out the | |
516 | interface. | |
517 | */ | |
518 | if (send_hello_now) | |
519 | pim_hello_restart_now(ifp); | |
520 | else | |
521 | pim_hello_restart_triggered(neigh->interface); | |
522 | ||
9b29ea95 | 523 | pim_upstream_find_new_rpf(pim_ifp->pim); |
d62a17ae | 524 | |
525 | /* RNH can send nexthop update prior to PIM neibhor UP | |
526 | in that case nexthop cache would not consider this neighbor | |
527 | as RPF. | |
528 | Upon PIM neighbor UP, iterate all RPs and update | |
529 | nexthop cache with this neighbor. | |
530 | */ | |
c9cd7fbc | 531 | pim_resolve_rp_nh(pim_ifp->pim, neigh); |
d62a17ae | 532 | |
fec883d9 | 533 | pim_rp_setup(pim_ifp->pim); |
d62a17ae | 534 | |
da11e325 | 535 | sched_rpf_cache_refresh(pim_ifp->pim); |
d62a17ae | 536 | return neigh; |
12e41d03 DL |
537 | } |
538 | ||
d62a17ae | 539 | static uint16_t find_neighbors_next_highest_propagation_delay_msec( |
540 | struct interface *ifp, struct pim_neighbor *highest_neigh) | |
12e41d03 | 541 | { |
d62a17ae | 542 | struct pim_interface *pim_ifp; |
543 | struct listnode *neigh_node; | |
544 | struct pim_neighbor *neigh; | |
545 | uint16_t next_highest_delay_msec; | |
546 | ||
547 | pim_ifp = ifp->info; | |
df5dfb77 | 548 | assert(pim_ifp); |
d62a17ae | 549 | |
550 | next_highest_delay_msec = pim_ifp->pim_propagation_delay_msec; | |
551 | ||
552 | for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neigh_node, | |
553 | neigh)) { | |
554 | if (neigh == highest_neigh) | |
555 | continue; | |
556 | if (neigh->propagation_delay_msec > next_highest_delay_msec) | |
557 | next_highest_delay_msec = neigh->propagation_delay_msec; | |
558 | } | |
12e41d03 | 559 | |
d62a17ae | 560 | return next_highest_delay_msec; |
561 | } | |
12e41d03 | 562 | |
d62a17ae | 563 | static uint16_t find_neighbors_next_highest_override_interval_msec( |
564 | struct interface *ifp, struct pim_neighbor *highest_neigh) | |
565 | { | |
566 | struct pim_interface *pim_ifp; | |
567 | struct listnode *neigh_node; | |
568 | struct pim_neighbor *neigh; | |
569 | uint16_t next_highest_interval_msec; | |
570 | ||
571 | pim_ifp = ifp->info; | |
df5dfb77 | 572 | assert(pim_ifp); |
d62a17ae | 573 | |
574 | next_highest_interval_msec = pim_ifp->pim_override_interval_msec; | |
575 | ||
576 | for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neigh_node, | |
577 | neigh)) { | |
578 | if (neigh == highest_neigh) | |
579 | continue; | |
580 | if (neigh->override_interval_msec > next_highest_interval_msec) | |
581 | next_highest_interval_msec = | |
582 | neigh->override_interval_msec; | |
583 | } | |
12e41d03 | 584 | |
d62a17ae | 585 | return next_highest_interval_msec; |
12e41d03 DL |
586 | } |
587 | ||
d62a17ae | 588 | void pim_neighbor_delete(struct interface *ifp, struct pim_neighbor *neigh, |
12e41d03 DL |
589 | const char *delete_message) |
590 | { | |
d62a17ae | 591 | struct pim_interface *pim_ifp; |
12e41d03 | 592 | |
d62a17ae | 593 | pim_ifp = ifp->info; |
df5dfb77 | 594 | assert(pim_ifp); |
12e41d03 | 595 | |
9bb93fa0 DL |
596 | zlog_notice("PIM NEIGHBOR DOWN: neighbor %pPA on interface %s: %s", |
597 | &neigh->source_addr, ifp->name, delete_message); | |
12e41d03 | 598 | |
d62a17ae | 599 | THREAD_OFF(neigh->t_expire_timer); |
12e41d03 | 600 | |
d62a17ae | 601 | pim_if_assert_on_neighbor_down(ifp, neigh->source_addr); |
12e41d03 | 602 | |
d62a17ae | 603 | if (!PIM_OPTION_IS_SET(neigh->hello_options, |
604 | PIM_OPTION_MASK_LAN_PRUNE_DELAY)) { | |
605 | /* update num. of neighbors without hello option lan_delay */ | |
12e41d03 | 606 | |
d62a17ae | 607 | --pim_ifp->pim_number_of_nonlandelay_neighbors; |
608 | } | |
12e41d03 | 609 | |
d62a17ae | 610 | if (!PIM_OPTION_IS_SET(neigh->hello_options, |
611 | PIM_OPTION_MASK_DR_PRIORITY)) { | |
612 | /* update num. of neighbors without dr_pri */ | |
12e41d03 | 613 | |
d62a17ae | 614 | --pim_ifp->pim_dr_num_nondrpri_neighbors; |
615 | } | |
12e41d03 | 616 | |
df5dfb77 DL |
617 | assert(neigh->propagation_delay_msec |
618 | <= pim_ifp->pim_neighbors_highest_propagation_delay_msec); | |
619 | assert(neigh->override_interval_msec | |
620 | <= pim_ifp->pim_neighbors_highest_override_interval_msec); | |
d62a17ae | 621 | |
622 | if (pim_if_lan_delay_enabled(ifp)) { | |
623 | ||
624 | /* will delete a neighbor with highest propagation delay? */ | |
625 | if (neigh->propagation_delay_msec | |
626 | == pim_ifp->pim_neighbors_highest_propagation_delay_msec) { | |
627 | /* then find the next highest propagation delay */ | |
628 | pim_ifp->pim_neighbors_highest_propagation_delay_msec = | |
629 | find_neighbors_next_highest_propagation_delay_msec( | |
630 | ifp, neigh); | |
631 | } | |
632 | ||
633 | /* will delete a neighbor with highest override interval? */ | |
634 | if (neigh->override_interval_msec | |
635 | == pim_ifp->pim_neighbors_highest_override_interval_msec) { | |
636 | /* then find the next highest propagation delay */ | |
637 | pim_ifp->pim_neighbors_highest_override_interval_msec = | |
638 | find_neighbors_next_highest_override_interval_msec( | |
639 | ifp, neigh); | |
640 | } | |
641 | } | |
12e41d03 | 642 | |
d62a17ae | 643 | if (PIM_DEBUG_PIM_TRACE) { |
9bb93fa0 DL |
644 | zlog_debug("%s: deleting PIM neighbor %pPA on interface %s", |
645 | __func__, &neigh->source_addr, ifp->name); | |
d62a17ae | 646 | } |
12e41d03 | 647 | |
d62a17ae | 648 | listnode_delete(pim_ifp->pim_neighbor_list, neigh); |
12e41d03 | 649 | |
d62a17ae | 650 | pim_neighbor_free(neigh); |
da72c9fd | 651 | |
da11e325 | 652 | sched_rpf_cache_refresh(pim_ifp->pim); |
12e41d03 DL |
653 | } |
654 | ||
d62a17ae | 655 | void pim_neighbor_delete_all(struct interface *ifp, const char *delete_message) |
12e41d03 | 656 | { |
d62a17ae | 657 | struct pim_interface *pim_ifp; |
658 | struct listnode *neigh_node; | |
659 | struct listnode *neigh_nextnode; | |
660 | struct pim_neighbor *neigh; | |
661 | ||
662 | pim_ifp = ifp->info; | |
df5dfb77 | 663 | assert(pim_ifp); |
d62a17ae | 664 | |
665 | for (ALL_LIST_ELEMENTS(pim_ifp->pim_neighbor_list, neigh_node, | |
666 | neigh_nextnode, neigh)) { | |
667 | pim_neighbor_delete(ifp, neigh, delete_message); | |
668 | } | |
12e41d03 DL |
669 | } |
670 | ||
671 | struct prefix *pim_neighbor_find_secondary(struct pim_neighbor *neigh, | |
07b17d59 | 672 | struct prefix *addr) |
12e41d03 | 673 | { |
d62a17ae | 674 | struct listnode *node; |
675 | struct prefix *p; | |
12e41d03 | 676 | |
d62a17ae | 677 | if (!neigh->prefix_list) |
678 | return 0; | |
12e41d03 | 679 | |
d62a17ae | 680 | for (ALL_LIST_ELEMENTS_RO(neigh->prefix_list, node, p)) { |
681 | if (prefix_same(p, addr)) | |
682 | return p; | |
683 | } | |
12e41d03 | 684 | |
d62a17ae | 685 | return NULL; |
12e41d03 DL |
686 | } |
687 | ||
688 | /* | |
689 | RFC 4601: 4.3.4. Maintaining Secondary Address Lists | |
d62a17ae | 690 | |
12e41d03 DL |
691 | All the advertised secondary addresses in received Hello messages |
692 | must be checked against those previously advertised by all other | |
693 | PIM neighbors on that interface. If there is a conflict and the | |
694 | same secondary address was previously advertised by another | |
695 | neighbor, then only the most recently received mapping MUST be | |
696 | maintained, and an error message SHOULD be logged to the | |
697 | administrator in a rate-limited manner. | |
698 | */ | |
699 | static void delete_from_neigh_addr(struct interface *ifp, | |
9bb93fa0 | 700 | struct list *addr_list, pim_addr neigh_addr) |
12e41d03 | 701 | { |
d62a17ae | 702 | struct listnode *addr_node; |
703 | struct prefix *addr; | |
704 | struct pim_interface *pim_ifp; | |
705 | ||
706 | pim_ifp = ifp->info; | |
df5dfb77 | 707 | assert(pim_ifp); |
d62a17ae | 708 | |
df5dfb77 | 709 | assert(addr_list); |
d62a17ae | 710 | |
711 | /* | |
712 | Scan secondary address list | |
713 | */ | |
714 | for (ALL_LIST_ELEMENTS_RO(addr_list, addr_node, addr)) { | |
715 | struct listnode *neigh_node; | |
716 | struct pim_neighbor *neigh; | |
717 | ||
718 | if (addr->family != AF_INET) | |
719 | continue; | |
720 | ||
721 | /* | |
722 | Scan neighbors | |
723 | */ | |
724 | for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, | |
725 | neigh_node, neigh)) { | |
726 | { | |
727 | struct prefix *p = pim_neighbor_find_secondary( | |
728 | neigh, addr); | |
729 | if (p) { | |
730 | char addr_str[INET_ADDRSTRLEN]; | |
d62a17ae | 731 | |
732 | pim_inet4_dump( | |
733 | "<addr?>", addr->u.prefix4, | |
734 | addr_str, sizeof(addr_str)); | |
d62a17ae | 735 | |
736 | zlog_info( | |
9bb93fa0 DL |
737 | "secondary addr %s recvd from neigh %pPA deleted from neigh %pPA on %s", |
738 | addr_str, &neigh_addr, | |
739 | &neigh->source_addr, ifp->name); | |
d62a17ae | 740 | |
741 | listnode_delete(neigh->prefix_list, p); | |
63265b5c | 742 | prefix_free(&p); |
d62a17ae | 743 | } |
744 | } | |
745 | ||
746 | } /* scan neighbors */ | |
747 | ||
748 | } /* scan addr list */ | |
12e41d03 DL |
749 | } |
750 | ||
751 | void pim_neighbor_update(struct pim_neighbor *neigh, | |
d62a17ae | 752 | pim_hello_options hello_options, uint16_t holdtime, |
753 | uint32_t dr_priority, struct list *addr_list) | |
12e41d03 | 754 | { |
d62a17ae | 755 | struct pim_interface *pim_ifp = neigh->interface->info; |
b3a474d8 | 756 | uint32_t old, new; |
d62a17ae | 757 | |
758 | /* Received holdtime ? */ | |
759 | if (PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME)) { | |
760 | pim_neighbor_timer_reset(neigh, holdtime); | |
761 | } else { | |
762 | pim_neighbor_timer_reset(neigh, | |
763 | PIM_IF_DEFAULT_HOLDTIME(pim_ifp)); | |
764 | } | |
12e41d03 DL |
765 | |
766 | #ifdef DUMP_PREFIX_LIST | |
d62a17ae | 767 | zlog_debug( |
768 | "%s: DUMP_PREFIX_LIST old_prefix_list=%x old_size=%d new_prefix_list=%x new_size=%d", | |
15569c58 | 769 | __func__, (unsigned)neigh->prefix_list, |
d62a17ae | 770 | neigh->prefix_list ? (int)listcount(neigh->prefix_list) : -1, |
771 | (unsigned)addr_list, | |
772 | addr_list ? (int)listcount(addr_list) : -1); | |
12e41d03 DL |
773 | #endif |
774 | ||
d62a17ae | 775 | if (neigh->prefix_list == addr_list) { |
776 | if (addr_list) { | |
af4c2728 | 777 | flog_err( |
450971aa | 778 | EC_LIB_DEVELOPMENT, |
d62a17ae | 779 | "%s: internal error: trying to replace same prefix list=%p", |
15569c58 | 780 | __func__, (void *)addr_list); |
d62a17ae | 781 | } |
782 | } else { | |
783 | /* Delete existing secondary address list */ | |
784 | delete_prefix_list(neigh); | |
785 | } | |
786 | ||
787 | if (addr_list) { | |
788 | delete_from_neigh_addr(neigh->interface, addr_list, | |
789 | neigh->source_addr); | |
790 | } | |
791 | ||
792 | /* Replace secondary address list */ | |
793 | neigh->prefix_list = addr_list; | |
794 | ||
795 | update_dr_priority(neigh, hello_options, dr_priority); | |
b3a474d8 | 796 | new = PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_LAN_PRUNE_DELAY); |
797 | old = PIM_OPTION_IS_SET(neigh->hello_options, | |
798 | PIM_OPTION_MASK_LAN_PRUNE_DELAY); | |
799 | ||
800 | if (old != new) { | |
801 | if (old) | |
802 | ++pim_ifp->pim_number_of_nonlandelay_neighbors; | |
803 | else | |
804 | --pim_ifp->pim_number_of_nonlandelay_neighbors; | |
805 | } | |
d62a17ae | 806 | /* |
807 | Copy flags | |
808 | */ | |
809 | neigh->hello_options = hello_options; | |
12e41d03 | 810 | } |