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