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