]>
Commit | Line | Data |
---|---|---|
12e41d03 DL |
1 | /* |
2 | PIM for Quagga | |
3 | Copyright (C) 2008 Everton da Silva Marques | |
4 | ||
5 | This program is free software; you can redistribute it and/or modify | |
6 | it under the terms of the GNU General Public License as published by | |
7 | the Free Software Foundation; either version 2 of the License, or | |
8 | (at your option) any later version. | |
9 | ||
10 | This program is distributed in the hope that it will be useful, but | |
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU General Public License | |
16 | along with this program; see the file COPYING; if not, write to the | |
17 | Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, | |
18 | MA 02110-1301 USA | |
19 | ||
20 | $QuaggaId: $Format:%an, %ai, %h$ $ | |
21 | */ | |
22 | ||
23 | #include <zebra.h> | |
24 | ||
25 | #include "linklist.h" | |
26 | #include "thread.h" | |
27 | #include "memory.h" | |
744d91b3 | 28 | #include "if.h" |
12e41d03 DL |
29 | |
30 | #include "pimd.h" | |
31 | #include "pim_str.h" | |
32 | #include "pim_iface.h" | |
33 | #include "pim_ifchannel.h" | |
34 | #include "pim_zebra.h" | |
35 | #include "pim_time.h" | |
36 | #include "pim_msg.h" | |
37 | #include "pim_pim.h" | |
38 | #include "pim_join.h" | |
39 | #include "pim_rpf.h" | |
40 | #include "pim_macro.h" | |
41 | ||
42 | void pim_ifchannel_free(struct pim_ifchannel *ch) | |
43 | { | |
44 | zassert(!ch->t_ifjoin_expiry_timer); | |
45 | zassert(!ch->t_ifjoin_prune_pending_timer); | |
46 | zassert(!ch->t_ifassert_timer); | |
47 | ||
48 | XFREE(MTYPE_PIM_IFCHANNEL, ch); | |
49 | } | |
50 | ||
51 | void pim_ifchannel_delete(struct pim_ifchannel *ch) | |
52 | { | |
53 | struct pim_interface *pim_ifp; | |
54 | ||
55 | pim_ifp = ch->interface->info; | |
56 | zassert(pim_ifp); | |
57 | ||
58 | if (ch->ifjoin_state != PIM_IFJOIN_NOINFO) { | |
59 | pim_upstream_update_join_desired(ch->upstream); | |
60 | } | |
61 | ||
62 | pim_upstream_del(ch->upstream); | |
63 | ||
64 | THREAD_OFF(ch->t_ifjoin_expiry_timer); | |
65 | THREAD_OFF(ch->t_ifjoin_prune_pending_timer); | |
66 | THREAD_OFF(ch->t_ifassert_timer); | |
67 | ||
68 | /* | |
69 | notice that listnode_delete() can't be moved | |
70 | into pim_ifchannel_free() because the later is | |
71 | called by list_delete_all_node() | |
72 | */ | |
73 | listnode_delete(pim_ifp->pim_ifchannel_list, ch); | |
74 | ||
75 | pim_ifchannel_free(ch); | |
76 | } | |
77 | ||
78 | #define IFCHANNEL_NOINFO(ch) \ | |
79 | ( \ | |
80 | ((ch)->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO) \ | |
81 | && \ | |
82 | ((ch)->ifjoin_state == PIM_IFJOIN_NOINFO) \ | |
83 | && \ | |
84 | ((ch)->ifassert_state == PIM_IFASSERT_NOINFO) \ | |
85 | ) | |
86 | ||
87 | static void delete_on_noinfo(struct pim_ifchannel *ch) | |
88 | { | |
89 | if (IFCHANNEL_NOINFO(ch)) { | |
90 | ||
91 | /* In NOINFO state, timers should have been cleared */ | |
92 | zassert(!ch->t_ifjoin_expiry_timer); | |
93 | zassert(!ch->t_ifjoin_prune_pending_timer); | |
94 | zassert(!ch->t_ifassert_timer); | |
95 | ||
96 | pim_ifchannel_delete(ch); | |
97 | } | |
98 | } | |
99 | ||
100 | void pim_ifchannel_ifjoin_switch(const char *caller, | |
101 | struct pim_ifchannel *ch, | |
102 | enum pim_ifjoin_state new_state) | |
103 | { | |
104 | enum pim_ifjoin_state old_state = ch->ifjoin_state; | |
105 | ||
106 | if (old_state == new_state) { | |
7adf0260 DS |
107 | if (PIM_DEBUG_PIM_EVENTS) { |
108 | zlog_debug("%s calledby %s: non-transition on state %d (%s)", | |
109 | __PRETTY_FUNCTION__, caller, new_state, | |
110 | pim_ifchannel_ifjoin_name(new_state)); | |
111 | } | |
12e41d03 DL |
112 | return; |
113 | } | |
114 | ||
115 | zassert(old_state != new_state); | |
116 | ||
117 | ch->ifjoin_state = new_state; | |
118 | ||
119 | /* Transition to/from NOINFO ? */ | |
120 | if ( | |
121 | (old_state == PIM_IFJOIN_NOINFO) | |
122 | || | |
123 | (new_state == PIM_IFJOIN_NOINFO) | |
124 | ) { | |
125 | ||
126 | if (PIM_DEBUG_PIM_EVENTS) { | |
ee1a477a | 127 | zlog_debug("PIM_IFCHANNEL_%s: (S,G)=%s on interface %s", |
12e41d03 | 128 | ((new_state == PIM_IFJOIN_NOINFO) ? "DOWN" : "UP"), |
ee1a477a | 129 | pim_str_sg_dump (&ch->sg), ch->interface->name); |
12e41d03 DL |
130 | } |
131 | ||
132 | /* | |
133 | Record uptime of state transition to/from NOINFO | |
134 | */ | |
135 | ch->ifjoin_creation = pim_time_monotonic_sec(); | |
136 | ||
137 | pim_upstream_update_join_desired(ch->upstream); | |
138 | pim_ifchannel_update_could_assert(ch); | |
139 | pim_ifchannel_update_assert_tracking_desired(ch); | |
140 | } | |
141 | } | |
142 | ||
143 | const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state) | |
144 | { | |
145 | switch (ifjoin_state) { | |
146 | case PIM_IFJOIN_NOINFO: return "NOINFO"; | |
56638739 | 147 | case PIM_IFJOIN_JOIN_PIMREG: return "REGT"; |
12e41d03 DL |
148 | case PIM_IFJOIN_JOIN: return "JOIN"; |
149 | case PIM_IFJOIN_PRUNE_PENDING: return "PRUNEP"; | |
150 | } | |
151 | ||
152 | return "ifjoin_bad_state"; | |
153 | } | |
154 | ||
155 | const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state) | |
156 | { | |
157 | switch (ifassert_state) { | |
158 | case PIM_IFASSERT_NOINFO: return "NOINFO"; | |
159 | case PIM_IFASSERT_I_AM_WINNER: return "WINNER"; | |
160 | case PIM_IFASSERT_I_AM_LOSER: return "LOSER"; | |
161 | } | |
162 | ||
163 | return "ifassert_bad_state"; | |
164 | } | |
165 | ||
166 | /* | |
167 | RFC 4601: 4.6.5. Assert State Macros | |
168 | ||
169 | AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I) | |
170 | defaults to Infinity when in the NoInfo state. | |
171 | */ | |
172 | void reset_ifassert_state(struct pim_ifchannel *ch) | |
173 | { | |
174 | THREAD_OFF(ch->t_ifassert_timer); | |
175 | ||
176 | pim_ifassert_winner_set(ch, | |
177 | PIM_IFASSERT_NOINFO, | |
178 | qpim_inaddr_any, | |
179 | qpim_infinite_assert_metric); | |
180 | } | |
181 | ||
12e41d03 | 182 | struct pim_ifchannel *pim_ifchannel_find(struct interface *ifp, |
5074a423 | 183 | struct prefix *sg) |
12e41d03 DL |
184 | { |
185 | struct pim_interface *pim_ifp; | |
186 | struct listnode *ch_node; | |
187 | struct pim_ifchannel *ch; | |
188 | ||
189 | zassert(ifp); | |
190 | ||
191 | pim_ifp = ifp->info; | |
192 | ||
193 | if (!pim_ifp) { | |
5074a423 | 194 | zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s", |
12e41d03 | 195 | __PRETTY_FUNCTION__, |
5074a423 | 196 | pim_str_sg_dump (sg), |
12e41d03 DL |
197 | ifp->name); |
198 | return 0; | |
199 | } | |
200 | ||
201 | for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) { | |
202 | if ( | |
5074a423 DS |
203 | (sg->u.sg.src.s_addr == ch->sg.u.sg.src.s_addr) && |
204 | (sg->u.sg.grp.s_addr == ch->sg.u.sg.grp.s_addr) | |
12e41d03 DL |
205 | ) { |
206 | return ch; | |
207 | } | |
208 | } | |
209 | ||
210 | return 0; | |
211 | } | |
212 | ||
213 | static void ifmembership_set(struct pim_ifchannel *ch, | |
214 | enum pim_ifmembership membership) | |
215 | { | |
216 | if (ch->local_ifmembership == membership) | |
217 | return; | |
218 | ||
7adf0260 | 219 | if (PIM_DEBUG_PIM_EVENTS) { |
ee1a477a | 220 | zlog_debug("%s: (S,G)=%s membership now is %s on interface %s", |
12e41d03 | 221 | __PRETTY_FUNCTION__, |
ee1a477a | 222 | pim_str_sg_dump (&ch->sg), |
12e41d03 DL |
223 | membership == PIM_IFMEMBERSHIP_INCLUDE ? "INCLUDE" : "NOINFO", |
224 | ch->interface->name); | |
225 | } | |
226 | ||
227 | ch->local_ifmembership = membership; | |
228 | ||
229 | pim_upstream_update_join_desired(ch->upstream); | |
230 | pim_ifchannel_update_could_assert(ch); | |
231 | pim_ifchannel_update_assert_tracking_desired(ch); | |
232 | } | |
233 | ||
234 | ||
235 | void pim_ifchannel_membership_clear(struct interface *ifp) | |
236 | { | |
237 | struct pim_interface *pim_ifp; | |
238 | struct listnode *ch_node; | |
239 | struct pim_ifchannel *ch; | |
240 | ||
241 | pim_ifp = ifp->info; | |
242 | zassert(pim_ifp); | |
243 | ||
244 | for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) { | |
245 | ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO); | |
246 | } | |
247 | } | |
248 | ||
249 | void pim_ifchannel_delete_on_noinfo(struct interface *ifp) | |
250 | { | |
251 | struct pim_interface *pim_ifp; | |
252 | struct listnode *node; | |
253 | struct listnode *next_node; | |
254 | struct pim_ifchannel *ch; | |
255 | ||
256 | pim_ifp = ifp->info; | |
257 | zassert(pim_ifp); | |
258 | ||
259 | for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) { | |
260 | delete_on_noinfo(ch); | |
261 | } | |
262 | } | |
263 | ||
264 | struct pim_ifchannel *pim_ifchannel_add(struct interface *ifp, | |
5074a423 | 265 | struct prefix *sg) |
12e41d03 | 266 | { |
535db05b | 267 | struct pim_interface *pim_ifp; |
12e41d03 | 268 | struct pim_ifchannel *ch; |
535db05b | 269 | struct pim_upstream *up; |
12e41d03 | 270 | |
5074a423 | 271 | ch = pim_ifchannel_find(ifp, sg); |
12e41d03 DL |
272 | if (ch) |
273 | return ch; | |
274 | ||
535db05b DS |
275 | pim_ifp = ifp->info; |
276 | zassert(pim_ifp); | |
277 | ||
5074a423 | 278 | up = pim_upstream_add(sg, NULL); |
535db05b | 279 | if (!up) { |
5074a423 | 280 | zlog_err("%s: could not attach upstream (S,G)=%s on interface %s", |
535db05b | 281 | __PRETTY_FUNCTION__, |
5074a423 | 282 | pim_str_sg_dump (sg), ifp->name); |
535db05b DS |
283 | return NULL; |
284 | } | |
285 | ||
286 | ch = XMALLOC(MTYPE_PIM_IFCHANNEL, sizeof(*ch)); | |
287 | if (!ch) { | |
5074a423 | 288 | zlog_warn("%s: pim_ifchannel_new() failure for (S,G)=%s on interface %s", |
535db05b | 289 | __PRETTY_FUNCTION__, |
5074a423 | 290 | pim_str_sg_dump (sg), ifp->name); |
12e41d03 | 291 | |
535db05b DS |
292 | return NULL; |
293 | } | |
12e41d03 | 294 | |
535db05b DS |
295 | ch->flags = 0; |
296 | ch->upstream = up; | |
297 | ch->interface = ifp; | |
5074a423 | 298 | ch->sg = *sg; |
535db05b DS |
299 | ch->local_ifmembership = PIM_IFMEMBERSHIP_NOINFO; |
300 | ||
301 | ch->ifjoin_state = PIM_IFJOIN_NOINFO; | |
302 | ch->t_ifjoin_expiry_timer = NULL; | |
303 | ch->t_ifjoin_prune_pending_timer = NULL; | |
304 | ch->ifjoin_creation = 0; | |
305 | ||
306 | ch->ifassert_my_metric = pim_macro_ch_my_assert_metric_eval(ch); | |
307 | ch->ifassert_winner_metric = pim_macro_ch_my_assert_metric_eval (ch); | |
308 | ||
309 | ch->ifassert_winner.s_addr = 0; | |
310 | ||
311 | /* Assert state */ | |
312 | ch->t_ifassert_timer = NULL; | |
313 | reset_ifassert_state(ch); | |
314 | if (pim_macro_ch_could_assert_eval(ch)) | |
315 | PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags); | |
316 | else | |
317 | PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags); | |
318 | ||
319 | if (pim_macro_assert_tracking_desired_eval(ch)) | |
320 | PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags); | |
321 | else | |
322 | PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags); | |
323 | ||
324 | /* Attach to list */ | |
325 | listnode_add(pim_ifp->pim_ifchannel_list, ch); | |
326 | ||
327 | zassert(IFCHANNEL_NOINFO(ch)); | |
328 | ||
329 | return ch; | |
12e41d03 DL |
330 | } |
331 | ||
332 | static void ifjoin_to_noinfo(struct pim_ifchannel *ch) | |
333 | { | |
334 | pim_forward_stop(ch); | |
335 | pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_NOINFO); | |
336 | delete_on_noinfo(ch); | |
337 | } | |
338 | ||
339 | static int on_ifjoin_expiry_timer(struct thread *t) | |
340 | { | |
341 | struct pim_ifchannel *ch; | |
342 | ||
343 | zassert(t); | |
344 | ch = THREAD_ARG(t); | |
345 | zassert(ch); | |
346 | ||
59ba0ac3 | 347 | ch->t_ifjoin_expiry_timer = NULL; |
12e41d03 DL |
348 | |
349 | zassert(ch->ifjoin_state == PIM_IFJOIN_JOIN); | |
350 | ||
351 | ifjoin_to_noinfo(ch); | |
352 | /* ch may have been deleted */ | |
353 | ||
354 | return 0; | |
355 | } | |
356 | ||
12e41d03 DL |
357 | static int on_ifjoin_prune_pending_timer(struct thread *t) |
358 | { | |
359 | struct pim_ifchannel *ch; | |
360 | int send_prune_echo; /* boolean */ | |
361 | struct interface *ifp; | |
362 | struct pim_interface *pim_ifp; | |
05e451f8 | 363 | struct prefix sg; |
12e41d03 DL |
364 | |
365 | zassert(t); | |
366 | ch = THREAD_ARG(t); | |
367 | zassert(ch); | |
368 | ||
59ba0ac3 | 369 | ch->t_ifjoin_prune_pending_timer = NULL; |
12e41d03 DL |
370 | |
371 | zassert(ch->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING); | |
372 | ||
373 | /* Send PruneEcho(S,G) ? */ | |
374 | ifp = ch->interface; | |
375 | pim_ifp = ifp->info; | |
376 | send_prune_echo = (listcount(pim_ifp->pim_neighbor_list) > 1); | |
377 | ||
378 | /* Save (S,G) */ | |
05e451f8 | 379 | sg = ch->sg; |
12e41d03 DL |
380 | |
381 | ifjoin_to_noinfo(ch); | |
382 | /* from here ch may have been deleted */ | |
383 | ||
384 | if (send_prune_echo) | |
64a70f46 | 385 | pim_joinprune_send (ifp, pim_ifp->primary_address, |
05e451f8 | 386 | &sg, 0); |
12e41d03 DL |
387 | |
388 | return 0; | |
389 | } | |
390 | ||
391 | static void check_recv_upstream(int is_join, | |
392 | struct interface *recv_ifp, | |
393 | struct in_addr upstream, | |
394 | struct in_addr source_addr, | |
395 | struct in_addr group_addr, | |
396 | uint8_t source_flags, | |
397 | int holdtime) | |
398 | { | |
399 | struct pim_upstream *up; | |
5074a423 | 400 | struct prefix sg; |
12e41d03 | 401 | |
5074a423 DS |
402 | memset (&sg, 0, sizeof (struct prefix)); |
403 | sg.u.sg.src = source_addr; | |
404 | sg.u.sg.grp = group_addr; | |
12e41d03 | 405 | /* Upstream (S,G) in Joined state ? */ |
5074a423 | 406 | up = pim_upstream_find(&sg); |
12e41d03 DL |
407 | if (!up) |
408 | return; | |
409 | if (up->join_state != PIM_UPSTREAM_JOINED) | |
410 | return; | |
411 | ||
412 | /* Upstream (S,G) in Joined state */ | |
413 | ||
414 | if (PIM_INADDR_IS_ANY(up->rpf.rpf_addr)) { | |
415 | /* RPF'(S,G) not found */ | |
416 | char src_str[100]; | |
417 | char grp_str[100]; | |
418 | pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str)); | |
419 | pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str)); | |
420 | zlog_warn("%s %s: RPF'(%s,%s) not found", | |
421 | __FILE__, __PRETTY_FUNCTION__, | |
422 | src_str, grp_str); | |
423 | return; | |
424 | } | |
425 | ||
426 | /* upstream directed to RPF'(S,G) ? */ | |
427 | if (upstream.s_addr != up->rpf.rpf_addr.s_addr) { | |
428 | char src_str[100]; | |
429 | char grp_str[100]; | |
430 | char up_str[100]; | |
431 | char rpf_str[100]; | |
432 | pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str)); | |
433 | pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str)); | |
434 | pim_inet4_dump("<up?>", upstream, up_str, sizeof(up_str)); | |
435 | pim_inet4_dump("<rpf?>", up->rpf.rpf_addr, rpf_str, sizeof(rpf_str)); | |
436 | zlog_warn("%s %s: (S,G)=(%s,%s) upstream=%s not directed to RPF'(S,G)=%s on interface %s", | |
437 | __FILE__, __PRETTY_FUNCTION__, | |
438 | src_str, grp_str, | |
439 | up_str, rpf_str, recv_ifp->name); | |
440 | return; | |
441 | } | |
442 | /* upstream directed to RPF'(S,G) */ | |
443 | ||
444 | if (is_join) { | |
445 | /* Join(S,G) to RPF'(S,G) */ | |
446 | pim_upstream_join_suppress(up, up->rpf.rpf_addr, holdtime); | |
447 | return; | |
448 | } | |
449 | ||
450 | /* Prune to RPF'(S,G) */ | |
451 | ||
452 | if (source_flags & PIM_RPT_BIT_MASK) { | |
453 | if (source_flags & PIM_WILDCARD_BIT_MASK) { | |
454 | /* Prune(*,G) to RPF'(S,G) */ | |
455 | pim_upstream_join_timer_decrease_to_t_override("Prune(*,G)", | |
456 | up, up->rpf.rpf_addr); | |
457 | return; | |
458 | } | |
459 | ||
460 | /* Prune(S,G,rpt) to RPF'(S,G) */ | |
461 | pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)", | |
462 | up, up->rpf.rpf_addr); | |
463 | return; | |
464 | } | |
465 | ||
466 | /* Prune(S,G) to RPF'(S,G) */ | |
467 | pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up, | |
468 | up->rpf.rpf_addr); | |
469 | } | |
470 | ||
471 | static int nonlocal_upstream(int is_join, | |
472 | struct interface *recv_ifp, | |
473 | struct in_addr upstream, | |
474 | struct in_addr source_addr, | |
475 | struct in_addr group_addr, | |
476 | uint8_t source_flags, | |
477 | uint16_t holdtime) | |
478 | { | |
479 | struct pim_interface *recv_pim_ifp; | |
480 | int is_local; /* boolean */ | |
481 | ||
482 | recv_pim_ifp = recv_ifp->info; | |
483 | zassert(recv_pim_ifp); | |
484 | ||
485 | is_local = (upstream.s_addr == recv_pim_ifp->primary_address.s_addr); | |
486 | ||
487 | if (PIM_DEBUG_PIM_TRACE) { | |
488 | char up_str[100]; | |
489 | char src_str[100]; | |
490 | char grp_str[100]; | |
491 | pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str)); | |
492 | pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str)); | |
493 | pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str)); | |
494 | zlog_warn("%s: recv %s (S,G)=(%s,%s) to %s upstream=%s on %s", | |
495 | __PRETTY_FUNCTION__, | |
496 | is_join ? "join" : "prune", | |
497 | src_str, grp_str, | |
498 | is_local ? "local" : "non-local", | |
499 | up_str, recv_ifp->name); | |
500 | } | |
501 | ||
502 | if (is_local) | |
503 | return 0; | |
504 | ||
505 | /* | |
506 | Since recv upstream addr was not directed to our primary | |
507 | address, check if we should react to it in any way. | |
508 | */ | |
509 | check_recv_upstream(is_join, recv_ifp, upstream, source_addr, group_addr, | |
510 | source_flags, holdtime); | |
511 | ||
512 | return 1; /* non-local */ | |
513 | } | |
514 | ||
515 | void pim_ifchannel_join_add(struct interface *ifp, | |
516 | struct in_addr neigh_addr, | |
517 | struct in_addr upstream, | |
ee1a477a | 518 | struct prefix *sg, |
12e41d03 DL |
519 | uint8_t source_flags, |
520 | uint16_t holdtime) | |
521 | { | |
522 | struct pim_interface *pim_ifp; | |
523 | struct pim_ifchannel *ch; | |
524 | ||
525 | if (nonlocal_upstream(1 /* join */, ifp, upstream, | |
ee1a477a | 526 | sg->u.sg.src, sg->u.sg.grp, source_flags, holdtime)) { |
12e41d03 DL |
527 | return; |
528 | } | |
529 | ||
ee1a477a | 530 | ch = pim_ifchannel_add(ifp, sg); |
12e41d03 DL |
531 | if (!ch) |
532 | return; | |
533 | ||
534 | /* | |
535 | RFC 4601: 4.6.1. (S,G) Assert Message State Machine | |
536 | ||
537 | Transitions from "I am Assert Loser" State | |
538 | ||
539 | Receive Join(S,G) on Interface I | |
540 | ||
541 | We receive a Join(S,G) that has the Upstream Neighbor Address | |
542 | field set to my primary IP address on interface I. The action is | |
543 | to transition to NoInfo state, delete this (S,G) assert state | |
544 | (Actions A5 below), and allow the normal PIM Join/Prune mechanisms | |
545 | to operate. | |
546 | ||
547 | Notice: The nonlocal_upstream() test above ensures the upstream | |
548 | address of the join message is our primary address. | |
549 | */ | |
550 | if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) { | |
12e41d03 | 551 | char neigh_str[100]; |
12e41d03 | 552 | pim_inet4_dump("<neigh?>", neigh_addr, neigh_str, sizeof(neigh_str)); |
5074a423 | 553 | zlog_warn("%s: Assert Loser recv Join%s from %s on %s", |
12e41d03 | 554 | __PRETTY_FUNCTION__, |
ee1a477a | 555 | pim_str_sg_dump (sg), neigh_str, ifp->name); |
12e41d03 DL |
556 | |
557 | assert_action_a5(ch); | |
558 | } | |
559 | ||
560 | pim_ifp = ifp->info; | |
561 | zassert(pim_ifp); | |
562 | ||
563 | switch (ch->ifjoin_state) { | |
564 | case PIM_IFJOIN_NOINFO: | |
565 | pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN); | |
566 | if (pim_macro_chisin_oiflist(ch)) { | |
567 | pim_forward_start(ch); | |
568 | } | |
569 | break; | |
570 | case PIM_IFJOIN_JOIN: | |
571 | zassert(!ch->t_ifjoin_prune_pending_timer); | |
572 | ||
573 | /* | |
574 | In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to a | |
575 | previously received join message with holdtime=0xFFFF. | |
576 | */ | |
577 | if (ch->t_ifjoin_expiry_timer) { | |
578 | unsigned long remain = | |
579 | thread_timer_remain_second(ch->t_ifjoin_expiry_timer); | |
580 | if (remain > holdtime) { | |
581 | /* | |
582 | RFC 4601: 4.5.3. Receiving (S,G) Join/Prune Messages | |
583 | ||
584 | Transitions from Join State | |
585 | ||
586 | The (S,G) downstream state machine on interface I remains in | |
587 | Join state, and the Expiry Timer (ET) is restarted, set to | |
588 | maximum of its current value and the HoldTime from the | |
589 | triggering Join/Prune message. | |
590 | ||
591 | Conclusion: Do not change the ET if the current value is | |
592 | higher than the received join holdtime. | |
593 | */ | |
594 | return; | |
595 | } | |
596 | } | |
597 | THREAD_OFF(ch->t_ifjoin_expiry_timer); | |
598 | break; | |
599 | case PIM_IFJOIN_PRUNE_PENDING: | |
600 | zassert(!ch->t_ifjoin_expiry_timer); | |
601 | zassert(ch->t_ifjoin_prune_pending_timer); | |
602 | THREAD_OFF(ch->t_ifjoin_prune_pending_timer); | |
603 | pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN); | |
604 | break; | |
56638739 DS |
605 | case PIM_IFJOIN_JOIN_PIMREG: |
606 | zlog_warn("Received Incorrect new state"); | |
12e41d03 DL |
607 | } |
608 | ||
609 | zassert(!IFCHANNEL_NOINFO(ch)); | |
610 | ||
611 | if (holdtime != 0xFFFF) { | |
612 | THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer, | |
613 | on_ifjoin_expiry_timer, | |
614 | ch, holdtime); | |
615 | } | |
616 | } | |
617 | ||
618 | void pim_ifchannel_prune(struct interface *ifp, | |
619 | struct in_addr upstream, | |
7bd2c6fa | 620 | struct prefix *sg, |
12e41d03 DL |
621 | uint8_t source_flags, |
622 | uint16_t holdtime) | |
623 | { | |
624 | struct pim_ifchannel *ch; | |
625 | int jp_override_interval_msec; | |
626 | ||
627 | if (nonlocal_upstream(0 /* prune */, ifp, upstream, | |
7bd2c6fa | 628 | sg->u.sg.src, sg->u.sg.grp, source_flags, holdtime)) { |
12e41d03 DL |
629 | return; |
630 | } | |
631 | ||
7bd2c6fa | 632 | ch = pim_ifchannel_add(ifp, sg); |
12e41d03 DL |
633 | if (!ch) |
634 | return; | |
635 | ||
636 | switch (ch->ifjoin_state) { | |
637 | case PIM_IFJOIN_NOINFO: | |
638 | case PIM_IFJOIN_PRUNE_PENDING: | |
56638739 | 639 | case PIM_IFJOIN_JOIN_PIMREG: |
12e41d03 DL |
640 | /* nothing to do */ |
641 | break; | |
642 | case PIM_IFJOIN_JOIN: | |
643 | { | |
644 | struct pim_interface *pim_ifp; | |
645 | ||
646 | pim_ifp = ifp->info; | |
647 | ||
648 | zassert(ch->t_ifjoin_expiry_timer); | |
649 | zassert(!ch->t_ifjoin_prune_pending_timer); | |
650 | ||
651 | THREAD_OFF(ch->t_ifjoin_expiry_timer); | |
652 | ||
653 | pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_PRUNE_PENDING); | |
654 | ||
655 | if (listcount(pim_ifp->pim_neighbor_list) > 1) { | |
656 | jp_override_interval_msec = pim_if_jp_override_interval_msec(ifp); | |
657 | } | |
658 | else { | |
659 | jp_override_interval_msec = 0; /* schedule to expire immediately */ | |
660 | /* If we called ifjoin_prune() directly instead, care should | |
661 | be taken not to use "ch" afterwards since it would be | |
662 | deleted. */ | |
663 | } | |
664 | ||
665 | THREAD_TIMER_MSEC_ON(master, ch->t_ifjoin_prune_pending_timer, | |
666 | on_ifjoin_prune_pending_timer, | |
667 | ch, jp_override_interval_msec); | |
668 | ||
669 | zassert(!ch->t_ifjoin_expiry_timer); | |
670 | zassert(ch->t_ifjoin_prune_pending_timer); | |
671 | } | |
672 | break; | |
673 | } | |
674 | ||
675 | } | |
676 | ||
677 | void pim_ifchannel_local_membership_add(struct interface *ifp, | |
69283639 | 678 | struct prefix *sg) |
12e41d03 DL |
679 | { |
680 | struct pim_ifchannel *ch; | |
681 | struct pim_interface *pim_ifp; | |
682 | ||
683 | /* PIM enabled on interface? */ | |
684 | pim_ifp = ifp->info; | |
685 | if (!pim_ifp) | |
686 | return; | |
687 | if (!PIM_IF_TEST_PIM(pim_ifp->options)) | |
688 | return; | |
689 | ||
69283639 | 690 | ch = pim_ifchannel_add(ifp, sg); |
12e41d03 DL |
691 | if (!ch) { |
692 | return; | |
693 | } | |
694 | ||
695 | ifmembership_set(ch, PIM_IFMEMBERSHIP_INCLUDE); | |
696 | ||
697 | zassert(!IFCHANNEL_NOINFO(ch)); | |
698 | } | |
699 | ||
700 | void pim_ifchannel_local_membership_del(struct interface *ifp, | |
701 | struct in_addr source_addr, | |
702 | struct in_addr group_addr) | |
703 | { | |
704 | struct pim_ifchannel *ch; | |
705 | struct pim_interface *pim_ifp; | |
5074a423 DS |
706 | struct prefix sg; |
707 | ||
708 | memset (&sg, 0, sizeof (struct prefix)); | |
709 | sg.u.sg.src = source_addr; | |
710 | sg.u.sg.grp = group_addr; | |
12e41d03 DL |
711 | |
712 | /* PIM enabled on interface? */ | |
713 | pim_ifp = ifp->info; | |
714 | if (!pim_ifp) | |
715 | return; | |
716 | if (!PIM_IF_TEST_PIM(pim_ifp->options)) | |
717 | return; | |
718 | ||
5074a423 | 719 | ch = pim_ifchannel_find(ifp, &sg); |
12e41d03 DL |
720 | if (!ch) |
721 | return; | |
722 | ||
723 | ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO); | |
724 | ||
725 | delete_on_noinfo(ch); | |
726 | } | |
727 | ||
728 | void pim_ifchannel_update_could_assert(struct pim_ifchannel *ch) | |
729 | { | |
730 | int old_couldassert = PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)); | |
731 | int new_couldassert = PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch)); | |
732 | ||
733 | if (new_couldassert == old_couldassert) | |
734 | return; | |
735 | ||
736 | if (PIM_DEBUG_PIM_EVENTS) { | |
737 | char src_str[100]; | |
738 | char grp_str[100]; | |
99064df9 DS |
739 | pim_inet4_dump("<src?>", ch->sg.u.sg.src, src_str, sizeof(src_str)); |
740 | pim_inet4_dump("<grp?>", ch->sg.u.sg.grp, grp_str, sizeof(grp_str)); | |
12e41d03 DL |
741 | zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d", |
742 | __PRETTY_FUNCTION__, | |
743 | src_str, grp_str, ch->interface->name, | |
744 | old_couldassert, new_couldassert); | |
745 | } | |
746 | ||
747 | if (new_couldassert) { | |
748 | /* CouldAssert(S,G,I) switched from FALSE to TRUE */ | |
749 | PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags); | |
750 | } | |
751 | else { | |
752 | /* CouldAssert(S,G,I) switched from TRUE to FALSE */ | |
753 | PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags); | |
754 | ||
755 | if (ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER) { | |
756 | assert_action_a4(ch); | |
757 | } | |
758 | } | |
759 | ||
760 | pim_ifchannel_update_my_assert_metric(ch); | |
761 | } | |
762 | ||
763 | /* | |
764 | my_assert_metric may be affected by: | |
765 | ||
766 | CouldAssert(S,G) | |
767 | pim_ifp->primary_address | |
768 | rpf->source_nexthop.mrib_metric_preference; | |
769 | rpf->source_nexthop.mrib_route_metric; | |
770 | */ | |
771 | void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel *ch) | |
772 | { | |
773 | struct pim_assert_metric my_metric_new = pim_macro_ch_my_assert_metric_eval(ch); | |
774 | ||
775 | if (pim_assert_metric_match(&my_metric_new, &ch->ifassert_my_metric)) | |
776 | return; | |
777 | ||
778 | if (PIM_DEBUG_PIM_EVENTS) { | |
779 | char src_str[100]; | |
780 | char grp_str[100]; | |
781 | char old_addr_str[100]; | |
782 | char new_addr_str[100]; | |
99064df9 DS |
783 | pim_inet4_dump("<src?>", ch->sg.u.sg.src, src_str, sizeof(src_str)); |
784 | pim_inet4_dump("<grp?>", ch->sg.u.sg.grp, grp_str, sizeof(grp_str)); | |
12e41d03 DL |
785 | pim_inet4_dump("<old_addr?>", ch->ifassert_my_metric.ip_address, old_addr_str, sizeof(old_addr_str)); |
786 | pim_inet4_dump("<new_addr?>", my_metric_new.ip_address, new_addr_str, sizeof(new_addr_str)); | |
787 | zlog_debug("%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s", | |
788 | __PRETTY_FUNCTION__, | |
789 | src_str, grp_str, ch->interface->name, | |
790 | ch->ifassert_my_metric.rpt_bit_flag, | |
791 | ch->ifassert_my_metric.metric_preference, | |
792 | ch->ifassert_my_metric.route_metric, | |
793 | old_addr_str, | |
794 | my_metric_new.rpt_bit_flag, | |
795 | my_metric_new.metric_preference, | |
796 | my_metric_new.route_metric, | |
797 | new_addr_str); | |
798 | } | |
799 | ||
800 | ch->ifassert_my_metric = my_metric_new; | |
801 | ||
802 | if (pim_assert_metric_better(&ch->ifassert_my_metric, | |
803 | &ch->ifassert_winner_metric)) { | |
804 | assert_action_a5(ch); | |
805 | } | |
806 | } | |
807 | ||
808 | void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch) | |
809 | { | |
810 | int old_atd = PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags)); | |
811 | int new_atd = PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch)); | |
812 | ||
813 | if (new_atd == old_atd) | |
814 | return; | |
815 | ||
816 | if (PIM_DEBUG_PIM_EVENTS) { | |
817 | char src_str[100]; | |
818 | char grp_str[100]; | |
99064df9 DS |
819 | pim_inet4_dump("<src?>", ch->sg.u.sg.src, src_str, sizeof(src_str)); |
820 | pim_inet4_dump("<grp?>", ch->sg.u.sg.grp, grp_str, sizeof(grp_str)); | |
12e41d03 DL |
821 | zlog_debug("%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d", |
822 | __PRETTY_FUNCTION__, | |
823 | src_str, grp_str, ch->interface->name, | |
824 | old_atd, new_atd); | |
825 | } | |
826 | ||
827 | if (new_atd) { | |
828 | /* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */ | |
829 | PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags); | |
830 | } | |
831 | else { | |
832 | /* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */ | |
833 | PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags); | |
834 | ||
835 | if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) { | |
836 | assert_action_a5(ch); | |
837 | } | |
838 | } | |
839 | } |