/src/frr/pimd/pim_iface.c
Line | Count | Source (jump to first uncovered line) |
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 "if.h" |
10 | | #include "log.h" |
11 | | #include "vty.h" |
12 | | #include "memory.h" |
13 | | #include "prefix.h" |
14 | | #include "vrf.h" |
15 | | #include "linklist.h" |
16 | | #include "plist.h" |
17 | | #include "hash.h" |
18 | | #include "ferr.h" |
19 | | #include "network.h" |
20 | | |
21 | | #include "pimd.h" |
22 | | #include "pim_instance.h" |
23 | | #include "pim_zebra.h" |
24 | | #include "pim_iface.h" |
25 | | #include "pim_igmp.h" |
26 | | #include "pim_mroute.h" |
27 | | #include "pim_oil.h" |
28 | | #include "pim_str.h" |
29 | | #include "pim_pim.h" |
30 | | #include "pim_neighbor.h" |
31 | | #include "pim_ifchannel.h" |
32 | | #include "pim_sock.h" |
33 | | #include "pim_time.h" |
34 | | #include "pim_ssmpingd.h" |
35 | | #include "pim_rp.h" |
36 | | #include "pim_nht.h" |
37 | | #include "pim_jp_agg.h" |
38 | | #include "pim_igmp_join.h" |
39 | | #include "pim_vxlan.h" |
40 | | |
41 | | #include "pim6_mld.h" |
42 | | |
43 | | static void pim_if_gm_join_del_all(struct interface *ifp); |
44 | | |
45 | | static int gm_join_sock(const char *ifname, ifindex_t ifindex, |
46 | | pim_addr group_addr, pim_addr source_addr, |
47 | | struct pim_interface *pim_ifp); |
48 | | |
49 | | void pim_if_init(struct pim_instance *pim) |
50 | 1 | { |
51 | 1 | int i; |
52 | | |
53 | 33 | for (i = 0; i < MAXVIFS; i++) |
54 | 32 | pim->iface_vif_index[i] = 0; |
55 | 1 | } |
56 | | |
57 | | void pim_if_terminate(struct pim_instance *pim) |
58 | 0 | { |
59 | 0 | struct interface *ifp; |
60 | |
|
61 | 0 | FOR_ALL_INTERFACES (pim->vrf, ifp) { |
62 | 0 | struct pim_interface *pim_ifp = ifp->info; |
63 | |
|
64 | 0 | if (!pim_ifp) |
65 | 0 | continue; |
66 | | |
67 | 0 | pim_if_delete(ifp); |
68 | 0 | } |
69 | 0 | return; |
70 | 0 | } |
71 | | |
72 | | static void pim_sec_addr_free(struct pim_secondary_addr *sec_addr) |
73 | 0 | { |
74 | 0 | XFREE(MTYPE_PIM_SEC_ADDR, sec_addr); |
75 | 0 | } |
76 | | |
77 | | __attribute__((unused)) |
78 | | static int pim_sec_addr_comp(const void *p1, const void *p2) |
79 | 0 | { |
80 | 0 | const struct pim_secondary_addr *sec1 = p1; |
81 | 0 | const struct pim_secondary_addr *sec2 = p2; |
82 | |
|
83 | 0 | if (sec1->addr.family == AF_INET && sec2->addr.family == AF_INET6) |
84 | 0 | return -1; |
85 | | |
86 | 0 | if (sec1->addr.family == AF_INET6 && sec2->addr.family == AF_INET) |
87 | 0 | return 1; |
88 | | |
89 | 0 | if (sec1->addr.family == AF_INET) { |
90 | 0 | if (ntohl(sec1->addr.u.prefix4.s_addr) |
91 | 0 | < ntohl(sec2->addr.u.prefix4.s_addr)) |
92 | 0 | return -1; |
93 | | |
94 | 0 | if (ntohl(sec1->addr.u.prefix4.s_addr) |
95 | 0 | > ntohl(sec2->addr.u.prefix4.s_addr)) |
96 | 0 | return 1; |
97 | 0 | } else { |
98 | 0 | return memcmp(&sec1->addr.u.prefix6, &sec2->addr.u.prefix6, |
99 | 0 | sizeof(struct in6_addr)); |
100 | 0 | } |
101 | | |
102 | 0 | return 0; |
103 | 0 | } |
104 | | |
105 | | struct pim_interface *pim_if_new(struct interface *ifp, bool gm, bool pim, |
106 | | bool ispimreg, bool is_vxlan_term) |
107 | 2 | { |
108 | 2 | struct pim_interface *pim_ifp; |
109 | | |
110 | 2 | assert(ifp); |
111 | 2 | assert(!ifp->info); |
112 | | |
113 | 2 | pim_ifp = XCALLOC(MTYPE_PIM_INTERFACE, sizeof(*pim_ifp)); |
114 | | |
115 | 2 | pim_ifp->pim = ifp->vrf->info; |
116 | 2 | pim_ifp->mroute_vif_index = -1; |
117 | | |
118 | 2 | pim_ifp->igmp_version = IGMP_DEFAULT_VERSION; |
119 | 2 | pim_ifp->mld_version = MLD_DEFAULT_VERSION; |
120 | 2 | pim_ifp->gm_default_robustness_variable = |
121 | 2 | GM_DEFAULT_ROBUSTNESS_VARIABLE; |
122 | 2 | pim_ifp->gm_default_query_interval = GM_GENERAL_QUERY_INTERVAL; |
123 | 2 | pim_ifp->gm_query_max_response_time_dsec = |
124 | 2 | GM_QUERY_MAX_RESPONSE_TIME_DSEC; |
125 | 2 | pim_ifp->gm_specific_query_max_response_time_dsec = |
126 | 2 | GM_SPECIFIC_QUERY_MAX_RESPONSE_TIME_DSEC; |
127 | 2 | pim_ifp->gm_last_member_query_count = GM_DEFAULT_ROBUSTNESS_VARIABLE; |
128 | | |
129 | | /* BSM config on interface: true by default */ |
130 | 2 | pim_ifp->bsm_enable = true; |
131 | 2 | pim_ifp->ucast_bsm_accept = true; |
132 | 2 | pim_ifp->am_i_dr = false; |
133 | | |
134 | | /* |
135 | | RFC 3376: 8.3. Query Response Interval |
136 | | The number of seconds represented by the [Query Response Interval] |
137 | | must be less than the [Query Interval]. |
138 | | */ |
139 | 2 | assert(pim_ifp->gm_query_max_response_time_dsec < |
140 | 2 | pim_ifp->gm_default_query_interval); |
141 | | |
142 | 2 | pim_ifp->pim_enable = pim; |
143 | 2 | pim_ifp->pim_passive_enable = false; |
144 | 2 | pim_ifp->gm_enable = gm; |
145 | | |
146 | 2 | pim_ifp->gm_join_list = NULL; |
147 | 2 | pim_ifp->pim_neighbor_list = NULL; |
148 | 2 | pim_ifp->upstream_switch_list = NULL; |
149 | 2 | pim_ifp->pim_generation_id = 0; |
150 | | |
151 | | /* list of struct gm_sock */ |
152 | 2 | pim_igmp_if_init(pim_ifp, ifp); |
153 | | |
154 | | /* list of struct pim_neighbor */ |
155 | 2 | pim_ifp->pim_neighbor_list = list_new(); |
156 | 2 | pim_ifp->pim_neighbor_list->del = (void (*)(void *))pim_neighbor_free; |
157 | | |
158 | 2 | pim_ifp->upstream_switch_list = list_new(); |
159 | 2 | pim_ifp->upstream_switch_list->del = |
160 | 2 | (void (*)(void *))pim_jp_agg_group_list_free; |
161 | 2 | pim_ifp->upstream_switch_list->cmp = pim_jp_agg_group_list_cmp; |
162 | | |
163 | 2 | pim_ifp->sec_addr_list = list_new(); |
164 | 2 | pim_ifp->sec_addr_list->del = (void (*)(void *))pim_sec_addr_free; |
165 | 2 | pim_ifp->sec_addr_list->cmp = |
166 | 2 | (int (*)(void *, void *))pim_sec_addr_comp; |
167 | | |
168 | 2 | pim_ifp->activeactive = false; |
169 | | |
170 | 2 | RB_INIT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb); |
171 | | |
172 | 2 | ifp->info = pim_ifp; |
173 | | |
174 | 2 | pim_sock_reset(ifp); |
175 | | |
176 | 2 | pim_if_add_vif(ifp, ispimreg, is_vxlan_term); |
177 | 2 | pim_ifp->pim->mcast_if_count++; |
178 | | |
179 | 2 | return pim_ifp; |
180 | 2 | } |
181 | | |
182 | | void pim_if_delete(struct interface *ifp) |
183 | 0 | { |
184 | 0 | struct pim_interface *pim_ifp; |
185 | |
|
186 | 0 | assert(ifp); |
187 | 0 | pim_ifp = ifp->info; |
188 | 0 | assert(pim_ifp); |
189 | | |
190 | 0 | pim_ifp->pim->mcast_if_count--; |
191 | 0 | if (pim_ifp->gm_join_list) { |
192 | 0 | pim_if_gm_join_del_all(ifp); |
193 | 0 | } |
194 | |
|
195 | 0 | pim_ifchannel_delete_all(ifp); |
196 | 0 | #if PIM_IPV == 4 |
197 | 0 | igmp_sock_delete_all(ifp); |
198 | 0 | #endif |
199 | 0 | if (pim_ifp->pim_sock_fd >= 0) |
200 | 0 | pim_sock_delete(ifp, "Interface removed from configuration"); |
201 | |
|
202 | 0 | pim_if_del_vif(ifp); |
203 | |
|
204 | 0 | pim_igmp_if_fini(pim_ifp); |
205 | |
|
206 | 0 | list_delete(&pim_ifp->pim_neighbor_list); |
207 | 0 | list_delete(&pim_ifp->upstream_switch_list); |
208 | 0 | list_delete(&pim_ifp->sec_addr_list); |
209 | |
|
210 | 0 | if (pim_ifp->bfd_config.profile) |
211 | 0 | XFREE(MTYPE_TMP, pim_ifp->bfd_config.profile); |
212 | |
|
213 | 0 | XFREE(MTYPE_PIM_INTERFACE, pim_ifp->boundary_oil_plist); |
214 | 0 | XFREE(MTYPE_PIM_INTERFACE, pim_ifp); |
215 | |
|
216 | 0 | ifp->info = NULL; |
217 | 0 | } |
218 | | |
219 | | void pim_if_update_could_assert(struct interface *ifp) |
220 | 3 | { |
221 | 3 | struct pim_interface *pim_ifp; |
222 | 3 | struct pim_ifchannel *ch; |
223 | | |
224 | 3 | pim_ifp = ifp->info; |
225 | 3 | assert(pim_ifp); |
226 | | |
227 | 3 | RB_FOREACH (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb) { |
228 | 0 | pim_ifchannel_update_could_assert(ch); |
229 | 0 | } |
230 | 3 | } |
231 | | |
232 | | static void pim_if_update_my_assert_metric(struct interface *ifp) |
233 | 0 | { |
234 | 0 | struct pim_interface *pim_ifp; |
235 | 0 | struct pim_ifchannel *ch; |
236 | |
|
237 | 0 | pim_ifp = ifp->info; |
238 | 0 | assert(pim_ifp); |
239 | | |
240 | 0 | RB_FOREACH (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb) { |
241 | 0 | pim_ifchannel_update_my_assert_metric(ch); |
242 | 0 | } |
243 | 0 | } |
244 | | |
245 | | static void pim_addr_change(struct interface *ifp) |
246 | 0 | { |
247 | 0 | struct pim_interface *pim_ifp; |
248 | |
|
249 | 0 | pim_ifp = ifp->info; |
250 | 0 | assert(pim_ifp); |
251 | | |
252 | 0 | pim_if_dr_election(ifp); /* router's own DR Priority (addr) changes -- |
253 | | Done TODO T30 */ |
254 | 0 | pim_if_update_join_desired(pim_ifp); /* depends on DR */ |
255 | 0 | pim_if_update_could_assert(ifp); /* depends on DR */ |
256 | 0 | pim_if_update_my_assert_metric(ifp); /* depends on could_assert */ |
257 | 0 | pim_if_update_assert_tracking_desired( |
258 | 0 | ifp); /* depends on DR, join_desired */ |
259 | | |
260 | | /* |
261 | | RFC 4601: 4.3.1. Sending Hello Messages |
262 | | |
263 | | 1) Before an interface goes down or changes primary IP address, a |
264 | | Hello message with a zero HoldTime should be sent immediately |
265 | | (with the old IP address if the IP address changed). |
266 | | -- Done at the caller of the function as new ip already updated here |
267 | | |
268 | | 2) After an interface has changed its IP address, it MUST send a |
269 | | Hello message with its new IP address. |
270 | | -- DONE below |
271 | | |
272 | | 3) If an interface changes one of its secondary IP addresses, a |
273 | | Hello message with an updated Address_List option and a non-zero |
274 | | HoldTime should be sent immediately. |
275 | | -- FIXME See TODO T31 |
276 | | */ |
277 | 0 | PIM_IF_FLAG_UNSET_HELLO_SENT(pim_ifp->flags); |
278 | 0 | if (pim_ifp->pim_sock_fd < 0) |
279 | 0 | return; |
280 | 0 | pim_hello_restart_now(ifp); /* send hello and restart timer */ |
281 | 0 | } |
282 | | |
283 | | static int detect_primary_address_change(struct interface *ifp, |
284 | | int force_prim_as_any, |
285 | | const char *caller) |
286 | 0 | { |
287 | 0 | struct pim_interface *pim_ifp = ifp->info; |
288 | 0 | pim_addr new_prim_addr; |
289 | 0 | int changed; |
290 | |
|
291 | 0 | if (force_prim_as_any) |
292 | 0 | new_prim_addr = PIMADDR_ANY; |
293 | 0 | else |
294 | 0 | new_prim_addr = pim_find_primary_addr(ifp); |
295 | |
|
296 | 0 | changed = pim_addr_cmp(new_prim_addr, pim_ifp->primary_address); |
297 | |
|
298 | 0 | if (PIM_DEBUG_ZEBRA) |
299 | 0 | zlog_debug("%s: old=%pPA new=%pPA on interface %s: %s", |
300 | 0 | __func__, &pim_ifp->primary_address, &new_prim_addr, |
301 | 0 | ifp->name, changed ? "changed" : "unchanged"); |
302 | |
|
303 | 0 | if (changed) { |
304 | | /* Before updating pim_ifp send Hello time with 0 hold time */ |
305 | 0 | if (pim_ifp->pim_enable) { |
306 | 0 | pim_hello_send(ifp, 0 /* zero-sec holdtime */); |
307 | 0 | } |
308 | 0 | pim_ifp->primary_address = new_prim_addr; |
309 | 0 | } |
310 | |
|
311 | 0 | return changed; |
312 | 0 | } |
313 | | |
314 | | static struct pim_secondary_addr * |
315 | | pim_sec_addr_find(struct pim_interface *pim_ifp, struct prefix *addr) |
316 | 0 | { |
317 | 0 | struct pim_secondary_addr *sec_addr; |
318 | 0 | struct listnode *node; |
319 | |
|
320 | 0 | for (ALL_LIST_ELEMENTS_RO(pim_ifp->sec_addr_list, node, sec_addr)) { |
321 | 0 | if (prefix_cmp(&sec_addr->addr, addr) == 0) { |
322 | 0 | return sec_addr; |
323 | 0 | } |
324 | 0 | } |
325 | | |
326 | 0 | return NULL; |
327 | 0 | } |
328 | | |
329 | | static void pim_sec_addr_del(struct pim_interface *pim_ifp, |
330 | | struct pim_secondary_addr *sec_addr) |
331 | 0 | { |
332 | 0 | listnode_delete(pim_ifp->sec_addr_list, sec_addr); |
333 | 0 | pim_sec_addr_free(sec_addr); |
334 | 0 | } |
335 | | |
336 | | static int pim_sec_addr_add(struct pim_interface *pim_ifp, struct prefix *addr) |
337 | 0 | { |
338 | 0 | int changed = 0; |
339 | 0 | struct pim_secondary_addr *sec_addr; |
340 | |
|
341 | 0 | sec_addr = pim_sec_addr_find(pim_ifp, addr); |
342 | 0 | if (sec_addr) { |
343 | 0 | sec_addr->flags &= ~PIM_SEC_ADDRF_STALE; |
344 | 0 | return changed; |
345 | 0 | } |
346 | | |
347 | 0 | sec_addr = XCALLOC(MTYPE_PIM_SEC_ADDR, sizeof(*sec_addr)); |
348 | |
|
349 | 0 | changed = 1; |
350 | 0 | sec_addr->addr = *addr; |
351 | 0 | listnode_add_sort(pim_ifp->sec_addr_list, sec_addr); |
352 | |
|
353 | 0 | return changed; |
354 | 0 | } |
355 | | |
356 | | static int pim_sec_addr_del_all(struct pim_interface *pim_ifp) |
357 | 0 | { |
358 | 0 | int changed = 0; |
359 | |
|
360 | 0 | if (!list_isempty(pim_ifp->sec_addr_list)) { |
361 | 0 | changed = 1; |
362 | | /* remove all nodes and free up the list itself */ |
363 | 0 | list_delete_all_node(pim_ifp->sec_addr_list); |
364 | 0 | } |
365 | |
|
366 | 0 | return changed; |
367 | 0 | } |
368 | | |
369 | | static int pim_sec_addr_update(struct interface *ifp) |
370 | 0 | { |
371 | 0 | struct pim_interface *pim_ifp = ifp->info; |
372 | 0 | struct connected *ifc; |
373 | 0 | struct listnode *node; |
374 | 0 | struct listnode *nextnode; |
375 | 0 | struct pim_secondary_addr *sec_addr; |
376 | 0 | int changed = 0; |
377 | |
|
378 | 0 | for (ALL_LIST_ELEMENTS_RO(pim_ifp->sec_addr_list, node, sec_addr)) { |
379 | 0 | sec_addr->flags |= PIM_SEC_ADDRF_STALE; |
380 | 0 | } |
381 | | |
382 | 0 | for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { |
383 | 0 | pim_addr addr = pim_addr_from_prefix(ifc->address); |
384 | |
|
385 | 0 | if (pim_addr_is_any(addr)) |
386 | 0 | continue; |
387 | | |
388 | 0 | if (!pim_addr_cmp(addr, pim_ifp->primary_address)) { |
389 | | /* don't add the primary address into the secondary |
390 | | * address list */ |
391 | 0 | continue; |
392 | 0 | } |
393 | | |
394 | 0 | if (pim_sec_addr_add(pim_ifp, ifc->address)) { |
395 | 0 | changed = 1; |
396 | 0 | } |
397 | 0 | } |
398 | | |
399 | | /* Drop stale entries */ |
400 | 0 | for (ALL_LIST_ELEMENTS(pim_ifp->sec_addr_list, node, nextnode, |
401 | 0 | sec_addr)) { |
402 | 0 | if (sec_addr->flags & PIM_SEC_ADDRF_STALE) { |
403 | 0 | pim_sec_addr_del(pim_ifp, sec_addr); |
404 | 0 | changed = 1; |
405 | 0 | } |
406 | 0 | } |
407 | | |
408 | 0 | return changed; |
409 | 0 | } |
410 | | |
411 | | static int detect_secondary_address_change(struct interface *ifp, |
412 | | int force_prim_as_any, |
413 | | const char *caller) |
414 | 0 | { |
415 | 0 | struct pim_interface *pim_ifp = ifp->info; |
416 | 0 | int changed = 0; |
417 | |
|
418 | 0 | if (force_prim_as_any) { |
419 | | /* if primary address is being forced to zero just flush the |
420 | | * secondary address list */ |
421 | 0 | changed = pim_sec_addr_del_all(pim_ifp); |
422 | 0 | } else { |
423 | | /* re-evaluate the secondary address list */ |
424 | 0 | changed = pim_sec_addr_update(ifp); |
425 | 0 | } |
426 | |
|
427 | 0 | return changed; |
428 | 0 | } |
429 | | |
430 | | static void detect_address_change(struct interface *ifp, int force_prim_as_any, |
431 | | const char *caller) |
432 | 0 | { |
433 | 0 | int changed = 0; |
434 | 0 | struct pim_interface *pim_ifp; |
435 | |
|
436 | 0 | pim_ifp = ifp->info; |
437 | 0 | if (!pim_ifp) |
438 | 0 | return; |
439 | | |
440 | 0 | if (detect_primary_address_change(ifp, force_prim_as_any, caller)) { |
441 | 0 | changed = 1; |
442 | 0 | } |
443 | |
|
444 | 0 | if (detect_secondary_address_change(ifp, force_prim_as_any, caller)) { |
445 | 0 | changed = 1; |
446 | 0 | } |
447 | | |
448 | |
|
449 | 0 | if (changed) { |
450 | 0 | if (!pim_ifp->pim_enable) { |
451 | 0 | return; |
452 | 0 | } |
453 | | |
454 | 0 | pim_addr_change(ifp); |
455 | 0 | } |
456 | | |
457 | | /* XXX: if we have unnumbered interfaces we need to run detect address |
458 | | * address change on all of them when the lo address changes */ |
459 | 0 | } |
460 | | |
461 | | int pim_update_source_set(struct interface *ifp, pim_addr source) |
462 | 0 | { |
463 | 0 | struct pim_interface *pim_ifp = ifp->info; |
464 | |
|
465 | 0 | if (!pim_ifp) { |
466 | 0 | return PIM_IFACE_NOT_FOUND; |
467 | 0 | } |
468 | | |
469 | 0 | if (!pim_addr_cmp(pim_ifp->update_source, source)) { |
470 | 0 | return PIM_UPDATE_SOURCE_DUP; |
471 | 0 | } |
472 | | |
473 | 0 | pim_ifp->update_source = source; |
474 | 0 | detect_address_change(ifp, 0 /* force_prim_as_any */, __func__); |
475 | |
|
476 | 0 | return PIM_SUCCESS; |
477 | 0 | } |
478 | | |
479 | | void pim_if_addr_add(struct connected *ifc) |
480 | 0 | { |
481 | 0 | struct pim_interface *pim_ifp; |
482 | 0 | struct interface *ifp; |
483 | 0 | bool vxlan_term; |
484 | |
|
485 | 0 | assert(ifc); |
486 | | |
487 | 0 | ifp = ifc->ifp; |
488 | 0 | assert(ifp); |
489 | 0 | pim_ifp = ifp->info; |
490 | 0 | if (!pim_ifp) |
491 | 0 | return; |
492 | | |
493 | 0 | if (!if_is_operative(ifp)) |
494 | 0 | return; |
495 | | |
496 | 0 | if (PIM_DEBUG_ZEBRA) |
497 | 0 | zlog_debug("%s: %s ifindex=%d connected IP address %pFX %s", |
498 | 0 | __func__, ifp->name, ifp->ifindex, ifc->address, |
499 | 0 | CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY) |
500 | 0 | ? "secondary" |
501 | 0 | : "primary"); |
502 | | #if PIM_IPV != 4 |
503 | | if (IN6_IS_ADDR_LINKLOCAL(&ifc->address->u.prefix6) || |
504 | | IN6_IS_ADDR_LOOPBACK(&ifc->address->u.prefix6)) { |
505 | | if (IN6_IS_ADDR_UNSPECIFIED(&pim_ifp->ll_lowest)) |
506 | | pim_ifp->ll_lowest = ifc->address->u.prefix6; |
507 | | else if (IPV6_ADDR_CMP(&ifc->address->u.prefix6, |
508 | | &pim_ifp->ll_lowest) < 0) |
509 | | pim_ifp->ll_lowest = ifc->address->u.prefix6; |
510 | | |
511 | | if (IPV6_ADDR_CMP(&ifc->address->u.prefix6, |
512 | | &pim_ifp->ll_highest) > 0) |
513 | | pim_ifp->ll_highest = ifc->address->u.prefix6; |
514 | | |
515 | | if (PIM_DEBUG_ZEBRA) |
516 | | zlog_debug( |
517 | | "%s: new link-local %pI6, lowest now %pI6, highest %pI6", |
518 | | ifc->ifp->name, &ifc->address->u.prefix6, |
519 | | &pim_ifp->ll_lowest, &pim_ifp->ll_highest); |
520 | | } |
521 | | #endif |
522 | |
|
523 | 0 | detect_address_change(ifp, 0, __func__); |
524 | | |
525 | | // if (ifc->address->family != AF_INET) |
526 | | // return; |
527 | |
|
528 | 0 | #if PIM_IPV == 4 |
529 | 0 | struct in_addr ifaddr = ifc->address->u.prefix4; |
530 | |
|
531 | 0 | if (pim_ifp->gm_enable) { |
532 | 0 | struct gm_sock *igmp; |
533 | | |
534 | | /* lookup IGMP socket */ |
535 | 0 | igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->gm_socket_list, |
536 | 0 | ifaddr); |
537 | 0 | if (!igmp) { |
538 | | /* if addr new, add IGMP socket */ |
539 | 0 | if (ifc->address->family == AF_INET) |
540 | 0 | pim_igmp_sock_add(pim_ifp->gm_socket_list, |
541 | 0 | ifaddr, ifp, false); |
542 | 0 | } else if (igmp->mtrace_only) { |
543 | 0 | igmp_sock_delete(igmp); |
544 | 0 | pim_igmp_sock_add(pim_ifp->gm_socket_list, ifaddr, ifp, |
545 | 0 | false); |
546 | 0 | } |
547 | | |
548 | | /* Replay Static IGMP groups */ |
549 | 0 | if (pim_ifp->gm_join_list) { |
550 | 0 | struct listnode *node; |
551 | 0 | struct listnode *nextnode; |
552 | 0 | struct gm_join *ij; |
553 | 0 | int join_fd; |
554 | |
|
555 | 0 | for (ALL_LIST_ELEMENTS(pim_ifp->gm_join_list, node, |
556 | 0 | nextnode, ij)) { |
557 | | /* Close socket and reopen with Source and Group |
558 | | */ |
559 | 0 | close(ij->sock_fd); |
560 | 0 | join_fd = gm_join_sock( |
561 | 0 | ifp->name, ifp->ifindex, ij->group_addr, |
562 | 0 | ij->source_addr, pim_ifp); |
563 | 0 | if (join_fd < 0) { |
564 | 0 | char group_str[INET_ADDRSTRLEN]; |
565 | 0 | char source_str[INET_ADDRSTRLEN]; |
566 | 0 | pim_inet4_dump("<grp?>", ij->group_addr, |
567 | 0 | group_str, |
568 | 0 | sizeof(group_str)); |
569 | 0 | pim_inet4_dump( |
570 | 0 | "<src?>", ij->source_addr, |
571 | 0 | source_str, sizeof(source_str)); |
572 | 0 | zlog_warn( |
573 | 0 | "%s: gm_join_sock() failure for IGMP group %s source %s on interface %s", |
574 | 0 | __func__, group_str, source_str, |
575 | 0 | ifp->name); |
576 | | /* warning only */ |
577 | 0 | } else |
578 | 0 | ij->sock_fd = join_fd; |
579 | 0 | } |
580 | 0 | } |
581 | 0 | } /* igmp */ |
582 | 0 | else { |
583 | 0 | struct gm_sock *igmp; |
584 | | |
585 | | /* lookup IGMP socket */ |
586 | 0 | igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->gm_socket_list, |
587 | 0 | ifaddr); |
588 | 0 | if (ifc->address->family == AF_INET) { |
589 | 0 | if (igmp) |
590 | 0 | igmp_sock_delete(igmp); |
591 | | /* if addr new, add IGMP socket */ |
592 | 0 | pim_igmp_sock_add(pim_ifp->gm_socket_list, ifaddr, ifp, |
593 | 0 | true); |
594 | 0 | } |
595 | 0 | } /* igmp mtrace only */ |
596 | 0 | #endif |
597 | | |
598 | 0 | if (pim_ifp->pim_enable) { |
599 | |
|
600 | 0 | if (!pim_addr_is_any(pim_ifp->primary_address)) { |
601 | | |
602 | | /* Interface has a valid socket ? */ |
603 | 0 | if (pim_ifp->pim_sock_fd < 0) { |
604 | 0 | if (pim_sock_add(ifp)) { |
605 | 0 | zlog_warn( |
606 | 0 | "Failure creating PIM socket for interface %s", |
607 | 0 | ifp->name); |
608 | 0 | } |
609 | 0 | } |
610 | 0 | struct pim_nexthop_cache *pnc = NULL; |
611 | 0 | struct pim_rpf rpf; |
612 | 0 | struct zclient *zclient = NULL; |
613 | |
|
614 | 0 | zclient = pim_zebra_zclient_get(); |
615 | | /* RP config might come prior to (local RP's interface) |
616 | | IF UP event. |
617 | | In this case, pnc would not have pim enabled |
618 | | nexthops. |
619 | | Once Interface is UP and pim info is available, |
620 | | reregister |
621 | | with RNH address to receive update and add the |
622 | | interface as nexthop. */ |
623 | 0 | memset(&rpf, 0, sizeof(struct pim_rpf)); |
624 | 0 | rpf.rpf_addr = pim_addr_from_prefix(ifc->address); |
625 | 0 | pnc = pim_nexthop_cache_find(pim_ifp->pim, &rpf); |
626 | 0 | if (pnc) |
627 | 0 | pim_sendmsg_zebra_rnh(pim_ifp->pim, zclient, |
628 | 0 | pnc, |
629 | 0 | ZEBRA_NEXTHOP_REGISTER); |
630 | 0 | } |
631 | 0 | } /* pim */ |
632 | | |
633 | | /* |
634 | | PIM or IGMP is enabled on interface, and there is at least one |
635 | | address assigned, then try to create a vif_index. |
636 | | */ |
637 | 0 | if (pim_ifp->mroute_vif_index < 0) { |
638 | 0 | vxlan_term = pim_vxlan_is_term_dev_cfg(pim_ifp->pim, ifp); |
639 | 0 | pim_if_add_vif(ifp, false, vxlan_term); |
640 | 0 | } |
641 | 0 | gm_ifp_update(ifp); |
642 | 0 | pim_ifchannel_scan_forward_start(ifp); |
643 | 0 | } |
644 | | |
645 | | static void pim_if_addr_del_igmp(struct connected *ifc) |
646 | 0 | { |
647 | 0 | #if PIM_IPV == 4 |
648 | 0 | struct pim_interface *pim_ifp = ifc->ifp->info; |
649 | 0 | struct gm_sock *igmp; |
650 | 0 | struct in_addr ifaddr; |
651 | |
|
652 | 0 | if (ifc->address->family != AF_INET) { |
653 | | /* non-IPv4 address */ |
654 | 0 | return; |
655 | 0 | } |
656 | | |
657 | 0 | if (!pim_ifp) { |
658 | | /* IGMP not enabled on interface */ |
659 | 0 | return; |
660 | 0 | } |
661 | | |
662 | 0 | ifaddr = ifc->address->u.prefix4; |
663 | | |
664 | | /* lookup IGMP socket */ |
665 | 0 | igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->gm_socket_list, ifaddr); |
666 | 0 | if (igmp) { |
667 | | /* if addr found, del IGMP socket */ |
668 | 0 | igmp_sock_delete(igmp); |
669 | 0 | } |
670 | 0 | #endif |
671 | 0 | } |
672 | | |
673 | | static void pim_if_addr_del_pim(struct connected *ifc) |
674 | 0 | { |
675 | 0 | struct pim_interface *pim_ifp = ifc->ifp->info; |
676 | |
|
677 | 0 | if (ifc->address->family != PIM_AF) { |
678 | | /* non-IPv4 address */ |
679 | 0 | return; |
680 | 0 | } |
681 | | |
682 | 0 | if (!pim_ifp) { |
683 | | /* PIM not enabled on interface */ |
684 | 0 | return; |
685 | 0 | } |
686 | | |
687 | 0 | if (!pim_addr_is_any(pim_ifp->primary_address)) { |
688 | | /* Interface keeps a valid primary address */ |
689 | 0 | return; |
690 | 0 | } |
691 | | |
692 | 0 | if (pim_ifp->pim_sock_fd < 0) { |
693 | | /* Interface does not hold a valid socket any longer */ |
694 | 0 | return; |
695 | 0 | } |
696 | | |
697 | | /* |
698 | | pim_sock_delete() closes the socket, stops read and timer threads, |
699 | | and kills all neighbors. |
700 | | */ |
701 | 0 | pim_sock_delete(ifc->ifp, |
702 | 0 | "last address has been removed from interface"); |
703 | 0 | } |
704 | | |
705 | | void pim_if_addr_del(struct connected *ifc, int force_prim_as_any) |
706 | 0 | { |
707 | 0 | struct interface *ifp; |
708 | |
|
709 | 0 | assert(ifc); |
710 | 0 | ifp = ifc->ifp; |
711 | 0 | assert(ifp); |
712 | | |
713 | 0 | if (PIM_DEBUG_ZEBRA) |
714 | 0 | zlog_debug("%s: %s ifindex=%d disconnected IP address %pFX %s", |
715 | 0 | __func__, ifp->name, ifp->ifindex, ifc->address, |
716 | 0 | CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY) |
717 | 0 | ? "secondary" |
718 | 0 | : "primary"); |
719 | |
|
720 | | #if PIM_IPV == 6 |
721 | | struct pim_interface *pim_ifp = ifc->ifp->info; |
722 | | |
723 | | if (pim_ifp && |
724 | | (!IPV6_ADDR_CMP(&ifc->address->u.prefix6, &pim_ifp->ll_lowest) || |
725 | | !IPV6_ADDR_CMP(&ifc->address->u.prefix6, &pim_ifp->ll_highest))) { |
726 | | struct listnode *cnode; |
727 | | struct connected *cc; |
728 | | |
729 | | memset(&pim_ifp->ll_lowest, 0xff, sizeof(pim_ifp->ll_lowest)); |
730 | | memset(&pim_ifp->ll_highest, 0, sizeof(pim_ifp->ll_highest)); |
731 | | |
732 | | for (ALL_LIST_ELEMENTS_RO(ifc->ifp->connected, cnode, cc)) { |
733 | | if (!IN6_IS_ADDR_LINKLOCAL(&cc->address->u.prefix6) && |
734 | | !IN6_IS_ADDR_LOOPBACK(&cc->address->u.prefix6)) |
735 | | continue; |
736 | | |
737 | | if (IPV6_ADDR_CMP(&cc->address->u.prefix6, |
738 | | &pim_ifp->ll_lowest) < 0) |
739 | | pim_ifp->ll_lowest = cc->address->u.prefix6; |
740 | | if (IPV6_ADDR_CMP(&cc->address->u.prefix6, |
741 | | &pim_ifp->ll_highest) > 0) |
742 | | pim_ifp->ll_highest = cc->address->u.prefix6; |
743 | | } |
744 | | |
745 | | if (pim_ifp->ll_lowest.s6_addr[0] == 0xff) |
746 | | memset(&pim_ifp->ll_lowest, 0, |
747 | | sizeof(pim_ifp->ll_lowest)); |
748 | | |
749 | | if (PIM_DEBUG_ZEBRA) |
750 | | zlog_debug( |
751 | | "%s: removed link-local %pI6, lowest now %pI6, highest %pI6", |
752 | | ifc->ifp->name, &ifc->address->u.prefix6, |
753 | | &pim_ifp->ll_lowest, &pim_ifp->ll_highest); |
754 | | |
755 | | gm_ifp_update(ifp); |
756 | | } |
757 | | #endif |
758 | |
|
759 | 0 | detect_address_change(ifp, force_prim_as_any, __func__); |
760 | |
|
761 | 0 | pim_if_addr_del_igmp(ifc); |
762 | 0 | pim_if_addr_del_pim(ifc); |
763 | 0 | } |
764 | | |
765 | | void pim_if_addr_add_all(struct interface *ifp) |
766 | 0 | { |
767 | 0 | struct connected *ifc; |
768 | 0 | struct listnode *node; |
769 | 0 | struct listnode *nextnode; |
770 | 0 | int v4_addrs = 0; |
771 | 0 | int v6_addrs = 0; |
772 | 0 | struct pim_interface *pim_ifp = ifp->info; |
773 | 0 | bool vxlan_term; |
774 | | |
775 | | |
776 | | /* PIM/IGMP enabled ? */ |
777 | 0 | if (!pim_ifp) |
778 | 0 | return; |
779 | | |
780 | 0 | for (ALL_LIST_ELEMENTS(ifp->connected, node, nextnode, ifc)) { |
781 | 0 | struct prefix *p = ifc->address; |
782 | |
|
783 | 0 | if (p->family != AF_INET) |
784 | 0 | v6_addrs++; |
785 | 0 | else |
786 | 0 | v4_addrs++; |
787 | 0 | pim_if_addr_add(ifc); |
788 | 0 | } |
789 | | |
790 | 0 | if (!v4_addrs && v6_addrs && !if_is_loopback(ifp) && |
791 | 0 | pim_ifp->pim_enable && !pim_addr_is_any(pim_ifp->primary_address) && |
792 | 0 | pim_ifp->pim_sock_fd < 0 && pim_sock_add(ifp)) { |
793 | | /* Interface has a valid primary address ? */ |
794 | | /* Interface has a valid socket ? */ |
795 | 0 | zlog_warn("Failure creating PIM socket for interface %s", |
796 | 0 | ifp->name); |
797 | 0 | } |
798 | | /* |
799 | | * PIM or IGMP/MLD is enabled on interface, and there is at least one |
800 | | * address assigned, then try to create a vif_index. |
801 | | */ |
802 | 0 | if (pim_ifp->mroute_vif_index < 0) { |
803 | 0 | vxlan_term = pim_vxlan_is_term_dev_cfg(pim_ifp->pim, ifp); |
804 | 0 | pim_if_add_vif(ifp, false, vxlan_term); |
805 | 0 | } |
806 | 0 | gm_ifp_update(ifp); |
807 | 0 | pim_ifchannel_scan_forward_start(ifp); |
808 | |
|
809 | 0 | pim_rp_setup(pim_ifp->pim); |
810 | 0 | pim_rp_check_on_if_add(pim_ifp); |
811 | 0 | } |
812 | | |
813 | | void pim_if_addr_del_all(struct interface *ifp) |
814 | 0 | { |
815 | 0 | struct connected *ifc; |
816 | 0 | struct listnode *node; |
817 | 0 | struct listnode *nextnode; |
818 | 0 | struct pim_instance *pim; |
819 | |
|
820 | 0 | pim = ifp->vrf->info; |
821 | 0 | if (!pim) |
822 | 0 | return; |
823 | | |
824 | | /* PIM/IGMP enabled ? */ |
825 | 0 | if (!ifp->info) |
826 | 0 | return; |
827 | | |
828 | 0 | for (ALL_LIST_ELEMENTS(ifp->connected, node, nextnode, ifc)) { |
829 | 0 | struct prefix *p = ifc->address; |
830 | |
|
831 | 0 | if (p->family != PIM_AF) |
832 | 0 | continue; |
833 | | |
834 | 0 | pim_if_addr_del(ifc, 1 /* force_prim_as_any=true */); |
835 | 0 | } |
836 | | |
837 | 0 | pim_rp_setup(pim); |
838 | 0 | pim_i_am_rp_re_evaluate(pim); |
839 | 0 | } |
840 | | |
841 | | void pim_if_addr_del_all_igmp(struct interface *ifp) |
842 | 0 | { |
843 | 0 | struct connected *ifc; |
844 | 0 | struct listnode *node; |
845 | 0 | struct listnode *nextnode; |
846 | | |
847 | | /* PIM/IGMP enabled ? */ |
848 | 0 | if (!ifp->info) |
849 | 0 | return; |
850 | | |
851 | 0 | for (ALL_LIST_ELEMENTS(ifp->connected, node, nextnode, ifc)) { |
852 | 0 | struct prefix *p = ifc->address; |
853 | |
|
854 | 0 | if (p->family != AF_INET) |
855 | 0 | continue; |
856 | | |
857 | 0 | pim_if_addr_del_igmp(ifc); |
858 | 0 | } |
859 | 0 | } |
860 | | |
861 | | pim_addr pim_find_primary_addr(struct interface *ifp) |
862 | 2 | { |
863 | 2 | struct connected *ifc; |
864 | 2 | struct listnode *node; |
865 | 2 | struct pim_interface *pim_ifp = ifp->info; |
866 | | |
867 | 2 | if (pim_ifp && !pim_addr_is_any(pim_ifp->update_source)) |
868 | 0 | return pim_ifp->update_source; |
869 | | |
870 | | #if PIM_IPV == 6 |
871 | | if (pim_ifp && !pim_addr_is_any(pim_ifp->ll_highest)) |
872 | | return pim_ifp->ll_highest; |
873 | | |
874 | | pim_addr best_addr = PIMADDR_ANY; |
875 | | |
876 | | for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { |
877 | | pim_addr addr; |
878 | | |
879 | | if (ifc->address->family != AF_INET6) |
880 | | continue; |
881 | | |
882 | | addr = pim_addr_from_prefix(ifc->address); |
883 | | if (!IN6_IS_ADDR_LINKLOCAL(&addr)) |
884 | | continue; |
885 | | if (pim_addr_cmp(addr, best_addr) > 0) |
886 | | best_addr = addr; |
887 | | } |
888 | | |
889 | | return best_addr; |
890 | | #else |
891 | 2 | int v4_addrs = 0; |
892 | 2 | int v6_addrs = 0; |
893 | | |
894 | 2 | for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { |
895 | 1 | switch (ifc->address->family) { |
896 | 1 | case AF_INET: |
897 | 1 | v4_addrs++; |
898 | 1 | break; |
899 | 0 | case AF_INET6: |
900 | 0 | v6_addrs++; |
901 | 0 | break; |
902 | 0 | default: |
903 | 0 | continue; |
904 | 1 | } |
905 | | |
906 | 1 | if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY)) |
907 | 0 | continue; |
908 | | |
909 | 1 | if (ifc->address->family != PIM_AF) |
910 | 0 | continue; |
911 | | |
912 | 1 | return pim_addr_from_prefix(ifc->address); |
913 | 1 | } |
914 | | |
915 | | /* |
916 | | * If we have no v4_addrs and v6 is configured |
917 | | * We probably are using unnumbered |
918 | | * So let's grab the loopbacks v4 address |
919 | | * and use that as the primary address |
920 | | */ |
921 | 1 | if (!v4_addrs && v6_addrs) { |
922 | 0 | struct interface *lo_ifp; |
923 | | |
924 | | // DBS - Come back and check here |
925 | 0 | if (ifp->vrf->vrf_id == VRF_DEFAULT) |
926 | 0 | lo_ifp = if_lookup_by_name("lo", ifp->vrf->vrf_id); |
927 | 0 | else |
928 | 0 | lo_ifp = if_lookup_by_name(ifp->vrf->name, |
929 | 0 | ifp->vrf->vrf_id); |
930 | |
|
931 | 0 | if (lo_ifp && (lo_ifp != ifp)) |
932 | 0 | return pim_find_primary_addr(lo_ifp); |
933 | 0 | } |
934 | 1 | return PIMADDR_ANY; |
935 | 1 | #endif |
936 | 1 | } |
937 | | |
938 | | static int pim_iface_next_vif_index(struct interface *ifp) |
939 | 2 | { |
940 | 2 | struct pim_interface *pim_ifp = ifp->info; |
941 | 2 | struct pim_instance *pim = pim_ifp->pim; |
942 | 2 | int i; |
943 | | |
944 | | /* |
945 | | * The pimreg vif is always going to be in index 0 |
946 | | * of the table. |
947 | | */ |
948 | 2 | if (ifp->ifindex == PIM_OIF_PIM_REGISTER_VIF) |
949 | 1 | return 0; |
950 | | |
951 | 1 | for (i = 1; i < MAXVIFS; i++) { |
952 | 1 | if (pim->iface_vif_index[i] == 0) |
953 | 1 | return i; |
954 | 1 | } |
955 | 0 | return MAXVIFS; |
956 | 1 | } |
957 | | |
958 | | /* |
959 | | pim_if_add_vif() uses ifindex as vif_index |
960 | | |
961 | | see also pim_if_find_vifindex_by_ifindex() |
962 | | */ |
963 | | int pim_if_add_vif(struct interface *ifp, bool ispimreg, bool is_vxlan_term) |
964 | 2 | { |
965 | 2 | struct pim_interface *pim_ifp = ifp->info; |
966 | 2 | pim_addr ifaddr; |
967 | 2 | unsigned char flags = 0; |
968 | | |
969 | 2 | assert(pim_ifp); |
970 | | |
971 | 2 | if (pim_ifp->mroute_vif_index > 0) { |
972 | 0 | zlog_warn("%s: vif_index=%d > 0 on interface %s ifindex=%d", |
973 | 0 | __func__, pim_ifp->mroute_vif_index, ifp->name, |
974 | 0 | ifp->ifindex); |
975 | 0 | return -1; |
976 | 0 | } |
977 | | |
978 | 2 | if (ifp->ifindex < 0) { |
979 | 0 | zlog_warn("%s: ifindex=%d < 0 on interface %s", __func__, |
980 | 0 | ifp->ifindex, ifp->name); |
981 | 0 | return -2; |
982 | 2 | } else if ((ifp->ifindex == PIM_OIF_PIM_REGISTER_VIF) && |
983 | 2 | ((strncmp(ifp->name, "pimreg", 6)) && |
984 | 1 | (strncmp(ifp->name, "pim6reg", 7)))) { |
985 | 0 | zlog_warn("%s: ifindex=%d on interface %s", __func__, |
986 | 0 | ifp->ifindex, ifp->name); |
987 | 0 | return -2; |
988 | 0 | } |
989 | | |
990 | 2 | ifaddr = pim_ifp->primary_address; |
991 | 2 | #if PIM_IPV != 6 |
992 | | /* IPv6 API is always by interface index */ |
993 | 2 | if (!ispimreg && !is_vxlan_term && pim_addr_is_any(ifaddr)) { |
994 | 0 | zlog_warn( |
995 | 0 | "%s: could not get address for interface %s ifindex=%d", |
996 | 0 | __func__, ifp->name, ifp->ifindex); |
997 | 0 | return -4; |
998 | 0 | } |
999 | 2 | #endif |
1000 | | |
1001 | 2 | pim_ifp->mroute_vif_index = pim_iface_next_vif_index(ifp); |
1002 | | |
1003 | 2 | if (pim_ifp->mroute_vif_index >= MAXVIFS) { |
1004 | 0 | zlog_warn( |
1005 | 0 | "%s: Attempting to configure more than MAXVIFS=%d on pim enabled interface %s", |
1006 | 0 | __func__, MAXVIFS, ifp->name); |
1007 | 0 | return -3; |
1008 | 0 | } |
1009 | | |
1010 | 2 | if (ifp->ifindex == PIM_OIF_PIM_REGISTER_VIF) |
1011 | 1 | flags = VIFF_REGISTER; |
1012 | 1 | #ifdef VIFF_USE_IFINDEX |
1013 | 1 | else |
1014 | 1 | flags = VIFF_USE_IFINDEX; |
1015 | 2 | #endif |
1016 | | |
1017 | 2 | if (pim_mroute_add_vif(ifp, ifaddr, flags)) { |
1018 | | /* pim_mroute_add_vif reported error */ |
1019 | 0 | return -5; |
1020 | 0 | } |
1021 | | |
1022 | 2 | pim_ifp->pim->iface_vif_index[pim_ifp->mroute_vif_index] = 1; |
1023 | | |
1024 | 2 | if (!ispimreg) |
1025 | 1 | gm_ifp_update(ifp); |
1026 | | |
1027 | | /* if the device qualifies as pim_vxlan iif/oif update vxlan entries */ |
1028 | 2 | pim_vxlan_add_vif(ifp); |
1029 | 2 | return 0; |
1030 | 2 | } |
1031 | | |
1032 | | int pim_if_del_vif(struct interface *ifp) |
1033 | 0 | { |
1034 | 0 | struct pim_interface *pim_ifp = ifp->info; |
1035 | |
|
1036 | 0 | if (pim_ifp->mroute_vif_index < 1) { |
1037 | 0 | zlog_warn("%s: vif_index=%d < 1 on interface %s ifindex=%d", |
1038 | 0 | __func__, pim_ifp->mroute_vif_index, ifp->name, |
1039 | 0 | ifp->ifindex); |
1040 | 0 | return -1; |
1041 | 0 | } |
1042 | | |
1043 | | /* if the device was a pim_vxlan iif/oif update vxlan mroute entries */ |
1044 | 0 | pim_vxlan_del_vif(ifp); |
1045 | |
|
1046 | 0 | gm_ifp_teardown(ifp); |
1047 | |
|
1048 | 0 | pim_mroute_del_vif(ifp); |
1049 | | |
1050 | | /* |
1051 | | Update vif_index |
1052 | | */ |
1053 | 0 | pim_ifp->pim->iface_vif_index[pim_ifp->mroute_vif_index] = 0; |
1054 | |
|
1055 | 0 | pim_ifp->mroute_vif_index = -1; |
1056 | 0 | return 0; |
1057 | 0 | } |
1058 | | |
1059 | | // DBS - VRF Revist |
1060 | | struct interface *pim_if_find_by_vif_index(struct pim_instance *pim, |
1061 | | ifindex_t vif_index) |
1062 | 0 | { |
1063 | 0 | struct interface *ifp; |
1064 | |
|
1065 | 0 | FOR_ALL_INTERFACES (pim->vrf, ifp) { |
1066 | 0 | if (ifp->info) { |
1067 | 0 | struct pim_interface *pim_ifp; |
1068 | 0 | pim_ifp = ifp->info; |
1069 | |
|
1070 | 0 | if (vif_index == pim_ifp->mroute_vif_index) |
1071 | 0 | return ifp; |
1072 | 0 | } |
1073 | 0 | } |
1074 | | |
1075 | 0 | return 0; |
1076 | 0 | } |
1077 | | |
1078 | | /* |
1079 | | pim_if_add_vif() uses ifindex as vif_index |
1080 | | */ |
1081 | | int pim_if_find_vifindex_by_ifindex(struct pim_instance *pim, ifindex_t ifindex) |
1082 | 0 | { |
1083 | 0 | struct pim_interface *pim_ifp; |
1084 | 0 | struct interface *ifp; |
1085 | |
|
1086 | 0 | ifp = if_lookup_by_index(ifindex, pim->vrf->vrf_id); |
1087 | 0 | if (!ifp || !ifp->info) |
1088 | 0 | return -1; |
1089 | 0 | pim_ifp = ifp->info; |
1090 | |
|
1091 | 0 | return pim_ifp->mroute_vif_index; |
1092 | 0 | } |
1093 | | |
1094 | | int pim_if_lan_delay_enabled(struct interface *ifp) |
1095 | 116k | { |
1096 | 116k | struct pim_interface *pim_ifp; |
1097 | | |
1098 | 116k | pim_ifp = ifp->info; |
1099 | 116k | assert(pim_ifp); |
1100 | 116k | assert(pim_ifp->pim_number_of_nonlandelay_neighbors >= 0); |
1101 | | |
1102 | 116k | return pim_ifp->pim_number_of_nonlandelay_neighbors == 0; |
1103 | 116k | } |
1104 | | |
1105 | | uint16_t pim_if_effective_propagation_delay_msec(struct interface *ifp) |
1106 | 57.9k | { |
1107 | 57.9k | if (pim_if_lan_delay_enabled(ifp)) { |
1108 | 0 | struct pim_interface *pim_ifp; |
1109 | 0 | pim_ifp = ifp->info; |
1110 | 0 | return pim_ifp->pim_neighbors_highest_propagation_delay_msec; |
1111 | 57.9k | } else { |
1112 | 57.9k | return PIM_DEFAULT_PROPAGATION_DELAY_MSEC; |
1113 | 57.9k | } |
1114 | 57.9k | } |
1115 | | |
1116 | | uint16_t pim_if_effective_override_interval_msec(struct interface *ifp) |
1117 | 57.9k | { |
1118 | 57.9k | if (pim_if_lan_delay_enabled(ifp)) { |
1119 | 0 | struct pim_interface *pim_ifp; |
1120 | 0 | pim_ifp = ifp->info; |
1121 | 0 | return pim_ifp->pim_neighbors_highest_override_interval_msec; |
1122 | 57.9k | } else { |
1123 | 57.9k | return PIM_DEFAULT_OVERRIDE_INTERVAL_MSEC; |
1124 | 57.9k | } |
1125 | 57.9k | } |
1126 | | |
1127 | | int pim_if_t_override_msec(struct interface *ifp) |
1128 | 0 | { |
1129 | 0 | int effective_override_interval_msec; |
1130 | 0 | int t_override_msec; |
1131 | |
|
1132 | 0 | effective_override_interval_msec = |
1133 | 0 | pim_if_effective_override_interval_msec(ifp); |
1134 | |
|
1135 | 0 | t_override_msec = |
1136 | 0 | frr_weak_random() % (effective_override_interval_msec + 1); |
1137 | |
|
1138 | 0 | return t_override_msec; |
1139 | 0 | } |
1140 | | |
1141 | | uint16_t pim_if_jp_override_interval_msec(struct interface *ifp) |
1142 | 57.9k | { |
1143 | 57.9k | return pim_if_effective_propagation_delay_msec(ifp) |
1144 | 57.9k | + pim_if_effective_override_interval_msec(ifp); |
1145 | 57.9k | } |
1146 | | |
1147 | | /* |
1148 | | RFC 4601: 4.1.6. State Summarization Macros |
1149 | | |
1150 | | The function NBR( I, A ) uses information gathered through PIM Hello |
1151 | | messages to map the IP address A of a directly connected PIM |
1152 | | neighbor router on interface I to the primary IP address of the same |
1153 | | router (Section 4.3.4). The primary IP address of a neighbor is the |
1154 | | address that it uses as the source of its PIM Hello messages. |
1155 | | */ |
1156 | | struct pim_neighbor *pim_if_find_neighbor(struct interface *ifp, pim_addr addr) |
1157 | 0 | { |
1158 | 0 | struct listnode *neighnode; |
1159 | 0 | struct pim_neighbor *neigh; |
1160 | 0 | struct pim_interface *pim_ifp; |
1161 | 0 | struct prefix p; |
1162 | |
|
1163 | 0 | assert(ifp); |
1164 | | |
1165 | 0 | pim_ifp = ifp->info; |
1166 | 0 | if (!pim_ifp) { |
1167 | 0 | zlog_warn("%s: multicast not enabled on interface %s", __func__, |
1168 | 0 | ifp->name); |
1169 | 0 | return 0; |
1170 | 0 | } |
1171 | | |
1172 | 0 | pim_addr_to_prefix(&p, addr); |
1173 | |
|
1174 | 0 | for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, |
1175 | 0 | neigh)) { |
1176 | | |
1177 | | /* primary address ? */ |
1178 | 0 | if (!pim_addr_cmp(neigh->source_addr, addr)) |
1179 | 0 | return neigh; |
1180 | | |
1181 | | /* secondary address ? */ |
1182 | 0 | if (pim_neighbor_find_secondary(neigh, &p)) |
1183 | 0 | return neigh; |
1184 | 0 | } |
1185 | | |
1186 | 0 | if (PIM_DEBUG_PIM_TRACE) |
1187 | 0 | zlog_debug( |
1188 | 0 | "%s: neighbor not found for address %pPA on interface %s", |
1189 | 0 | __func__, &addr, ifp->name); |
1190 | |
|
1191 | 0 | return NULL; |
1192 | 0 | } |
1193 | | |
1194 | | long pim_if_t_suppressed_msec(struct interface *ifp) |
1195 | 0 | { |
1196 | 0 | struct pim_interface *pim_ifp; |
1197 | 0 | long t_suppressed_msec; |
1198 | 0 | uint32_t ramount = 0; |
1199 | |
|
1200 | 0 | pim_ifp = ifp->info; |
1201 | 0 | assert(pim_ifp); |
1202 | | |
1203 | | /* join suppression disabled ? */ |
1204 | 0 | if (pim_ifp->pim_can_disable_join_suppression) |
1205 | 0 | return 0; |
1206 | | |
1207 | | /* t_suppressed = t_periodic * rand(1.1, 1.4) */ |
1208 | 0 | ramount = 1100 + (frr_weak_random() % (1400 - 1100 + 1)); |
1209 | 0 | t_suppressed_msec = router->t_periodic * ramount; |
1210 | |
|
1211 | 0 | return t_suppressed_msec; |
1212 | 0 | } |
1213 | | |
1214 | | static void gm_join_free(struct gm_join *ij) |
1215 | 0 | { |
1216 | 0 | XFREE(MTYPE_PIM_IGMP_JOIN, ij); |
1217 | 0 | } |
1218 | | |
1219 | | static struct gm_join *gm_join_find(struct list *join_list, pim_addr group_addr, |
1220 | | pim_addr source_addr) |
1221 | 0 | { |
1222 | 0 | struct listnode *node; |
1223 | 0 | struct gm_join *ij; |
1224 | |
|
1225 | 0 | assert(join_list); |
1226 | | |
1227 | 0 | for (ALL_LIST_ELEMENTS_RO(join_list, node, ij)) { |
1228 | 0 | if ((!pim_addr_cmp(group_addr, ij->group_addr)) && |
1229 | 0 | (!pim_addr_cmp(source_addr, ij->source_addr))) |
1230 | 0 | return ij; |
1231 | 0 | } |
1232 | | |
1233 | 0 | return 0; |
1234 | 0 | } |
1235 | | |
1236 | | static int gm_join_sock(const char *ifname, ifindex_t ifindex, |
1237 | | pim_addr group_addr, pim_addr source_addr, |
1238 | | struct pim_interface *pim_ifp) |
1239 | 0 | { |
1240 | 0 | int join_fd; |
1241 | |
|
1242 | 0 | pim_ifp->igmp_ifstat_joins_sent++; |
1243 | |
|
1244 | 0 | join_fd = pim_socket_raw(IPPROTO_GM); |
1245 | 0 | if (join_fd < 0) { |
1246 | 0 | pim_ifp->igmp_ifstat_joins_failed++; |
1247 | 0 | return -1; |
1248 | 0 | } |
1249 | | |
1250 | 0 | if (pim_gm_join_source(join_fd, ifindex, group_addr, source_addr)) { |
1251 | 0 | zlog_warn( |
1252 | 0 | "%s: setsockopt(fd=%d) failure for " GM |
1253 | 0 | " group %pPAs source %pPAs ifindex %d on interface %s: errno=%d: %s", |
1254 | 0 | __func__, join_fd, &group_addr, &source_addr, ifindex, |
1255 | 0 | ifname, errno, safe_strerror(errno)); |
1256 | |
|
1257 | 0 | pim_ifp->igmp_ifstat_joins_failed++; |
1258 | |
|
1259 | 0 | close(join_fd); |
1260 | 0 | return -2; |
1261 | 0 | } |
1262 | | |
1263 | 0 | return join_fd; |
1264 | 0 | } |
1265 | | |
1266 | | static struct gm_join *gm_join_new(struct interface *ifp, pim_addr group_addr, |
1267 | | pim_addr source_addr) |
1268 | 0 | { |
1269 | 0 | struct pim_interface *pim_ifp; |
1270 | 0 | struct gm_join *ij; |
1271 | 0 | int join_fd; |
1272 | |
|
1273 | 0 | pim_ifp = ifp->info; |
1274 | 0 | assert(pim_ifp); |
1275 | | |
1276 | 0 | join_fd = gm_join_sock(ifp->name, ifp->ifindex, group_addr, source_addr, |
1277 | 0 | pim_ifp); |
1278 | 0 | if (join_fd < 0) { |
1279 | 0 | zlog_warn("%s: gm_join_sock() failure for " GM |
1280 | 0 | " group %pPAs source %pPAs on interface %s", |
1281 | 0 | __func__, &group_addr, &source_addr, ifp->name); |
1282 | 0 | return 0; |
1283 | 0 | } |
1284 | | |
1285 | 0 | ij = XCALLOC(MTYPE_PIM_IGMP_JOIN, sizeof(*ij)); |
1286 | |
|
1287 | 0 | ij->sock_fd = join_fd; |
1288 | 0 | ij->group_addr = group_addr; |
1289 | 0 | ij->source_addr = source_addr; |
1290 | 0 | ij->sock_creation = pim_time_monotonic_sec(); |
1291 | |
|
1292 | 0 | listnode_add(pim_ifp->gm_join_list, ij); |
1293 | |
|
1294 | 0 | return ij; |
1295 | 0 | } |
1296 | | |
1297 | | ferr_r pim_if_gm_join_add(struct interface *ifp, pim_addr group_addr, |
1298 | | pim_addr source_addr) |
1299 | 0 | { |
1300 | 0 | struct pim_interface *pim_ifp; |
1301 | 0 | struct gm_join *ij; |
1302 | |
|
1303 | 0 | pim_ifp = ifp->info; |
1304 | 0 | if (!pim_ifp) { |
1305 | 0 | return ferr_cfg_invalid("multicast not enabled on interface %s", |
1306 | 0 | ifp->name); |
1307 | 0 | } |
1308 | | |
1309 | 0 | if (!pim_ifp->gm_join_list) { |
1310 | 0 | pim_ifp->gm_join_list = list_new(); |
1311 | 0 | pim_ifp->gm_join_list->del = (void (*)(void *))gm_join_free; |
1312 | 0 | } |
1313 | |
|
1314 | 0 | ij = gm_join_find(pim_ifp->gm_join_list, group_addr, source_addr); |
1315 | | |
1316 | | /* This interface has already been configured to join this IGMP/MLD |
1317 | | * group |
1318 | | */ |
1319 | 0 | if (ij) { |
1320 | 0 | return ferr_ok(); |
1321 | 0 | } |
1322 | | |
1323 | 0 | (void)gm_join_new(ifp, group_addr, source_addr); |
1324 | |
|
1325 | 0 | if (PIM_DEBUG_GM_EVENTS) { |
1326 | 0 | zlog_debug( |
1327 | 0 | "%s: issued static " GM |
1328 | 0 | " join for channel (S,G)=(%pPA,%pPA) on interface %s", |
1329 | 0 | __func__, &source_addr, &group_addr, ifp->name); |
1330 | 0 | } |
1331 | |
|
1332 | 0 | return ferr_ok(); |
1333 | 0 | } |
1334 | | |
1335 | | int pim_if_gm_join_del(struct interface *ifp, pim_addr group_addr, |
1336 | | pim_addr source_addr) |
1337 | 0 | { |
1338 | 0 | struct pim_interface *pim_ifp; |
1339 | 0 | struct gm_join *ij; |
1340 | |
|
1341 | 0 | pim_ifp = ifp->info; |
1342 | 0 | if (!pim_ifp) { |
1343 | 0 | zlog_warn("%s: multicast not enabled on interface %s", __func__, |
1344 | 0 | ifp->name); |
1345 | 0 | return -1; |
1346 | 0 | } |
1347 | | |
1348 | 0 | if (!pim_ifp->gm_join_list) { |
1349 | 0 | zlog_warn("%s: no " GM " join on interface %s", __func__, |
1350 | 0 | ifp->name); |
1351 | 0 | return -2; |
1352 | 0 | } |
1353 | | |
1354 | 0 | ij = gm_join_find(pim_ifp->gm_join_list, group_addr, source_addr); |
1355 | 0 | if (!ij) { |
1356 | 0 | zlog_warn("%s: could not find " GM |
1357 | 0 | " group %pPAs source %pPAs on interface %s", |
1358 | 0 | __func__, &group_addr, &source_addr, ifp->name); |
1359 | 0 | return -3; |
1360 | 0 | } |
1361 | | |
1362 | 0 | if (close(ij->sock_fd)) { |
1363 | 0 | zlog_warn( |
1364 | 0 | "%s: failure closing sock_fd=%d for " GM |
1365 | 0 | " group %pPAs source %pPAs on interface %s: errno=%d: %s", |
1366 | 0 | __func__, ij->sock_fd, &group_addr, &source_addr, |
1367 | 0 | ifp->name, errno, safe_strerror(errno)); |
1368 | | /* warning only */ |
1369 | 0 | } |
1370 | 0 | listnode_delete(pim_ifp->gm_join_list, ij); |
1371 | 0 | gm_join_free(ij); |
1372 | 0 | if (listcount(pim_ifp->gm_join_list) < 1) { |
1373 | 0 | list_delete(&pim_ifp->gm_join_list); |
1374 | 0 | pim_ifp->gm_join_list = 0; |
1375 | 0 | } |
1376 | |
|
1377 | 0 | return 0; |
1378 | 0 | } |
1379 | | |
1380 | | __attribute__((unused)) |
1381 | | static void pim_if_gm_join_del_all(struct interface *ifp) |
1382 | 0 | { |
1383 | 0 | struct pim_interface *pim_ifp; |
1384 | 0 | struct listnode *node; |
1385 | 0 | struct listnode *nextnode; |
1386 | 0 | struct gm_join *ij; |
1387 | |
|
1388 | 0 | pim_ifp = ifp->info; |
1389 | 0 | if (!pim_ifp) { |
1390 | 0 | zlog_warn("%s: multicast not enabled on interface %s", __func__, |
1391 | 0 | ifp->name); |
1392 | 0 | return; |
1393 | 0 | } |
1394 | | |
1395 | 0 | if (!pim_ifp->gm_join_list) |
1396 | 0 | return; |
1397 | | |
1398 | 0 | for (ALL_LIST_ELEMENTS(pim_ifp->gm_join_list, node, nextnode, ij)) |
1399 | 0 | pim_if_gm_join_del(ifp, ij->group_addr, ij->source_addr); |
1400 | 0 | } |
1401 | | |
1402 | | /* |
1403 | | RFC 4601 |
1404 | | |
1405 | | Transitions from "I am Assert Loser" State |
1406 | | |
1407 | | Current Winner's GenID Changes or NLT Expires |
1408 | | |
1409 | | The Neighbor Liveness Timer associated with the current winner |
1410 | | expires or we receive a Hello message from the current winner |
1411 | | reporting a different GenID from the one it previously reported. |
1412 | | This indicates that the current winner's interface or router has |
1413 | | gone down (and may have come back up), and so we must assume it no |
1414 | | longer knows it was the winner. |
1415 | | */ |
1416 | | void pim_if_assert_on_neighbor_down(struct interface *ifp, pim_addr neigh_addr) |
1417 | 95 | { |
1418 | 95 | struct pim_interface *pim_ifp; |
1419 | 95 | struct pim_ifchannel *ch; |
1420 | | |
1421 | 95 | pim_ifp = ifp->info; |
1422 | 95 | assert(pim_ifp); |
1423 | | |
1424 | 13.7k | RB_FOREACH (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb) { |
1425 | | /* Is (S,G,I) assert loser ? */ |
1426 | 13.7k | if (ch->ifassert_state != PIM_IFASSERT_I_AM_LOSER) |
1427 | 13.7k | continue; |
1428 | | /* Dead neighbor was winner ? */ |
1429 | 11 | if (pim_addr_cmp(ch->ifassert_winner, neigh_addr)) |
1430 | 11 | continue; |
1431 | | |
1432 | 0 | assert_action_a5(ch); |
1433 | 0 | } |
1434 | 95 | } |
1435 | | |
1436 | | void pim_if_update_join_desired(struct pim_interface *pim_ifp) |
1437 | 3 | { |
1438 | 3 | struct pim_ifchannel *ch; |
1439 | | |
1440 | | /* clear off flag from interface's upstreams */ |
1441 | 3 | RB_FOREACH (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb) { |
1442 | 0 | PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED_UPDATED( |
1443 | 0 | ch->upstream->flags); |
1444 | 0 | } |
1445 | | |
1446 | | /* scan per-interface (S,G,I) state on this I interface */ |
1447 | 3 | RB_FOREACH (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb) { |
1448 | 0 | struct pim_upstream *up = ch->upstream; |
1449 | |
|
1450 | 0 | if (PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED_UPDATED(up->flags)) |
1451 | 0 | continue; |
1452 | | |
1453 | | /* update join_desired for the global (S,G) state */ |
1454 | 0 | pim_upstream_update_join_desired(pim_ifp->pim, up); |
1455 | 0 | PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED_UPDATED(up->flags); |
1456 | 0 | } |
1457 | 3 | } |
1458 | | |
1459 | | void pim_if_update_assert_tracking_desired(struct interface *ifp) |
1460 | 3 | { |
1461 | 3 | struct pim_interface *pim_ifp; |
1462 | 3 | struct pim_ifchannel *ch; |
1463 | | |
1464 | 3 | pim_ifp = ifp->info; |
1465 | 3 | if (!pim_ifp) |
1466 | 0 | return; |
1467 | | |
1468 | 3 | RB_FOREACH (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb) { |
1469 | 0 | pim_ifchannel_update_assert_tracking_desired(ch); |
1470 | 0 | } |
1471 | 3 | } |
1472 | | |
1473 | | /* |
1474 | | * PIM wants to have an interface pointer for everything it does. |
1475 | | * The pimreg is a special interface that we have that is not |
1476 | | * quite an interface but a VIF is created for it. |
1477 | | */ |
1478 | | void pim_if_create_pimreg(struct pim_instance *pim) |
1479 | 1 | { |
1480 | 1 | char pimreg_name[INTERFACE_NAMSIZ]; |
1481 | | |
1482 | 1 | if (!pim->regiface) { |
1483 | 1 | if (pim->vrf->vrf_id == VRF_DEFAULT) |
1484 | 1 | strlcpy(pimreg_name, PIMREG, sizeof(pimreg_name)); |
1485 | 0 | else |
1486 | 0 | snprintf(pimreg_name, sizeof(pimreg_name), PIMREG "%u", |
1487 | 0 | pim->vrf->data.l.table_id); |
1488 | | |
1489 | 1 | pim->regiface = if_get_by_name(pimreg_name, pim->vrf->vrf_id, |
1490 | 1 | pim->vrf->name); |
1491 | 1 | pim->regiface->ifindex = PIM_OIF_PIM_REGISTER_VIF; |
1492 | | |
1493 | 1 | if (!pim->regiface->info) |
1494 | 1 | pim_if_new(pim->regiface, false, false, true, |
1495 | 1 | false /*vxlan_term*/); |
1496 | | |
1497 | | /* |
1498 | | * On vrf moves we delete the interface if there |
1499 | | * is nothing going on with it. We cannot have |
1500 | | * the pimregiface deleted. |
1501 | | */ |
1502 | 1 | pim->regiface->configured = true; |
1503 | | |
1504 | 1 | } |
1505 | 1 | } |
1506 | | |
1507 | | struct prefix *pim_if_connected_to_source(struct interface *ifp, pim_addr src) |
1508 | 19.3M | { |
1509 | 19.3M | struct listnode *cnode; |
1510 | 19.3M | struct connected *c; |
1511 | 19.3M | struct prefix p; |
1512 | | |
1513 | 19.3M | if (!ifp) |
1514 | 19.3M | return NULL; |
1515 | | |
1516 | 0 | pim_addr_to_prefix(&p, src); |
1517 | |
|
1518 | 0 | for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, c)) { |
1519 | 0 | if (c->address->family != PIM_AF) |
1520 | 0 | continue; |
1521 | 0 | if (prefix_match(c->address, &p)) |
1522 | 0 | return c->address; |
1523 | 0 | if (CONNECTED_PEER(c) && prefix_match(c->destination, &p)) |
1524 | | /* this is not a typo, on PtP we need to return the |
1525 | | * *local* address that lines up with src. |
1526 | | */ |
1527 | 0 | return c->address; |
1528 | 0 | } |
1529 | | |
1530 | 0 | return NULL; |
1531 | 0 | } |
1532 | | |
1533 | | bool pim_if_is_vrf_device(struct interface *ifp) |
1534 | 0 | { |
1535 | 0 | if (if_is_vrf(ifp)) |
1536 | 0 | return true; |
1537 | | |
1538 | 0 | return false; |
1539 | 0 | } |
1540 | | |
1541 | | int pim_if_ifchannel_count(struct pim_interface *pim_ifp) |
1542 | 0 | { |
1543 | 0 | struct pim_ifchannel *ch; |
1544 | 0 | int count = 0; |
1545 | |
|
1546 | 0 | RB_FOREACH (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb) { |
1547 | 0 | count++; |
1548 | 0 | } |
1549 | |
|
1550 | 0 | return count; |
1551 | 0 | } |
1552 | | #ifndef FUZZING |
1553 | | static |
1554 | | #endif |
1555 | | int pim_ifp_create(struct interface *ifp) |
1556 | 0 | { |
1557 | 0 | struct pim_instance *pim; |
1558 | |
|
1559 | 0 | pim = ifp->vrf->info; |
1560 | 0 | if (PIM_DEBUG_ZEBRA) { |
1561 | 0 | zlog_debug( |
1562 | 0 | "%s: %s index %d vrf %s(%u) flags %ld metric %d mtu %d operative %d", |
1563 | 0 | __func__, ifp->name, ifp->ifindex, ifp->vrf->name, |
1564 | 0 | ifp->vrf->vrf_id, (long)ifp->flags, ifp->metric, |
1565 | 0 | ifp->mtu, if_is_operative(ifp)); |
1566 | 0 | } |
1567 | |
|
1568 | 0 | if (if_is_operative(ifp)) { |
1569 | 0 | struct pim_interface *pim_ifp; |
1570 | |
|
1571 | 0 | pim_ifp = ifp->info; |
1572 | | /* |
1573 | | * If we have a pim_ifp already and this is an if_add |
1574 | | * that means that we probably have a vrf move event |
1575 | | * If that is the case, set the proper vrfness. |
1576 | | */ |
1577 | 0 | if (pim_ifp) |
1578 | 0 | pim_ifp->pim = pim; |
1579 | 0 | pim_if_addr_add_all(ifp); |
1580 | | |
1581 | | /* |
1582 | | * Due to ordering issues based upon when |
1583 | | * a command is entered we should ensure that |
1584 | | * the pim reg is created for this vrf if we |
1585 | | * have configuration for it already. |
1586 | | * |
1587 | | * this is a no-op if it's already been done. |
1588 | | */ |
1589 | 0 | pim_if_create_pimreg(pim); |
1590 | 0 | } |
1591 | |
|
1592 | 0 | #if PIM_IPV == 4 |
1593 | | /* |
1594 | | * If we are a vrf device that is up, open up the pim_socket for |
1595 | | * listening |
1596 | | * to incoming pim messages irrelevant if the user has configured us |
1597 | | * for pim or not. |
1598 | | */ |
1599 | 0 | if (pim_if_is_vrf_device(ifp)) { |
1600 | 0 | struct pim_interface *pim_ifp; |
1601 | |
|
1602 | 0 | if (!ifp->info) { |
1603 | 0 | pim_ifp = pim_if_new(ifp, false, false, false, |
1604 | 0 | false /*vxlan_term*/); |
1605 | 0 | ifp->info = pim_ifp; |
1606 | 0 | } |
1607 | |
|
1608 | 0 | pim_sock_add(ifp); |
1609 | 0 | } |
1610 | |
|
1611 | 0 | if (!strncmp(ifp->name, PIM_VXLAN_TERM_DEV_NAME, |
1612 | 0 | sizeof(PIM_VXLAN_TERM_DEV_NAME))) { |
1613 | 0 | if (pim->mcast_if_count < MAXVIFS) |
1614 | 0 | pim_vxlan_add_term_dev(pim, ifp); |
1615 | 0 | else |
1616 | 0 | zlog_warn( |
1617 | 0 | "%s: Cannot enable pim on %s. MAXVIFS(%d) reached. Deleting and readding the vxlan termimation device after unconfiguring pim from other interfaces may succeed.", |
1618 | 0 | __func__, ifp->name, MAXVIFS); |
1619 | 0 | } |
1620 | 0 | #endif |
1621 | |
|
1622 | 0 | return 0; |
1623 | 0 | } |
1624 | | |
1625 | | #ifndef FUZZING |
1626 | | static |
1627 | | #endif |
1628 | | int pim_ifp_up(struct interface *ifp) |
1629 | 0 | { |
1630 | 0 | uint32_t table_id; |
1631 | 0 | struct pim_interface *pim_ifp; |
1632 | 0 | struct pim_instance *pim; |
1633 | |
|
1634 | 0 | if (PIM_DEBUG_ZEBRA) { |
1635 | 0 | zlog_debug( |
1636 | 0 | "%s: %s index %d vrf %s(%u) flags %ld metric %d mtu %d operative %d", |
1637 | 0 | __func__, ifp->name, ifp->ifindex, ifp->vrf->name, |
1638 | 0 | ifp->vrf->vrf_id, (long)ifp->flags, ifp->metric, |
1639 | 0 | ifp->mtu, if_is_operative(ifp)); |
1640 | 0 | } |
1641 | |
|
1642 | 0 | pim = ifp->vrf->info; |
1643 | |
|
1644 | 0 | pim_ifp = ifp->info; |
1645 | | /* |
1646 | | * If we have a pim_ifp already and this is an if_add |
1647 | | * that means that we probably have a vrf move event |
1648 | | * If that is the case, set the proper vrfness. |
1649 | | */ |
1650 | 0 | if (pim_ifp) |
1651 | 0 | pim_ifp->pim = pim; |
1652 | | |
1653 | | /* |
1654 | | pim_if_addr_add_all() suffices for bringing up both IGMP and |
1655 | | PIM |
1656 | | */ |
1657 | 0 | pim_if_addr_add_all(ifp); |
1658 | | |
1659 | | /* |
1660 | | * If we have a pimreg device callback and it's for a specific |
1661 | | * table set the master appropriately |
1662 | | */ |
1663 | 0 | if (sscanf(ifp->name, "" PIMREG "%" SCNu32, &table_id) == 1) { |
1664 | 0 | struct vrf *vrf; |
1665 | 0 | RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { |
1666 | 0 | if ((table_id == vrf->data.l.table_id) |
1667 | 0 | && (ifp->vrf->vrf_id != vrf->vrf_id)) { |
1668 | 0 | struct interface *master = if_lookup_by_name( |
1669 | 0 | vrf->name, vrf->vrf_id); |
1670 | |
|
1671 | 0 | if (!master) { |
1672 | 0 | zlog_debug( |
1673 | 0 | "%s: Unable to find Master interface for %s", |
1674 | 0 | __func__, vrf->name); |
1675 | 0 | return 0; |
1676 | 0 | } |
1677 | 0 | pim_zebra_interface_set_master(master, ifp); |
1678 | 0 | } |
1679 | 0 | } |
1680 | 0 | } |
1681 | 0 | return 0; |
1682 | 0 | } |
1683 | | |
1684 | | #ifndef FUZZING |
1685 | | static |
1686 | | #endif |
1687 | | int pim_ifp_down(struct interface *ifp) |
1688 | 0 | { |
1689 | 0 | if (PIM_DEBUG_ZEBRA) { |
1690 | 0 | zlog_debug( |
1691 | 0 | "%s: %s index %d vrf %s(%u) flags %ld metric %d mtu %d operative %d", |
1692 | 0 | __func__, ifp->name, ifp->ifindex, ifp->vrf->name, |
1693 | 0 | ifp->vrf->vrf_id, (long)ifp->flags, ifp->metric, |
1694 | 0 | ifp->mtu, if_is_operative(ifp)); |
1695 | 0 | } |
1696 | |
|
1697 | 0 | if (!if_is_operative(ifp)) { |
1698 | 0 | pim_ifchannel_delete_all(ifp); |
1699 | | /* |
1700 | | pim_if_addr_del_all() suffices for shutting down IGMP, |
1701 | | but not for shutting down PIM |
1702 | | */ |
1703 | 0 | pim_if_addr_del_all(ifp); |
1704 | | |
1705 | | /* |
1706 | | pim_sock_delete() closes the socket, stops read and timer |
1707 | | threads, |
1708 | | and kills all neighbors. |
1709 | | */ |
1710 | 0 | if (ifp->info) { |
1711 | 0 | pim_sock_delete(ifp, "link down"); |
1712 | 0 | } |
1713 | 0 | } |
1714 | |
|
1715 | 0 | if (ifp->info) { |
1716 | 0 | pim_if_del_vif(ifp); |
1717 | 0 | pim_ifstat_reset(ifp); |
1718 | 0 | } |
1719 | |
|
1720 | 0 | return 0; |
1721 | 0 | } |
1722 | | #ifndef FUZZING |
1723 | | static |
1724 | | #endif |
1725 | | int pim_ifp_destroy(struct interface *ifp) |
1726 | 0 | { |
1727 | 0 | if (PIM_DEBUG_ZEBRA) { |
1728 | 0 | zlog_debug( |
1729 | 0 | "%s: %s index %d vrf %s(%u) flags %ld metric %d mtu %d operative %d", |
1730 | 0 | __func__, ifp->name, ifp->ifindex, ifp->vrf->name, |
1731 | 0 | ifp->vrf->vrf_id, (long)ifp->flags, ifp->metric, |
1732 | 0 | ifp->mtu, if_is_operative(ifp)); |
1733 | 0 | } |
1734 | |
|
1735 | 0 | if (!if_is_operative(ifp)) |
1736 | 0 | pim_if_addr_del_all(ifp); |
1737 | |
|
1738 | 0 | #if PIM_IPV == 4 |
1739 | 0 | struct pim_instance *pim; |
1740 | |
|
1741 | 0 | pim = ifp->vrf->info; |
1742 | 0 | if (pim && pim->vxlan.term_if == ifp) |
1743 | 0 | pim_vxlan_del_term_dev(pim); |
1744 | 0 | #endif |
1745 | |
|
1746 | 0 | return 0; |
1747 | 0 | } |
1748 | | |
1749 | | static int pim_if_new_hook(struct interface *ifp) |
1750 | 0 | { |
1751 | 0 | return 0; |
1752 | 0 | } |
1753 | | |
1754 | | static int pim_if_delete_hook(struct interface *ifp) |
1755 | 0 | { |
1756 | 0 | if (ifp->info) |
1757 | 0 | pim_if_delete(ifp); |
1758 | |
|
1759 | 0 | return 0; |
1760 | 0 | } |
1761 | | |
1762 | | void pim_iface_init(void) |
1763 | 0 | { |
1764 | 0 | hook_register_prio(if_add, 0, pim_if_new_hook); |
1765 | 0 | hook_register_prio(if_del, 0, pim_if_delete_hook); |
1766 | |
|
1767 | 0 | if_zapi_callbacks(pim_ifp_create, pim_ifp_up, pim_ifp_down, |
1768 | 0 | pim_ifp_destroy); |
1769 | 0 | } |