back to topotato report
topotato coverage report
Current view: top level - pimd - pim_pim.c (source / functions) Hit Total Coverage
Test: test_pim_cbsr.py::PIMCandidateBSRTest Lines: 284 427 66.5 %
Date: 2023-02-16 02:09:14 Functions: 18 19 94.7 %

          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             : 
      22             : #include "log.h"
      23             : #include "thread.h"
      24             : #include "memory.h"
      25             : #include "if.h"
      26             : #include "network.h"
      27             : 
      28             : #include "pimd.h"
      29             : #include "pim_instance.h"
      30             : #include "pim_pim.h"
      31             : #include "pim_time.h"
      32             : #include "pim_iface.h"
      33             : #include "pim_sock.h"
      34             : #include "pim_str.h"
      35             : #include "pim_util.h"
      36             : #include "pim_tlv.h"
      37             : #include "pim_neighbor.h"
      38             : #include "pim_hello.h"
      39             : #include "pim_join.h"
      40             : #include "pim_assert.h"
      41             : #include "pim_msg.h"
      42             : #include "pim_register.h"
      43             : #include "pim_errors.h"
      44             : #include "pim_bsm.h"
      45             : #include <lib/lib_errors.h>
      46             : 
      47             : static void on_pim_hello_send(struct thread *t);
      48             : 
      49           0 : static const char *pim_pim_msgtype2str(enum pim_msg_type type)
      50             : {
      51           0 :         switch (type) {
      52             :         case PIM_MSG_TYPE_HELLO:
      53             :                 return "HELLO";
      54           0 :         case PIM_MSG_TYPE_REGISTER:
      55           0 :                 return "REGISTER";
      56           0 :         case PIM_MSG_TYPE_REG_STOP:
      57           0 :                 return "REGSTOP";
      58           0 :         case PIM_MSG_TYPE_JOIN_PRUNE:
      59           0 :                 return "JOINPRUNE";
      60           0 :         case PIM_MSG_TYPE_BOOTSTRAP:
      61           0 :                 return "BOOT";
      62           0 :         case PIM_MSG_TYPE_ASSERT:
      63           0 :                 return "ASSERT";
      64           0 :         case PIM_MSG_TYPE_GRAFT:
      65           0 :                 return "GRAFT";
      66           0 :         case PIM_MSG_TYPE_GRAFT_ACK:
      67           0 :                 return "GACK";
      68           0 :         case PIM_MSG_TYPE_CANDIDATE:
      69           0 :                 return "CANDIDATE";
      70             :         }
      71             : 
      72           0 :         return "UNKNOWN";
      73             : }
      74             : 
      75          10 : static void sock_close(struct interface *ifp)
      76             : {
      77          10 :         struct pim_interface *pim_ifp = ifp->info;
      78             : 
      79          10 :         if (PIM_DEBUG_PIM_TRACE) {
      80           0 :                 if (pim_ifp->t_pim_sock_read) {
      81           0 :                         zlog_debug(
      82             :                                 "Cancelling READ event for PIM socket fd=%d on interface %s",
      83             :                                 pim_ifp->pim_sock_fd, ifp->name);
      84             :                 }
      85             :         }
      86          10 :         THREAD_OFF(pim_ifp->t_pim_sock_read);
      87             : 
      88          10 :         if (PIM_DEBUG_PIM_TRACE) {
      89           0 :                 if (pim_ifp->t_pim_hello_timer) {
      90           0 :                         zlog_debug(
      91             :                                 "Cancelling PIM hello timer for interface %s",
      92             :                                 ifp->name);
      93             :                 }
      94             :         }
      95          10 :         THREAD_OFF(pim_ifp->t_pim_hello_timer);
      96             : 
      97          10 :         if (PIM_DEBUG_PIM_TRACE) {
      98           0 :                 zlog_debug("Deleting PIM socket fd=%d on interface %s",
      99             :                            pim_ifp->pim_sock_fd, ifp->name);
     100             :         }
     101             : 
     102             :         /*
     103             :          * If the fd is already deleted no need to do anything here
     104             :          */
     105          10 :         if (pim_ifp->pim_sock_fd > 0 && close(pim_ifp->pim_sock_fd)) {
     106           0 :                 zlog_warn(
     107             :                         "Failure closing PIM socket fd=%d on interface %s: errno=%d: %s",
     108             :                         pim_ifp->pim_sock_fd, ifp->name, errno,
     109             :                         safe_strerror(errno));
     110             :         }
     111             : 
     112          10 :         pim_ifp->pim_sock_fd = -1;
     113          10 :         pim_ifp->pim_sock_creation = 0;
     114          10 : }
     115             : 
     116          10 : void pim_sock_delete(struct interface *ifp, const char *delete_message)
     117             : {
     118          10 :         zlog_info("PIM INTERFACE DOWN: on interface %s: %s", ifp->name,
     119             :                   delete_message);
     120             : 
     121          10 :         if (!ifp->info) {
     122           0 :                 flog_err(EC_PIM_CONFIG,
     123             :                          "%s: %s: but PIM not enabled on interface %s (!)",
     124             :                          __func__, delete_message, ifp->name);
     125           0 :                 return;
     126             :         }
     127             : 
     128             :         /*
     129             :           RFC 4601: 4.3.1.  Sending Hello Messages
     130             : 
     131             :           Before an interface goes down or changes primary IP address, a Hello
     132             :           message with a zero HoldTime should be sent immediately (with the
     133             :           old IP address if the IP address changed).
     134             :         */
     135          10 :         pim_hello_send(ifp, 0 /* zero-sec holdtime */);
     136             : 
     137          10 :         pim_neighbor_delete_all(ifp, delete_message);
     138             : 
     139          10 :         sock_close(ifp);
     140             : }
     141             : 
     142             : /* For now check dst address for hello, assrt and join/prune is all pim rtr */
     143         116 : static bool pim_pkt_dst_addr_ok(enum pim_msg_type type, pim_addr addr)
     144             : {
     145         116 :         if ((type == PIM_MSG_TYPE_HELLO) || (type == PIM_MSG_TYPE_ASSERT)
     146             :             || (type == PIM_MSG_TYPE_JOIN_PRUNE)) {
     147         104 :                 if (pim_addr_cmp(addr, qpim_all_pim_routers_addr))
     148             :                         return false;
     149             :         }
     150             : 
     151             :         return true;
     152             : }
     153             : 
     154         116 : int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len,
     155             :                    pim_sgaddr sg)
     156             : {
     157         116 :         struct iovec iov[2], *iovp = iov;
     158             : #if PIM_IPV == 4
     159         116 :         struct ip *ip_hdr = (struct ip *)buf;
     160         116 :         size_t ip_hlen; /* ip header length in bytes */
     161             : #endif
     162         116 :         uint8_t *pim_msg;
     163         116 :         uint32_t pim_msg_len = 0;
     164         116 :         uint16_t pim_checksum; /* received checksum */
     165         116 :         uint16_t checksum;     /* computed checksum */
     166         116 :         struct pim_neighbor *neigh;
     167         116 :         struct pim_msg_header *header;
     168         116 :         bool   no_fwd;
     169             : 
     170             : #if PIM_IPV == 4
     171         116 :         if (len < sizeof(*ip_hdr)) {
     172           0 :                 if (PIM_DEBUG_PIM_PACKETS)
     173           0 :                         zlog_debug(
     174             :                                 "PIM packet size=%zu shorter than minimum=%zu",
     175             :                                 len, sizeof(*ip_hdr));
     176           0 :                 return -1;
     177             :         }
     178             : 
     179         116 :         ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */
     180         116 :         sg = pim_sgaddr_from_iphdr(ip_hdr);
     181             : 
     182         116 :         pim_msg = buf + ip_hlen;
     183         116 :         pim_msg_len = len - ip_hlen;
     184             : #else
     185             :         struct ipv6_ph phdr = {
     186             :                 .src = sg.src,
     187             :                 .dst = sg.grp,
     188             :                 .ulpl = htonl(len),
     189             :                 .next_hdr = IPPROTO_PIM,
     190             :         };
     191             : 
     192             :         iovp->iov_base = &phdr;
     193             :         iovp->iov_len = sizeof(phdr);
     194             :         iovp++;
     195             : 
     196             :         /* NB: header is not included in IPv6 RX */
     197             :         pim_msg = buf;
     198             :         pim_msg_len = len;
     199             : #endif
     200             : 
     201         116 :         iovp->iov_base = pim_msg;
     202         116 :         iovp->iov_len = pim_msg_len;
     203         116 :         iovp++;
     204             : 
     205         116 :         header = (struct pim_msg_header *)pim_msg;
     206         116 :         if (pim_msg_len < PIM_PIM_MIN_LEN) {
     207           0 :                 if (PIM_DEBUG_PIM_PACKETS)
     208           0 :                         zlog_debug(
     209             :                                 "PIM message size=%d shorter than minimum=%d",
     210             :                                 pim_msg_len, PIM_PIM_MIN_LEN);
     211           0 :                 return -1;
     212             :         }
     213             : 
     214         116 :         if (header->ver != PIM_PROTO_VERSION) {
     215           0 :                 if (PIM_DEBUG_PIM_PACKETS)
     216           0 :                         zlog_debug(
     217             :                                 "Ignoring PIM pkt from %s with unsupported version: %d",
     218             :                                 ifp->name, header->ver);
     219           0 :                 return -1;
     220             :         }
     221             : 
     222             :         /* save received checksum */
     223         116 :         pim_checksum = header->checksum;
     224             : 
     225             :         /* for computing checksum */
     226         116 :         header->checksum = 0;
     227         116 :         no_fwd = header->Nbit;
     228             : 
     229         116 :         if (header->type == PIM_MSG_TYPE_REGISTER) {
     230           0 :                 if (pim_msg_len < PIM_MSG_REGISTER_LEN) {
     231           0 :                         if (PIM_DEBUG_PIM_PACKETS)
     232           0 :                                 zlog_debug("PIM Register Message size=%d shorther than min length %d",
     233             :                                            pim_msg_len, PIM_MSG_REGISTER_LEN);
     234           0 :                         return -1;
     235             :                 }
     236             : 
     237             : #if PIM_IPV == 6
     238             :                 phdr.ulpl = htonl(PIM_MSG_REGISTER_LEN);
     239             : #endif
     240             :                 /* First 8 byte header checksum */
     241           0 :                 iovp[-1].iov_len = PIM_MSG_REGISTER_LEN;
     242           0 :                 checksum = in_cksumv(iov, iovp - iov);
     243             : 
     244           0 :                 if (checksum != pim_checksum) {
     245             : #if PIM_IPV == 6
     246             :                         phdr.ulpl = htonl(pim_msg_len);
     247             : #endif
     248           0 :                         iovp[-1].iov_len = pim_msg_len;
     249             : 
     250           0 :                         checksum = in_cksumv(iov, iovp - iov);
     251           0 :                         if (checksum != pim_checksum) {
     252           0 :                                 if (PIM_DEBUG_PIM_PACKETS)
     253           0 :                                         zlog_debug(
     254             :                                                 "Ignoring PIM pkt from %s with invalid checksum: received=%x calculated=%x",
     255             :                                                 ifp->name, pim_checksum,
     256             :                                                 checksum);
     257             : 
     258           0 :                                 return -1;
     259             :                         }
     260             :                 }
     261             :         } else {
     262         116 :                 checksum = in_cksumv(iov, iovp - iov);
     263         116 :                 if (checksum != pim_checksum) {
     264           0 :                         if (PIM_DEBUG_PIM_PACKETS)
     265           0 :                                 zlog_debug(
     266             :                                         "Ignoring PIM pkt from %s with invalid checksum: received=%x calculated=%x",
     267             :                                         ifp->name, pim_checksum, checksum);
     268             : 
     269           0 :                         return -1;
     270             :                 }
     271             :         }
     272             : 
     273         116 :         if (PIM_DEBUG_PIM_PACKETS) {
     274           0 :                 zlog_debug(
     275             :                         "Recv PIM %s packet from %pPA to %pPA on %s: pim_version=%d pim_msg_size=%d checksum=%x",
     276             :                         pim_pim_msgtype2str(header->type), &sg.src, &sg.grp,
     277             :                         ifp->name, header->ver, pim_msg_len, checksum);
     278           0 :                 if (PIM_DEBUG_PIM_PACKETDUMP_RECV)
     279           0 :                         pim_pkt_dump(__func__, pim_msg, pim_msg_len);
     280             :         }
     281             : 
     282         116 :         if (!pim_pkt_dst_addr_ok(header->type, sg.grp)) {
     283           0 :                 zlog_warn(
     284             :                         "%s: Ignoring Pkt. Unexpected IP destination %pPA for %s (Expected: all_pim_routers_addr) from %pPA",
     285             :                         __func__, &sg.grp, pim_pim_msgtype2str(header->type),
     286             :                         &sg.src);
     287           0 :                 return -1;
     288             :         }
     289             : 
     290         116 :         switch (header->type) {
     291         104 :         case PIM_MSG_TYPE_HELLO:
     292         104 :                 return pim_hello_recv(ifp, sg.src, pim_msg + PIM_MSG_HEADER_LEN,
     293         104 :                                       pim_msg_len - PIM_MSG_HEADER_LEN);
     294           0 :                 break;
     295           0 :         case PIM_MSG_TYPE_REGISTER:
     296           0 :                 return pim_register_recv(ifp, sg.grp, sg.src,
     297             :                                          pim_msg + PIM_MSG_HEADER_LEN,
     298           0 :                                          pim_msg_len - PIM_MSG_HEADER_LEN);
     299           0 :                 break;
     300           0 :         case PIM_MSG_TYPE_REG_STOP:
     301           0 :                 return pim_register_stop_recv(ifp, pim_msg + PIM_MSG_HEADER_LEN,
     302           0 :                                               pim_msg_len - PIM_MSG_HEADER_LEN);
     303           0 :                 break;
     304           0 :         case PIM_MSG_TYPE_JOIN_PRUNE:
     305           0 :                 neigh = pim_neighbor_find(ifp, sg.src);
     306           0 :                 if (!neigh) {
     307           0 :                         if (PIM_DEBUG_PIM_PACKETS)
     308           0 :                                 zlog_debug(
     309             :                                         "%s %s: non-hello PIM message type=%d from non-neighbor %pPA on %s",
     310             :                                         __FILE__, __func__, header->type,
     311             :                                         &sg.src, ifp->name);
     312           0 :                         return -1;
     313             :                 }
     314           0 :                 pim_neighbor_timer_reset(neigh, neigh->holdtime);
     315           0 :                 return pim_joinprune_recv(ifp, neigh, sg.src,
     316             :                                           pim_msg + PIM_MSG_HEADER_LEN,
     317           0 :                                           pim_msg_len - PIM_MSG_HEADER_LEN);
     318           0 :                 break;
     319           0 :         case PIM_MSG_TYPE_ASSERT:
     320           0 :                 neigh = pim_neighbor_find(ifp, sg.src);
     321           0 :                 if (!neigh) {
     322           0 :                         if (PIM_DEBUG_PIM_PACKETS)
     323           0 :                                 zlog_debug(
     324             :                                         "%s %s: non-hello PIM message type=%d from non-neighbor %pPA on %s",
     325             :                                         __FILE__, __func__, header->type,
     326             :                                         &sg.src, ifp->name);
     327           0 :                         return -1;
     328             :                 }
     329           0 :                 pim_neighbor_timer_reset(neigh, neigh->holdtime);
     330           0 :                 return pim_assert_recv(ifp, neigh, sg.src,
     331             :                                        pim_msg + PIM_MSG_HEADER_LEN,
     332           0 :                                        pim_msg_len - PIM_MSG_HEADER_LEN);
     333          12 :                 break;
     334          12 :         case PIM_MSG_TYPE_BOOTSTRAP:
     335          12 :                 return pim_bsm_process(ifp, &sg, pim_msg, pim_msg_len, no_fwd);
     336           0 :                 break;
     337             : 
     338           0 :         default:
     339           0 :                 if (PIM_DEBUG_PIM_PACKETS) {
     340           0 :                         zlog_debug(
     341             :                                 "Recv PIM packet type %d which is not currently understood",
     342             :                                 header->type);
     343             :                 }
     344             :                 return -1;
     345             :         }
     346             : }
     347             : 
     348             : static void pim_sock_read_on(struct interface *ifp);
     349             : 
     350          98 : static void pim_sock_read(struct thread *t)
     351             : {
     352          98 :         struct interface *ifp, *orig_ifp;
     353          98 :         struct pim_interface *pim_ifp;
     354          98 :         int fd;
     355          98 :         struct sockaddr_storage from;
     356          98 :         struct sockaddr_storage to;
     357          98 :         socklen_t fromlen = sizeof(from);
     358          98 :         socklen_t tolen = sizeof(to);
     359          98 :         uint8_t buf[PIM_PIM_BUFSIZE_READ];
     360          98 :         int len;
     361          98 :         ifindex_t ifindex = -1;
     362          98 :         int result = -1; /* defaults to bad */
     363          98 :         static long long count = 0;
     364          98 :         int cont = 1;
     365             : 
     366          98 :         orig_ifp = ifp = THREAD_ARG(t);
     367          98 :         fd = THREAD_FD(t);
     368             : 
     369          98 :         pim_ifp = ifp->info;
     370             : 
     371          98 :         while (cont) {
     372         171 :                 pim_sgaddr sg;
     373             : 
     374         171 :                 len = pim_socket_recvfromto(fd, buf, sizeof(buf), &from,
     375             :                                             &fromlen, &to, &tolen, &ifindex);
     376         171 :                 if (len < 0) {
     377          55 :                         if (errno == EINTR)
     378           0 :                                 continue;
     379          55 :                         if (errno == EWOULDBLOCK || errno == EAGAIN)
     380             :                                 break;
     381             : 
     382           0 :                         if (PIM_DEBUG_PIM_PACKETS)
     383           0 :                                 zlog_debug("Received errno: %d %s", errno,
     384             :                                            safe_strerror(errno));
     385           8 :                         goto done;
     386             :                 }
     387             : 
     388             :                 /*
     389             :                  * What?  So with vrf's the incoming packet is received
     390             :                  * on the vrf interface but recvfromto above returns
     391             :                  * the right ifindex, so just use it.  We know
     392             :                  * it's the right interface because we bind to it
     393             :                  */
     394         116 :                 ifp = if_lookup_by_index(ifindex, pim_ifp->pim->vrf->vrf_id);
     395         116 :                 if (!ifp || !ifp->info) {
     396           0 :                         if (PIM_DEBUG_PIM_PACKETS)
     397           0 :                                 zlog_debug(
     398             :                                         "%s: Received incoming pim packet on interface(%s:%d) not yet configured for pim",
     399             :                                         __func__, ifp ? ifp->name : "Unknown",
     400             :                                         ifindex);
     401           0 :                         goto done;
     402             :                 }
     403             : #if PIM_IPV == 4
     404         116 :                 sg.src = ((struct sockaddr_in *)&from)->sin_addr;
     405         116 :                 sg.grp = ((struct sockaddr_in *)&to)->sin_addr;
     406             : #else
     407             :                 sg.src = ((struct sockaddr_in6 *)&from)->sin6_addr;
     408             :                 sg.grp = ((struct sockaddr_in6 *)&to)->sin6_addr;
     409             : #endif
     410             : 
     411         116 :                 int fail = pim_pim_packet(ifp, buf, len, sg);
     412         116 :                 if (fail) {
     413           8 :                         if (PIM_DEBUG_PIM_PACKETS)
     414           0 :                                 zlog_debug("%s: pim_pim_packet() return=%d",
     415             :                                            __func__, fail);
     416           8 :                         goto done;
     417             :                 }
     418             : 
     419         108 :                 count++;
     420         108 :                 if (count % router->packet_process == 0)
     421          35 :                         cont = 0;
     422             :         }
     423             : 
     424             :         result = 0; /* good */
     425             : 
     426          98 : done:
     427          98 :         pim_sock_read_on(orig_ifp);
     428             : 
     429          98 :         if (result) {
     430           8 :                 ++pim_ifp->pim_ifstat_hello_recvfail;
     431             :         }
     432          98 : }
     433             : 
     434         106 : static void pim_sock_read_on(struct interface *ifp)
     435             : {
     436         106 :         struct pim_interface *pim_ifp;
     437             : 
     438         106 :         assert(ifp);
     439         106 :         assert(ifp->info);
     440             : 
     441         106 :         pim_ifp = ifp->info;
     442             : 
     443         106 :         if (PIM_DEBUG_PIM_TRACE_DETAIL) {
     444           0 :                 zlog_debug("Scheduling READ event on PIM socket fd=%d",
     445             :                            pim_ifp->pim_sock_fd);
     446             :         }
     447         106 :         thread_add_read(router->master, pim_sock_read, ifp,
     448             :                         pim_ifp->pim_sock_fd, &pim_ifp->t_pim_sock_read);
     449         106 : }
     450             : 
     451           8 : static int pim_sock_open(struct interface *ifp)
     452             : {
     453           8 :         int fd;
     454           8 :         struct pim_interface *pim_ifp = ifp->info;
     455             : 
     456           8 :         fd = pim_socket_mcast(IPPROTO_PIM, pim_ifp->primary_address, ifp,
     457             :                               0 /* loop=false */);
     458           8 :         if (fd < 0)
     459             :                 return -1;
     460             : 
     461           8 :         if (pim_socket_join(fd, qpim_all_pim_routers_addr,
     462             :                             pim_ifp->primary_address, ifp->ifindex, pim_ifp)) {
     463           0 :                 close(fd);
     464           0 :                 return -2;
     465             :         }
     466             : 
     467             :         return fd;
     468             : }
     469             : 
     470          11 : void pim_ifstat_reset(struct interface *ifp)
     471             : {
     472          11 :         struct pim_interface *pim_ifp;
     473             : 
     474          11 :         assert(ifp);
     475             : 
     476          11 :         pim_ifp = ifp->info;
     477          11 :         if (!pim_ifp) {
     478             :                 return;
     479             :         }
     480             : 
     481          11 :         pim_ifp->pim_ifstat_start = pim_time_monotonic_sec();
     482          11 :         pim_ifp->pim_ifstat_hello_sent = 0;
     483          11 :         pim_ifp->pim_ifstat_hello_sendfail = 0;
     484          11 :         pim_ifp->pim_ifstat_hello_recv = 0;
     485          11 :         pim_ifp->pim_ifstat_hello_recvfail = 0;
     486          11 :         pim_ifp->pim_ifstat_bsm_rx = 0;
     487          11 :         pim_ifp->pim_ifstat_bsm_tx = 0;
     488          11 :         pim_ifp->pim_ifstat_join_recv = 0;
     489          11 :         pim_ifp->pim_ifstat_join_send = 0;
     490          11 :         pim_ifp->pim_ifstat_prune_recv = 0;
     491          11 :         pim_ifp->pim_ifstat_prune_send = 0;
     492          11 :         pim_ifp->pim_ifstat_reg_recv = 0;
     493          11 :         pim_ifp->pim_ifstat_reg_send = 0;
     494          11 :         pim_ifp->pim_ifstat_reg_stop_recv = 0;
     495          11 :         pim_ifp->pim_ifstat_reg_stop_send = 0;
     496          11 :         pim_ifp->pim_ifstat_assert_recv = 0;
     497          11 :         pim_ifp->pim_ifstat_assert_send = 0;
     498          11 :         pim_ifp->pim_ifstat_bsm_cfg_miss = 0;
     499          11 :         pim_ifp->pim_ifstat_ucast_bsm_cfg_miss = 0;
     500          11 :         pim_ifp->pim_ifstat_bsm_invalid_sz = 0;
     501          11 :         pim_ifp->igmp_ifstat_joins_sent = 0;
     502          11 :         pim_ifp->igmp_ifstat_joins_failed = 0;
     503          11 :         pim_ifp->igmp_peak_group_count = 0;
     504             : }
     505             : 
     506           9 : void pim_sock_reset(struct interface *ifp)
     507             : {
     508           9 :         struct pim_interface *pim_ifp;
     509             : 
     510           9 :         assert(ifp);
     511           9 :         assert(ifp->info);
     512             : 
     513           9 :         pim_ifp = ifp->info;
     514             : 
     515           9 :         pim_ifp->primary_address = pim_find_primary_addr(ifp);
     516             : 
     517           9 :         pim_ifp->pim_sock_fd = -1;
     518           9 :         pim_ifp->pim_sock_creation = 0;
     519           9 :         pim_ifp->t_pim_sock_read = NULL;
     520             : 
     521           9 :         pim_ifp->t_pim_hello_timer = NULL;
     522           9 :         pim_ifp->pim_hello_period = PIM_DEFAULT_HELLO_PERIOD;
     523           9 :         pim_ifp->pim_default_holdtime =
     524             :                 -1; /* unset: means 3.5 * pim_hello_period */
     525           9 :         pim_ifp->pim_triggered_hello_delay = PIM_DEFAULT_TRIGGERED_HELLO_DELAY;
     526           9 :         pim_ifp->pim_dr_priority = PIM_DEFAULT_DR_PRIORITY;
     527           9 :         pim_ifp->pim_propagation_delay_msec =
     528             :                 PIM_DEFAULT_PROPAGATION_DELAY_MSEC;
     529           9 :         pim_ifp->pim_override_interval_msec =
     530             :                 PIM_DEFAULT_OVERRIDE_INTERVAL_MSEC;
     531           9 :         pim_ifp->pim_can_disable_join_suppression =
     532             :                 PIM_DEFAULT_CAN_DISABLE_JOIN_SUPPRESSION;
     533             : 
     534             :         /* neighbors without lan_delay */
     535           9 :         pim_ifp->pim_number_of_nonlandelay_neighbors = 0;
     536           9 :         pim_ifp->pim_neighbors_highest_propagation_delay_msec = 0;
     537           9 :         pim_ifp->pim_neighbors_highest_override_interval_msec = 0;
     538             : 
     539             :         /* DR Election */
     540           9 :         pim_ifp->pim_dr_election_last = 0; /* timestamp */
     541           9 :         pim_ifp->pim_dr_election_count = 0;
     542           9 :         pim_ifp->pim_dr_election_changes = 0;
     543           9 :         pim_ifp->pim_dr_num_nondrpri_neighbors =
     544             :                 0; /* neighbors without dr_pri */
     545           9 :         pim_ifp->pim_dr_addr = pim_ifp->primary_address;
     546           9 :         pim_ifp->am_i_dr = true;
     547             : 
     548           9 :         pim_ifstat_reset(ifp);
     549           9 : }
     550             : 
     551             : #if PIM_IPV == 4
     552             : static uint16_t ip_id = 0;
     553             : #endif
     554             : 
     555             : #if PIM_IPV == 4
     556          78 : static int pim_msg_send_frame(int fd, char *buf, size_t len,
     557             :                               struct sockaddr *dst, size_t salen,
     558             :                               const char *ifname)
     559             : {
     560          78 :         if (sendto(fd, buf, len, MSG_DONTWAIT, dst, salen) >= 0)
     561             :                 return 0;
     562             : 
     563           9 :         if (errno == EMSGSIZE) {
     564           0 :                 struct ip *ip = (struct ip *)buf;
     565           0 :                 size_t hdrsize = sizeof(struct ip);
     566           0 :                 size_t newlen1 = ((len - hdrsize) / 2) & 0xFFF8;
     567           0 :                 size_t sendlen = newlen1 + hdrsize;
     568           0 :                 size_t offset = ntohs(ip->ip_off);
     569           0 :                 int ret;
     570             : 
     571           0 :                 ip->ip_len = htons(sendlen);
     572           0 :                 ip->ip_off = htons(offset | IP_MF);
     573             : 
     574           0 :                 ret = pim_msg_send_frame(fd, buf, sendlen, dst, salen, ifname);
     575           0 :                 if (ret)
     576             :                         return ret;
     577             : 
     578           0 :                 struct ip *ip2 = (struct ip *)(buf + newlen1);
     579           0 :                 size_t newlen2 = len - sendlen;
     580             : 
     581           0 :                 sendlen = newlen2 + hdrsize;
     582             : 
     583           0 :                 memcpy(ip2, ip, hdrsize);
     584           0 :                 ip2->ip_len = htons(sendlen);
     585           0 :                 ip2->ip_off = htons(offset + (newlen1 >> 3));
     586           0 :                 return pim_msg_send_frame(fd, (char *)ip2, sendlen, dst, salen,
     587             :                                           ifname);
     588             :         }
     589             : 
     590           9 :         zlog_warn(
     591             :                 "%s: sendto() failure to %pSU: iface=%s fd=%d msg_size=%zd: %m",
     592             :                 __func__, dst, ifname, fd, len);
     593           9 :         return -1;
     594             : }
     595             : 
     596             : #else
     597             : static int pim_msg_send_frame(pim_addr src, pim_addr dst, ifindex_t ifindex,
     598             :                               struct iovec *message, int fd)
     599             : {
     600             :         int retval;
     601             :         struct msghdr smsghdr = {};
     602             :         struct cmsghdr *scmsgp;
     603             :         union cmsgbuf {
     604             :                 struct cmsghdr hdr;
     605             :                 uint8_t buf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
     606             :         };
     607             :         struct in6_pktinfo *pktinfo;
     608             :         struct sockaddr_in6 dst_sin6 = {};
     609             : 
     610             :         union cmsgbuf cmsg_buf = {};
     611             : 
     612             :         /* destination address */
     613             :         dst_sin6.sin6_family = AF_INET6;
     614             : #ifdef SIN6_LEN
     615             :         dst_sin6.sin6_len = sizeof(struct sockaddr_in6);
     616             : #endif /*SIN6_LEN*/
     617             :         dst_sin6.sin6_addr = dst;
     618             :         dst_sin6.sin6_scope_id = ifindex;
     619             : 
     620             :         /* send msg hdr */
     621             :         smsghdr.msg_iov = message;
     622             :         smsghdr.msg_iovlen = 1;
     623             :         smsghdr.msg_name = (caddr_t)&dst_sin6;
     624             :         smsghdr.msg_namelen = sizeof(dst_sin6);
     625             :         smsghdr.msg_control = (caddr_t)&cmsg_buf.buf;
     626             :         smsghdr.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
     627             :         smsghdr.msg_flags = 0;
     628             : 
     629             :         scmsgp = CMSG_FIRSTHDR(&smsghdr);
     630             :         scmsgp->cmsg_level = IPPROTO_IPV6;
     631             :         scmsgp->cmsg_type = IPV6_PKTINFO;
     632             :         scmsgp->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
     633             : 
     634             :         pktinfo = (struct in6_pktinfo *)(CMSG_DATA(scmsgp));
     635             :         pktinfo->ipi6_ifindex = ifindex;
     636             :         pktinfo->ipi6_addr = src;
     637             : 
     638             :         retval = sendmsg(fd, &smsghdr, 0);
     639             :         if (retval < 0)
     640             :                 flog_err(
     641             :                         EC_LIB_SOCKET,
     642             :                         "sendmsg failed: source: %pI6 Dest: %pI6 ifindex: %d: %s (%d)",
     643             :                         &src, &dst, ifindex, safe_strerror(errno), errno);
     644             : 
     645             :         return retval;
     646             : }
     647             : #endif
     648             : 
     649          78 : int pim_msg_send(int fd, pim_addr src, pim_addr dst, uint8_t *pim_msg,
     650             :                  int pim_msg_size, struct interface *ifp)
     651             : {
     652          78 :         struct pim_interface *pim_ifp;
     653             : 
     654             : 
     655          78 :         pim_ifp = ifp->info;
     656             : 
     657          78 :         if (pim_ifp->pim_passive_enable) {
     658           0 :                 if (PIM_DEBUG_PIM_PACKETS)
     659           0 :                         zlog_debug(
     660             :                                 "skip sending PIM message on passive interface %s",
     661             :                                 ifp->name);
     662           0 :                 return 0;
     663             :         }
     664             : 
     665             : #if PIM_IPV == 4
     666          78 :         uint8_t ttl;
     667          78 :         struct pim_msg_header *header;
     668          78 :         unsigned char buffer[10000];
     669             : 
     670          78 :         memset(buffer, 0, 10000);
     671             : 
     672          78 :         header = (struct pim_msg_header *)pim_msg;
     673             : 
     674             : /*
     675             :  * Omnios apparently doesn't have a #define for IP default
     676             :  * ttl that is the same as all other platforms.
     677             :  */
     678             : #ifndef IPDEFTTL
     679             : #define IPDEFTTL   64
     680             : #endif
     681             :         /* TTL for packets destine to ALL-PIM-ROUTERS is 1 */
     682          78 :         switch (header->type) {
     683             :         case PIM_MSG_TYPE_HELLO:
     684             :         case PIM_MSG_TYPE_JOIN_PRUNE:
     685             :         case PIM_MSG_TYPE_BOOTSTRAP:
     686             :         case PIM_MSG_TYPE_ASSERT:
     687             :                 ttl = 1;
     688             :                 break;
     689           0 :         case PIM_MSG_TYPE_REGISTER:
     690             :         case PIM_MSG_TYPE_REG_STOP:
     691             :         case PIM_MSG_TYPE_GRAFT:
     692             :         case PIM_MSG_TYPE_GRAFT_ACK:
     693             :         case PIM_MSG_TYPE_CANDIDATE:
     694           0 :                 ttl = IPDEFTTL;
     695           0 :                 break;
     696           0 :         default:
     697           0 :                 ttl = MAXTTL;
     698           0 :                 break;
     699             :         }
     700             : 
     701          78 :         struct ip *ip = (struct ip *)buffer;
     702          78 :         struct sockaddr_in to = {};
     703          78 :         int sendlen = sizeof(*ip) + pim_msg_size;
     704          78 :         socklen_t tolen;
     705          78 :         unsigned char *msg_start;
     706             : 
     707          78 :         ip->ip_id = htons(++ip_id);
     708          78 :         ip->ip_hl = 5;
     709          78 :         ip->ip_v = 4;
     710          78 :         ip->ip_tos = IPTOS_PREC_INTERNETCONTROL;
     711          78 :         ip->ip_p = PIM_IP_PROTO_PIM;
     712          78 :         ip->ip_src = src;
     713          78 :         ip->ip_dst = dst;
     714          78 :         ip->ip_ttl = ttl;
     715          78 :         ip->ip_len = htons(sendlen);
     716             : 
     717          78 :         to.sin_family = AF_INET;
     718          78 :         to.sin_addr = dst;
     719          78 :         tolen = sizeof(to);
     720             : 
     721          78 :         msg_start = buffer + sizeof(*ip);
     722          78 :         memcpy(msg_start, pim_msg, pim_msg_size);
     723             : 
     724          78 :         if (PIM_DEBUG_PIM_PACKETS)
     725           0 :                 zlog_debug("%s: to %pPA on %s: msg_size=%d checksum=%x",
     726             :                            __func__, &dst, ifp->name, pim_msg_size,
     727             :                            header->checksum);
     728             : 
     729          78 :         if (PIM_DEBUG_PIM_PACKETDUMP_SEND) {
     730           0 :                 pim_pkt_dump(__func__, pim_msg, pim_msg_size);
     731             :         }
     732             : 
     733          78 :         pim_msg_send_frame(fd, (char *)buffer, sendlen, (struct sockaddr *)&to,
     734          78 :                            tolen, ifp->name);
     735          78 :         return 0;
     736             : 
     737             : #else
     738             :         struct iovec iovector[2];
     739             : 
     740             :         iovector[0].iov_base = pim_msg;
     741             :         iovector[0].iov_len = pim_msg_size;
     742             : 
     743             :         pim_msg_send_frame(src, dst, ifp->ifindex, &iovector[0], fd);
     744             : 
     745             :         return 0;
     746             : #endif
     747             : }
     748             : 
     749          74 : static int hello_send(struct interface *ifp, uint16_t holdtime)
     750             : {
     751          74 :         uint8_t pim_msg[PIM_PIM_BUFSIZE_WRITE];
     752          74 :         struct pim_interface *pim_ifp;
     753          74 :         int pim_tlv_size;
     754          74 :         int pim_msg_size;
     755             : 
     756          74 :         pim_ifp = ifp->info;
     757             : 
     758          74 :         if (PIM_DEBUG_PIM_HELLO)
     759           0 :                 zlog_debug(
     760             :                         "%s: to %pPA on %s: holdt=%u prop_d=%u overr_i=%u dis_join_supp=%d dr_prio=%u gen_id=%08x addrs=%d",
     761             :                         __func__, &qpim_all_pim_routers_addr, ifp->name,
     762             :                         holdtime, pim_ifp->pim_propagation_delay_msec,
     763             :                         pim_ifp->pim_override_interval_msec,
     764             :                         pim_ifp->pim_can_disable_join_suppression,
     765             :                         pim_ifp->pim_dr_priority, pim_ifp->pim_generation_id,
     766             :                         listcount(ifp->connected));
     767             : 
     768         148 :         pim_tlv_size = pim_hello_build_tlv(
     769             :                 ifp, pim_msg + PIM_PIM_MIN_LEN,
     770             :                 sizeof(pim_msg) - PIM_PIM_MIN_LEN, holdtime,
     771             :                 pim_ifp->pim_dr_priority, pim_ifp->pim_generation_id,
     772          74 :                 pim_ifp->pim_propagation_delay_msec,
     773          74 :                 pim_ifp->pim_override_interval_msec,
     774          74 :                 pim_ifp->pim_can_disable_join_suppression);
     775          74 :         if (pim_tlv_size < 0) {
     776             :                 return -1;
     777             :         }
     778             : 
     779          74 :         pim_msg_size = pim_tlv_size + PIM_PIM_MIN_LEN;
     780             : 
     781          74 :         assert(pim_msg_size >= PIM_PIM_MIN_LEN);
     782          74 :         assert(pim_msg_size <= PIM_PIM_BUFSIZE_WRITE);
     783             : 
     784          74 :         pim_msg_build_header(pim_ifp->primary_address,
     785             :                              qpim_all_pim_routers_addr, pim_msg, pim_msg_size,
     786             :                              PIM_MSG_TYPE_HELLO, false);
     787             : 
     788          74 :         if (pim_msg_send(pim_ifp->pim_sock_fd, pim_ifp->primary_address,
     789             :                          qpim_all_pim_routers_addr, pim_msg, pim_msg_size,
     790             :                          ifp)) {
     791           0 :                 if (PIM_DEBUG_PIM_HELLO) {
     792           0 :                         zlog_debug(
     793             :                                 "%s: could not send PIM message on interface %s",
     794             :                                 __func__, ifp->name);
     795             :                 }
     796           0 :                 return -2;
     797             :         }
     798             : 
     799             :         return 0;
     800             : }
     801             : 
     802          80 : int pim_hello_send(struct interface *ifp, uint16_t holdtime)
     803             : {
     804          80 :         struct pim_interface *pim_ifp = ifp->info;
     805             : 
     806          80 :         if (if_is_loopback(ifp))
     807             :                 return 0;
     808             : 
     809          74 :         if (hello_send(ifp, holdtime)) {
     810           0 :                 ++pim_ifp->pim_ifstat_hello_sendfail;
     811             : 
     812           0 :                 if (PIM_DEBUG_PIM_HELLO) {
     813           0 :                         zlog_warn("Could not send PIM hello on interface %s",
     814             :                                   ifp->name);
     815             :                 }
     816           0 :                 return -1;
     817             :         }
     818             : 
     819          74 :         if (!pim_ifp->pim_passive_enable) {
     820          74 :                 ++pim_ifp->pim_ifstat_hello_sent;
     821          74 :                 PIM_IF_FLAG_SET_HELLO_SENT(pim_ifp->flags);
     822             :         }
     823             : 
     824             :         return 0;
     825             : }
     826             : 
     827          58 : static void hello_resched(struct interface *ifp)
     828             : {
     829          58 :         struct pim_interface *pim_ifp;
     830             : 
     831          58 :         pim_ifp = ifp->info;
     832             : 
     833          58 :         if (PIM_DEBUG_PIM_HELLO) {
     834           0 :                 zlog_debug("Rescheduling %d sec hello on interface %s",
     835             :                            pim_ifp->pim_hello_period, ifp->name);
     836             :         }
     837          58 :         THREAD_OFF(pim_ifp->t_pim_hello_timer);
     838          58 :         thread_add_timer(router->master, on_pim_hello_send, ifp,
     839             :                          pim_ifp->pim_hello_period,
     840             :                          &pim_ifp->t_pim_hello_timer);
     841          58 : }
     842             : 
     843             : /*
     844             :   Periodic hello timer
     845             :  */
     846          56 : static void on_pim_hello_send(struct thread *t)
     847             : {
     848          56 :         struct pim_interface *pim_ifp;
     849          56 :         struct interface *ifp;
     850             : 
     851          56 :         ifp = THREAD_ARG(t);
     852          56 :         pim_ifp = ifp->info;
     853             : 
     854             :         /*
     855             :          * Schedule next hello
     856             :          */
     857          56 :         hello_resched(ifp);
     858             : 
     859             :         /*
     860             :          * Send hello
     861             :          */
     862         112 :         pim_hello_send(ifp, PIM_IF_DEFAULT_HOLDTIME(pim_ifp));
     863          56 : }
     864             : 
     865             : /*
     866             :   RFC 4601: 4.3.1.  Sending Hello Messages
     867             : 
     868             :   Thus, if a router needs to send a Join/Prune or Assert message on an
     869             :   interface on which it has not yet sent a Hello message with the
     870             :   currently configured IP address, then it MUST immediately send the
     871             :   relevant Hello message without waiting for the Hello Timer to
     872             :   expire, followed by the Join/Prune or Assert message.
     873             :  */
     874           2 : void pim_hello_restart_now(struct interface *ifp)
     875             : {
     876           2 :         struct pim_interface *pim_ifp;
     877             : 
     878           2 :         pim_ifp = ifp->info;
     879             : 
     880             :         /*
     881             :          * Reset next hello timer
     882             :          */
     883           2 :         hello_resched(ifp);
     884             : 
     885             :         /*
     886             :          * Immediately send hello
     887             :          */
     888           4 :         pim_hello_send(ifp, PIM_IF_DEFAULT_HOLDTIME(pim_ifp));
     889           2 : }
     890             : 
     891             : /*
     892             :   RFC 4601: 4.3.1.  Sending Hello Messages
     893             : 
     894             :   To allow new or rebooting routers to learn of PIM neighbors quickly,
     895             :   when a Hello message is received from a new neighbor, or a Hello
     896             :   message with a new GenID is received from an existing neighbor, a
     897             :   new Hello message should be sent on this interface after a
     898             :   randomized delay between 0 and Triggered_Hello_Delay.
     899             :  */
     900          14 : void pim_hello_restart_triggered(struct interface *ifp)
     901             : {
     902          14 :         struct pim_interface *pim_ifp;
     903          14 :         int triggered_hello_delay_msec;
     904          14 :         int random_msec;
     905             : 
     906          14 :         pim_ifp = ifp->info;
     907             : 
     908             :         /*
     909             :          * No need to ever start loopback or vrf device hello's
     910             :          */
     911          14 :         if (if_is_loopback(ifp))
     912             :                 return;
     913             : 
     914             :         /*
     915             :          * There exists situations where we have the a RPF out this
     916             :          * interface, but we haven't formed a neighbor yet.  This
     917             :          * happens especially during interface flaps.  While
     918             :          * we would like to handle this more gracefully in other
     919             :          * parts of the code.  In order to get us up and running
     920             :          * let's just send the hello immediate'ish
     921             :          * This should be revisited when we get nexthop tracking
     922             :          * in and when we have a better handle on safely
     923             :          * handling the rpf information for upstreams that
     924             :          * we cannot legally reach yet.
     925             :          */
     926          11 :         triggered_hello_delay_msec = 1;
     927             :         // triggered_hello_delay_msec = 1000 *
     928             :         // pim_ifp->pim_triggered_hello_delay;
     929             : 
     930          11 :         if (pim_ifp->t_pim_hello_timer) {
     931           6 :                 long remain_msec =
     932           6 :                         pim_time_timer_remain_msec(pim_ifp->t_pim_hello_timer);
     933           6 :                 if (remain_msec <= triggered_hello_delay_msec) {
     934             :                         /* Rescheduling hello would increase the delay, then
     935             :                            it's faster
     936             :                            to just wait for the scheduled periodic hello. */
     937             :                         return;
     938             :                 }
     939             : 
     940           5 :                 THREAD_OFF(pim_ifp->t_pim_hello_timer);
     941             :         }
     942             : 
     943          10 :         random_msec = triggered_hello_delay_msec;
     944             :         // random_msec = random() % (triggered_hello_delay_msec + 1);
     945             : 
     946          10 :         if (PIM_DEBUG_PIM_HELLO) {
     947           0 :                 zlog_debug("Scheduling %d msec triggered hello on interface %s",
     948             :                            random_msec, ifp->name);
     949             :         }
     950             : 
     951          10 :         thread_add_timer_msec(router->master, on_pim_hello_send, ifp,
     952             :                               random_msec, &pim_ifp->t_pim_hello_timer);
     953             : }
     954             : 
     955           8 : int pim_sock_add(struct interface *ifp)
     956             : {
     957           8 :         struct pim_interface *pim_ifp;
     958           8 :         uint32_t old_genid;
     959             : 
     960           8 :         pim_ifp = ifp->info;
     961           8 :         assert(pim_ifp);
     962             : 
     963           8 :         if (pim_ifp->pim_sock_fd >= 0) {
     964           0 :                 if (PIM_DEBUG_PIM_PACKETS)
     965           0 :                         zlog_debug(
     966             :                                 "Can't recreate existing PIM socket fd=%d for interface %s",
     967             :                                 pim_ifp->pim_sock_fd, ifp->name);
     968           0 :                 return -1;
     969             :         }
     970             : 
     971           8 :         pim_ifp->pim_sock_fd = pim_sock_open(ifp);
     972           8 :         if (pim_ifp->pim_sock_fd < 0) {
     973           0 :                 if (PIM_DEBUG_PIM_PACKETS)
     974           0 :                         zlog_debug("Could not open PIM socket on interface %s",
     975             :                                    ifp->name);
     976           0 :                 return -2;
     977             :         }
     978             : 
     979           8 :         pim_socket_ip_hdr(pim_ifp->pim_sock_fd);
     980             : 
     981           8 :         pim_ifp->t_pim_sock_read = NULL;
     982           8 :         pim_ifp->pim_sock_creation = pim_time_monotonic_sec();
     983             : 
     984             :         /*
     985             :          * Just ensure that the new generation id
     986             :          * actually chooses something different.
     987             :          * Actually ran across a case where this
     988             :          * happened, pre-switch to random().
     989             :          * While this is unlikely to happen now
     990             :          * let's make sure it doesn't.
     991             :          */
     992           8 :         old_genid = pim_ifp->pim_generation_id;
     993             : 
     994          16 :         while (old_genid == pim_ifp->pim_generation_id)
     995           8 :                 pim_ifp->pim_generation_id = frr_weak_random();
     996             : 
     997           8 :         zlog_info("PIM INTERFACE UP: on interface %s ifindex=%d", ifp->name,
     998             :                   ifp->ifindex);
     999             : 
    1000             :         /*
    1001             :          * Start receiving PIM messages
    1002             :          */
    1003           8 :         pim_sock_read_on(ifp);
    1004             : 
    1005             :         /*
    1006             :          * Start sending PIM hello's
    1007             :          */
    1008           8 :         pim_hello_restart_triggered(ifp);
    1009             : 
    1010           8 :         return 0;
    1011             : }

Generated by: LCOV version v1.16-topotato