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