]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_mroute.c
pimd: On RP Allow no output interfaces
[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 {
6c7197b1 134 if (PIM_DEBUG_MROUTE_DETAIL)
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
4304f95c 159 pim_upstream_keep_alive_timer_start (up, qpim_keep_alive_time);
3667e8a0 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) {
6c7197b1 196 if (PIM_DEBUG_MROUTE_DETAIL) {
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)
4df01a4e
DS
221 pim_register_send((uint8_t *)buf + sizeof(struct ip), ntohs (ip_hdr->ip_len),
222 pim_ifp->primary_address, rpg, 0);
2ddab288 223
e355e30f
DS
224 return 0;
225}
12e41d03 226
e355e30f 227static int
c29a5806 228pim_mroute_msg_wrongvif (int fd, struct interface *ifp, const struct igmpmsg *msg)
e355e30f
DS
229{
230 struct pim_ifchannel *ch;
231 struct pim_interface *pim_ifp;
4ed0af70 232 struct prefix_sg sg;
12e41d03 233
c29a5806
DS
234 memset (&sg, 0, sizeof (struct prefix_sg));
235 sg.src = msg->im_src;
236 sg.grp = msg->im_dst;
237
e355e30f
DS
238 /*
239 Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
12e41d03 240
e355e30f
DS
241 RFC 4601 4.8.2. PIM-SSM-Only Routers
242
243 iif is the incoming interface of the packet.
244 if (iif is in inherited_olist(S,G)) {
245 send Assert(S,G) on iif
246 }
247 */
12e41d03 248
e355e30f 249 if (!ifp) {
630f76b6 250 if (PIM_DEBUG_MROUTE) {
c29a5806 251 zlog_debug("%s: WRONGVIF (S,G)=%s could not find input interface for input_vif_index=%d",
12e41d03 252 __PRETTY_FUNCTION__,
c29a5806 253 pim_str_sg_dump (&sg), msg->im_vif);
12e41d03 254 }
e355e30f
DS
255 return -1;
256 }
12e41d03 257
e355e30f
DS
258 pim_ifp = ifp->info;
259 if (!pim_ifp) {
630f76b6 260 if (PIM_DEBUG_MROUTE) {
c29a5806 261 zlog_debug("%s: WRONGVIF (S,G)=%s multicast not enabled on interface %s",
e355e30f 262 __PRETTY_FUNCTION__,
c29a5806 263 pim_str_sg_dump (&sg), ifp->name);
12e41d03 264 }
e355e30f
DS
265 return -2;
266 }
12e41d03 267
5074a423 268 ch = pim_ifchannel_find(ifp, &sg);
e355e30f 269 if (!ch) {
630f76b6 270 if (PIM_DEBUG_MROUTE) {
c29a5806 271 zlog_debug("%s: WRONGVIF (S,G)=%s could not find channel on interface %s",
e355e30f 272 __PRETTY_FUNCTION__,
c29a5806 273 pim_str_sg_dump (&sg), ifp->name);
12e41d03 274 }
e355e30f
DS
275 return -3;
276 }
12e41d03 277
e355e30f
DS
278 /*
279 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
280
281 Transitions from NoInfo State
282
283 An (S,G) data packet arrives on interface I, AND
284 CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an
285 downstream interface that is in our (S,G) outgoing interface
286 list. We optimistically assume that we will be the assert
287 winner for this (S,G), and so we transition to the "I am Assert
288 Winner" state and perform Actions A1 (below), which will
289 initiate the assert negotiation for (S,G).
290 */
12e41d03 291
e355e30f 292 if (ch->ifassert_state != PIM_IFASSERT_NOINFO) {
630f76b6 293 if (PIM_DEBUG_MROUTE) {
c29a5806 294 zlog_debug("%s: WRONGVIF (S,G)=%s channel is not on Assert NoInfo state for interface %s",
e355e30f 295 __PRETTY_FUNCTION__,
c29a5806 296 pim_str_sg_dump (&sg), ifp->name);
12e41d03 297 }
e355e30f
DS
298 return -4;
299 }
12e41d03 300
e355e30f 301 if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) {
630f76b6 302 if (PIM_DEBUG_MROUTE) {
c29a5806 303 zlog_debug("%s: WRONGVIF (S,G)=%s interface %s is not downstream for channel",
e355e30f 304 __PRETTY_FUNCTION__,
c29a5806 305 pim_str_sg_dump (&sg), ifp->name);
12e41d03 306 }
e355e30f
DS
307 return -5;
308 }
12e41d03 309
e355e30f 310 if (assert_action_a1(ch)) {
630f76b6 311 if (PIM_DEBUG_MROUTE) {
c29a5806 312 zlog_debug("%s: WRONGVIF (S,G)=%s assert_action_a1 failure on interface %s",
e355e30f 313 __PRETTY_FUNCTION__,
c29a5806 314 pim_str_sg_dump (&sg), ifp->name);
12e41d03 315 }
e355e30f
DS
316 return -6;
317 }
318
319 return 0;
320}
321
08e1fe76
DS
322static int
323pim_mroute_msg_wrvifwhole (int fd, struct interface *ifp, const char *buf)
324{
325 const struct ip *ip_hdr = (const struct ip *)buf;
326 struct pim_interface *pim_ifp;
327 struct pim_ifchannel *ch;
328 struct pim_upstream *up;
329 struct prefix_sg sg;
330
331 memset (&sg, 0, sizeof (struct prefix_sg));
332 sg.src = ip_hdr->ip_src;
333 sg.grp = ip_hdr->ip_dst;
334
335 if (PIM_DEBUG_MROUTE)
336 zlog_debug ("Received WHOLEPKT Wrong Vif for %s on %s",
337 pim_str_sg_dump (&sg), ifp->name);
338
339 ch = pim_ifchannel_find(ifp, &sg);
340 if (ch)
341 {
342 if (PIM_DEBUG_MROUTE)
343 zlog_debug ("WRVIFWHOLE (S,G)=%s found ifchannel on interface %s",
344 pim_str_sg_dump (&sg), ifp->name);
345 return -1;
346 }
347
348 if (PIM_DEBUG_MROUTE)
349 zlog_debug ("If channel: %p", ch);
350
8e38a2cf
DS
351 up = pim_upstream_find (&sg);
352 if (up)
353 {
7fe1f662
DS
354 /*
355 * If we are the fhr that means we are getting a callback during
356 * the pimreg period, so I believe we can ignore this packet
357 */
358 if (!up->fhr)
359 {
360 struct pim_nexthop source;
4df01a4e
DS
361 struct pim_rpf *rpf = RP (sg.grp);
362 pim_ifp = rpf->source_nexthop.interface->info;
363
7fe1f662 364 //No if channel, but upstream we are at the RP.
f24405b1 365 pim_nexthop_lookup (&source, up->upstream_register);
4df01a4e 366 pim_register_stop_send(source.interface, &sg, pim_ifp->primary_address, up->upstream_register);
51e82833
DS
367 if (!up->channel_oil)
368 up->channel_oil = pim_channel_oil_add (&sg, pim_ifp->mroute_vif_index);
369 if (!up->channel_oil->installed)
370 pim_mroute_add (up->channel_oil);
7fe1f662
DS
371 //Send S bit down the join.
372 up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
373 }
374 return 0;
8e38a2cf
DS
375 }
376
08e1fe76
DS
377 up = pim_upstream_add (&sg, ifp);
378
379 if (!up)
380 {
381 if (PIM_DEBUG_MROUTE)
382 zlog_debug ("%s: WRONGVIF%s unable to create upstream on interface",
383 pim_str_sg_dump (&sg), ifp->name);
384 return -2;
385 }
386
387 if (pim_mroute_connected_to_source (ifp, sg.src))
388 up->fhr = 1;
389
390 pim_ifp = ifp->info;
4304f95c 391 pim_upstream_keep_alive_timer_start (up, qpim_keep_alive_time);
08e1fe76
DS
392 up->channel_oil = pim_channel_oil_add (&sg, pim_ifp->mroute_vif_index);
393 up->channel_oil->cc.pktcnt++;
394 pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM);
395 up->join_state = PIM_UPSTREAM_JOINED;
396 pim_upstream_inherited_olist (up);
397
398 // Send the packet to the RP
399 pim_mroute_msg_wholepkt (fd, ifp, buf);
400
401 return 0;
402}
403
e355e30f
DS
404int pim_mroute_msg(int fd, const char *buf, int buf_size)
405{
406 struct interface *ifp;
407 const struct ip *ip_hdr;
408 const struct igmpmsg *msg;
409 char src_str[100] = "<src?>";
410 char grp_str[100] = "<grp?>";
12e41d03 411
e355e30f
DS
412 ip_hdr = (const struct ip *) buf;
413
414 /* kernel upcall must have protocol=0 */
415 if (ip_hdr->ip_p) {
416 /* this is not a kernel upcall */
6c7197b1 417 if (PIM_DEBUG_MROUTE_DETAIL) {
e5d33c83
DS
418 pim_inet4_dump("<src?>", ip_hdr->ip_src, src_str, sizeof(src_str));
419 pim_inet4_dump("<grp?>", ip_hdr->ip_dst, grp_str, sizeof(grp_str));
420 zlog_debug("%s: not a kernel upcall proto=%d src: %s dst: %s msg_size=%d",
421 __PRETTY_FUNCTION__, ip_hdr->ip_p, src_str, grp_str, buf_size);
e355e30f 422 }
12e41d03 423 return 0;
e355e30f
DS
424 }
425
426 msg = (const struct igmpmsg *) buf;
427
428 ifp = pim_if_find_by_vif_index(msg->im_vif);
429
630f76b6 430 if (PIM_DEBUG_MROUTE) {
e355e30f
DS
431 pim_inet4_dump("<src?>", msg->im_src, src_str, sizeof(src_str));
432 pim_inet4_dump("<grp?>", msg->im_dst, grp_str, sizeof(grp_str));
433 zlog_warn("%s: kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d",
434 __PRETTY_FUNCTION__,
435 igmpmsgtype2str[msg->im_msgtype],
436 msg->im_msgtype,
437 ip_hdr->ip_p,
438 fd,
439 src_str,
440 grp_str,
0bc327f7 441 ifp->name,
e355e30f
DS
442 msg->im_vif);
443 }
444
445 switch (msg->im_msgtype) {
446 case IGMPMSG_WRONGVIF:
c29a5806 447 return pim_mroute_msg_wrongvif(fd, ifp, msg);
e355e30f
DS
448 break;
449 case IGMPMSG_NOCACHE:
c29a5806 450 return pim_mroute_msg_nocache(fd, ifp, msg);
e355e30f
DS
451 break;
452 case IGMPMSG_WHOLEPKT:
c29a5806 453 return pim_mroute_msg_wholepkt(fd, ifp, (const char *)msg);
e355e30f 454 break;
08e1fe76
DS
455 case IGMPMSG_WRVIFWHOLE:
456 return pim_mroute_msg_wrvifwhole (fd, ifp, (const char *)msg);
457 break;
e355e30f
DS
458 default:
459 break;
460 }
12e41d03
DL
461
462 return 0;
463}
464
465static int mroute_read_msg(int fd)
466{
f0ce50d4 467 char buf[2000];
12e41d03
DL
468 int rd;
469
12e41d03
DL
470 rd = read(fd, buf, sizeof(buf));
471 if (rd < 0) {
472 zlog_warn("%s: failure reading fd=%d: errno=%d: %s",
473 __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
f0ce50d4 474 return -1;
12e41d03
DL
475 }
476
477 return pim_mroute_msg(fd, buf, rd);
478}
479
480static int mroute_read(struct thread *t)
481{
482 int fd;
483 int result;
484
485 zassert(t);
486 zassert(!THREAD_ARG(t));
487
488 fd = THREAD_FD(t);
489 zassert(fd == qpim_mroute_socket_fd);
490
491 result = mroute_read_msg(fd);
492
493 /* Keep reading */
494 qpim_mroute_socket_reader = 0;
495 mroute_read_on();
496
497 return result;
498}
499
500static void mroute_read_on()
501{
502 zassert(!qpim_mroute_socket_reader);
503 zassert(PIM_MROUTE_IS_ENABLED);
504
505 THREAD_READ_ON(master, qpim_mroute_socket_reader,
506 mroute_read, 0, qpim_mroute_socket_fd);
507}
508
509static void mroute_read_off()
510{
511 THREAD_OFF(qpim_mroute_socket_reader);
512}
513
514int pim_mroute_socket_enable()
515{
516 int fd;
517
518 if (PIM_MROUTE_IS_ENABLED)
519 return -1;
520
521 if ( pimd_privs.change (ZPRIVS_RAISE) )
522 zlog_err ("pim_mroute_socket_enable: could not raise privs, %s",
523 safe_strerror (errno) );
524
525 fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
526
527 if ( pimd_privs.change (ZPRIVS_LOWER) )
528 zlog_err ("pim_mroute_socket_enable: could not lower privs, %s",
529 safe_strerror (errno) );
530
531 if (fd < 0) {
532 zlog_warn("Could not create mroute socket: errno=%d: %s",
533 errno, safe_strerror(errno));
534 return -2;
535 }
536
537 if (pim_mroute_set(fd, 1)) {
538 zlog_warn("Could not enable mroute on socket fd=%d: errno=%d: %s",
539 fd, errno, safe_strerror(errno));
540 close(fd);
541 return -3;
542 }
543
544 qpim_mroute_socket_fd = fd;
b45cefcb 545
12e41d03
DL
546 qpim_mroute_socket_creation = pim_time_monotonic_sec();
547 mroute_read_on();
548
12e41d03
DL
549 return 0;
550}
551
552int pim_mroute_socket_disable()
553{
554 if (PIM_MROUTE_IS_DISABLED)
555 return -1;
556
557 if (pim_mroute_set(qpim_mroute_socket_fd, 0)) {
558 zlog_warn("Could not disable mroute on socket fd=%d: errno=%d: %s",
559 qpim_mroute_socket_fd, errno, safe_strerror(errno));
560 return -2;
561 }
562
563 if (close(qpim_mroute_socket_fd)) {
564 zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
565 qpim_mroute_socket_fd, errno, safe_strerror(errno));
566 return -3;
567 }
568
569 mroute_read_off();
570 qpim_mroute_socket_fd = -1;
571
12e41d03
DL
572 return 0;
573}
574
575/*
576 For each network interface (e.g., physical or a virtual tunnel) that
577 would be used for multicast forwarding, a corresponding multicast
578 interface must be added to the kernel.
579 */
744d91b3 580int pim_mroute_add_vif(struct interface *ifp, struct in_addr ifaddr, unsigned char flags)
12e41d03 581{
744d91b3 582 struct pim_interface *pim_ifp = ifp->info;
12e41d03
DL
583 struct vifctl vc;
584 int err;
585
586 if (PIM_MROUTE_IS_DISABLED) {
587 zlog_warn("%s: global multicast is disabled",
588 __PRETTY_FUNCTION__);
589 return -1;
590 }
591
592 memset(&vc, 0, sizeof(vc));
744d91b3 593 vc.vifc_vifi = pim_ifp->mroute_vif_index;
b3f2bf7c 594#ifdef VIFF_USE_IFINDEX
744d91b3 595 vc.vifc_lcl_ifindex = ifp->ifindex;
b3f2bf7c
RW
596#else
597 if (ifaddr.s_addr == INADDR_ANY) {
598 zlog_warn("%s: unnumbered interfaces are not supported on this platform",
599 __PRETTY_FUNCTION__);
600 return -1;
601 }
602 memcpy(&vc.vifc_lcl_addr, &ifaddr, sizeof(vc.vifc_lcl_addr));
603#endif
b45cefcb 604 vc.vifc_flags = flags;
12e41d03
DL
605 vc.vifc_threshold = PIM_MROUTE_MIN_TTL;
606 vc.vifc_rate_limit = 0;
12e41d03
DL
607
608#ifdef PIM_DVMRP_TUNNEL
609 if (vc.vifc_flags & VIFF_TUNNEL) {
610 memcpy(&vc.vifc_rmt_addr, &vif_remote_addr, sizeof(vc.vifc_rmt_addr));
611 }
612#endif
613
b45cefcb 614 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_VIF, (void*) &vc, sizeof(vc));
12e41d03
DL
615 if (err) {
616 char ifaddr_str[100];
12e41d03
DL
617
618 pim_inet4_dump("<ifaddr?>", ifaddr, ifaddr_str, sizeof(ifaddr_str));
619
59471fb8 620 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s,flag=%d): errno=%d: %s",
12e41d03 621 __FILE__, __PRETTY_FUNCTION__,
744d91b3 622 qpim_mroute_socket_fd, ifp->ifindex, ifaddr_str, flags,
3d7765d7 623 errno, safe_strerror(errno));
12e41d03
DL
624 return -2;
625 }
626
627 return 0;
628}
629
630int pim_mroute_del_vif(int vif_index)
631{
632 struct vifctl vc;
633 int err;
634
635 if (PIM_MROUTE_IS_DISABLED) {
636 zlog_warn("%s: global multicast is disabled",
637 __PRETTY_FUNCTION__);
638 return -1;
639 }
640
641 memset(&vc, 0, sizeof(vc));
642 vc.vifc_vifi = vif_index;
643
644 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_VIF, (void*) &vc, sizeof(vc));
645 if (err) {
12e41d03
DL
646 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
647 __FILE__, __PRETTY_FUNCTION__,
648 qpim_mroute_socket_fd, vif_index,
3d7765d7 649 errno, safe_strerror(errno));
12e41d03
DL
650 return -2;
651 }
652
653 return 0;
654}
655
c171d6d8 656int pim_mroute_add(struct channel_oil *c_oil)
12e41d03
DL
657{
658 int err;
2ca35b3d 659 int orig = 0;
0365f56b 660 int orig_iif_vif = 0;
12e41d03
DL
661
662 qpim_mroute_add_last = pim_time_monotonic_sec();
663 ++qpim_mroute_add_events;
664
665 if (PIM_MROUTE_IS_DISABLED) {
666 zlog_warn("%s: global multicast is disabled",
667 __PRETTY_FUNCTION__);
668 return -1;
669 }
670
d3aded99
DS
671 /* The linux kernel *expects* the incoming
672 * vif to be part of the outgoing list
673 * in the case of a (*,G).
674 */
c171d6d8 675 if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY)
d3aded99 676 {
c171d6d8
DS
677 orig = c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent];
678 c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = 1;
d3aded99
DS
679 }
680
0365f56b
DS
681 /*
682 * If we have an unresolved cache entry for the S,G
683 * it is owned by the pimreg for the incoming IIF
684 * So set pimreg as the IIF temporarily to cause
685 * the packets to be forwarded. Then set it
686 * to the correct IIF afterwords.
687 */
688 if (!c_oil->installed && c_oil->oil.mfcc_origin.s_addr != INADDR_ANY &&
689 c_oil->oil.mfcc_parent != 0)
690 {
691 orig_iif_vif = c_oil->oil.mfcc_parent;
692 c_oil->oil.mfcc_parent = 0;
693 }
12e41d03 694 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_MFC,
c171d6d8 695 &c_oil->oil, sizeof(c_oil->oil));
d3aded99 696
0365f56b
DS
697 if (!err && !c_oil->installed && c_oil->oil.mfcc_origin.s_addr != INADDR_ANY &&
698 orig_iif_vif != 0)
699 {
700 c_oil->oil.mfcc_parent = orig_iif_vif;
701 err = setsockopt (qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_MFC,
702 &c_oil->oil, sizeof (c_oil->oil));
703 }
704
c171d6d8
DS
705 if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY)
706 c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = orig;
d3aded99 707
12e41d03 708 if (err) {
12e41d03
DL
709 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
710 __FILE__, __PRETTY_FUNCTION__,
711 qpim_mroute_socket_fd,
3d7765d7 712 errno, safe_strerror(errno));
12e41d03
DL
713 return -2;
714 }
715
58302dc7 716 c_oil->installed = 1;
12e41d03
DL
717 return 0;
718}
719
c171d6d8 720int pim_mroute_del (struct channel_oil *c_oil)
12e41d03
DL
721{
722 int err;
723
724 qpim_mroute_del_last = pim_time_monotonic_sec();
725 ++qpim_mroute_del_events;
726
727 if (PIM_MROUTE_IS_DISABLED) {
728 zlog_warn("%s: global multicast is disabled",
729 __PRETTY_FUNCTION__);
730 return -1;
731 }
732
c171d6d8 733 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_MFC, &c_oil->oil, sizeof(c_oil->oil));
12e41d03 734 if (err) {
12e41d03
DL
735 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
736 __FILE__, __PRETTY_FUNCTION__,
737 qpim_mroute_socket_fd,
3d7765d7 738 errno, safe_strerror(errno));
12e41d03
DL
739 return -2;
740 }
741
58302dc7
DS
742 c_oil->installed = 0;
743
12e41d03
DL
744 return 0;
745}
3667e8a0
DS
746
747void
748pim_mroute_update_counters (struct channel_oil *c_oil)
749{
750 struct sioc_sg_req sgreq;
751
752 memset (&sgreq, 0, sizeof(sgreq));
753 sgreq.src = c_oil->oil.mfcc_origin;
754 sgreq.grp = c_oil->oil.mfcc_mcastgrp;
755
756 c_oil->cc.oldpktcnt = c_oil->cc.pktcnt;
757 c_oil->cc.oldbytecnt = c_oil->cc.bytecnt;
758 c_oil->cc.oldwrong_if = c_oil->cc.wrong_if;
e3be0432 759 c_oil->cc.oldlastused = c_oil->cc.lastused;
3667e8a0 760
51e82833 761 pim_zlookup_sg_statistics (c_oil);
3667e8a0
DS
762 if (ioctl (qpim_mroute_socket_fd, SIOCGETSGCNT, &sgreq))
763 {
764 char group_str[100];
765 char source_str[100];
766
767 pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, group_str, sizeof(group_str));
768 pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, source_str, sizeof(source_str));
769
770 zlog_warn ("ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=(%s,%s): errno=%d: %s",
771 (unsigned long)SIOCGETSGCNT,
772 source_str,
773 group_str,
774 errno,
775 safe_strerror(errno));
776 return;
777 }
778
779 c_oil->cc.pktcnt = sgreq.pktcnt;
780 c_oil->cc.bytecnt = sgreq.bytecnt;
781 c_oil->cc.wrong_if = sgreq.wrong_if;
782
783 return;
784}