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