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