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