]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_zebra.c
Merge remote-tracking branch 'origin/cmaster' into cmaster-next
[mirror_frr.git] / pimd / pim_zebra.c
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 "zebra/rib.h"
26
27 #include "if.h"
28 #include "log.h"
29 #include "prefix.h"
30 #include "zclient.h"
31 #include "stream.h"
32 #include "network.h"
33
34 #include "pimd.h"
35 #include "pim_pim.h"
36 #include "pim_zebra.h"
37 #include "pim_iface.h"
38 #include "pim_str.h"
39 #include "pim_oil.h"
40 #include "pim_rpf.h"
41 #include "pim_time.h"
42 #include "pim_join.h"
43 #include "pim_zlookup.h"
44 #include "pim_ifchannel.h"
45 #include "pim_rp.h"
46 #include "pim_igmpv3.h"
47
48 #undef PIM_DEBUG_IFADDR_DUMP
49 #define PIM_DEBUG_IFADDR_DUMP
50
51 static int fib_lookup_if_vif_index(struct in_addr addr);
52 static int del_oif(struct channel_oil *channel_oil,
53 struct interface *oif,
54 uint32_t proto_mask);
55
56 /* Router-id update message from zebra. */
57 static int pim_router_id_update_zebra(int command, struct zclient *zclient,
58 zebra_size_t length, vrf_id_t vrf_id)
59 {
60 struct prefix router_id;
61
62 zebra_router_id_update_read(zclient->ibuf, &router_id);
63
64 return 0;
65 }
66
67 static int pim_zebra_if_add(int command, struct zclient *zclient,
68 zebra_size_t length, vrf_id_t vrf_id)
69 {
70 struct interface *ifp;
71
72 /*
73 zebra api adds/dels interfaces using the same call
74 interface_add_read below, see comments in lib/zclient.c
75 */
76 ifp = zebra_interface_add_read(zclient->ibuf, vrf_id);
77 if (!ifp)
78 return 0;
79
80 if (PIM_DEBUG_ZEBRA) {
81 zlog_debug("%s: %s index %d flags %ld metric %d mtu %d operative %d",
82 __PRETTY_FUNCTION__,
83 ifp->name, ifp->ifindex, (long)ifp->flags, ifp->metric,
84 ifp->mtu, if_is_operative(ifp));
85 }
86
87 if (if_is_operative(ifp))
88 pim_if_addr_add_all(ifp);
89
90 return 0;
91 }
92
93 static int pim_zebra_if_del(int command, struct zclient *zclient,
94 zebra_size_t length, vrf_id_t vrf_id)
95 {
96 struct interface *ifp;
97
98 /*
99 zebra api adds/dels interfaces using the same call
100 interface_add_read below, see comments in lib/zclient.c
101
102 comments in lib/zclient.c seem to indicate that calling
103 zebra_interface_add_read is the correct call, but that
104 results in an attemted out of bounds read which causes
105 pimd to assert. Other clients use zebra_interface_state_read
106 and it appears to work just fine.
107 */
108 ifp = zebra_interface_state_read(zclient->ibuf, vrf_id);
109 if (!ifp)
110 return 0;
111
112 if (PIM_DEBUG_ZEBRA) {
113 zlog_debug("%s: %s index %d flags %ld metric %d mtu %d operative %d",
114 __PRETTY_FUNCTION__,
115 ifp->name, ifp->ifindex, (long)ifp->flags, ifp->metric,
116 ifp->mtu, if_is_operative(ifp));
117 }
118
119 if (!if_is_operative(ifp))
120 pim_if_addr_del_all(ifp);
121
122 return 0;
123 }
124
125 static int pim_zebra_if_state_up(int command, struct zclient *zclient,
126 zebra_size_t length, vrf_id_t vrf_id)
127 {
128 struct interface *ifp;
129
130 /*
131 zebra api notifies interface up/down events by using the same call
132 zebra_interface_state_read below, see comments in lib/zclient.c
133 */
134 ifp = zebra_interface_state_read(zclient->ibuf, vrf_id);
135 if (!ifp)
136 return 0;
137
138 if (PIM_DEBUG_ZEBRA) {
139 zlog_debug("%s: %s index %d flags %ld metric %d mtu %d operative %d",
140 __PRETTY_FUNCTION__,
141 ifp->name, ifp->ifindex, (long)ifp->flags, ifp->metric,
142 ifp->mtu, if_is_operative(ifp));
143 }
144
145 if (if_is_operative(ifp)) {
146 /*
147 pim_if_addr_add_all() suffices for bringing up both IGMP and PIM
148 */
149 pim_if_addr_add_all(ifp);
150 }
151
152 return 0;
153 }
154
155 static int pim_zebra_if_state_down(int command, struct zclient *zclient,
156 zebra_size_t length, vrf_id_t vrf_id)
157 {
158 struct interface *ifp;
159
160 /*
161 zebra api notifies interface up/down events by using the same call
162 zebra_interface_state_read below, see comments in lib/zclient.c
163 */
164 ifp = zebra_interface_state_read(zclient->ibuf, vrf_id);
165 if (!ifp)
166 return 0;
167
168 if (PIM_DEBUG_ZEBRA) {
169 zlog_debug("%s: %s index %d flags %ld metric %d mtu %d operative %d",
170 __PRETTY_FUNCTION__,
171 ifp->name, ifp->ifindex, (long)ifp->flags, ifp->metric,
172 ifp->mtu, if_is_operative(ifp));
173 }
174
175 if (!if_is_operative(ifp)) {
176 /*
177 pim_if_addr_del_all() suffices for shutting down IGMP,
178 but not for shutting down PIM
179 */
180 pim_if_addr_del_all(ifp);
181
182 /*
183 pim_sock_delete() closes the socket, stops read and timer threads,
184 and kills all neighbors.
185 */
186 if (ifp->info) {
187 pim_sock_delete(ifp, "link down");
188 }
189 }
190
191 return 0;
192 }
193
194 #ifdef PIM_DEBUG_IFADDR_DUMP
195 static void dump_if_address(struct interface *ifp)
196 {
197 struct connected *ifc;
198 struct listnode *node;
199
200 zlog_debug("%s %s: interface %s addresses:",
201 __FILE__, __PRETTY_FUNCTION__,
202 ifp->name);
203
204 for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) {
205 struct prefix *p = ifc->address;
206
207 if (p->family != AF_INET)
208 continue;
209
210 zlog_debug("%s %s: interface %s address %s %s",
211 __FILE__, __PRETTY_FUNCTION__,
212 ifp->name,
213 inet_ntoa(p->u.prefix4),
214 CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY) ?
215 "secondary" : "primary");
216 }
217 }
218 #endif
219
220 static int pim_zebra_if_address_add(int command, struct zclient *zclient,
221 zebra_size_t length, vrf_id_t vrf_id)
222 {
223 struct connected *c;
224 struct prefix *p;
225 struct in_addr old = { .s_addr = 0 };
226
227 /*
228 zebra api notifies address adds/dels events by using the same call
229 interface_add_read below, see comments in lib/zclient.c
230
231 zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_ADD, ...)
232 will add address to interface list by calling
233 connected_add_by_prefix()
234 */
235 c = zebra_interface_address_read(command, zclient->ibuf, vrf_id);
236 if (!c)
237 return 0;
238
239 p = c->address;
240 if (p->family != AF_INET)
241 return 0;
242
243 if (PIM_DEBUG_ZEBRA) {
244 char buf[BUFSIZ];
245 prefix2str(p, buf, BUFSIZ);
246 zlog_debug("%s: %s connected IP address %s flags %u %s",
247 __PRETTY_FUNCTION__,
248 c->ifp->name, buf, c->flags,
249 CHECK_FLAG(c->flags, ZEBRA_IFA_SECONDARY) ? "secondary" : "primary");
250
251 #ifdef PIM_DEBUG_IFADDR_DUMP
252 dump_if_address(c->ifp);
253 #endif
254 }
255
256 pim_rp_check_rp (old, p->u.prefix4);
257
258 if (!CHECK_FLAG(c->flags, ZEBRA_IFA_SECONDARY)) {
259 /* trying to add primary address */
260
261 struct in_addr primary_addr = pim_find_primary_addr(c->ifp);
262 if (primary_addr.s_addr != p->u.prefix4.s_addr) {
263 if (PIM_DEBUG_ZEBRA) {
264 /* but we had a primary address already */
265
266 char buf[BUFSIZ];
267 char old[100];
268
269 prefix2str(p, buf, BUFSIZ);
270 pim_inet4_dump("<old?>", primary_addr, old, sizeof(old));
271
272 zlog_warn("%s: %s primary addr old=%s: forcing secondary flag on new=%s",
273 __PRETTY_FUNCTION__,
274 c->ifp->name, old, buf);
275 }
276 SET_FLAG(c->flags, ZEBRA_IFA_SECONDARY);
277 }
278 }
279
280 pim_if_addr_add(c);
281
282 return 0;
283 }
284
285 static int pim_zebra_if_address_del(int command, struct zclient *client,
286 zebra_size_t length, vrf_id_t vrf_id)
287 {
288 struct connected *c;
289 struct prefix *p;
290 struct in_addr new = { .s_addr = 0 };
291
292 /*
293 zebra api notifies address adds/dels events by using the same call
294 interface_add_read below, see comments in lib/zclient.c
295
296 zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_DELETE, ...)
297 will remove address from interface list by calling
298 connected_delete_by_prefix()
299 */
300 c = zebra_interface_address_read(command, client->ibuf, vrf_id);
301 if (!c)
302 return 0;
303
304 p = c->address;
305 if (p->family != AF_INET)
306 return 0;
307
308 if (PIM_DEBUG_ZEBRA) {
309 char buf[BUFSIZ];
310 prefix2str(p, buf, BUFSIZ);
311 zlog_debug("%s: %s disconnected IP address %s flags %u %s",
312 __PRETTY_FUNCTION__,
313 c->ifp->name, buf, c->flags,
314 CHECK_FLAG(c->flags, ZEBRA_IFA_SECONDARY) ? "secondary" : "primary");
315
316 #ifdef PIM_DEBUG_IFADDR_DUMP
317 dump_if_address(c->ifp);
318 #endif
319 }
320
321 pim_rp_check_rp (p->u.prefix4, new);
322 pim_if_addr_del(c, 0);
323
324 return 0;
325 }
326
327 static void scan_upstream_rpf_cache()
328 {
329 struct listnode *up_node;
330 struct listnode *up_nextnode;
331 struct pim_upstream *up;
332
333 for (ALL_LIST_ELEMENTS(qpim_upstream_list, up_node, up_nextnode, up)) {
334 struct in_addr old_rpf_addr;
335 enum pim_rpf_result rpf_result;
336
337 rpf_result = pim_rpf_update(up, &old_rpf_addr, NULL);
338 if (rpf_result == PIM_RPF_FAILURE)
339 continue;
340
341 if (rpf_result == PIM_RPF_CHANGED) {
342
343 if (up->join_state == PIM_UPSTREAM_JOINED) {
344
345 /*
346 RFC 4601: 4.5.7. Sending (S,G) Join/Prune Messages
347
348 Transitions from Joined State
349
350 RPF'(S,G) changes not due to an Assert
351
352 The upstream (S,G) state machine remains in Joined
353 state. Send Join(S,G) to the new upstream neighbor, which is
354 the new value of RPF'(S,G). Send Prune(S,G) to the old
355 upstream neighbor, which is the old value of RPF'(S,G). Set
356 the Join Timer (JT) to expire after t_periodic seconds.
357 */
358
359
360 /* send Prune(S,G) to the old upstream neighbor */
361 pim_joinprune_send(up->rpf.source_nexthop.interface,
362 old_rpf_addr,
363 up->source_addr,
364 up->group_addr,
365 0 /* prune */);
366
367 /* send Join(S,G) to the current upstream neighbor */
368 pim_joinprune_send(up->rpf.source_nexthop.interface,
369 up->rpf.rpf_addr,
370 up->source_addr,
371 up->group_addr,
372 1 /* join */);
373
374 pim_upstream_join_timer_restart(up);
375 } /* up->join_state == PIM_UPSTREAM_JOINED */
376
377 /* FIXME can join_desired actually be changed by pim_rpf_update()
378 returning PIM_RPF_CHANGED ? */
379 pim_upstream_update_join_desired(up);
380
381 } /* PIM_RPF_CHANGED */
382
383 } /* for (qpim_upstream_list) */
384
385 }
386
387 void
388 pim_scan_individual_oil (struct channel_oil *c_oil)
389 {
390 struct in_addr vif_source;
391 int input_iface_vif_index;
392 int old_vif_index;
393
394 if (!pim_rp_set_upstream_addr (&vif_source, c_oil->oil.mfcc_origin))
395 return;
396
397 input_iface_vif_index = fib_lookup_if_vif_index (vif_source);
398 if (input_iface_vif_index < 1)
399 {
400 if (PIM_DEBUG_ZEBRA)
401 {
402 char source_str[100];
403 char group_str[100];
404 pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
405 pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
406 zlog_debug("%s %s: could not find input interface(%d) for (S,G)=(%s,%s)",
407 __FILE__, __PRETTY_FUNCTION__, c_oil->oil.mfcc_parent,
408 source_str, group_str);
409 }
410 pim_mroute_del (c_oil);
411 return;
412 }
413
414 if (input_iface_vif_index == c_oil->oil.mfcc_parent)
415 {
416 /* RPF unchanged */
417 return;
418 }
419
420 if (PIM_DEBUG_ZEBRA)
421 {
422 struct interface *old_iif = pim_if_find_by_vif_index(c_oil->oil.mfcc_parent);
423 struct interface *new_iif = pim_if_find_by_vif_index(input_iface_vif_index);
424 char source_str[100];
425 char group_str[100];
426 pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
427 pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
428 zlog_debug("%s %s: (S,G)=(%s,%s) input interface changed from %s vif_index=%d to %s vif_index=%d",
429 __FILE__, __PRETTY_FUNCTION__,
430 source_str, group_str,
431 old_iif ? old_iif->name : "<old_iif?>", c_oil->oil.mfcc_parent,
432 new_iif ? new_iif->name : "<new_iif?>", input_iface_vif_index);
433 }
434
435 /* new iif loops to existing oif ? */
436 if (c_oil->oil.mfcc_ttls[input_iface_vif_index])
437 {
438 struct interface *new_iif = pim_if_find_by_vif_index(input_iface_vif_index);
439
440 if (PIM_DEBUG_ZEBRA) {
441 char source_str[100];
442 char group_str[100];
443 pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
444 pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
445 zlog_debug("%s %s: (S,G)=(%s,%s) new iif loops to existing oif: %s vif_index=%d",
446 __FILE__, __PRETTY_FUNCTION__,
447 source_str, group_str,
448 new_iif ? new_iif->name : "<new_iif?>", input_iface_vif_index);
449 }
450
451 del_oif(c_oil, new_iif, PIM_OIF_FLAG_PROTO_ANY);
452 }
453
454 /* update iif vif_index */
455 old_vif_index = c_oil->oil.mfcc_parent;
456 c_oil->oil.mfcc_parent = input_iface_vif_index;
457
458 zlog_debug ("FF");
459 /* update kernel multicast forwarding cache (MFC) */
460 if (pim_mroute_add(c_oil))
461 {
462 /* just log warning */
463 struct interface *old_iif = pim_if_find_by_vif_index(old_vif_index);
464 struct interface *new_iif = pim_if_find_by_vif_index(input_iface_vif_index);
465 char source_str[100];
466 char group_str[100];
467 pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
468 pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
469 zlog_warn("%s %s: (S,G)=(%s,%s) failure updating input interface from %s vif_index=%d to %s vif_index=%d",
470 __FILE__, __PRETTY_FUNCTION__,
471 source_str, group_str,
472 old_iif ? old_iif->name : "<old_iif?>", c_oil->oil.mfcc_parent,
473 new_iif ? new_iif->name : "<new_iif?>", input_iface_vif_index);
474 }
475 }
476
477 void pim_scan_oil()
478 {
479 struct listnode *node;
480 struct listnode *nextnode;
481 struct channel_oil *c_oil;
482
483 qpim_scan_oil_last = pim_time_monotonic_sec();
484 ++qpim_scan_oil_events;
485
486 for (ALL_LIST_ELEMENTS(qpim_channel_oil_list, node, nextnode, c_oil))
487 pim_scan_individual_oil (c_oil);
488 }
489
490 static int on_rpf_cache_refresh(struct thread *t)
491 {
492 zassert(t);
493 zassert(qpim_rpf_cache_refresher);
494
495 qpim_rpf_cache_refresher = 0;
496
497 /* update PIM protocol state */
498 scan_upstream_rpf_cache();
499
500 /* update kernel multicast forwarding cache (MFC) */
501 pim_scan_oil();
502
503 qpim_rpf_cache_refresh_last = pim_time_monotonic_sec();
504 ++qpim_rpf_cache_refresh_events;
505
506 return 0;
507 }
508
509 static void sched_rpf_cache_refresh()
510 {
511 ++qpim_rpf_cache_refresh_requests;
512
513 if (qpim_rpf_cache_refresher) {
514 /* Refresh timer is already running */
515 return;
516 }
517
518 /* Start refresh timer */
519
520 if (PIM_DEBUG_ZEBRA) {
521 zlog_debug("%s: triggering %ld msec timer",
522 __PRETTY_FUNCTION__,
523 qpim_rpf_cache_refresh_delay_msec);
524 }
525
526 THREAD_TIMER_MSEC_ON(master, qpim_rpf_cache_refresher,
527 on_rpf_cache_refresh,
528 0, qpim_rpf_cache_refresh_delay_msec);
529 }
530
531 static int redist_read_ipv4_route(int command, struct zclient *zclient,
532 zebra_size_t length, vrf_id_t vrf_id)
533 {
534 struct stream *s;
535 struct zapi_ipv4 api;
536 unsigned long ifindex;
537 struct in_addr nexthop;
538 struct prefix_ipv4 p;
539 int min_len = 4;
540
541 if (length < min_len) {
542 zlog_warn("%s %s: short buffer: length=%d min=%d",
543 __FILE__, __PRETTY_FUNCTION__,
544 length, min_len);
545 return -1;
546 }
547
548 s = zclient->ibuf;
549 ifindex = 0;
550 nexthop.s_addr = 0;
551
552 /* Type, flags, message. */
553 api.type = stream_getc(s);
554 api.instance = stream_getw (s);
555 api.flags = stream_getc(s);
556 api.message = stream_getc(s);
557
558 /* IPv4 prefix length. */
559 memset(&p, 0, sizeof(struct prefix_ipv4));
560 p.family = AF_INET;
561 p.prefixlen = stream_getc(s);
562
563 min_len +=
564 PSIZE(p.prefixlen) +
565 CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP) ? 5 : 0 +
566 CHECK_FLAG(api.message, ZAPI_MESSAGE_IFINDEX) ? 5 : 0 +
567 CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ? 1 : 0 +
568 CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? 4 : 0;
569
570 if (PIM_DEBUG_ZEBRA) {
571 zlog_debug("%s %s: length=%d min_len=%d flags=%s%s%s%s",
572 __FILE__, __PRETTY_FUNCTION__,
573 length, min_len,
574 CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP) ? "nh" : "",
575 CHECK_FLAG(api.message, ZAPI_MESSAGE_IFINDEX) ? " ifi" : "",
576 CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ? " dist" : "",
577 CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? " metr" : "");
578 }
579
580 if (length < min_len) {
581 zlog_warn("%s %s: short buffer: length=%d min_len=%d flags=%s%s%s%s",
582 __FILE__, __PRETTY_FUNCTION__,
583 length, min_len,
584 CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP) ? "nh" : "",
585 CHECK_FLAG(api.message, ZAPI_MESSAGE_IFINDEX) ? " ifi" : "",
586 CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ? " dist" : "",
587 CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? " metr" : "");
588 return -1;
589 }
590
591 /* IPv4 prefix. */
592 stream_get(&p.prefix, s, PSIZE(p.prefixlen));
593
594 /* Nexthop, ifindex, distance, metric. */
595 if (CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP)) {
596 api.nexthop_num = stream_getc(s);
597 nexthop.s_addr = stream_get_ipv4(s);
598 }
599 if (CHECK_FLAG(api.message, ZAPI_MESSAGE_IFINDEX)) {
600 api.ifindex_num = stream_getc(s);
601 ifindex = stream_getl(s);
602 }
603
604 api.distance = CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ?
605 stream_getc(s) :
606 0;
607
608 api.metric = CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ?
609 stream_getl(s) :
610 0;
611
612 if (CHECK_FLAG (api.message, ZAPI_MESSAGE_TAG))
613 api.tag = stream_getw (s);
614 else
615 api.tag = 0;
616
617 switch (command) {
618 case ZEBRA_REDISTRIBUTE_IPV4_ADD:
619 case ZEBRA_IPV4_ROUTE_ADD:
620 if (PIM_DEBUG_ZEBRA) {
621 char buf[2][INET_ADDRSTRLEN];
622 zlog_debug("%s: add %s %s/%d "
623 "nexthop %s ifindex %ld metric%s %u distance%s %u",
624 __PRETTY_FUNCTION__,
625 zebra_route_string(api.type),
626 inet_ntop(AF_INET, &p.prefix, buf[0], sizeof(buf[0])),
627 p.prefixlen,
628 inet_ntop(AF_INET, &nexthop, buf[1], sizeof(buf[1])),
629 ifindex,
630 CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? "-recv" : "-miss",
631 api.metric,
632 CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ? "-recv" : "-miss",
633 api.distance);
634 }
635 break;
636 case ZEBRA_REDISTRIBUTE_IPV4_DEL:
637 case ZEBRA_IPV4_ROUTE_DELETE:
638 if (PIM_DEBUG_ZEBRA) {
639 char buf[2][INET_ADDRSTRLEN];
640 zlog_debug("%s: delete %s %s/%d "
641 "nexthop %s ifindex %ld metric%s %u distance%s %u",
642 __PRETTY_FUNCTION__,
643 zebra_route_string(api.type),
644 inet_ntop(AF_INET, &p.prefix, buf[0], sizeof(buf[0])),
645 p.prefixlen,
646 inet_ntop(AF_INET, &nexthop, buf[1], sizeof(buf[1])),
647 ifindex,
648 CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? "-recv" : "-miss",
649 api.metric,
650 CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ? "-recv" : "-miss",
651 api.distance);
652 }
653 break;
654 default:
655 zlog_warn("%s: unknown command=%d", __PRETTY_FUNCTION__, command);
656 return -1;
657 }
658
659 sched_rpf_cache_refresh();
660
661 return 0;
662 }
663
664 static void
665 pim_zebra_connected (struct zclient *zclient)
666 {
667 zclient_send_reg_requests (zclient, VRF_DEFAULT);
668 }
669 void pim_zebra_init(char *zebra_sock_path)
670 {
671 int i;
672
673 if (zebra_sock_path)
674 zclient_serv_path_set(zebra_sock_path);
675
676 #ifdef HAVE_TCP_ZEBRA
677 zlog_notice("zclient update contacting ZEBRA daemon at socket TCP %s,%d", "127.0.0.1", ZEBRA_PORT);
678 #else
679 zlog_notice("zclient update contacting ZEBRA daemon at socket UNIX %s", zclient_serv_path_get());
680 #endif
681
682 /* Socket for receiving updates from Zebra daemon */
683 qpim_zclient_update = zclient_new (master);
684
685 qpim_zclient_update->zebra_connected = pim_zebra_connected;
686 qpim_zclient_update->router_id_update = pim_router_id_update_zebra;
687 qpim_zclient_update->interface_add = pim_zebra_if_add;
688 qpim_zclient_update->interface_delete = pim_zebra_if_del;
689 qpim_zclient_update->interface_up = pim_zebra_if_state_up;
690 qpim_zclient_update->interface_down = pim_zebra_if_state_down;
691 qpim_zclient_update->interface_address_add = pim_zebra_if_address_add;
692 qpim_zclient_update->interface_address_delete = pim_zebra_if_address_del;
693 qpim_zclient_update->ipv4_route_add = redist_read_ipv4_route;
694 qpim_zclient_update->ipv4_route_delete = redist_read_ipv4_route;
695 qpim_zclient_update->redistribute_route_ipv4_add = redist_read_ipv4_route;
696 qpim_zclient_update->redistribute_route_ipv4_del = redist_read_ipv4_route;
697
698 zclient_init(qpim_zclient_update, ZEBRA_ROUTE_PIM, 0);
699 if (PIM_DEBUG_PIM_TRACE) {
700 zlog_info("zclient_init cleared redistribution request");
701 }
702
703 zassert(qpim_zclient_update->redist_default == ZEBRA_ROUTE_PIM);
704
705 /* Request all redistribution */
706 for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
707 if (i == qpim_zclient_update->redist_default)
708 continue;
709 vrf_bitmap_set (qpim_zclient_update->redist[AFI_IP][i], VRF_DEFAULT);;
710 if (PIM_DEBUG_PIM_TRACE) {
711 zlog_debug("%s: requesting redistribution for %s (%i)",
712 __PRETTY_FUNCTION__, zebra_route_string(i), i);
713 }
714 }
715
716 /* Request default information */
717 zclient_redistribute_default (ZEBRA_REDISTRIBUTE_DEFAULT_ADD,
718 qpim_zclient_update, VRF_DEFAULT);
719
720 if (PIM_DEBUG_PIM_TRACE) {
721 zlog_info("%s: requesting default information redistribution",
722 __PRETTY_FUNCTION__);
723
724 zlog_notice("%s: zclient update socket initialized",
725 __PRETTY_FUNCTION__);
726 }
727
728 zassert(!qpim_zclient_lookup);
729 qpim_zclient_lookup = zclient_lookup_new();
730 zassert(qpim_zclient_lookup);
731 }
732
733 void igmp_anysource_forward_start(struct igmp_group *group)
734 {
735 struct igmp_source *source;
736 struct in_addr src_addr = { .s_addr = 0 };
737 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
738 zassert(group->group_filtermode_isexcl);
739 zassert(listcount(group->group_source_list) < 1);
740
741 source = source_new (group, src_addr);
742 if (!source)
743 {
744 zlog_warn ("%s: Failure to create * source", __PRETTY_FUNCTION__);
745 return;
746 }
747
748 igmp_source_forward_start (source);
749 }
750
751 void igmp_anysource_forward_stop(struct igmp_group *group)
752 {
753 struct igmp_source *source;
754 struct in_addr star = { .s_addr = 0 };
755
756 source = igmp_find_source_by_addr (group, star);
757 if (source)
758 igmp_source_forward_stop (source);
759 }
760
761 static int fib_lookup_if_vif_index(struct in_addr addr)
762 {
763 struct pim_zlookup_nexthop nexthop_tab[PIM_NEXTHOP_IFINDEX_TAB_SIZE];
764 int num_ifindex;
765 int vif_index;
766 int first_ifindex;
767
768 num_ifindex = zclient_lookup_nexthop(qpim_zclient_lookup, nexthop_tab,
769 PIM_NEXTHOP_IFINDEX_TAB_SIZE, addr,
770 PIM_NEXTHOP_LOOKUP_MAX);
771 if (num_ifindex < 1) {
772 char addr_str[100];
773 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
774 zlog_warn("%s %s: could not find nexthop ifindex for address %s",
775 __FILE__, __PRETTY_FUNCTION__,
776 addr_str);
777 return -1;
778 }
779
780 first_ifindex = nexthop_tab[0].ifindex;
781
782 if (num_ifindex > 1) {
783 char addr_str[100];
784 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
785 zlog_info("%s %s: FIXME ignoring multiple nexthop ifindex'es num_ifindex=%d for address %s (using only ifindex=%d)",
786 __FILE__, __PRETTY_FUNCTION__,
787 num_ifindex, addr_str, first_ifindex);
788 /* debug warning only, do not return */
789 }
790
791 if (PIM_DEBUG_ZEBRA) {
792 char addr_str[100];
793 pim_inet4_dump("<ifaddr?>", addr, addr_str, sizeof(addr_str));
794 zlog_debug("%s %s: found nexthop ifindex=%d (interface %s) for address %s",
795 __FILE__, __PRETTY_FUNCTION__,
796 first_ifindex, ifindex2ifname(first_ifindex), addr_str);
797 }
798
799 vif_index = pim_if_find_vifindex_by_ifindex(first_ifindex);
800
801 if (vif_index < 0) {
802 char addr_str[100];
803 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
804 zlog_warn("%s %s: low vif_index=%d < 1 nexthop for address %s",
805 __FILE__, __PRETTY_FUNCTION__,
806 vif_index, addr_str);
807 return -2;
808 }
809
810 zassert(qpim_mroute_oif_highest_vif_index < MAXVIFS);
811
812 if (vif_index > qpim_mroute_oif_highest_vif_index) {
813 char addr_str[100];
814 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
815 zlog_warn("%s %s: high vif_index=%d > highest_vif_index=%d nexthop for address %s",
816 __FILE__, __PRETTY_FUNCTION__,
817 vif_index, qpim_mroute_oif_highest_vif_index, addr_str);
818
819 zlog_warn("%s %s: pim disabled on interface %s vif_index=%d ?",
820 __FILE__, __PRETTY_FUNCTION__,
821 ifindex2ifname(vif_index),
822 vif_index);
823
824 return -3;
825 }
826
827 return vif_index;
828 }
829
830 static int del_oif(struct channel_oil *channel_oil,
831 struct interface *oif,
832 uint32_t proto_mask)
833 {
834 struct pim_interface *pim_ifp;
835 int old_ttl;
836
837 zassert(channel_oil);
838
839 pim_ifp = oif->info;
840
841 zassert(pim_ifp->mroute_vif_index >= 1);
842 zassert(qpim_mroute_oif_highest_vif_index < MAXVIFS);
843 zassert(pim_ifp->mroute_vif_index <= qpim_mroute_oif_highest_vif_index);
844
845 if (PIM_DEBUG_MROUTE) {
846 char group_str[100];
847 char source_str[100];
848 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
849 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
850 zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d",
851 __FILE__, __PRETTY_FUNCTION__,
852 source_str, group_str,
853 proto_mask, oif->name, pim_ifp->mroute_vif_index);
854 }
855
856 /* Prevent single protocol from unsubscribing same interface from
857 channel (S,G) multiple times */
858 if (!(channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask)) {
859 char group_str[100];
860 char source_str[100];
861 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
862 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
863 zlog_warn("%s %s: nonexistent protocol mask %u removed OIF %s (vif_index=%d, min_ttl=%d) from channel (S,G)=(%s,%s)",
864 __FILE__, __PRETTY_FUNCTION__,
865 proto_mask, oif->name, pim_ifp->mroute_vif_index,
866 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
867 source_str, group_str);
868 return -2;
869 }
870
871 /* Mark that protocol is no longer interested in this OIF */
872 channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~proto_mask;
873
874 /* Allow multiple protocols to unsubscribe same interface from
875 channel (S,G) multiple times, by silently ignoring requests while
876 there is at least one protocol interested in the channel */
877 if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & PIM_OIF_FLAG_PROTO_ANY) {
878
879 /* Check the OIF keeps existing before returning, and only log
880 warning otherwise */
881 if (channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] < 1) {
882 char group_str[100];
883 char source_str[100];
884 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
885 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
886 zlog_warn("%s %s: protocol mask %u removing nonexistent OIF %s (vif_index=%d, min_ttl=%d) from channel (S,G)=(%s,%s)",
887 __FILE__, __PRETTY_FUNCTION__,
888 proto_mask, oif->name, pim_ifp->mroute_vif_index,
889 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
890 source_str, group_str);
891 }
892
893 return 0;
894 }
895
896 old_ttl = channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index];
897
898 if (old_ttl < 1) {
899 char group_str[100];
900 char source_str[100];
901 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
902 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
903 zlog_warn("%s %s: interface %s (vif_index=%d) is not output for channel (S,G)=(%s,%s)",
904 __FILE__, __PRETTY_FUNCTION__,
905 oif->name, pim_ifp->mroute_vif_index,
906 source_str, group_str);
907 return -3;
908 }
909
910 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = 0;
911
912 if (pim_mroute_add(channel_oil)) {
913 char group_str[100];
914 char source_str[100];
915 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
916 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
917 zlog_warn("%s %s: could not remove output interface %s (vif_index=%d) from channel (S,G)=(%s,%s)",
918 __FILE__, __PRETTY_FUNCTION__,
919 oif->name, pim_ifp->mroute_vif_index,
920 source_str, group_str);
921
922 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = old_ttl;
923 return -4;
924 }
925
926 --channel_oil->oil_size;
927
928 if (channel_oil->oil_size < 1) {
929 if (pim_mroute_del(channel_oil)) {
930 /* just log a warning in case of failure */
931 char group_str[100];
932 char source_str[100];
933 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
934 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
935 zlog_warn("%s %s: failure removing OIL for channel (S,G)=(%s,%s)",
936 __FILE__, __PRETTY_FUNCTION__,
937 source_str, group_str);
938 }
939 }
940
941 if (PIM_DEBUG_MROUTE) {
942 char group_str[100];
943 char source_str[100];
944 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
945 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
946 zlog_debug("%s %s: (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
947 __FILE__, __PRETTY_FUNCTION__,
948 source_str, group_str,
949 proto_mask, oif->name, pim_ifp->mroute_vif_index);
950 }
951
952 return 0;
953 }
954
955 void igmp_source_forward_start(struct igmp_source *source)
956 {
957 struct igmp_group *group;
958 int result;
959
960 if (PIM_DEBUG_IGMP_TRACE) {
961 char source_str[100];
962 char group_str[100];
963 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
964 pim_inet4_dump("<group?>", source->source_group->group_addr, group_str, sizeof(group_str));
965 zlog_debug("%s: (S,G)=(%s,%s) igmp_sock=%d oif=%s fwd=%d",
966 __PRETTY_FUNCTION__,
967 source_str, group_str,
968 source->source_group->group_igmp_sock->fd,
969 source->source_group->group_igmp_sock->interface->name,
970 IGMP_SOURCE_TEST_FORWARDING(source->source_flags));
971 }
972
973 /* Prevent IGMP interface from installing multicast route multiple
974 times */
975 if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
976 return;
977 }
978
979 group = source->source_group;
980
981 if (!source->source_channel_oil) {
982 struct in_addr vif_source;
983 struct pim_interface *pim_oif;
984
985 if (!pim_rp_set_upstream_addr (&vif_source, source->source_addr))
986 return;
987
988 int input_iface_vif_index = fib_lookup_if_vif_index(vif_source);
989 if (input_iface_vif_index < 1) {
990 char source_str[100];
991 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
992 zlog_warn("%s %s: could not find input interface for source %s",
993 __FILE__, __PRETTY_FUNCTION__,
994 source_str);
995 return;
996 }
997
998 /*
999 Protect IGMP against adding looped MFC entries created by both
1000 source and receiver attached to the same interface. See TODO
1001 T22.
1002 */
1003 pim_oif = source->source_group->group_igmp_sock->interface->info;
1004 if (!pim_oif) {
1005 zlog_warn("%s: multicast not enabled on oif=%s ?",
1006 __PRETTY_FUNCTION__,
1007 source->source_group->group_igmp_sock->interface->name);
1008 return;
1009 }
1010 if (pim_oif->mroute_vif_index < 1) {
1011 zlog_warn("%s %s: oif=%s vif_index=%d < 1",
1012 __FILE__, __PRETTY_FUNCTION__,
1013 source->source_group->group_igmp_sock->interface->name,
1014 pim_oif->mroute_vif_index);
1015 return;
1016 }
1017 if (input_iface_vif_index == pim_oif->mroute_vif_index) {
1018 /* ignore request for looped MFC entry */
1019 if (PIM_DEBUG_IGMP_TRACE) {
1020 char source_str[100];
1021 char group_str[100];
1022 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
1023 pim_inet4_dump("<group?>", source->source_group->group_addr, group_str, sizeof(group_str));
1024 zlog_debug("%s: ignoring request for looped MFC entry (S,G)=(%s,%s): igmp_sock=%d oif=%s vif_index=%d",
1025 __PRETTY_FUNCTION__,
1026 source_str, group_str,
1027 source->source_group->group_igmp_sock->fd,
1028 source->source_group->group_igmp_sock->interface->name,
1029 input_iface_vif_index);
1030 }
1031 return;
1032 }
1033
1034 source->source_channel_oil = pim_channel_oil_add(group->group_addr,
1035 source->source_addr,
1036 input_iface_vif_index);
1037 if (!source->source_channel_oil) {
1038 char group_str[100];
1039 char source_str[100];
1040 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1041 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
1042 zlog_warn("%s %s: could not create OIL for channel (S,G)=(%s,%s)",
1043 __FILE__, __PRETTY_FUNCTION__,
1044 source_str, group_str);
1045 return;
1046 }
1047 }
1048
1049 result = pim_channel_add_oif(source->source_channel_oil,
1050 group->group_igmp_sock->interface,
1051 PIM_OIF_FLAG_PROTO_IGMP);
1052 if (result) {
1053 zlog_warn("%s: add_oif() failed with return=%d",
1054 __func__, result);
1055 return;
1056 }
1057
1058 /*
1059 Feed IGMPv3-gathered local membership information into PIM
1060 per-interface (S,G) state.
1061 */
1062 pim_ifchannel_local_membership_add(group->group_igmp_sock->interface,
1063 source->source_addr, group->group_addr);
1064
1065 IGMP_SOURCE_DO_FORWARDING(source->source_flags);
1066 }
1067
1068 /*
1069 igmp_source_forward_stop: stop fowarding, but keep the source
1070 igmp_source_delete: stop fowarding, and delete the source
1071 */
1072 void igmp_source_forward_stop(struct igmp_source *source)
1073 {
1074 struct igmp_group *group;
1075 int result;
1076
1077 if (PIM_DEBUG_IGMP_TRACE) {
1078 char source_str[100];
1079 char group_str[100];
1080 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
1081 pim_inet4_dump("<group?>", source->source_group->group_addr, group_str, sizeof(group_str));
1082 zlog_debug("%s: (S,G)=(%s,%s) igmp_sock=%d oif=%s fwd=%d",
1083 __PRETTY_FUNCTION__,
1084 source_str, group_str,
1085 source->source_group->group_igmp_sock->fd,
1086 source->source_group->group_igmp_sock->interface->name,
1087 IGMP_SOURCE_TEST_FORWARDING(source->source_flags));
1088 }
1089
1090 /* Prevent IGMP interface from removing multicast route multiple
1091 times */
1092 if (!IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
1093 return;
1094 }
1095
1096 group = source->source_group;
1097
1098 /*
1099 It appears that in certain circumstances that
1100 igmp_source_forward_stop is called when IGMP forwarding
1101 was not enabled in oif_flags for this outgoing interface.
1102 Possibly because of multiple calls. When that happens, we
1103 enter the below if statement and this function returns early
1104 which in turn triggers the calling function to assert.
1105 Making the call to del_oif and ignoring the return code
1106 fixes the issue without ill effect, similar to
1107 pim_forward_stop below.
1108 */
1109 result = del_oif(source->source_channel_oil,
1110 group->group_igmp_sock->interface,
1111 PIM_OIF_FLAG_PROTO_IGMP);
1112 if (result) {
1113 zlog_warn("%s: del_oif() failed with return=%d",
1114 __func__, result);
1115 return;
1116 }
1117
1118 /*
1119 Feed IGMPv3-gathered local membership information into PIM
1120 per-interface (S,G) state.
1121 */
1122 pim_ifchannel_local_membership_del(group->group_igmp_sock->interface,
1123 source->source_addr, group->group_addr);
1124
1125 IGMP_SOURCE_DONT_FORWARDING(source->source_flags);
1126 }
1127
1128 void pim_forward_start(struct pim_ifchannel *ch)
1129 {
1130 struct pim_upstream *up = ch->upstream;
1131
1132 if (PIM_DEBUG_PIM_TRACE) {
1133 char source_str[100];
1134 char group_str[100];
1135 char upstream_str[100];
1136
1137 pim_inet4_dump("<source?>", ch->source_addr, source_str, sizeof(source_str));
1138 pim_inet4_dump("<group?>", ch->group_addr, group_str, sizeof(group_str));
1139 pim_inet4_dump("<upstream?>", up->upstream_addr, upstream_str, sizeof(upstream_str));
1140 zlog_debug("%s: (S,G)=(%s,%s) oif=%s(%s)",
1141 __PRETTY_FUNCTION__,
1142 source_str, group_str, ch->interface->name, upstream_str);
1143 }
1144
1145 if (!up->channel_oil) {
1146 int input_iface_vif_index = fib_lookup_if_vif_index(up->upstream_addr);
1147 if (input_iface_vif_index < 1) {
1148 char source_str[100];
1149 pim_inet4_dump("<source?>", up->source_addr, source_str, sizeof(source_str));
1150 zlog_warn("%s %s: could not find input interface for source %s",
1151 __FILE__, __PRETTY_FUNCTION__,
1152 source_str);
1153 return;
1154 }
1155
1156 up->channel_oil = pim_channel_oil_add(up->group_addr, up->source_addr,
1157 input_iface_vif_index);
1158 if (!up->channel_oil) {
1159 char group_str[100];
1160 char source_str[100];
1161 pim_inet4_dump("<group?>", up->group_addr, group_str, sizeof(group_str));
1162 pim_inet4_dump("<source?>", up->source_addr, source_str, sizeof(source_str));
1163 zlog_warn("%s %s: could not create OIL for channel (S,G)=(%s,%s)",
1164 __FILE__, __PRETTY_FUNCTION__,
1165 source_str, group_str);
1166 return;
1167 }
1168 }
1169
1170 pim_channel_add_oif(up->channel_oil,
1171 ch->interface,
1172 PIM_OIF_FLAG_PROTO_PIM);
1173 }
1174
1175 void pim_forward_stop(struct pim_ifchannel *ch)
1176 {
1177 struct pim_upstream *up = ch->upstream;
1178
1179 if (PIM_DEBUG_PIM_TRACE) {
1180 char source_str[100];
1181 char group_str[100];
1182 pim_inet4_dump("<source?>", ch->source_addr, source_str, sizeof(source_str));
1183 pim_inet4_dump("<group?>", ch->group_addr, group_str, sizeof(group_str));
1184 zlog_debug("%s: (S,G)=(%s,%s) oif=%s",
1185 __PRETTY_FUNCTION__,
1186 source_str, group_str, ch->interface->name);
1187 }
1188
1189 if (!up->channel_oil) {
1190 char source_str[100];
1191 char group_str[100];
1192 pim_inet4_dump("<source?>", ch->source_addr, source_str, sizeof(source_str));
1193 pim_inet4_dump("<group?>", ch->group_addr, group_str, sizeof(group_str));
1194 zlog_warn("%s: (S,G)=(%s,%s) oif=%s missing channel OIL",
1195 __PRETTY_FUNCTION__,
1196 source_str, group_str, ch->interface->name);
1197
1198 return;
1199 }
1200
1201 del_oif(up->channel_oil,
1202 ch->interface,
1203 PIM_OIF_FLAG_PROTO_PIM);
1204 }