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