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