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