]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_mroute.c
*: make consistent & update GPLv2 file headers
[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:
7a90f85c 628 qpim_mroute_socket_reader = NULL;
12e41d03
DL
629 mroute_read_on();
630
631 return result;
632}
633
634static void mroute_read_on()
635{
636 zassert(!qpim_mroute_socket_reader);
12e41d03 637
ffa2c898
QY
638 thread_add_read(master, mroute_read, 0, qpim_mroute_socket_fd,
639 &qpim_mroute_socket_reader);
12e41d03
DL
640}
641
642static void mroute_read_off()
643{
644 THREAD_OFF(qpim_mroute_socket_reader);
645}
646
647int pim_mroute_socket_enable()
648{
649 int fd;
650
12e41d03
DL
651 if ( pimd_privs.change (ZPRIVS_RAISE) )
652 zlog_err ("pim_mroute_socket_enable: could not raise privs, %s",
653 safe_strerror (errno) );
654
655 fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
656
657 if ( pimd_privs.change (ZPRIVS_LOWER) )
658 zlog_err ("pim_mroute_socket_enable: could not lower privs, %s",
659 safe_strerror (errno) );
660
661 if (fd < 0) {
662 zlog_warn("Could not create mroute socket: errno=%d: %s",
663 errno, safe_strerror(errno));
664 return -2;
665 }
666
667 if (pim_mroute_set(fd, 1)) {
668 zlog_warn("Could not enable mroute on socket fd=%d: errno=%d: %s",
669 fd, errno, safe_strerror(errno));
670 close(fd);
671 return -3;
672 }
673
674 qpim_mroute_socket_fd = fd;
b45cefcb 675
12e41d03
DL
676 qpim_mroute_socket_creation = pim_time_monotonic_sec();
677 mroute_read_on();
678
12e41d03
DL
679 return 0;
680}
681
682int pim_mroute_socket_disable()
683{
12e41d03
DL
684 if (pim_mroute_set(qpim_mroute_socket_fd, 0)) {
685 zlog_warn("Could not disable mroute on socket fd=%d: errno=%d: %s",
686 qpim_mroute_socket_fd, errno, safe_strerror(errno));
687 return -2;
688 }
689
690 if (close(qpim_mroute_socket_fd)) {
691 zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
692 qpim_mroute_socket_fd, errno, safe_strerror(errno));
693 return -3;
694 }
695
696 mroute_read_off();
697 qpim_mroute_socket_fd = -1;
698
12e41d03
DL
699 return 0;
700}
701
702/*
703 For each network interface (e.g., physical or a virtual tunnel) that
704 would be used for multicast forwarding, a corresponding multicast
705 interface must be added to the kernel.
706 */
744d91b3 707int pim_mroute_add_vif(struct interface *ifp, struct in_addr ifaddr, unsigned char flags)
12e41d03 708{
744d91b3 709 struct pim_interface *pim_ifp = ifp->info;
12e41d03
DL
710 struct vifctl vc;
711 int err;
712
12e41d03 713 memset(&vc, 0, sizeof(vc));
744d91b3 714 vc.vifc_vifi = pim_ifp->mroute_vif_index;
b3f2bf7c 715#ifdef VIFF_USE_IFINDEX
744d91b3 716 vc.vifc_lcl_ifindex = ifp->ifindex;
b3f2bf7c
RW
717#else
718 if (ifaddr.s_addr == INADDR_ANY) {
719 zlog_warn("%s: unnumbered interfaces are not supported on this platform",
720 __PRETTY_FUNCTION__);
721 return -1;
722 }
723 memcpy(&vc.vifc_lcl_addr, &ifaddr, sizeof(vc.vifc_lcl_addr));
724#endif
b45cefcb 725 vc.vifc_flags = flags;
12e41d03
DL
726 vc.vifc_threshold = PIM_MROUTE_MIN_TTL;
727 vc.vifc_rate_limit = 0;
12e41d03
DL
728
729#ifdef PIM_DVMRP_TUNNEL
730 if (vc.vifc_flags & VIFF_TUNNEL) {
731 memcpy(&vc.vifc_rmt_addr, &vif_remote_addr, sizeof(vc.vifc_rmt_addr));
732 }
733#endif
734
b45cefcb 735 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_VIF, (void*) &vc, sizeof(vc));
12e41d03 736 if (err) {
eaa54bdb 737 char ifaddr_str[INET_ADDRSTRLEN];
12e41d03
DL
738
739 pim_inet4_dump("<ifaddr?>", ifaddr, ifaddr_str, sizeof(ifaddr_str));
740
59471fb8 741 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s,flag=%d): errno=%d: %s",
12e41d03 742 __FILE__, __PRETTY_FUNCTION__,
744d91b3 743 qpim_mroute_socket_fd, ifp->ifindex, ifaddr_str, flags,
3d7765d7 744 errno, safe_strerror(errno));
12e41d03
DL
745 return -2;
746 }
747
748 return 0;
749}
750
751int pim_mroute_del_vif(int vif_index)
752{
753 struct vifctl vc;
754 int err;
755
e81d9709
CS
756 if (PIM_DEBUG_MROUTE)
757 {
758 struct interface *ifp = pim_if_find_by_vif_index (vif_index);
759 zlog_debug ("%s %s: Del Vif %d (%s) ", __FILE__,
760 __PRETTY_FUNCTION__, vif_index, ifp ? ifp->name : "NULL");
761 }
762
12e41d03
DL
763 memset(&vc, 0, sizeof(vc));
764 vc.vifc_vifi = vif_index;
765
766 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_VIF, (void*) &vc, sizeof(vc));
767 if (err) {
12e41d03
DL
768 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
769 __FILE__, __PRETTY_FUNCTION__,
770 qpim_mroute_socket_fd, vif_index,
3d7765d7 771 errno, safe_strerror(errno));
12e41d03
DL
772 return -2;
773 }
774
775 return 0;
776}
777
6a78764e 778int pim_mroute_add(struct channel_oil *c_oil, const char *name)
12e41d03
DL
779{
780 int err;
2ca35b3d 781 int orig = 0;
0365f56b 782 int orig_iif_vif = 0;
12e41d03
DL
783
784 qpim_mroute_add_last = pim_time_monotonic_sec();
785 ++qpim_mroute_add_events;
786
429a291b 787 /* Do not install route if incoming interface is undefined. */
1bc98276 788 if (c_oil->oil.mfcc_parent >= MAXVIFS)
429a291b
CS
789 {
790 if (PIM_DEBUG_MROUTE)
791 {
792 char buf[1000];
793 zlog_debug("%s(%s) %s Attempting to add vifi that is invalid to mroute table",
794 __PRETTY_FUNCTION__, name, pim_channel_oil_dump (c_oil, buf, sizeof(buf)));
795 }
796 return -2;
797 }
12e41d03 798
d3aded99
DS
799 /* The linux kernel *expects* the incoming
800 * vif to be part of the outgoing list
801 * in the case of a (*,G).
802 */
c171d6d8 803 if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY)
d3aded99 804 {
c171d6d8
DS
805 orig = c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent];
806 c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = 1;
d3aded99
DS
807 }
808
0365f56b
DS
809 /*
810 * If we have an unresolved cache entry for the S,G
811 * it is owned by the pimreg for the incoming IIF
812 * So set pimreg as the IIF temporarily to cause
813 * the packets to be forwarded. Then set it
814 * to the correct IIF afterwords.
815 */
816 if (!c_oil->installed && c_oil->oil.mfcc_origin.s_addr != INADDR_ANY &&
817 c_oil->oil.mfcc_parent != 0)
818 {
819 orig_iif_vif = c_oil->oil.mfcc_parent;
820 c_oil->oil.mfcc_parent = 0;
821 }
12e41d03 822 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_MFC,
c171d6d8 823 &c_oil->oil, sizeof(c_oil->oil));
d3aded99 824
0365f56b
DS
825 if (!err && !c_oil->installed && c_oil->oil.mfcc_origin.s_addr != INADDR_ANY &&
826 orig_iif_vif != 0)
827 {
828 c_oil->oil.mfcc_parent = orig_iif_vif;
829 err = setsockopt (qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_MFC,
830 &c_oil->oil, sizeof (c_oil->oil));
831 }
832
c171d6d8
DS
833 if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY)
834 c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = orig;
d3aded99 835
12e41d03 836 if (err) {
12e41d03
DL
837 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
838 __FILE__, __PRETTY_FUNCTION__,
839 qpim_mroute_socket_fd,
3d7765d7 840 errno, safe_strerror(errno));
12e41d03
DL
841 return -2;
842 }
843
6a78764e
DS
844 if (PIM_DEBUG_MROUTE)
845 {
781a1745
DS
846 char buf[1000];
847 zlog_debug("%s(%s), Added Route: %s",
848 __PRETTY_FUNCTION__, name,
849 pim_channel_oil_dump (c_oil, buf, sizeof(buf)));
6a78764e
DS
850 }
851
58302dc7 852 c_oil->installed = 1;
12e41d03
DL
853 return 0;
854}
855
6a78764e 856int pim_mroute_del (struct channel_oil *c_oil, const char *name)
12e41d03
DL
857{
858 int err;
859
860 qpim_mroute_del_last = pim_time_monotonic_sec();
861 ++qpim_mroute_del_events;
862
429a291b
CS
863 if (!c_oil->installed)
864 {
865 if (PIM_DEBUG_MROUTE)
866 {
867 char buf[1000];
868 zlog_debug("%s %s: vifi %d for route is %s not installed, do not need to send del req. ",
869 __FILE__, __PRETTY_FUNCTION__, c_oil->oil.mfcc_parent,
870 pim_channel_oil_dump (c_oil, buf, sizeof(buf)));
871 }
872 return -2;
873 }
874
c171d6d8 875 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_MFC, &c_oil->oil, sizeof(c_oil->oil));
12e41d03 876 if (err) {
05ca4827
DS
877 if (PIM_DEBUG_MROUTE)
878 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
879 __FILE__, __PRETTY_FUNCTION__,
880 qpim_mroute_socket_fd,
881 errno, safe_strerror(errno));
12e41d03
DL
882 return -2;
883 }
884
6a78764e
DS
885 if (PIM_DEBUG_MROUTE)
886 {
781a1745
DS
887 char buf[1000];
888 zlog_debug("%s(%s), Deleted Route: %s",
889 __PRETTY_FUNCTION__, name,
890 pim_channel_oil_dump (c_oil, buf, sizeof(buf)));
6a78764e 891 }
781a1745 892
cba44481 893 //Reset kernel installed flag
58302dc7
DS
894 c_oil->installed = 0;
895
12e41d03
DL
896 return 0;
897}
3667e8a0
DS
898
899void
900pim_mroute_update_counters (struct channel_oil *c_oil)
901{
902 struct sioc_sg_req sgreq;
903
3667e8a0
DS
904 c_oil->cc.oldpktcnt = c_oil->cc.pktcnt;
905 c_oil->cc.oldbytecnt = c_oil->cc.bytecnt;
906 c_oil->cc.oldwrong_if = c_oil->cc.wrong_if;
907
c7b1183f
DS
908 if (!c_oil->installed)
909 {
910 c_oil->cc.lastused = 100 * qpim_keep_alive_time;
911 if (PIM_DEBUG_MROUTE)
912 {
913 struct prefix_sg sg;
914
915 sg.src = c_oil->oil.mfcc_origin;
916 sg.grp = c_oil->oil.mfcc_mcastgrp;
917 if (PIM_DEBUG_MROUTE)
918 zlog_debug("Channel(%s) is not installed no need to collect data from kernel",
919 pim_str_sg_dump (&sg));
920 }
921 return;
922 }
923
924 memset (&sgreq, 0, sizeof(sgreq));
925 sgreq.src = c_oil->oil.mfcc_origin;
926 sgreq.grp = c_oil->oil.mfcc_mcastgrp;
927
51e82833 928 pim_zlookup_sg_statistics (c_oil);
3667e8a0
DS
929 if (ioctl (qpim_mroute_socket_fd, SIOCGETSGCNT, &sgreq))
930 {
c7b1183f
DS
931 if (PIM_DEBUG_MROUTE)
932 {
933 struct prefix_sg sg;
934
935 sg.src = c_oil->oil.mfcc_origin;
936 sg.grp = c_oil->oil.mfcc_mcastgrp;
937
938 zlog_warn ("ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=(%s): errno=%d: %s",
939 (unsigned long)SIOCGETSGCNT,
940 pim_str_sg_dump (&sg),
941 errno,
942 safe_strerror(errno));
943 }
3667e8a0
DS
944 return;
945 }
946
947 c_oil->cc.pktcnt = sgreq.pktcnt;
948 c_oil->cc.bytecnt = sgreq.bytecnt;
949 c_oil->cc.wrong_if = sgreq.wrong_if;
950
951 return;
952}