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