]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_mroute.c
pimd: Store the pim instance off of the pim interface
[mirror_frr.git] / pimd / pim_mroute.c
CommitLineData
12e41d03 1/*
896014f4
DL
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 along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
12e41d03
DL
19
20#include <zebra.h>
21#include "log.h"
22#include "privs.h"
744d91b3 23#include "if.h"
065bee4b 24#include "prefix.h"
dfe43e25
DW
25#include "vty.h"
26#include "plist.h"
fe232c19 27#include "sockopt.h"
12e41d03
DL
28
29#include "pimd.h"
8e38a2cf 30#include "pim_rpf.h"
12e41d03 31#include "pim_mroute.h"
37653d4f 32#include "pim_oil.h"
12e41d03
DL
33#include "pim_str.h"
34#include "pim_time.h"
35#include "pim_iface.h"
36#include "pim_macro.h"
c8ae3ce8 37#include "pim_rp.h"
59471fb8 38#include "pim_oil.h"
998af219 39#include "pim_register.h"
56638739 40#include "pim_ifchannel.h"
e3be0432 41#include "pim_zlookup.h"
15a5dafe 42#include "pim_ssm.h"
12e41d03 43
8ea5d944 44static void mroute_read_on(struct pim_instance *pim);
12e41d03 45
cdbfaec5 46static int pim_mroute_set(struct pim_instance *pim, int enable)
12e41d03 47{
d62a17ae 48 int err;
f507c196 49 int opt;
d62a17ae 50 socklen_t opt_len = sizeof(opt);
d62a17ae 51 long flags;
52
cdbfaec5
DS
53 /*
54 * We need to create the VRF table for the pim mroute_socket
55 */
56 if (pim->vrf_id != VRF_DEFAULT) {
f507c196
DS
57 if (pimd_privs.change(ZPRIVS_RAISE))
58 zlog_err(
59 "pim_mroute_socket_enable: could not raise privs, %s",
60 safe_strerror(errno));
61
cdbfaec5
DS
62 opt = pim->vrf_id;
63 err = setsockopt(pim->mroute_socket, IPPROTO_IP, MRT_TABLE,
64 &opt, opt_len);
65 if (err) {
66 zlog_warn(
67 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP, MRT_TABLE=%d): errno=%d: %s",
68 __FILE__, __PRETTY_FUNCTION__,
69 pim->mroute_socket, opt, errno,
70 safe_strerror(errno));
71 return -1;
72 }
f507c196
DS
73
74 if (pimd_privs.change(ZPRIVS_LOWER))
75 zlog_err(
76 "pim_mroute_socket_enable: could not lower privs, %s",
77 safe_strerror(errno));
78 }
79
80 opt = enable ? MRT_INIT : MRT_DONE;
81 err = setsockopt(pim->mroute_socket, IPPROTO_IP, opt, &opt, opt_len);
82 if (err) {
83 zlog_warn(
84 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,%s=%d): errno=%d: %s",
85 __FILE__, __PRETTY_FUNCTION__, pim->mroute_socket,
86 enable ? "MRT_INIT" : "MRT_DONE", opt, errno,
87 safe_strerror(errno));
88 return -1;
cdbfaec5
DS
89 }
90
91 setsockopt_so_recvbuf(pim->mroute_socket, 1024 * 1024 * 8);
d62a17ae 92
cdbfaec5 93 flags = fcntl(pim->mroute_socket, F_GETFL, 0);
d62a17ae 94 if (flags < 0) {
cdbfaec5
DS
95 zlog_warn("Could not get flags on socket fd:%d %d %s",
96 pim->mroute_socket, errno, safe_strerror(errno));
97 close(pim->mroute_socket);
d62a17ae 98 return -1;
99 }
cdbfaec5
DS
100 if (fcntl(pim->mroute_socket, F_SETFL, flags | O_NONBLOCK)) {
101 zlog_warn("Could not set O_NONBLOCK on socket fd:%d %d %s",
102 pim->mroute_socket, errno, safe_strerror(errno));
103 close(pim->mroute_socket);
d62a17ae 104 return -1;
105 }
106
107 if (enable) {
77b7d90b 108#if defined linux
d62a17ae 109 int upcalls = IGMPMSG_WRVIFWHOLE;
110 opt = MRT_PIM;
111
cdbfaec5 112 err = setsockopt(pim->mroute_socket, IPPROTO_IP, opt, &upcalls,
d62a17ae 113 sizeof(upcalls));
114 if (err) {
115 zlog_warn(
116 "Failure to register for VIFWHOLE and WRONGVIF upcalls %d %s",
117 errno, safe_strerror(errno));
118 return -1;
119 }
77b7d90b 120#else
d62a17ae 121 zlog_warn(
122 "PIM-SM will not work properly on this platform, until the ability to receive the WRVIFWHOLE upcall");
77b7d90b 123#endif
d62a17ae 124 }
125
126 return 0;
12e41d03
DL
127}
128
08e1fe76 129static const char *igmpmsgtype2str[IGMPMSG_WRVIFWHOLE + 1] = {
d62a17ae 130 "<unknown_upcall?>", "NOCACHE", "WRONGVIF", "WHOLEPKT", "WRVIFWHOLE"};
131
132static int pim_mroute_msg_nocache(int fd, struct interface *ifp,
133 const struct igmpmsg *msg)
12e41d03 134{
d62a17ae 135 struct pim_interface *pim_ifp = ifp->info;
136 struct pim_upstream *up;
137 struct pim_rpf *rpg;
138 struct prefix_sg sg;
139
140 rpg = RP(msg->im_dst);
141 /*
142 * If the incoming interface is unknown OR
143 * the Interface type is SSM we don't need to
144 * do anything here
145 */
146 if ((pim_rpf_addr_is_inaddr_none(rpg)) || (!pim_ifp)
147 || (!(PIM_I_am_DR(pim_ifp)))) {
148 if (PIM_DEBUG_MROUTE_DETAIL)
149 zlog_debug(
150 "%s: Interface is not configured correctly to handle incoming packet: Could be !DR, !pim_ifp, !SM, !RP",
151 __PRETTY_FUNCTION__);
152 return 0;
153 }
04b40f02 154
d62a17ae 155 /*
156 * If we've received a multicast packet that isn't connected to
157 * us
158 */
159 if (!pim_if_connected_to_source(ifp, msg->im_src)) {
160 if (PIM_DEBUG_MROUTE_DETAIL)
161 zlog_debug(
162 "%s: Received incoming packet that doesn't originate on our seg",
163 __PRETTY_FUNCTION__);
164 return 0;
165 }
065bee4b 166
d62a17ae 167 memset(&sg, 0, sizeof(struct prefix_sg));
168 sg.src = msg->im_src;
169 sg.grp = msg->im_dst;
170
171 up = pim_upstream_find_or_add(&sg, ifp, PIM_UPSTREAM_FLAG_MASK_FHR,
172 __PRETTY_FUNCTION__);
173 if (!up) {
174 if (PIM_DEBUG_MROUTE) {
175 zlog_debug(
176 "%s: Failure to add upstream information for %s",
177 __PRETTY_FUNCTION__, pim_str_sg_dump(&sg));
178 }
179 return 0;
180 }
c29a5806 181
d62a17ae 182 /*
183 * I moved this debug till after the actual add because
184 * I want to take advantage of the up->sg_str being filled in.
185 */
186 if (PIM_DEBUG_MROUTE) {
187 zlog_debug("%s: Adding a Route %s for WHOLEPKT consumption",
188 __PRETTY_FUNCTION__, up->sg_str);
189 }
8bfb8b67 190
d62a17ae 191 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up->flags);
192 pim_upstream_keep_alive_timer_start(up, qpim_keep_alive_time);
193
194 up->channel_oil->cc.pktcnt++;
195 PIM_UPSTREAM_FLAG_SET_FHR(up->flags);
196 // resolve mfcc_parent prior to mroute_add in channel_add_oif
197 if (up->channel_oil->oil.mfcc_parent >= MAXVIFS) {
198 int vif_index = 0;
199 vif_index = pim_if_find_vifindex_by_ifindex(
200 up->rpf.source_nexthop.interface->ifindex);
201 up->channel_oil->oil.mfcc_parent = vif_index;
202 }
203 pim_register_join(up);
59471fb8 204
d62a17ae 205 return 0;
e355e30f 206}
12e41d03 207
d62a17ae 208static int pim_mroute_msg_wholepkt(int fd, struct interface *ifp,
209 const char *buf)
e355e30f 210{
d62a17ae 211 struct pim_interface *pim_ifp;
212 struct prefix_sg sg;
213 struct pim_rpf *rpg;
214 const struct ip *ip_hdr;
215 struct pim_upstream *up;
216
217 ip_hdr = (const struct ip *)buf;
218
219 memset(&sg, 0, sizeof(struct prefix_sg));
220 sg.src = ip_hdr->ip_src;
221 sg.grp = ip_hdr->ip_dst;
222
223 up = pim_upstream_find(&sg);
224 if (!up) {
225 struct prefix_sg star = sg;
226 star.src.s_addr = INADDR_ANY;
227
228 up = pim_upstream_find(&star);
229
230 if (up && PIM_UPSTREAM_FLAG_TEST_SRC_IGMP(up->flags)) {
231 up = pim_upstream_add(&sg, ifp,
232 PIM_UPSTREAM_FLAG_MASK_SRC_LHR,
233 __PRETTY_FUNCTION__);
234 if (!up) {
235 if (PIM_DEBUG_MROUTE)
236 zlog_debug(
237 "%s: Unable to create upstream information for %s",
238 __PRETTY_FUNCTION__,
239 pim_str_sg_dump(&sg));
240 return 0;
241 }
242 pim_upstream_keep_alive_timer_start(
243 up, qpim_keep_alive_time);
244 pim_upstream_inherited_olist(up);
245 pim_upstream_switch(up, PIM_UPSTREAM_JOINED);
246
247 if (PIM_DEBUG_MROUTE)
248 zlog_debug("%s: Creating %s upstream on LHR",
249 __PRETTY_FUNCTION__, up->sg_str);
250 return 0;
251 }
252 if (PIM_DEBUG_MROUTE_DETAIL) {
253 zlog_debug(
254 "%s: Unable to find upstream channel WHOLEPKT%s",
255 __PRETTY_FUNCTION__, pim_str_sg_dump(&sg));
256 }
257 return 0;
258 }
59471fb8 259
d62a17ae 260 pim_ifp = up->rpf.source_nexthop.interface->info;
998af219 261
d62a17ae 262 rpg = RP(sg.grp);
c8ae3ce8 263
d62a17ae 264 if ((pim_rpf_addr_is_inaddr_none(rpg)) || (!pim_ifp)
265 || (!(PIM_I_am_DR(pim_ifp)))) {
266 if (PIM_DEBUG_MROUTE) {
267 zlog_debug("%s: Failed Check send packet",
268 __PRETTY_FUNCTION__);
269 }
270 return 0;
271 }
84366c7e 272
d62a17ae 273 /*
274 * If we've received a register suppress
275 */
276 if (!up->t_rs_timer) {
277 if (pim_is_grp_ssm(sg.grp)) {
278 if (PIM_DEBUG_PIM_REG)
279 zlog_debug(
280 "%s register forward skipped as group is SSM",
281 pim_str_sg_dump(&sg));
282 return 0;
283 }
284 pim_register_send((uint8_t *)buf + sizeof(struct ip),
285 ntohs(ip_hdr->ip_len) - sizeof(struct ip),
286 pim_ifp->primary_address, rpg, 0, up);
287 }
288 return 0;
e355e30f 289}
12e41d03 290
d62a17ae 291static int pim_mroute_msg_wrongvif(int fd, struct interface *ifp,
292 const struct igmpmsg *msg)
e355e30f 293{
d62a17ae 294 struct pim_ifchannel *ch;
295 struct pim_interface *pim_ifp;
296 struct prefix_sg sg;
297
298 memset(&sg, 0, sizeof(struct prefix_sg));
299 sg.src = msg->im_src;
300 sg.grp = msg->im_dst;
301
302 /*
303 Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
304
305 RFC 4601 4.8.2. PIM-SSM-Only Routers
306
307 iif is the incoming interface of the packet.
308 if (iif is in inherited_olist(S,G)) {
309 send Assert(S,G) on iif
310 }
311 */
312
313 if (!ifp) {
314 if (PIM_DEBUG_MROUTE)
315 zlog_debug(
316 "%s: WRONGVIF (S,G)=%s could not find input interface for input_vif_index=%d",
317 __PRETTY_FUNCTION__, pim_str_sg_dump(&sg),
318 msg->im_vif);
319 return -1;
320 }
12e41d03 321
d62a17ae 322 pim_ifp = ifp->info;
323 if (!pim_ifp) {
324 if (PIM_DEBUG_MROUTE)
325 zlog_debug(
326 "%s: WRONGVIF (S,G)=%s multicast not enabled on interface %s",
327 __PRETTY_FUNCTION__, pim_str_sg_dump(&sg),
328 ifp->name);
329 return -2;
330 }
c29a5806 331
d62a17ae 332 ch = pim_ifchannel_find(ifp, &sg);
333 if (!ch) {
334 struct prefix_sg star_g = sg;
335 if (PIM_DEBUG_MROUTE)
336 zlog_debug(
337 "%s: WRONGVIF (S,G)=%s could not find channel on interface %s",
338 __PRETTY_FUNCTION__, pim_str_sg_dump(&sg),
339 ifp->name);
340
341 star_g.src.s_addr = INADDR_ANY;
342 ch = pim_ifchannel_find(ifp, &star_g);
343 if (!ch) {
344 if (PIM_DEBUG_MROUTE)
345 zlog_debug(
346 "%s: WRONGVIF (*,G)=%s could not find channel on interface %s",
347 __PRETTY_FUNCTION__,
348 pim_str_sg_dump(&star_g), ifp->name);
349 return -3;
350 }
351 }
12e41d03 352
d62a17ae 353 /*
354 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
355
356 Transitions from NoInfo State
357
358 An (S,G) data packet arrives on interface I, AND
359 CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an
360 downstream interface that is in our (S,G) outgoing interface
361 list. We optimistically assume that we will be the assert
362 winner for this (S,G), and so we transition to the "I am Assert
363 Winner" state and perform Actions A1 (below), which will
364 initiate the assert negotiation for (S,G).
365 */
366
367 if (ch->ifassert_state != PIM_IFASSERT_NOINFO) {
368 if (PIM_DEBUG_MROUTE) {
369 zlog_debug(
370 "%s: WRONGVIF (S,G)=%s channel is not on Assert NoInfo state for interface %s",
371 __PRETTY_FUNCTION__, ch->sg_str, ifp->name);
372 }
373 return -4;
374 }
e355e30f 375
d62a17ae 376 if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) {
377 if (PIM_DEBUG_MROUTE) {
378 zlog_debug(
379 "%s: WRONGVIF (S,G)=%s interface %s is not downstream for channel",
380 __PRETTY_FUNCTION__, ch->sg_str, ifp->name);
381 }
382 return -5;
383 }
384
385 if (assert_action_a1(ch)) {
386 if (PIM_DEBUG_MROUTE) {
387 zlog_debug(
388 "%s: WRONGVIF (S,G)=%s assert_action_a1 failure on interface %s",
389 __PRETTY_FUNCTION__, ch->sg_str, ifp->name);
390 }
391 return -6;
392 }
e355e30f 393
d62a17ae 394 return 0;
e355e30f
DS
395}
396
d62a17ae 397static int pim_mroute_msg_wrvifwhole(int fd, struct interface *ifp,
398 const char *buf)
08e1fe76 399{
d62a17ae 400 const struct ip *ip_hdr = (const struct ip *)buf;
401 struct pim_interface *pim_ifp;
402 struct pim_ifchannel *ch;
403 struct pim_upstream *up;
404 struct prefix_sg star_g;
405 struct prefix_sg sg;
406 struct channel_oil *oil;
407
408 memset(&sg, 0, sizeof(struct prefix_sg));
409 sg.src = ip_hdr->ip_src;
410 sg.grp = ip_hdr->ip_dst;
411
412 ch = pim_ifchannel_find(ifp, &sg);
413 if (ch) {
414 if (PIM_DEBUG_MROUTE)
415 zlog_debug(
416 "WRVIFWHOLE (S,G)=%s found ifchannel on interface %s",
417 ch->sg_str, ifp->name);
418 return -1;
419 }
b7ddd2ec 420
d62a17ae 421 star_g = sg;
422 star_g.src.s_addr = INADDR_ANY;
b7ddd2ec 423#if 0
0490c22d
DS
424 ch = pim_ifchannel_find(ifp, &star_g);
425 if (ch)
426 {
427 if (PIM_DEBUG_MROUTE)
428 zlog_debug ("WRVIFWHOLE (*,G)=%s found ifchannel on interface %s",
429 pim_str_sg_dump (&star_g), ifp->name);
430 return -1;
431 }
5cd11e3c 432#endif
08e1fe76 433
d62a17ae 434 up = pim_upstream_find(&sg);
435 if (up) {
436 struct pim_upstream *parent;
437 struct pim_nexthop source;
438 struct pim_rpf *rpf = RP(sg.grp);
439 if (!rpf || !rpf->source_nexthop.interface)
440 return 0;
441
442 /*
443 * If we have received a WRVIFWHOLE and are at this
444 * point, we could be receiving the packet on the *,G
445 * tree, let's check and if so we can safely drop
446 * it.
447 */
448 parent = pim_upstream_find(&star_g);
449 if (parent && parent->rpf.source_nexthop.interface == ifp)
450 return 0;
451
452 pim_ifp = rpf->source_nexthop.interface->info;
453
454 memset(&source, 0, sizeof(source));
455 /*
456 * If we are the fhr that means we are getting a callback during
457 * the pimreg period, so I believe we can ignore this packet
458 */
459 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags)) {
460 // No if channel, but upstream we are at the RP.
461 if (pim_nexthop_lookup(&source, up->upstream_register,
462 0)
463 == 0)
464 pim_register_stop_send(source.interface, &sg,
465 pim_ifp->primary_address,
466 up->upstream_register);
467 if (!up->channel_oil)
468 up->channel_oil = pim_channel_oil_add(
469 &sg, pim_ifp->mroute_vif_index);
470 pim_upstream_inherited_olist(up);
471 if (!up->channel_oil->installed)
472 pim_mroute_add(up->channel_oil,
473 __PRETTY_FUNCTION__);
474 pim_upstream_set_sptbit(up, ifp);
475 } else {
476 if (I_am_RP(up->sg.grp)) {
477 if (pim_nexthop_lookup(&source,
478 up->upstream_register, 0)
479 == 0)
480 pim_register_stop_send(
481 source.interface, &sg,
482 pim_ifp->primary_address,
483 up->upstream_register);
484 up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
485 }
486 pim_upstream_keep_alive_timer_start(
487 up, qpim_keep_alive_time);
488 pim_upstream_inherited_olist(up);
489 pim_mroute_msg_wholepkt(fd, ifp, buf);
490 }
491 return 0;
492 }
8e38a2cf 493
d62a17ae 494 pim_ifp = ifp->info;
495 oil = pim_channel_oil_add(&sg, pim_ifp->mroute_vif_index);
496 if (!oil->installed)
497 pim_mroute_add(oil, __PRETTY_FUNCTION__);
498 if (pim_if_connected_to_source(ifp, sg.src)) {
499 up = pim_upstream_add(&sg, ifp, PIM_UPSTREAM_FLAG_MASK_FHR,
500 __PRETTY_FUNCTION__);
501 if (!up) {
502 if (PIM_DEBUG_MROUTE)
503 zlog_debug(
504 "%s: WRONGVIF%s unable to create upstream on interface",
505 pim_str_sg_dump(&sg), ifp->name);
506 return -2;
507 }
508 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up->flags);
509 pim_upstream_keep_alive_timer_start(up, qpim_keep_alive_time);
510 up->channel_oil = oil;
511 up->channel_oil->cc.pktcnt++;
512 pim_register_join(up);
513 pim_upstream_inherited_olist(up);
514
515 // Send the packet to the RP
516 pim_mroute_msg_wholepkt(fd, ifp, buf);
517 }
08e1fe76 518
d62a17ae 519 return 0;
08e1fe76
DS
520}
521
405d6357
DS
522static int pim_mroute_msg(struct pim_instance *pim, const char *buf,
523 int buf_size)
e355e30f 524{
d62a17ae 525 struct interface *ifp;
526 struct pim_interface *pim_ifp;
527 const struct ip *ip_hdr;
528 const struct igmpmsg *msg;
529 char ip_src_str[INET_ADDRSTRLEN] = "";
530 char ip_dst_str[INET_ADDRSTRLEN] = "";
531 char src_str[INET_ADDRSTRLEN] = "<src?>";
532 char grp_str[INET_ADDRSTRLEN] = "<grp?>";
533 struct in_addr ifaddr;
534 struct igmp_sock *igmp;
535
536 ip_hdr = (const struct ip *)buf;
537
538 if (ip_hdr->ip_p == IPPROTO_IGMP) {
539
540 /* We have the IP packet but we do not know which interface this
541 * packet was
542 * received on. Find the interface that is on the same subnet as
543 * the source
544 * of the IP packet.
545 */
405d6357 546 ifp = pim_if_lookup_address_vrf(ip_hdr->ip_src, pim->vrf_id);
d62a17ae 547
548 if (!ifp) {
549 if (PIM_DEBUG_MROUTE_DETAIL) {
550 pim_inet4_dump("<src?>", ip_hdr->ip_src,
551 ip_src_str, sizeof(ip_src_str));
552 pim_inet4_dump("<dst?>", ip_hdr->ip_dst,
553 ip_dst_str, sizeof(ip_dst_str));
554
555 zlog_warn(
556 "%s: igmp kernel upcall could not find usable interface for %s -> %s",
557 __PRETTY_FUNCTION__, ip_src_str,
558 ip_dst_str);
559 }
560 return 0;
561 }
562 pim_ifp = ifp->info;
563 ifaddr = pim_find_primary_addr(ifp);
564 igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->igmp_socket_list,
565 ifaddr);
566
567 if (PIM_DEBUG_MROUTE) {
568 pim_inet4_dump("<src?>", ip_hdr->ip_src, ip_src_str,
569 sizeof(ip_src_str));
570 pim_inet4_dump("<dst?>", ip_hdr->ip_dst, ip_dst_str,
571 sizeof(ip_dst_str));
572
573 zlog_warn(
574 "%s: igmp kernel upcall on %s(%p) for %s -> %s",
575 __PRETTY_FUNCTION__, ifp->name, igmp,
576 ip_src_str, ip_dst_str);
577 }
578 if (igmp)
579 pim_igmp_packet(igmp, (char *)buf, buf_size);
580
581 } else if (ip_hdr->ip_p) {
582 if (PIM_DEBUG_MROUTE_DETAIL) {
583 pim_inet4_dump("<src?>", ip_hdr->ip_src, src_str,
584 sizeof(src_str));
585 pim_inet4_dump("<grp?>", ip_hdr->ip_dst, grp_str,
586 sizeof(grp_str));
587 zlog_debug(
588 "%s: no kernel upcall proto=%d src: %s dst: %s msg_size=%d",
589 __PRETTY_FUNCTION__, ip_hdr->ip_p, src_str,
590 grp_str, buf_size);
591 }
592
593 } else {
594 msg = (const struct igmpmsg *)buf;
595
596 ifp = pim_if_find_by_vif_index(msg->im_vif);
597
598 if (!ifp)
599 return 0;
600 if (PIM_DEBUG_MROUTE) {
601 pim_inet4_dump("<src?>", msg->im_src, src_str,
602 sizeof(src_str));
603 pim_inet4_dump("<grp?>", msg->im_dst, grp_str,
604 sizeof(grp_str));
605 zlog_warn(
606 "%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d size=%d",
607 __PRETTY_FUNCTION__,
608 igmpmsgtype2str[msg->im_msgtype],
405d6357
DS
609 msg->im_msgtype, ip_hdr->ip_p,
610 pim->mroute_socket, src_str, grp_str, ifp->name,
611 msg->im_vif, buf_size);
d62a17ae 612 }
613
614 switch (msg->im_msgtype) {
615 case IGMPMSG_WRONGVIF:
405d6357
DS
616 return pim_mroute_msg_wrongvif(pim->mroute_socket, ifp,
617 msg);
d62a17ae 618 break;
619 case IGMPMSG_NOCACHE:
405d6357
DS
620 return pim_mroute_msg_nocache(pim->mroute_socket, ifp,
621 msg);
d62a17ae 622 break;
623 case IGMPMSG_WHOLEPKT:
405d6357 624 return pim_mroute_msg_wholepkt(pim->mroute_socket, ifp,
d62a17ae 625 (const char *)msg);
626 break;
627 case IGMPMSG_WRVIFWHOLE:
405d6357
DS
628 return pim_mroute_msg_wrvifwhole(
629 pim->mroute_socket, ifp, (const char *)msg);
d62a17ae 630 break;
631 default:
632 break;
633 }
634 }
12e41d03 635
d62a17ae 636 return 0;
12e41d03
DL
637}
638
12e41d03
DL
639static int mroute_read(struct thread *t)
640{
8ea5d944 641 struct pim_instance *pim;
d62a17ae 642 static long long count;
643 char buf[10000];
644 int result = 0;
645 int cont = 1;
d62a17ae 646 int rd;
647
8ea5d944 648 pim = THREAD_ARG(t);
d62a17ae 649
650 while (cont) {
8ea5d944 651 rd = read(pim->mroute_socket, buf, sizeof(buf));
61e99c94 652 if (rd <= 0) {
d62a17ae 653 if (errno == EINTR)
654 continue;
655 if (errno == EWOULDBLOCK || errno == EAGAIN)
656 break;
657
658 if (PIM_DEBUG_MROUTE)
659 zlog_warn(
61e99c94 660 "%s: failure reading rd=%d: fd=%d: errno=%d: %s",
8ea5d944
DS
661 __PRETTY_FUNCTION__, rd,
662 pim->mroute_socket, errno,
d62a17ae 663 safe_strerror(errno));
664 goto done;
665 }
666
405d6357 667 result = pim_mroute_msg(pim, buf, rd);
d62a17ae 668
669 count++;
670 if (count % qpim_packet_process == 0)
671 cont = 0;
672 }
673/* Keep reading */
674done:
8ea5d944 675 mroute_read_on(pim);
12e41d03 676
d62a17ae 677 return result;
12e41d03
DL
678}
679
8ea5d944 680static void mroute_read_on(struct pim_instance *pim)
12e41d03 681{
ff673f45 682 thread_add_read(master, mroute_read, 0, pimg->mroute_socket,
8ea5d944 683 &pim->thread);
12e41d03
DL
684}
685
8ea5d944 686static void mroute_read_off(struct pim_instance *pim)
12e41d03 687{
8ea5d944 688 THREAD_OFF(pim->thread);
12e41d03
DL
689}
690
6beed987 691int pim_mroute_socket_enable(struct pim_instance *pim)
12e41d03 692{
d62a17ae 693 int fd;
12e41d03 694
d62a17ae 695 if (pimd_privs.change(ZPRIVS_RAISE))
696 zlog_err("pim_mroute_socket_enable: could not raise privs, %s",
697 safe_strerror(errno));
12e41d03 698
d62a17ae 699 fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
12e41d03 700
d62a17ae 701 if (pimd_privs.change(ZPRIVS_LOWER))
702 zlog_err("pim_mroute_socket_enable: could not lower privs, %s",
703 safe_strerror(errno));
12e41d03 704
d62a17ae 705 if (fd < 0) {
706 zlog_warn("Could not create mroute socket: errno=%d: %s", errno,
707 safe_strerror(errno));
708 return -2;
709 }
12e41d03 710
cdbfaec5
DS
711 pim->mroute_socket = fd;
712 if (pim_mroute_set(pim, 1)) {
d62a17ae 713 zlog_warn(
714 "Could not enable mroute on socket fd=%d: errno=%d: %s",
715 fd, errno, safe_strerror(errno));
716 close(fd);
cdbfaec5 717 pim->mroute_socket = -1;
d62a17ae 718 return -3;
719 }
12e41d03 720
6beed987 721 pim->mroute_socket_creation = pim_time_monotonic_sec();
b45cefcb 722
8ea5d944 723 mroute_read_on(pim);
12e41d03 724
d62a17ae 725 return 0;
12e41d03
DL
726}
727
6beed987 728int pim_mroute_socket_disable(struct pim_instance *pim)
12e41d03 729{
cdbfaec5 730 if (pim_mroute_set(pim, 0)) {
d62a17ae 731 zlog_warn(
732 "Could not disable mroute on socket fd=%d: errno=%d: %s",
405d6357 733 pim->mroute_socket, errno, safe_strerror(errno));
d62a17ae 734 return -2;
735 }
736
6beed987 737 if (close(pim->mroute_socket)) {
d62a17ae 738 zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
405d6357 739 pim->mroute_socket, errno, safe_strerror(errno));
d62a17ae 740 return -3;
741 }
742
8ea5d944 743 mroute_read_off(pim);
6beed987 744 pim->mroute_socket = -1;
d62a17ae 745
746 return 0;
12e41d03
DL
747}
748
749/*
750 For each network interface (e.g., physical or a virtual tunnel) that
751 would be used for multicast forwarding, a corresponding multicast
752 interface must be added to the kernel.
753 */
d62a17ae 754int pim_mroute_add_vif(struct interface *ifp, struct in_addr ifaddr,
755 unsigned char flags)
12e41d03 756{
d62a17ae 757 struct pim_interface *pim_ifp = ifp->info;
758 struct vifctl vc;
759 int err;
12e41d03 760
d62a17ae 761 memset(&vc, 0, sizeof(vc));
762 vc.vifc_vifi = pim_ifp->mroute_vif_index;
b3f2bf7c 763#ifdef VIFF_USE_IFINDEX
d62a17ae 764 vc.vifc_lcl_ifindex = ifp->ifindex;
b3f2bf7c 765#else
d62a17ae 766 if (ifaddr.s_addr == INADDR_ANY) {
767 zlog_warn(
768 "%s: unnumbered interfaces are not supported on this platform",
769 __PRETTY_FUNCTION__);
770 return -1;
771 }
772 memcpy(&vc.vifc_lcl_addr, &ifaddr, sizeof(vc.vifc_lcl_addr));
b3f2bf7c 773#endif
d62a17ae 774 vc.vifc_flags = flags;
775 vc.vifc_threshold = PIM_MROUTE_MIN_TTL;
776 vc.vifc_rate_limit = 0;
777
778#ifdef PIM_DVMRP_TUNNEL
779 if (vc.vifc_flags & VIFF_TUNNEL) {
780 memcpy(&vc.vifc_rmt_addr, &vif_remote_addr,
781 sizeof(vc.vifc_rmt_addr));
782 }
12e41d03
DL
783#endif
784
ff673f45 785 err = setsockopt(pimg->mroute_socket, IPPROTO_IP, MRT_ADD_VIF,
d62a17ae 786 (void *)&vc, sizeof(vc));
787 if (err) {
788 char ifaddr_str[INET_ADDRSTRLEN];
12e41d03 789
d62a17ae 790 pim_inet4_dump("<ifaddr?>", ifaddr, ifaddr_str,
791 sizeof(ifaddr_str));
12e41d03 792
d62a17ae 793 zlog_warn(
794 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s,flag=%d): errno=%d: %s",
ff673f45 795 __FILE__, __PRETTY_FUNCTION__, pimg->mroute_socket,
d62a17ae 796 ifp->ifindex, ifaddr_str, flags, errno,
797 safe_strerror(errno));
798 return -2;
799 }
12e41d03 800
d62a17ae 801 return 0;
12e41d03
DL
802}
803
804int pim_mroute_del_vif(int vif_index)
805{
d62a17ae 806 struct vifctl vc;
807 int err;
808
809 if (PIM_DEBUG_MROUTE) {
810 struct interface *ifp = pim_if_find_by_vif_index(vif_index);
811 zlog_debug("%s %s: Del Vif %d (%s) ", __FILE__,
812 __PRETTY_FUNCTION__, vif_index,
813 ifp ? ifp->name : "NULL");
814 }
12e41d03 815
d62a17ae 816 memset(&vc, 0, sizeof(vc));
817 vc.vifc_vifi = vif_index;
818
ff673f45 819 err = setsockopt(pimg->mroute_socket, IPPROTO_IP, MRT_DEL_VIF,
d62a17ae 820 (void *)&vc, sizeof(vc));
821 if (err) {
822 zlog_warn(
823 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
ff673f45 824 __FILE__, __PRETTY_FUNCTION__, pimg->mroute_socket,
d62a17ae 825 vif_index, errno, safe_strerror(errno));
826 return -2;
827 }
12e41d03 828
d62a17ae 829 return 0;
12e41d03
DL
830}
831
6a78764e 832int pim_mroute_add(struct channel_oil *c_oil, const char *name)
12e41d03 833{
d62a17ae 834 int err;
835 int orig = 0;
836 int orig_iif_vif = 0;
837
ff673f45
DS
838 pimg->mroute_add_last = pim_time_monotonic_sec();
839 ++pimg->mroute_add_events;
d62a17ae 840
841 /* Do not install route if incoming interface is undefined. */
842 if (c_oil->oil.mfcc_parent >= MAXVIFS) {
843 if (PIM_DEBUG_MROUTE) {
844 char buf[1000];
845 zlog_debug(
846 "%s(%s) %s Attempting to add vifi that is invalid to mroute table",
847 __PRETTY_FUNCTION__, name,
848 pim_channel_oil_dump(c_oil, buf, sizeof(buf)));
849 }
850 return -2;
851 }
d3aded99 852
d62a17ae 853 /* The linux kernel *expects* the incoming
854 * vif to be part of the outgoing list
855 * in the case of a (*,G).
856 */
857 if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY) {
858 orig = c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent];
859 c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = 1;
860 }
d3aded99 861
d62a17ae 862 /*
863 * If we have an unresolved cache entry for the S,G
864 * it is owned by the pimreg for the incoming IIF
865 * So set pimreg as the IIF temporarily to cause
866 * the packets to be forwarded. Then set it
867 * to the correct IIF afterwords.
868 */
869 if (!c_oil->installed && c_oil->oil.mfcc_origin.s_addr != INADDR_ANY
870 && c_oil->oil.mfcc_parent != 0) {
871 orig_iif_vif = c_oil->oil.mfcc_parent;
872 c_oil->oil.mfcc_parent = 0;
873 }
ff673f45 874 err = setsockopt(pimg->mroute_socket, IPPROTO_IP, MRT_ADD_MFC,
d62a17ae 875 &c_oil->oil, sizeof(c_oil->oil));
876
877 if (!err && !c_oil->installed
878 && c_oil->oil.mfcc_origin.s_addr != INADDR_ANY
879 && orig_iif_vif != 0) {
880 c_oil->oil.mfcc_parent = orig_iif_vif;
ff673f45 881 err = setsockopt(pimg->mroute_socket, IPPROTO_IP, MRT_ADD_MFC,
d62a17ae 882 &c_oil->oil, sizeof(c_oil->oil));
883 }
0365f56b 884
d62a17ae 885 if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY)
886 c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = orig;
d3aded99 887
d62a17ae 888 if (err) {
889 zlog_warn(
890 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
ff673f45 891 __FILE__, __PRETTY_FUNCTION__, pimg->mroute_socket,
d62a17ae 892 errno, safe_strerror(errno));
893 return -2;
894 }
12e41d03 895
d62a17ae 896 if (PIM_DEBUG_MROUTE) {
897 char buf[1000];
898 zlog_debug("%s(%s), Added Route: %s", __PRETTY_FUNCTION__, name,
899 pim_channel_oil_dump(c_oil, buf, sizeof(buf)));
900 }
6a78764e 901
d62a17ae 902 c_oil->installed = 1;
903 return 0;
12e41d03
DL
904}
905
d62a17ae 906int pim_mroute_del(struct channel_oil *c_oil, const char *name)
12e41d03 907{
d62a17ae 908 int err;
909
ff673f45
DS
910 pimg->mroute_del_last = pim_time_monotonic_sec();
911 ++pimg->mroute_del_events;
d62a17ae 912
913 if (!c_oil->installed) {
914 if (PIM_DEBUG_MROUTE) {
915 char buf[1000];
916 zlog_debug(
917 "%s %s: vifi %d for route is %s not installed, do not need to send del req. ",
918 __FILE__, __PRETTY_FUNCTION__,
919 c_oil->oil.mfcc_parent,
920 pim_channel_oil_dump(c_oil, buf, sizeof(buf)));
921 }
922 return -2;
923 }
12e41d03 924
ff673f45 925 err = setsockopt(pimg->mroute_socket, IPPROTO_IP, MRT_DEL_MFC,
d62a17ae 926 &c_oil->oil, sizeof(c_oil->oil));
927 if (err) {
928 if (PIM_DEBUG_MROUTE)
929 zlog_warn(
930 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
931 __FILE__, __PRETTY_FUNCTION__,
ff673f45 932 pimg->mroute_socket, errno,
d62a17ae 933 safe_strerror(errno));
934 return -2;
935 }
429a291b 936
d62a17ae 937 if (PIM_DEBUG_MROUTE) {
938 char buf[1000];
939 zlog_debug("%s(%s), Deleted Route: %s", __PRETTY_FUNCTION__,
940 name, pim_channel_oil_dump(c_oil, buf, sizeof(buf)));
941 }
781a1745 942
d62a17ae 943 // Reset kernel installed flag
944 c_oil->installed = 0;
58302dc7 945
d62a17ae 946 return 0;
12e41d03 947}
3667e8a0 948
d62a17ae 949void pim_mroute_update_counters(struct channel_oil *c_oil)
3667e8a0 950{
d62a17ae 951 struct sioc_sg_req sgreq;
952
953 c_oil->cc.oldpktcnt = c_oil->cc.pktcnt;
954 c_oil->cc.oldbytecnt = c_oil->cc.bytecnt;
955 c_oil->cc.oldwrong_if = c_oil->cc.wrong_if;
956
957 if (!c_oil->installed) {
958 c_oil->cc.lastused = 100 * qpim_keep_alive_time;
959 if (PIM_DEBUG_MROUTE) {
960 struct prefix_sg sg;
961
962 sg.src = c_oil->oil.mfcc_origin;
963 sg.grp = c_oil->oil.mfcc_mcastgrp;
964 if (PIM_DEBUG_MROUTE)
965 zlog_debug(
966 "Channel(%s) is not installed no need to collect data from kernel",
967 pim_str_sg_dump(&sg));
968 }
969 return;
c7b1183f 970 }
c7b1183f 971
d62a17ae 972 memset(&sgreq, 0, sizeof(sgreq));
973 sgreq.src = c_oil->oil.mfcc_origin;
974 sgreq.grp = c_oil->oil.mfcc_mcastgrp;
975
976 pim_zlookup_sg_statistics(c_oil);
ff673f45 977 if (ioctl(pimg->mroute_socket, SIOCGETSGCNT, &sgreq)) {
d62a17ae 978 if (PIM_DEBUG_MROUTE) {
979 struct prefix_sg sg;
980
981 sg.src = c_oil->oil.mfcc_origin;
982 sg.grp = c_oil->oil.mfcc_mcastgrp;
983
984 zlog_warn(
985 "ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=(%s): errno=%d: %s",
986 (unsigned long)SIOCGETSGCNT,
987 pim_str_sg_dump(&sg), errno,
988 safe_strerror(errno));
989 }
990 return;
c7b1183f 991 }
3667e8a0 992
d62a17ae 993 c_oil->cc.pktcnt = sgreq.pktcnt;
994 c_oil->cc.bytecnt = sgreq.bytecnt;
995 c_oil->cc.wrong_if = sgreq.wrong_if;
3667e8a0 996
d62a17ae 997 return;
3667e8a0 998}