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