]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_mroute.c
pimd: Fix test for (S,G,rpt) prune inclusion.
[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 {
9244dd13
DS
338 struct pim_nexthop source;
339 struct pim_rpf *rpf = RP (sg.grp);
340 if (!rpf || !rpf->source_nexthop.interface)
341 return 0;
342
343 pim_ifp = rpf->source_nexthop.interface->info;
344
345 memset (&source, 0, sizeof (source));
7fe1f662
DS
346 /*
347 * If we are the fhr that means we are getting a callback during
348 * the pimreg period, so I believe we can ignore this packet
349 */
0bf27c5c 350 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags))
7fe1f662 351 {
7fe1f662 352 //No if channel, but upstream we are at the RP.
88d6652e
DS
353 if (pim_nexthop_lookup (&source, up->upstream_register, 0) == 0)
354 pim_register_stop_send(source.interface, &sg, pim_ifp->primary_address, up->upstream_register);
51e82833
DS
355 if (!up->channel_oil)
356 up->channel_oil = pim_channel_oil_add (&sg, pim_ifp->mroute_vif_index);
5b668dd7 357 pim_upstream_inherited_olist (up);
51e82833
DS
358 if (!up->channel_oil->installed)
359 pim_mroute_add (up->channel_oil);
3a66b17b 360 pim_upstream_set_sptbit (up, ifp);
7fe1f662 361 }
5b668dd7
DS
362 else
363 {
9244dd13
DS
364 if (I_am_RP (up->sg.grp))
365 {
366 if (pim_nexthop_lookup (&source, up->upstream_register, 0) == 0)
367 pim_register_stop_send(source.interface, &sg, pim_ifp->primary_address, up->upstream_register);
368 up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
369 }
5b668dd7
DS
370 pim_upstream_keep_alive_timer_start (up, qpim_keep_alive_time);
371 pim_upstream_inherited_olist (up);
372 pim_mroute_msg_wholepkt (fd, ifp, buf);
373 }
374 return 0;
8e38a2cf
DS
375 }
376
5f6f65b1
DS
377 pim_ifp = ifp->info;
378 oil = pim_channel_oil_add (&sg, pim_ifp->mroute_vif_index);
379 if (!oil->installed)
380 pim_mroute_add (oil);
381 if (pim_if_connected_to_source (ifp, sg.src))
08e1fe76 382 {
e5905a3b 383 up = pim_upstream_add (&sg, ifp, PIM_UPSTREAM_FLAG_MASK_FHR, __PRETTY_FUNCTION__);
5f6f65b1
DS
384 if (!up)
385 {
386 if (PIM_DEBUG_MROUTE)
387 zlog_debug ("%s: WRONGVIF%s unable to create upstream on interface",
388 pim_str_sg_dump (&sg), ifp->name);
389 return -2;
390 }
a9b59879 391 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up->flags);
5ce79466 392 PIM_UPSTREAM_FLAG_SET_CREATED_BY_UPSTREAM(up->flags);
08e1fe76 393
5f6f65b1
DS
394 pim_upstream_keep_alive_timer_start (up, qpim_keep_alive_time);
395 up->channel_oil = oil;
396 up->channel_oil->cc.pktcnt++;
397 pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM);
398 up->join_state = PIM_UPSTREAM_JOINED;
399 pim_upstream_inherited_olist (up);
08e1fe76 400
5f6f65b1
DS
401 // Send the packet to the RP
402 pim_mroute_msg_wholepkt (fd, ifp, buf);
403 }
08e1fe76
DS
404
405 return 0;
406}
407
e355e30f
DS
408int pim_mroute_msg(int fd, const char *buf, int buf_size)
409{
410 struct interface *ifp;
b05b72e8 411 struct pim_interface *pim_ifp;
e355e30f
DS
412 const struct ip *ip_hdr;
413 const struct igmpmsg *msg;
eaa54bdb
DW
414 char ip_src_str[INET_ADDRSTRLEN] = "";
415 char ip_dst_str[INET_ADDRSTRLEN] = "";
416 char src_str[INET_ADDRSTRLEN] = "<src?>";
417 char grp_str[INET_ADDRSTRLEN] = "<grp?>";
b05b72e8
DW
418 struct in_addr ifaddr;
419 struct igmp_sock *igmp;
12e41d03 420
e355e30f
DS
421 ip_hdr = (const struct ip *) buf;
422
b05b72e8
DW
423 if (ip_hdr->ip_p == IPPROTO_IGMP) {
424
425 /* We have the IP packet but we do not know which interface this packet was
426 * received on. Find the interface that is on the same subnet as the source
427 * of the IP packet.
428 */
9f0edbc9 429 ifp = pim_if_lookup_address_vrf (ip_hdr->ip_src, VRF_DEFAULT);
b05b72e8
DW
430
431 if (!ifp) {
432 if (PIM_DEBUG_MROUTE_DETAIL) {
433 pim_inet4_dump("<src?>", ip_hdr->ip_src, ip_src_str, sizeof(ip_src_str));
434 pim_inet4_dump("<dst?>", ip_hdr->ip_dst, ip_dst_str, sizeof(ip_dst_str));
435
9f0edbc9 436 zlog_warn("%s: igmp kernel upcall could not find usable interface for %s -> %s",
b05b72e8
DW
437 __PRETTY_FUNCTION__,
438 ip_src_str,
439 ip_dst_str);
440 }
441 return 0;
442 }
b05b72e8
DW
443 pim_ifp = ifp->info;
444 ifaddr = pim_find_primary_addr(ifp);
445 igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->igmp_socket_list, ifaddr);
446
02e5bd72 447 if (PIM_DEBUG_MROUTE) {
b05b72e8
DW
448 pim_inet4_dump("<src?>", ip_hdr->ip_src, ip_src_str, sizeof(ip_src_str));
449 pim_inet4_dump("<dst?>", ip_hdr->ip_dst, ip_dst_str, sizeof(ip_dst_str));
450
02e5bd72
DS
451 zlog_warn("%s: igmp kernel upcall on %s(%p) for %s -> %s",
452 __PRETTY_FUNCTION__, ifp->name, igmp, ip_src_str, ip_dst_str);
b05b72e8 453 }
02e5bd72
DS
454 if (igmp)
455 pim_igmp_packet(igmp, (char *)buf, buf_size);
b05b72e8
DW
456
457 } else if (ip_hdr->ip_p) {
6c7197b1 458 if (PIM_DEBUG_MROUTE_DETAIL) {
e5d33c83
DS
459 pim_inet4_dump("<src?>", ip_hdr->ip_src, src_str, sizeof(src_str));
460 pim_inet4_dump("<grp?>", ip_hdr->ip_dst, grp_str, sizeof(grp_str));
b05b72e8
DW
461 zlog_debug("%s: no kernel upcall proto=%d src: %s dst: %s msg_size=%d",
462 __PRETTY_FUNCTION__, ip_hdr->ip_p, src_str, grp_str, buf_size);
e355e30f 463 }
e355e30f 464
b05b72e8
DW
465 } else {
466 msg = (const struct igmpmsg *) buf;
e355e30f 467
b05b72e8 468 ifp = pim_if_find_by_vif_index(msg->im_vif);
e355e30f 469
b05b72e8
DW
470 if (PIM_DEBUG_MROUTE) {
471 pim_inet4_dump("<src?>", msg->im_src, src_str, sizeof(src_str));
472 pim_inet4_dump("<grp?>", msg->im_dst, grp_str, sizeof(grp_str));
473 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",
474 __PRETTY_FUNCTION__,
475 igmpmsgtype2str[msg->im_msgtype],
476 msg->im_msgtype,
477 ip_hdr->ip_p,
478 fd,
479 src_str,
480 grp_str,
481 ifp->name,
482 msg->im_vif, buf_size);
483 }
e355e30f 484
b05b72e8
DW
485 switch (msg->im_msgtype) {
486 case IGMPMSG_WRONGVIF:
487 return pim_mroute_msg_wrongvif(fd, ifp, msg);
488 break;
489 case IGMPMSG_NOCACHE:
490 return pim_mroute_msg_nocache(fd, ifp, msg);
491 break;
492 case IGMPMSG_WHOLEPKT:
493 return pim_mroute_msg_wholepkt(fd, ifp, (const char *)msg);
494 break;
495 case IGMPMSG_WRVIFWHOLE:
496 return pim_mroute_msg_wrvifwhole (fd, ifp, (const char *)msg);
497 break;
498 default:
499 break;
500 }
e355e30f 501 }
12e41d03
DL
502
503 return 0;
504}
505
506static int mroute_read_msg(int fd)
507{
eab2a327 508 char buf[10000];
12e41d03
DL
509 int rd;
510
12e41d03
DL
511 rd = read(fd, buf, sizeof(buf));
512 if (rd < 0) {
513 zlog_warn("%s: failure reading fd=%d: errno=%d: %s",
514 __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
f0ce50d4 515 return -1;
12e41d03
DL
516 }
517
518 return pim_mroute_msg(fd, buf, rd);
519}
520
521static int mroute_read(struct thread *t)
522{
523 int fd;
524 int result;
525
526 zassert(t);
527 zassert(!THREAD_ARG(t));
528
529 fd = THREAD_FD(t);
530 zassert(fd == qpim_mroute_socket_fd);
531
532 result = mroute_read_msg(fd);
533
534 /* Keep reading */
535 qpim_mroute_socket_reader = 0;
536 mroute_read_on();
537
538 return result;
539}
540
541static void mroute_read_on()
542{
543 zassert(!qpim_mroute_socket_reader);
544 zassert(PIM_MROUTE_IS_ENABLED);
545
546 THREAD_READ_ON(master, qpim_mroute_socket_reader,
547 mroute_read, 0, qpim_mroute_socket_fd);
548}
549
550static void mroute_read_off()
551{
552 THREAD_OFF(qpim_mroute_socket_reader);
553}
554
555int pim_mroute_socket_enable()
556{
557 int fd;
558
559 if (PIM_MROUTE_IS_ENABLED)
560 return -1;
561
562 if ( pimd_privs.change (ZPRIVS_RAISE) )
563 zlog_err ("pim_mroute_socket_enable: could not raise privs, %s",
564 safe_strerror (errno) );
565
566 fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
567
568 if ( pimd_privs.change (ZPRIVS_LOWER) )
569 zlog_err ("pim_mroute_socket_enable: could not lower privs, %s",
570 safe_strerror (errno) );
571
572 if (fd < 0) {
573 zlog_warn("Could not create mroute socket: errno=%d: %s",
574 errno, safe_strerror(errno));
575 return -2;
576 }
577
578 if (pim_mroute_set(fd, 1)) {
579 zlog_warn("Could not enable mroute on socket fd=%d: errno=%d: %s",
580 fd, errno, safe_strerror(errno));
581 close(fd);
582 return -3;
583 }
584
585 qpim_mroute_socket_fd = fd;
b45cefcb 586
12e41d03
DL
587 qpim_mroute_socket_creation = pim_time_monotonic_sec();
588 mroute_read_on();
589
12e41d03
DL
590 return 0;
591}
592
593int pim_mroute_socket_disable()
594{
595 if (PIM_MROUTE_IS_DISABLED)
596 return -1;
597
598 if (pim_mroute_set(qpim_mroute_socket_fd, 0)) {
599 zlog_warn("Could not disable mroute on socket fd=%d: errno=%d: %s",
600 qpim_mroute_socket_fd, errno, safe_strerror(errno));
601 return -2;
602 }
603
604 if (close(qpim_mroute_socket_fd)) {
605 zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
606 qpim_mroute_socket_fd, errno, safe_strerror(errno));
607 return -3;
608 }
609
610 mroute_read_off();
611 qpim_mroute_socket_fd = -1;
612
12e41d03
DL
613 return 0;
614}
615
616/*
617 For each network interface (e.g., physical or a virtual tunnel) that
618 would be used for multicast forwarding, a corresponding multicast
619 interface must be added to the kernel.
620 */
744d91b3 621int pim_mroute_add_vif(struct interface *ifp, struct in_addr ifaddr, unsigned char flags)
12e41d03 622{
744d91b3 623 struct pim_interface *pim_ifp = ifp->info;
12e41d03
DL
624 struct vifctl vc;
625 int err;
626
627 if (PIM_MROUTE_IS_DISABLED) {
628 zlog_warn("%s: global multicast is disabled",
629 __PRETTY_FUNCTION__);
630 return -1;
631 }
632
633 memset(&vc, 0, sizeof(vc));
744d91b3 634 vc.vifc_vifi = pim_ifp->mroute_vif_index;
b3f2bf7c 635#ifdef VIFF_USE_IFINDEX
744d91b3 636 vc.vifc_lcl_ifindex = ifp->ifindex;
b3f2bf7c
RW
637#else
638 if (ifaddr.s_addr == INADDR_ANY) {
639 zlog_warn("%s: unnumbered interfaces are not supported on this platform",
640 __PRETTY_FUNCTION__);
641 return -1;
642 }
643 memcpy(&vc.vifc_lcl_addr, &ifaddr, sizeof(vc.vifc_lcl_addr));
644#endif
b45cefcb 645 vc.vifc_flags = flags;
12e41d03
DL
646 vc.vifc_threshold = PIM_MROUTE_MIN_TTL;
647 vc.vifc_rate_limit = 0;
12e41d03
DL
648
649#ifdef PIM_DVMRP_TUNNEL
650 if (vc.vifc_flags & VIFF_TUNNEL) {
651 memcpy(&vc.vifc_rmt_addr, &vif_remote_addr, sizeof(vc.vifc_rmt_addr));
652 }
653#endif
654
b45cefcb 655 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_VIF, (void*) &vc, sizeof(vc));
12e41d03 656 if (err) {
eaa54bdb 657 char ifaddr_str[INET_ADDRSTRLEN];
12e41d03
DL
658
659 pim_inet4_dump("<ifaddr?>", ifaddr, ifaddr_str, sizeof(ifaddr_str));
660
59471fb8 661 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s,flag=%d): errno=%d: %s",
12e41d03 662 __FILE__, __PRETTY_FUNCTION__,
744d91b3 663 qpim_mroute_socket_fd, ifp->ifindex, ifaddr_str, flags,
3d7765d7 664 errno, safe_strerror(errno));
12e41d03
DL
665 return -2;
666 }
667
668 return 0;
669}
670
671int pim_mroute_del_vif(int vif_index)
672{
673 struct vifctl vc;
674 int err;
675
676 if (PIM_MROUTE_IS_DISABLED) {
677 zlog_warn("%s: global multicast is disabled",
678 __PRETTY_FUNCTION__);
679 return -1;
680 }
681
682 memset(&vc, 0, sizeof(vc));
683 vc.vifc_vifi = vif_index;
684
685 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_VIF, (void*) &vc, sizeof(vc));
686 if (err) {
12e41d03
DL
687 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
688 __FILE__, __PRETTY_FUNCTION__,
689 qpim_mroute_socket_fd, vif_index,
3d7765d7 690 errno, safe_strerror(errno));
12e41d03
DL
691 return -2;
692 }
693
694 return 0;
695}
696
c171d6d8 697int pim_mroute_add(struct channel_oil *c_oil)
12e41d03
DL
698{
699 int err;
2ca35b3d 700 int orig = 0;
0365f56b 701 int orig_iif_vif = 0;
12e41d03
DL
702
703 qpim_mroute_add_last = pim_time_monotonic_sec();
704 ++qpim_mroute_add_events;
705
706 if (PIM_MROUTE_IS_DISABLED) {
707 zlog_warn("%s: global multicast is disabled",
708 __PRETTY_FUNCTION__);
709 return -1;
710 }
711
d3aded99
DS
712 /* The linux kernel *expects* the incoming
713 * vif to be part of the outgoing list
714 * in the case of a (*,G).
715 */
c171d6d8 716 if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY)
d3aded99 717 {
c171d6d8
DS
718 orig = c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent];
719 c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = 1;
d3aded99
DS
720 }
721
0365f56b
DS
722 /*
723 * If we have an unresolved cache entry for the S,G
724 * it is owned by the pimreg for the incoming IIF
725 * So set pimreg as the IIF temporarily to cause
726 * the packets to be forwarded. Then set it
727 * to the correct IIF afterwords.
728 */
729 if (!c_oil->installed && c_oil->oil.mfcc_origin.s_addr != INADDR_ANY &&
730 c_oil->oil.mfcc_parent != 0)
731 {
732 orig_iif_vif = c_oil->oil.mfcc_parent;
733 c_oil->oil.mfcc_parent = 0;
734 }
12e41d03 735 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_MFC,
c171d6d8 736 &c_oil->oil, sizeof(c_oil->oil));
d3aded99 737
0365f56b
DS
738 if (!err && !c_oil->installed && c_oil->oil.mfcc_origin.s_addr != INADDR_ANY &&
739 orig_iif_vif != 0)
740 {
741 c_oil->oil.mfcc_parent = orig_iif_vif;
742 err = setsockopt (qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_MFC,
743 &c_oil->oil, sizeof (c_oil->oil));
744 }
745
c171d6d8
DS
746 if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY)
747 c_oil->oil.mfcc_ttls[c_oil->oil.mfcc_parent] = orig;
d3aded99 748
12e41d03 749 if (err) {
12e41d03
DL
750 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
751 __FILE__, __PRETTY_FUNCTION__,
752 qpim_mroute_socket_fd,
3d7765d7 753 errno, safe_strerror(errno));
12e41d03
DL
754 return -2;
755 }
756
58302dc7 757 c_oil->installed = 1;
12e41d03
DL
758 return 0;
759}
760
c171d6d8 761int pim_mroute_del (struct channel_oil *c_oil)
12e41d03
DL
762{
763 int err;
764
765 qpim_mroute_del_last = pim_time_monotonic_sec();
766 ++qpim_mroute_del_events;
767
768 if (PIM_MROUTE_IS_DISABLED) {
769 zlog_warn("%s: global multicast is disabled",
770 __PRETTY_FUNCTION__);
771 return -1;
772 }
773
c171d6d8 774 err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_MFC, &c_oil->oil, sizeof(c_oil->oil));
12e41d03 775 if (err) {
05ca4827
DS
776 if (PIM_DEBUG_MROUTE)
777 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
778 __FILE__, __PRETTY_FUNCTION__,
779 qpim_mroute_socket_fd,
780 errno, safe_strerror(errno));
12e41d03
DL
781 return -2;
782 }
783
58302dc7
DS
784 c_oil->installed = 0;
785
12e41d03
DL
786 return 0;
787}
3667e8a0
DS
788
789void
790pim_mroute_update_counters (struct channel_oil *c_oil)
791{
792 struct sioc_sg_req sgreq;
793
3667e8a0
DS
794 c_oil->cc.oldpktcnt = c_oil->cc.pktcnt;
795 c_oil->cc.oldbytecnt = c_oil->cc.bytecnt;
796 c_oil->cc.oldwrong_if = c_oil->cc.wrong_if;
797
c7b1183f
DS
798 if (!c_oil->installed)
799 {
800 c_oil->cc.lastused = 100 * qpim_keep_alive_time;
801 if (PIM_DEBUG_MROUTE)
802 {
803 struct prefix_sg sg;
804
805 sg.src = c_oil->oil.mfcc_origin;
806 sg.grp = c_oil->oil.mfcc_mcastgrp;
807 if (PIM_DEBUG_MROUTE)
808 zlog_debug("Channel(%s) is not installed no need to collect data from kernel",
809 pim_str_sg_dump (&sg));
810 }
811 return;
812 }
813
814 memset (&sgreq, 0, sizeof(sgreq));
815 sgreq.src = c_oil->oil.mfcc_origin;
816 sgreq.grp = c_oil->oil.mfcc_mcastgrp;
817
51e82833 818 pim_zlookup_sg_statistics (c_oil);
3667e8a0
DS
819 if (ioctl (qpim_mroute_socket_fd, SIOCGETSGCNT, &sgreq))
820 {
c7b1183f
DS
821 if (PIM_DEBUG_MROUTE)
822 {
823 struct prefix_sg sg;
824
825 sg.src = c_oil->oil.mfcc_origin;
826 sg.grp = c_oil->oil.mfcc_mcastgrp;
827
828 zlog_warn ("ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=(%s): errno=%d: %s",
829 (unsigned long)SIOCGETSGCNT,
830 pim_str_sg_dump (&sg),
831 errno,
832 safe_strerror(errno));
833 }
3667e8a0
DS
834 return;
835 }
836
837 c_oil->cc.pktcnt = sgreq.pktcnt;
838 c_oil->cc.bytecnt = sgreq.bytecnt;
839 c_oil->cc.wrong_if = sgreq.wrong_if;
840
841 return;
842}