]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_mroute.c
pimd: Fix dropped(?) telling of the kernel to recv cmsg data
[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, qpim_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__);
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, qpim_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 if (!up->channel_oil)
489 up->channel_oil = pim_channel_oil_add(
490 pim_ifp->pim, &sg,
491 pim_ifp->mroute_vif_index);
492 pim_upstream_inherited_olist(pim_ifp->pim, up);
493 if (!up->channel_oil->installed)
494 pim_mroute_add(up->channel_oil,
495 __PRETTY_FUNCTION__);
496 pim_upstream_set_sptbit(up, ifp);
497 } else {
498 if (I_am_RP(pim_ifp->pim, up->sg.grp)) {
499 if (pim_nexthop_lookup(pim_ifp->pim, &source,
500 up->upstream_register, 0)
501 == 0)
502 pim_register_stop_send(
503 source.interface, &sg,
504 pim_ifp->primary_address,
505 up->upstream_register);
506 up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
507 }
508 pim_upstream_keep_alive_timer_start(
509 up, qpim_keep_alive_time);
510 pim_upstream_inherited_olist(pim_ifp->pim, up);
511 pim_mroute_msg_wholepkt(fd, ifp, buf);
512 }
513 return 0;
514 }
515
516 pim_ifp = ifp->info;
517 oil = pim_channel_oil_add(pim_ifp->pim, &sg, pim_ifp->mroute_vif_index);
518 if (!oil->installed)
519 pim_mroute_add(oil, __PRETTY_FUNCTION__);
520 if (pim_if_connected_to_source(ifp, sg.src)) {
521 up = pim_upstream_add(pim_ifp->pim, &sg, ifp,
522 PIM_UPSTREAM_FLAG_MASK_FHR,
523 __PRETTY_FUNCTION__);
524 if (!up) {
525 if (PIM_DEBUG_MROUTE)
526 zlog_debug(
527 "%s: WRONGVIF%s unable to create upstream on interface",
528 pim_str_sg_dump(&sg), ifp->name);
529 return -2;
530 }
531 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up->flags);
532 pim_upstream_keep_alive_timer_start(up, qpim_keep_alive_time);
533 up->channel_oil = oil;
534 up->channel_oil->cc.pktcnt++;
535 pim_register_join(up);
536 pim_upstream_inherited_olist(pim_ifp->pim, up);
537
538 // Send the packet to the RP
539 pim_mroute_msg_wholepkt(fd, ifp, buf);
540 }
541
542 return 0;
543 }
544
545 static int pim_mroute_msg(struct pim_instance *pim, const char *buf,
546 int buf_size, ifindex_t ifindex)
547 {
548 struct interface *ifp;
549 struct pim_interface *pim_ifp;
550 const struct ip *ip_hdr;
551 const struct igmpmsg *msg;
552 char ip_src_str[INET_ADDRSTRLEN] = "";
553 char ip_dst_str[INET_ADDRSTRLEN] = "";
554 char src_str[INET_ADDRSTRLEN] = "<src?>";
555 char grp_str[INET_ADDRSTRLEN] = "<grp?>";
556 struct in_addr ifaddr;
557 struct igmp_sock *igmp;
558
559 ip_hdr = (const struct ip *)buf;
560
561 if (ip_hdr->ip_p == IPPROTO_IGMP) {
562
563 /* We have the IP packet but we do not know which interface this
564 * packet was
565 * received on. Find the interface that is on the same subnet as
566 * the source
567 * of the IP packet.
568 */
569 ifp = if_lookup_by_index(ifindex, pim->vrf_id);
570
571 if (!ifp)
572 return 0;
573
574 pim_ifp = ifp->info;
575 ifaddr = pim_find_primary_addr(ifp);
576 igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->igmp_socket_list,
577 ifaddr);
578
579 if (PIM_DEBUG_MROUTE) {
580 pim_inet4_dump("<src?>", ip_hdr->ip_src, ip_src_str,
581 sizeof(ip_src_str));
582 pim_inet4_dump("<dst?>", ip_hdr->ip_dst, ip_dst_str,
583 sizeof(ip_dst_str));
584
585 zlog_warn(
586 "%s(%s): igmp kernel upcall on %s(%p) for %s -> %s",
587 __PRETTY_FUNCTION__, pim->vrf->name, ifp->name,
588 igmp, ip_src_str, ip_dst_str);
589 }
590 if (igmp)
591 pim_igmp_packet(igmp, (char *)buf, buf_size);
592
593 } else if (ip_hdr->ip_p) {
594 if (PIM_DEBUG_MROUTE_DETAIL) {
595 pim_inet4_dump("<src?>", ip_hdr->ip_src, src_str,
596 sizeof(src_str));
597 pim_inet4_dump("<grp?>", ip_hdr->ip_dst, grp_str,
598 sizeof(grp_str));
599 zlog_debug(
600 "%s: no kernel upcall proto=%d src: %s dst: %s msg_size=%d",
601 __PRETTY_FUNCTION__, ip_hdr->ip_p, src_str,
602 grp_str, buf_size);
603 }
604
605 } else {
606 msg = (const struct igmpmsg *)buf;
607
608 ifp = pim_if_find_by_vif_index(pim, msg->im_vif);
609
610 if (!ifp)
611 return 0;
612 if (PIM_DEBUG_MROUTE) {
613 pim_inet4_dump("<src?>", msg->im_src, src_str,
614 sizeof(src_str));
615 pim_inet4_dump("<grp?>", msg->im_dst, grp_str,
616 sizeof(grp_str));
617 zlog_warn(
618 "%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d size=%d",
619 __PRETTY_FUNCTION__,
620 igmpmsgtype2str[msg->im_msgtype],
621 msg->im_msgtype, ip_hdr->ip_p,
622 pim->mroute_socket, src_str, grp_str, ifp->name,
623 msg->im_vif, buf_size);
624 }
625
626 switch (msg->im_msgtype) {
627 case IGMPMSG_WRONGVIF:
628 return pim_mroute_msg_wrongvif(pim->mroute_socket, ifp,
629 msg);
630 break;
631 case IGMPMSG_NOCACHE:
632 return pim_mroute_msg_nocache(pim->mroute_socket, ifp,
633 msg);
634 break;
635 case IGMPMSG_WHOLEPKT:
636 return pim_mroute_msg_wholepkt(pim->mroute_socket, ifp,
637 (const char *)msg);
638 break;
639 case IGMPMSG_WRVIFWHOLE:
640 return pim_mroute_msg_wrvifwhole(
641 pim->mroute_socket, ifp, (const char *)msg);
642 break;
643 default:
644 break;
645 }
646 }
647
648 return 0;
649 }
650
651 static int mroute_read(struct thread *t)
652 {
653 struct pim_instance *pim;
654 static long long count;
655 char buf[10000];
656 int result = 0;
657 int cont = 1;
658 int rd;
659 ifindex_t ifindex;
660 pim = THREAD_ARG(t);
661
662 while (cont) {
663 rd = pim_socket_recvfromto(pim->mroute_socket, (uint8_t *)buf,
664 sizeof(buf), NULL, NULL, NULL, NULL,
665 &ifindex);
666 if (rd <= 0) {
667 if (errno == EINTR)
668 continue;
669 if (errno == EWOULDBLOCK || errno == EAGAIN)
670 break;
671
672 if (PIM_DEBUG_MROUTE)
673 zlog_warn(
674 "%s: failure reading rd=%d: fd=%d: errno=%d: %s",
675 __PRETTY_FUNCTION__, rd,
676 pim->mroute_socket, errno,
677 safe_strerror(errno));
678 goto done;
679 }
680
681 result = pim_mroute_msg(pim, buf, rd, ifindex);
682
683 count++;
684 if (count % qpim_packet_process == 0)
685 cont = 0;
686 }
687 /* Keep reading */
688 done:
689 mroute_read_on(pim);
690
691 return result;
692 }
693
694 static void mroute_read_on(struct pim_instance *pim)
695 {
696 thread_add_read(master, mroute_read, pim, pim->mroute_socket,
697 &pim->thread);
698 }
699
700 static void mroute_read_off(struct pim_instance *pim)
701 {
702 THREAD_OFF(pim->thread);
703 }
704
705 int pim_mroute_socket_enable(struct pim_instance *pim)
706 {
707 int fd;
708
709 if (pimd_privs.change(ZPRIVS_RAISE))
710 zlog_err("pim_mroute_socket_enable: could not raise privs, %s",
711 safe_strerror(errno));
712
713 fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
714
715 #ifdef SO_BINDTODEVICE
716 setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, pim->vrf->name,
717 strlen(pim->vrf->name));
718 #endif
719
720 if (pimd_privs.change(ZPRIVS_LOWER))
721 zlog_err("pim_mroute_socket_enable: could not lower privs, %s",
722 safe_strerror(errno));
723
724 if (fd < 0) {
725 zlog_warn("Could not create mroute socket: errno=%d: %s", errno,
726 safe_strerror(errno));
727 return -2;
728 }
729
730 pim->mroute_socket = fd;
731 if (pim_mroute_set(pim, 1)) {
732 zlog_warn(
733 "Could not enable mroute on socket fd=%d: errno=%d: %s",
734 fd, errno, safe_strerror(errno));
735 close(fd);
736 pim->mroute_socket = -1;
737 return -3;
738 }
739
740 pim->mroute_socket_creation = pim_time_monotonic_sec();
741
742 mroute_read_on(pim);
743
744 return 0;
745 }
746
747 int pim_mroute_socket_disable(struct pim_instance *pim)
748 {
749 if (pim_mroute_set(pim, 0)) {
750 zlog_warn(
751 "Could not disable mroute on socket fd=%d: errno=%d: %s",
752 pim->mroute_socket, errno, safe_strerror(errno));
753 return -2;
754 }
755
756 if (close(pim->mroute_socket)) {
757 zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
758 pim->mroute_socket, errno, safe_strerror(errno));
759 return -3;
760 }
761
762 mroute_read_off(pim);
763 pim->mroute_socket = -1;
764
765 return 0;
766 }
767
768 /*
769 For each network interface (e.g., physical or a virtual tunnel) that
770 would be used for multicast forwarding, a corresponding multicast
771 interface must be added to the kernel.
772 */
773 int pim_mroute_add_vif(struct interface *ifp, struct in_addr ifaddr,
774 unsigned char flags)
775 {
776 struct pim_interface *pim_ifp = ifp->info;
777 struct vifctl vc;
778 int err;
779
780 memset(&vc, 0, sizeof(vc));
781 vc.vifc_vifi = pim_ifp->mroute_vif_index;
782 #ifdef VIFF_USE_IFINDEX
783 vc.vifc_lcl_ifindex = ifp->ifindex;
784 #else
785 if (ifaddr.s_addr == INADDR_ANY) {
786 zlog_warn(
787 "%s: unnumbered interfaces are not supported on this platform",
788 __PRETTY_FUNCTION__);
789 return -1;
790 }
791 memcpy(&vc.vifc_lcl_addr, &ifaddr, sizeof(vc.vifc_lcl_addr));
792 #endif
793 vc.vifc_flags = flags;
794 vc.vifc_threshold = PIM_MROUTE_MIN_TTL;
795 vc.vifc_rate_limit = 0;
796
797 #ifdef PIM_DVMRP_TUNNEL
798 if (vc.vifc_flags & VIFF_TUNNEL) {
799 memcpy(&vc.vifc_rmt_addr, &vif_remote_addr,
800 sizeof(vc.vifc_rmt_addr));
801 }
802 #endif
803
804 err = setsockopt(pim_ifp->pim->mroute_socket, IPPROTO_IP, MRT_ADD_VIF,
805 (void *)&vc, sizeof(vc));
806 if (err) {
807 char ifaddr_str[INET_ADDRSTRLEN];
808
809 pim_inet4_dump("<ifaddr?>", ifaddr, ifaddr_str,
810 sizeof(ifaddr_str));
811
812 zlog_warn(
813 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s,flag=%d): errno=%d: %s",
814 __FILE__, __PRETTY_FUNCTION__,
815 pim_ifp->pim->mroute_socket, ifp->ifindex, ifaddr_str,
816 flags, errno, safe_strerror(errno));
817 return -2;
818 }
819
820 return 0;
821 }
822
823 int pim_mroute_del_vif(struct interface *ifp)
824 {
825 struct pim_interface *pim_ifp = ifp->info;
826 struct vifctl vc;
827 int err;
828
829 if (PIM_DEBUG_MROUTE)
830 zlog_debug("%s %s: Del Vif %d (%s) ", __FILE__,
831 __PRETTY_FUNCTION__, pim_ifp->mroute_vif_index,
832 ifp->name);
833
834 memset(&vc, 0, sizeof(vc));
835 vc.vifc_vifi = pim_ifp->mroute_vif_index;
836
837 err = setsockopt(pim_ifp->pim->mroute_socket, IPPROTO_IP, MRT_DEL_VIF,
838 (void *)&vc, sizeof(vc));
839 if (err) {
840 zlog_warn(
841 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
842 __FILE__, __PRETTY_FUNCTION__,
843 pim_ifp->pim->mroute_socket, pim_ifp->mroute_vif_index,
844 errno, safe_strerror(errno));
845 return -2;
846 }
847
848 return 0;
849 }
850
851 int pim_mroute_add(struct channel_oil *c_oil, const char *name)
852 {
853 struct pim_instance *pim = c_oil->pim;
854 int err;
855 int orig = 0;
856 int orig_iif_vif = 0;
857
858 pim->mroute_add_last = pim_time_monotonic_sec();
859 ++pim->mroute_add_events;
860
861 /* Do not install route if incoming interface is undefined. */
862 if (c_oil->oil.mfcc_parent >= MAXVIFS) {
863 if (PIM_DEBUG_MROUTE) {
864 char buf[1000];
865 zlog_debug(
866 "%s(%s) %s Attempting to add vifi that is invalid to mroute table",
867 __PRETTY_FUNCTION__, name,
868 pim_channel_oil_dump(c_oil, buf, sizeof(buf)));
869 }
870 return -2;
871 }
872
873 /* The linux kernel *expects* the incoming
874 * vif to be part of the outgoing list
875 * in the case of a (*,G).
876 */
877 if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY) {
878 orig = c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent];
879 c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = 1;
880 }
881
882 /*
883 * If we have an unresolved cache entry for the S,G
884 * it is owned by the pimreg for the incoming IIF
885 * So set pimreg as the IIF temporarily to cause
886 * the packets to be forwarded. Then set it
887 * to the correct IIF afterwords.
888 */
889 if (!c_oil->installed && c_oil->oil.mfcc_origin.s_addr != INADDR_ANY
890 && c_oil->oil.mfcc_parent != 0) {
891 orig_iif_vif = c_oil->oil.mfcc_parent;
892 c_oil->oil.mfcc_parent = 0;
893 }
894 err = setsockopt(pim->mroute_socket, IPPROTO_IP, MRT_ADD_MFC,
895 &c_oil->oil, sizeof(c_oil->oil));
896
897 if (!err && !c_oil->installed
898 && c_oil->oil.mfcc_origin.s_addr != INADDR_ANY
899 && orig_iif_vif != 0) {
900 c_oil->oil.mfcc_parent = orig_iif_vif;
901 err = setsockopt(pim->mroute_socket, IPPROTO_IP, MRT_ADD_MFC,
902 &c_oil->oil, sizeof(c_oil->oil));
903 }
904
905 if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY)
906 c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = orig;
907
908 if (err) {
909 zlog_warn(
910 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
911 __FILE__, __PRETTY_FUNCTION__, pim->mroute_socket,
912 errno, safe_strerror(errno));
913 return -2;
914 }
915
916 if (PIM_DEBUG_MROUTE) {
917 char buf[1000];
918 zlog_debug("%s(%s), Added Route: %s", __PRETTY_FUNCTION__, name,
919 pim_channel_oil_dump(c_oil, buf, sizeof(buf)));
920 }
921
922 c_oil->installed = 1;
923 return 0;
924 }
925
926 int pim_mroute_del(struct channel_oil *c_oil, const char *name)
927 {
928 struct pim_instance *pim = c_oil->pim;
929 int err;
930
931 pim->mroute_del_last = pim_time_monotonic_sec();
932 ++pim->mroute_del_events;
933
934 if (!c_oil->installed) {
935 if (PIM_DEBUG_MROUTE) {
936 char buf[1000];
937 zlog_debug(
938 "%s %s: vifi %d for route is %s not installed, do not need to send del req. ",
939 __FILE__, __PRETTY_FUNCTION__,
940 c_oil->oil.mfcc_parent,
941 pim_channel_oil_dump(c_oil, buf, sizeof(buf)));
942 }
943 return -2;
944 }
945
946 err = setsockopt(pim->mroute_socket, IPPROTO_IP, MRT_DEL_MFC,
947 &c_oil->oil, sizeof(c_oil->oil));
948 if (err) {
949 if (PIM_DEBUG_MROUTE)
950 zlog_warn(
951 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
952 __FILE__, __PRETTY_FUNCTION__,
953 pim->mroute_socket, errno,
954 safe_strerror(errno));
955 return -2;
956 }
957
958 if (PIM_DEBUG_MROUTE) {
959 char buf[1000];
960 zlog_debug("%s(%s), Deleted Route: %s", __PRETTY_FUNCTION__,
961 name, pim_channel_oil_dump(c_oil, buf, sizeof(buf)));
962 }
963
964 // Reset kernel installed flag
965 c_oil->installed = 0;
966
967 return 0;
968 }
969
970 void pim_mroute_update_counters(struct channel_oil *c_oil)
971 {
972 struct pim_instance *pim = c_oil->pim;
973 struct sioc_sg_req sgreq;
974
975 c_oil->cc.oldpktcnt = c_oil->cc.pktcnt;
976 c_oil->cc.oldbytecnt = c_oil->cc.bytecnt;
977 c_oil->cc.oldwrong_if = c_oil->cc.wrong_if;
978
979 if (!c_oil->installed) {
980 c_oil->cc.lastused = 100 * qpim_keep_alive_time;
981 if (PIM_DEBUG_MROUTE) {
982 struct prefix_sg sg;
983
984 sg.src = c_oil->oil.mfcc_origin;
985 sg.grp = c_oil->oil.mfcc_mcastgrp;
986 if (PIM_DEBUG_MROUTE)
987 zlog_debug(
988 "Channel(%s) is not installed no need to collect data from kernel",
989 pim_str_sg_dump(&sg));
990 }
991 return;
992 }
993
994 memset(&sgreq, 0, sizeof(sgreq));
995 sgreq.src = c_oil->oil.mfcc_origin;
996 sgreq.grp = c_oil->oil.mfcc_mcastgrp;
997
998 pim_zlookup_sg_statistics(c_oil);
999 if (ioctl(pim->mroute_socket, SIOCGETSGCNT, &sgreq)) {
1000 if (PIM_DEBUG_MROUTE) {
1001 struct prefix_sg sg;
1002
1003 sg.src = c_oil->oil.mfcc_origin;
1004 sg.grp = c_oil->oil.mfcc_mcastgrp;
1005
1006 zlog_warn(
1007 "ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=(%s): errno=%d: %s",
1008 (unsigned long)SIOCGETSGCNT,
1009 pim_str_sg_dump(&sg), errno,
1010 safe_strerror(errno));
1011 }
1012 return;
1013 }
1014
1015 c_oil->cc.pktcnt = sgreq.pktcnt;
1016 c_oil->cc.bytecnt = sgreq.bytecnt;
1017 c_oil->cc.wrong_if = sgreq.wrong_if;
1018
1019 return;
1020 }