]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_mroute.c
pimd: Refactor pim_joinprune_send
[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);
56638739 192 up->join_state = PIM_UPSTREAM_JOINED;
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;
5cd11e3c 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 }
5cd11e3c 370#if 0
0490c22d
DS
371 star_g = sg;
372 star_g.src.s_addr = INADDR_ANY;
373 ch = pim_ifchannel_find(ifp, &star_g);
374 if (ch)
375 {
376 if (PIM_DEBUG_MROUTE)
377 zlog_debug ("WRVIFWHOLE (*,G)=%s found ifchannel on interface %s",
378 pim_str_sg_dump (&star_g), ifp->name);
379 return -1;
380 }
5cd11e3c 381#endif
08e1fe76 382
8e38a2cf
DS
383 up = pim_upstream_find (&sg);
384 if (up)
385 {
9244dd13
DS
386 struct pim_nexthop source;
387 struct pim_rpf *rpf = RP (sg.grp);
388 if (!rpf || !rpf->source_nexthop.interface)
389 return 0;
390
391 pim_ifp = rpf->source_nexthop.interface->info;
392
393 memset (&source, 0, sizeof (source));
7fe1f662
DS
394 /*
395 * If we are the fhr that means we are getting a callback during
396 * the pimreg period, so I believe we can ignore this packet
397 */
0bf27c5c 398 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags))
7fe1f662 399 {
7fe1f662 400 //No if channel, but upstream we are at the RP.
88d6652e
DS
401 if (pim_nexthop_lookup (&source, up->upstream_register, 0) == 0)
402 pim_register_stop_send(source.interface, &sg, pim_ifp->primary_address, up->upstream_register);
51e82833
DS
403 if (!up->channel_oil)
404 up->channel_oil = pim_channel_oil_add (&sg, pim_ifp->mroute_vif_index);
5b668dd7 405 pim_upstream_inherited_olist (up);
51e82833 406 if (!up->channel_oil->installed)
6a78764e 407 pim_mroute_add (up->channel_oil, __PRETTY_FUNCTION__);
3a66b17b 408 pim_upstream_set_sptbit (up, ifp);
7fe1f662 409 }
5b668dd7
DS
410 else
411 {
9244dd13
DS
412 if (I_am_RP (up->sg.grp))
413 {
414 if (pim_nexthop_lookup (&source, up->upstream_register, 0) == 0)
415 pim_register_stop_send(source.interface, &sg, pim_ifp->primary_address, up->upstream_register);
416 up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
417 }
5b668dd7
DS
418 pim_upstream_keep_alive_timer_start (up, qpim_keep_alive_time);
419 pim_upstream_inherited_olist (up);
420 pim_mroute_msg_wholepkt (fd, ifp, buf);
421 }
422 return 0;
8e38a2cf
DS
423 }
424
5f6f65b1
DS
425 pim_ifp = ifp->info;
426 oil = pim_channel_oil_add (&sg, pim_ifp->mroute_vif_index);
427 if (!oil->installed)
6a78764e 428 pim_mroute_add (oil, __PRETTY_FUNCTION__);
5f6f65b1 429 if (pim_if_connected_to_source (ifp, sg.src))
08e1fe76 430 {
e5905a3b 431 up = pim_upstream_add (&sg, ifp, PIM_UPSTREAM_FLAG_MASK_FHR, __PRETTY_FUNCTION__);
5f6f65b1
DS
432 if (!up)
433 {
434 if (PIM_DEBUG_MROUTE)
435 zlog_debug ("%s: WRONGVIF%s unable to create upstream on interface",
436 pim_str_sg_dump (&sg), ifp->name);
437 return -2;
438 }
a9b59879 439 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up->flags);
5f6f65b1
DS
440 pim_upstream_keep_alive_timer_start (up, qpim_keep_alive_time);
441 up->channel_oil = oil;
442 up->channel_oil->cc.pktcnt++;
443 pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM);
444 up->join_state = PIM_UPSTREAM_JOINED;
445 pim_upstream_inherited_olist (up);
08e1fe76 446
5f6f65b1
DS
447 // Send the packet to the RP
448 pim_mroute_msg_wholepkt (fd, ifp, buf);
449 }
08e1fe76
DS
450
451 return 0;
452}
453
e355e30f
DS
454int pim_mroute_msg(int fd, const char *buf, int buf_size)
455{
456 struct interface *ifp;
b05b72e8 457 struct pim_interface *pim_ifp;
e355e30f
DS
458 const struct ip *ip_hdr;
459 const struct igmpmsg *msg;
eaa54bdb
DW
460 char ip_src_str[INET_ADDRSTRLEN] = "";
461 char ip_dst_str[INET_ADDRSTRLEN] = "";
462 char src_str[INET_ADDRSTRLEN] = "<src?>";
463 char grp_str[INET_ADDRSTRLEN] = "<grp?>";
b05b72e8
DW
464 struct in_addr ifaddr;
465 struct igmp_sock *igmp;
12e41d03 466
e355e30f
DS
467 ip_hdr = (const struct ip *) buf;
468
b05b72e8
DW
469 if (ip_hdr->ip_p == IPPROTO_IGMP) {
470
471 /* We have the IP packet but we do not know which interface this packet was
472 * received on. Find the interface that is on the same subnet as the source
473 * of the IP packet.
474 */
9f0edbc9 475 ifp = pim_if_lookup_address_vrf (ip_hdr->ip_src, VRF_DEFAULT);
b05b72e8
DW
476
477 if (!ifp) {
478 if (PIM_DEBUG_MROUTE_DETAIL) {
479 pim_inet4_dump("<src?>", ip_hdr->ip_src, ip_src_str, sizeof(ip_src_str));
480 pim_inet4_dump("<dst?>", ip_hdr->ip_dst, ip_dst_str, sizeof(ip_dst_str));
481
9f0edbc9 482 zlog_warn("%s: igmp kernel upcall could not find usable interface for %s -> %s",
b05b72e8
DW
483 __PRETTY_FUNCTION__,
484 ip_src_str,
485 ip_dst_str);
486 }
487 return 0;
488 }
b05b72e8
DW
489 pim_ifp = ifp->info;
490 ifaddr = pim_find_primary_addr(ifp);
491 igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->igmp_socket_list, ifaddr);
492
02e5bd72 493 if (PIM_DEBUG_MROUTE) {
b05b72e8
DW
494 pim_inet4_dump("<src?>", ip_hdr->ip_src, ip_src_str, sizeof(ip_src_str));
495 pim_inet4_dump("<dst?>", ip_hdr->ip_dst, ip_dst_str, sizeof(ip_dst_str));
496
02e5bd72
DS
497 zlog_warn("%s: igmp kernel upcall on %s(%p) for %s -> %s",
498 __PRETTY_FUNCTION__, ifp->name, igmp, ip_src_str, ip_dst_str);
b05b72e8 499 }
02e5bd72
DS
500 if (igmp)
501 pim_igmp_packet(igmp, (char *)buf, buf_size);
b05b72e8
DW
502
503 } else if (ip_hdr->ip_p) {
6c7197b1 504 if (PIM_DEBUG_MROUTE_DETAIL) {
e5d33c83
DS
505 pim_inet4_dump("<src?>", ip_hdr->ip_src, src_str, sizeof(src_str));
506 pim_inet4_dump("<grp?>", ip_hdr->ip_dst, grp_str, sizeof(grp_str));
b05b72e8
DW
507 zlog_debug("%s: no kernel upcall proto=%d src: %s dst: %s msg_size=%d",
508 __PRETTY_FUNCTION__, ip_hdr->ip_p, src_str, grp_str, buf_size);
e355e30f 509 }
e355e30f 510
b05b72e8
DW
511 } else {
512 msg = (const struct igmpmsg *) buf;
e355e30f 513
b05b72e8 514 ifp = pim_if_find_by_vif_index(msg->im_vif);
e355e30f 515
38f380f5
DS
516 if (!ifp)
517 return 0;
b05b72e8
DW
518 if (PIM_DEBUG_MROUTE) {
519 pim_inet4_dump("<src?>", msg->im_src, src_str, sizeof(src_str));
520 pim_inet4_dump("<grp?>", msg->im_dst, grp_str, sizeof(grp_str));
521 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",
522 __PRETTY_FUNCTION__,
523 igmpmsgtype2str[msg->im_msgtype],
524 msg->im_msgtype,
525 ip_hdr->ip_p,
526 fd,
527 src_str,
528 grp_str,
529 ifp->name,
530 msg->im_vif, buf_size);
531 }
e355e30f 532
b05b72e8
DW
533 switch (msg->im_msgtype) {
534 case IGMPMSG_WRONGVIF:
535 return pim_mroute_msg_wrongvif(fd, ifp, msg);
536 break;
537 case IGMPMSG_NOCACHE:
538 return pim_mroute_msg_nocache(fd, ifp, msg);
539 break;
540 case IGMPMSG_WHOLEPKT:
541 return pim_mroute_msg_wholepkt(fd, ifp, (const char *)msg);
542 break;
543 case IGMPMSG_WRVIFWHOLE:
544 return pim_mroute_msg_wrvifwhole (fd, ifp, (const char *)msg);
545 break;
546 default:
547 break;
548 }
e355e30f 549 }
12e41d03
DL
550
551 return 0;
552}
553
12e41d03
DL
554static int mroute_read(struct thread *t)
555{
6806e04d
DS
556 static long long count;
557 char buf[10000];
558 int result = 0;
559 int cont = 1;
12e41d03 560 int fd;
6806e04d 561 int rd;
12e41d03
DL
562
563 fd = THREAD_FD(t);
12e41d03 564
6806e04d
DS
565 while (cont)
566 {
567 rd = read(fd, buf, sizeof(buf));
568 if (rd < 0) {
569 if (errno == EINTR)
570 continue;
571 if (errno == EWOULDBLOCK || errno == EAGAIN)
572 {
573 cont = 0;
574 break;
575 }
576 if (PIM_DEBUG_MROUTE)
577 zlog_warn("%s: failure reading fd=%d: errno=%d: %s",
578 __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
579 goto done;
580 }
12e41d03 581
6806e04d
DS
582 result = pim_mroute_msg(fd, buf, rd);
583
584 count++;
8e4c9ef3 585 if (count % qpim_packet_process == 0)
6806e04d
DS
586 cont = 0;
587 }
12e41d03 588 /* Keep reading */
6806e04d 589 done:
7a90f85c 590 qpim_mroute_socket_reader = NULL;
12e41d03
DL
591 mroute_read_on();
592
593 return result;
594}
595
596static void mroute_read_on()
597{
598 zassert(!qpim_mroute_socket_reader);
599 zassert(PIM_MROUTE_IS_ENABLED);
600
601 THREAD_READ_ON(master, qpim_mroute_socket_reader,
602 mroute_read, 0, qpim_mroute_socket_fd);
603}
604
605static void mroute_read_off()
606{
607 THREAD_OFF(qpim_mroute_socket_reader);
608}
609
610int pim_mroute_socket_enable()
611{
612 int fd;
613
614 if (PIM_MROUTE_IS_ENABLED)
615 return -1;
616
617 if ( pimd_privs.change (ZPRIVS_RAISE) )
618 zlog_err ("pim_mroute_socket_enable: could not raise privs, %s",
619 safe_strerror (errno) );
620
621 fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
622
623 if ( pimd_privs.change (ZPRIVS_LOWER) )
624 zlog_err ("pim_mroute_socket_enable: could not lower privs, %s",
625 safe_strerror (errno) );
626
627 if (fd < 0) {
628 zlog_warn("Could not create mroute socket: errno=%d: %s",
629 errno, safe_strerror(errno));
630 return -2;
631 }
632
633 if (pim_mroute_set(fd, 1)) {
634 zlog_warn("Could not enable mroute on socket fd=%d: errno=%d: %s",
635 fd, errno, safe_strerror(errno));
636 close(fd);
637 return -3;
638 }
639
640 qpim_mroute_socket_fd = fd;
b45cefcb 641
12e41d03
DL
642 qpim_mroute_socket_creation = pim_time_monotonic_sec();
643 mroute_read_on();
644
12e41d03
DL
645 return 0;
646}
647
648int pim_mroute_socket_disable()
649{
650 if (PIM_MROUTE_IS_DISABLED)
651 return -1;
652
653 if (pim_mroute_set(qpim_mroute_socket_fd, 0)) {
654 zlog_warn("Could not disable mroute on socket fd=%d: errno=%d: %s",
655 qpim_mroute_socket_fd, errno, safe_strerror(errno));
656 return -2;
657 }
658
659 if (close(qpim_mroute_socket_fd)) {
660 zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
661 qpim_mroute_socket_fd, errno, safe_strerror(errno));
662 return -3;
663 }
664
665 mroute_read_off();
666 qpim_mroute_socket_fd = -1;
667
12e41d03
DL
668 return 0;
669}
670
671/*
672 For each network interface (e.g., physical or a virtual tunnel) that
673 would be used for multicast forwarding, a corresponding multicast
674 interface must be added to the kernel.
675 */
744d91b3 676int pim_mroute_add_vif(struct interface *ifp, struct in_addr ifaddr, unsigned char flags)
12e41d03 677{
744d91b3 678 struct pim_interface *pim_ifp = ifp->info;
12e41d03
DL
679 struct vifctl vc;
680 int err;
681
682 if (PIM_MROUTE_IS_DISABLED) {
683 zlog_warn("%s: global multicast is disabled",
684 __PRETTY_FUNCTION__);
685 return -1;
686 }
687
688 memset(&vc, 0, sizeof(vc));
744d91b3 689 vc.vifc_vifi = pim_ifp->mroute_vif_index;
b3f2bf7c 690#ifdef VIFF_USE_IFINDEX
744d91b3 691 vc.vifc_lcl_ifindex = ifp->ifindex;
b3f2bf7c
RW
692#else
693 if (ifaddr.s_addr == INADDR_ANY) {
694 zlog_warn("%s: unnumbered interfaces are not supported on this platform",
695 __PRETTY_FUNCTION__);
696 return -1;
697 }
698 memcpy(&vc.vifc_lcl_addr, &ifaddr, sizeof(vc.vifc_lcl_addr));
699#endif
b45cefcb 700 vc.vifc_flags = flags;
12e41d03
DL
701 vc.vifc_threshold = PIM_MROUTE_MIN_TTL;
702 vc.vifc_rate_limit = 0;
12e41d03
DL
703
704#ifdef PIM_DVMRP_TUNNEL
705 if (vc.vifc_flags & VIFF_TUNNEL) {
706 memcpy(&vc.vifc_rmt_addr, &vif_remote_addr, sizeof(vc.vifc_rmt_addr));
707 }
708#endif
709
b45cefcb 710 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_VIF, (void*) &vc, sizeof(vc));
12e41d03 711 if (err) {
eaa54bdb 712 char ifaddr_str[INET_ADDRSTRLEN];
12e41d03
DL
713
714 pim_inet4_dump("<ifaddr?>", ifaddr, ifaddr_str, sizeof(ifaddr_str));
715
59471fb8 716 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s,flag=%d): errno=%d: %s",
12e41d03 717 __FILE__, __PRETTY_FUNCTION__,
744d91b3 718 qpim_mroute_socket_fd, ifp->ifindex, ifaddr_str, flags,
3d7765d7 719 errno, safe_strerror(errno));
12e41d03
DL
720 return -2;
721 }
722
723 return 0;
724}
725
726int pim_mroute_del_vif(int vif_index)
727{
728 struct vifctl vc;
729 int err;
730
731 if (PIM_MROUTE_IS_DISABLED) {
732 zlog_warn("%s: global multicast is disabled",
733 __PRETTY_FUNCTION__);
734 return -1;
735 }
736
e81d9709
CS
737 if (PIM_DEBUG_MROUTE)
738 {
739 struct interface *ifp = pim_if_find_by_vif_index (vif_index);
740 zlog_debug ("%s %s: Del Vif %d (%s) ", __FILE__,
741 __PRETTY_FUNCTION__, vif_index, ifp ? ifp->name : "NULL");
742 }
743
12e41d03
DL
744 memset(&vc, 0, sizeof(vc));
745 vc.vifc_vifi = vif_index;
746
747 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_VIF, (void*) &vc, sizeof(vc));
748 if (err) {
12e41d03
DL
749 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
750 __FILE__, __PRETTY_FUNCTION__,
751 qpim_mroute_socket_fd, vif_index,
3d7765d7 752 errno, safe_strerror(errno));
12e41d03
DL
753 return -2;
754 }
755
756 return 0;
757}
758
6a78764e 759int pim_mroute_add(struct channel_oil *c_oil, const char *name)
12e41d03
DL
760{
761 int err;
2ca35b3d 762 int orig = 0;
0365f56b 763 int orig_iif_vif = 0;
12e41d03
DL
764
765 qpim_mroute_add_last = pim_time_monotonic_sec();
766 ++qpim_mroute_add_events;
767
768 if (PIM_MROUTE_IS_DISABLED) {
769 zlog_warn("%s: global multicast is disabled",
770 __PRETTY_FUNCTION__);
771 return -1;
772 }
429a291b
CS
773 /* Do not install route if incoming interface is undefined. */
774 if (c_oil->oil.mfcc_parent == MAXVIFS)
775 {
776 if (PIM_DEBUG_MROUTE)
777 {
778 char buf[1000];
779 zlog_debug("%s(%s) %s Attempting to add vifi that is invalid to mroute table",
780 __PRETTY_FUNCTION__, name, pim_channel_oil_dump (c_oil, buf, sizeof(buf)));
781 }
782 return -2;
783 }
12e41d03 784
d3aded99
DS
785 /* The linux kernel *expects* the incoming
786 * vif to be part of the outgoing list
787 * in the case of a (*,G).
788 */
c171d6d8 789 if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY)
d3aded99 790 {
c171d6d8
DS
791 orig = c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent];
792 c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = 1;
d3aded99
DS
793 }
794
0365f56b
DS
795 /*
796 * If we have an unresolved cache entry for the S,G
797 * it is owned by the pimreg for the incoming IIF
798 * So set pimreg as the IIF temporarily to cause
799 * the packets to be forwarded. Then set it
800 * to the correct IIF afterwords.
801 */
802 if (!c_oil->installed && c_oil->oil.mfcc_origin.s_addr != INADDR_ANY &&
803 c_oil->oil.mfcc_parent != 0)
804 {
805 orig_iif_vif = c_oil->oil.mfcc_parent;
806 c_oil->oil.mfcc_parent = 0;
807 }
12e41d03 808 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_MFC,
c171d6d8 809 &c_oil->oil, sizeof(c_oil->oil));
d3aded99 810
0365f56b
DS
811 if (!err && !c_oil->installed && c_oil->oil.mfcc_origin.s_addr != INADDR_ANY &&
812 orig_iif_vif != 0)
813 {
814 c_oil->oil.mfcc_parent = orig_iif_vif;
815 err = setsockopt (qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_MFC,
816 &c_oil->oil, sizeof (c_oil->oil));
817 }
818
c171d6d8
DS
819 if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY)
820 c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = orig;
d3aded99 821
12e41d03 822 if (err) {
12e41d03
DL
823 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
824 __FILE__, __PRETTY_FUNCTION__,
825 qpim_mroute_socket_fd,
3d7765d7 826 errno, safe_strerror(errno));
12e41d03
DL
827 return -2;
828 }
829
6a78764e
DS
830 if (PIM_DEBUG_MROUTE)
831 {
781a1745
DS
832 char buf[1000];
833 zlog_debug("%s(%s), Added Route: %s",
834 __PRETTY_FUNCTION__, name,
835 pim_channel_oil_dump (c_oil, buf, sizeof(buf)));
6a78764e
DS
836 }
837
58302dc7 838 c_oil->installed = 1;
12e41d03
DL
839 return 0;
840}
841
6a78764e 842int pim_mroute_del (struct channel_oil *c_oil, const char *name)
12e41d03
DL
843{
844 int err;
845
846 qpim_mroute_del_last = pim_time_monotonic_sec();
847 ++qpim_mroute_del_events;
848
849 if (PIM_MROUTE_IS_DISABLED) {
850 zlog_warn("%s: global multicast is disabled",
851 __PRETTY_FUNCTION__);
852 return -1;
853 }
854
429a291b
CS
855 if (!c_oil->installed)
856 {
857 if (PIM_DEBUG_MROUTE)
858 {
859 char buf[1000];
860 zlog_debug("%s %s: vifi %d for route is %s not installed, do not need to send del req. ",
861 __FILE__, __PRETTY_FUNCTION__, c_oil->oil.mfcc_parent,
862 pim_channel_oil_dump (c_oil, buf, sizeof(buf)));
863 }
864 return -2;
865 }
866
c171d6d8 867 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_MFC, &c_oil->oil, sizeof(c_oil->oil));
12e41d03 868 if (err) {
05ca4827
DS
869 if (PIM_DEBUG_MROUTE)
870 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
871 __FILE__, __PRETTY_FUNCTION__,
872 qpim_mroute_socket_fd,
873 errno, safe_strerror(errno));
12e41d03
DL
874 return -2;
875 }
876
6a78764e
DS
877 if (PIM_DEBUG_MROUTE)
878 {
781a1745
DS
879 char buf[1000];
880 zlog_debug("%s(%s), Deleted Route: %s",
881 __PRETTY_FUNCTION__, name,
882 pim_channel_oil_dump (c_oil, buf, sizeof(buf)));
6a78764e 883 }
781a1745 884
429a291b 885 /*reset incoming vifi and kernel installed flags*/
58302dc7 886 c_oil->installed = 0;
429a291b 887 c_oil->oil.mfcc_parent = MAXVIFS;
58302dc7 888
12e41d03
DL
889 return 0;
890}
3667e8a0
DS
891
892void
893pim_mroute_update_counters (struct channel_oil *c_oil)
894{
895 struct sioc_sg_req sgreq;
896
3667e8a0
DS
897 c_oil->cc.oldpktcnt = c_oil->cc.pktcnt;
898 c_oil->cc.oldbytecnt = c_oil->cc.bytecnt;
899 c_oil->cc.oldwrong_if = c_oil->cc.wrong_if;
900
c7b1183f
DS
901 if (!c_oil->installed)
902 {
903 c_oil->cc.lastused = 100 * qpim_keep_alive_time;
904 if (PIM_DEBUG_MROUTE)
905 {
906 struct prefix_sg sg;
907
908 sg.src = c_oil->oil.mfcc_origin;
909 sg.grp = c_oil->oil.mfcc_mcastgrp;
910 if (PIM_DEBUG_MROUTE)
911 zlog_debug("Channel(%s) is not installed no need to collect data from kernel",
912 pim_str_sg_dump (&sg));
913 }
914 return;
915 }
916
917 memset (&sgreq, 0, sizeof(sgreq));
918 sgreq.src = c_oil->oil.mfcc_origin;
919 sgreq.grp = c_oil->oil.mfcc_mcastgrp;
920
51e82833 921 pim_zlookup_sg_statistics (c_oil);
3667e8a0
DS
922 if (ioctl (qpim_mroute_socket_fd, SIOCGETSGCNT, &sgreq))
923 {
c7b1183f
DS
924 if (PIM_DEBUG_MROUTE)
925 {
926 struct prefix_sg sg;
927
928 sg.src = c_oil->oil.mfcc_origin;
929 sg.grp = c_oil->oil.mfcc_mcastgrp;
930
931 zlog_warn ("ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=(%s): errno=%d: %s",
932 (unsigned long)SIOCGETSGCNT,
933 pim_str_sg_dump (&sg),
934 errno,
935 safe_strerror(errno));
936 }
3667e8a0
DS
937 return;
938 }
939
940 c_oil->cc.pktcnt = sgreq.pktcnt;
941 c_oil->cc.bytecnt = sgreq.bytecnt;
942 c_oil->cc.wrong_if = sgreq.wrong_if;
943
944 return;
945}