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