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