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