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