]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_mroute.c
lib: Add LIB_ERR_SNMP
[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"
2002dcdb 43#include "pim_sock.h"
12e41d03 44
8ea5d944 45static void mroute_read_on(struct pim_instance *pim);
12e41d03 46
cdbfaec5 47static int pim_mroute_set(struct pim_instance *pim, int enable)
12e41d03 48{
d62a17ae 49 int err;
f507c196 50 int opt;
d62a17ae 51 socklen_t opt_len = sizeof(opt);
d62a17ae 52 long flags;
53
cdbfaec5
DS
54 /*
55 * We need to create the VRF table for the pim mroute_socket
56 */
57 if (pim->vrf_id != VRF_DEFAULT) {
f507c196
DS
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
44a1bd88 63 opt = pim->vrf->data.l.table_id;
cdbfaec5
DS
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 }
f507c196
DS
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;
cdbfaec5
DS
90 }
91
466e4e5b
DS
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
cdbfaec5 106 setsockopt_so_recvbuf(pim->mroute_socket, 1024 * 1024 * 8);
d62a17ae 107
cdbfaec5 108 flags = fcntl(pim->mroute_socket, F_GETFL, 0);
d62a17ae 109 if (flags < 0) {
cdbfaec5
DS
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);
d62a17ae 113 return -1;
114 }
cdbfaec5
DS
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);
d62a17ae 119 return -1;
120 }
121
122 if (enable) {
77b7d90b 123#if defined linux
d62a17ae 124 int upcalls = IGMPMSG_WRVIFWHOLE;
125 opt = MRT_PIM;
126
cdbfaec5 127 err = setsockopt(pim->mroute_socket, IPPROTO_IP, opt, &upcalls,
d62a17ae 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 }
77b7d90b 135#else
d62a17ae 136 zlog_warn(
137 "PIM-SM will not work properly on this platform, until the ability to receive the WRVIFWHOLE upcall");
77b7d90b 138#endif
d62a17ae 139 }
140
141 return 0;
12e41d03
DL
142}
143
08e1fe76 144static const char *igmpmsgtype2str[IGMPMSG_WRVIFWHOLE + 1] = {
d62a17ae 145 "<unknown_upcall?>", "NOCACHE", "WRONGVIF", "WHOLEPKT", "WRVIFWHOLE"};
146
147static int pim_mroute_msg_nocache(int fd, struct interface *ifp,
148 const struct igmpmsg *msg)
12e41d03 149{
d62a17ae 150 struct pim_interface *pim_ifp = ifp->info;
151 struct pim_upstream *up;
152 struct pim_rpf *rpg;
153 struct prefix_sg sg;
154
b575a12c 155 rpg = pim_ifp ? RP(pim_ifp->pim, msg->im_dst) : NULL;
d62a17ae 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 */
2ffc213b 161 if (!rpg || (pim_rpf_addr_is_inaddr_none(rpg))
d62a17ae 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 }
04b40f02 169
d62a17ae 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 }
065bee4b 181
d62a17ae 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 }
c29a5806 196
d62a17ae 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 }
8bfb8b67 205
d62a17ae 206 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up->flags);
19b807ca 207 pim_upstream_keep_alive_timer_start(up, pim_ifp->pim->keep_alive_time);
d62a17ae 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(
7cfc7bcf 215 pim_ifp->pim,
d62a17ae 216 up->rpf.source_nexthop.interface->ifindex);
217 up->channel_oil->oil.mfcc_parent = vif_index;
218 }
219 pim_register_join(up);
59471fb8 220
d62a17ae 221 return 0;
e355e30f 222}
12e41d03 223
d62a17ae 224static int pim_mroute_msg_wholepkt(int fd, struct interface *ifp,
225 const char *buf)
e355e30f 226{
d62a17ae 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
9b29ea95
DS
233 pim_ifp = ifp->info;
234
d62a17ae 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
9b29ea95 241 up = pim_upstream_find(pim_ifp->pim, &sg);
d62a17ae 242 if (!up) {
243 struct prefix_sg star = sg;
244 star.src.s_addr = INADDR_ANY;
245
9b29ea95 246 up = pim_upstream_find(pim_ifp->pim, &star);
d62a17ae 247
248 if (up && PIM_UPSTREAM_FLAG_TEST_SRC_IGMP(up->flags)) {
2002dcdb 249 up = pim_upstream_add(pim_ifp->pim, &sg, ifp,
d62a17ae 250 PIM_UPSTREAM_FLAG_MASK_SRC_LHR,
0885a9f1 251 __PRETTY_FUNCTION__, NULL);
d62a17ae 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(
19b807ca 261 up, pim_ifp->pim->keep_alive_time);
9b29ea95 262 pim_upstream_inherited_olist(pim_ifp->pim, up);
1eca8576
DS
263 pim_upstream_switch(pim_ifp->pim, up,
264 PIM_UPSTREAM_JOINED);
d62a17ae 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 }
59471fb8 278
d62a17ae 279 pim_ifp = up->rpf.source_nexthop.interface->info;
998af219 280
b575a12c 281 rpg = pim_ifp ? RP(pim_ifp->pim, sg.grp) : NULL;
c8ae3ce8 282
d62a17ae 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 }
84366c7e 291
d62a17ae 292 /*
293 * If we've received a register suppress
294 */
295 if (!up->t_rs_timer) {
6f439a70 296 if (pim_is_grp_ssm(pim_ifp->pim, sg.grp)) {
d62a17ae 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;
e355e30f 308}
12e41d03 309
d62a17ae 310static int pim_mroute_msg_wrongvif(int fd, struct interface *ifp,
311 const struct igmpmsg *msg)
e355e30f 312{
d62a17ae 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 }
12e41d03 340
d62a17ae 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 }
c29a5806 350
d62a17ae 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 }
12e41d03 371
d62a17ae 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 }
e355e30f 394
d62a17ae 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 }
e355e30f 412
d62a17ae 413 return 0;
e355e30f
DS
414}
415
d62a17ae 416static int pim_mroute_msg_wrvifwhole(int fd, struct interface *ifp,
417 const char *buf)
08e1fe76 418{
d62a17ae 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
fec883d9
DS
427 pim_ifp = ifp->info;
428
d62a17ae 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 }
b7ddd2ec 441
d62a17ae 442 star_g = sg;
443 star_g.src.s_addr = INADDR_ANY;
b7ddd2ec 444#if 0
0490c22d
DS
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 }
5cd11e3c 453#endif
08e1fe76 454
9b29ea95 455 up = pim_upstream_find(pim_ifp->pim, &sg);
d62a17ae 456 if (up) {
457 struct pim_upstream *parent;
458 struct pim_nexthop source;
fec883d9 459 struct pim_rpf *rpf = RP(pim_ifp->pim, sg.grp);
d62a17ae 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 */
9b29ea95 469 parent = pim_upstream_find(pim_ifp->pim, &star_g);
d62a17ae 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.
d9c9a9ee
DS
482 if (pim_nexthop_lookup(pim_ifp->pim, &source,
483 up->upstream_register, 0)
df766618 484 == 0) {
d62a17ae 485 pim_register_stop_send(source.interface, &sg,
486 pim_ifp->primary_address,
487 up->upstream_register);
df766618
DS
488 up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
489 }
d62a17ae 490 if (!up->channel_oil)
491 up->channel_oil = pim_channel_oil_add(
611925dc
DS
492 pim_ifp->pim, &sg,
493 pim_ifp->mroute_vif_index);
9b29ea95 494 pim_upstream_inherited_olist(pim_ifp->pim, up);
d62a17ae 495 if (!up->channel_oil->installed)
496 pim_mroute_add(up->channel_oil,
497 __PRETTY_FUNCTION__);
d62a17ae 498 } else {
d9c9a9ee
DS
499 if (I_am_RP(pim_ifp->pim, up->sg.grp)) {
500 if (pim_nexthop_lookup(pim_ifp->pim, &source,
d62a17ae 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(
19b807ca 510 up, pim_ifp->pim->keep_alive_time);
9b29ea95 511 pim_upstream_inherited_olist(pim_ifp->pim, up);
d62a17ae 512 pim_mroute_msg_wholepkt(fd, ifp, buf);
513 }
514 return 0;
515 }
8e38a2cf 516
d62a17ae 517 pim_ifp = ifp->info;
611925dc 518 oil = pim_channel_oil_add(pim_ifp->pim, &sg, pim_ifp->mroute_vif_index);
d62a17ae 519 if (!oil->installed)
520 pim_mroute_add(oil, __PRETTY_FUNCTION__);
521 if (pim_if_connected_to_source(ifp, sg.src)) {
2002dcdb
DS
522 up = pim_upstream_add(pim_ifp->pim, &sg, ifp,
523 PIM_UPSTREAM_FLAG_MASK_FHR,
0885a9f1 524 __PRETTY_FUNCTION__, NULL);
d62a17ae 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);
996c9314
LB
533 pim_upstream_keep_alive_timer_start(
534 up, pim_ifp->pim->keep_alive_time);
d62a17ae 535 up->channel_oil = oil;
536 up->channel_oil->cc.pktcnt++;
537 pim_register_join(up);
9b29ea95 538 pim_upstream_inherited_olist(pim_ifp->pim, up);
d62a17ae 539
540 // Send the packet to the RP
541 pim_mroute_msg_wholepkt(fd, ifp, buf);
542 }
08e1fe76 543
d62a17ae 544 return 0;
08e1fe76
DS
545}
546
405d6357 547static int pim_mroute_msg(struct pim_instance *pim, const char *buf,
90450a3d 548 int buf_size, ifindex_t ifindex)
e355e30f 549{
d62a17ae 550 struct interface *ifp;
551 struct pim_interface *pim_ifp;
552 const struct ip *ip_hdr;
553 const struct igmpmsg *msg;
554 char ip_src_str[INET_ADDRSTRLEN] = "";
555 char ip_dst_str[INET_ADDRSTRLEN] = "";
556 char src_str[INET_ADDRSTRLEN] = "<src?>";
557 char grp_str[INET_ADDRSTRLEN] = "<grp?>";
558 struct in_addr ifaddr;
559 struct igmp_sock *igmp;
560
561 ip_hdr = (const struct ip *)buf;
562
563 if (ip_hdr->ip_p == IPPROTO_IGMP) {
564
565 /* We have the IP packet but we do not know which interface this
566 * packet was
567 * received on. Find the interface that is on the same subnet as
568 * the source
569 * of the IP packet.
570 */
90450a3d 571 ifp = if_lookup_by_index(ifindex, pim->vrf_id);
d62a17ae 572
1ef8c24e 573 if (!ifp || !ifp->info)
d62a17ae 574 return 0;
90450a3d 575
d62a17ae 576 pim_ifp = ifp->info;
577 ifaddr = pim_find_primary_addr(ifp);
578 igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->igmp_socket_list,
579 ifaddr);
580
581 if (PIM_DEBUG_MROUTE) {
582 pim_inet4_dump("<src?>", ip_hdr->ip_src, ip_src_str,
583 sizeof(ip_src_str));
584 pim_inet4_dump("<dst?>", ip_hdr->ip_dst, ip_dst_str,
585 sizeof(ip_dst_str));
586
587 zlog_warn(
7c2bfc2a
DS
588 "%s(%s): igmp kernel upcall on %s(%p) for %s -> %s",
589 __PRETTY_FUNCTION__, pim->vrf->name, ifp->name,
590 igmp, ip_src_str, ip_dst_str);
d62a17ae 591 }
592 if (igmp)
593 pim_igmp_packet(igmp, (char *)buf, buf_size);
594
595 } else if (ip_hdr->ip_p) {
596 if (PIM_DEBUG_MROUTE_DETAIL) {
597 pim_inet4_dump("<src?>", ip_hdr->ip_src, src_str,
598 sizeof(src_str));
599 pim_inet4_dump("<grp?>", ip_hdr->ip_dst, grp_str,
600 sizeof(grp_str));
601 zlog_debug(
602 "%s: no kernel upcall proto=%d src: %s dst: %s msg_size=%d",
603 __PRETTY_FUNCTION__, ip_hdr->ip_p, src_str,
604 grp_str, buf_size);
605 }
606
607 } else {
608 msg = (const struct igmpmsg *)buf;
609
7cfc7bcf 610 ifp = pim_if_find_by_vif_index(pim, msg->im_vif);
d62a17ae 611
612 if (!ifp)
613 return 0;
614 if (PIM_DEBUG_MROUTE) {
615 pim_inet4_dump("<src?>", msg->im_src, src_str,
616 sizeof(src_str));
617 pim_inet4_dump("<grp?>", msg->im_dst, grp_str,
618 sizeof(grp_str));
619 zlog_warn(
620 "%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d size=%d",
621 __PRETTY_FUNCTION__,
622 igmpmsgtype2str[msg->im_msgtype],
405d6357
DS
623 msg->im_msgtype, ip_hdr->ip_p,
624 pim->mroute_socket, src_str, grp_str, ifp->name,
625 msg->im_vif, buf_size);
d62a17ae 626 }
627
628 switch (msg->im_msgtype) {
629 case IGMPMSG_WRONGVIF:
405d6357
DS
630 return pim_mroute_msg_wrongvif(pim->mroute_socket, ifp,
631 msg);
d62a17ae 632 break;
633 case IGMPMSG_NOCACHE:
405d6357
DS
634 return pim_mroute_msg_nocache(pim->mroute_socket, ifp,
635 msg);
d62a17ae 636 break;
637 case IGMPMSG_WHOLEPKT:
405d6357 638 return pim_mroute_msg_wholepkt(pim->mroute_socket, ifp,
d62a17ae 639 (const char *)msg);
640 break;
641 case IGMPMSG_WRVIFWHOLE:
405d6357
DS
642 return pim_mroute_msg_wrvifwhole(
643 pim->mroute_socket, ifp, (const char *)msg);
d62a17ae 644 break;
645 default:
646 break;
647 }
648 }
12e41d03 649
d62a17ae 650 return 0;
12e41d03
DL
651}
652
12e41d03
DL
653static int mroute_read(struct thread *t)
654{
8ea5d944 655 struct pim_instance *pim;
d62a17ae 656 static long long count;
657 char buf[10000];
658 int result = 0;
659 int cont = 1;
d62a17ae 660 int rd;
90450a3d 661 ifindex_t ifindex;
8ea5d944 662 pim = THREAD_ARG(t);
d62a17ae 663
664 while (cont) {
90450a3d
DS
665 rd = pim_socket_recvfromto(pim->mroute_socket, (uint8_t *)buf,
666 sizeof(buf), NULL, NULL, NULL, NULL,
667 &ifindex);
61e99c94 668 if (rd <= 0) {
d62a17ae 669 if (errno == EINTR)
670 continue;
671 if (errno == EWOULDBLOCK || errno == EAGAIN)
672 break;
673
674 if (PIM_DEBUG_MROUTE)
675 zlog_warn(
61e99c94 676 "%s: failure reading rd=%d: fd=%d: errno=%d: %s",
8ea5d944
DS
677 __PRETTY_FUNCTION__, rd,
678 pim->mroute_socket, errno,
d62a17ae 679 safe_strerror(errno));
680 goto done;
681 }
682
90450a3d 683 result = pim_mroute_msg(pim, buf, rd, ifindex);
d62a17ae 684
685 count++;
686 if (count % qpim_packet_process == 0)
687 cont = 0;
688 }
689/* Keep reading */
690done:
8ea5d944 691 mroute_read_on(pim);
12e41d03 692
d62a17ae 693 return result;
12e41d03
DL
694}
695
8ea5d944 696static void mroute_read_on(struct pim_instance *pim)
12e41d03 697{
f509d941 698 thread_add_read(master, mroute_read, pim, pim->mroute_socket,
8ea5d944 699 &pim->thread);
12e41d03
DL
700}
701
8ea5d944 702static void mroute_read_off(struct pim_instance *pim)
12e41d03 703{
8ea5d944 704 THREAD_OFF(pim->thread);
12e41d03
DL
705}
706
6beed987 707int pim_mroute_socket_enable(struct pim_instance *pim)
12e41d03 708{
d62a17ae 709 int fd;
12e41d03 710
d62a17ae 711 if (pimd_privs.change(ZPRIVS_RAISE))
712 zlog_err("pim_mroute_socket_enable: could not raise privs, %s",
713 safe_strerror(errno));
12e41d03 714
d62a17ae 715 fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
12e41d03 716
e691f179
DS
717 if (fd < 0) {
718 zlog_warn("Could not create mroute socket: errno=%d: %s", errno,
719 safe_strerror(errno));
720 return -2;
721 }
722
466e4e5b 723#ifdef SO_BINDTODEVICE
996c9314
LB
724 if (pim->vrf->vrf_id != VRF_DEFAULT
725 && setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, pim->vrf->name,
726 strlen(pim->vrf->name))) {
e691f179
DS
727 zlog_warn("Could not setsockopt SO_BINDTODEVICE: %s",
728 safe_strerror(errno));
729 close(fd);
730 return -3;
731 }
466e4e5b 732#endif
90450a3d 733
d62a17ae 734 if (pimd_privs.change(ZPRIVS_LOWER))
735 zlog_err("pim_mroute_socket_enable: could not lower privs, %s",
736 safe_strerror(errno));
12e41d03 737
cdbfaec5
DS
738 pim->mroute_socket = fd;
739 if (pim_mroute_set(pim, 1)) {
d62a17ae 740 zlog_warn(
741 "Could not enable mroute on socket fd=%d: errno=%d: %s",
742 fd, errno, safe_strerror(errno));
743 close(fd);
cdbfaec5 744 pim->mroute_socket = -1;
d62a17ae 745 return -3;
746 }
12e41d03 747
6beed987 748 pim->mroute_socket_creation = pim_time_monotonic_sec();
b45cefcb 749
8ea5d944 750 mroute_read_on(pim);
12e41d03 751
d62a17ae 752 return 0;
12e41d03
DL
753}
754
6beed987 755int pim_mroute_socket_disable(struct pim_instance *pim)
12e41d03 756{
cdbfaec5 757 if (pim_mroute_set(pim, 0)) {
d62a17ae 758 zlog_warn(
759 "Could not disable mroute on socket fd=%d: errno=%d: %s",
405d6357 760 pim->mroute_socket, errno, safe_strerror(errno));
d62a17ae 761 return -2;
762 }
763
6beed987 764 if (close(pim->mroute_socket)) {
d62a17ae 765 zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
405d6357 766 pim->mroute_socket, errno, safe_strerror(errno));
d62a17ae 767 return -3;
768 }
769
8ea5d944 770 mroute_read_off(pim);
6beed987 771 pim->mroute_socket = -1;
d62a17ae 772
773 return 0;
12e41d03
DL
774}
775
776/*
777 For each network interface (e.g., physical or a virtual tunnel) that
778 would be used for multicast forwarding, a corresponding multicast
779 interface must be added to the kernel.
780 */
d62a17ae 781int pim_mroute_add_vif(struct interface *ifp, struct in_addr ifaddr,
782 unsigned char flags)
12e41d03 783{
d62a17ae 784 struct pim_interface *pim_ifp = ifp->info;
785 struct vifctl vc;
786 int err;
12e41d03 787
08f4f901 788 if (PIM_DEBUG_MROUTE)
d62fd596 789 zlog_debug("%s: Add Vif %d (%s[%s])", __PRETTY_FUNCTION__,
996c9314
LB
790 pim_ifp->mroute_vif_index, ifp->name,
791 pim_ifp->pim->vrf->name);
08f4f901 792
d62a17ae 793 memset(&vc, 0, sizeof(vc));
794 vc.vifc_vifi = pim_ifp->mroute_vif_index;
b3f2bf7c 795#ifdef VIFF_USE_IFINDEX
d62a17ae 796 vc.vifc_lcl_ifindex = ifp->ifindex;
b3f2bf7c 797#else
d62a17ae 798 if (ifaddr.s_addr == INADDR_ANY) {
799 zlog_warn(
800 "%s: unnumbered interfaces are not supported on this platform",
801 __PRETTY_FUNCTION__);
802 return -1;
803 }
804 memcpy(&vc.vifc_lcl_addr, &ifaddr, sizeof(vc.vifc_lcl_addr));
b3f2bf7c 805#endif
d62a17ae 806 vc.vifc_flags = flags;
807 vc.vifc_threshold = PIM_MROUTE_MIN_TTL;
808 vc.vifc_rate_limit = 0;
809
810#ifdef PIM_DVMRP_TUNNEL
811 if (vc.vifc_flags & VIFF_TUNNEL) {
812 memcpy(&vc.vifc_rmt_addr, &vif_remote_addr,
813 sizeof(vc.vifc_rmt_addr));
814 }
12e41d03
DL
815#endif
816
ea3d967b 817 err = setsockopt(pim_ifp->pim->mroute_socket, IPPROTO_IP, MRT_ADD_VIF,
d62a17ae 818 (void *)&vc, sizeof(vc));
819 if (err) {
820 char ifaddr_str[INET_ADDRSTRLEN];
12e41d03 821
d62a17ae 822 pim_inet4_dump("<ifaddr?>", ifaddr, ifaddr_str,
823 sizeof(ifaddr_str));
12e41d03 824
d62a17ae 825 zlog_warn(
08f4f901 826 "%s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s,flag=%d): errno=%d: %s",
996c9314
LB
827 __PRETTY_FUNCTION__, pim_ifp->pim->mroute_socket,
828 ifp->ifindex, ifaddr_str, flags, errno,
829 safe_strerror(errno));
d62a17ae 830 return -2;
831 }
12e41d03 832
d62a17ae 833 return 0;
12e41d03
DL
834}
835
ea3d967b 836int pim_mroute_del_vif(struct interface *ifp)
12e41d03 837{
ea3d967b 838 struct pim_interface *pim_ifp = ifp->info;
d62a17ae 839 struct vifctl vc;
840 int err;
841
ea3d967b 842 if (PIM_DEBUG_MROUTE)
996c9314
LB
843 zlog_debug("%s: Del Vif %d (%s[%s])", __PRETTY_FUNCTION__,
844 pim_ifp->mroute_vif_index, ifp->name,
845 pim_ifp->pim->vrf->name);
12e41d03 846
d62a17ae 847 memset(&vc, 0, sizeof(vc));
ea3d967b 848 vc.vifc_vifi = pim_ifp->mroute_vif_index;
d62a17ae 849
ea3d967b 850 err = setsockopt(pim_ifp->pim->mroute_socket, IPPROTO_IP, MRT_DEL_VIF,
d62a17ae 851 (void *)&vc, sizeof(vc));
852 if (err) {
853 zlog_warn(
854 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
ea3d967b
DS
855 __FILE__, __PRETTY_FUNCTION__,
856 pim_ifp->pim->mroute_socket, pim_ifp->mroute_vif_index,
857 errno, safe_strerror(errno));
d62a17ae 858 return -2;
859 }
12e41d03 860
d62a17ae 861 return 0;
12e41d03
DL
862}
863
6a78764e 864int pim_mroute_add(struct channel_oil *c_oil, const char *name)
12e41d03 865{
9a0f71c9 866 struct pim_instance *pim = c_oil->pim;
d62a17ae 867 int err;
868 int orig = 0;
869 int orig_iif_vif = 0;
870
856e863f
DS
871 pim->mroute_add_last = pim_time_monotonic_sec();
872 ++pim->mroute_add_events;
d62a17ae 873
874 /* Do not install route if incoming interface is undefined. */
875 if (c_oil->oil.mfcc_parent >= MAXVIFS) {
876 if (PIM_DEBUG_MROUTE) {
877 char buf[1000];
878 zlog_debug(
879 "%s(%s) %s Attempting to add vifi that is invalid to mroute table",
880 __PRETTY_FUNCTION__, name,
881 pim_channel_oil_dump(c_oil, buf, sizeof(buf)));
882 }
883 return -2;
884 }
d3aded99 885
d62a17ae 886 /* The linux kernel *expects* the incoming
887 * vif to be part of the outgoing list
888 * in the case of a (*,G).
889 */
890 if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY) {
891 orig = c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent];
892 c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = 1;
893 }
d3aded99 894
d62a17ae 895 /*
896 * If we have an unresolved cache entry for the S,G
897 * it is owned by the pimreg for the incoming IIF
898 * So set pimreg as the IIF temporarily to cause
899 * the packets to be forwarded. Then set it
900 * to the correct IIF afterwords.
901 */
902 if (!c_oil->installed && c_oil->oil.mfcc_origin.s_addr != INADDR_ANY
903 && c_oil->oil.mfcc_parent != 0) {
904 orig_iif_vif = c_oil->oil.mfcc_parent;
905 c_oil->oil.mfcc_parent = 0;
906 }
856e863f 907 err = setsockopt(pim->mroute_socket, IPPROTO_IP, MRT_ADD_MFC,
d62a17ae 908 &c_oil->oil, sizeof(c_oil->oil));
909
910 if (!err && !c_oil->installed
911 && c_oil->oil.mfcc_origin.s_addr != INADDR_ANY
912 && orig_iif_vif != 0) {
913 c_oil->oil.mfcc_parent = orig_iif_vif;
856e863f 914 err = setsockopt(pim->mroute_socket, IPPROTO_IP, MRT_ADD_MFC,
d62a17ae 915 &c_oil->oil, sizeof(c_oil->oil));
916 }
0365f56b 917
d62a17ae 918 if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY)
919 c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = orig;
d3aded99 920
d62a17ae 921 if (err) {
922 zlog_warn(
923 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
856e863f 924 __FILE__, __PRETTY_FUNCTION__, pim->mroute_socket,
d62a17ae 925 errno, safe_strerror(errno));
926 return -2;
927 }
12e41d03 928
d62a17ae 929 if (PIM_DEBUG_MROUTE) {
930 char buf[1000];
996c9314
LB
931 zlog_debug("%s(%s), vrf %s Added Route: %s",
932 __PRETTY_FUNCTION__, name, pim->vrf->name,
d62a17ae 933 pim_channel_oil_dump(c_oil, buf, sizeof(buf)));
934 }
6a78764e 935
d62a17ae 936 c_oil->installed = 1;
937 return 0;
12e41d03
DL
938}
939
d62a17ae 940int pim_mroute_del(struct channel_oil *c_oil, const char *name)
12e41d03 941{
9a0f71c9 942 struct pim_instance *pim = c_oil->pim;
d62a17ae 943 int err;
944
856e863f
DS
945 pim->mroute_del_last = pim_time_monotonic_sec();
946 ++pim->mroute_del_events;
d62a17ae 947
948 if (!c_oil->installed) {
949 if (PIM_DEBUG_MROUTE) {
950 char buf[1000];
951 zlog_debug(
952 "%s %s: vifi %d for route is %s not installed, do not need to send del req. ",
953 __FILE__, __PRETTY_FUNCTION__,
954 c_oil->oil.mfcc_parent,
955 pim_channel_oil_dump(c_oil, buf, sizeof(buf)));
956 }
957 return -2;
958 }
12e41d03 959
856e863f 960 err = setsockopt(pim->mroute_socket, IPPROTO_IP, MRT_DEL_MFC,
d62a17ae 961 &c_oil->oil, sizeof(c_oil->oil));
962 if (err) {
963 if (PIM_DEBUG_MROUTE)
964 zlog_warn(
965 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
966 __FILE__, __PRETTY_FUNCTION__,
856e863f 967 pim->mroute_socket, errno,
d62a17ae 968 safe_strerror(errno));
969 return -2;
970 }
429a291b 971
d62a17ae 972 if (PIM_DEBUG_MROUTE) {
973 char buf[1000];
996c9314
LB
974 zlog_debug("%s(%s), vrf %s Deleted Route: %s",
975 __PRETTY_FUNCTION__, name, pim->vrf->name,
d62fd596 976 pim_channel_oil_dump(c_oil, buf, sizeof(buf)));
d62a17ae 977 }
781a1745 978
d62a17ae 979 // Reset kernel installed flag
980 c_oil->installed = 0;
58302dc7 981
d62a17ae 982 return 0;
12e41d03 983}
3667e8a0 984
d62a17ae 985void pim_mroute_update_counters(struct channel_oil *c_oil)
3667e8a0 986{
9a0f71c9 987 struct pim_instance *pim = c_oil->pim;
d62a17ae 988 struct sioc_sg_req sgreq;
989
990 c_oil->cc.oldpktcnt = c_oil->cc.pktcnt;
991 c_oil->cc.oldbytecnt = c_oil->cc.bytecnt;
992 c_oil->cc.oldwrong_if = c_oil->cc.wrong_if;
993
994 if (!c_oil->installed) {
19b807ca 995 c_oil->cc.lastused = 100 * pim->keep_alive_time;
d62a17ae 996 if (PIM_DEBUG_MROUTE) {
997 struct prefix_sg sg;
998
999 sg.src = c_oil->oil.mfcc_origin;
1000 sg.grp = c_oil->oil.mfcc_mcastgrp;
1001 if (PIM_DEBUG_MROUTE)
1002 zlog_debug(
1003 "Channel(%s) is not installed no need to collect data from kernel",
1004 pim_str_sg_dump(&sg));
1005 }
1006 return;
c7b1183f 1007 }
c7b1183f 1008
d62a17ae 1009 memset(&sgreq, 0, sizeof(sgreq));
1010 sgreq.src = c_oil->oil.mfcc_origin;
1011 sgreq.grp = c_oil->oil.mfcc_mcastgrp;
1012
1013 pim_zlookup_sg_statistics(c_oil);
856e863f 1014 if (ioctl(pim->mroute_socket, SIOCGETSGCNT, &sgreq)) {
d62a17ae 1015 if (PIM_DEBUG_MROUTE) {
1016 struct prefix_sg sg;
1017
1018 sg.src = c_oil->oil.mfcc_origin;
1019 sg.grp = c_oil->oil.mfcc_mcastgrp;
1020
1021 zlog_warn(
1022 "ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=(%s): errno=%d: %s",
1023 (unsigned long)SIOCGETSGCNT,
1024 pim_str_sg_dump(&sg), errno,
1025 safe_strerror(errno));
1026 }
1027 return;
c7b1183f 1028 }
3667e8a0 1029
d62a17ae 1030 c_oil->cc.pktcnt = sgreq.pktcnt;
1031 c_oil->cc.bytecnt = sgreq.bytecnt;
1032 c_oil->cc.wrong_if = sgreq.wrong_if;
3667e8a0 1033
d62a17ae 1034 return;
3667e8a0 1035}