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