/src/frr/pimd/pim_zebra.c
Line | Count | Source (jump to first uncovered line) |
1 | | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | | /* |
3 | | * PIM for Quagga |
4 | | * Copyright (C) 2008 Everton da Silva Marques |
5 | | */ |
6 | | |
7 | | #include <zebra.h> |
8 | | |
9 | | #include "if.h" |
10 | | #include "log.h" |
11 | | #include "prefix.h" |
12 | | #include "zclient.h" |
13 | | #include "stream.h" |
14 | | #include "network.h" |
15 | | #include "vty.h" |
16 | | #include "plist.h" |
17 | | #include "lib/bfd.h" |
18 | | |
19 | | #include "pimd.h" |
20 | | #include "pim_pim.h" |
21 | | #include "pim_zebra.h" |
22 | | #include "pim_iface.h" |
23 | | #include "pim_str.h" |
24 | | #include "pim_oil.h" |
25 | | #include "pim_rpf.h" |
26 | | #include "pim_time.h" |
27 | | #include "pim_join.h" |
28 | | #include "pim_zlookup.h" |
29 | | #include "pim_ifchannel.h" |
30 | | #include "pim_rp.h" |
31 | | #include "pim_igmpv3.h" |
32 | | #include "pim_jp_agg.h" |
33 | | #include "pim_nht.h" |
34 | | #include "pim_ssm.h" |
35 | | #include "pim_vxlan.h" |
36 | | #include "pim_mlag.h" |
37 | | |
38 | | #undef PIM_DEBUG_IFADDR_DUMP |
39 | | #define PIM_DEBUG_IFADDR_DUMP |
40 | | |
41 | | struct zclient *zclient; |
42 | | |
43 | | |
44 | | /* Router-id update message from zebra. */ |
45 | | static int pim_router_id_update_zebra(ZAPI_CALLBACK_ARGS) |
46 | 0 | { |
47 | 0 | struct prefix router_id; |
48 | |
|
49 | 0 | zebra_router_id_update_read(zclient->ibuf, &router_id); |
50 | |
|
51 | 0 | return 0; |
52 | 0 | } |
53 | | |
54 | | static int pim_zebra_interface_vrf_update(ZAPI_CALLBACK_ARGS) |
55 | 0 | { |
56 | 0 | struct interface *ifp; |
57 | 0 | vrf_id_t new_vrf_id; |
58 | 0 | struct pim_instance *pim; |
59 | 0 | struct pim_interface *pim_ifp; |
60 | |
|
61 | 0 | ifp = zebra_interface_vrf_update_read(zclient->ibuf, vrf_id, |
62 | 0 | &new_vrf_id); |
63 | 0 | if (!ifp) |
64 | 0 | return 0; |
65 | | |
66 | 0 | if (PIM_DEBUG_ZEBRA) |
67 | 0 | zlog_debug("%s: %s updating from %u to %u", __func__, ifp->name, |
68 | 0 | vrf_id, new_vrf_id); |
69 | |
|
70 | 0 | pim = pim_get_pim_instance(new_vrf_id); |
71 | 0 | if (!pim) |
72 | 0 | return 0; |
73 | | |
74 | 0 | if_update_to_new_vrf(ifp, new_vrf_id); |
75 | |
|
76 | 0 | pim_ifp = ifp->info; |
77 | 0 | if (!pim_ifp) |
78 | 0 | return 0; |
79 | | |
80 | 0 | pim_ifp->pim->mcast_if_count--; |
81 | 0 | pim_ifp->pim = pim; |
82 | 0 | pim_ifp->pim->mcast_if_count++; |
83 | |
|
84 | 0 | return 0; |
85 | 0 | } |
86 | | |
87 | | #ifdef PIM_DEBUG_IFADDR_DUMP |
88 | | static void dump_if_address(struct interface *ifp) |
89 | 0 | { |
90 | 0 | struct connected *ifc; |
91 | 0 | struct listnode *node; |
92 | |
|
93 | 0 | zlog_debug("%s %s: interface %s addresses:", __FILE__, __func__, |
94 | 0 | ifp->name); |
95 | |
|
96 | 0 | for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { |
97 | 0 | struct prefix *p = ifc->address; |
98 | |
|
99 | 0 | if (p->family != AF_INET) |
100 | 0 | continue; |
101 | | |
102 | 0 | zlog_debug("%s %s: interface %s address %pI4 %s", __FILE__, |
103 | 0 | __func__, ifp->name, &p->u.prefix4, |
104 | 0 | CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY) |
105 | 0 | ? "secondary" |
106 | 0 | : "primary"); |
107 | 0 | } |
108 | 0 | } |
109 | | #endif |
110 | | |
111 | | static int pim_zebra_if_address_add(ZAPI_CALLBACK_ARGS) |
112 | 0 | { |
113 | 0 | struct connected *c; |
114 | 0 | struct prefix *p; |
115 | 0 | struct pim_interface *pim_ifp; |
116 | | |
117 | | /* |
118 | | zebra api notifies address adds/dels events by using the same call |
119 | | interface_add_read below, see comments in lib/zclient.c |
120 | | |
121 | | zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_ADD, ...) |
122 | | will add address to interface list by calling |
123 | | connected_add_by_prefix() |
124 | | */ |
125 | 0 | c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); |
126 | 0 | if (!c) |
127 | 0 | return 0; |
128 | | |
129 | 0 | pim_ifp = c->ifp->info; |
130 | 0 | p = c->address; |
131 | |
|
132 | 0 | if (PIM_DEBUG_ZEBRA) { |
133 | 0 | zlog_debug("%s: %s(%u) connected IP address %pFX flags %u %s", |
134 | 0 | __func__, c->ifp->name, vrf_id, p, c->flags, |
135 | 0 | CHECK_FLAG(c->flags, ZEBRA_IFA_SECONDARY) |
136 | 0 | ? "secondary" |
137 | 0 | : "primary"); |
138 | |
|
139 | 0 | #ifdef PIM_DEBUG_IFADDR_DUMP |
140 | 0 | dump_if_address(c->ifp); |
141 | 0 | #endif |
142 | 0 | } |
143 | |
|
144 | 0 | #if PIM_IPV == 4 |
145 | 0 | if (p->family != PIM_AF) |
146 | 0 | SET_FLAG(c->flags, ZEBRA_IFA_SECONDARY); |
147 | 0 | else if (!CHECK_FLAG(c->flags, ZEBRA_IFA_SECONDARY)) { |
148 | | /* trying to add primary address? */ |
149 | 0 | pim_addr primary_addr = pim_find_primary_addr(c->ifp); |
150 | 0 | pim_addr addr = pim_addr_from_prefix(p); |
151 | |
|
152 | 0 | if (pim_addr_cmp(primary_addr, addr)) { |
153 | 0 | if (PIM_DEBUG_ZEBRA) |
154 | 0 | zlog_warn( |
155 | 0 | "%s: %s : forcing secondary flag on %pFX", |
156 | 0 | __func__, c->ifp->name, p); |
157 | 0 | SET_FLAG(c->flags, ZEBRA_IFA_SECONDARY); |
158 | 0 | } |
159 | 0 | } |
160 | | #else /* PIM_IPV != 4 */ |
161 | | if (p->family != PIM_AF) |
162 | | return 0; |
163 | | #endif |
164 | |
|
165 | 0 | pim_if_addr_add(c); |
166 | 0 | if (pim_ifp) { |
167 | 0 | struct pim_instance *pim; |
168 | |
|
169 | 0 | pim = pim_get_pim_instance(vrf_id); |
170 | 0 | if (!pim) { |
171 | 0 | if (PIM_DEBUG_ZEBRA) |
172 | 0 | zlog_debug("%s: Unable to find pim instance", |
173 | 0 | __func__); |
174 | 0 | return 0; |
175 | 0 | } |
176 | | |
177 | 0 | pim_ifp->pim = pim; |
178 | |
|
179 | 0 | pim_rp_check_on_if_add(pim_ifp); |
180 | 0 | } |
181 | | |
182 | 0 | if (if_is_loopback(c->ifp)) { |
183 | 0 | struct vrf *vrf = vrf_lookup_by_id(vrf_id); |
184 | 0 | struct interface *ifp; |
185 | |
|
186 | 0 | FOR_ALL_INTERFACES (vrf, ifp) { |
187 | 0 | if (!if_is_loopback(ifp) && if_is_operative(ifp)) |
188 | 0 | pim_if_addr_add_all(ifp); |
189 | 0 | } |
190 | 0 | } |
191 | 0 | return 0; |
192 | 0 | } |
193 | | |
194 | | static int pim_zebra_if_address_del(ZAPI_CALLBACK_ARGS) |
195 | 0 | { |
196 | 0 | struct connected *c; |
197 | 0 | struct prefix *p; |
198 | 0 | struct vrf *vrf = vrf_lookup_by_id(vrf_id); |
199 | |
|
200 | 0 | if (!vrf) |
201 | 0 | return 0; |
202 | | |
203 | | /* |
204 | | zebra api notifies address adds/dels events by using the same call |
205 | | interface_add_read below, see comments in lib/zclient.c |
206 | | |
207 | | zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_DELETE, ...) |
208 | | will remove address from interface list by calling |
209 | | connected_delete_by_prefix() |
210 | | */ |
211 | 0 | c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); |
212 | 0 | if (!c) |
213 | 0 | return 0; |
214 | | |
215 | 0 | p = c->address; |
216 | |
|
217 | 0 | if (PIM_DEBUG_ZEBRA) { |
218 | 0 | zlog_debug( |
219 | 0 | "%s: %s(%u) disconnected IP address %pFX flags %u %s", |
220 | 0 | __func__, c->ifp->name, vrf_id, p, c->flags, |
221 | 0 | CHECK_FLAG(c->flags, ZEBRA_IFA_SECONDARY) |
222 | 0 | ? "secondary" |
223 | 0 | : "primary"); |
224 | 0 | #ifdef PIM_DEBUG_IFADDR_DUMP |
225 | 0 | dump_if_address(c->ifp); |
226 | 0 | #endif |
227 | 0 | } |
228 | |
|
229 | 0 | if (p->family == PIM_AF) { |
230 | 0 | struct pim_instance *pim; |
231 | |
|
232 | 0 | pim = vrf->info; |
233 | 0 | pim_if_addr_del(c, 0); |
234 | 0 | pim_rp_setup(pim); |
235 | 0 | pim_i_am_rp_re_evaluate(pim); |
236 | 0 | } |
237 | |
|
238 | 0 | connected_free(&c); |
239 | 0 | return 0; |
240 | 0 | } |
241 | | |
242 | | void pim_zebra_update_all_interfaces(struct pim_instance *pim) |
243 | 7.04k | { |
244 | 7.04k | struct interface *ifp; |
245 | | |
246 | 14.0k | FOR_ALL_INTERFACES (pim->vrf, ifp) { |
247 | 14.0k | struct pim_interface *pim_ifp = ifp->info; |
248 | 14.0k | struct pim_iface_upstream_switch *us; |
249 | 14.0k | struct listnode *node; |
250 | | |
251 | 14.0k | if (!pim_ifp) |
252 | 0 | continue; |
253 | | |
254 | 14.0k | for (ALL_LIST_ELEMENTS_RO(pim_ifp->upstream_switch_list, node, |
255 | 14.0k | us)) { |
256 | 0 | struct pim_rpf rpf; |
257 | |
|
258 | 0 | rpf.source_nexthop.interface = ifp; |
259 | 0 | rpf.rpf_addr = us->address; |
260 | 0 | pim_joinprune_send(&rpf, us->us); |
261 | 0 | pim_jp_agg_clear_group(us->us); |
262 | 0 | } |
263 | 14.0k | } |
264 | 7.04k | } |
265 | | |
266 | | void pim_zebra_upstream_rpf_changed(struct pim_instance *pim, |
267 | | struct pim_upstream *up, |
268 | | struct pim_rpf *old) |
269 | 0 | { |
270 | 0 | if (old->source_nexthop.interface) { |
271 | 0 | struct pim_neighbor *nbr; |
272 | |
|
273 | 0 | nbr = pim_neighbor_find(old->source_nexthop.interface, |
274 | 0 | old->rpf_addr, true); |
275 | |
|
276 | 0 | if (nbr) |
277 | 0 | pim_jp_agg_remove_group(nbr->upstream_jp_agg, up, nbr); |
278 | | |
279 | | /* |
280 | | * We have detected a case where we might need |
281 | | * to rescan the inherited o_list so do it. |
282 | | */ |
283 | 0 | if (up->channel_oil->oil_inherited_rescan) { |
284 | 0 | pim_upstream_inherited_olist_decide(pim, up); |
285 | 0 | up->channel_oil->oil_inherited_rescan = 0; |
286 | 0 | } |
287 | |
|
288 | 0 | if (up->join_state == PIM_UPSTREAM_JOINED) { |
289 | | /* |
290 | | * If we come up real fast we can be here |
291 | | * where the mroute has not been installed |
292 | | * so install it. |
293 | | */ |
294 | 0 | if (!up->channel_oil->installed) |
295 | 0 | pim_upstream_mroute_add(up->channel_oil, |
296 | 0 | __func__); |
297 | | |
298 | | /* |
299 | | * RFC 4601: 4.5.7. Sending (S,G) |
300 | | * Join/Prune Messages |
301 | | * |
302 | | * Transitions from Joined State |
303 | | * |
304 | | * RPF'(S,G) changes not due to an Assert |
305 | | * |
306 | | * The upstream (S,G) state machine remains |
307 | | * in Joined state. Send Join(S,G) to the new |
308 | | * upstream neighbor, which is the new value |
309 | | * of RPF'(S,G). Send Prune(S,G) to the old |
310 | | * upstream neighbor, which is the old value |
311 | | * of RPF'(S,G). Set the Join Timer (JT) to |
312 | | * expire after t_periodic seconds. |
313 | | */ |
314 | 0 | pim_jp_agg_switch_interface(old, &up->rpf, up); |
315 | |
|
316 | 0 | pim_upstream_join_timer_restart(up, old); |
317 | 0 | } /* up->join_state == PIM_UPSTREAM_JOINED */ |
318 | 0 | } |
319 | | |
320 | 0 | else { |
321 | | /* |
322 | | * We have detected a case where we might need |
323 | | * to rescan the inherited o_list so do it. |
324 | | */ |
325 | 0 | if (up->channel_oil->oil_inherited_rescan) { |
326 | 0 | pim_upstream_inherited_olist_decide(pim, up); |
327 | 0 | up->channel_oil->oil_inherited_rescan = 0; |
328 | 0 | } |
329 | |
|
330 | 0 | if (up->join_state == PIM_UPSTREAM_JOINED) |
331 | 0 | pim_jp_agg_switch_interface(old, &up->rpf, up); |
332 | |
|
333 | 0 | if (!up->channel_oil->installed) |
334 | 0 | pim_upstream_mroute_add(up->channel_oil, __func__); |
335 | 0 | } |
336 | | |
337 | | /* FIXME can join_desired actually be changed by pim_rpf_update() |
338 | | * returning PIM_RPF_CHANGED ? |
339 | | */ |
340 | 0 | pim_upstream_update_join_desired(pim, up); |
341 | 0 | } |
342 | | |
343 | | __attribute__((unused)) |
344 | | static int pim_zebra_vxlan_sg_proc(ZAPI_CALLBACK_ARGS) |
345 | 0 | { |
346 | 0 | struct stream *s; |
347 | 0 | struct pim_instance *pim; |
348 | 0 | pim_sgaddr sg; |
349 | 0 | size_t prefixlen; |
350 | |
|
351 | 0 | pim = pim_get_pim_instance(vrf_id); |
352 | 0 | if (!pim) |
353 | 0 | return 0; |
354 | | |
355 | 0 | s = zclient->ibuf; |
356 | |
|
357 | 0 | prefixlen = stream_getl(s); |
358 | 0 | stream_get(&sg.src, s, prefixlen); |
359 | 0 | stream_get(&sg.grp, s, prefixlen); |
360 | |
|
361 | 0 | if (PIM_DEBUG_ZEBRA) |
362 | 0 | zlog_debug("%u:recv SG %s %pSG", vrf_id, |
363 | 0 | (cmd == ZEBRA_VXLAN_SG_ADD) ? "add" : "del", &sg); |
364 | |
|
365 | 0 | if (cmd == ZEBRA_VXLAN_SG_ADD) |
366 | 0 | pim_vxlan_sg_add(pim, &sg); |
367 | 0 | else |
368 | 0 | pim_vxlan_sg_del(pim, &sg); |
369 | |
|
370 | 0 | return 0; |
371 | 0 | } |
372 | | |
373 | | __attribute__((unused)) |
374 | | static void pim_zebra_vxlan_replay(void) |
375 | 0 | { |
376 | 0 | struct stream *s = NULL; |
377 | | |
378 | | /* Check socket. */ |
379 | 0 | if (!zclient || zclient->sock < 0) |
380 | 0 | return; |
381 | | |
382 | 0 | s = zclient->obuf; |
383 | 0 | stream_reset(s); |
384 | |
|
385 | 0 | zclient_create_header(s, ZEBRA_VXLAN_SG_REPLAY, VRF_DEFAULT); |
386 | 0 | stream_putw_at(s, 0, stream_get_endp(s)); |
387 | |
|
388 | 0 | zclient_send_message(zclient); |
389 | 0 | } |
390 | | |
391 | | void pim_scan_oil(struct pim_instance *pim) |
392 | 0 | { |
393 | 0 | struct channel_oil *c_oil; |
394 | |
|
395 | 0 | pim->scan_oil_last = pim_time_monotonic_sec(); |
396 | 0 | ++pim->scan_oil_events; |
397 | |
|
398 | 0 | frr_each (rb_pim_oil, &pim->channel_oil_head, c_oil) |
399 | 0 | pim_upstream_mroute_iif_update(c_oil, __func__); |
400 | 0 | } |
401 | | |
402 | | static void on_rpf_cache_refresh(struct event *t) |
403 | 0 | { |
404 | 0 | struct pim_instance *pim = EVENT_ARG(t); |
405 | 0 |
|
406 | 0 | /* update kernel multicast forwarding cache (MFC) */ |
407 | 0 | pim_scan_oil(pim); |
408 | 0 |
|
409 | 0 | pim->rpf_cache_refresh_last = pim_time_monotonic_sec(); |
410 | 0 | ++pim->rpf_cache_refresh_events; |
411 | 0 |
|
412 | 0 | // It is called as part of pim_neighbor_add |
413 | 0 | // pim_rp_setup (); |
414 | 0 | } |
415 | | |
416 | | void sched_rpf_cache_refresh(struct pim_instance *pim) |
417 | 394 | { |
418 | 394 | ++pim->rpf_cache_refresh_requests; |
419 | | |
420 | 394 | pim_rpf_set_refresh_time(pim); |
421 | | |
422 | 394 | if (pim->rpf_cache_refresher) { |
423 | | /* Refresh timer is already running */ |
424 | 0 | return; |
425 | 0 | } |
426 | | |
427 | | /* Start refresh timer */ |
428 | | |
429 | 394 | if (PIM_DEBUG_ZEBRA) { |
430 | 0 | zlog_debug("%s: triggering %ld msec timer", __func__, |
431 | 0 | router->rpf_cache_refresh_delay_msec); |
432 | 0 | } |
433 | | |
434 | 394 | event_add_timer_msec(router->master, on_rpf_cache_refresh, pim, |
435 | 394 | router->rpf_cache_refresh_delay_msec, |
436 | 394 | &pim->rpf_cache_refresher); |
437 | 394 | } |
438 | | |
439 | | static void pim_zebra_connected(struct zclient *zclient) |
440 | 0 | { |
441 | 0 | #if PIM_IPV == 4 |
442 | | /* Send the client registration */ |
443 | 0 | bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, router->vrf_id); |
444 | 0 | #endif |
445 | |
|
446 | 0 | zclient_send_reg_requests(zclient, router->vrf_id); |
447 | |
|
448 | 0 | #if PIM_IPV == 4 |
449 | | /* request for VxLAN BUM group addresses */ |
450 | 0 | pim_zebra_vxlan_replay(); |
451 | 0 | #endif |
452 | 0 | } |
453 | | |
454 | | static void pim_zebra_capabilities(struct zclient_capabilities *cap) |
455 | 0 | { |
456 | 0 | router->mlag_role = cap->role; |
457 | 0 | router->multipath = cap->ecmp; |
458 | 0 | } |
459 | | |
460 | | static zclient_handler *const pim_handlers[] = { |
461 | | [ZEBRA_INTERFACE_ADDRESS_ADD] = pim_zebra_if_address_add, |
462 | | [ZEBRA_INTERFACE_ADDRESS_DELETE] = pim_zebra_if_address_del, |
463 | | |
464 | | [ZEBRA_NEXTHOP_UPDATE] = pim_parse_nexthop_update, |
465 | | [ZEBRA_ROUTER_ID_UPDATE] = pim_router_id_update_zebra, |
466 | | [ZEBRA_INTERFACE_VRF_UPDATE] = pim_zebra_interface_vrf_update, |
467 | | |
468 | | #if PIM_IPV == 4 |
469 | | [ZEBRA_VXLAN_SG_ADD] = pim_zebra_vxlan_sg_proc, |
470 | | [ZEBRA_VXLAN_SG_DEL] = pim_zebra_vxlan_sg_proc, |
471 | | |
472 | | [ZEBRA_MLAG_PROCESS_UP] = pim_zebra_mlag_process_up, |
473 | | [ZEBRA_MLAG_PROCESS_DOWN] = pim_zebra_mlag_process_down, |
474 | | [ZEBRA_MLAG_FORWARD_MSG] = pim_zebra_mlag_handle_msg, |
475 | | #endif |
476 | | }; |
477 | | |
478 | | void pim_zebra_init(void) |
479 | 1 | { |
480 | | /* Socket for receiving updates from Zebra daemon */ |
481 | 1 | zclient = zclient_new(router->master, &zclient_options_default, |
482 | 1 | pim_handlers, array_size(pim_handlers)); |
483 | | |
484 | 1 | zclient->zebra_capabilities = pim_zebra_capabilities; |
485 | 1 | zclient->zebra_connected = pim_zebra_connected; |
486 | | |
487 | 1 | zclient_init(zclient, ZEBRA_ROUTE_PIM, 0, &pimd_privs); |
488 | 1 | if (PIM_DEBUG_PIM_TRACE) { |
489 | 0 | zlog_notice("%s: zclient socket initialized", __func__); |
490 | 0 | } |
491 | | |
492 | 1 | zclient_lookup_new(); |
493 | 1 | } |
494 | | |
495 | | void pim_forward_start(struct pim_ifchannel *ch) |
496 | 0 | { |
497 | 0 | struct pim_upstream *up = ch->upstream; |
498 | 0 | uint32_t mask = 0; |
499 | |
|
500 | 0 | if (PIM_DEBUG_PIM_TRACE) |
501 | 0 | zlog_debug("%s: (S,G)=%pSG oif=%s (%pPA)", __func__, &ch->sg, |
502 | 0 | ch->interface->name, &up->upstream_addr); |
503 | |
|
504 | 0 | if (PIM_IF_FLAG_TEST_PROTO_IGMP(ch->flags)) |
505 | 0 | mask = PIM_OIF_FLAG_PROTO_GM; |
506 | |
|
507 | 0 | if (PIM_IF_FLAG_TEST_PROTO_PIM(ch->flags)) |
508 | 0 | mask |= PIM_OIF_FLAG_PROTO_PIM; |
509 | |
|
510 | 0 | pim_channel_add_oif(up->channel_oil, ch->interface, |
511 | 0 | mask, __func__); |
512 | 0 | } |
513 | | |
514 | | void pim_forward_stop(struct pim_ifchannel *ch) |
515 | 0 | { |
516 | 0 | struct pim_upstream *up = ch->upstream; |
517 | |
|
518 | 0 | if (PIM_DEBUG_PIM_TRACE) { |
519 | 0 | zlog_debug("%s: (S,G)=%s oif=%s installed: %d", |
520 | 0 | __func__, ch->sg_str, ch->interface->name, |
521 | 0 | up->channel_oil->installed); |
522 | 0 | } |
523 | | |
524 | | /* |
525 | | * If a channel is being removed, check to see if we still need |
526 | | * to inherit the interface. If so make sure it is added in |
527 | | */ |
528 | 0 | if (pim_upstream_evaluate_join_desired_interface(up, ch, ch->parent)) |
529 | 0 | pim_channel_add_oif(up->channel_oil, ch->interface, |
530 | 0 | PIM_OIF_FLAG_PROTO_PIM, __func__); |
531 | 0 | else |
532 | 0 | pim_channel_del_oif(up->channel_oil, ch->interface, |
533 | 0 | PIM_OIF_FLAG_PROTO_PIM, __func__); |
534 | 0 | } |
535 | | |
536 | | void pim_zebra_zclient_update(struct vty *vty) |
537 | 0 | { |
538 | 0 | vty_out(vty, "Zclient update socket: "); |
539 | |
|
540 | 0 | if (zclient) { |
541 | 0 | vty_out(vty, "%d failures=%d\n", zclient->sock, zclient->fail); |
542 | 0 | } else { |
543 | 0 | vty_out(vty, "<null zclient>\n"); |
544 | 0 | } |
545 | 0 | } |
546 | | |
547 | | struct zclient *pim_zebra_zclient_get(void) |
548 | 164k | { |
549 | 164k | if (zclient) |
550 | 164k | return zclient; |
551 | 0 | else |
552 | 0 | return NULL; |
553 | 164k | } |
554 | | |
555 | | void pim_zebra_interface_set_master(struct interface *vrf, |
556 | | struct interface *ifp) |
557 | 0 | { |
558 | 0 | zclient_interface_set_master(zclient, vrf, ifp); |
559 | 0 | } |