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