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