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