Line | Count | Source (jump to first uncovered line) |
1 | | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | | /* |
3 | | * PIM for Quagga |
4 | | * Copyright (C) 2008 Everton da Silva Marques |
5 | | */ |
6 | | |
7 | | #include <zebra.h> |
8 | | |
9 | | #include "log.h" |
10 | | #include "memory.h" |
11 | | #include "linklist.h" |
12 | | #include "if.h" |
13 | | #include "hash.h" |
14 | | #include "jhash.h" |
15 | | |
16 | | #include "pimd.h" |
17 | | #include "pim_oil.h" |
18 | | #include "pim_str.h" |
19 | | #include "pim_iface.h" |
20 | | #include "pim_time.h" |
21 | | #include "pim_vxlan.h" |
22 | | |
23 | | static void pim_channel_update_mute(struct channel_oil *c_oil); |
24 | | |
25 | | char *pim_channel_oil_dump(struct channel_oil *c_oil, char *buf, size_t size) |
26 | 0 | { |
27 | 0 | char *out; |
28 | 0 | struct interface *ifp; |
29 | 0 | pim_sgaddr sg; |
30 | 0 | int i; |
31 | |
|
32 | 0 | sg.src = *oil_origin(c_oil); |
33 | 0 | sg.grp = *oil_mcastgrp(c_oil); |
34 | 0 | ifp = pim_if_find_by_vif_index(c_oil->pim, *oil_parent(c_oil)); |
35 | 0 | snprintfrr(buf, size, "%pSG IIF: %s, OIFS: ", &sg, |
36 | 0 | ifp ? ifp->name : "(?)"); |
37 | |
|
38 | 0 | out = buf + strlen(buf); |
39 | 0 | for (i = 0; i < MAXVIFS; i++) { |
40 | 0 | if (oil_if_has(c_oil, i) != 0) { |
41 | 0 | ifp = pim_if_find_by_vif_index(c_oil->pim, i); |
42 | 0 | snprintf(out, buf + size - out, "%s ", |
43 | 0 | ifp ? ifp->name : "(?)"); |
44 | 0 | out += strlen(out); |
45 | 0 | } |
46 | 0 | } |
47 | |
|
48 | 0 | return buf; |
49 | 0 | } |
50 | | |
51 | | int pim_channel_oil_compare(const struct channel_oil *cc1, |
52 | | const struct channel_oil *cc2) |
53 | 935k | { |
54 | 935k | struct channel_oil *c1 = (struct channel_oil *)cc1; |
55 | 935k | struct channel_oil *c2 = (struct channel_oil *)cc2; |
56 | 935k | int rv; |
57 | | |
58 | 935k | rv = pim_addr_cmp(*oil_mcastgrp(c1), *oil_mcastgrp(c2)); |
59 | 935k | if (rv) |
60 | 645k | return rv; |
61 | 290k | rv = pim_addr_cmp(*oil_origin(c1), *oil_origin(c2)); |
62 | 290k | if (rv) |
63 | 290k | return rv; |
64 | 0 | return 0; |
65 | 290k | } |
66 | | |
67 | | void pim_oil_init(struct pim_instance *pim) |
68 | 1 | { |
69 | 1 | rb_pim_oil_init(&pim->channel_oil_head); |
70 | 1 | } |
71 | | |
72 | | void pim_oil_terminate(struct pim_instance *pim) |
73 | 0 | { |
74 | 0 | struct channel_oil *c_oil; |
75 | |
|
76 | 0 | while ((c_oil = rb_pim_oil_pop(&pim->channel_oil_head))) |
77 | 0 | pim_channel_oil_free(c_oil); |
78 | |
|
79 | 0 | rb_pim_oil_fini(&pim->channel_oil_head); |
80 | 0 | } |
81 | | |
82 | | void pim_channel_oil_free(struct channel_oil *c_oil) |
83 | 46.0k | { |
84 | 46.0k | XFREE(MTYPE_PIM_CHANNEL_OIL, c_oil); |
85 | 46.0k | } |
86 | | |
87 | | struct channel_oil *pim_find_channel_oil(struct pim_instance *pim, |
88 | | pim_sgaddr *sg) |
89 | 46.9k | { |
90 | 46.9k | struct channel_oil *c_oil = NULL; |
91 | 46.9k | struct channel_oil lookup; |
92 | | |
93 | 46.9k | *oil_mcastgrp(&lookup) = sg->grp; |
94 | 46.9k | *oil_origin(&lookup) = sg->src; |
95 | | |
96 | 46.9k | c_oil = rb_pim_oil_find(&pim->channel_oil_head, &lookup); |
97 | | |
98 | 46.9k | return c_oil; |
99 | 46.9k | } |
100 | | |
101 | | struct channel_oil *pim_channel_oil_add(struct pim_instance *pim, |
102 | | pim_sgaddr *sg, const char *name) |
103 | 46.9k | { |
104 | 46.9k | struct channel_oil *c_oil; |
105 | | |
106 | 46.9k | c_oil = pim_find_channel_oil(pim, sg); |
107 | 46.9k | if (c_oil) { |
108 | 0 | ++c_oil->oil_ref_count; |
109 | |
|
110 | 0 | if (!c_oil->up) { |
111 | | /* channel might be present prior to upstream */ |
112 | 0 | c_oil->up = pim_upstream_find( |
113 | 0 | pim, sg); |
114 | | /* if the upstream entry is being anchored to an |
115 | | * already existing channel OIL we need to re-evaluate |
116 | | * the "Mute" state on AA OIFs |
117 | | */ |
118 | 0 | pim_channel_update_mute(c_oil); |
119 | 0 | } |
120 | | |
121 | | /* check if the IIF has changed |
122 | | * XXX - is this really needed |
123 | | */ |
124 | 0 | pim_upstream_mroute_iif_update(c_oil, __func__); |
125 | |
|
126 | 0 | if (PIM_DEBUG_MROUTE) |
127 | 0 | zlog_debug( |
128 | 0 | "%s(%s): Existing oil for %pSG Ref Count: %d (Post Increment)", |
129 | 0 | __func__, name, sg, c_oil->oil_ref_count); |
130 | 0 | return c_oil; |
131 | 0 | } |
132 | | |
133 | 46.9k | c_oil = XCALLOC(MTYPE_PIM_CHANNEL_OIL, sizeof(*c_oil)); |
134 | | |
135 | 46.9k | *oil_mcastgrp(c_oil) = sg->grp; |
136 | 46.9k | *oil_origin(c_oil) = sg->src; |
137 | | |
138 | 46.9k | *oil_parent(c_oil) = MAXVIFS; |
139 | 46.9k | c_oil->oil_ref_count = 1; |
140 | 46.9k | c_oil->installed = 0; |
141 | 46.9k | c_oil->up = pim_upstream_find(pim, sg); |
142 | 46.9k | c_oil->pim = pim; |
143 | | |
144 | 46.9k | rb_pim_oil_add(&pim->channel_oil_head, c_oil); |
145 | | |
146 | 46.9k | if (PIM_DEBUG_MROUTE) |
147 | 0 | zlog_debug("%s(%s): c_oil %pSG add", __func__, name, sg); |
148 | | |
149 | 46.9k | return c_oil; |
150 | 46.9k | } |
151 | | |
152 | | |
153 | | /* |
154 | | * Clean up mroute and channel oil created for dropping pkts from directly |
155 | | * connected source when the interface was non DR. |
156 | | */ |
157 | | void pim_clear_nocache_state(struct pim_interface *pim_ifp) |
158 | 0 | { |
159 | 0 | struct channel_oil *c_oil; |
160 | |
|
161 | 0 | frr_each_safe (rb_pim_oil, &pim_ifp->pim->channel_oil_head, c_oil) { |
162 | |
|
163 | 0 | if ((!c_oil->up) || |
164 | 0 | !(PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(c_oil->up->flags))) |
165 | 0 | continue; |
166 | | |
167 | 0 | if (*oil_parent(c_oil) != pim_ifp->mroute_vif_index) |
168 | 0 | continue; |
169 | | |
170 | 0 | EVENT_OFF(c_oil->up->t_ka_timer); |
171 | 0 | PIM_UPSTREAM_FLAG_UNSET_SRC_NOCACHE(c_oil->up->flags); |
172 | 0 | PIM_UPSTREAM_FLAG_UNSET_SRC_STREAM(c_oil->up->flags); |
173 | 0 | pim_upstream_del(pim_ifp->pim, c_oil->up, __func__); |
174 | 0 | } |
175 | 0 | } |
176 | | |
177 | | struct channel_oil *pim_channel_oil_del(struct channel_oil *c_oil, |
178 | | const char *name) |
179 | 46.0k | { |
180 | 46.0k | if (PIM_DEBUG_MROUTE) { |
181 | 0 | pim_sgaddr sg = {.src = *oil_origin(c_oil), |
182 | 0 | .grp = *oil_mcastgrp(c_oil)}; |
183 | |
|
184 | 0 | zlog_debug( |
185 | 0 | "%s(%s): Del oil for %pSG, Ref Count: %d (Predecrement)", |
186 | 0 | __func__, name, &sg, c_oil->oil_ref_count); |
187 | 0 | } |
188 | 46.0k | --c_oil->oil_ref_count; |
189 | | |
190 | 46.0k | if (c_oil->oil_ref_count < 1) { |
191 | | /* |
192 | | * notice that listnode_delete() can't be moved |
193 | | * into pim_channel_oil_free() because the later is |
194 | | * called by list_delete_all_node() |
195 | | */ |
196 | 46.0k | c_oil->up = NULL; |
197 | 46.0k | rb_pim_oil_del(&c_oil->pim->channel_oil_head, c_oil); |
198 | | |
199 | 46.0k | pim_channel_oil_free(c_oil); |
200 | 46.0k | return NULL; |
201 | 46.0k | } |
202 | | |
203 | 0 | return c_oil; |
204 | 46.0k | } |
205 | | |
206 | | void pim_channel_oil_upstream_deref(struct channel_oil *c_oil) |
207 | 46.0k | { |
208 | | /* The upstream entry associated with a channel_oil is abt to be |
209 | | * deleted. If the channel_oil is kept around because of other |
210 | | * references we need to remove upstream based states out of it. |
211 | | */ |
212 | 46.0k | c_oil = pim_channel_oil_del(c_oil, __func__); |
213 | 46.0k | if (c_oil) { |
214 | | /* note: here we assume that c_oil->up has already been |
215 | | * cleared |
216 | | */ |
217 | 0 | pim_channel_update_mute(c_oil); |
218 | 0 | } |
219 | 46.0k | } |
220 | | |
221 | | int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif, |
222 | | uint32_t proto_mask, const char *caller) |
223 | 51.8k | { |
224 | 51.8k | struct pim_interface *pim_ifp; |
225 | | |
226 | 51.8k | assert(channel_oil); |
227 | 51.8k | assert(oif); |
228 | | |
229 | 51.8k | pim_ifp = oif->info; |
230 | | |
231 | 51.8k | assertf(pim_ifp->mroute_vif_index >= 0, |
232 | 51.8k | "trying to del OIF %s with VIF (%d)", oif->name, |
233 | 51.8k | pim_ifp->mroute_vif_index); |
234 | | |
235 | | /* |
236 | | * Don't do anything if we've been asked to remove a source |
237 | | * that is not actually on it. |
238 | | */ |
239 | 51.8k | if (!(channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask)) { |
240 | 51.4k | if (PIM_DEBUG_MROUTE) { |
241 | 0 | zlog_debug( |
242 | 0 | "%s %s: no existing protocol mask %u(%u) for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%pPAs,%pPAs)", |
243 | 0 | __FILE__, __func__, proto_mask, |
244 | 0 | channel_oil |
245 | 0 | ->oif_flags[pim_ifp->mroute_vif_index], |
246 | 0 | oif->name, pim_ifp->mroute_vif_index, |
247 | 0 | oil_if_has(channel_oil, pim_ifp->mroute_vif_index), |
248 | 0 | oil_origin(channel_oil), |
249 | 0 | oil_mcastgrp(channel_oil)); |
250 | 0 | } |
251 | 51.4k | return 0; |
252 | 51.4k | } |
253 | | |
254 | 383 | channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~proto_mask; |
255 | | |
256 | 383 | if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & |
257 | 383 | PIM_OIF_FLAG_PROTO_ANY) { |
258 | 204 | if (PIM_DEBUG_MROUTE) { |
259 | 0 | zlog_debug( |
260 | 0 | "%s %s: other protocol masks remain for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%pPAs,%pPAs)", |
261 | 0 | __FILE__, __func__, oif->name, |
262 | 0 | pim_ifp->mroute_vif_index, |
263 | 0 | oil_if_has(channel_oil, pim_ifp->mroute_vif_index), |
264 | 0 | oil_origin(channel_oil), |
265 | 0 | oil_mcastgrp(channel_oil)); |
266 | 0 | } |
267 | 204 | return 0; |
268 | 204 | } |
269 | | |
270 | 179 | oil_if_set(channel_oil, pim_ifp->mroute_vif_index, 0); |
271 | | /* clear mute; will be re-evaluated when the OIF becomes valid again */ |
272 | 179 | channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~PIM_OIF_FLAG_MUTE; |
273 | | |
274 | 179 | if (pim_upstream_mroute_add(channel_oil, __func__)) { |
275 | 0 | if (PIM_DEBUG_MROUTE) { |
276 | 0 | zlog_debug( |
277 | 0 | "%s %s: could not remove output interface %s (vif_index=%d) for channel (S,G)=(%pPAs,%pPAs)", |
278 | 0 | __FILE__, __func__, oif->name, |
279 | 0 | pim_ifp->mroute_vif_index, |
280 | 0 | oil_origin(channel_oil), |
281 | 0 | oil_mcastgrp(channel_oil)); |
282 | 0 | } |
283 | 0 | return -1; |
284 | 0 | } |
285 | | |
286 | 179 | --channel_oil->oil_size; |
287 | | |
288 | 179 | if (PIM_DEBUG_MROUTE) { |
289 | 0 | zlog_debug( |
290 | 0 | "%s(%s): (S,G)=(%pPAs,%pPAs): proto_mask=%u IIF:%d OIF=%s vif_index=%d", |
291 | 0 | __func__, caller, oil_origin(channel_oil), |
292 | 0 | oil_mcastgrp(channel_oil), |
293 | 0 | proto_mask, |
294 | 0 | *oil_parent(channel_oil), oif->name, |
295 | 0 | pim_ifp->mroute_vif_index); |
296 | 0 | } |
297 | | |
298 | 179 | return 0; |
299 | 179 | } |
300 | | |
301 | | void pim_channel_del_inherited_oif(struct channel_oil *c_oil, |
302 | | struct interface *oif, const char *caller) |
303 | 5.73k | { |
304 | 5.73k | struct pim_upstream *up = c_oil->up; |
305 | | |
306 | 5.73k | pim_channel_del_oif(c_oil, oif, PIM_OIF_FLAG_PROTO_STAR, |
307 | 5.73k | caller); |
308 | | |
309 | | /* if an inherited OIF is being removed join-desired can change |
310 | | * if the inherited OIL is now empty and KAT is running |
311 | | */ |
312 | 5.73k | if (up && !pim_addr_is_any(up->sg.src) && |
313 | 5.73k | pim_upstream_empty_inherited_olist(up)) |
314 | 5.28k | pim_upstream_update_join_desired(up->pim, up); |
315 | 5.73k | } |
316 | | |
317 | | static bool pim_channel_eval_oif_mute(struct channel_oil *c_oil, |
318 | | struct pim_interface *pim_ifp) |
319 | 46.3k | { |
320 | 46.3k | struct pim_interface *pim_reg_ifp; |
321 | 46.3k | struct pim_interface *vxlan_ifp; |
322 | 46.3k | bool do_mute = false; |
323 | 46.3k | struct pim_instance *pim = c_oil->pim; |
324 | | |
325 | 46.3k | if (!c_oil->up) |
326 | 0 | return do_mute; |
327 | | |
328 | 46.3k | pim_reg_ifp = pim->regiface->info; |
329 | 46.3k | if (pim_ifp == pim_reg_ifp) { |
330 | | /* suppress pimreg in the OIL if the mroute is not supposed to |
331 | | * trigger register encapsulated data |
332 | | */ |
333 | 0 | if (PIM_UPSTREAM_FLAG_TEST_NO_PIMREG_DATA(c_oil->up->flags)) |
334 | 0 | do_mute = true; |
335 | |
|
336 | 0 | return do_mute; |
337 | 0 | } |
338 | | |
339 | 46.3k | vxlan_ifp = pim_vxlan_get_term_ifp(pim); |
340 | 46.3k | if (pim_ifp == vxlan_ifp) { |
341 | | /* 1. vxlan termination device must never be added to the |
342 | | * origination mroute (and that can actually happen because |
343 | | * of XG inheritance from the termination mroute) otherwise |
344 | | * traffic will end up looping. |
345 | | * PS: This check has also been extended to non-orig mroutes |
346 | | * that have a local SIP as such mroutes can move back and |
347 | | * forth between orig<=>non-orig type. |
348 | | * 2. vxlan termination device should be removed from the non-DF |
349 | | * to prevent duplicates to the overlay rxer |
350 | | */ |
351 | 0 | if (PIM_UPSTREAM_FLAG_TEST_SRC_VXLAN_ORIG(c_oil->up->flags) || |
352 | 0 | PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil->up->flags) || |
353 | 0 | pim_vxlan_is_local_sip(c_oil->up)) |
354 | 0 | do_mute = true; |
355 | |
|
356 | 0 | return do_mute; |
357 | 0 | } |
358 | | |
359 | 46.3k | if (PIM_I_am_DualActive(pim_ifp)) { |
360 | 0 | struct pim_upstream *starup = c_oil->up->parent; |
361 | 0 | if (PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(c_oil->up->flags) |
362 | 0 | && (PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil->up->flags))) |
363 | 0 | do_mute = true; |
364 | | |
365 | | /* In case entry is (S,G), Negotiation happens at (*.G) */ |
366 | 0 | if (starup |
367 | |
|
368 | 0 | && PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(starup->flags) |
369 | 0 | && (PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(starup->flags))) |
370 | 0 | do_mute = true; |
371 | 0 | return do_mute; |
372 | 0 | } |
373 | 46.3k | return do_mute; |
374 | 46.3k | } |
375 | | |
376 | | void pim_channel_update_oif_mute(struct channel_oil *c_oil, |
377 | | struct pim_interface *pim_ifp) |
378 | 0 | { |
379 | 0 | bool old_mute; |
380 | 0 | bool new_mute; |
381 | | |
382 | | /* If pim_ifp is not a part of the OIL there is nothing to do */ |
383 | 0 | if (!oil_if_has(c_oil, pim_ifp->mroute_vif_index)) |
384 | 0 | return; |
385 | | |
386 | 0 | old_mute = !!(c_oil->oif_flags[pim_ifp->mroute_vif_index] & |
387 | 0 | PIM_OIF_FLAG_MUTE); |
388 | 0 | new_mute = pim_channel_eval_oif_mute(c_oil, pim_ifp); |
389 | 0 | if (old_mute == new_mute) |
390 | 0 | return; |
391 | | |
392 | 0 | if (new_mute) |
393 | 0 | c_oil->oif_flags[pim_ifp->mroute_vif_index] |= |
394 | 0 | PIM_OIF_FLAG_MUTE; |
395 | 0 | else |
396 | 0 | c_oil->oif_flags[pim_ifp->mroute_vif_index] &= |
397 | 0 | ~PIM_OIF_FLAG_MUTE; |
398 | |
|
399 | 0 | pim_upstream_mroute_add(c_oil, __func__); |
400 | 0 | } |
401 | | |
402 | | /* pim_upstream has been set or cleared on the c_oil. re-eval mute state |
403 | | * on all existing OIFs |
404 | | */ |
405 | | static void pim_channel_update_mute(struct channel_oil *c_oil) |
406 | 0 | { |
407 | 0 | struct pim_interface *pim_reg_ifp; |
408 | 0 | struct pim_interface *vxlan_ifp; |
409 | |
|
410 | 0 | if (c_oil->pim->regiface) { |
411 | 0 | pim_reg_ifp = c_oil->pim->regiface->info; |
412 | 0 | if (pim_reg_ifp) |
413 | 0 | pim_channel_update_oif_mute(c_oil, pim_reg_ifp); |
414 | 0 | } |
415 | 0 | vxlan_ifp = pim_vxlan_get_term_ifp(c_oil->pim); |
416 | 0 | if (vxlan_ifp) |
417 | 0 | pim_channel_update_oif_mute(c_oil, vxlan_ifp); |
418 | 0 | } |
419 | | |
420 | | int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif, |
421 | | uint32_t proto_mask, const char *caller) |
422 | 49.7k | { |
423 | 49.7k | struct pim_interface *pim_ifp; |
424 | 49.7k | int old_ttl; |
425 | | |
426 | | /* |
427 | | * If we've gotten here we've gone bad, but let's |
428 | | * not take down pim |
429 | | */ |
430 | 49.7k | if (!channel_oil) { |
431 | 0 | zlog_warn("Attempt to Add OIF for non-existent channel oil"); |
432 | 0 | return -1; |
433 | 0 | } |
434 | | |
435 | 49.7k | pim_ifp = oif->info; |
436 | | |
437 | 49.7k | assertf(pim_ifp->mroute_vif_index >= 0, |
438 | 49.7k | "trying to add OIF %s with VIF (%d)", oif->name, |
439 | 49.7k | pim_ifp->mroute_vif_index); |
440 | | |
441 | | /* Prevent single protocol from subscribing same interface to |
442 | | channel (S,G) multiple times */ |
443 | 49.7k | if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask) { |
444 | 3.17k | channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask; |
445 | | |
446 | 3.17k | if (PIM_DEBUG_MROUTE) { |
447 | 0 | zlog_debug( |
448 | 0 | "%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%pPAs,%pPAs)", |
449 | 0 | __FILE__, __func__, proto_mask, oif->name, |
450 | 0 | pim_ifp->mroute_vif_index, |
451 | 0 | oil_if_has(channel_oil, pim_ifp->mroute_vif_index), |
452 | 0 | oil_origin(channel_oil), |
453 | 0 | oil_mcastgrp(channel_oil)); |
454 | 0 | } |
455 | 3.17k | return -3; |
456 | 3.17k | } |
457 | | |
458 | | /* Allow other protocol to request subscription of same interface to |
459 | | * channel (S,G), we need to note this information |
460 | | */ |
461 | 46.5k | if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] |
462 | 46.5k | & PIM_OIF_FLAG_PROTO_ANY) { |
463 | | |
464 | | /* Updating time here is not required as this time has to |
465 | | * indicate when the interface is added |
466 | | */ |
467 | | |
468 | 206 | channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask; |
469 | | /* Check the OIF really exists before returning, and only log |
470 | | warning otherwise */ |
471 | 206 | if (oil_if_has(channel_oil, pim_ifp->mroute_vif_index) < 1) { |
472 | 0 | zlog_warn( |
473 | 0 | "%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%pPAs,%pPAs)", |
474 | 0 | __FILE__, __func__, proto_mask, oif->name, |
475 | 0 | pim_ifp->mroute_vif_index, |
476 | 0 | oil_if_has(channel_oil, pim_ifp->mroute_vif_index), |
477 | 0 | oil_origin(channel_oil), |
478 | 0 | oil_mcastgrp(channel_oil)); |
479 | 0 | } |
480 | | |
481 | 206 | if (PIM_DEBUG_MROUTE) { |
482 | 0 | zlog_debug( |
483 | 0 | "%s(%s): (S,G)=(%pPAs,%pPAs): proto_mask=%u OIF=%s vif_index=%d added to 0x%x", |
484 | 0 | __func__, caller, oil_origin(channel_oil), |
485 | 0 | oil_mcastgrp(channel_oil), |
486 | 0 | proto_mask, oif->name, |
487 | 0 | pim_ifp->mroute_vif_index, |
488 | 0 | channel_oil |
489 | 0 | ->oif_flags[pim_ifp->mroute_vif_index]); |
490 | 0 | } |
491 | 206 | return 0; |
492 | 206 | } |
493 | | |
494 | 46.3k | old_ttl = oil_if_has(channel_oil, pim_ifp->mroute_vif_index); |
495 | | |
496 | 46.3k | if (old_ttl > 0) { |
497 | 0 | if (PIM_DEBUG_MROUTE) { |
498 | 0 | zlog_debug( |
499 | 0 | "%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%pPAs,%pPAs)", |
500 | 0 | __FILE__, __func__, oif->name, |
501 | 0 | pim_ifp->mroute_vif_index, |
502 | 0 | oil_origin(channel_oil), |
503 | 0 | oil_mcastgrp(channel_oil)); |
504 | 0 | } |
505 | 0 | return -4; |
506 | 0 | } |
507 | | |
508 | 46.3k | oil_if_set(channel_oil, pim_ifp->mroute_vif_index, PIM_MROUTE_MIN_TTL); |
509 | | |
510 | | /* Some OIFs are held in a muted state i.e. the PIM state machine |
511 | | * decided to include the OIF but additional status check such as |
512 | | * MLAG DF role prevent it from being activated for traffic |
513 | | * forwarding. |
514 | | */ |
515 | 46.3k | if (pim_channel_eval_oif_mute(channel_oil, pim_ifp)) |
516 | 0 | channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= |
517 | 0 | PIM_OIF_FLAG_MUTE; |
518 | 46.3k | else |
519 | 46.3k | channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= |
520 | 46.3k | ~PIM_OIF_FLAG_MUTE; |
521 | | |
522 | | /* channel_oil->oil.mfcc_parent != MAXVIFS indicate this entry is not |
523 | | * valid to get installed in kernel. |
524 | | */ |
525 | 46.3k | if (*oil_parent(channel_oil) != MAXVIFS) { |
526 | 0 | if (pim_upstream_mroute_add(channel_oil, __func__)) { |
527 | 0 | if (PIM_DEBUG_MROUTE) { |
528 | 0 | zlog_debug( |
529 | 0 | "%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%pPAs,%pPAs)", |
530 | 0 | __FILE__, __func__, oif->name, |
531 | 0 | pim_ifp->mroute_vif_index, |
532 | 0 | oil_origin(channel_oil), |
533 | 0 | oil_mcastgrp(channel_oil)); |
534 | 0 | } |
535 | |
|
536 | 0 | oil_if_set(channel_oil, pim_ifp->mroute_vif_index, |
537 | 0 | old_ttl); |
538 | 0 | return -5; |
539 | 0 | } |
540 | 0 | } |
541 | | |
542 | 46.3k | channel_oil->oif_creation[pim_ifp->mroute_vif_index] = |
543 | 46.3k | pim_time_monotonic_sec(); |
544 | 46.3k | ++channel_oil->oil_size; |
545 | 46.3k | channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask; |
546 | | |
547 | 46.3k | if (PIM_DEBUG_MROUTE) { |
548 | 0 | zlog_debug( |
549 | 0 | "%s(%s): (S,G)=(%pPAs,%pPAs): proto_mask=%u OIF=%s vif_index=%d: DONE", |
550 | 0 | __func__, caller, oil_origin(channel_oil), |
551 | 0 | oil_mcastgrp(channel_oil), |
552 | 0 | proto_mask, |
553 | 0 | oif->name, pim_ifp->mroute_vif_index); |
554 | 0 | } |
555 | | |
556 | 46.3k | return 0; |
557 | 46.3k | } |
558 | | |
559 | | int pim_channel_oil_empty(struct channel_oil *c_oil) |
560 | 57.1k | { |
561 | 57.1k | static struct channel_oil null_oil; |
562 | | |
563 | 57.1k | if (!c_oil) |
564 | 0 | return 1; |
565 | | |
566 | | /* exclude pimreg from the OIL when checking if the inherited_oil is |
567 | | * non-NULL. |
568 | | * pimreg device (in all vrfs) uses a vifi of |
569 | | * 0 (PIM_OIF_PIM_REGISTER_VIF) so we simply mfcc_ttls[0] */ |
570 | 57.1k | if (oil_if_has(c_oil, 0)) |
571 | 0 | oil_if_set(&null_oil, 0, 1); |
572 | 57.1k | else |
573 | 57.1k | oil_if_set(&null_oil, 0, 0); |
574 | | |
575 | 57.1k | return !oil_if_cmp(&c_oil->oil, &null_oil.oil); |
576 | 57.1k | } |