]> git.proxmox.com Git - mirror_frr.git/blame_incremental - pimd/pim_mroute.c
*: add indent control files
[mirror_frr.git] / pimd / pim_mroute.c
... / ...
CommitLineData
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 along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#include <zebra.h>
21#include "log.h"
22#include "privs.h"
23#include "if.h"
24#include "prefix.h"
25#include "vty.h"
26#include "plist.h"
27
28#include "pimd.h"
29#include "pim_rpf.h"
30#include "pim_mroute.h"
31#include "pim_oil.h"
32#include "pim_str.h"
33#include "pim_time.h"
34#include "pim_iface.h"
35#include "pim_macro.h"
36#include "pim_rp.h"
37#include "pim_oil.h"
38#include "pim_register.h"
39#include "pim_ifchannel.h"
40#include "pim_zlookup.h"
41#include "pim_ssm.h"
42
43/* GLOBAL VARS */
44static struct thread *qpim_mroute_socket_reader = NULL;
45
46static void mroute_read_on(void);
47
48static int pim_mroute_set(int fd, int enable)
49{
50 int err;
51 int opt = enable ? MRT_INIT : MRT_DONE;
52 socklen_t opt_len = sizeof(opt);
53 int rcvbuf = 1024 * 1024 * 8;
54 long flags;
55
56 err = setsockopt(fd, IPPROTO_IP, opt, &opt, opt_len);
57 if (err) {
58 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,%s=%d): errno=%d: %s",
59 __FILE__, __PRETTY_FUNCTION__,
60 fd, enable ? "MRT_INIT" : "MRT_DONE", opt, errno, safe_strerror(errno));
61 return -1;
62 }
63
64 err = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));
65 if (err) {
66 zlog_warn("%s: failure: setsockopt(fd=%d, SOL_SOCKET, %d): errno=%d: %s",
67 __PRETTY_FUNCTION__, fd, rcvbuf, errno, safe_strerror(errno));
68 }
69
70 flags = fcntl(fd, F_GETFL, 0);
71 if (flags < 0)
72 {
73 zlog_warn("Could not get flags on socket fd:%d %d %s",
74 fd, errno, safe_strerror(errno));
75 close (fd);
76 return -1;
77 }
78 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK))
79 {
80 zlog_warn("Could not set O_NONBLOCK on socket fd:%d %d %s",
81 fd, errno, safe_strerror(errno));
82 close(fd);
83 return -1;
84 }
85
86 if (enable)
87 {
88#if defined linux
89 int upcalls = IGMPMSG_WRVIFWHOLE;
90 opt = MRT_PIM;
91
92 err = setsockopt (fd, IPPROTO_IP, opt, &upcalls, sizeof (upcalls));
93 if (err)
94 {
95 zlog_warn ("Failure to register for VIFWHOLE and WRONGVIF upcalls %d %s",
96 errno, safe_strerror (errno));
97 return -1;
98 }
99#else
100 zlog_warn ("PIM-SM will not work properly on this platform, until the ability to receive the WRVIFWHOLE upcall");
101#endif
102 }
103
104 return 0;
105}
106
107static const char *igmpmsgtype2str[IGMPMSG_WRVIFWHOLE + 1] = {
108 "<unknown_upcall?>",
109 "NOCACHE",
110 "WRONGVIF",
111 "WHOLEPKT",
112 "WRVIFWHOLE" };
113
114static int
115pim_mroute_msg_nocache (int fd, struct interface *ifp, const struct igmpmsg *msg)
116{
117 struct pim_interface *pim_ifp = ifp->info;
118 struct pim_upstream *up;
119 struct pim_rpf *rpg;
120 struct prefix_sg sg;
121
122 rpg = RP(msg->im_dst);
123 /*
124 * If the incoming interface is unknown OR
125 * the Interface type is SSM we don't need to
126 * do anything here
127 */
128 if ((pim_rpf_addr_is_inaddr_none (rpg)) ||
129 (!pim_ifp) ||
130 (!(PIM_I_am_DR(pim_ifp))))
131 {
132 if (PIM_DEBUG_MROUTE_DETAIL)
133 zlog_debug ("%s: Interface is not configured correctly to handle incoming packet: Could be !DR, !pim_ifp, !SM, !RP",
134 __PRETTY_FUNCTION__);
135 return 0;
136 }
137
138 /*
139 * If we've received a multicast packet that isn't connected to
140 * us
141 */
142 if (!pim_if_connected_to_source (ifp, msg->im_src))
143 {
144 if (PIM_DEBUG_MROUTE_DETAIL)
145 zlog_debug ("%s: Received incoming packet that doesn't originate on our seg",
146 __PRETTY_FUNCTION__);
147 return 0;
148 }
149
150 memset (&sg, 0, sizeof (struct prefix_sg));
151 sg.src = msg->im_src;
152 sg.grp = msg->im_dst;
153
154 up = pim_upstream_find_or_add (&sg, ifp, PIM_UPSTREAM_FLAG_MASK_FHR, __PRETTY_FUNCTION__);
155 if (!up)
156 {
157 if (PIM_DEBUG_MROUTE)
158 {
159 zlog_debug("%s: Failure to add upstream information for %s",
160 __PRETTY_FUNCTION__,
161 pim_str_sg_dump (&sg));
162 }
163 return 0;
164 }
165
166 /*
167 * I moved this debug till after the actual add because
168 * I want to take advantage of the up->sg_str being filled in.
169 */
170 if (PIM_DEBUG_MROUTE) {
171 zlog_debug("%s: Adding a Route %s for WHOLEPKT consumption",
172 __PRETTY_FUNCTION__, up->sg_str);
173 }
174
175 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up->flags);
176 pim_upstream_keep_alive_timer_start (up, qpim_keep_alive_time);
177
178 up->channel_oil->cc.pktcnt++;
179 PIM_UPSTREAM_FLAG_SET_FHR(up->flags);
180 // resolve mfcc_parent prior to mroute_add in channel_add_oif
181 if (up->channel_oil->oil.mfcc_parent >= MAXVIFS)
182 {
183 int vif_index = 0;
184 vif_index =
185 pim_if_find_vifindex_by_ifindex (up->rpf.source_nexthop.
186 interface->ifindex);
187 up->channel_oil->oil.mfcc_parent = vif_index;
188 }
189 pim_register_join (up);
190
191 return 0;
192}
193
194static int
195pim_mroute_msg_wholepkt (int fd, struct interface *ifp, const char *buf)
196{
197 struct pim_interface *pim_ifp;
198 struct prefix_sg sg;
199 struct pim_rpf *rpg;
200 const struct ip *ip_hdr;
201 struct pim_upstream *up;
202
203 ip_hdr = (const struct ip *)buf;
204
205 memset (&sg, 0, sizeof (struct prefix_sg));
206 sg.src = ip_hdr->ip_src;
207 sg.grp = ip_hdr->ip_dst;
208
209 up = pim_upstream_find(&sg);
210 if (!up) {
211 struct prefix_sg star = sg;
212 star.src.s_addr = INADDR_ANY;
213
214 up = pim_upstream_find(&star);
215
216 if (up && PIM_UPSTREAM_FLAG_TEST_SRC_IGMP(up->flags))
217 {
218 up = pim_upstream_add (&sg, ifp, PIM_UPSTREAM_FLAG_MASK_SRC_LHR, __PRETTY_FUNCTION__);
219 if (!up)
220 {
221 if (PIM_DEBUG_MROUTE)
222 zlog_debug ("%s: Unable to create upstream information for %s",
223 __PRETTY_FUNCTION__, pim_str_sg_dump (&sg));
224 return 0;
225 }
226 pim_upstream_keep_alive_timer_start (up, qpim_keep_alive_time);
227 pim_upstream_inherited_olist (up);
228 pim_upstream_switch(up, PIM_UPSTREAM_JOINED);
229
230 if (PIM_DEBUG_MROUTE)
231 zlog_debug ("%s: Creating %s upstream on LHR",
232 __PRETTY_FUNCTION__, up->sg_str);
233 return 0;
234 }
235 if (PIM_DEBUG_MROUTE_DETAIL) {
236 zlog_debug("%s: Unable to find upstream channel WHOLEPKT%s",
237 __PRETTY_FUNCTION__, pim_str_sg_dump (&sg));
238 }
239 return 0;
240 }
241
242 pim_ifp = up->rpf.source_nexthop.interface->info;
243
244 rpg = RP(sg.grp);
245
246 if ((pim_rpf_addr_is_inaddr_none (rpg)) ||
247 (!pim_ifp) ||
248 (!(PIM_I_am_DR(pim_ifp)))) {
249 if (PIM_DEBUG_MROUTE) {
250 zlog_debug("%s: Failed Check send packet", __PRETTY_FUNCTION__);
251 }
252 return 0;
253 }
254
255 /*
256 * If we've received a register suppress
257 */
258 if (!up->t_rs_timer)
259 {
260 if (pim_is_grp_ssm (sg.grp))
261 {
262 if (PIM_DEBUG_PIM_REG)
263 zlog_debug ("%s register forward skipped as group is SSM",
264 pim_str_sg_dump (&sg));
265 return 0;
266 }
267 pim_register_send((uint8_t *)buf + sizeof(struct ip),
268 ntohs (ip_hdr->ip_len) - sizeof (struct ip),
269 pim_ifp->primary_address, rpg, 0, up);
270 }
271 return 0;
272}
273
274static int
275pim_mroute_msg_wrongvif (int fd, struct interface *ifp, const struct igmpmsg *msg)
276{
277 struct pim_ifchannel *ch;
278 struct pim_interface *pim_ifp;
279 struct prefix_sg sg;
280
281 memset (&sg, 0, sizeof (struct prefix_sg));
282 sg.src = msg->im_src;
283 sg.grp = msg->im_dst;
284
285 /*
286 Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
287
288 RFC 4601 4.8.2. PIM-SSM-Only Routers
289
290 iif is the incoming interface of the packet.
291 if (iif is in inherited_olist(S,G)) {
292 send Assert(S,G) on iif
293 }
294 */
295
296 if (!ifp) {
297 if (PIM_DEBUG_MROUTE)
298 zlog_debug("%s: WRONGVIF (S,G)=%s could not find input interface for input_vif_index=%d",
299 __PRETTY_FUNCTION__,
300 pim_str_sg_dump (&sg), msg->im_vif);
301 return -1;
302 }
303
304 pim_ifp = ifp->info;
305 if (!pim_ifp) {
306 if (PIM_DEBUG_MROUTE)
307 zlog_debug("%s: WRONGVIF (S,G)=%s multicast not enabled on interface %s",
308 __PRETTY_FUNCTION__,
309 pim_str_sg_dump (&sg), ifp->name);
310 return -2;
311 }
312
313 ch = pim_ifchannel_find(ifp, &sg);
314 if (!ch) {
315 struct prefix_sg star_g = sg;
316 if (PIM_DEBUG_MROUTE)
317 zlog_debug("%s: WRONGVIF (S,G)=%s could not find channel on interface %s",
318 __PRETTY_FUNCTION__,
319 pim_str_sg_dump(&sg), ifp->name);
320
321 star_g.src.s_addr = INADDR_ANY;
322 ch = pim_ifchannel_find(ifp, &star_g);
323 if (!ch) {
324 if (PIM_DEBUG_MROUTE)
325 zlog_debug("%s: WRONGVIF (*,G)=%s could not find channel on interface %s",
326 __PRETTY_FUNCTION__,
327 pim_str_sg_dump(&star_g), ifp->name);
328 return -3;
329 }
330 }
331
332 /*
333 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
334
335 Transitions from NoInfo State
336
337 An (S,G) data packet arrives on interface I, AND
338 CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an
339 downstream interface that is in our (S,G) outgoing interface
340 list. We optimistically assume that we will be the assert
341 winner for this (S,G), and so we transition to the "I am Assert
342 Winner" state and perform Actions A1 (below), which will
343 initiate the assert negotiation for (S,G).
344 */
345
346 if (ch->ifassert_state != PIM_IFASSERT_NOINFO) {
347 if (PIM_DEBUG_MROUTE) {
348 zlog_debug("%s: WRONGVIF (S,G)=%s channel is not on Assert NoInfo state for interface %s",
349 __PRETTY_FUNCTION__,
350 ch->sg_str, ifp->name);
351 }
352 return -4;
353 }
354
355 if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) {
356 if (PIM_DEBUG_MROUTE) {
357 zlog_debug("%s: WRONGVIF (S,G)=%s interface %s is not downstream for channel",
358 __PRETTY_FUNCTION__,
359 ch->sg_str, ifp->name);
360 }
361 return -5;
362 }
363
364 if (assert_action_a1(ch)) {
365 if (PIM_DEBUG_MROUTE) {
366 zlog_debug("%s: WRONGVIF (S,G)=%s assert_action_a1 failure on interface %s",
367 __PRETTY_FUNCTION__,
368 ch->sg_str, ifp->name);
369 }
370 return -6;
371 }
372
373 return 0;
374}
375
376static int
377pim_mroute_msg_wrvifwhole (int fd, struct interface *ifp, const char *buf)
378{
379 const struct ip *ip_hdr = (const struct ip *)buf;
380 struct pim_interface *pim_ifp;
381 struct pim_ifchannel *ch;
382 struct pim_upstream *up;
383 struct prefix_sg star_g;
384 struct prefix_sg sg;
385 struct channel_oil *oil;
386
387 memset (&sg, 0, sizeof (struct prefix_sg));
388 sg.src = ip_hdr->ip_src;
389 sg.grp = ip_hdr->ip_dst;
390
391 ch = pim_ifchannel_find(ifp, &sg);
392 if (ch)
393 {
394 if (PIM_DEBUG_MROUTE)
395 zlog_debug ("WRVIFWHOLE (S,G)=%s found ifchannel on interface %s",
396 ch->sg_str, ifp->name);
397 return -1;
398 }
399
400 star_g = sg;
401 star_g.src.s_addr = INADDR_ANY;
402#if 0
403 ch = pim_ifchannel_find(ifp, &star_g);
404 if (ch)
405 {
406 if (PIM_DEBUG_MROUTE)
407 zlog_debug ("WRVIFWHOLE (*,G)=%s found ifchannel on interface %s",
408 pim_str_sg_dump (&star_g), ifp->name);
409 return -1;
410 }
411#endif
412
413 up = pim_upstream_find (&sg);
414 if (up)
415 {
416 struct pim_upstream *parent;
417 struct pim_nexthop source;
418 struct pim_rpf *rpf = RP (sg.grp);
419 if (!rpf || !rpf->source_nexthop.interface)
420 return 0;
421
422 /*
423 * If we have received a WRVIFWHOLE and are at this
424 * point, we could be receiving the packet on the *,G
425 * tree, let's check and if so we can safely drop
426 * it.
427 */
428 parent = pim_upstream_find (&star_g);
429 if (parent && parent->rpf.source_nexthop.interface == ifp)
430 return 0;
431
432 pim_ifp = rpf->source_nexthop.interface->info;
433
434 memset (&source, 0, sizeof (source));
435 /*
436 * If we are the fhr that means we are getting a callback during
437 * the pimreg period, so I believe we can ignore this packet
438 */
439 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags))
440 {
441 //No if channel, but upstream we are at the RP.
442 if (pim_nexthop_lookup (&source, up->upstream_register, 0) == 0)
443 pim_register_stop_send(source.interface, &sg, pim_ifp->primary_address, up->upstream_register);
444 if (!up->channel_oil)
445 up->channel_oil = pim_channel_oil_add (&sg, pim_ifp->mroute_vif_index);
446 pim_upstream_inherited_olist (up);
447 if (!up->channel_oil->installed)
448 pim_mroute_add (up->channel_oil, __PRETTY_FUNCTION__);
449 pim_upstream_set_sptbit (up, ifp);
450 }
451 else
452 {
453 if (I_am_RP (up->sg.grp))
454 {
455 if (pim_nexthop_lookup (&source, up->upstream_register, 0) == 0)
456 pim_register_stop_send(source.interface, &sg, pim_ifp->primary_address, up->upstream_register);
457 up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
458 }
459 pim_upstream_keep_alive_timer_start (up, qpim_keep_alive_time);
460 pim_upstream_inherited_olist (up);
461 pim_mroute_msg_wholepkt (fd, ifp, buf);
462 }
463 return 0;
464 }
465
466 pim_ifp = ifp->info;
467 oil = pim_channel_oil_add (&sg, pim_ifp->mroute_vif_index);
468 if (!oil->installed)
469 pim_mroute_add (oil, __PRETTY_FUNCTION__);
470 if (pim_if_connected_to_source (ifp, sg.src))
471 {
472 up = pim_upstream_add (&sg, ifp, PIM_UPSTREAM_FLAG_MASK_FHR, __PRETTY_FUNCTION__);
473 if (!up)
474 {
475 if (PIM_DEBUG_MROUTE)
476 zlog_debug ("%s: WRONGVIF%s unable to create upstream on interface",
477 pim_str_sg_dump (&sg), ifp->name);
478 return -2;
479 }
480 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up->flags);
481 pim_upstream_keep_alive_timer_start (up, qpim_keep_alive_time);
482 up->channel_oil = oil;
483 up->channel_oil->cc.pktcnt++;
484 pim_register_join (up);
485 pim_upstream_inherited_olist (up);
486
487 // Send the packet to the RP
488 pim_mroute_msg_wholepkt (fd, ifp, buf);
489 }
490
491 return 0;
492}
493
494int pim_mroute_msg(int fd, const char *buf, int buf_size)
495{
496 struct interface *ifp;
497 struct pim_interface *pim_ifp;
498 const struct ip *ip_hdr;
499 const struct igmpmsg *msg;
500 char ip_src_str[INET_ADDRSTRLEN] = "";
501 char ip_dst_str[INET_ADDRSTRLEN] = "";
502 char src_str[INET_ADDRSTRLEN] = "<src?>";
503 char grp_str[INET_ADDRSTRLEN] = "<grp?>";
504 struct in_addr ifaddr;
505 struct igmp_sock *igmp;
506
507 ip_hdr = (const struct ip *) buf;
508
509 if (ip_hdr->ip_p == IPPROTO_IGMP) {
510
511 /* We have the IP packet but we do not know which interface this packet was
512 * received on. Find the interface that is on the same subnet as the source
513 * of the IP packet.
514 */
515 ifp = pim_if_lookup_address_vrf (ip_hdr->ip_src, VRF_DEFAULT);
516
517 if (!ifp) {
518 if (PIM_DEBUG_MROUTE_DETAIL) {
519 pim_inet4_dump("<src?>", ip_hdr->ip_src, ip_src_str, sizeof(ip_src_str));
520 pim_inet4_dump("<dst?>", ip_hdr->ip_dst, ip_dst_str, sizeof(ip_dst_str));
521
522 zlog_warn("%s: igmp kernel upcall could not find usable interface for %s -> %s",
523 __PRETTY_FUNCTION__,
524 ip_src_str,
525 ip_dst_str);
526 }
527 return 0;
528 }
529 pim_ifp = ifp->info;
530 ifaddr = pim_find_primary_addr(ifp);
531 igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->igmp_socket_list, ifaddr);
532
533 if (PIM_DEBUG_MROUTE) {
534 pim_inet4_dump("<src?>", ip_hdr->ip_src, ip_src_str, sizeof(ip_src_str));
535 pim_inet4_dump("<dst?>", ip_hdr->ip_dst, ip_dst_str, sizeof(ip_dst_str));
536
537 zlog_warn("%s: igmp kernel upcall on %s(%p) for %s -> %s",
538 __PRETTY_FUNCTION__, ifp->name, igmp, ip_src_str, ip_dst_str);
539 }
540 if (igmp)
541 pim_igmp_packet(igmp, (char *)buf, buf_size);
542
543 } else if (ip_hdr->ip_p) {
544 if (PIM_DEBUG_MROUTE_DETAIL) {
545 pim_inet4_dump("<src?>", ip_hdr->ip_src, src_str, sizeof(src_str));
546 pim_inet4_dump("<grp?>", ip_hdr->ip_dst, grp_str, sizeof(grp_str));
547 zlog_debug("%s: no kernel upcall proto=%d src: %s dst: %s msg_size=%d",
548 __PRETTY_FUNCTION__, ip_hdr->ip_p, src_str, grp_str, buf_size);
549 }
550
551 } else {
552 msg = (const struct igmpmsg *) buf;
553
554 ifp = pim_if_find_by_vif_index(msg->im_vif);
555
556 if (!ifp)
557 return 0;
558 if (PIM_DEBUG_MROUTE) {
559 pim_inet4_dump("<src?>", msg->im_src, src_str, sizeof(src_str));
560 pim_inet4_dump("<grp?>", msg->im_dst, grp_str, sizeof(grp_str));
561 zlog_warn("%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d size=%d",
562 __PRETTY_FUNCTION__,
563 igmpmsgtype2str[msg->im_msgtype],
564 msg->im_msgtype,
565 ip_hdr->ip_p,
566 fd,
567 src_str,
568 grp_str,
569 ifp->name,
570 msg->im_vif, buf_size);
571 }
572
573 switch (msg->im_msgtype) {
574 case IGMPMSG_WRONGVIF:
575 return pim_mroute_msg_wrongvif(fd, ifp, msg);
576 break;
577 case IGMPMSG_NOCACHE:
578 return pim_mroute_msg_nocache(fd, ifp, msg);
579 break;
580 case IGMPMSG_WHOLEPKT:
581 return pim_mroute_msg_wholepkt(fd, ifp, (const char *)msg);
582 break;
583 case IGMPMSG_WRVIFWHOLE:
584 return pim_mroute_msg_wrvifwhole (fd, ifp, (const char *)msg);
585 break;
586 default:
587 break;
588 }
589 }
590
591 return 0;
592}
593
594static int mroute_read(struct thread *t)
595{
596 static long long count;
597 char buf[10000];
598 int result = 0;
599 int cont = 1;
600 int fd;
601 int rd;
602
603 fd = THREAD_FD(t);
604
605 while (cont)
606 {
607 rd = read(fd, buf, sizeof(buf));
608 if (rd < 0) {
609 if (errno == EINTR)
610 continue;
611 if (errno == EWOULDBLOCK || errno == EAGAIN)
612 break;
613
614 if (PIM_DEBUG_MROUTE)
615 zlog_warn("%s: failure reading fd=%d: errno=%d: %s",
616 __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
617 goto done;
618 }
619
620 result = pim_mroute_msg(fd, buf, rd);
621
622 count++;
623 if (count % qpim_packet_process == 0)
624 cont = 0;
625 }
626 /* Keep reading */
627 done:
628 mroute_read_on();
629
630 return result;
631}
632
633static void mroute_read_on()
634{
635 thread_add_read(master, mroute_read, 0, qpim_mroute_socket_fd,
636 &qpim_mroute_socket_reader);
637}
638
639static void mroute_read_off()
640{
641 THREAD_OFF(qpim_mroute_socket_reader);
642}
643
644int pim_mroute_socket_enable()
645{
646 int fd;
647
648 if ( pimd_privs.change (ZPRIVS_RAISE) )
649 zlog_err ("pim_mroute_socket_enable: could not raise privs, %s",
650 safe_strerror (errno) );
651
652 fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
653
654 if ( pimd_privs.change (ZPRIVS_LOWER) )
655 zlog_err ("pim_mroute_socket_enable: could not lower privs, %s",
656 safe_strerror (errno) );
657
658 if (fd < 0) {
659 zlog_warn("Could not create mroute socket: errno=%d: %s",
660 errno, safe_strerror(errno));
661 return -2;
662 }
663
664 if (pim_mroute_set(fd, 1)) {
665 zlog_warn("Could not enable mroute on socket fd=%d: errno=%d: %s",
666 fd, errno, safe_strerror(errno));
667 close(fd);
668 return -3;
669 }
670
671 qpim_mroute_socket_fd = fd;
672
673 qpim_mroute_socket_creation = pim_time_monotonic_sec();
674 mroute_read_on();
675
676 return 0;
677}
678
679int pim_mroute_socket_disable()
680{
681 if (pim_mroute_set(qpim_mroute_socket_fd, 0)) {
682 zlog_warn("Could not disable mroute on socket fd=%d: errno=%d: %s",
683 qpim_mroute_socket_fd, errno, safe_strerror(errno));
684 return -2;
685 }
686
687 if (close(qpim_mroute_socket_fd)) {
688 zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
689 qpim_mroute_socket_fd, errno, safe_strerror(errno));
690 return -3;
691 }
692
693 mroute_read_off();
694 qpim_mroute_socket_fd = -1;
695
696 return 0;
697}
698
699/*
700 For each network interface (e.g., physical or a virtual tunnel) that
701 would be used for multicast forwarding, a corresponding multicast
702 interface must be added to the kernel.
703 */
704int pim_mroute_add_vif(struct interface *ifp, struct in_addr ifaddr, unsigned char flags)
705{
706 struct pim_interface *pim_ifp = ifp->info;
707 struct vifctl vc;
708 int err;
709
710 memset(&vc, 0, sizeof(vc));
711 vc.vifc_vifi = pim_ifp->mroute_vif_index;
712#ifdef VIFF_USE_IFINDEX
713 vc.vifc_lcl_ifindex = ifp->ifindex;
714#else
715 if (ifaddr.s_addr == INADDR_ANY) {
716 zlog_warn("%s: unnumbered interfaces are not supported on this platform",
717 __PRETTY_FUNCTION__);
718 return -1;
719 }
720 memcpy(&vc.vifc_lcl_addr, &ifaddr, sizeof(vc.vifc_lcl_addr));
721#endif
722 vc.vifc_flags = flags;
723 vc.vifc_threshold = PIM_MROUTE_MIN_TTL;
724 vc.vifc_rate_limit = 0;
725
726#ifdef PIM_DVMRP_TUNNEL
727 if (vc.vifc_flags & VIFF_TUNNEL) {
728 memcpy(&vc.vifc_rmt_addr, &vif_remote_addr, sizeof(vc.vifc_rmt_addr));
729 }
730#endif
731
732 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_VIF, (void*) &vc, sizeof(vc));
733 if (err) {
734 char ifaddr_str[INET_ADDRSTRLEN];
735
736 pim_inet4_dump("<ifaddr?>", ifaddr, ifaddr_str, sizeof(ifaddr_str));
737
738 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s,flag=%d): errno=%d: %s",
739 __FILE__, __PRETTY_FUNCTION__,
740 qpim_mroute_socket_fd, ifp->ifindex, ifaddr_str, flags,
741 errno, safe_strerror(errno));
742 return -2;
743 }
744
745 return 0;
746}
747
748int pim_mroute_del_vif(int vif_index)
749{
750 struct vifctl vc;
751 int err;
752
753 if (PIM_DEBUG_MROUTE)
754 {
755 struct interface *ifp = pim_if_find_by_vif_index (vif_index);
756 zlog_debug ("%s %s: Del Vif %d (%s) ", __FILE__,
757 __PRETTY_FUNCTION__, vif_index, ifp ? ifp->name : "NULL");
758 }
759
760 memset(&vc, 0, sizeof(vc));
761 vc.vifc_vifi = vif_index;
762
763 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_VIF, (void*) &vc, sizeof(vc));
764 if (err) {
765 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
766 __FILE__, __PRETTY_FUNCTION__,
767 qpim_mroute_socket_fd, vif_index,
768 errno, safe_strerror(errno));
769 return -2;
770 }
771
772 return 0;
773}
774
775int pim_mroute_add(struct channel_oil *c_oil, const char *name)
776{
777 int err;
778 int orig = 0;
779 int orig_iif_vif = 0;
780
781 qpim_mroute_add_last = pim_time_monotonic_sec();
782 ++qpim_mroute_add_events;
783
784 /* Do not install route if incoming interface is undefined. */
785 if (c_oil->oil.mfcc_parent >= MAXVIFS)
786 {
787 if (PIM_DEBUG_MROUTE)
788 {
789 char buf[1000];
790 zlog_debug("%s(%s) %s Attempting to add vifi that is invalid to mroute table",
791 __PRETTY_FUNCTION__, name, pim_channel_oil_dump (c_oil, buf, sizeof(buf)));
792 }
793 return -2;
794 }
795
796 /* The linux kernel *expects* the incoming
797 * vif to be part of the outgoing list
798 * in the case of a (*,G).
799 */
800 if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY)
801 {
802 orig = c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent];
803 c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = 1;
804 }
805
806 /*
807 * If we have an unresolved cache entry for the S,G
808 * it is owned by the pimreg for the incoming IIF
809 * So set pimreg as the IIF temporarily to cause
810 * the packets to be forwarded. Then set it
811 * to the correct IIF afterwords.
812 */
813 if (!c_oil->installed && c_oil->oil.mfcc_origin.s_addr != INADDR_ANY &&
814 c_oil->oil.mfcc_parent != 0)
815 {
816 orig_iif_vif = c_oil->oil.mfcc_parent;
817 c_oil->oil.mfcc_parent = 0;
818 }
819 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_MFC,
820 &c_oil->oil, sizeof(c_oil->oil));
821
822 if (!err && !c_oil->installed && c_oil->oil.mfcc_origin.s_addr != INADDR_ANY &&
823 orig_iif_vif != 0)
824 {
825 c_oil->oil.mfcc_parent = orig_iif_vif;
826 err = setsockopt (qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_MFC,
827 &c_oil->oil, sizeof (c_oil->oil));
828 }
829
830 if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY)
831 c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = orig;
832
833 if (err) {
834 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
835 __FILE__, __PRETTY_FUNCTION__,
836 qpim_mroute_socket_fd,
837 errno, safe_strerror(errno));
838 return -2;
839 }
840
841 if (PIM_DEBUG_MROUTE)
842 {
843 char buf[1000];
844 zlog_debug("%s(%s), Added Route: %s",
845 __PRETTY_FUNCTION__, name,
846 pim_channel_oil_dump (c_oil, buf, sizeof(buf)));
847 }
848
849 c_oil->installed = 1;
850 return 0;
851}
852
853int pim_mroute_del (struct channel_oil *c_oil, const char *name)
854{
855 int err;
856
857 qpim_mroute_del_last = pim_time_monotonic_sec();
858 ++qpim_mroute_del_events;
859
860 if (!c_oil->installed)
861 {
862 if (PIM_DEBUG_MROUTE)
863 {
864 char buf[1000];
865 zlog_debug("%s %s: vifi %d for route is %s not installed, do not need to send del req. ",
866 __FILE__, __PRETTY_FUNCTION__, c_oil->oil.mfcc_parent,
867 pim_channel_oil_dump (c_oil, buf, sizeof(buf)));
868 }
869 return -2;
870 }
871
872 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_MFC, &c_oil->oil, sizeof(c_oil->oil));
873 if (err) {
874 if (PIM_DEBUG_MROUTE)
875 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
876 __FILE__, __PRETTY_FUNCTION__,
877 qpim_mroute_socket_fd,
878 errno, safe_strerror(errno));
879 return -2;
880 }
881
882 if (PIM_DEBUG_MROUTE)
883 {
884 char buf[1000];
885 zlog_debug("%s(%s), Deleted Route: %s",
886 __PRETTY_FUNCTION__, name,
887 pim_channel_oil_dump (c_oil, buf, sizeof(buf)));
888 }
889
890 //Reset kernel installed flag
891 c_oil->installed = 0;
892
893 return 0;
894}
895
896void
897pim_mroute_update_counters (struct channel_oil *c_oil)
898{
899 struct sioc_sg_req sgreq;
900
901 c_oil->cc.oldpktcnt = c_oil->cc.pktcnt;
902 c_oil->cc.oldbytecnt = c_oil->cc.bytecnt;
903 c_oil->cc.oldwrong_if = c_oil->cc.wrong_if;
904
905 if (!c_oil->installed)
906 {
907 c_oil->cc.lastused = 100 * qpim_keep_alive_time;
908 if (PIM_DEBUG_MROUTE)
909 {
910 struct prefix_sg sg;
911
912 sg.src = c_oil->oil.mfcc_origin;
913 sg.grp = c_oil->oil.mfcc_mcastgrp;
914 if (PIM_DEBUG_MROUTE)
915 zlog_debug("Channel(%s) is not installed no need to collect data from kernel",
916 pim_str_sg_dump (&sg));
917 }
918 return;
919 }
920
921 memset (&sgreq, 0, sizeof(sgreq));
922 sgreq.src = c_oil->oil.mfcc_origin;
923 sgreq.grp = c_oil->oil.mfcc_mcastgrp;
924
925 pim_zlookup_sg_statistics (c_oil);
926 if (ioctl (qpim_mroute_socket_fd, SIOCGETSGCNT, &sgreq))
927 {
928 if (PIM_DEBUG_MROUTE)
929 {
930 struct prefix_sg sg;
931
932 sg.src = c_oil->oil.mfcc_origin;
933 sg.grp = c_oil->oil.mfcc_mcastgrp;
934
935 zlog_warn ("ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=(%s): errno=%d: %s",
936 (unsigned long)SIOCGETSGCNT,
937 pim_str_sg_dump (&sg),
938 errno,
939 safe_strerror(errno));
940 }
941 return;
942 }
943
944 c_oil->cc.pktcnt = sgreq.pktcnt;
945 c_oil->cc.bytecnt = sgreq.bytecnt;
946 c_oil->cc.wrong_if = sgreq.wrong_if;
947
948 return;
949}