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