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