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