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