back to topotato report
topotato coverage report
Current view: top level - pimd - pim_mroute.c (source / functions) Hit Total Coverage
Test: test_pim_bfd.py::PIMBFDTest Lines: 108 533 20.3 %
Date: 2023-02-24 18:39:40 Functions: 9 25 36.0 %

          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           8 : int pim_mroute_set(struct pim_instance *pim, int enable)
      52             : {
      53           8 :         int err;
      54           8 :         int opt, data;
      55           8 :         socklen_t data_len = sizeof(data);
      56             : 
      57             :         /*
      58             :          * We need to create the VRF table for the pim mroute_socket
      59             :          */
      60           8 :         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          16 :         frr_with_privs (&pimd_privs) {
      77           8 :                 opt = enable ? MRT_INIT : MRT_DONE;
      78             :                 /*
      79             :                  * *BSD *cares* about what value we pass down
      80             :                  * here
      81             :                  */
      82           8 :                 data = 1;
      83           8 :                 err = setsockopt(pim->mroute_socket, PIM_IPPROTO, opt, &data,
      84             :                                  data_len);
      85           8 :                 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           8 :         if (enable) {
      97             :                 /* Linux and Solaris IP_PKTINFO */
      98           4 :                 data = 1;
      99           4 :                 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             :         if (enable) {
     111             :                 /* Linux and Solaris IPV6_PKTINFO */
     112             :                 data = 1;
     113             :                 if (setsockopt(pim->mroute_socket, PIM_IPPROTO,
     114             :                                IPV6_RECVPKTINFO, &data, data_len)) {
     115             :                         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           8 :         setsockopt_so_recvbuf(pim->mroute_socket, 1024 * 1024 * 8);
     123             : 
     124           8 :         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           8 :         if (enable) {
     132             : #if defined linux
     133           4 :                 int upcalls = GMMSG_WRVIFWHOLE;
     134           4 :                 opt = MRT_PIM;
     135             : 
     136           4 :                 err = setsockopt(pim->mroute_socket, PIM_IPPROTO, opt, &upcalls,
     137             :                                  sizeof(upcalls));
     138           4 :                 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          31 : static int process_igmp_packet(struct pim_instance *pim, const char *buf,
     615             :                                size_t buf_size, ifindex_t ifindex)
     616             : {
     617          31 :         struct interface *ifp;
     618          31 :         struct pim_interface *pim_ifp;
     619          31 :         struct in_addr ifaddr;
     620          31 :         struct gm_sock *igmp;
     621          31 :         const struct prefix *connected_src;
     622          31 :         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          31 :         ifp = if_lookup_by_index(ifindex, pim->vrf->vrf_id);
     631             : 
     632          31 :         if (!ifp || !ifp->info)
     633             :                 return 0;
     634             : 
     635          31 :         connected_src = pim_if_connected_to_source(ifp, ip_hdr->ip_src);
     636             : 
     637          31 :         if (!connected_src && !pim_addr_is_any(ip_hdr->ip_src)) {
     638           0 :                 if (PIM_DEBUG_GM_PACKETS) {
     639           0 :                         zlog_debug(
     640             :                                 "Recv IGMP packet on interface: %s from a non-connected source: %pI4",
     641             :                                 ifp->name, &ip_hdr->ip_src);
     642             :                 }
     643           0 :                 return 0;
     644             :         }
     645             : 
     646          31 :         pim_ifp = ifp->info;
     647          31 :         ifaddr = connected_src ? connected_src->u.prefix4
     648             :                                : pim_ifp->primary_address;
     649          31 :         igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->gm_socket_list, ifaddr);
     650             : 
     651          31 :         if (PIM_DEBUG_GM_PACKETS) {
     652           0 :                 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          31 :         if (igmp)
     658          31 :                 pim_igmp_packet(igmp, (char *)buf, buf_size);
     659           0 :         else if (PIM_DEBUG_GM_PACKETS)
     660           0 :                 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          31 : int pim_mroute_msg(struct pim_instance *pim, const char *buf, size_t buf_size,
     669             :                    ifindex_t ifindex)
     670             : {
     671          31 :         struct interface *ifp;
     672          31 :         const ipv_hdr *ip_hdr;
     673          31 :         const kernmsg *msg;
     674             : 
     675          31 :         if (buf_size < (int)sizeof(ipv_hdr))
     676             :                 return 0;
     677             : 
     678          31 :         ip_hdr = (const ipv_hdr *)buf;
     679             : 
     680             : #if PIM_IPV == 4
     681          31 :         if (ip_hdr->ip_p == IPPROTO_IGMP) {
     682          31 :                 process_igmp_packet(pim, buf, buf_size, ifindex);
     683           0 :         } else if (ip_hdr->ip_p) {
     684           0 :                 if (PIM_DEBUG_MROUTE_DETAIL) {
     685           0 :                         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             :         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           0 :                         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             :                         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          29 : static void mroute_read(struct thread *t)
     746             : {
     747          29 :         struct pim_instance *pim;
     748          29 :         static long long count;
     749          29 :         char buf[10000];
     750          29 :         int cont = 1;
     751          29 :         int rd;
     752          29 :         ifindex_t ifindex;
     753          29 :         pim = THREAD_ARG(t);
     754             : 
     755          29 :         while (cont) {
     756          51 :                 rd = pim_socket_recvfromto(pim->mroute_socket, (uint8_t *)buf,
     757             :                                            sizeof(buf), NULL, NULL, NULL, NULL,
     758             :                                            &ifindex);
     759          51 :                 if (rd <= 0) {
     760          20 :                         if (errno == EINTR)
     761           0 :                                 continue;
     762          20 :                         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          31 :                 pim_mroute_msg(pim, buf, rd, ifindex);
     773             : 
     774          31 :                 count++;
     775          31 :                 if (count % router->packet_process == 0)
     776             :                         cont = 0;
     777             :         }
     778             : /* Keep reading */
     779          20 : done:
     780          29 :         mroute_read_on(pim);
     781             : 
     782          29 :         return;
     783             : }
     784             : 
     785          33 : static void mroute_read_on(struct pim_instance *pim)
     786             : {
     787          33 :         thread_add_read(router->master, mroute_read, pim, pim->mroute_socket,
     788             :                         &pim->thread);
     789          33 : }
     790             : 
     791           4 : static void mroute_read_off(struct pim_instance *pim)
     792             : {
     793           4 :         THREAD_OFF(pim->thread);
     794             : }
     795             : 
     796           4 : int pim_mroute_socket_enable(struct pim_instance *pim)
     797             : {
     798           4 :         int fd;
     799             : 
     800           8 :         frr_with_privs(&pimd_privs) {
     801             : 
     802             : #if PIM_IPV == 4
     803           4 :                 fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
     804             : #else
     805             :                 fd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
     806             : #endif
     807           4 :                 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             :                 struct icmp6_filter filter[1];
     816             :                 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             :                 ICMP6_FILTER_SETBLOCKALL(filter);
     827             :                 ret = setsockopt(fd, SOL_ICMPV6, ICMP6_FILTER, filter,
     828             :                                  sizeof(filter));
     829             :                 if (ret)
     830             :                         zlog_err(
     831             :                                 "(VRF %s) failed to set mroute control filter: %m",
     832             :                                 pim->vrf->name);
     833             : #endif
     834             : 
     835             : #ifdef SO_BINDTODEVICE
     836           4 :                 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           4 :         pim->mroute_socket = fd;
     849           4 :         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           4 :         pim->mroute_socket_creation = pim_time_monotonic_sec();
     859             : 
     860           4 :         mroute_read_on(pim);
     861             : 
     862           4 :         return 0;
     863             : }
     864             : 
     865           4 : int pim_mroute_socket_disable(struct pim_instance *pim)
     866             : {
     867           4 :         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           4 :         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           4 :         mroute_read_off(pim);
     881           4 :         pim->mroute_socket = -1;
     882             : 
     883           4 :         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          17 : int pim_mroute_add_vif(struct interface *ifp, pim_addr ifaddr,
     892             :                        unsigned char flags)
     893             : {
     894          17 :         struct pim_interface *pim_ifp = ifp->info;
     895          17 :         pim_vifctl vc;
     896          17 :         int err;
     897             : 
     898          17 :         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          17 :         memset(&vc, 0, sizeof(vc));
     904          17 :         vc.vc_vifi = pim_ifp->mroute_vif_index;
     905             : #if PIM_IPV == 4
     906             : #ifdef VIFF_USE_IFINDEX
     907          17 :         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             :         vc.vc_pifi = ifp->ifindex;
     919             : #endif
     920          17 :         vc.vc_flags = flags;
     921          17 :         vc.vc_threshold = PIM_MROUTE_MIN_TTL;
     922          17 :         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          17 :         err = setsockopt(pim_ifp->pim->mroute_socket, PIM_IPPROTO, MRT_ADD_VIF,
     934             :                          (void *)&vc, sizeof(vc));
     935          17 :         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          13 : int pim_mroute_del_vif(struct interface *ifp)
     947             : {
     948          13 :         struct pim_interface *pim_ifp = ifp->info;
     949          13 :         pim_vifctl vc;
     950          13 :         int err;
     951             : 
     952          13 :         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          13 :         memset(&vc, 0, sizeof(vc));
     958          13 :         vc.vc_vifi = pim_ifp->mroute_vif_index;
     959             : 
     960          13 :         err = setsockopt(pim_ifp->pim->mroute_socket, PIM_IPPROTO, MRT_DEL_VIF,
     961             :                          (void *)&vc, sizeof(vc));
     962          13 :         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           0 :         sgreq.src = *oil_origin(c_oil);
    1315           0 :         sgreq.grp = *oil_mcastgrp(c_oil);
    1316             : #else
    1317             :         sgreq.src = c_oil->oil.mf6cc_origin;
    1318             :         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             : }

Generated by: LCOV version v1.16-topotato