]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_mroute.c
zebra: Fully decode mcast messages
[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{
f0ce50d4 438 char buf[2000];
12e41d03
DL
439 int rd;
440
12e41d03
DL
441 rd = read(fd, buf, sizeof(buf));
442 if (rd < 0) {
443 zlog_warn("%s: failure reading fd=%d: errno=%d: %s",
444 __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
f0ce50d4 445 return -1;
12e41d03
DL
446 }
447
448 return pim_mroute_msg(fd, buf, rd);
449}
450
451static int mroute_read(struct thread *t)
452{
453 int fd;
454 int result;
455
456 zassert(t);
457 zassert(!THREAD_ARG(t));
458
459 fd = THREAD_FD(t);
460 zassert(fd == qpim_mroute_socket_fd);
461
462 result = mroute_read_msg(fd);
463
464 /* Keep reading */
465 qpim_mroute_socket_reader = 0;
466 mroute_read_on();
467
468 return result;
469}
470
471static void mroute_read_on()
472{
473 zassert(!qpim_mroute_socket_reader);
474 zassert(PIM_MROUTE_IS_ENABLED);
475
476 THREAD_READ_ON(master, qpim_mroute_socket_reader,
477 mroute_read, 0, qpim_mroute_socket_fd);
478}
479
480static void mroute_read_off()
481{
482 THREAD_OFF(qpim_mroute_socket_reader);
483}
484
485int pim_mroute_socket_enable()
486{
487 int fd;
488
489 if (PIM_MROUTE_IS_ENABLED)
490 return -1;
491
492 if ( pimd_privs.change (ZPRIVS_RAISE) )
493 zlog_err ("pim_mroute_socket_enable: could not raise privs, %s",
494 safe_strerror (errno) );
495
496 fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
497
498 if ( pimd_privs.change (ZPRIVS_LOWER) )
499 zlog_err ("pim_mroute_socket_enable: could not lower privs, %s",
500 safe_strerror (errno) );
501
502 if (fd < 0) {
503 zlog_warn("Could not create mroute socket: errno=%d: %s",
504 errno, safe_strerror(errno));
505 return -2;
506 }
507
508 if (pim_mroute_set(fd, 1)) {
509 zlog_warn("Could not enable mroute on socket fd=%d: errno=%d: %s",
510 fd, errno, safe_strerror(errno));
511 close(fd);
512 return -3;
513 }
514
515 qpim_mroute_socket_fd = fd;
b45cefcb 516
12e41d03
DL
517 qpim_mroute_socket_creation = pim_time_monotonic_sec();
518 mroute_read_on();
519
12e41d03
DL
520 return 0;
521}
522
523int pim_mroute_socket_disable()
524{
525 if (PIM_MROUTE_IS_DISABLED)
526 return -1;
527
528 if (pim_mroute_set(qpim_mroute_socket_fd, 0)) {
529 zlog_warn("Could not disable mroute on socket fd=%d: errno=%d: %s",
530 qpim_mroute_socket_fd, errno, safe_strerror(errno));
531 return -2;
532 }
533
534 if (close(qpim_mroute_socket_fd)) {
535 zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
536 qpim_mroute_socket_fd, errno, safe_strerror(errno));
537 return -3;
538 }
539
540 mroute_read_off();
541 qpim_mroute_socket_fd = -1;
542
12e41d03
DL
543 return 0;
544}
545
546/*
547 For each network interface (e.g., physical or a virtual tunnel) that
548 would be used for multicast forwarding, a corresponding multicast
549 interface must be added to the kernel.
550 */
744d91b3 551int pim_mroute_add_vif(struct interface *ifp, struct in_addr ifaddr, unsigned char flags)
12e41d03 552{
744d91b3 553 struct pim_interface *pim_ifp = ifp->info;
12e41d03
DL
554 struct vifctl vc;
555 int err;
556
557 if (PIM_MROUTE_IS_DISABLED) {
558 zlog_warn("%s: global multicast is disabled",
559 __PRETTY_FUNCTION__);
560 return -1;
561 }
562
563 memset(&vc, 0, sizeof(vc));
744d91b3 564 vc.vifc_vifi = pim_ifp->mroute_vif_index;
b3f2bf7c 565#ifdef VIFF_USE_IFINDEX
744d91b3 566 vc.vifc_lcl_ifindex = ifp->ifindex;
b3f2bf7c
RW
567#else
568 if (ifaddr.s_addr == INADDR_ANY) {
569 zlog_warn("%s: unnumbered interfaces are not supported on this platform",
570 __PRETTY_FUNCTION__);
571 return -1;
572 }
573 memcpy(&vc.vifc_lcl_addr, &ifaddr, sizeof(vc.vifc_lcl_addr));
574#endif
b45cefcb 575 vc.vifc_flags = flags;
12e41d03
DL
576 vc.vifc_threshold = PIM_MROUTE_MIN_TTL;
577 vc.vifc_rate_limit = 0;
12e41d03
DL
578
579#ifdef PIM_DVMRP_TUNNEL
580 if (vc.vifc_flags & VIFF_TUNNEL) {
581 memcpy(&vc.vifc_rmt_addr, &vif_remote_addr, sizeof(vc.vifc_rmt_addr));
582 }
583#endif
584
b45cefcb 585 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_VIF, (void*) &vc, sizeof(vc));
12e41d03
DL
586 if (err) {
587 char ifaddr_str[100];
12e41d03
DL
588
589 pim_inet4_dump("<ifaddr?>", ifaddr, ifaddr_str, sizeof(ifaddr_str));
590
59471fb8 591 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s,flag=%d): errno=%d: %s",
12e41d03 592 __FILE__, __PRETTY_FUNCTION__,
744d91b3 593 qpim_mroute_socket_fd, ifp->ifindex, ifaddr_str, flags,
3d7765d7 594 errno, safe_strerror(errno));
12e41d03
DL
595 return -2;
596 }
597
598 return 0;
599}
600
601int pim_mroute_del_vif(int vif_index)
602{
603 struct vifctl vc;
604 int err;
605
606 if (PIM_MROUTE_IS_DISABLED) {
607 zlog_warn("%s: global multicast is disabled",
608 __PRETTY_FUNCTION__);
609 return -1;
610 }
611
612 memset(&vc, 0, sizeof(vc));
613 vc.vifc_vifi = vif_index;
614
615 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_VIF, (void*) &vc, sizeof(vc));
616 if (err) {
12e41d03
DL
617 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
618 __FILE__, __PRETTY_FUNCTION__,
619 qpim_mroute_socket_fd, vif_index,
3d7765d7 620 errno, safe_strerror(errno));
12e41d03
DL
621 return -2;
622 }
623
624 return 0;
625}
626
c171d6d8 627int pim_mroute_add(struct channel_oil *c_oil)
12e41d03
DL
628{
629 int err;
2ca35b3d 630 int orig = 0;
0365f56b 631 int orig_iif_vif = 0;
12e41d03
DL
632
633 qpim_mroute_add_last = pim_time_monotonic_sec();
634 ++qpim_mroute_add_events;
635
636 if (PIM_MROUTE_IS_DISABLED) {
637 zlog_warn("%s: global multicast is disabled",
638 __PRETTY_FUNCTION__);
639 return -1;
640 }
641
d3aded99
DS
642 /* The linux kernel *expects* the incoming
643 * vif to be part of the outgoing list
644 * in the case of a (*,G).
645 */
c171d6d8 646 if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY)
d3aded99 647 {
c171d6d8
DS
648 orig = c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent];
649 c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = 1;
d3aded99
DS
650 }
651
0365f56b
DS
652 /*
653 * If we have an unresolved cache entry for the S,G
654 * it is owned by the pimreg for the incoming IIF
655 * So set pimreg as the IIF temporarily to cause
656 * the packets to be forwarded. Then set it
657 * to the correct IIF afterwords.
658 */
659 if (!c_oil->installed && c_oil->oil.mfcc_origin.s_addr != INADDR_ANY &&
660 c_oil->oil.mfcc_parent != 0)
661 {
662 orig_iif_vif = c_oil->oil.mfcc_parent;
663 c_oil->oil.mfcc_parent = 0;
664 }
12e41d03 665 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_MFC,
c171d6d8 666 &c_oil->oil, sizeof(c_oil->oil));
d3aded99 667
0365f56b
DS
668 if (!err && !c_oil->installed && c_oil->oil.mfcc_origin.s_addr != INADDR_ANY &&
669 orig_iif_vif != 0)
670 {
671 c_oil->oil.mfcc_parent = orig_iif_vif;
672 err = setsockopt (qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_MFC,
673 &c_oil->oil, sizeof (c_oil->oil));
674 }
675
c171d6d8
DS
676 if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY)
677 c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = orig;
d3aded99 678
12e41d03 679 if (err) {
12e41d03
DL
680 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
681 __FILE__, __PRETTY_FUNCTION__,
682 qpim_mroute_socket_fd,
3d7765d7 683 errno, safe_strerror(errno));
12e41d03
DL
684 return -2;
685 }
686
58302dc7 687 c_oil->installed = 1;
12e41d03
DL
688 return 0;
689}
690
c171d6d8 691int pim_mroute_del (struct channel_oil *c_oil)
12e41d03
DL
692{
693 int err;
694
695 qpim_mroute_del_last = pim_time_monotonic_sec();
696 ++qpim_mroute_del_events;
697
698 if (PIM_MROUTE_IS_DISABLED) {
699 zlog_warn("%s: global multicast is disabled",
700 __PRETTY_FUNCTION__);
701 return -1;
702 }
703
c171d6d8 704 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_MFC, &c_oil->oil, sizeof(c_oil->oil));
12e41d03 705 if (err) {
12e41d03
DL
706 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
707 __FILE__, __PRETTY_FUNCTION__,
708 qpim_mroute_socket_fd,
3d7765d7 709 errno, safe_strerror(errno));
12e41d03
DL
710 return -2;
711 }
712
58302dc7
DS
713 c_oil->installed = 0;
714
12e41d03
DL
715 return 0;
716}
3667e8a0
DS
717
718void
719pim_mroute_update_counters (struct channel_oil *c_oil)
720{
721 struct sioc_sg_req sgreq;
722
723 memset (&sgreq, 0, sizeof(sgreq));
724 sgreq.src = c_oil->oil.mfcc_origin;
725 sgreq.grp = c_oil->oil.mfcc_mcastgrp;
726
727 c_oil->cc.oldpktcnt = c_oil->cc.pktcnt;
728 c_oil->cc.oldbytecnt = c_oil->cc.bytecnt;
729 c_oil->cc.oldwrong_if = c_oil->cc.wrong_if;
730
731 if (ioctl (qpim_mroute_socket_fd, SIOCGETSGCNT, &sgreq))
732 {
733 char group_str[100];
734 char source_str[100];
735
736 pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
737 pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
738
739 zlog_warn ("ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=(%s,%s): errno=%d: %s",
740 (unsigned long)SIOCGETSGCNT,
741 source_str,
742 group_str,
743 errno,
744 safe_strerror(errno));
745 return;
746 }
747
748 c_oil->cc.pktcnt = sgreq.pktcnt;
749 c_oil->cc.bytecnt = sgreq.bytecnt;
750 c_oil->cc.wrong_if = sgreq.wrong_if;
751
752 return;
753}