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