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