blob: 3530434dde01524be19884156ce0f62da2100906 [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 Marquesbcc4abe2009-08-17 18:18:59 -0300450 qpim_rpf_cache_refresh_last = pim_time_monotonic_sec();
Everton Marques613938d2009-08-13 15:39:31 -0300451 ++qpim_rpf_cache_refresh_events;
452
Everton Marques871dbcf2009-08-11 15:43:05 -0300453 return 0;
454}
455
456static void sched_rpf_cache_refresh()
457{
Everton Marques613938d2009-08-13 15:39:31 -0300458 ++qpim_rpf_cache_refresh_requests;
459
460 if (qpim_rpf_cache_refresher) {
461 /* Refresh timer is already running */
Everton Marques871dbcf2009-08-11 15:43:05 -0300462 return;
Everton Marques613938d2009-08-13 15:39:31 -0300463 }
464
465 /* Start refresh timer */
Everton Marques871dbcf2009-08-11 15:43:05 -0300466
467 if (PIM_DEBUG_ZEBRA) {
468 zlog_debug("%s: triggering %ld msec timer",
469 __PRETTY_FUNCTION__,
470 qpim_rpf_cache_refresh_delay_msec);
471 }
472
473 THREAD_TIMER_MSEC_ON(master, qpim_rpf_cache_refresher,
474 on_rpf_cache_refresh,
475 0, qpim_rpf_cache_refresh_delay_msec);
476}
477
478static int redist_read_ipv4_route(int command, struct zclient *zclient,
479 zebra_size_t length)
480{
481 struct stream *s;
482 struct zapi_ipv4 api;
483 unsigned long ifindex;
484 struct in_addr nexthop;
485 struct prefix_ipv4 p;
486 int min_len = 4;
487
488 if (length < min_len) {
489 zlog_warn("%s %s: short buffer: length=%d min=%d",
490 __FILE__, __PRETTY_FUNCTION__,
491 length, min_len);
492 return -1;
493 }
494
495 s = zclient->ibuf;
496 ifindex = 0;
497 nexthop.s_addr = 0;
498
499 /* Type, flags, message. */
500 api.type = stream_getc(s);
501 api.flags = stream_getc(s);
502 api.message = stream_getc(s);
503
504 /* IPv4 prefix length. */
505 memset(&p, 0, sizeof(struct prefix_ipv4));
506 p.family = AF_INET;
507 p.prefixlen = stream_getc(s);
508
509 min_len +=
510 PSIZE(p.prefixlen) +
511 CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP) ? 5 : 0 +
512 CHECK_FLAG(api.message, ZAPI_MESSAGE_IFINDEX) ? 5 : 0 +
513 CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ? 1 : 0 +
514 CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? 4 : 0;
515
516 if (PIM_DEBUG_ZEBRA) {
517 zlog_debug("%s %s: length=%d min_len=%d flags=%s%s%s%s",
518 __FILE__, __PRETTY_FUNCTION__,
519 length, min_len,
520 CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP) ? "nh" : "",
521 CHECK_FLAG(api.message, ZAPI_MESSAGE_IFINDEX) ? " ifi" : "",
522 CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ? " dist" : "",
523 CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? " metr" : "");
524 }
525
526 if (length < min_len) {
527 zlog_warn("%s %s: short buffer: length=%d min_len=%d flags=%s%s%s%s",
528 __FILE__, __PRETTY_FUNCTION__,
529 length, min_len,
530 CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP) ? "nh" : "",
531 CHECK_FLAG(api.message, ZAPI_MESSAGE_IFINDEX) ? " ifi" : "",
532 CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ? " dist" : "",
533 CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? " metr" : "");
534 return -1;
535 }
536
537 /* IPv4 prefix. */
538 stream_get(&p.prefix, s, PSIZE(p.prefixlen));
539
540 /* Nexthop, ifindex, distance, metric. */
541 if (CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP)) {
542 api.nexthop_num = stream_getc(s);
543 nexthop.s_addr = stream_get_ipv4(s);
544 }
545 if (CHECK_FLAG(api.message, ZAPI_MESSAGE_IFINDEX)) {
546 api.ifindex_num = stream_getc(s);
547 ifindex = stream_getl(s);
548 }
549
550 api.distance = CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ?
551 api.distance = stream_getc(s) :
552 0;
553
554 api.metric = CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ?
555 stream_getl(s) :
556 0;
557
558 switch (command) {
559 case ZEBRA_IPV4_ROUTE_ADD:
560 if (PIM_DEBUG_ZEBRA) {
561 char buf[2][INET_ADDRSTRLEN];
562 zlog_debug("%s: add %s %s/%d "
563 "nexthop %s ifindex %ld metric%s %u distance%s %u",
564 __PRETTY_FUNCTION__,
565 zebra_route_string(api.type),
566 inet_ntop(AF_INET, &p.prefix, buf[0], sizeof(buf[0])),
567 p.prefixlen,
568 inet_ntop(AF_INET, &nexthop, buf[1], sizeof(buf[1])),
569 ifindex,
570 CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? "-recv" : "-miss",
571 api.metric,
572 CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ? "-recv" : "-miss",
573 api.distance);
574 }
575 break;
576 case ZEBRA_IPV4_ROUTE_DELETE:
577 if (PIM_DEBUG_ZEBRA) {
578 char buf[2][INET_ADDRSTRLEN];
579 zlog_debug("%s: delete %s %s/%d "
580 "nexthop %s ifindex %ld metric%s %u distance%s %u",
581 __PRETTY_FUNCTION__,
582 zebra_route_string(api.type),
583 inet_ntop(AF_INET, &p.prefix, buf[0], sizeof(buf[0])),
584 p.prefixlen,
585 inet_ntop(AF_INET, &nexthop, buf[1], sizeof(buf[1])),
586 ifindex,
587 CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC) ? "-recv" : "-miss",
588 api.metric,
589 CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE) ? "-recv" : "-miss",
590 api.distance);
591 }
592 break;
593 default:
594 zlog_warn("%s: unknown command=%d", __PRETTY_FUNCTION__, command);
595 return -1;
596 }
597
598 sched_rpf_cache_refresh();
599
600 return 0;
601}
602
603void pim_zebra_init()
604{
605 struct zclient *zclient;
606 int i;
607
608#ifdef HAVE_TCP_ZEBRA
609 zlog_notice("zclient update contacting ZEBRA daemon at socket TCP %s,%d", "127.0.0.1", ZEBRA_PORT);
610#else
611 zlog_notice("zclient update contacting ZEBRA daemon at socket UNIX %s", ZEBRA_SERV_PATH);
612#endif
613
614 /* Socket for receiving updates from Zebra daemon */
615 zclient = zclient_new();
616
617 zclient->router_id_update = pim_router_id_update_zebra;
618 zclient->interface_add = pim_zebra_if_add;
619 zclient->interface_delete = pim_zebra_if_del;
620 zclient->interface_up = pim_zebra_if_state_up;
621 zclient->interface_down = pim_zebra_if_state_down;
622 zclient->interface_address_add = pim_zebra_if_address_add;
623 zclient->interface_address_delete = pim_zebra_if_address_del;
624 zclient->ipv4_route_add = redist_read_ipv4_route;
625 zclient->ipv4_route_delete = redist_read_ipv4_route;
626
627 zclient_init(zclient, ZEBRA_ROUTE_PIM);
628 zlog_info("zclient_init cleared redistribution request");
629
630 zassert(zclient->redist_default == ZEBRA_ROUTE_PIM);
631
632 /* Request all redistribution */
633 for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
634 if (i == zclient->redist_default)
635 continue;
636 zclient->redist[i] = 1;
637 zlog_info("%s: requesting redistribution for %s (%i)",
638 __PRETTY_FUNCTION__, zebra_route_string(i), i);
639 }
640
641 /* Request default information */
642 zclient->default_information = 1;
643 zlog_info("%s: requesting default information redistribution",
644 __PRETTY_FUNCTION__);
645
646 zlog_notice("%s: zclient update socket initialized",
647 __PRETTY_FUNCTION__);
648
649 zassert(!qpim_zclient_lookup);
650 qpim_zclient_lookup = zclient_lookup_new();
651 zassert(qpim_zclient_lookup);
652}
653
654void igmp_anysource_forward_start(struct igmp_group *group)
655{
656 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
657 zassert(group->group_filtermode_isexcl);
658 zassert(listcount(group->group_source_list) < 1);
659
660 if (PIM_DEBUG_IGMP_TRACE) {
661 zlog_debug("%s %s: UNIMPLEMENTED",
662 __FILE__, __PRETTY_FUNCTION__);
663 }
664}
665
666void igmp_anysource_forward_stop(struct igmp_group *group)
667{
668 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
669 zassert((!group->group_filtermode_isexcl) || (listcount(group->group_source_list) > 0));
670
671 if (PIM_DEBUG_IGMP_TRACE) {
672 zlog_debug("%s %s: UNIMPLEMENTED",
673 __FILE__, __PRETTY_FUNCTION__);
674 }
675}
676
677static int fib_lookup_if_vif_index(struct in_addr addr)
678{
679 struct pim_zlookup_nexthop nexthop_tab[PIM_NEXTHOP_IFINDEX_TAB_SIZE];
680 int num_ifindex;
681 int vif_index;
682 int first_ifindex;
683
684 num_ifindex = zclient_lookup_nexthop(qpim_zclient_lookup, nexthop_tab,
685 PIM_NEXTHOP_IFINDEX_TAB_SIZE, addr,
686 PIM_NEXTHOP_LOOKUP_MAX);
687 if (num_ifindex < 1) {
688 char addr_str[100];
689 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
690 zlog_warn("%s %s: could not find nexthop ifindex for address %s",
691 __FILE__, __PRETTY_FUNCTION__,
692 addr_str);
693 return -1;
694 }
695
696 first_ifindex = nexthop_tab[0].ifindex;
697
698 if (num_ifindex > 1) {
699 char addr_str[100];
700 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
701 zlog_debug("%s %s: FIXME ignoring multiple nexthop ifindex'es num_ifindex=%d for address %s (using only ifindex=%d)",
702 __FILE__, __PRETTY_FUNCTION__,
703 num_ifindex, addr_str, first_ifindex);
704 /* debug warning only, do not return */
705 }
706
707 if (PIM_DEBUG_ZEBRA) {
708 char addr_str[100];
709 pim_inet4_dump("<ifaddr?>", addr, addr_str, sizeof(addr_str));
710 zlog_debug("%s %s: found nexthop ifindex=%d (interface %s) for address %s",
711 __FILE__, __PRETTY_FUNCTION__,
712 first_ifindex, ifindex2ifname(first_ifindex), addr_str);
713 }
714
715 vif_index = pim_if_find_vifindex_by_ifindex(first_ifindex);
716
717 if (vif_index < 1) {
718 char addr_str[100];
719 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
720 zlog_warn("%s %s: low vif_index=%d < 1 nexthop for address %s",
721 __FILE__, __PRETTY_FUNCTION__,
722 vif_index, addr_str);
723 return -2;
724 }
725
726 zassert(qpim_mroute_oif_highest_vif_index < MAXVIFS);
727
728 if (vif_index > qpim_mroute_oif_highest_vif_index) {
729 char addr_str[100];
730 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
731 zlog_warn("%s %s: high vif_index=%d > highest_vif_index=%d nexthop for address %s",
732 __FILE__, __PRETTY_FUNCTION__,
733 vif_index, qpim_mroute_oif_highest_vif_index, addr_str);
734
735 zlog_warn("%s %s: pim disabled on interface %s vif_index=%d ?",
736 __FILE__, __PRETTY_FUNCTION__,
737 ifindex2ifname(vif_index),
738 vif_index);
739
740 return -3;
741 }
742
743 return vif_index;
744}
745
746static int add_oif(struct channel_oil *channel_oil,
747 struct interface *oif,
748 uint32_t proto_mask)
749{
750 struct pim_interface *pim_ifp;
751 int old_ttl;
752
753 zassert(channel_oil);
754
755 pim_ifp = oif->info;
756
757 if (pim_ifp->mroute_vif_index < 1) {
758 zlog_warn("%s %s: interface %s vif_index=%d < 1",
759 __FILE__, __PRETTY_FUNCTION__,
760 oif->name, pim_ifp->mroute_vif_index);
761 return -1;
762 }
763
764#ifdef PIM_ENFORCE_LOOPFREE_MFC
765 /*
766 Prevent creating MFC entry with OIF=IIF.
767
768 This is a protection against implementation mistakes.
769
770 PIM protocol implicitely ensures loopfree multicast topology.
771
772 IGMP must be protected against adding looped MFC entries created
773 by both source and receiver attached to the same interface. See
774 TODO T22.
775 */
776 if (pim_ifp->mroute_vif_index == channel_oil->oil.mfcc_parent) {
777 char group_str[100];
778 char source_str[100];
779 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
780 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
781 zlog_warn("%s %s: refusing protocol mask %u request for IIF=OIF=%s (vif_index=%d) for channel (S,G)=(%s,%s)",
782 __FILE__, __PRETTY_FUNCTION__,
783 proto_mask, oif->name, pim_ifp->mroute_vif_index,
784 source_str, group_str);
785 return -2;
786 }
787#endif
788
789 zassert(qpim_mroute_oif_highest_vif_index < MAXVIFS);
790 zassert(pim_ifp->mroute_vif_index <= qpim_mroute_oif_highest_vif_index);
791
792 /* Prevent single protocol from subscribing same interface to
793 channel (S,G) multiple times */
794 if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask) {
795 char group_str[100];
796 char source_str[100];
797 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
798 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
799 zlog_warn("%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
800 __FILE__, __PRETTY_FUNCTION__,
801 proto_mask, oif->name, pim_ifp->mroute_vif_index,
802 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
803 source_str, group_str);
804 return -3;
805 }
806
807 /* Allow other protocol to request subscription of same interface to
808 channel (S,G) multiple times, by silently ignoring further
809 requests */
810 if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & PIM_OIF_FLAG_PROTO_ANY) {
811
812 /* Check the OIF really exists before returning, and only log
813 warning otherwise */
814 if (channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] < 1) {
815 char group_str[100];
816 char source_str[100];
817 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
818 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
819 zlog_warn("%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
820 __FILE__, __PRETTY_FUNCTION__,
821 proto_mask, oif->name, pim_ifp->mroute_vif_index,
822 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
823 source_str, group_str);
824 }
825
826 return 0;
827 }
828
829 old_ttl = channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index];
830
831 if (old_ttl > 0) {
832 char group_str[100];
833 char source_str[100];
834 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
835 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
836 zlog_warn("%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%s,%s)",
837 __FILE__, __PRETTY_FUNCTION__,
838 oif->name, pim_ifp->mroute_vif_index,
839 source_str, group_str);
840 return -4;
841 }
842
843 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = PIM_MROUTE_MIN_TTL;
844
845 if (pim_mroute_add(&channel_oil->oil)) {
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_warn("%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
851 __FILE__, __PRETTY_FUNCTION__,
852 oif->name, pim_ifp->mroute_vif_index,
853 source_str, group_str);
854
855 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = old_ttl;
856 return -5;
857 }
858
859 channel_oil->oif_creation[pim_ifp->mroute_vif_index] = pim_time_monotonic_sec();
860 ++channel_oil->oil_size;
861 channel_oil->oif_flags[pim_ifp->mroute_vif_index] |= proto_mask;
862
863 return 0;
864}
865
866static int del_oif(struct channel_oil *channel_oil,
867 struct interface *oif,
868 uint32_t proto_mask)
869{
870 struct pim_interface *pim_ifp;
871 int old_ttl;
872
873 zassert(channel_oil);
874
875 pim_ifp = oif->info;
876
877 zassert(pim_ifp->mroute_vif_index >= 1);
878 zassert(qpim_mroute_oif_highest_vif_index < MAXVIFS);
879 zassert(pim_ifp->mroute_vif_index <= qpim_mroute_oif_highest_vif_index);
880
881 /* Prevent single protocol from unsubscribing same interface from
882 channel (S,G) multiple times */
883 if (!(channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask)) {
884 char group_str[100];
885 char source_str[100];
886 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
887 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
888 zlog_warn("%s %s: nonexistent protocol mask %u removed OIF %s (vif_index=%d, min_ttl=%d) from channel (S,G)=(%s,%s)",
889 __FILE__, __PRETTY_FUNCTION__,
890 proto_mask, oif->name, pim_ifp->mroute_vif_index,
891 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
892 source_str, group_str);
893 return -2;
894 }
895
896 /* Mark that protocol is no longer interested in this OIF */
897 channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~proto_mask;
898
899 /* Allow multiple protocols to unsubscribe same interface from
900 channel (S,G) multiple times, by silently ignoring requests while
901 there is at least one protocol interested in the channel */
902 if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & PIM_OIF_FLAG_PROTO_ANY) {
903
904 /* Check the OIF keeps existing before returning, and only log
905 warning otherwise */
906 if (channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] < 1) {
907 char group_str[100];
908 char source_str[100];
909 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
910 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
911 zlog_warn("%s %s: protocol mask %u removing nonexistent OIF %s (vif_index=%d, min_ttl=%d) from channel (S,G)=(%s,%s)",
912 __FILE__, __PRETTY_FUNCTION__,
913 proto_mask, oif->name, pim_ifp->mroute_vif_index,
914 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index],
915 source_str, group_str);
916 }
917
918 return 0;
919 }
920
921 old_ttl = channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index];
922
923 if (old_ttl < 1) {
924 char group_str[100];
925 char source_str[100];
926 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
927 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
928 zlog_warn("%s %s: interface %s (vif_index=%d) is not output for channel (S,G)=(%s,%s)",
929 __FILE__, __PRETTY_FUNCTION__,
930 oif->name, pim_ifp->mroute_vif_index,
931 source_str, group_str);
932 return -3;
933 }
934
935 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = 0;
936
937 if (pim_mroute_add(&channel_oil->oil)) {
938 char group_str[100];
939 char source_str[100];
940 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
941 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
942 zlog_warn("%s %s: could not remove output interface %s (vif_index=%d) from channel (S,G)=(%s,%s)",
943 __FILE__, __PRETTY_FUNCTION__,
944 oif->name, pim_ifp->mroute_vif_index,
945 source_str, group_str);
946
947 channel_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index] = old_ttl;
948 return -4;
949 }
950
951 --channel_oil->oil_size;
952
953 if (channel_oil->oil_size < 1) {
954 if (pim_mroute_del(&channel_oil->oil)) {
955 /* just log a warning in case of failure */
956 char group_str[100];
957 char source_str[100];
958 pim_inet4_dump("<group?>", channel_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
959 pim_inet4_dump("<source?>", channel_oil->oil.mfcc_origin, source_str, sizeof(source_str));
960 zlog_warn("%s %s: failure removing OIL for channel (S,G)=(%s,%s)",
961 __FILE__, __PRETTY_FUNCTION__,
962 source_str, group_str);
963 }
964 }
965
966 return 0;
967}
968
969void igmp_source_forward_start(struct igmp_source *source)
970{
971 struct igmp_group *group;
972
973 if (PIM_DEBUG_IGMP_TRACE) {
974 char source_str[100];
975 char group_str[100];
976 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
977 pim_inet4_dump("<group?>", source->source_group->group_addr, group_str, sizeof(group_str));
978 zlog_debug("%s: (S,G)=(%s,%s) igmp_sock=%d oif=%s fwd=%d",
979 __PRETTY_FUNCTION__,
980 source_str, group_str,
981 source->source_group->group_igmp_sock->fd,
982 source->source_group->group_igmp_sock->interface->name,
983 IGMP_SOURCE_TEST_FORWARDING(source->source_flags));
984 }
985
986 /* Prevent IGMP interface from installing multicast route multiple
987 times */
988 if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
989 return;
990 }
991
992 group = source->source_group;
993
994 if (!source->source_channel_oil) {
995 struct pim_interface *pim_oif;
996 int input_iface_vif_index = fib_lookup_if_vif_index(source->source_addr);
997 if (input_iface_vif_index < 1) {
998 char source_str[100];
999 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
1000 zlog_warn("%s %s: could not find input interface for source %s",
1001 __FILE__, __PRETTY_FUNCTION__,
1002 source_str);
1003 return;
1004 }
1005
1006 /*
1007 Protect IGMP against adding looped MFC entries created by both
1008 source and receiver attached to the same interface. See TODO
1009 T22.
1010 */
1011 pim_oif = source->source_group->group_igmp_sock->interface->info;
1012 if (!pim_oif) {
1013 zlog_warn("%s: multicast not enabled on oif=%s ?",
1014 __PRETTY_FUNCTION__,
1015 source->source_group->group_igmp_sock->interface->name);
1016 return;
1017 }
1018 if (pim_oif->mroute_vif_index < 1) {
1019 zlog_warn("%s %s: oif=%s vif_index=%d < 1",
1020 __FILE__, __PRETTY_FUNCTION__,
1021 source->source_group->group_igmp_sock->interface->name,
1022 pim_oif->mroute_vif_index);
1023 return;
1024 }
1025 if (input_iface_vif_index == pim_oif->mroute_vif_index) {
1026 /* ignore request for looped MFC entry */
1027 if (PIM_DEBUG_IGMP_TRACE) {
1028 char source_str[100];
1029 char group_str[100];
1030 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
1031 pim_inet4_dump("<group?>", source->source_group->group_addr, group_str, sizeof(group_str));
1032 zlog_debug("%s: ignoring request for looped MFC entry (S,G)=(%s,%s): igmp_sock=%d oif=%s vif_index=%d",
1033 __PRETTY_FUNCTION__,
1034 source_str, group_str,
1035 source->source_group->group_igmp_sock->fd,
1036 source->source_group->group_igmp_sock->interface->name,
1037 input_iface_vif_index);
1038 }
1039 return;
1040 }
1041
1042 source->source_channel_oil = pim_channel_oil_add(group->group_addr,
1043 source->source_addr,
1044 input_iface_vif_index);
1045 if (!source->source_channel_oil) {
1046 char group_str[100];
1047 char source_str[100];
1048 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1049 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
1050 zlog_warn("%s %s: could not create OIL for channel (S,G)=(%s,%s)",
1051 __FILE__, __PRETTY_FUNCTION__,
1052 source_str, group_str);
1053 return;
1054 }
1055 }
1056
1057 if (add_oif(source->source_channel_oil,
1058 group->group_igmp_sock->interface,
1059 PIM_OIF_FLAG_PROTO_IGMP)) {
1060 return;
1061 }
1062
1063 /*
1064 Feed IGMPv3-gathered local membership information into PIM
1065 per-interface (S,G) state.
1066 */
1067 pim_ifchannel_local_membership_add(group->group_igmp_sock->interface,
1068 source->source_addr, group->group_addr);
1069
1070 IGMP_SOURCE_DO_FORWARDING(source->source_flags);
1071}
1072
1073void igmp_source_forward_stop(struct igmp_source *source)
1074{
1075 struct igmp_group *group;
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 if (del_oif(source->source_channel_oil,
1099 group->group_igmp_sock->interface,
1100 PIM_OIF_FLAG_PROTO_IGMP)) {
1101 return;
1102 }
1103
1104 /*
1105 Feed IGMPv3-gathered local membership information into PIM
1106 per-interface (S,G) state.
1107 */
1108 pim_ifchannel_local_membership_del(group->group_igmp_sock->interface,
1109 source->source_addr, group->group_addr);
1110
1111 IGMP_SOURCE_DONT_FORWARDING(source->source_flags);
1112}
1113
1114void pim_forward_start(struct pim_ifchannel *ch)
1115{
1116 struct pim_upstream *up = ch->upstream;
1117
1118 if (PIM_DEBUG_PIM_TRACE) {
1119 char source_str[100];
1120 char group_str[100];
1121 pim_inet4_dump("<source?>", ch->source_addr, source_str, sizeof(source_str));
1122 pim_inet4_dump("<group?>", ch->group_addr, group_str, sizeof(group_str));
1123 zlog_debug("%s: (S,G)=(%s,%s) oif=%s",
1124 __PRETTY_FUNCTION__,
1125 source_str, group_str, ch->interface->name);
1126 }
1127
1128 if (!up->channel_oil) {
1129 int input_iface_vif_index = fib_lookup_if_vif_index(up->source_addr);
1130 if (input_iface_vif_index < 1) {
1131 char source_str[100];
1132 pim_inet4_dump("<source?>", up->source_addr, source_str, sizeof(source_str));
1133 zlog_warn("%s %s: could not find input interface for source %s",
1134 __FILE__, __PRETTY_FUNCTION__,
1135 source_str);
1136 return;
1137 }
1138
1139 up->channel_oil = pim_channel_oil_add(up->group_addr, up->source_addr,
1140 input_iface_vif_index);
1141 if (!up->channel_oil) {
1142 char group_str[100];
1143 char source_str[100];
1144 pim_inet4_dump("<group?>", up->group_addr, group_str, sizeof(group_str));
1145 pim_inet4_dump("<source?>", up->source_addr, source_str, sizeof(source_str));
1146 zlog_warn("%s %s: could not create OIL for channel (S,G)=(%s,%s)",
1147 __FILE__, __PRETTY_FUNCTION__,
1148 source_str, group_str);
1149 return;
1150 }
1151 }
1152
1153 add_oif(up->channel_oil,
1154 ch->interface,
1155 PIM_OIF_FLAG_PROTO_PIM);
1156}
1157
1158void pim_forward_stop(struct pim_ifchannel *ch)
1159{
1160 struct pim_upstream *up = ch->upstream;
1161
1162 if (PIM_DEBUG_PIM_TRACE) {
1163 char source_str[100];
1164 char group_str[100];
1165 pim_inet4_dump("<source?>", ch->source_addr, source_str, sizeof(source_str));
1166 pim_inet4_dump("<group?>", ch->group_addr, group_str, sizeof(group_str));
1167 zlog_debug("%s: (S,G)=(%s,%s) oif=%s",
1168 __PRETTY_FUNCTION__,
1169 source_str, group_str, ch->interface->name);
1170 }
1171
1172 if (!up->channel_oil) {
1173 char source_str[100];
1174 char group_str[100];
1175 pim_inet4_dump("<source?>", ch->source_addr, source_str, sizeof(source_str));
1176 pim_inet4_dump("<group?>", ch->group_addr, group_str, sizeof(group_str));
1177 zlog_warn("%s: (S,G)=(%s,%s) oif=%s missing channel OIL",
1178 __PRETTY_FUNCTION__,
1179 source_str, group_str, ch->interface->name);
1180
1181 return;
1182 }
1183
1184 del_oif(up->channel_oil,
1185 ch->interface,
1186 PIM_OIF_FLAG_PROTO_PIM);
1187}