Line data Source code
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 : #include "lib_errors.h"
29 : #include "lib/network.h"
30 :
31 : #include "pimd.h"
32 : #include "pim_rpf.h"
33 : #include "pim_mroute.h"
34 : #include "pim_oil.h"
35 : #include "pim_str.h"
36 : #include "pim_time.h"
37 : #include "pim_iface.h"
38 : #include "pim_macro.h"
39 : #include "pim_rp.h"
40 : #include "pim_oil.h"
41 : #include "pim_register.h"
42 : #include "pim_ifchannel.h"
43 : #include "pim_zlookup.h"
44 : #include "pim_ssm.h"
45 : #include "pim_sock.h"
46 : #include "pim_vxlan.h"
47 : #include "pim_msg.h"
48 :
49 : static void mroute_read_on(struct pim_instance *pim);
50 :
51 2 : int pim_mroute_set(struct pim_instance *pim, int enable)
52 : {
53 2 : int err;
54 2 : int opt, data;
55 2 : socklen_t data_len = sizeof(data);
56 :
57 : /*
58 : * We need to create the VRF table for the pim mroute_socket
59 : */
60 2 : if (pim->vrf->vrf_id != VRF_DEFAULT) {
61 0 : frr_with_privs (&pimd_privs) {
62 :
63 0 : data = pim->vrf->data.l.table_id;
64 0 : err = setsockopt(pim->mroute_socket, PIM_IPPROTO,
65 : MRT_TABLE, &data, data_len);
66 0 : if (err) {
67 0 : zlog_warn(
68 : "%s %s: failure: setsockopt(fd=%d,PIM_IPPROTO, MRT_TABLE=%d): errno=%d: %s",
69 : __FILE__, __func__, pim->mroute_socket,
70 : data, errno, safe_strerror(errno));
71 0 : return -1;
72 : }
73 : }
74 : }
75 :
76 4 : frr_with_privs (&pimd_privs) {
77 2 : opt = enable ? MRT_INIT : MRT_DONE;
78 : /*
79 : * *BSD *cares* about what value we pass down
80 : * here
81 : */
82 2 : data = 1;
83 2 : err = setsockopt(pim->mroute_socket, PIM_IPPROTO, opt, &data,
84 : data_len);
85 2 : if (err) {
86 0 : zlog_warn(
87 : "%s %s: failure: setsockopt(fd=%d,PIM_IPPROTO,%s=%d): errno=%d: %s",
88 : __FILE__, __func__, pim->mroute_socket,
89 : enable ? "MRT_INIT" : "MRT_DONE", data, errno,
90 : safe_strerror(errno));
91 0 : return -1;
92 : }
93 : }
94 :
95 : #if defined(HAVE_IP_PKTINFO)
96 2 : if (enable) {
97 : /* Linux and Solaris IP_PKTINFO */
98 1 : data = 1;
99 1 : if (setsockopt(pim->mroute_socket, PIM_IPPROTO, IP_PKTINFO,
100 : &data, data_len)) {
101 0 : zlog_warn(
102 : "Could not set IP_PKTINFO on socket fd=%d: errno=%d: %s",
103 : pim->mroute_socket, errno,
104 : safe_strerror(errno));
105 : }
106 : }
107 : #endif
108 :
109 : #if PIM_IPV == 6
110 1 : if (enable) {
111 : /* Linux and Solaris IPV6_PKTINFO */
112 1 : data = 1;
113 1 : if (setsockopt(pim->mroute_socket, PIM_IPPROTO,
114 : IPV6_RECVPKTINFO, &data, data_len)) {
115 0 : zlog_warn(
116 : "Could not set IPV6_RECVPKTINFO on socket fd=%d: errno=%d: %s",
117 : pim->mroute_socket, errno,
118 : safe_strerror(errno));
119 : }
120 : }
121 : #endif
122 2 : setsockopt_so_recvbuf(pim->mroute_socket, 1024 * 1024 * 8);
123 :
124 2 : if (set_nonblocking(pim->mroute_socket) < 0) {
125 0 : zlog_warn(
126 : "Could not set non blocking on socket fd=%d: errno=%d: %s",
127 : pim->mroute_socket, errno, safe_strerror(errno));
128 0 : return -1;
129 : }
130 :
131 2 : if (enable) {
132 : #if defined linux
133 1 : int upcalls = GMMSG_WRVIFWHOLE;
134 1 : opt = MRT_PIM;
135 :
136 1 : err = setsockopt(pim->mroute_socket, PIM_IPPROTO, opt, &upcalls,
137 : sizeof(upcalls));
138 1 : if (err) {
139 0 : zlog_warn(
140 : "Failure to register for VIFWHOLE and WRONGVIF upcalls %d %s",
141 : errno, safe_strerror(errno));
142 0 : return -1;
143 : }
144 : #else
145 : zlog_warn(
146 : "PIM-SM will not work properly on this platform, until the ability to receive the WRVIFWHOLE upcall");
147 : #endif
148 : }
149 :
150 : return 0;
151 : }
152 :
153 : static const char *const gmmsgtype2str[GMMSG_WRVIFWHOLE + 1] = {
154 : "<unknown_upcall?>", "NOCACHE", "WRONGVIF", "WHOLEPKT", "WRVIFWHOLE"};
155 :
156 :
157 0 : int pim_mroute_msg_nocache(int fd, struct interface *ifp, const kernmsg *msg)
158 : {
159 0 : struct pim_interface *pim_ifp = ifp->info;
160 0 : struct pim_upstream *up;
161 0 : struct pim_rpf *rpg;
162 0 : pim_sgaddr sg;
163 :
164 0 : rpg = pim_ifp ? RP(pim_ifp->pim, msg->msg_im_dst) : NULL;
165 : /*
166 : * If the incoming interface is unknown OR
167 : * the Interface type is SSM we don't need to
168 : * do anything here
169 : */
170 0 : if (!rpg || pim_rpf_addr_is_inaddr_any(rpg)) {
171 0 : if (PIM_DEBUG_MROUTE_DETAIL)
172 0 : zlog_debug(
173 : "%s: Interface is not configured correctly to handle incoming packet: Could be !pim_ifp, !SM, !RP",
174 : __func__);
175 :
176 0 : return 0;
177 : }
178 :
179 : /*
180 : * If we've received a multicast packet that isn't connected to
181 : * us
182 : */
183 0 : if (!pim_if_connected_to_source(ifp, msg->msg_im_src)) {
184 0 : if (PIM_DEBUG_MROUTE_DETAIL)
185 0 : zlog_debug(
186 : "%s: Received incoming packet that doesn't originate on our seg",
187 : __func__);
188 0 : return 0;
189 : }
190 :
191 0 : memset(&sg, 0, sizeof(sg));
192 0 : sg.src = msg->msg_im_src;
193 0 : sg.grp = msg->msg_im_dst;
194 :
195 0 : if (!(PIM_I_am_DR(pim_ifp))) {
196 0 : if (PIM_DEBUG_MROUTE_DETAIL)
197 0 : zlog_debug(
198 : "%s: Interface is not the DR blackholing incoming traffic for %pSG",
199 : __func__, &sg);
200 :
201 : /*
202 : * We are not the DR, but we are still receiving packets
203 : * Let's blackhole those packets for the moment
204 : * As that they will be coming up to the cpu
205 : * and causing us to consider them.
206 : *
207 : * This *will* create a dangling channel_oil
208 : * that I see no way to get rid of. Just noting
209 : * this for future reference.
210 : */
211 0 : up = pim_upstream_find_or_add(
212 : &sg, ifp, PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE, __func__);
213 0 : pim_upstream_mroute_add(up->channel_oil, __func__);
214 :
215 0 : return 0;
216 : }
217 :
218 0 : up = pim_upstream_find_or_add(&sg, ifp, PIM_UPSTREAM_FLAG_MASK_FHR,
219 : __func__);
220 :
221 : /*
222 : * I moved this debug till after the actual add because
223 : * I want to take advantage of the up->sg_str being filled in.
224 : */
225 0 : if (PIM_DEBUG_MROUTE) {
226 0 : zlog_debug("%s: Adding a Route %s for WHOLEPKT consumption",
227 : __func__, up->sg_str);
228 : }
229 :
230 0 : PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up->flags);
231 0 : pim_upstream_keep_alive_timer_start(up, pim_ifp->pim->keep_alive_time);
232 :
233 0 : up->channel_oil->cc.pktcnt++;
234 : // resolve mfcc_parent prior to mroute_add in channel_add_oif
235 0 : if (up->rpf.source_nexthop.interface &&
236 0 : *oil_parent(up->channel_oil) >= MAXVIFS) {
237 0 : pim_upstream_mroute_iif_update(up->channel_oil, __func__);
238 : }
239 0 : pim_register_join(up);
240 : /* if we have receiver, inherit from parent */
241 0 : pim_upstream_inherited_olist_decide(pim_ifp->pim, up);
242 :
243 0 : return 0;
244 : }
245 :
246 0 : int pim_mroute_msg_wholepkt(int fd, struct interface *ifp, const char *buf,
247 : size_t len)
248 : {
249 0 : struct pim_interface *pim_ifp;
250 0 : pim_sgaddr sg;
251 0 : struct pim_rpf *rpg;
252 0 : const ipv_hdr *ip_hdr;
253 0 : struct pim_upstream *up;
254 :
255 0 : pim_ifp = ifp->info;
256 :
257 0 : ip_hdr = (const ipv_hdr *)buf;
258 :
259 0 : memset(&sg, 0, sizeof(sg));
260 0 : sg.src = IPV_SRC(ip_hdr);
261 0 : sg.grp = IPV_DST(ip_hdr);
262 :
263 0 : up = pim_upstream_find(pim_ifp->pim, &sg);
264 0 : if (!up) {
265 0 : pim_sgaddr star = sg;
266 0 : star.src = PIMADDR_ANY;
267 :
268 0 : up = pim_upstream_find(pim_ifp->pim, &star);
269 :
270 0 : if (up && PIM_UPSTREAM_FLAG_TEST_CAN_BE_LHR(up->flags)) {
271 0 : up = pim_upstream_add(pim_ifp->pim, &sg, ifp,
272 : PIM_UPSTREAM_FLAG_MASK_SRC_LHR,
273 : __func__, NULL);
274 0 : if (!up) {
275 0 : if (PIM_DEBUG_MROUTE)
276 0 : zlog_debug(
277 : "%s: Unable to create upstream information for %pSG",
278 : __func__, &sg);
279 0 : return 0;
280 : }
281 0 : pim_upstream_keep_alive_timer_start(
282 0 : up, pim_ifp->pim->keep_alive_time);
283 0 : pim_upstream_inherited_olist(pim_ifp->pim, up);
284 0 : pim_upstream_update_join_desired(pim_ifp->pim, up);
285 :
286 0 : if (PIM_DEBUG_MROUTE)
287 0 : zlog_debug("%s: Creating %s upstream on LHR",
288 : __func__, up->sg_str);
289 0 : return 0;
290 : }
291 0 : if (PIM_DEBUG_MROUTE_DETAIL) {
292 0 : zlog_debug(
293 : "%s: Unable to find upstream channel WHOLEPKT%pSG",
294 : __func__, &sg);
295 : }
296 0 : return 0;
297 : }
298 :
299 0 : if (!up->rpf.source_nexthop.interface) {
300 0 : if (PIM_DEBUG_PIM_TRACE)
301 0 : zlog_debug("%s: up %s RPF is not present", __func__,
302 : up->sg_str);
303 0 : return 0;
304 : }
305 :
306 0 : pim_ifp = up->rpf.source_nexthop.interface->info;
307 :
308 0 : rpg = pim_ifp ? RP(pim_ifp->pim, sg.grp) : NULL;
309 :
310 0 : if ((pim_rpf_addr_is_inaddr_any(rpg)) || (!pim_ifp) ||
311 0 : (!(PIM_I_am_DR(pim_ifp)))) {
312 0 : if (PIM_DEBUG_MROUTE) {
313 0 : zlog_debug("%s: Failed Check send packet", __func__);
314 : }
315 0 : return 0;
316 : }
317 :
318 : /*
319 : * If we've received a register suppress
320 : */
321 0 : if (!up->t_rs_timer) {
322 0 : if (pim_is_grp_ssm(pim_ifp->pim, sg.grp)) {
323 0 : if (PIM_DEBUG_PIM_REG)
324 0 : zlog_debug(
325 : "%pSG register forward skipped as group is SSM",
326 : &sg);
327 0 : return 0;
328 : }
329 :
330 0 : if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags)) {
331 0 : if (PIM_DEBUG_PIM_REG)
332 0 : zlog_debug(
333 : "%s register forward skipped, not FHR",
334 : up->sg_str);
335 0 : return 0;
336 : }
337 :
338 0 : pim_register_send((uint8_t *)buf + sizeof(ipv_hdr),
339 0 : len - sizeof(ipv_hdr),
340 : pim_ifp->primary_address, rpg, 0, up);
341 : }
342 : return 0;
343 : }
344 :
345 0 : int pim_mroute_msg_wrongvif(int fd, struct interface *ifp, const kernmsg *msg)
346 : {
347 0 : struct pim_ifchannel *ch;
348 0 : struct pim_interface *pim_ifp;
349 0 : pim_sgaddr sg;
350 :
351 0 : memset(&sg, 0, sizeof(sg));
352 0 : sg.src = msg->msg_im_src;
353 0 : sg.grp = msg->msg_im_dst;
354 :
355 : /*
356 : Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
357 :
358 : RFC 4601 4.8.2. PIM-SSM-Only Routers
359 :
360 : iif is the incoming interface of the packet.
361 : if (iif is in inherited_olist(S,G)) {
362 : send Assert(S,G) on iif
363 : }
364 : */
365 :
366 0 : if (!ifp) {
367 0 : if (PIM_DEBUG_MROUTE)
368 0 : zlog_debug(
369 : "%s: WRONGVIF (S,G)=%pSG could not find input interface for input_vif_index=%d",
370 : __func__, &sg, msg->msg_im_vif);
371 0 : return -1;
372 : }
373 :
374 0 : pim_ifp = ifp->info;
375 0 : if (!pim_ifp) {
376 0 : if (PIM_DEBUG_MROUTE)
377 0 : zlog_debug(
378 : "%s: WRONGVIF (S,G)=%pSG multicast not enabled on interface %s",
379 : __func__, &sg, ifp->name);
380 0 : return -2;
381 : }
382 :
383 0 : ch = pim_ifchannel_find(ifp, &sg);
384 0 : if (!ch) {
385 0 : pim_sgaddr star_g = sg;
386 0 : if (PIM_DEBUG_MROUTE)
387 0 : zlog_debug(
388 : "%s: WRONGVIF (S,G)=%pSG could not find channel on interface %s",
389 : __func__, &sg, ifp->name);
390 :
391 0 : star_g.src = PIMADDR_ANY;
392 0 : ch = pim_ifchannel_find(ifp, &star_g);
393 0 : if (!ch) {
394 0 : if (PIM_DEBUG_MROUTE)
395 0 : zlog_debug(
396 : "%s: WRONGVIF (*,G)=%pSG could not find channel on interface %s",
397 : __func__, &star_g, ifp->name);
398 0 : return -3;
399 : }
400 : }
401 :
402 : /*
403 : RFC 4601: 4.6.1. (S,G) Assert Message State Machine
404 :
405 : Transitions from NoInfo State
406 :
407 : An (S,G) data packet arrives on interface I, AND
408 : CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an
409 : downstream interface that is in our (S,G) outgoing interface
410 : list. We optimistically assume that we will be the assert
411 : winner for this (S,G), and so we transition to the "I am Assert
412 : Winner" state and perform Actions A1 (below), which will
413 : initiate the assert negotiation for (S,G).
414 : */
415 :
416 0 : if (ch->ifassert_state != PIM_IFASSERT_NOINFO) {
417 0 : if (PIM_DEBUG_MROUTE) {
418 0 : zlog_debug(
419 : "%s: WRONGVIF (S,G)=%s channel is not on Assert NoInfo state for interface %s",
420 : __func__, ch->sg_str, ifp->name);
421 : }
422 0 : return -4;
423 : }
424 :
425 0 : if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) {
426 0 : if (PIM_DEBUG_MROUTE) {
427 0 : zlog_debug(
428 : "%s: WRONGVIF (S,G)=%s interface %s is not downstream for channel",
429 : __func__, ch->sg_str, ifp->name);
430 : }
431 0 : return -5;
432 : }
433 :
434 0 : if (assert_action_a1(ch)) {
435 0 : if (PIM_DEBUG_MROUTE) {
436 0 : zlog_debug(
437 : "%s: WRONGVIF (S,G)=%s assert_action_a1 failure on interface %s",
438 : __func__, ch->sg_str, ifp->name);
439 : }
440 0 : return -6;
441 : }
442 :
443 : return 0;
444 : }
445 :
446 0 : int pim_mroute_msg_wrvifwhole(int fd, struct interface *ifp, const char *buf,
447 : size_t len)
448 : {
449 0 : const ipv_hdr *ip_hdr = (const ipv_hdr *)buf;
450 0 : struct pim_interface *pim_ifp;
451 0 : struct pim_instance *pim;
452 0 : struct pim_ifchannel *ch;
453 0 : struct pim_upstream *up;
454 0 : pim_sgaddr star_g;
455 0 : pim_sgaddr sg;
456 :
457 0 : pim_ifp = ifp->info;
458 :
459 0 : memset(&sg, 0, sizeof(sg));
460 0 : sg.src = IPV_SRC(ip_hdr);
461 0 : sg.grp = IPV_DST(ip_hdr);
462 :
463 0 : ch = pim_ifchannel_find(ifp, &sg);
464 0 : if (ch) {
465 0 : if (PIM_DEBUG_MROUTE)
466 0 : zlog_debug(
467 : "WRVIFWHOLE (S,G)=%s found ifchannel on interface %s",
468 : ch->sg_str, ifp->name);
469 0 : return -1;
470 : }
471 :
472 0 : star_g = sg;
473 0 : star_g.src = PIMADDR_ANY;
474 :
475 0 : pim = pim_ifp->pim;
476 : /*
477 : * If the incoming interface is the pimreg, then
478 : * we know the callback is associated with a pim register
479 : * packet and there is nothing to do here as that
480 : * normal pim processing will see the packet and allow
481 : * us to do the right thing.
482 : */
483 0 : if (ifp == pim->regiface) {
484 : return 0;
485 : }
486 :
487 0 : up = pim_upstream_find(pim_ifp->pim, &sg);
488 0 : if (up) {
489 0 : struct pim_upstream *parent;
490 0 : struct pim_nexthop source;
491 0 : struct pim_rpf *rpf = RP(pim_ifp->pim, sg.grp);
492 :
493 : /* No RPF or No RPF interface or No mcast on RPF interface */
494 0 : if (!rpf || !rpf->source_nexthop.interface ||
495 0 : !rpf->source_nexthop.interface->info)
496 : return 0;
497 :
498 : /*
499 : * If we have received a WRVIFWHOLE and are at this
500 : * point, we could be receiving the packet on the *,G
501 : * tree, let's check and if so we can safely drop
502 : * it.
503 : */
504 0 : parent = pim_upstream_find(pim_ifp->pim, &star_g);
505 0 : if (parent && parent->rpf.source_nexthop.interface == ifp)
506 : return 0;
507 :
508 0 : pim_ifp = rpf->source_nexthop.interface->info;
509 :
510 0 : memset(&source, 0, sizeof(source));
511 : /*
512 : * If we are the fhr that means we are getting a callback during
513 : * the pimreg period, so I believe we can ignore this packet
514 : */
515 0 : if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags)) {
516 : /*
517 : * No if channel, but upstream we are at the RP.
518 : *
519 : * This could be a anycast RP too and we may
520 : * not have received a register packet from
521 : * the source here at all. So gracefully
522 : * bow out of doing a nexthop lookup and
523 : * setting the SPTBIT to true
524 : */
525 0 : if (!(pim_addr_is_any(up->upstream_register)) &&
526 0 : pim_nexthop_lookup(pim_ifp->pim, &source,
527 : up->upstream_register, 0)) {
528 0 : pim_register_stop_send(source.interface, &sg,
529 : pim_ifp->primary_address,
530 : up->upstream_register);
531 0 : up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
532 : }
533 :
534 0 : pim_upstream_inherited_olist(pim_ifp->pim, up);
535 0 : if (!up->channel_oil->installed)
536 0 : pim_upstream_mroute_add(up->channel_oil,
537 : __func__);
538 : } else {
539 0 : if (I_am_RP(pim_ifp->pim, up->sg.grp)) {
540 0 : if (pim_nexthop_lookup(pim_ifp->pim, &source,
541 : up->upstream_register,
542 : 0))
543 0 : pim_register_stop_send(
544 : source.interface, &sg,
545 : pim_ifp->primary_address,
546 : up->upstream_register);
547 0 : up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
548 : } else {
549 : /*
550 : * At this point pimd is connected to
551 : * the source, it has a parent, we are not
552 : * the RP and the SPTBIT should be set
553 : * since we know *the* S,G is on the SPT.
554 : * The first time this happens, let's cause
555 : * an immediate join to go out so that
556 : * the RP can trim this guy immediately
557 : * if necessary, instead of waiting
558 : * one join/prune send cycle
559 : */
560 0 : if (up->sptbit != PIM_UPSTREAM_SPTBIT_TRUE &&
561 0 : up->parent &&
562 0 : up->rpf.source_nexthop.interface !=
563 : up->parent->rpf.source_nexthop
564 0 : .interface) {
565 0 : up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
566 0 : pim_jp_agg_single_upstream_send(
567 : &up->parent->rpf, up->parent,
568 : true);
569 : }
570 : }
571 0 : pim_upstream_keep_alive_timer_start(
572 0 : up, pim_ifp->pim->keep_alive_time);
573 0 : pim_upstream_inherited_olist(pim_ifp->pim, up);
574 0 : pim_mroute_msg_wholepkt(fd, ifp, buf, len);
575 : }
576 0 : return 0;
577 : }
578 :
579 0 : pim_ifp = ifp->info;
580 0 : if (pim_if_connected_to_source(ifp, sg.src)) {
581 0 : up = pim_upstream_add(pim_ifp->pim, &sg, ifp,
582 : PIM_UPSTREAM_FLAG_MASK_FHR, __func__,
583 : NULL);
584 0 : if (!up) {
585 0 : if (PIM_DEBUG_MROUTE)
586 0 : zlog_debug(
587 : "%pSG: WRONGVIF%s unable to create upstream on interface",
588 : &sg, ifp->name);
589 0 : return -2;
590 : }
591 0 : PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up->flags);
592 0 : pim_upstream_keep_alive_timer_start(
593 0 : up, pim_ifp->pim->keep_alive_time);
594 0 : up->channel_oil->cc.pktcnt++;
595 0 : pim_register_join(up);
596 0 : pim_upstream_inherited_olist(pim_ifp->pim, up);
597 0 : if (!up->channel_oil->installed)
598 0 : pim_upstream_mroute_add(up->channel_oil, __func__);
599 :
600 : // Send the packet to the RP
601 0 : pim_mroute_msg_wholepkt(fd, ifp, buf, len);
602 : } else {
603 0 : up = pim_upstream_add(pim_ifp->pim, &sg, ifp,
604 : PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE,
605 : __func__, NULL);
606 0 : if (!up->channel_oil->installed)
607 0 : pim_upstream_mroute_add(up->channel_oil, __func__);
608 : }
609 :
610 : return 0;
611 : }
612 :
613 : #if PIM_IPV == 4
614 : static int process_igmp_packet(struct pim_instance *pim, const char *buf,
615 : size_t buf_size, ifindex_t ifindex)
616 : {
617 : struct interface *ifp;
618 : struct pim_interface *pim_ifp;
619 : struct in_addr ifaddr;
620 : struct gm_sock *igmp;
621 : const struct prefix *connected_src;
622 : const struct ip *ip_hdr = (const struct ip *)buf;
623 :
624 : /* We have the IP packet but we do not know which interface this
625 : * packet was
626 : * received on. Find the interface that is on the same subnet as
627 : * the source
628 : * of the IP packet.
629 : */
630 : ifp = if_lookup_by_index(ifindex, pim->vrf->vrf_id);
631 :
632 : if (!ifp || !ifp->info)
633 : return 0;
634 :
635 : connected_src = pim_if_connected_to_source(ifp, ip_hdr->ip_src);
636 :
637 : if (!connected_src && !pim_addr_is_any(ip_hdr->ip_src)) {
638 : if (PIM_DEBUG_GM_PACKETS) {
639 : zlog_debug(
640 : "Recv IGMP packet on interface: %s from a non-connected source: %pI4",
641 : ifp->name, &ip_hdr->ip_src);
642 : }
643 : return 0;
644 : }
645 :
646 : pim_ifp = ifp->info;
647 : ifaddr = connected_src ? connected_src->u.prefix4
648 : : pim_ifp->primary_address;
649 : igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->gm_socket_list, ifaddr);
650 :
651 : if (PIM_DEBUG_GM_PACKETS) {
652 : zlog_debug(
653 : "%s(%s): igmp kernel upcall on %s(%p) for %pI4 -> %pI4",
654 : __func__, pim->vrf->name, ifp->name, igmp,
655 : &ip_hdr->ip_src, &ip_hdr->ip_dst);
656 : }
657 : if (igmp)
658 : pim_igmp_packet(igmp, (char *)buf, buf_size);
659 : else if (PIM_DEBUG_GM_PACKETS)
660 : zlog_debug(
661 : "No IGMP socket on interface: %s with connected source: %pI4",
662 : ifp->name, &ifaddr);
663 :
664 : return 0;
665 : }
666 : #endif
667 :
668 0 : int pim_mroute_msg(struct pim_instance *pim, const char *buf, size_t buf_size,
669 : ifindex_t ifindex)
670 : {
671 0 : struct interface *ifp;
672 0 : const ipv_hdr *ip_hdr;
673 0 : const kernmsg *msg;
674 :
675 0 : if (buf_size < (int)sizeof(ipv_hdr))
676 : return 0;
677 :
678 0 : ip_hdr = (const ipv_hdr *)buf;
679 :
680 : #if PIM_IPV == 4
681 : if (ip_hdr->ip_p == IPPROTO_IGMP) {
682 : process_igmp_packet(pim, buf, buf_size, ifindex);
683 : } else if (ip_hdr->ip_p) {
684 : if (PIM_DEBUG_MROUTE_DETAIL) {
685 : zlog_debug(
686 : "%s: no kernel upcall proto=%d src: %pI4 dst: %pI4 msg_size=%ld",
687 : __func__, ip_hdr->ip_p, &ip_hdr->ip_src,
688 : &ip_hdr->ip_dst, (long int)buf_size);
689 : }
690 :
691 : } else {
692 : #else
693 :
694 0 : if ((ip_hdr->ip6_vfc & 0xf) == 0) {
695 : #endif
696 0 : msg = (const kernmsg *)buf;
697 :
698 0 : ifp = pim_if_find_by_vif_index(pim, msg->msg_im_vif);
699 :
700 0 : if (!ifp)
701 : return 0;
702 0 : if (PIM_DEBUG_MROUTE) {
703 : #if PIM_IPV == 4
704 : zlog_debug(
705 : "%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%pI4,%pI4) on %s vifi=%d size=%ld",
706 : __func__, gmmsgtype2str[msg->msg_im_msgtype],
707 : msg->msg_im_msgtype, ip_hdr->ip_p,
708 : pim->mroute_socket, &msg->msg_im_src,
709 : &msg->msg_im_dst, ifp->name, msg->msg_im_vif,
710 : (long int)buf_size);
711 : #else
712 0 : zlog_debug(
713 : "%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%pI6,%pI6) on %s vifi=%d size=%ld",
714 : __func__, gmmsgtype2str[msg->msg_im_msgtype],
715 : msg->msg_im_msgtype, ip_hdr->ip6_nxt,
716 : pim->mroute_socket, &msg->msg_im_src,
717 : &msg->msg_im_dst, ifp->name, msg->msg_im_vif,
718 : (long int)buf_size);
719 : #endif
720 : }
721 :
722 0 : switch (msg->msg_im_msgtype) {
723 0 : case GMMSG_WRONGVIF:
724 0 : return pim_mroute_msg_wrongvif(pim->mroute_socket, ifp,
725 : msg);
726 0 : case GMMSG_NOCACHE:
727 0 : return pim_mroute_msg_nocache(pim->mroute_socket, ifp,
728 : msg);
729 0 : case GMMSG_WHOLEPKT:
730 0 : return pim_mroute_msg_wholepkt(pim->mroute_socket, ifp,
731 : (const char *)msg,
732 : buf_size);
733 0 : case GMMSG_WRVIFWHOLE:
734 0 : return pim_mroute_msg_wrvifwhole(pim->mroute_socket,
735 : ifp, (const char *)msg,
736 : buf_size);
737 : default:
738 : break;
739 : }
740 : }
741 :
742 : return 0;
743 : }
744 :
745 0 : static void mroute_read(struct thread *t)
746 : {
747 0 : struct pim_instance *pim;
748 0 : static long long count;
749 0 : char buf[10000];
750 0 : int cont = 1;
751 0 : int rd;
752 0 : ifindex_t ifindex;
753 0 : pim = THREAD_ARG(t);
754 :
755 0 : while (cont) {
756 0 : rd = pim_socket_recvfromto(pim->mroute_socket, (uint8_t *)buf,
757 : sizeof(buf), NULL, NULL, NULL, NULL,
758 : &ifindex);
759 0 : if (rd <= 0) {
760 0 : if (errno == EINTR)
761 0 : continue;
762 0 : if (errno == EWOULDBLOCK || errno == EAGAIN)
763 : break;
764 :
765 0 : zlog_warn(
766 : "%s: failure reading rd=%d: fd=%d: errno=%d: %s",
767 : __func__, rd, pim->mroute_socket, errno,
768 : safe_strerror(errno));
769 0 : goto done;
770 : }
771 :
772 0 : pim_mroute_msg(pim, buf, rd, ifindex);
773 :
774 0 : count++;
775 0 : if (count % router->packet_process == 0)
776 : cont = 0;
777 : }
778 : /* Keep reading */
779 0 : done:
780 0 : mroute_read_on(pim);
781 :
782 0 : return;
783 : }
784 :
785 1 : static void mroute_read_on(struct pim_instance *pim)
786 : {
787 1 : thread_add_read(router->master, mroute_read, pim, pim->mroute_socket,
788 : &pim->thread);
789 1 : }
790 :
791 1 : static void mroute_read_off(struct pim_instance *pim)
792 : {
793 1 : THREAD_OFF(pim->thread);
794 : }
795 :
796 1 : int pim_mroute_socket_enable(struct pim_instance *pim)
797 : {
798 1 : int fd;
799 :
800 2 : frr_with_privs(&pimd_privs) {
801 :
802 : #if PIM_IPV == 4
803 : fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
804 : #else
805 1 : fd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
806 : #endif
807 1 : if (fd < 0) {
808 0 : zlog_warn("Could not create mroute socket: errno=%d: %s",
809 : errno,
810 : safe_strerror(errno));
811 0 : return -2;
812 : }
813 :
814 : #if PIM_IPV == 6
815 1 : struct icmp6_filter filter[1];
816 1 : int ret;
817 :
818 : /* Unlike IPv4, this socket is not used for MLD, so just drop
819 : * everything with an empty ICMP6 filter. Otherwise we get
820 : * all kinds of garbage here, possibly even non-multicast
821 : * related ICMPv6 traffic (e.g. ping)
822 : *
823 : * (mroute kernel upcall "packets" are injected directly on the
824 : * socket, this sockopt -or any other- has no effect on them)
825 : */
826 1 : ICMP6_FILTER_SETBLOCKALL(filter);
827 1 : ret = setsockopt(fd, SOL_ICMPV6, ICMP6_FILTER, filter,
828 : sizeof(filter));
829 1 : if (ret)
830 0 : zlog_err(
831 : "(VRF %s) failed to set mroute control filter: %m",
832 : pim->vrf->name);
833 : #endif
834 :
835 : #ifdef SO_BINDTODEVICE
836 1 : if (pim->vrf->vrf_id != VRF_DEFAULT
837 0 : && setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
838 0 : pim->vrf->name, strlen(pim->vrf->name))) {
839 0 : zlog_warn("Could not setsockopt SO_BINDTODEVICE: %s",
840 : safe_strerror(errno));
841 0 : close(fd);
842 0 : return -3;
843 : }
844 : #endif
845 :
846 : }
847 :
848 1 : pim->mroute_socket = fd;
849 1 : if (pim_mroute_set(pim, 1)) {
850 0 : zlog_warn(
851 : "Could not enable mroute on socket fd=%d: errno=%d: %s",
852 : fd, errno, safe_strerror(errno));
853 0 : close(fd);
854 0 : pim->mroute_socket = -1;
855 0 : return -3;
856 : }
857 :
858 1 : pim->mroute_socket_creation = pim_time_monotonic_sec();
859 :
860 1 : mroute_read_on(pim);
861 :
862 1 : return 0;
863 : }
864 :
865 1 : int pim_mroute_socket_disable(struct pim_instance *pim)
866 : {
867 1 : if (pim_mroute_set(pim, 0)) {
868 0 : zlog_warn(
869 : "Could not disable mroute on socket fd=%d: errno=%d: %s",
870 : pim->mroute_socket, errno, safe_strerror(errno));
871 0 : return -2;
872 : }
873 :
874 1 : if (close(pim->mroute_socket)) {
875 0 : zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
876 : pim->mroute_socket, errno, safe_strerror(errno));
877 0 : return -3;
878 : }
879 :
880 1 : mroute_read_off(pim);
881 1 : pim->mroute_socket = -1;
882 :
883 1 : return 0;
884 : }
885 :
886 : /*
887 : For each network interface (e.g., physical or a virtual tunnel) that
888 : would be used for multicast forwarding, a corresponding multicast
889 : interface must be added to the kernel.
890 : */
891 4 : int pim_mroute_add_vif(struct interface *ifp, pim_addr ifaddr,
892 : unsigned char flags)
893 : {
894 4 : struct pim_interface *pim_ifp = ifp->info;
895 4 : pim_vifctl vc;
896 4 : int err;
897 :
898 4 : if (PIM_DEBUG_MROUTE)
899 0 : zlog_debug("%s: Add Vif %d (%s[%s])", __func__,
900 : pim_ifp->mroute_vif_index, ifp->name,
901 : pim_ifp->pim->vrf->name);
902 :
903 4 : memset(&vc, 0, sizeof(vc));
904 4 : vc.vc_vifi = pim_ifp->mroute_vif_index;
905 : #if PIM_IPV == 4
906 : #ifdef VIFF_USE_IFINDEX
907 : vc.vc_lcl_ifindex = ifp->ifindex;
908 : #else
909 : if (ifaddr.s_addr == INADDR_ANY) {
910 : zlog_warn(
911 : "%s: unnumbered interfaces are not supported on this platform",
912 : __func__);
913 : return -1;
914 : }
915 : memcpy(&vc.vc_lcl_addr, &ifaddr, sizeof(vc.vc_lcl_addr));
916 : #endif
917 : #else
918 4 : vc.vc_pifi = ifp->ifindex;
919 : #endif
920 4 : vc.vc_flags = flags;
921 4 : vc.vc_threshold = PIM_MROUTE_MIN_TTL;
922 4 : vc.vc_rate_limit = 0;
923 :
924 : #if PIM_IPV == 4
925 : #ifdef PIM_DVMRP_TUNNEL
926 : if (vc.vc_flags & VIFF_TUNNEL) {
927 : memcpy(&vc.vc_rmt_addr, &vif_remote_addr,
928 : sizeof(vc.vc_rmt_addr));
929 : }
930 : #endif
931 : #endif
932 :
933 4 : err = setsockopt(pim_ifp->pim->mroute_socket, PIM_IPPROTO, MRT_ADD_VIF,
934 : (void *)&vc, sizeof(vc));
935 4 : if (err) {
936 0 : zlog_warn(
937 : "%s: failure: setsockopt(fd=%d,PIM_IPPROTO,MRT_ADD_VIF,vif_index=%d,ifaddr=%pPAs,flag=%d): errno=%d: %s",
938 : __func__, pim_ifp->pim->mroute_socket, ifp->ifindex,
939 : &ifaddr, flags, errno, safe_strerror(errno));
940 0 : return -2;
941 : }
942 :
943 : return 0;
944 : }
945 :
946 3 : int pim_mroute_del_vif(struct interface *ifp)
947 : {
948 3 : struct pim_interface *pim_ifp = ifp->info;
949 3 : pim_vifctl vc;
950 3 : int err;
951 :
952 3 : if (PIM_DEBUG_MROUTE)
953 0 : zlog_debug("%s: Del Vif %d (%s[%s])", __func__,
954 : pim_ifp->mroute_vif_index, ifp->name,
955 : pim_ifp->pim->vrf->name);
956 :
957 3 : memset(&vc, 0, sizeof(vc));
958 3 : vc.vc_vifi = pim_ifp->mroute_vif_index;
959 :
960 3 : err = setsockopt(pim_ifp->pim->mroute_socket, PIM_IPPROTO, MRT_DEL_VIF,
961 : (void *)&vc, sizeof(vc));
962 3 : if (err) {
963 0 : zlog_warn(
964 : "%s %s: failure: setsockopt(fd=%d,PIM_IPPROTO,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
965 : __FILE__, __func__, pim_ifp->pim->mroute_socket,
966 : pim_ifp->mroute_vif_index, errno, safe_strerror(errno));
967 0 : return -2;
968 : }
969 :
970 : return 0;
971 : }
972 :
973 : /*
974 : * Prevent creating MFC entry with OIF=IIF.
975 : *
976 : * This is a protection against implementation mistakes.
977 : *
978 : * PIM protocol implicitely ensures loopfree multicast topology.
979 : *
980 : * IGMP must be protected against adding looped MFC entries created
981 : * by both source and receiver attached to the same interface. See
982 : * TODO T22.
983 : * We shall allow igmp to create upstream when it is DR for the intf.
984 : * Assume RP reachable via non DR.
985 : */
986 0 : bool pim_mroute_allow_iif_in_oil(struct channel_oil *c_oil,
987 : int oif_index)
988 : {
989 : #ifdef PIM_ENFORCE_LOOPFREE_MFC
990 0 : struct interface *ifp_out;
991 0 : struct pim_interface *pim_ifp;
992 :
993 0 : if (c_oil->up &&
994 0 : PIM_UPSTREAM_FLAG_TEST_ALLOW_IIF_IN_OIL(c_oil->up->flags))
995 : return true;
996 :
997 0 : ifp_out = pim_if_find_by_vif_index(c_oil->pim, oif_index);
998 0 : if (!ifp_out)
999 : return false;
1000 0 : pim_ifp = ifp_out->info;
1001 0 : if (!pim_ifp)
1002 : return false;
1003 0 : if ((c_oil->oif_flags[oif_index] & PIM_OIF_FLAG_PROTO_GM) &&
1004 0 : PIM_I_am_DR(pim_ifp))
1005 : return true;
1006 :
1007 : return false;
1008 : #else
1009 : return true;
1010 : #endif
1011 : }
1012 :
1013 0 : static inline void pim_mroute_copy(struct channel_oil *out,
1014 : struct channel_oil *in)
1015 : {
1016 0 : int i;
1017 :
1018 0 : *oil_origin(out) = *oil_origin(in);
1019 0 : *oil_mcastgrp(out) = *oil_mcastgrp(in);
1020 0 : *oil_parent(out) = *oil_parent(in);
1021 :
1022 0 : for (i = 0; i < MAXVIFS; ++i) {
1023 0 : if (*oil_parent(out) == i &&
1024 0 : !pim_mroute_allow_iif_in_oil(in, i)) {
1025 0 : oil_if_set(out, i, 0);
1026 0 : continue;
1027 : }
1028 :
1029 0 : if (in->oif_flags[i] & PIM_OIF_FLAG_MUTE)
1030 0 : oil_if_set(out, i, 0);
1031 : else
1032 0 : oil_if_set(out, i, oil_if_has(in, i));
1033 : }
1034 0 : }
1035 :
1036 : /* This function must not be called directly 0
1037 : * use pim_upstream_mroute_add or pim_static_mroute_add instead
1038 : */
1039 0 : static int pim_mroute_add(struct channel_oil *c_oil, const char *name)
1040 : {
1041 0 : struct pim_instance *pim = c_oil->pim;
1042 0 : struct channel_oil tmp_oil[1] = { };
1043 0 : int err;
1044 :
1045 0 : pim->mroute_add_last = pim_time_monotonic_sec();
1046 0 : ++pim->mroute_add_events;
1047 :
1048 : /* Copy the oil to a temporary structure to fixup (without need to
1049 : * later restore) before sending the mroute add to the dataplane
1050 : */
1051 0 : pim_mroute_copy(tmp_oil, c_oil);
1052 :
1053 : /* The linux kernel *expects* the incoming
1054 : * vif to be part of the outgoing list
1055 : * in the case of a (*,G).
1056 : */
1057 0 : if (pim_addr_is_any(*oil_origin(c_oil))) {
1058 0 : oil_if_set(tmp_oil, *oil_parent(c_oil), 1);
1059 : }
1060 :
1061 : /*
1062 : * If we have an unresolved cache entry for the S,G
1063 : * it is owned by the pimreg for the incoming IIF
1064 : * So set pimreg as the IIF temporarily to cause
1065 : * the packets to be forwarded. Then set it
1066 : * to the correct IIF afterwords.
1067 : */
1068 0 : if (!c_oil->installed && !pim_addr_is_any(*oil_origin(c_oil))
1069 0 : && *oil_parent(c_oil) != 0) {
1070 0 : *oil_parent(tmp_oil) = 0;
1071 : }
1072 : /* For IPv6 MRT_ADD_MFC is defined to MRT6_ADD_MFC */
1073 0 : err = setsockopt(pim->mroute_socket, PIM_IPPROTO, MRT_ADD_MFC,
1074 : &tmp_oil->oil, sizeof(tmp_oil->oil));
1075 :
1076 0 : if (!err && !c_oil->installed
1077 0 : && !pim_addr_is_any(*oil_origin(c_oil))
1078 0 : && *oil_parent(c_oil) != 0) {
1079 0 : *oil_parent(tmp_oil) = *oil_parent(c_oil);
1080 0 : err = setsockopt(pim->mroute_socket, PIM_IPPROTO, MRT_ADD_MFC,
1081 : &tmp_oil->oil, sizeof(tmp_oil->oil));
1082 : }
1083 :
1084 0 : if (err) {
1085 0 : zlog_warn(
1086 : "%s %s: failure: setsockopt(fd=%d,PIM_IPPROTO,MRT_ADD_MFC): errno=%d: %s",
1087 : __FILE__, __func__, pim->mroute_socket, errno,
1088 : safe_strerror(errno));
1089 0 : return -2;
1090 : }
1091 :
1092 0 : if (PIM_DEBUG_MROUTE) {
1093 0 : char buf[1000];
1094 0 : zlog_debug("%s(%s), vrf %s Added Route: %s", __func__, name,
1095 : pim->vrf->name,
1096 : pim_channel_oil_dump(c_oil, buf, sizeof(buf)));
1097 : }
1098 :
1099 0 : if (!c_oil->installed) {
1100 0 : c_oil->installed = 1;
1101 0 : c_oil->mroute_creation = pim_time_monotonic_sec();
1102 : }
1103 :
1104 : return 0;
1105 : }
1106 :
1107 0 : static int pim_upstream_get_mroute_iif(struct channel_oil *c_oil,
1108 : const char *name)
1109 : {
1110 0 : vifi_t iif = MAXVIFS;
1111 0 : struct interface *ifp = NULL;
1112 0 : struct pim_interface *pim_ifp;
1113 0 : struct pim_upstream *up = c_oil->up;
1114 :
1115 0 : if (up) {
1116 0 : if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags)) {
1117 0 : if (up->parent)
1118 0 : ifp = up->parent->rpf.source_nexthop.interface;
1119 : } else {
1120 0 : ifp = up->rpf.source_nexthop.interface;
1121 : }
1122 0 : if (ifp) {
1123 0 : pim_ifp = (struct pim_interface *)ifp->info;
1124 0 : if (pim_ifp)
1125 0 : iif = pim_ifp->mroute_vif_index;
1126 : }
1127 : }
1128 0 : return iif;
1129 : }
1130 :
1131 0 : static int pim_upstream_mroute_update(struct channel_oil *c_oil,
1132 : const char *name)
1133 : {
1134 0 : char buf[1000];
1135 :
1136 0 : if (*oil_parent(c_oil) >= MAXVIFS) {
1137 : /* the c_oil cannot be installed as a mroute yet */
1138 0 : if (PIM_DEBUG_MROUTE)
1139 0 : zlog_debug(
1140 : "%s(%s) %s mroute not ready to be installed; %s",
1141 : __func__, name,
1142 : pim_channel_oil_dump(c_oil, buf,
1143 : sizeof(buf)),
1144 : c_oil->installed ?
1145 : "uninstall" : "skip");
1146 : /* if already installed flush it out as we are going to stop
1147 : * updates to it leaving it in a stale state
1148 : */
1149 0 : if (c_oil->installed)
1150 0 : pim_mroute_del(c_oil, name);
1151 : /* return success (skipped) */
1152 0 : return 0;
1153 : }
1154 :
1155 0 : return pim_mroute_add(c_oil, name);
1156 : }
1157 :
1158 : /* IIF associated with SGrpt entries are re-evaluated when the parent
1159 : * (*,G) entries IIF changes
1160 : */
1161 0 : static void pim_upstream_all_sources_iif_update(struct pim_upstream *up)
1162 : {
1163 0 : struct listnode *listnode;
1164 0 : struct pim_upstream *child;
1165 :
1166 0 : for (ALL_LIST_ELEMENTS_RO(up->sources, listnode,
1167 : child)) {
1168 0 : if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(child->flags))
1169 0 : pim_upstream_mroute_iif_update(child->channel_oil,
1170 : __func__);
1171 : }
1172 0 : }
1173 :
1174 : /* In the case of "PIM state machine" added mroutes an upstream entry
1175 : * must be present to decide on the SPT-forwarding vs. RPT-forwarding.
1176 : */
1177 0 : int pim_upstream_mroute_add(struct channel_oil *c_oil, const char *name)
1178 : {
1179 0 : vifi_t iif;
1180 :
1181 0 : iif = pim_upstream_get_mroute_iif(c_oil, name);
1182 :
1183 0 : if (*oil_parent(c_oil) != iif) {
1184 0 : *oil_parent(c_oil) = iif;
1185 0 : if (pim_addr_is_any(*oil_origin(c_oil)) &&
1186 0 : c_oil->up)
1187 0 : pim_upstream_all_sources_iif_update(c_oil->up);
1188 : } else {
1189 0 : *oil_parent(c_oil) = iif;
1190 : }
1191 :
1192 0 : return pim_upstream_mroute_update(c_oil, name);
1193 : }
1194 :
1195 : /* Look for IIF changes and update the dateplane entry only if the IIF
1196 : * has changed.
1197 : */
1198 0 : int pim_upstream_mroute_iif_update(struct channel_oil *c_oil, const char *name)
1199 : {
1200 0 : vifi_t iif;
1201 0 : char buf[1000];
1202 :
1203 0 : iif = pim_upstream_get_mroute_iif(c_oil, name);
1204 0 : if (*oil_parent(c_oil) == iif) {
1205 : /* no change */
1206 : return 0;
1207 : }
1208 0 : *oil_parent(c_oil) = iif;
1209 :
1210 0 : if (pim_addr_is_any(*oil_origin(c_oil)) &&
1211 0 : c_oil->up)
1212 0 : pim_upstream_all_sources_iif_update(c_oil->up);
1213 :
1214 0 : if (PIM_DEBUG_MROUTE_DETAIL)
1215 0 : zlog_debug("%s(%s) %s mroute iif update %d",
1216 : __func__, name,
1217 : pim_channel_oil_dump(c_oil, buf,
1218 : sizeof(buf)), iif);
1219 : /* XXX: is this hack needed? */
1220 0 : c_oil->oil_inherited_rescan = 1;
1221 0 : return pim_upstream_mroute_update(c_oil, name);
1222 : }
1223 :
1224 0 : int pim_static_mroute_add(struct channel_oil *c_oil, const char *name)
1225 : {
1226 0 : return pim_mroute_add(c_oil, name);
1227 : }
1228 :
1229 0 : void pim_static_mroute_iif_update(struct channel_oil *c_oil,
1230 : int input_vif_index,
1231 : const char *name)
1232 : {
1233 0 : if (*oil_parent(c_oil) == input_vif_index)
1234 : return;
1235 :
1236 0 : *oil_parent(c_oil) = input_vif_index;
1237 0 : if (input_vif_index == MAXVIFS)
1238 0 : pim_mroute_del(c_oil, name);
1239 : else
1240 0 : pim_static_mroute_add(c_oil, name);
1241 : }
1242 :
1243 0 : int pim_mroute_del(struct channel_oil *c_oil, const char *name)
1244 : {
1245 0 : struct pim_instance *pim = c_oil->pim;
1246 0 : int err;
1247 :
1248 0 : pim->mroute_del_last = pim_time_monotonic_sec();
1249 0 : ++pim->mroute_del_events;
1250 :
1251 0 : if (!c_oil->installed) {
1252 0 : if (PIM_DEBUG_MROUTE) {
1253 0 : char buf[1000];
1254 0 : zlog_debug(
1255 : "%s %s: vifi %d for route is %s not installed, do not need to send del req. ",
1256 : __FILE__, __func__, *oil_parent(c_oil),
1257 : pim_channel_oil_dump(c_oil, buf, sizeof(buf)));
1258 : }
1259 0 : return -2;
1260 : }
1261 :
1262 0 : err = setsockopt(pim->mroute_socket, PIM_IPPROTO, MRT_DEL_MFC,
1263 0 : &c_oil->oil, sizeof(c_oil->oil));
1264 0 : if (err) {
1265 0 : if (PIM_DEBUG_MROUTE)
1266 0 : zlog_warn(
1267 : "%s %s: failure: setsockopt(fd=%d,PIM_IPPROTO,MRT_DEL_MFC): errno=%d: %s",
1268 : __FILE__, __func__, pim->mroute_socket, errno,
1269 : safe_strerror(errno));
1270 0 : return -2;
1271 : }
1272 :
1273 0 : if (PIM_DEBUG_MROUTE) {
1274 0 : char buf[1000];
1275 0 : zlog_debug("%s(%s), vrf %s Deleted Route: %s", __func__, name,
1276 : pim->vrf->name,
1277 : pim_channel_oil_dump(c_oil, buf, sizeof(buf)));
1278 : }
1279 :
1280 : // Reset kernel installed flag
1281 0 : c_oil->installed = 0;
1282 :
1283 0 : return 0;
1284 : }
1285 :
1286 0 : void pim_mroute_update_counters(struct channel_oil *c_oil)
1287 : {
1288 0 : struct pim_instance *pim = c_oil->pim;
1289 0 : pim_sioc_sg_req sgreq;
1290 :
1291 0 : c_oil->cc.oldpktcnt = c_oil->cc.pktcnt;
1292 0 : c_oil->cc.oldbytecnt = c_oil->cc.bytecnt;
1293 0 : c_oil->cc.oldwrong_if = c_oil->cc.wrong_if;
1294 :
1295 0 : if (!c_oil->installed) {
1296 0 : c_oil->cc.lastused = 100 * pim->keep_alive_time;
1297 0 : if (PIM_DEBUG_MROUTE) {
1298 0 : pim_sgaddr sg;
1299 :
1300 0 : sg.src = *oil_origin(c_oil);
1301 0 : sg.grp = *oil_mcastgrp(c_oil);
1302 0 : zlog_debug("Channel%pSG is not installed no need to collect data from kernel",
1303 : &sg);
1304 : }
1305 0 : return;
1306 : }
1307 :
1308 :
1309 0 : memset(&sgreq, 0, sizeof(sgreq));
1310 :
1311 0 : pim_zlookup_sg_statistics(c_oil);
1312 :
1313 : #if PIM_IPV == 4
1314 : sgreq.src = *oil_origin(c_oil);
1315 : sgreq.grp = *oil_mcastgrp(c_oil);
1316 : #else
1317 0 : sgreq.src = c_oil->oil.mf6cc_origin;
1318 0 : sgreq.grp = c_oil->oil.mf6cc_mcastgrp;
1319 : #endif
1320 0 : if (ioctl(pim->mroute_socket, PIM_SIOCGETSGCNT, &sgreq)) {
1321 0 : pim_sgaddr sg;
1322 :
1323 0 : sg.src = *oil_origin(c_oil);
1324 0 : sg.grp = *oil_mcastgrp(c_oil);
1325 :
1326 0 : zlog_warn(
1327 : "ioctl(PIM_SIOCGETSGCNT=%lu) failure for (S,G)=%pSG: errno=%d: %s",
1328 : (unsigned long)PIM_SIOCGETSGCNT, &sg, errno,
1329 : safe_strerror(errno));
1330 0 : return;
1331 : }
1332 :
1333 0 : c_oil->cc.pktcnt = sgreq.pktcnt;
1334 0 : c_oil->cc.bytecnt = sgreq.bytecnt;
1335 0 : c_oil->cc.wrong_if = sgreq.wrong_if;
1336 0 : return;
1337 : }
|