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