]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_mroute.c
pimd: Add some more vrf debug information
[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(up, pim_ifp->pim->keep_alive_time);
534 up->channel_oil = oil;
535 up->channel_oil->cc.pktcnt++;
536 pim_register_join(up);
537 pim_upstream_inherited_olist(pim_ifp->pim, up);
538
539 // Send the packet to the RP
540 pim_mroute_msg_wholepkt(fd, ifp, buf);
541 }
542
543 return 0;
544 }
545
546 static int pim_mroute_msg(struct pim_instance *pim, const char *buf,
547 int buf_size, ifindex_t ifindex)
548 {
549 struct interface *ifp;
550 struct pim_interface *pim_ifp;
551 const struct ip *ip_hdr;
552 const struct igmpmsg *msg;
553 char ip_src_str[INET_ADDRSTRLEN] = "";
554 char ip_dst_str[INET_ADDRSTRLEN] = "";
555 char src_str[INET_ADDRSTRLEN] = "<src?>";
556 char grp_str[INET_ADDRSTRLEN] = "<grp?>";
557 struct in_addr ifaddr;
558 struct igmp_sock *igmp;
559
560 ip_hdr = (const struct ip *)buf;
561
562 if (ip_hdr->ip_p == IPPROTO_IGMP) {
563
564 /* We have the IP packet but we do not know which interface this
565 * packet was
566 * received on. Find the interface that is on the same subnet as
567 * the source
568 * of the IP packet.
569 */
570 ifp = if_lookup_by_index(ifindex, pim->vrf_id);
571
572 if (!ifp || !ifp->info)
573 return 0;
574
575 pim_ifp = ifp->info;
576 ifaddr = pim_find_primary_addr(ifp);
577 igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->igmp_socket_list,
578 ifaddr);
579
580 if (PIM_DEBUG_MROUTE) {
581 pim_inet4_dump("<src?>", ip_hdr->ip_src, ip_src_str,
582 sizeof(ip_src_str));
583 pim_inet4_dump("<dst?>", ip_hdr->ip_dst, ip_dst_str,
584 sizeof(ip_dst_str));
585
586 zlog_warn(
587 "%s(%s): igmp kernel upcall on %s(%p) for %s -> %s",
588 __PRETTY_FUNCTION__, pim->vrf->name, ifp->name,
589 igmp, ip_src_str, ip_dst_str);
590 }
591 if (igmp)
592 pim_igmp_packet(igmp, (char *)buf, buf_size);
593
594 } else if (ip_hdr->ip_p) {
595 if (PIM_DEBUG_MROUTE_DETAIL) {
596 pim_inet4_dump("<src?>", ip_hdr->ip_src, src_str,
597 sizeof(src_str));
598 pim_inet4_dump("<grp?>", ip_hdr->ip_dst, grp_str,
599 sizeof(grp_str));
600 zlog_debug(
601 "%s: no kernel upcall proto=%d src: %s dst: %s msg_size=%d",
602 __PRETTY_FUNCTION__, ip_hdr->ip_p, src_str,
603 grp_str, buf_size);
604 }
605
606 } else {
607 msg = (const struct igmpmsg *)buf;
608
609 ifp = pim_if_find_by_vif_index(pim, msg->im_vif);
610
611 if (!ifp)
612 return 0;
613 if (PIM_DEBUG_MROUTE) {
614 pim_inet4_dump("<src?>", msg->im_src, src_str,
615 sizeof(src_str));
616 pim_inet4_dump("<grp?>", msg->im_dst, grp_str,
617 sizeof(grp_str));
618 zlog_warn(
619 "%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d size=%d",
620 __PRETTY_FUNCTION__,
621 igmpmsgtype2str[msg->im_msgtype],
622 msg->im_msgtype, ip_hdr->ip_p,
623 pim->mroute_socket, src_str, grp_str, ifp->name,
624 msg->im_vif, buf_size);
625 }
626
627 switch (msg->im_msgtype) {
628 case IGMPMSG_WRONGVIF:
629 return pim_mroute_msg_wrongvif(pim->mroute_socket, ifp,
630 msg);
631 break;
632 case IGMPMSG_NOCACHE:
633 return pim_mroute_msg_nocache(pim->mroute_socket, ifp,
634 msg);
635 break;
636 case IGMPMSG_WHOLEPKT:
637 return pim_mroute_msg_wholepkt(pim->mroute_socket, ifp,
638 (const char *)msg);
639 break;
640 case IGMPMSG_WRVIFWHOLE:
641 return pim_mroute_msg_wrvifwhole(
642 pim->mroute_socket, ifp, (const char *)msg);
643 break;
644 default:
645 break;
646 }
647 }
648
649 return 0;
650 }
651
652 static int mroute_read(struct thread *t)
653 {
654 struct pim_instance *pim;
655 static long long count;
656 char buf[10000];
657 int result = 0;
658 int cont = 1;
659 int rd;
660 ifindex_t ifindex;
661 pim = THREAD_ARG(t);
662
663 while (cont) {
664 rd = pim_socket_recvfromto(pim->mroute_socket, (uint8_t *)buf,
665 sizeof(buf), NULL, NULL, NULL, NULL,
666 &ifindex);
667 if (rd <= 0) {
668 if (errno == EINTR)
669 continue;
670 if (errno == EWOULDBLOCK || errno == EAGAIN)
671 break;
672
673 if (PIM_DEBUG_MROUTE)
674 zlog_warn(
675 "%s: failure reading rd=%d: fd=%d: errno=%d: %s",
676 __PRETTY_FUNCTION__, rd,
677 pim->mroute_socket, errno,
678 safe_strerror(errno));
679 goto done;
680 }
681
682 result = pim_mroute_msg(pim, buf, rd, ifindex);
683
684 count++;
685 if (count % qpim_packet_process == 0)
686 cont = 0;
687 }
688 /* Keep reading */
689 done:
690 mroute_read_on(pim);
691
692 return result;
693 }
694
695 static void mroute_read_on(struct pim_instance *pim)
696 {
697 thread_add_read(master, mroute_read, pim, pim->mroute_socket,
698 &pim->thread);
699 }
700
701 static void mroute_read_off(struct pim_instance *pim)
702 {
703 THREAD_OFF(pim->thread);
704 }
705
706 int pim_mroute_socket_enable(struct pim_instance *pim)
707 {
708 int fd;
709
710 if (pimd_privs.change(ZPRIVS_RAISE))
711 zlog_err("pim_mroute_socket_enable: could not raise privs, %s",
712 safe_strerror(errno));
713
714 fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
715
716 #ifdef SO_BINDTODEVICE
717 setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, pim->vrf->name,
718 strlen(pim->vrf->name));
719 #endif
720
721 if (pimd_privs.change(ZPRIVS_LOWER))
722 zlog_err("pim_mroute_socket_enable: could not lower privs, %s",
723 safe_strerror(errno));
724
725 if (fd < 0) {
726 zlog_warn("Could not create mroute socket: errno=%d: %s", errno,
727 safe_strerror(errno));
728 return -2;
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,
784 ifp->name, 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__,
821 pim_ifp->pim->mroute_socket, ifp->ifindex, ifaddr_str,
822 flags, errno, 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,
838 ifp->name, 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", __PRETTY_FUNCTION__, name,
925 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", __PRETTY_FUNCTION__,
968 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 }