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