]>
Commit | Line | Data |
---|---|---|
12e41d03 DL |
1 | /* |
2 | PIM for Quagga | |
3 | Copyright (C) 2008 Everton da Silva Marques | |
4 | This program is free software; you can redistribute it and/or modify | |
5 | it under the terms of the GNU General Public License as published by | |
6 | the Free Software Foundation; either version 2 of the License, or | |
7 | (at your option) any later version. | |
8 | ||
9 | This program is distributed in the hope that it will be useful, but | |
10 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
12 | General Public License for more details. | |
13 | ||
14 | You should have received a copy of the GNU General Public License | |
15 | along with this program; see the file COPYING; if not, write to the | |
16 | Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, | |
17 | MA 02110-1301 USA | |
b58ed1f8 | 18 | <<<<<<< HEAD |
12e41d03 | 19 | |
b58ed1f8 DS |
20 | ======= |
21 | >>>>>>> origin/master | |
12e41d03 DL |
22 | */ |
23 | ||
24 | #include <zebra.h> | |
25 | ||
26 | #include "if.h" | |
27 | #include "log.h" | |
28 | #include "vty.h" | |
29 | #include "memory.h" | |
30 | #include "prefix.h" | |
469351b3 | 31 | #include "vrf.h" |
9f0edbc9 | 32 | #include "linklist.h" |
7176984f | 33 | #include "plist.h" |
12e41d03 DL |
34 | |
35 | #include "pimd.h" | |
36 | #include "pim_iface.h" | |
37 | #include "pim_igmp.h" | |
38 | #include "pim_mroute.h" | |
39 | #include "pim_oil.h" | |
40 | #include "pim_str.h" | |
41 | #include "pim_pim.h" | |
42 | #include "pim_neighbor.h" | |
43 | #include "pim_ifchannel.h" | |
12e41d03 DL |
44 | #include "pim_sock.h" |
45 | #include "pim_time.h" | |
46 | #include "pim_ssmpingd.h" | |
7176984f | 47 | #include "pim_rp.h" |
12e41d03 | 48 | |
c992c9a0 | 49 | struct interface *pim_regiface = NULL; |
ea4a71fc | 50 | struct list *pim_ifchannel_list = NULL; |
c992c9a0 | 51 | |
12e41d03 DL |
52 | static void pim_if_igmp_join_del_all(struct interface *ifp); |
53 | ||
ea4a71fc DS |
54 | void |
55 | pim_if_init (void) | |
56 | { | |
57 | vrf_iflist_create(VRF_DEFAULT); | |
58 | pim_ifchannel_list = list_new(); | |
59 | pim_ifchannel_list->cmp = (int (*)(void *, void *))pim_ifchannel_compare; | |
60 | } | |
61 | ||
62 | void | |
63 | pim_if_terminate (void) | |
64 | { | |
65 | if (pim_ifchannel_list) | |
66 | list_free (pim_ifchannel_list); | |
67 | } | |
68 | ||
69 | static void *if_list_clean(struct pim_interface *pim_ifp) | |
70 | { | |
71 | if (pim_ifp->igmp_join_list) { | |
72 | list_delete(pim_ifp->igmp_join_list); | |
73 | } | |
74 | ||
75 | if (pim_ifp->igmp_socket_list) { | |
76 | list_delete(pim_ifp->igmp_socket_list); | |
77 | } | |
78 | ||
79 | if (pim_ifp->pim_neighbor_list) { | |
80 | list_delete(pim_ifp->pim_neighbor_list); | |
81 | } | |
82 | ||
83 | if (pim_ifp->pim_ifchannel_list) { | |
84 | list_delete(pim_ifp->pim_ifchannel_list); | |
85 | } | |
86 | ||
87 | XFREE(MTYPE_PIM_INTERFACE, pim_ifp); | |
88 | ||
89 | return 0; | |
90 | } | |
91 | ||
12e41d03 DL |
92 | struct pim_interface *pim_if_new(struct interface *ifp, int igmp, int pim) |
93 | { | |
94 | struct pim_interface *pim_ifp; | |
95 | ||
96 | zassert(ifp); | |
97 | zassert(!ifp->info); | |
98 | ||
36d9e7dc | 99 | pim_ifp = XCALLOC(MTYPE_PIM_INTERFACE, sizeof(*pim_ifp)); |
12e41d03 | 100 | if (!pim_ifp) { |
36d9e7dc | 101 | zlog_err("PIM XCALLOC(%zu) failure", sizeof(*pim_ifp)); |
12e41d03 DL |
102 | return 0; |
103 | } | |
104 | ||
105 | pim_ifp->options = 0; | |
106 | pim_ifp->mroute_vif_index = -1; | |
107 | ||
b05b72e8 | 108 | pim_ifp->igmp_version = IGMP_DEFAULT_VERSION; |
12e41d03 DL |
109 | pim_ifp->igmp_default_robustness_variable = IGMP_DEFAULT_ROBUSTNESS_VARIABLE; |
110 | pim_ifp->igmp_default_query_interval = IGMP_GENERAL_QUERY_INTERVAL; | |
111 | pim_ifp->igmp_query_max_response_time_dsec = IGMP_QUERY_MAX_RESPONSE_TIME_DSEC; | |
112 | pim_ifp->igmp_specific_query_max_response_time_dsec = IGMP_SPECIFIC_QUERY_MAX_RESPONSE_TIME_DSEC; | |
113 | ||
114 | /* | |
115 | RFC 3376: 8.3. Query Response Interval | |
116 | The number of seconds represented by the [Query Response Interval] | |
117 | must be less than the [Query Interval]. | |
118 | */ | |
119 | zassert(pim_ifp->igmp_query_max_response_time_dsec < pim_ifp->igmp_default_query_interval); | |
120 | ||
121 | if (pim) | |
122 | PIM_IF_DO_PIM(pim_ifp->options); | |
123 | if (igmp) | |
124 | PIM_IF_DO_IGMP(pim_ifp->options); | |
125 | ||
12e41d03 | 126 | PIM_IF_DO_IGMP_LISTEN_ALLROUTERS(pim_ifp->options); |
12e41d03 | 127 | |
59ba0ac3 DS |
128 | pim_ifp->igmp_join_list = NULL; |
129 | pim_ifp->igmp_socket_list = NULL; | |
130 | pim_ifp->pim_neighbor_list = NULL; | |
131 | pim_ifp->pim_ifchannel_list = NULL; | |
e4cc0185 | 132 | pim_ifp->pim_generation_id = 0; |
12e41d03 DL |
133 | |
134 | /* list of struct igmp_sock */ | |
135 | pim_ifp->igmp_socket_list = list_new(); | |
136 | if (!pim_ifp->igmp_socket_list) { | |
137 | zlog_err("%s %s: failure: igmp_socket_list=list_new()", | |
138 | __FILE__, __PRETTY_FUNCTION__); | |
139 | return if_list_clean(pim_ifp); | |
140 | } | |
141 | pim_ifp->igmp_socket_list->del = (void (*)(void *)) igmp_sock_free; | |
142 | ||
143 | /* list of struct pim_neighbor */ | |
144 | pim_ifp->pim_neighbor_list = list_new(); | |
145 | if (!pim_ifp->pim_neighbor_list) { | |
146 | zlog_err("%s %s: failure: pim_neighbor_list=list_new()", | |
147 | __FILE__, __PRETTY_FUNCTION__); | |
148 | return if_list_clean(pim_ifp); | |
149 | } | |
150 | pim_ifp->pim_neighbor_list->del = (void (*)(void *)) pim_neighbor_free; | |
151 | ||
152 | /* list of struct pim_ifchannel */ | |
153 | pim_ifp->pim_ifchannel_list = list_new(); | |
154 | if (!pim_ifp->pim_ifchannel_list) { | |
155 | zlog_err("%s %s: failure: pim_ifchannel_list=list_new()", | |
156 | __FILE__, __PRETTY_FUNCTION__); | |
157 | return if_list_clean(pim_ifp); | |
158 | } | |
159 | pim_ifp->pim_ifchannel_list->del = (void (*)(void *)) pim_ifchannel_free; | |
bbd64ce1 | 160 | pim_ifp->pim_ifchannel_list->cmp = (int (*)(void *, void *)) pim_ifchannel_compare; |
12e41d03 DL |
161 | |
162 | ifp->info = pim_ifp; | |
163 | ||
164 | pim_sock_reset(ifp); | |
165 | ||
12e41d03 DL |
166 | if (PIM_MROUTE_IS_ENABLED) { |
167 | pim_if_add_vif(ifp); | |
168 | } | |
169 | ||
170 | return pim_ifp; | |
171 | } | |
172 | ||
173 | void pim_if_delete(struct interface *ifp) | |
174 | { | |
175 | struct pim_interface *pim_ifp; | |
176 | ||
177 | zassert(ifp); | |
178 | pim_ifp = ifp->info; | |
179 | zassert(pim_ifp); | |
180 | ||
181 | if (pim_ifp->igmp_join_list) { | |
182 | pim_if_igmp_join_del_all(ifp); | |
183 | } | |
12e41d03 | 184 | |
cb24fec4 DS |
185 | pim_ifchannel_delete_all (ifp); |
186 | igmp_sock_delete_all (ifp); | |
12e41d03 | 187 | |
cb24fec4 | 188 | pim_neighbor_delete_all (ifp, "Interface removed from configuration"); |
12e41d03 DL |
189 | |
190 | if (PIM_MROUTE_IS_ENABLED) { | |
191 | pim_if_del_vif(ifp); | |
192 | } | |
193 | ||
194 | list_delete(pim_ifp->igmp_socket_list); | |
195 | list_delete(pim_ifp->pim_neighbor_list); | |
196 | list_delete(pim_ifp->pim_ifchannel_list); | |
197 | ||
198 | XFREE(MTYPE_PIM_INTERFACE, pim_ifp); | |
199 | ||
59ba0ac3 | 200 | ifp->info = NULL; |
12e41d03 DL |
201 | } |
202 | ||
203 | void pim_if_update_could_assert(struct interface *ifp) | |
204 | { | |
205 | struct pim_interface *pim_ifp; | |
206 | struct listnode *node; | |
207 | struct listnode *next_node; | |
208 | struct pim_ifchannel *ch; | |
209 | ||
210 | pim_ifp = ifp->info; | |
211 | zassert(pim_ifp); | |
212 | ||
213 | for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) { | |
214 | pim_ifchannel_update_could_assert(ch); | |
215 | } | |
216 | } | |
217 | ||
218 | static void pim_if_update_my_assert_metric(struct interface *ifp) | |
219 | { | |
220 | struct pim_interface *pim_ifp; | |
221 | struct listnode *node; | |
222 | struct listnode *next_node; | |
223 | struct pim_ifchannel *ch; | |
224 | ||
225 | pim_ifp = ifp->info; | |
226 | zassert(pim_ifp); | |
227 | ||
228 | for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) { | |
229 | pim_ifchannel_update_my_assert_metric(ch); | |
230 | } | |
231 | } | |
232 | ||
233 | static void pim_addr_change(struct interface *ifp) | |
234 | { | |
235 | struct pim_interface *pim_ifp; | |
236 | ||
237 | pim_ifp = ifp->info; | |
238 | zassert(pim_ifp); | |
239 | ||
240 | pim_if_dr_election(ifp); /* router's own DR Priority (addr) changes -- Done TODO T30 */ | |
241 | pim_if_update_join_desired(pim_ifp); /* depends on DR */ | |
242 | pim_if_update_could_assert(ifp); /* depends on DR */ | |
243 | pim_if_update_my_assert_metric(ifp); /* depends on could_assert */ | |
244 | pim_if_update_assert_tracking_desired(ifp); /* depends on DR, join_desired */ | |
245 | ||
246 | /* | |
247 | RFC 4601: 4.3.1. Sending Hello Messages | |
248 | ||
249 | 1) Before an interface goes down or changes primary IP address, a | |
250 | Hello message with a zero HoldTime should be sent immediately | |
251 | (with the old IP address if the IP address changed). | |
252 | -- FIXME See CAVEAT C13 | |
253 | ||
254 | 2) After an interface has changed its IP address, it MUST send a | |
255 | Hello message with its new IP address. | |
256 | -- DONE below | |
257 | ||
258 | 3) If an interface changes one of its secondary IP addresses, a | |
259 | Hello message with an updated Address_List option and a non-zero | |
260 | HoldTime should be sent immediately. | |
261 | -- FIXME See TODO T31 | |
262 | */ | |
263 | pim_ifp->pim_ifstat_hello_sent = 0; /* reset hello counter */ | |
264 | if (pim_ifp->pim_sock_fd < 0) | |
265 | return; | |
266 | pim_hello_restart_now(ifp); /* send hello and restart timer */ | |
267 | } | |
268 | ||
12e41d03 DL |
269 | static int detect_primary_address_change(struct interface *ifp, |
270 | int force_prim_as_any, | |
271 | const char *caller) | |
272 | { | |
4763cd0e | 273 | struct pim_interface *pim_ifp = ifp->info; |
12e41d03 DL |
274 | struct in_addr new_prim_addr; |
275 | int changed; | |
276 | ||
12e41d03 DL |
277 | if (force_prim_as_any) |
278 | new_prim_addr = qpim_inaddr_any; | |
279 | else | |
280 | new_prim_addr = pim_find_primary_addr(ifp); | |
281 | ||
282 | changed = new_prim_addr.s_addr != pim_ifp->primary_address.s_addr; | |
283 | ||
284 | if (PIM_DEBUG_ZEBRA) { | |
eaa54bdb DW |
285 | char new_prim_str[INET_ADDRSTRLEN]; |
286 | char old_prim_str[INET_ADDRSTRLEN]; | |
12e41d03 DL |
287 | pim_inet4_dump("<new?>", new_prim_addr, new_prim_str, sizeof(new_prim_str)); |
288 | pim_inet4_dump("<old?>", pim_ifp->primary_address, old_prim_str, sizeof(old_prim_str)); | |
289 | zlog_debug("%s: old=%s new=%s on interface %s: %s", | |
290 | __PRETTY_FUNCTION__, | |
291 | old_prim_str, new_prim_str, ifp->name, | |
292 | changed ? "changed" : "unchanged"); | |
293 | } | |
294 | ||
295 | if (changed) { | |
12e41d03 | 296 | pim_ifp->primary_address = new_prim_addr; |
12e41d03 DL |
297 | } |
298 | ||
299 | return changed; | |
300 | } | |
301 | ||
7176984f | 302 | static int pim_sec_addr_comp(const void *p1, const void *p2) |
303 | { | |
304 | const struct pim_secondary_addr *sec1 = p1; | |
305 | const struct pim_secondary_addr *sec2 = p2; | |
306 | ||
307 | if (ntohl(sec1->addr.s_addr) < ntohl(sec2->addr.s_addr)) | |
308 | return -1; | |
309 | ||
310 | if (ntohl(sec1->addr.s_addr) > ntohl(sec2->addr.s_addr)) | |
311 | return 1; | |
312 | ||
313 | return 0; | |
314 | } | |
315 | ||
316 | static void pim_sec_addr_free(struct pim_secondary_addr *sec_addr) | |
317 | { | |
318 | XFREE(MTYPE_PIM_SEC_ADDR, sec_addr); | |
319 | } | |
320 | ||
321 | static struct pim_secondary_addr * | |
322 | pim_sec_addr_find(struct pim_interface *pim_ifp, struct in_addr addr) | |
323 | { | |
324 | struct pim_secondary_addr *sec_addr; | |
325 | struct listnode *node; | |
326 | ||
327 | if (!pim_ifp->sec_addr_list) { | |
328 | return NULL; | |
329 | } | |
330 | ||
331 | for (ALL_LIST_ELEMENTS_RO(pim_ifp->sec_addr_list, node, sec_addr)) { | |
332 | if (sec_addr->addr.s_addr == addr.s_addr) { | |
333 | return sec_addr; | |
334 | } | |
335 | } | |
336 | ||
337 | return NULL; | |
338 | } | |
339 | ||
340 | static void pim_sec_addr_del(struct pim_interface *pim_ifp, | |
341 | struct pim_secondary_addr *sec_addr) | |
342 | { | |
343 | listnode_delete(pim_ifp->sec_addr_list, sec_addr); | |
344 | pim_sec_addr_free(sec_addr); | |
345 | } | |
346 | ||
347 | static int pim_sec_addr_add(struct pim_interface *pim_ifp, struct in_addr addr) | |
348 | { | |
349 | int changed = 0; | |
350 | struct pim_secondary_addr *sec_addr; | |
351 | ||
352 | sec_addr = pim_sec_addr_find(pim_ifp, addr); | |
353 | if (sec_addr) { | |
354 | sec_addr->flags &= ~PIM_SEC_ADDRF_STALE; | |
355 | return changed; | |
356 | } | |
357 | ||
358 | if (!pim_ifp->sec_addr_list) { | |
359 | pim_ifp->sec_addr_list = list_new(); | |
360 | pim_ifp->sec_addr_list->del = (void (*)(void *))pim_sec_addr_free; | |
361 | pim_ifp->sec_addr_list->cmp = (int (*)(void *, void *))pim_sec_addr_comp; | |
362 | } | |
363 | ||
364 | sec_addr = XCALLOC(MTYPE_PIM_SEC_ADDR, sizeof(*sec_addr)); | |
365 | if (!sec_addr) { | |
366 | if (list_isempty(pim_ifp->sec_addr_list)) { | |
367 | list_free(pim_ifp->sec_addr_list); | |
368 | pim_ifp->sec_addr_list = NULL; | |
369 | } | |
370 | return changed; | |
371 | } | |
372 | ||
373 | changed = 1; | |
374 | sec_addr->addr = addr; | |
375 | listnode_add_sort(pim_ifp->sec_addr_list, sec_addr); | |
376 | ||
377 | return changed; | |
378 | } | |
379 | ||
380 | static int pim_sec_addr_del_all(struct pim_interface *pim_ifp) | |
381 | { | |
382 | int changed = 0; | |
383 | ||
384 | if (!pim_ifp->sec_addr_list) { | |
385 | return changed; | |
386 | } | |
387 | if (!list_isempty(pim_ifp->sec_addr_list)) { | |
388 | changed = 1; | |
389 | /* remove all nodes and free up the list itself */ | |
390 | list_delete_all_node(pim_ifp->sec_addr_list); | |
391 | list_free(pim_ifp->sec_addr_list); | |
392 | pim_ifp->sec_addr_list = NULL; | |
393 | } | |
394 | ||
395 | return changed; | |
396 | } | |
397 | ||
398 | static int pim_sec_addr_update(struct interface *ifp) | |
399 | { | |
400 | struct pim_interface *pim_ifp = ifp->info; | |
401 | struct connected *ifc; | |
402 | struct listnode *node; | |
403 | struct listnode *nextnode; | |
404 | struct pim_secondary_addr *sec_addr; | |
405 | int changed = 0; | |
406 | ||
407 | if (pim_ifp->sec_addr_list) { | |
408 | for (ALL_LIST_ELEMENTS_RO(pim_ifp->sec_addr_list, node, sec_addr)) { | |
409 | sec_addr->flags |= PIM_SEC_ADDRF_STALE; | |
410 | } | |
411 | } | |
412 | ||
413 | for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { | |
414 | struct prefix *p = ifc->address; | |
415 | ||
416 | if (p->family != AF_INET) { | |
417 | continue; | |
418 | } | |
419 | ||
420 | if (PIM_INADDR_IS_ANY(p->u.prefix4)) { | |
421 | continue; | |
422 | } | |
423 | ||
424 | if (pim_ifp->primary_address.s_addr == p->u.prefix4.s_addr) { | |
425 | /* don't add the primary address into the secondary address list */ | |
426 | continue; | |
427 | } | |
428 | ||
429 | if (pim_sec_addr_add(pim_ifp, p->u.prefix4)) { | |
430 | changed = 1; | |
431 | } | |
432 | } | |
433 | ||
434 | if (pim_ifp->sec_addr_list) { | |
435 | /* Drop stale entries */ | |
436 | for (ALL_LIST_ELEMENTS(pim_ifp->sec_addr_list, node, nextnode, sec_addr)) { | |
437 | if (sec_addr->flags & PIM_SEC_ADDRF_STALE) { | |
438 | pim_sec_addr_del(pim_ifp, sec_addr); | |
439 | changed = 1; | |
440 | } | |
441 | } | |
442 | ||
443 | /* If the list went empty free it up */ | |
444 | if (list_isempty(pim_ifp->sec_addr_list)) { | |
445 | list_free(pim_ifp->sec_addr_list); | |
446 | pim_ifp->sec_addr_list = NULL; | |
447 | } | |
448 | } | |
449 | ||
450 | return changed; | |
451 | } | |
452 | ||
4763cd0e | 453 | static int detect_secondary_address_change(struct interface *ifp, |
7176984f | 454 | int force_prim_as_any, |
12e41d03 DL |
455 | const char *caller) |
456 | { | |
4763cd0e | 457 | struct pim_interface *pim_ifp = ifp->info; |
7176984f | 458 | int changed = 0; |
12e41d03 | 459 | |
7176984f | 460 | if (force_prim_as_any) { |
461 | /* if primary address is being forced to zero just flush the | |
462 | * secondary address list */ | |
4763cd0e | 463 | changed = pim_sec_addr_del_all(pim_ifp); |
7176984f | 464 | } else { |
465 | /* re-evaluate the secondary address list */ | |
466 | changed = pim_sec_addr_update(ifp); | |
467 | } | |
468 | ||
4763cd0e | 469 | return changed; |
470 | } | |
471 | ||
472 | static void detect_address_change(struct interface *ifp, | |
473 | int force_prim_as_any, | |
474 | const char *caller) | |
475 | { | |
476 | int changed = 0; | |
477 | struct pim_interface *pim_ifp; | |
12e41d03 | 478 | |
4763cd0e | 479 | pim_ifp = ifp->info; |
480 | if (!pim_ifp) | |
12e41d03 | 481 | return; |
4763cd0e | 482 | |
483 | if (detect_primary_address_change(ifp, force_prim_as_any, caller)) { | |
484 | changed = 1; | |
12e41d03 DL |
485 | } |
486 | ||
4763cd0e | 487 | if (detect_secondary_address_change(ifp, force_prim_as_any, caller)) { |
488 | changed = 1; | |
489 | } | |
490 | ||
491 | ||
492 | if (changed) { | |
493 | if (!PIM_IF_TEST_PIM(pim_ifp->options)) { | |
494 | return; | |
495 | } | |
496 | ||
497 | pim_addr_change(ifp); | |
12e41d03 DL |
498 | } |
499 | ||
4763cd0e | 500 | /* XXX: if we have unnumbered interfaces we need to run detect address |
501 | * address change on all of them when the lo address changes */ | |
12e41d03 DL |
502 | } |
503 | ||
4763cd0e | 504 | int pim_update_source_set(struct interface *ifp, struct in_addr source) |
12e41d03 | 505 | { |
4763cd0e | 506 | struct pim_interface *pim_ifp = ifp->info; |
12e41d03 | 507 | |
4763cd0e | 508 | if (!pim_ifp) { |
509 | return PIM_IFACE_NOT_FOUND; | |
12e41d03 DL |
510 | } |
511 | ||
4763cd0e | 512 | if (pim_ifp->update_source.s_addr == source.s_addr) { |
513 | return PIM_UPDATE_SOURCE_DUP; | |
514 | } | |
515 | ||
516 | pim_ifp->update_source = source; | |
517 | detect_address_change(ifp, 0 /* force_prim_as_any */, | |
518 | __PRETTY_FUNCTION__); | |
519 | ||
520 | return PIM_SUCCESS; | |
12e41d03 DL |
521 | } |
522 | ||
523 | void pim_if_addr_add(struct connected *ifc) | |
524 | { | |
525 | struct pim_interface *pim_ifp; | |
526 | struct interface *ifp; | |
527 | struct in_addr ifaddr; | |
528 | ||
529 | zassert(ifc); | |
530 | ||
531 | ifp = ifc->ifp; | |
532 | zassert(ifp); | |
533 | pim_ifp = ifp->info; | |
534 | if (!pim_ifp) | |
535 | return; | |
536 | ||
537 | if (!if_is_operative(ifp)) | |
538 | return; | |
539 | ||
7adf0260 | 540 | if (PIM_DEBUG_ZEBRA) { |
12e41d03 DL |
541 | char buf[BUFSIZ]; |
542 | prefix2str(ifc->address, buf, BUFSIZ); | |
543 | zlog_debug("%s: %s ifindex=%d connected IP address %s %s", | |
544 | __PRETTY_FUNCTION__, | |
545 | ifp->name, ifp->ifindex, buf, | |
546 | CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY) ? | |
547 | "secondary" : "primary"); | |
548 | } | |
549 | ||
550 | ifaddr = ifc->address->u.prefix4; | |
551 | ||
552 | detect_address_change(ifp, 0, __PRETTY_FUNCTION__); | |
553 | ||
554 | if (PIM_IF_TEST_IGMP(pim_ifp->options)) { | |
555 | struct igmp_sock *igmp; | |
556 | ||
557 | /* lookup IGMP socket */ | |
558 | igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->igmp_socket_list, | |
559 | ifaddr); | |
560 | if (!igmp) { | |
561 | /* if addr new, add IGMP socket */ | |
562 | pim_igmp_sock_add(pim_ifp->igmp_socket_list, ifaddr, ifp); | |
563 | } | |
564 | } /* igmp */ | |
565 | ||
566 | if (PIM_IF_TEST_PIM(pim_ifp->options)) { | |
567 | ||
568 | /* Interface has a valid primary address ? */ | |
569 | if (PIM_INADDR_ISNOT_ANY(pim_ifp->primary_address)) { | |
570 | ||
571 | /* Interface has a valid socket ? */ | |
572 | if (pim_ifp->pim_sock_fd < 0) { | |
573 | if (pim_sock_add(ifp)) { | |
574 | zlog_warn("Failure creating PIM socket for interface %s", | |
575 | ifp->name); | |
576 | } | |
577 | } | |
578 | ||
579 | } | |
580 | } /* pim */ | |
581 | ||
582 | if (PIM_MROUTE_IS_ENABLED) { | |
583 | /* | |
584 | PIM or IGMP is enabled on interface, and there is at least one | |
585 | address assigned, then try to create a vif_index. | |
586 | */ | |
587 | if (pim_ifp->mroute_vif_index < 0) { | |
588 | pim_if_add_vif(ifp); | |
589 | } | |
c8507a16 | 590 | pim_ifchannel_scan_forward_start (ifp); |
12e41d03 DL |
591 | } |
592 | } | |
593 | ||
594 | static void pim_if_addr_del_igmp(struct connected *ifc) | |
595 | { | |
596 | struct pim_interface *pim_ifp = ifc->ifp->info; | |
597 | struct igmp_sock *igmp; | |
598 | struct in_addr ifaddr; | |
599 | ||
600 | if (ifc->address->family != AF_INET) { | |
601 | /* non-IPv4 address */ | |
602 | return; | |
603 | } | |
604 | ||
605 | if (!pim_ifp) { | |
606 | /* IGMP not enabled on interface */ | |
607 | return; | |
608 | } | |
609 | ||
610 | ifaddr = ifc->address->u.prefix4; | |
611 | ||
612 | /* lookup IGMP socket */ | |
613 | igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->igmp_socket_list, | |
614 | ifaddr); | |
615 | if (igmp) { | |
616 | /* if addr found, del IGMP socket */ | |
617 | igmp_sock_delete(igmp); | |
618 | } | |
619 | } | |
620 | ||
621 | static void pim_if_addr_del_pim(struct connected *ifc) | |
622 | { | |
623 | struct pim_interface *pim_ifp = ifc->ifp->info; | |
624 | ||
625 | if (ifc->address->family != AF_INET) { | |
626 | /* non-IPv4 address */ | |
627 | return; | |
628 | } | |
629 | ||
630 | if (!pim_ifp) { | |
631 | /* PIM not enabled on interface */ | |
632 | return; | |
633 | } | |
634 | ||
635 | if (PIM_INADDR_ISNOT_ANY(pim_ifp->primary_address)) { | |
636 | /* Interface keeps a valid primary address */ | |
637 | return; | |
638 | } | |
639 | ||
640 | if (pim_ifp->pim_sock_fd < 0) { | |
641 | /* Interface does not hold a valid socket any longer */ | |
642 | return; | |
643 | } | |
644 | ||
645 | /* | |
646 | pim_sock_delete() closes the socket, stops read and timer threads, | |
647 | and kills all neighbors. | |
648 | */ | |
649 | pim_sock_delete(ifc->ifp, "last address has been removed from interface"); | |
650 | } | |
651 | ||
652 | void pim_if_addr_del(struct connected *ifc, int force_prim_as_any) | |
653 | { | |
654 | struct interface *ifp; | |
655 | ||
656 | zassert(ifc); | |
657 | ifp = ifc->ifp; | |
658 | zassert(ifp); | |
659 | ||
7adf0260 | 660 | if (PIM_DEBUG_ZEBRA) { |
12e41d03 DL |
661 | char buf[BUFSIZ]; |
662 | prefix2str(ifc->address, buf, BUFSIZ); | |
663 | zlog_debug("%s: %s ifindex=%d disconnected IP address %s %s", | |
664 | __PRETTY_FUNCTION__, | |
665 | ifp->name, ifp->ifindex, buf, | |
666 | CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY) ? | |
667 | "secondary" : "primary"); | |
668 | } | |
669 | ||
670 | detect_address_change(ifp, force_prim_as_any, __PRETTY_FUNCTION__); | |
671 | ||
672 | pim_if_addr_del_igmp(ifc); | |
673 | pim_if_addr_del_pim(ifc); | |
674 | } | |
675 | ||
676 | void pim_if_addr_add_all(struct interface *ifp) | |
677 | { | |
678 | struct connected *ifc; | |
679 | struct listnode *node; | |
680 | struct listnode *nextnode; | |
d8424057 DS |
681 | int v4_addrs = 0; |
682 | int v6_addrs = 0; | |
7176984f | 683 | struct pim_interface *pim_ifp = ifp->info; |
684 | ||
12e41d03 DL |
685 | |
686 | /* PIM/IGMP enabled ? */ | |
7176984f | 687 | if (!pim_ifp) |
12e41d03 DL |
688 | return; |
689 | ||
690 | for (ALL_LIST_ELEMENTS(ifp->connected, node, nextnode, ifc)) { | |
691 | struct prefix *p = ifc->address; | |
692 | ||
693 | if (p->family != AF_INET) | |
d8424057 DS |
694 | { |
695 | v6_addrs++; | |
696 | continue; | |
697 | } | |
12e41d03 | 698 | |
d8424057 | 699 | v4_addrs++; |
12e41d03 DL |
700 | pim_if_addr_add(ifc); |
701 | } | |
d8424057 DS |
702 | |
703 | if (!v4_addrs && v6_addrs && !if_is_loopback (ifp)) | |
704 | { | |
7176984f | 705 | if (PIM_IF_TEST_PIM(pim_ifp->options)) { |
d8424057 DS |
706 | |
707 | /* Interface has a valid primary address ? */ | |
708 | if (PIM_INADDR_ISNOT_ANY(pim_ifp->primary_address)) { | |
709 | ||
710 | /* Interface has a valid socket ? */ | |
711 | if (pim_ifp->pim_sock_fd < 0) { | |
712 | if (pim_sock_add(ifp)) { | |
713 | zlog_warn("Failure creating PIM socket for interface %s", | |
714 | ifp->name); | |
715 | } | |
716 | } | |
717 | ||
718 | } | |
719 | } /* pim */ | |
720 | } | |
ef71d1f8 DS |
721 | if (PIM_MROUTE_IS_ENABLED) { |
722 | /* | |
723 | * PIM or IGMP is enabled on interface, and there is at least one | |
724 | * address assigned, then try to create a vif_index. | |
725 | */ | |
726 | if (pim_ifp->mroute_vif_index < 0) { | |
727 | pim_if_add_vif(ifp); | |
728 | } | |
729 | pim_ifchannel_scan_forward_start (ifp); | |
730 | } | |
731 | ||
da3dcffb | 732 | pim_rp_setup(); |
7176984f | 733 | pim_rp_check_on_if_add(pim_ifp); |
12e41d03 DL |
734 | } |
735 | ||
736 | void pim_if_addr_del_all(struct interface *ifp) | |
737 | { | |
738 | struct connected *ifc; | |
739 | struct listnode *node; | |
740 | struct listnode *nextnode; | |
741 | ||
742 | /* PIM/IGMP enabled ? */ | |
743 | if (!ifp->info) | |
744 | return; | |
745 | ||
746 | for (ALL_LIST_ELEMENTS(ifp->connected, node, nextnode, ifc)) { | |
747 | struct prefix *p = ifc->address; | |
748 | ||
749 | if (p->family != AF_INET) | |
750 | continue; | |
751 | ||
752 | pim_if_addr_del(ifc, 1 /* force_prim_as_any=true */); | |
753 | } | |
da3dcffb DS |
754 | |
755 | pim_rp_setup(); | |
7176984f | 756 | pim_i_am_rp_re_evaluate(); |
12e41d03 DL |
757 | } |
758 | ||
759 | void pim_if_addr_del_all_igmp(struct interface *ifp) | |
760 | { | |
761 | struct connected *ifc; | |
762 | struct listnode *node; | |
763 | struct listnode *nextnode; | |
764 | ||
765 | /* PIM/IGMP enabled ? */ | |
766 | if (!ifp->info) | |
767 | return; | |
768 | ||
769 | for (ALL_LIST_ELEMENTS(ifp->connected, node, nextnode, ifc)) { | |
770 | struct prefix *p = ifc->address; | |
771 | ||
772 | if (p->family != AF_INET) | |
773 | continue; | |
774 | ||
775 | pim_if_addr_del_igmp(ifc); | |
776 | } | |
777 | } | |
778 | ||
779 | void pim_if_addr_del_all_pim(struct interface *ifp) | |
780 | { | |
781 | struct connected *ifc; | |
782 | struct listnode *node; | |
783 | struct listnode *nextnode; | |
784 | ||
785 | /* PIM/IGMP enabled ? */ | |
786 | if (!ifp->info) | |
787 | return; | |
788 | ||
789 | for (ALL_LIST_ELEMENTS(ifp->connected, node, nextnode, ifc)) { | |
790 | struct prefix *p = ifc->address; | |
791 | ||
792 | if (p->family != AF_INET) | |
793 | continue; | |
794 | ||
795 | pim_if_addr_del_pim(ifc); | |
796 | } | |
797 | } | |
798 | ||
89f1d91e DS |
799 | struct in_addr |
800 | pim_find_primary_addr (struct interface *ifp) | |
12e41d03 DL |
801 | { |
802 | struct connected *ifc; | |
803 | struct listnode *node; | |
804 | struct in_addr addr; | |
d8424057 DS |
805 | int v4_addrs = 0; |
806 | int v6_addrs = 0; | |
4763cd0e | 807 | struct pim_interface *pim_ifp = ifp->info; |
808 | ||
809 | if (pim_ifp && PIM_INADDR_ISNOT_ANY(pim_ifp->update_source)) { | |
810 | return pim_ifp->update_source; | |
811 | } | |
12e41d03 DL |
812 | |
813 | for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { | |
814 | struct prefix *p = ifc->address; | |
d8424057 | 815 | |
12e41d03 | 816 | if (p->family != AF_INET) |
d8424057 DS |
817 | { |
818 | v6_addrs++; | |
819 | continue; | |
820 | } | |
12e41d03 DL |
821 | |
822 | if (PIM_INADDR_IS_ANY(p->u.prefix4)) { | |
823 | zlog_warn("%s: null IPv4 address connected to interface %s", | |
824 | __PRETTY_FUNCTION__, ifp->name); | |
825 | continue; | |
826 | } | |
827 | ||
d8424057 DS |
828 | v4_addrs++; |
829 | ||
12e41d03 DL |
830 | if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY)) |
831 | continue; | |
832 | ||
833 | return p->u.prefix4; | |
834 | } | |
835 | ||
d8424057 DS |
836 | /* |
837 | * If we have no v4_addrs and v6 is configured | |
838 | * We probably are using unnumbered | |
839 | * So let's grab the loopbacks v4 address | |
840 | * and use that as the primary address | |
841 | */ | |
842 | if (!v4_addrs && v6_addrs && !if_is_loopback (ifp)) | |
843 | { | |
844 | struct interface *lo_ifp; | |
845 | lo_ifp = if_lookup_by_name_vrf ("lo", VRF_DEFAULT); | |
846 | if (lo_ifp) | |
847 | return pim_find_primary_addr (lo_ifp); | |
848 | } | |
849 | ||
12e41d03 DL |
850 | addr.s_addr = PIM_NET_INADDR_ANY; |
851 | ||
852 | return addr; | |
853 | } | |
854 | ||
9df99407 | 855 | static int pim_iface_vif_index = 0; |
744d91b3 | 856 | |
9df99407 | 857 | static int |
744d91b3 DS |
858 | pim_iface_next_vif_index (struct interface *ifp) |
859 | { | |
860 | /* | |
861 | * The pimreg vif is always going to be in index 0 | |
862 | * of the table. | |
863 | */ | |
864 | if (ifp->ifindex == PIM_OIF_PIM_REGISTER_VIF) | |
865 | return 0; | |
866 | ||
867 | pim_iface_vif_index++; | |
868 | return pim_iface_vif_index; | |
869 | } | |
870 | ||
12e41d03 DL |
871 | /* |
872 | pim_if_add_vif() uses ifindex as vif_index | |
873 | ||
874 | see also pim_if_find_vifindex_by_ifindex() | |
875 | */ | |
876 | int pim_if_add_vif(struct interface *ifp) | |
877 | { | |
878 | struct pim_interface *pim_ifp = ifp->info; | |
879 | struct in_addr ifaddr; | |
b3f2bf7c | 880 | unsigned char flags = 0; |
12e41d03 DL |
881 | |
882 | zassert(pim_ifp); | |
883 | ||
884 | if (pim_ifp->mroute_vif_index > 0) { | |
885 | zlog_warn("%s: vif_index=%d > 0 on interface %s ifindex=%d", | |
886 | __PRETTY_FUNCTION__, | |
887 | pim_ifp->mroute_vif_index, ifp->name, ifp->ifindex); | |
888 | return -1; | |
889 | } | |
890 | ||
891 | if (ifp->ifindex < 1) { | |
892 | zlog_warn("%s: ifindex=%d < 1 on interface %s", | |
893 | __PRETTY_FUNCTION__, | |
894 | ifp->ifindex, ifp->name); | |
895 | return -2; | |
896 | } | |
897 | ||
12e41d03 | 898 | ifaddr = pim_ifp->primary_address; |
c992c9a0 | 899 | if (ifp->ifindex != PIM_OIF_PIM_REGISTER_VIF && PIM_INADDR_IS_ANY(ifaddr)) { |
12e41d03 DL |
900 | zlog_warn("%s: could not get address for interface %s ifindex=%d", |
901 | __PRETTY_FUNCTION__, | |
902 | ifp->name, ifp->ifindex); | |
903 | return -4; | |
904 | } | |
905 | ||
744d91b3 DS |
906 | pim_ifp->mroute_vif_index = pim_iface_next_vif_index (ifp); |
907 | ||
908 | if (pim_ifp->mroute_vif_index >= MAXVIFS) | |
909 | { | |
910 | zlog_warn("%s: Attempting to configure more than MAXVIFS=%d on pim enabled interface %s", | |
911 | __PRETTY_FUNCTION__, | |
912 | MAXVIFS, ifp->name); | |
913 | return -3; | |
914 | } | |
915 | ||
b3f2bf7c RW |
916 | if (ifp->ifindex == PIM_OIF_PIM_REGISTER_VIF) |
917 | flags = VIFF_REGISTER; | |
918 | #ifdef VIFF_USE_IFINDEX | |
919 | else | |
920 | flags = VIFF_USE_IFINDEX; | |
921 | #endif | |
922 | ||
744d91b3 | 923 | if (pim_mroute_add_vif(ifp, ifaddr, flags)) { |
12e41d03 DL |
924 | /* pim_mroute_add_vif reported error */ |
925 | return -5; | |
926 | } | |
927 | ||
12e41d03 DL |
928 | /* |
929 | Update highest vif_index | |
930 | */ | |
c992c9a0 DS |
931 | if (pim_ifp->mroute_vif_index != PIM_OIF_PIM_REGISTER_VIF && |
932 | pim_ifp->mroute_vif_index > qpim_mroute_oif_highest_vif_index) { | |
12e41d03 DL |
933 | qpim_mroute_oif_highest_vif_index = pim_ifp->mroute_vif_index; |
934 | } | |
935 | ||
936 | return 0; | |
937 | } | |
938 | ||
939 | static int iflist_find_highest_vif_index() | |
940 | { | |
941 | struct listnode *ifnode; | |
942 | struct interface *ifp; | |
943 | struct pim_interface *pim_ifp; | |
944 | int highest_vif_index = -1; | |
945 | ||
469351b3 | 946 | for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp)) { |
12e41d03 DL |
947 | pim_ifp = ifp->info; |
948 | if (!pim_ifp) | |
949 | continue; | |
950 | ||
951 | if (pim_ifp->mroute_vif_index > highest_vif_index) { | |
952 | highest_vif_index = pim_ifp->mroute_vif_index; | |
953 | } | |
954 | } | |
955 | ||
956 | return highest_vif_index; | |
957 | } | |
958 | ||
959 | int pim_if_del_vif(struct interface *ifp) | |
960 | { | |
961 | struct pim_interface *pim_ifp = ifp->info; | |
962 | int old_vif_index; | |
963 | ||
964 | if (pim_ifp->mroute_vif_index < 1) { | |
965 | zlog_warn("%s: vif_index=%d < 1 on interface %s ifindex=%d", | |
966 | __PRETTY_FUNCTION__, | |
967 | pim_ifp->mroute_vif_index, ifp->name, ifp->ifindex); | |
968 | return -1; | |
969 | } | |
970 | ||
971 | if (pim_mroute_del_vif(pim_ifp->mroute_vif_index)) { | |
972 | /* pim_mroute_del_vif reported error */ | |
973 | return -2; | |
974 | } | |
975 | ||
976 | /* | |
977 | Update highest vif_index | |
978 | */ | |
979 | ||
980 | /* save old vif_index in order to compare with highest below */ | |
981 | old_vif_index = pim_ifp->mroute_vif_index; | |
982 | ||
983 | pim_ifp->mroute_vif_index = -1; | |
984 | ||
985 | if (old_vif_index == qpim_mroute_oif_highest_vif_index) { | |
986 | qpim_mroute_oif_highest_vif_index = iflist_find_highest_vif_index(); | |
987 | } | |
988 | ||
989 | return 0; | |
990 | } | |
991 | ||
992 | void pim_if_add_vif_all() | |
993 | { | |
994 | struct listnode *ifnode; | |
995 | struct listnode *ifnextnode; | |
996 | struct interface *ifp; | |
997 | ||
469351b3 | 998 | for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) { |
12e41d03 DL |
999 | if (!ifp->info) |
1000 | continue; | |
1001 | ||
1002 | pim_if_add_vif(ifp); | |
1003 | } | |
1004 | } | |
1005 | ||
1006 | void pim_if_del_vif_all() | |
1007 | { | |
1008 | struct listnode *ifnode; | |
1009 | struct listnode *ifnextnode; | |
1010 | struct interface *ifp; | |
1011 | ||
469351b3 | 1012 | for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) { |
12e41d03 DL |
1013 | if (!ifp->info) |
1014 | continue; | |
1015 | ||
1016 | pim_if_del_vif(ifp); | |
1017 | } | |
1018 | } | |
1019 | ||
b892f1dd | 1020 | struct interface *pim_if_find_by_vif_index(ifindex_t vif_index) |
12e41d03 DL |
1021 | { |
1022 | struct listnode *ifnode; | |
1023 | struct interface *ifp; | |
1024 | ||
ae90dfbb DS |
1025 | if (vif_index == 0) |
1026 | return if_lookup_by_name_vrf ("pimreg", VRF_DEFAULT); | |
1027 | ||
469351b3 | 1028 | for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp)) { |
12e41d03 DL |
1029 | if (ifp->info) { |
1030 | struct pim_interface *pim_ifp; | |
1031 | pim_ifp = ifp->info; | |
b892f1dd | 1032 | |
12e41d03 DL |
1033 | if (vif_index == pim_ifp->mroute_vif_index) |
1034 | return ifp; | |
1035 | } | |
1036 | } | |
1037 | ||
1038 | return 0; | |
1039 | } | |
1040 | ||
1041 | /* | |
1042 | pim_if_add_vif() uses ifindex as vif_index | |
1043 | */ | |
b892f1dd | 1044 | int pim_if_find_vifindex_by_ifindex(ifindex_t ifindex) |
12e41d03 | 1045 | { |
ae90dfbb DS |
1046 | struct pim_interface *pim_ifp; |
1047 | struct interface *ifp; | |
1048 | ||
1049 | ifp = if_lookup_by_index_vrf (ifindex, VRF_DEFAULT); | |
28185336 | 1050 | if (!ifp || !ifp->info) |
ae90dfbb | 1051 | return -1; |
28185336 | 1052 | pim_ifp = ifp->info; |
ae90dfbb DS |
1053 | |
1054 | return pim_ifp->mroute_vif_index; | |
12e41d03 DL |
1055 | } |
1056 | ||
1057 | int pim_if_lan_delay_enabled(struct interface *ifp) | |
1058 | { | |
1059 | struct pim_interface *pim_ifp; | |
1060 | ||
1061 | pim_ifp = ifp->info; | |
1062 | zassert(pim_ifp); | |
1063 | zassert(pim_ifp->pim_number_of_nonlandelay_neighbors >= 0); | |
1064 | ||
1065 | return pim_ifp->pim_number_of_nonlandelay_neighbors == 0; | |
1066 | } | |
1067 | ||
1068 | uint16_t pim_if_effective_propagation_delay_msec(struct interface *ifp) | |
1069 | { | |
1070 | if (pim_if_lan_delay_enabled(ifp)) { | |
1071 | struct pim_interface *pim_ifp; | |
1072 | pim_ifp = ifp->info; | |
1073 | return pim_ifp->pim_neighbors_highest_propagation_delay_msec; | |
1074 | } | |
1075 | else { | |
1076 | return PIM_DEFAULT_PROPAGATION_DELAY_MSEC; | |
1077 | } | |
1078 | } | |
1079 | ||
1080 | uint16_t pim_if_effective_override_interval_msec(struct interface *ifp) | |
1081 | { | |
1082 | if (pim_if_lan_delay_enabled(ifp)) { | |
1083 | struct pim_interface *pim_ifp; | |
1084 | pim_ifp = ifp->info; | |
1085 | return pim_ifp->pim_neighbors_highest_override_interval_msec; | |
1086 | } | |
1087 | else { | |
1088 | return PIM_DEFAULT_OVERRIDE_INTERVAL_MSEC; | |
1089 | } | |
1090 | } | |
1091 | ||
1092 | int pim_if_t_override_msec(struct interface *ifp) | |
1093 | { | |
1094 | int effective_override_interval_msec; | |
1095 | int t_override_msec; | |
1096 | ||
1097 | effective_override_interval_msec = | |
1098 | pim_if_effective_override_interval_msec(ifp); | |
1099 | ||
60b40924 | 1100 | t_override_msec = random() % (effective_override_interval_msec + 1); |
12e41d03 DL |
1101 | |
1102 | return t_override_msec; | |
1103 | } | |
1104 | ||
1105 | uint16_t pim_if_jp_override_interval_msec(struct interface *ifp) | |
1106 | { | |
1107 | return pim_if_effective_propagation_delay_msec(ifp) + | |
1108 | pim_if_effective_override_interval_msec(ifp); | |
1109 | } | |
1110 | ||
1111 | /* | |
1112 | RFC 4601: 4.1.6. State Summarization Macros | |
1113 | ||
1114 | The function NBR( I, A ) uses information gathered through PIM Hello | |
1115 | messages to map the IP address A of a directly connected PIM | |
1116 | neighbor router on interface I to the primary IP address of the same | |
1117 | router (Section 4.3.4). The primary IP address of a neighbor is the | |
1118 | address that it uses as the source of its PIM Hello messages. | |
1119 | */ | |
1120 | struct pim_neighbor *pim_if_find_neighbor(struct interface *ifp, | |
1121 | struct in_addr addr) | |
1122 | { | |
1123 | struct listnode *neighnode; | |
1124 | struct pim_neighbor *neigh; | |
1125 | struct pim_interface *pim_ifp; | |
1126 | ||
1127 | zassert(ifp); | |
1128 | ||
1129 | pim_ifp = ifp->info; | |
1130 | if (!pim_ifp) { | |
1131 | zlog_warn("%s: multicast not enabled on interface %s", | |
1132 | __PRETTY_FUNCTION__, | |
1133 | ifp->name); | |
1134 | return 0; | |
1135 | } | |
1136 | ||
1137 | for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode, neigh)) { | |
1138 | ||
1139 | /* primary address ? */ | |
1140 | if (neigh->source_addr.s_addr == addr.s_addr) | |
1141 | return neigh; | |
1142 | ||
1143 | /* secondary address ? */ | |
1144 | if (pim_neighbor_find_secondary(neigh, addr)) | |
1145 | return neigh; | |
1146 | } | |
1147 | ||
1148 | if (PIM_DEBUG_PIM_TRACE) { | |
eaa54bdb | 1149 | char addr_str[INET_ADDRSTRLEN]; |
12e41d03 DL |
1150 | pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str)); |
1151 | zlog_debug("%s: neighbor not found for address %s on interface %s", | |
1152 | __PRETTY_FUNCTION__, | |
1153 | addr_str, ifp->name); | |
1154 | } | |
1155 | ||
ff67a923 | 1156 | return NULL; |
12e41d03 DL |
1157 | } |
1158 | ||
1159 | long pim_if_t_suppressed_msec(struct interface *ifp) | |
1160 | { | |
1161 | struct pim_interface *pim_ifp; | |
1162 | long t_suppressed_msec; | |
60b40924 | 1163 | uint32_t ramount = 0; |
12e41d03 DL |
1164 | |
1165 | pim_ifp = ifp->info; | |
1166 | zassert(pim_ifp); | |
1167 | ||
1168 | /* join suppression disabled ? */ | |
1169 | if (PIM_IF_TEST_PIM_CAN_DISABLE_JOIN_SUPRESSION(pim_ifp->options)) | |
1170 | return 0; | |
1171 | ||
1172 | /* t_suppressed = t_periodic * rand(1.1, 1.4) */ | |
60b40924 DS |
1173 | ramount = 1100 + (random() % (1400 - 1100 + 1)); |
1174 | t_suppressed_msec = qpim_t_periodic * ramount; | |
12e41d03 DL |
1175 | |
1176 | return t_suppressed_msec; | |
1177 | } | |
1178 | ||
1179 | static void igmp_join_free(struct igmp_join *ij) | |
1180 | { | |
1181 | XFREE(MTYPE_PIM_IGMP_JOIN, ij); | |
1182 | } | |
1183 | ||
1184 | static struct igmp_join *igmp_join_find(struct list *join_list, | |
1185 | struct in_addr group_addr, | |
1186 | struct in_addr source_addr) | |
1187 | { | |
1188 | struct listnode *node; | |
1189 | struct igmp_join *ij; | |
1190 | ||
1191 | zassert(join_list); | |
1192 | ||
1193 | for (ALL_LIST_ELEMENTS_RO(join_list, node, ij)) { | |
1194 | if ((group_addr.s_addr == ij->group_addr.s_addr) && | |
1195 | (source_addr.s_addr == ij->source_addr.s_addr)) | |
1196 | return ij; | |
1197 | } | |
1198 | ||
1199 | return 0; | |
1200 | } | |
1201 | ||
1202 | static int igmp_join_sock(const char *ifname, | |
b892f1dd | 1203 | ifindex_t ifindex, |
12e41d03 DL |
1204 | struct in_addr group_addr, |
1205 | struct in_addr source_addr) | |
1206 | { | |
1207 | int join_fd; | |
1208 | ||
1209 | join_fd = pim_socket_raw(IPPROTO_IGMP); | |
1210 | if (join_fd < 0) { | |
1211 | return -1; | |
1212 | } | |
1213 | ||
1214 | if (pim_socket_join_source(join_fd, ifindex, group_addr, source_addr, ifname)) { | |
1215 | close(join_fd); | |
1216 | return -2; | |
1217 | } | |
1218 | ||
1219 | return join_fd; | |
1220 | } | |
1221 | ||
1222 | static struct igmp_join *igmp_join_new(struct interface *ifp, | |
1223 | struct in_addr group_addr, | |
1224 | struct in_addr source_addr) | |
1225 | { | |
1226 | struct pim_interface *pim_ifp; | |
1227 | struct igmp_join *ij; | |
1228 | int join_fd; | |
1229 | ||
1230 | pim_ifp = ifp->info; | |
1231 | zassert(pim_ifp); | |
1232 | ||
1233 | join_fd = igmp_join_sock(ifp->name, ifp->ifindex, group_addr, source_addr); | |
1234 | if (join_fd < 0) { | |
eaa54bdb DW |
1235 | char group_str[INET_ADDRSTRLEN]; |
1236 | char source_str[INET_ADDRSTRLEN]; | |
12e41d03 DL |
1237 | pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str)); |
1238 | pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str)); | |
1239 | zlog_warn("%s: igmp_join_sock() failure for IGMP group %s source %s on interface %s", | |
1240 | __PRETTY_FUNCTION__, | |
1241 | group_str, source_str, ifp->name); | |
1242 | return 0; | |
1243 | } | |
1244 | ||
36d9e7dc | 1245 | ij = XCALLOC(MTYPE_PIM_IGMP_JOIN, sizeof(*ij)); |
12e41d03 | 1246 | if (!ij) { |
eaa54bdb DW |
1247 | char group_str[INET_ADDRSTRLEN]; |
1248 | char source_str[INET_ADDRSTRLEN]; | |
12e41d03 DL |
1249 | pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str)); |
1250 | pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str)); | |
36d9e7dc | 1251 | zlog_err("%s: XCALLOC(%zu) failure for IGMP group %s source %s on interface %s", |
12e41d03 DL |
1252 | __PRETTY_FUNCTION__, |
1253 | sizeof(*ij), group_str, source_str, ifp->name); | |
1254 | close(join_fd); | |
1255 | return 0; | |
1256 | } | |
1257 | ||
1258 | ij->sock_fd = join_fd; | |
1259 | ij->group_addr = group_addr; | |
1260 | ij->source_addr = source_addr; | |
1261 | ij->sock_creation = pim_time_monotonic_sec(); | |
1262 | ||
1263 | listnode_add(pim_ifp->igmp_join_list, ij); | |
1264 | ||
1265 | return ij; | |
1266 | } | |
1267 | ||
1268 | int pim_if_igmp_join_add(struct interface *ifp, | |
1269 | struct in_addr group_addr, | |
1270 | struct in_addr source_addr) | |
1271 | { | |
1272 | struct pim_interface *pim_ifp; | |
1273 | struct igmp_join *ij; | |
1274 | ||
1275 | pim_ifp = ifp->info; | |
1276 | if (!pim_ifp) { | |
1277 | zlog_warn("%s: multicast not enabled on interface %s", | |
1278 | __PRETTY_FUNCTION__, | |
1279 | ifp->name); | |
1280 | return -1; | |
1281 | } | |
1282 | ||
1283 | if (!pim_ifp->igmp_join_list) { | |
1284 | pim_ifp->igmp_join_list = list_new(); | |
1285 | if (!pim_ifp->igmp_join_list) { | |
1286 | zlog_err("%s %s: failure: igmp_join_list=list_new()", | |
1287 | __FILE__, __PRETTY_FUNCTION__); | |
1288 | return -2; | |
1289 | } | |
1290 | pim_ifp->igmp_join_list->del = (void (*)(void *)) igmp_join_free; | |
1291 | } | |
1292 | ||
1293 | ij = igmp_join_find(pim_ifp->igmp_join_list, group_addr, source_addr); | |
1294 | if (ij) { | |
eaa54bdb DW |
1295 | char group_str[INET_ADDRSTRLEN]; |
1296 | char source_str[INET_ADDRSTRLEN]; | |
12e41d03 DL |
1297 | pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str)); |
1298 | pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str)); | |
1299 | zlog_warn("%s: can't re-join existing IGMP group %s source %s on interface %s", | |
1300 | __PRETTY_FUNCTION__, | |
1301 | group_str, source_str, ifp->name); | |
1302 | return -3; | |
1303 | } | |
1304 | ||
1305 | ij = igmp_join_new(ifp, group_addr, source_addr); | |
1306 | if (!ij) { | |
eaa54bdb DW |
1307 | char group_str[INET_ADDRSTRLEN]; |
1308 | char source_str[INET_ADDRSTRLEN]; | |
12e41d03 DL |
1309 | pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str)); |
1310 | pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str)); | |
1311 | zlog_warn("%s: igmp_join_new() failure for IGMP group %s source %s on interface %s", | |
1312 | __PRETTY_FUNCTION__, | |
1313 | group_str, source_str, ifp->name); | |
1314 | return -4; | |
1315 | } | |
1316 | ||
7adf0260 | 1317 | if (PIM_DEBUG_IGMP_EVENTS) { |
eaa54bdb DW |
1318 | char group_str[INET_ADDRSTRLEN]; |
1319 | char source_str[INET_ADDRSTRLEN]; | |
12e41d03 DL |
1320 | pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str)); |
1321 | pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str)); | |
1322 | zlog_debug("%s: issued static igmp join for channel (S,G)=(%s,%s) on interface %s", | |
7adf0260 DS |
1323 | __PRETTY_FUNCTION__, |
1324 | source_str, group_str, ifp->name); | |
12e41d03 DL |
1325 | } |
1326 | ||
1327 | return 0; | |
1328 | } | |
1329 | ||
1330 | ||
1331 | ||
1332 | int pim_if_igmp_join_del(struct interface *ifp, | |
1333 | struct in_addr group_addr, | |
1334 | struct in_addr source_addr) | |
1335 | { | |
1336 | struct pim_interface *pim_ifp; | |
1337 | struct igmp_join *ij; | |
1338 | ||
1339 | pim_ifp = ifp->info; | |
1340 | if (!pim_ifp) { | |
1341 | zlog_warn("%s: multicast not enabled on interface %s", | |
1342 | __PRETTY_FUNCTION__, | |
1343 | ifp->name); | |
1344 | return -1; | |
1345 | } | |
1346 | ||
1347 | if (!pim_ifp->igmp_join_list) { | |
1348 | zlog_warn("%s: no IGMP join on interface %s", | |
1349 | __PRETTY_FUNCTION__, | |
1350 | ifp->name); | |
1351 | return -2; | |
1352 | } | |
1353 | ||
1354 | ij = igmp_join_find(pim_ifp->igmp_join_list, group_addr, source_addr); | |
1355 | if (!ij) { | |
eaa54bdb DW |
1356 | char group_str[INET_ADDRSTRLEN]; |
1357 | char source_str[INET_ADDRSTRLEN]; | |
12e41d03 DL |
1358 | pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str)); |
1359 | pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str)); | |
1360 | zlog_warn("%s: could not find IGMP group %s source %s on interface %s", | |
1361 | __PRETTY_FUNCTION__, | |
1362 | group_str, source_str, ifp->name); | |
1363 | return -3; | |
1364 | } | |
1365 | ||
1366 | if (close(ij->sock_fd)) { | |
eaa54bdb DW |
1367 | char group_str[INET_ADDRSTRLEN]; |
1368 | char source_str[INET_ADDRSTRLEN]; | |
12e41d03 DL |
1369 | pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str)); |
1370 | pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str)); | |
1371 | zlog_warn("%s: failure closing sock_fd=%d for IGMP group %s source %s on interface %s: errno=%d: %s", | |
1372 | __PRETTY_FUNCTION__, | |
3d7765d7 | 1373 | ij->sock_fd, group_str, source_str, ifp->name, errno, safe_strerror(errno)); |
12e41d03 DL |
1374 | /* warning only */ |
1375 | } | |
1376 | listnode_delete(pim_ifp->igmp_join_list, ij); | |
1377 | igmp_join_free(ij); | |
1378 | if (listcount(pim_ifp->igmp_join_list) < 1) { | |
1379 | list_delete(pim_ifp->igmp_join_list); | |
1380 | pim_ifp->igmp_join_list = 0; | |
1381 | } | |
1382 | ||
1383 | return 0; | |
1384 | } | |
1385 | ||
1386 | static void pim_if_igmp_join_del_all(struct interface *ifp) | |
1387 | { | |
1388 | struct pim_interface *pim_ifp; | |
1389 | struct listnode *node; | |
1390 | struct listnode *nextnode; | |
1391 | struct igmp_join *ij; | |
1392 | ||
1393 | pim_ifp = ifp->info; | |
1394 | if (!pim_ifp) { | |
1395 | zlog_warn("%s: multicast not enabled on interface %s", | |
1396 | __PRETTY_FUNCTION__, | |
1397 | ifp->name); | |
1398 | return; | |
1399 | } | |
1400 | ||
1401 | if (!pim_ifp->igmp_join_list) | |
1402 | return; | |
1403 | ||
1404 | for (ALL_LIST_ELEMENTS(pim_ifp->igmp_join_list, node, nextnode, ij)) | |
1405 | pim_if_igmp_join_del(ifp, ij->group_addr, ij->source_addr); | |
1406 | } | |
1407 | ||
1408 | /* | |
1409 | RFC 4601 | |
1410 | ||
1411 | Transitions from "I am Assert Loser" State | |
1412 | ||
1413 | Current Winner's GenID Changes or NLT Expires | |
1414 | ||
1415 | The Neighbor Liveness Timer associated with the current winner | |
1416 | expires or we receive a Hello message from the current winner | |
1417 | reporting a different GenID from the one it previously reported. | |
1418 | This indicates that the current winner's interface or router has | |
1419 | gone down (and may have come back up), and so we must assume it no | |
1420 | longer knows it was the winner. | |
1421 | */ | |
1422 | void pim_if_assert_on_neighbor_down(struct interface *ifp, | |
1423 | struct in_addr neigh_addr) | |
1424 | { | |
1425 | struct pim_interface *pim_ifp; | |
1426 | struct listnode *node; | |
1427 | struct listnode *next_node; | |
1428 | struct pim_ifchannel *ch; | |
1429 | ||
1430 | pim_ifp = ifp->info; | |
1431 | zassert(pim_ifp); | |
1432 | ||
1433 | for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) { | |
1434 | /* Is (S,G,I) assert loser ? */ | |
1435 | if (ch->ifassert_state != PIM_IFASSERT_I_AM_LOSER) | |
1436 | continue; | |
1437 | /* Dead neighbor was winner ? */ | |
1438 | if (ch->ifassert_winner.s_addr != neigh_addr.s_addr) | |
1439 | continue; | |
1440 | ||
1441 | assert_action_a5(ch); | |
1442 | } | |
1443 | } | |
1444 | ||
1445 | void pim_if_update_join_desired(struct pim_interface *pim_ifp) | |
1446 | { | |
1447 | struct listnode *ch_node; | |
1448 | struct pim_ifchannel *ch; | |
1449 | ||
1450 | /* clear off flag from interface's upstreams */ | |
1451 | for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) { | |
1452 | PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED_UPDATED(ch->upstream->flags); | |
1453 | } | |
1454 | ||
1455 | /* scan per-interface (S,G,I) state on this I interface */ | |
1456 | for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) { | |
1457 | struct pim_upstream *up = ch->upstream; | |
1458 | ||
1459 | if (PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED_UPDATED(up->flags)) | |
1460 | continue; | |
1461 | ||
1462 | /* update join_desired for the global (S,G) state */ | |
1463 | pim_upstream_update_join_desired(up); | |
1464 | PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED_UPDATED(up->flags); | |
1465 | } | |
1466 | } | |
1467 | ||
1468 | void pim_if_update_assert_tracking_desired(struct interface *ifp) | |
1469 | { | |
1470 | struct pim_interface *pim_ifp; | |
1471 | struct listnode *node; | |
1472 | struct listnode *next_node; | |
1473 | struct pim_ifchannel *ch; | |
1474 | ||
1475 | pim_ifp = ifp->info; | |
1476 | if (!pim_ifp) | |
1477 | return; | |
1478 | ||
1479 | for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) { | |
1480 | pim_ifchannel_update_assert_tracking_desired(ch); | |
1481 | } | |
1482 | } | |
c992c9a0 DS |
1483 | |
1484 | /* | |
1485 | * PIM wants to have an interface pointer for everything it does. | |
1486 | * The pimreg is a special interface that we have that is not | |
1487 | * quite an inteface but a VIF is created for it. | |
1488 | */ | |
1489 | void pim_if_create_pimreg (void) | |
1490 | { | |
1491 | if (!pim_regiface) { | |
1492 | pim_regiface = if_create("pimreg", strlen("pimreg")); | |
1493 | pim_regiface->ifindex = PIM_OIF_PIM_REGISTER_VIF; | |
1494 | ||
1495 | pim_if_new(pim_regiface, 0, 0); | |
1496 | } | |
1497 | } | |
3565202d DS |
1498 | |
1499 | int | |
1500 | pim_if_connected_to_source (struct interface *ifp, struct in_addr src) | |
1501 | { | |
1502 | struct listnode *cnode; | |
1503 | struct connected *c; | |
1504 | struct prefix p; | |
1505 | ||
1506 | p.family = AF_INET; | |
1507 | p.u.prefix4 = src; | |
1508 | p.prefixlen = IPV4_MAX_BITLEN; | |
1509 | ||
1510 | for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, c)) | |
1511 | { | |
1512 | if ((c->address->family == AF_INET) && | |
1513 | prefix_match (CONNECTED_PREFIX (c), &p)) | |
1514 | { | |
1515 | return 1; | |
1516 | } | |
1517 | } | |
1518 | ||
1519 | return 0; | |
1520 | } | |
9f0edbc9 DS |
1521 | |
1522 | struct interface * | |
1523 | pim_if_lookup_address_vrf (struct in_addr src, vrf_id_t vrf_id) | |
1524 | { | |
1525 | struct listnode *ifnode; | |
1526 | struct interface *ifp; | |
1527 | ||
1528 | for (ALL_LIST_ELEMENTS_RO (vrf_iflist(vrf_id), ifnode, ifp)) | |
1529 | { | |
1530 | if (pim_if_connected_to_source (ifp, src) && ifp->info) | |
1531 | return ifp; | |
1532 | } | |
1533 | return NULL; | |
1534 | } |