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