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