back to topotato report
topotato coverage report
Current view: top level - pimd - pim_mroute.c (source / functions) Hit Total Coverage
Test: aggregated run ( view descriptions ) Lines: 365 549 66.5 %
Date: 2023-02-24 14:41:08 Functions: 23 25 92.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          52 : int pim_mroute_set(struct pim_instance *pim, int enable)
      52             : {
      53          52 :         int err;
      54          52 :         int opt, data;
      55          52 :         socklen_t data_len = sizeof(data);
      56             : 
      57             :         /*
      58             :          * We need to create the VRF table for the pim mroute_socket
      59             :          */
      60          52 :         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         104 :         frr_with_privs (&pimd_privs) {
      77          52 :                 opt = enable ? MRT_INIT : MRT_DONE;
      78             :                 /*
      79             :                  * *BSD *cares* about what value we pass down
      80             :                  * here
      81             :                  */
      82          52 :                 data = 1;
      83          52 :                 err = setsockopt(pim->mroute_socket, PIM_IPPROTO, opt, &data,
      84             :                                  data_len);
      85          52 :                 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          52 :         if (enable) {
      97             :                 /* Linux and Solaris IP_PKTINFO */
      98          26 :                 data = 1;
      99          26 :                 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           8 :         if (enable) {
     111             :                 /* Linux and Solaris IPV6_PKTINFO */
     112           8 :                 data = 1;
     113           8 :                 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          52 :         setsockopt_so_recvbuf(pim->mroute_socket, 1024 * 1024 * 8);
     123             : 
     124          52 :         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          52 :         if (enable) {
     132             : #if defined linux
     133          26 :                 int upcalls = GMMSG_WRVIFWHOLE;
     134          26 :                 opt = MRT_PIM;
     135             : 
     136          26 :                 err = setsockopt(pim->mroute_socket, PIM_IPPROTO, opt, &upcalls,
     137             :                                  sizeof(upcalls));
     138          26 :                 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           7 : int pim_mroute_msg_nocache(int fd, struct interface *ifp, const kernmsg *msg)
     158             : {
     159           7 :         struct pim_interface *pim_ifp = ifp->info;
     160           7 :         struct pim_upstream *up;
     161           7 :         struct pim_rpf *rpg;
     162           7 :         pim_sgaddr sg;
     163             : 
     164           7 :         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           7 :         if (!rpg || pim_rpf_addr_is_inaddr_any(rpg)) {
     171           1 :                 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           1 :                 return 0;
     177             :         }
     178             : 
     179             :         /*
     180             :          * If we've received a multicast packet that isn't connected to
     181             :          * us
     182             :          */
     183           6 :         if (!pim_if_connected_to_source(ifp, msg->msg_im_src)) {
     184           3 :                 if (PIM_DEBUG_MROUTE_DETAIL)
     185           0 :                         zlog_debug(
     186             :                                 "%s: Received incoming packet that doesn't originate on our seg",
     187             :                                 __func__);
     188           3 :                 return 0;
     189             :         }
     190             : 
     191           3 :         memset(&sg, 0, sizeof(sg));
     192           3 :         sg.src = msg->msg_im_src;
     193           3 :         sg.grp = msg->msg_im_dst;
     194             : 
     195           3 :         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           3 :         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           3 :         if (PIM_DEBUG_MROUTE) {
     226           0 :                 zlog_debug("%s: Adding a Route %s for WHOLEPKT consumption",
     227             :                            __func__, up->sg_str);
     228             :         }
     229             : 
     230           3 :         PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up->flags);
     231           3 :         pim_upstream_keep_alive_timer_start(up, pim_ifp->pim->keep_alive_time);
     232             : 
     233           3 :         up->channel_oil->cc.pktcnt++;
     234             :         // resolve mfcc_parent prior to mroute_add in channel_add_oif
     235           3 :         if (up->rpf.source_nexthop.interface &&
     236           3 :             *oil_parent(up->channel_oil) >= MAXVIFS) {
     237           0 :                 pim_upstream_mroute_iif_update(up->channel_oil, __func__);
     238             :         }
     239           3 :         pim_register_join(up);
     240             :         /* if we have receiver, inherit from parent */
     241           3 :         pim_upstream_inherited_olist_decide(pim_ifp->pim, up);
     242             : 
     243           3 :         return 0;
     244             : }
     245             : 
     246           3 : int pim_mroute_msg_wholepkt(int fd, struct interface *ifp, const char *buf,
     247             :                             size_t len)
     248             : {
     249           3 :         struct pim_interface *pim_ifp;
     250           3 :         pim_sgaddr sg;
     251           3 :         struct pim_rpf *rpg;
     252           3 :         const ipv_hdr *ip_hdr;
     253           3 :         struct pim_upstream *up;
     254             : 
     255           3 :         pim_ifp = ifp->info;
     256             : 
     257           3 :         ip_hdr = (const ipv_hdr *)buf;
     258             : 
     259           3 :         memset(&sg, 0, sizeof(sg));
     260           3 :         sg.src = IPV_SRC(ip_hdr);
     261           3 :         sg.grp = IPV_DST(ip_hdr);
     262             : 
     263           3 :         up = pim_upstream_find(pim_ifp->pim, &sg);
     264           3 :         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           3 :         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           3 :         pim_ifp = up->rpf.source_nexthop.interface->info;
     307             : 
     308           3 :         rpg = pim_ifp ? RP(pim_ifp->pim, sg.grp) : NULL;
     309             : 
     310           3 :         if ((pim_rpf_addr_is_inaddr_any(rpg)) || (!pim_ifp) ||
     311           3 :             (!(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           3 :         if (!up->t_rs_timer) {
     322           3 :                 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           3 :                 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           3 :                 pim_register_send((uint8_t *)buf + sizeof(ipv_hdr),
     339           3 :                                   len - sizeof(ipv_hdr),
     340             :                                   pim_ifp->primary_address, rpg, 0, up);
     341             :         }
     342             :         return 0;
     343             : }
     344             : 
     345           4 : int pim_mroute_msg_wrongvif(int fd, struct interface *ifp, const kernmsg *msg)
     346             : {
     347           4 :         struct pim_ifchannel *ch;
     348           4 :         struct pim_interface *pim_ifp;
     349           4 :         pim_sgaddr sg;
     350             : 
     351           4 :         memset(&sg, 0, sizeof(sg));
     352           4 :         sg.src = msg->msg_im_src;
     353           4 :         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           4 :         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           4 :         pim_ifp = ifp->info;
     375           4 :         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           4 :         ch = pim_ifchannel_find(ifp, &sg);
     384           4 :         if (!ch) {
     385           4 :                 pim_sgaddr star_g = sg;
     386           4 :                 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           4 :                 star_g.src = PIMADDR_ANY;
     392           4 :                 ch = pim_ifchannel_find(ifp, &star_g);
     393           4 :                 if (!ch) {
     394           4 :                         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           4 :                         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           4 : int pim_mroute_msg_wrvifwhole(int fd, struct interface *ifp, const char *buf,
     447             :                               size_t len)
     448             : {
     449           4 :         const ipv_hdr *ip_hdr = (const ipv_hdr *)buf;
     450           4 :         struct pim_interface *pim_ifp;
     451           4 :         struct pim_instance *pim;
     452           4 :         struct pim_ifchannel *ch;
     453           4 :         struct pim_upstream *up;
     454           4 :         pim_sgaddr star_g;
     455           4 :         pim_sgaddr sg;
     456             : 
     457           4 :         pim_ifp = ifp->info;
     458             : 
     459           4 :         memset(&sg, 0, sizeof(sg));
     460           4 :         sg.src = IPV_SRC(ip_hdr);
     461           4 :         sg.grp = IPV_DST(ip_hdr);
     462             : 
     463           4 :         ch = pim_ifchannel_find(ifp, &sg);
     464           4 :         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           4 :         star_g = sg;
     473           4 :         star_g.src = PIMADDR_ANY;
     474             : 
     475           4 :         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           4 :         if (ifp == pim->regiface) {
     484             :                 return 0;
     485             :         }
     486             : 
     487           4 :         up = pim_upstream_find(pim_ifp->pim, &sg);
     488           4 :         if (up) {
     489           4 :                 struct pim_upstream *parent;
     490           4 :                 struct pim_nexthop source;
     491           4 :                 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           4 :                 if (!rpf || !rpf->source_nexthop.interface ||
     495           3 :                     !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           3 :                 parent = pim_upstream_find(pim_ifp->pim, &star_g);
     505           3 :                 if (parent && parent->rpf.source_nexthop.interface == ifp)
     506             :                         return 0;
     507             : 
     508           3 :                 pim_ifp = rpf->source_nexthop.interface->info;
     509             : 
     510           3 :                 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           3 :                 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           3 :                         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           3 :                                 if (up->sptbit != PIM_UPSTREAM_SPTBIT_TRUE &&
     561           3 :                                     up->parent &&
     562           1 :                                     up->rpf.source_nexthop.interface !=
     563             :                                             up->parent->rpf.source_nexthop
     564           1 :                                                     .interface) {
     565           1 :                                         up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
     566           1 :                                         pim_jp_agg_single_upstream_send(
     567             :                                                 &up->parent->rpf, up->parent,
     568             :                                                 true);
     569             :                                 }
     570             :                         }
     571           3 :                         pim_upstream_keep_alive_timer_start(
     572           3 :                                 up, pim_ifp->pim->keep_alive_time);
     573           3 :                         pim_upstream_inherited_olist(pim_ifp->pim, up);
     574           3 :                         pim_mroute_msg_wholepkt(fd, ifp, buf, len);
     575             :                 }
     576           3 :                 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         156 : static int process_igmp_packet(struct pim_instance *pim, const char *buf,
     615             :                                size_t buf_size, ifindex_t ifindex)
     616             : {
     617         156 :         struct interface *ifp;
     618         156 :         struct pim_interface *pim_ifp;
     619         156 :         struct in_addr ifaddr;
     620         156 :         struct gm_sock *igmp;
     621         156 :         const struct prefix *connected_src;
     622         156 :         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         156 :         ifp = if_lookup_by_index(ifindex, pim->vrf->vrf_id);
     631             : 
     632         156 :         if (!ifp || !ifp->info)
     633             :                 return 0;
     634             : 
     635         154 :         connected_src = pim_if_connected_to_source(ifp, ip_hdr->ip_src);
     636             : 
     637         154 :         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         154 :         pim_ifp = ifp->info;
     647         154 :         ifaddr = connected_src ? connected_src->u.prefix4
     648             :                                : pim_ifp->primary_address;
     649         154 :         igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->gm_socket_list, ifaddr);
     650             : 
     651         154 :         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         154 :         if (igmp)
     658         154 :                 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         171 : int pim_mroute_msg(struct pim_instance *pim, const char *buf, size_t buf_size,
     669             :                    ifindex_t ifindex)
     670             : {
     671         171 :         struct interface *ifp;
     672         171 :         const ipv_hdr *ip_hdr;
     673         171 :         const kernmsg *msg;
     674             : 
     675         171 :         if (buf_size < (int)sizeof(ipv_hdr))
     676             :                 return 0;
     677             : 
     678         171 :         ip_hdr = (const ipv_hdr *)buf;
     679             : 
     680             : #if PIM_IPV == 4
     681         168 :         if (ip_hdr->ip_p == IPPROTO_IGMP) {
     682         156 :                 process_igmp_packet(pim, buf, buf_size, ifindex);
     683          12 :         } 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           3 :         if ((ip_hdr->ip6_vfc & 0xf) == 0) {
     695             : #endif
     696          15 :                 msg = (const kernmsg *)buf;
     697             : 
     698          15 :                 ifp = pim_if_find_by_vif_index(pim, msg->msg_im_vif);
     699             : 
     700          15 :                 if (!ifp)
     701             :                         return 0;
     702          15 :                 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           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          15 :                 switch (msg->msg_im_msgtype) {
     723           4 :                 case GMMSG_WRONGVIF:
     724           4 :                         return pim_mroute_msg_wrongvif(pim->mroute_socket, ifp,
     725             :                                                        msg);
     726           7 :                 case GMMSG_NOCACHE:
     727           7 :                         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           4 :                 case GMMSG_WRVIFWHOLE:
     734           4 :                         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         157 : static void mroute_read(struct thread *t)
     746             : {
     747         157 :         struct pim_instance *pim;
     748         157 :         static long long count;
     749         157 :         char buf[10000];
     750         157 :         int cont = 1;
     751         157 :         int rd;
     752         157 :         ifindex_t ifindex;
     753         157 :         pim = THREAD_ARG(t);
     754             : 
     755         157 :         while (cont) {
     756         277 :                 rd = pim_socket_recvfromto(pim->mroute_socket, (uint8_t *)buf,
     757             :                                            sizeof(buf), NULL, NULL, NULL, NULL,
     758             :                                            &ifindex);
     759         277 :                 if (rd <= 0) {
     760         106 :                         if (errno == EINTR)
     761           0 :                                 continue;
     762         106 :                         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         171 :                 pim_mroute_msg(pim, buf, rd, ifindex);
     773             : 
     774         171 :                 count++;
     775         171 :                 if (count % router->packet_process == 0)
     776             :                         cont = 0;
     777             :         }
     778             : /* Keep reading */
     779         106 : done:
     780         157 :         mroute_read_on(pim);
     781             : 
     782         157 :         return;
     783             : }
     784             : 
     785         183 : static void mroute_read_on(struct pim_instance *pim)
     786             : {
     787         183 :         thread_add_read(router->master, mroute_read, pim, pim->mroute_socket,
     788             :                         &pim->thread);
     789         183 : }
     790             : 
     791          26 : static void mroute_read_off(struct pim_instance *pim)
     792             : {
     793          26 :         THREAD_OFF(pim->thread);
     794             : }
     795             : 
     796          26 : int pim_mroute_socket_enable(struct pim_instance *pim)
     797             : {
     798          26 :         int fd;
     799             : 
     800          52 :         frr_with_privs(&pimd_privs) {
     801             : 
     802             : #if PIM_IPV == 4
     803          18 :                 fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
     804             : #else
     805           8 :                 fd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
     806             : #endif
     807          26 :                 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           8 :                 struct icmp6_filter filter[1];
     816           8 :                 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           8 :                 ICMP6_FILTER_SETBLOCKALL(filter);
     827           8 :                 ret = setsockopt(fd, SOL_ICMPV6, ICMP6_FILTER, filter,
     828             :                                  sizeof(filter));
     829           8 :                 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          26 :                 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          26 :         pim->mroute_socket = fd;
     849          26 :         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          26 :         pim->mroute_socket_creation = pim_time_monotonic_sec();
     859             : 
     860          26 :         mroute_read_on(pim);
     861             : 
     862          26 :         return 0;
     863             : }
     864             : 
     865          26 : int pim_mroute_socket_disable(struct pim_instance *pim)
     866             : {
     867          26 :         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          26 :         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          26 :         mroute_read_off(pim);
     881          26 :         pim->mroute_socket = -1;
     882             : 
     883          26 :         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         107 : int pim_mroute_add_vif(struct interface *ifp, pim_addr ifaddr,
     892             :                        unsigned char flags)
     893             : {
     894         107 :         struct pim_interface *pim_ifp = ifp->info;
     895         107 :         pim_vifctl vc;
     896         107 :         int err;
     897             : 
     898         107 :         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         107 :         memset(&vc, 0, sizeof(vc));
     904         107 :         vc.vc_vifi = pim_ifp->mroute_vif_index;
     905             : #if PIM_IPV == 4
     906             : #ifdef VIFF_USE_IFINDEX
     907          64 :         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          43 :         vc.vc_pifi = ifp->ifindex;
     919             : #endif
     920         107 :         vc.vc_flags = flags;
     921         107 :         vc.vc_threshold = PIM_MROUTE_MIN_TTL;
     922         107 :         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         107 :         err = setsockopt(pim_ifp->pim->mroute_socket, PIM_IPPROTO, MRT_ADD_VIF,
     934             :                          (void *)&vc, sizeof(vc));
     935         107 :         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          81 : int pim_mroute_del_vif(struct interface *ifp)
     947             : {
     948          81 :         struct pim_interface *pim_ifp = ifp->info;
     949          81 :         pim_vifctl vc;
     950          81 :         int err;
     951             : 
     952          81 :         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          81 :         memset(&vc, 0, sizeof(vc));
     958          81 :         vc.vc_vifi = pim_ifp->mroute_vif_index;
     959             : 
     960          81 :         err = setsockopt(pim_ifp->pim->mroute_socket, PIM_IPPROTO, MRT_DEL_VIF,
     961             :                          (void *)&vc, sizeof(vc));
     962          81 :         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          26 : bool pim_mroute_allow_iif_in_oil(struct channel_oil *c_oil,
     987             :                 int oif_index)
     988             : {
     989             : #ifdef PIM_ENFORCE_LOOPFREE_MFC
     990          26 :         struct interface *ifp_out;
     991          26 :         struct pim_interface *pim_ifp;
     992             : 
     993          26 :         if (c_oil->up &&
     994          26 :                 PIM_UPSTREAM_FLAG_TEST_ALLOW_IIF_IN_OIL(c_oil->up->flags))
     995             :                 return true;
     996             : 
     997          26 :         ifp_out = pim_if_find_by_vif_index(c_oil->pim, oif_index);
     998          26 :         if (!ifp_out)
     999             :                 return false;
    1000          26 :         pim_ifp = ifp_out->info;
    1001          26 :         if (!pim_ifp)
    1002             :                 return false;
    1003          26 :         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          26 : static inline void pim_mroute_copy(struct channel_oil *out,
    1014             :                                    struct channel_oil *in)
    1015             : {
    1016          26 :         int i;
    1017             : 
    1018          26 :         *oil_origin(out) = *oil_origin(in);
    1019          26 :         *oil_mcastgrp(out) = *oil_mcastgrp(in);
    1020          26 :         *oil_parent(out) = *oil_parent(in);
    1021             : 
    1022        3098 :         for (i = 0; i < MAXVIFS; ++i) {
    1023        3098 :                 if (*oil_parent(out) == i &&
    1024          26 :                     !pim_mroute_allow_iif_in_oil(in, i)) {
    1025          26 :                         oil_if_set(out, i, 0);
    1026          26 :                         continue;
    1027             :                 }
    1028             : 
    1029        3046 :                 if (in->oif_flags[i] & PIM_OIF_FLAG_MUTE)
    1030           0 :                         oil_if_set(out, i, 0);
    1031             :                 else
    1032        3062 :                         oil_if_set(out, i, oil_if_has(in, i));
    1033             :         }
    1034          26 : }
    1035             : 
    1036             : /* This function must not be called directly 0
    1037             :  * use pim_upstream_mroute_add or pim_static_mroute_add instead
    1038             :  */
    1039          26 : static int pim_mroute_add(struct channel_oil *c_oil, const char *name)
    1040             : {
    1041          26 :         struct pim_instance *pim = c_oil->pim;
    1042          26 :         struct channel_oil tmp_oil[1] = { };
    1043          26 :         int err;
    1044             : 
    1045          26 :         pim->mroute_add_last = pim_time_monotonic_sec();
    1046          26 :         ++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          26 :         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          26 :         if (pim_addr_is_any(*oil_origin(c_oil))) {
    1058           4 :                 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          26 :         if (!c_oil->installed && !pim_addr_is_any(*oil_origin(c_oil))
    1069          12 :             && *oil_parent(c_oil) != 0) {
    1070          12 :                 *oil_parent(tmp_oil) = 0;
    1071             :         }
    1072             :         /* For IPv6 MRT_ADD_MFC is defined to MRT6_ADD_MFC */
    1073          26 :         err = setsockopt(pim->mroute_socket, PIM_IPPROTO, MRT_ADD_MFC,
    1074             :                          &tmp_oil->oil, sizeof(tmp_oil->oil));
    1075             : 
    1076          26 :         if (!err && !c_oil->installed
    1077          14 :             && !pim_addr_is_any(*oil_origin(c_oil))
    1078          12 :             && *oil_parent(c_oil) != 0) {
    1079          12 :                 *oil_parent(tmp_oil) = *oil_parent(c_oil);
    1080          12 :                 err = setsockopt(pim->mroute_socket, PIM_IPPROTO, MRT_ADD_MFC,
    1081             :                                  &tmp_oil->oil, sizeof(tmp_oil->oil));
    1082             :         }
    1083             : 
    1084          26 :         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          26 :         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          26 :         if (!c_oil->installed) {
    1100          14 :                 c_oil->installed = 1;
    1101          14 :                 c_oil->mroute_creation = pim_time_monotonic_sec();
    1102             :         }
    1103             : 
    1104             :         return 0;
    1105             : }
    1106             : 
    1107          42 : static int pim_upstream_get_mroute_iif(struct channel_oil *c_oil,
    1108             :                 const char *name)
    1109             : {
    1110          42 :         vifi_t iif = MAXVIFS;
    1111          42 :         struct interface *ifp = NULL;
    1112          42 :         struct pim_interface *pim_ifp;
    1113          42 :         struct pim_upstream *up = c_oil->up;
    1114             : 
    1115          42 :         if (up) {
    1116          38 :                 if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags)) {
    1117           7 :                         if (up->parent)
    1118           0 :                                 ifp = up->parent->rpf.source_nexthop.interface;
    1119             :                 } else {
    1120          31 :                         ifp = up->rpf.source_nexthop.interface;
    1121             :                 }
    1122          31 :                 if (ifp) {
    1123          26 :                         pim_ifp = (struct pim_interface *)ifp->info;
    1124          26 :                         if (pim_ifp)
    1125          26 :                                 iif = pim_ifp->mroute_vif_index;
    1126             :                 }
    1127             :         }
    1128          42 :         return iif;
    1129             : }
    1130             : 
    1131          31 : static int pim_upstream_mroute_update(struct channel_oil *c_oil,
    1132             :                 const char *name)
    1133             : {
    1134          31 :         char buf[1000];
    1135             : 
    1136          31 :         if (*oil_parent(c_oil) >= MAXVIFS) {
    1137             :                 /* the c_oil cannot be installed as a mroute yet */
    1138           5 :                 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           5 :                 if (c_oil->installed)
    1150           1 :                         pim_mroute_del(c_oil, name);
    1151             :                 /* return success (skipped) */
    1152           5 :                 return 0;
    1153             :         }
    1154             : 
    1155          26 :         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           2 : static void pim_upstream_all_sources_iif_update(struct pim_upstream *up)
    1162             : {
    1163           2 :         struct listnode *listnode;
    1164           2 :         struct pim_upstream *child;
    1165             : 
    1166           4 :         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           2 : }
    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          24 : int pim_upstream_mroute_add(struct channel_oil *c_oil, const char *name)
    1178             : {
    1179          24 :         vifi_t iif;
    1180             : 
    1181          24 :         iif = pim_upstream_get_mroute_iif(c_oil, name);
    1182             : 
    1183          24 :         if (*oil_parent(c_oil) != iif) {
    1184          11 :                 *oil_parent(c_oil) = iif;
    1185          11 :                 if (pim_addr_is_any(*oil_origin(c_oil)) &&
    1186           1 :                                 c_oil->up)
    1187           0 :                         pim_upstream_all_sources_iif_update(c_oil->up);
    1188             :         } else {
    1189          13 :                 *oil_parent(c_oil) = iif;
    1190             :         }
    1191             : 
    1192          24 :         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          18 : int pim_upstream_mroute_iif_update(struct channel_oil *c_oil, const char *name)
    1199             : {
    1200          18 :         vifi_t iif;
    1201          18 :         char buf[1000];
    1202             : 
    1203          18 :         iif = pim_upstream_get_mroute_iif(c_oil, name);
    1204          18 :         if (*oil_parent(c_oil) == iif) {
    1205             :                 /* no change */
    1206             :                 return 0;
    1207             :         }
    1208           7 :         *oil_parent(c_oil) = iif;
    1209             : 
    1210           7 :         if (pim_addr_is_any(*oil_origin(c_oil)) &&
    1211           2 :                         c_oil->up)
    1212           2 :                 pim_upstream_all_sources_iif_update(c_oil->up);
    1213             : 
    1214           7 :         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           7 :         c_oil->oil_inherited_rescan = 1;
    1221           7 :         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          16 : int pim_mroute_del(struct channel_oil *c_oil, const char *name)
    1244             : {
    1245          16 :         struct pim_instance *pim = c_oil->pim;
    1246          16 :         int err;
    1247             : 
    1248          16 :         pim->mroute_del_last = pim_time_monotonic_sec();
    1249          16 :         ++pim->mroute_del_events;
    1250             : 
    1251          16 :         if (!c_oil->installed) {
    1252           2 :                 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           2 :                 return -2;
    1260             :         }
    1261             : 
    1262          28 :         err = setsockopt(pim->mroute_socket, PIM_IPPROTO, MRT_DEL_MFC,
    1263          14 :                          &c_oil->oil, sizeof(c_oil->oil));
    1264          14 :         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          14 :         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          14 :         c_oil->installed = 0;
    1282             : 
    1283          14 :         return 0;
    1284             : }
    1285             : 
    1286           4 : void pim_mroute_update_counters(struct channel_oil *c_oil)
    1287             : {
    1288           4 :         struct pim_instance *pim = c_oil->pim;
    1289           4 :         pim_sioc_sg_req sgreq;
    1290             : 
    1291           4 :         c_oil->cc.oldpktcnt = c_oil->cc.pktcnt;
    1292           4 :         c_oil->cc.oldbytecnt = c_oil->cc.bytecnt;
    1293           4 :         c_oil->cc.oldwrong_if = c_oil->cc.wrong_if;
    1294             : 
    1295           4 :         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           4 :         memset(&sgreq, 0, sizeof(sgreq));
    1310             : 
    1311           4 :         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           4 :         sgreq.src = c_oil->oil.mf6cc_origin;
    1318           4 :         sgreq.grp = c_oil->oil.mf6cc_mcastgrp;
    1319             : #endif
    1320           4 :         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           4 :         c_oil->cc.pktcnt = sgreq.pktcnt;
    1334           4 :         c_oil->cc.bytecnt = sgreq.bytecnt;
    1335           4 :         c_oil->cc.wrong_if = sgreq.wrong_if;
    1336           4 :         return;
    1337             : }

Generated by: LCOV version v1.16-topotato