back to topotato report
topotato coverage report
Current view: top level - pimd - pim_ssmpingd.c (source / functions) Hit Total Coverage
Test: test_pim_crp.py::PIMCandidateBSRTest Lines: 10 148 6.8 %
Date: 2023-02-16 02:09:37 Functions: 2 14 14.3 %

          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 "if.h"
      23             : #include "log.h"
      24             : #include "memory.h"
      25             : #include "sockopt.h"
      26             : #include "vrf.h"
      27             : #include "lib_errors.h"
      28             : 
      29             : #include "pimd.h"
      30             : #include "pim_instance.h"
      31             : #include "pim_ssmpingd.h"
      32             : #include "pim_time.h"
      33             : #include "pim_sock.h"
      34             : #include "network.h"
      35             : 
      36             : #if PIM_IPV == 4
      37             : static const char *const PIM_SSMPINGD_REPLY_GROUP = "232.43.211.234";
      38             : #else
      39             : static const char *const PIM_SSMPINGD_REPLY_GROUP = "ff3e::4321:1234";
      40             : #endif
      41             : 
      42             : enum { PIM_SSMPINGD_REQUEST = 'Q', PIM_SSMPINGD_REPLY = 'A' };
      43             : 
      44             : static void ssmpingd_read_on(struct ssmpingd_sock *ss);
      45             : 
      46           3 : void pim_ssmpingd_init(struct pim_instance *pim)
      47             : {
      48           3 :         int result;
      49             : 
      50           3 :         assert(!pim->ssmpingd_list);
      51             : 
      52           6 :         result = inet_pton(PIM_AF, PIM_SSMPINGD_REPLY_GROUP,
      53           3 :                            &pim->ssmpingd_group_addr);
      54             : 
      55           3 :         assert(result > 0);
      56           3 : }
      57             : 
      58           3 : void pim_ssmpingd_destroy(struct pim_instance *pim)
      59             : {
      60           3 :         if (pim->ssmpingd_list)
      61           0 :                 list_delete(&pim->ssmpingd_list);
      62           3 : }
      63             : 
      64           0 : static struct ssmpingd_sock *ssmpingd_find(struct pim_instance *pim,
      65             :                                            pim_addr source_addr)
      66             : {
      67           0 :         struct listnode *node;
      68           0 :         struct ssmpingd_sock *ss;
      69             : 
      70           0 :         if (!pim->ssmpingd_list)
      71             :                 return 0;
      72             : 
      73           0 :         for (ALL_LIST_ELEMENTS_RO(pim->ssmpingd_list, node, ss))
      74           0 :                 if (!pim_addr_cmp(source_addr, ss->source_addr))
      75           0 :                         return ss;
      76             : 
      77             :         return 0;
      78             : }
      79             : 
      80           0 : static void ssmpingd_free(struct ssmpingd_sock *ss)
      81             : {
      82           0 :         XFREE(MTYPE_PIM_SSMPINGD, ss);
      83           0 : }
      84             : 
      85             : #if PIM_IPV == 4
      86           0 : static inline int ssmpingd_setsockopt(int fd, pim_addr addr, int mttl)
      87             : {
      88             :         /* Needed to obtain destination address from recvmsg() */
      89             : #if defined(HAVE_IP_PKTINFO)
      90             :         /* Linux and Solaris IP_PKTINFO */
      91           0 :         int opt = 1;
      92           0 :         if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt))) {
      93           0 :                 zlog_warn(
      94             :                         "%s: could not set IP_PKTINFO on socket fd=%d: errno=%d: %s",
      95             :                         __func__, fd, errno, safe_strerror(errno));
      96             :         }
      97             : #elif defined(HAVE_IP_RECVDSTADDR)
      98             :         /* BSD IP_RECVDSTADDR */
      99             :         int opt = 1;
     100             :         if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt))) {
     101             :                 zlog_warn(
     102             :                         "%s: could not set IP_RECVDSTADDR on socket fd=%d: errno=%d: %s",
     103             :                         __func__, fd, errno, safe_strerror(errno));
     104             :         }
     105             : #else
     106             :         flog_err(
     107             :                 EC_LIB_DEVELOPMENT,
     108             :                 "%s %s: missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()",
     109             :                 __FILE__, __func__);
     110             :         close(fd);
     111             :         return -1;
     112             : #endif
     113             : 
     114           0 :         if (setsockopt_ipv4_multicast_loop(fd, 0)) {
     115           0 :                 zlog_warn(
     116             :                         "%s: could not disable Multicast Loopback Option on socket fd=%d: errno=%d: %s",
     117             :                         __func__, fd, errno, safe_strerror(errno));
     118           0 :                 close(fd);
     119           0 :                 return PIM_SOCK_ERR_LOOP;
     120             :         }
     121             : 
     122           0 :         if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, (void *)&addr,
     123             :                        sizeof(addr))) {
     124           0 :                 zlog_warn(
     125             :                         "%s: could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s",
     126             :                         __func__, fd, errno, safe_strerror(errno));
     127           0 :                 close(fd);
     128           0 :                 return -1;
     129             :         }
     130             : 
     131           0 :         if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&mttl,
     132             :                        sizeof(mttl))) {
     133           0 :                 zlog_warn(
     134             :                         "%s: could not set multicast TTL=%d on socket fd=%d: errno=%d: %s",
     135             :                         __func__, mttl, fd, errno, safe_strerror(errno));
     136           0 :                 close(fd);
     137           0 :                 return -1;
     138             :         }
     139             : 
     140             :         return 0;
     141             : }
     142             : #else
     143             : static inline int ssmpingd_setsockopt(int fd, pim_addr addr, int mttl)
     144             : {
     145             :         setsockopt_ipv6_pktinfo(fd, 1);
     146             :         setsockopt_ipv6_multicast_hops(fd, mttl);
     147             : 
     148             :         if (setsockopt_ipv6_multicast_loop(fd, 0)) {
     149             :                 zlog_warn(
     150             :                         "%s: could not disable Multicast Loopback Option on socket fd=%d: errno=%d: %s",
     151             :                         __func__, fd, errno, safe_strerror(errno));
     152             :                 close(fd);
     153             :                 return PIM_SOCK_ERR_LOOP;
     154             :         }
     155             : 
     156             :         if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, (void *)&addr,
     157             :                        sizeof(addr))) {
     158             :                 zlog_warn(
     159             :                         "%s: could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s",
     160             :                         __func__, fd, errno, safe_strerror(errno));
     161             :                 close(fd);
     162             :                 return -1;
     163             :         }
     164             :         return 0;
     165             : }
     166             : #endif
     167             : 
     168             : 
     169           0 : static int ssmpingd_socket(pim_addr addr, int port, int mttl)
     170             : {
     171           0 :         struct sockaddr_storage sockaddr;
     172           0 :         int fd;
     173           0 :         int ret;
     174           0 :         socklen_t len = sizeof(sockaddr);
     175             : 
     176           0 :         fd = socket(PIM_AF, SOCK_DGRAM, IPPROTO_UDP);
     177           0 :         if (fd < 0) {
     178           0 :                 flog_err_sys(EC_LIB_SOCKET,
     179             :                              "%s: could not create socket: errno=%d: %s",
     180             :                              __func__, errno, safe_strerror(errno));
     181           0 :                 return -1;
     182             :         }
     183             : 
     184           0 :         pim_socket_getsockname(fd, (struct sockaddr *)&sockaddr, &len);
     185             : 
     186           0 :         if (bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr))) {
     187           0 :                 zlog_warn(
     188             :                         "%s: bind(fd=%d,addr=%pSUp,port=%d,len=%zu) failure: errno=%d: %s",
     189             :                         __func__, fd, &sockaddr, port, sizeof(sockaddr), errno,
     190             :                         safe_strerror(errno));
     191           0 :                 close(fd);
     192           0 :                 return -1;
     193             :         }
     194             : 
     195           0 :         set_nonblocking(fd);
     196           0 :         sockopt_reuseaddr(fd);
     197             : 
     198           0 :         ret = ssmpingd_setsockopt(fd, addr, mttl);
     199           0 :         if (ret) {
     200           0 :                 zlog_warn("ssmpingd_setsockopt failed");
     201           0 :                 close(fd);
     202           0 :                 return -1;
     203             :         }
     204             : 
     205             :         return fd;
     206             : }
     207             : 
     208           0 : static void ssmpingd_delete(struct ssmpingd_sock *ss)
     209             : {
     210           0 :         assert(ss);
     211             : 
     212           0 :         THREAD_OFF(ss->t_sock_read);
     213             : 
     214           0 :         if (close(ss->sock_fd)) {
     215           0 :                 zlog_warn(
     216             :                         "%s: failure closing ssmpingd sock_fd=%d for source %pPA: errno=%d: %s",
     217             :                         __func__, ss->sock_fd, &ss->source_addr, errno,
     218             :                         safe_strerror(errno));
     219             :                 /* warning only */
     220             :         }
     221             : 
     222           0 :         listnode_delete(ss->pim->ssmpingd_list, ss);
     223           0 :         ssmpingd_free(ss);
     224           0 : }
     225             : 
     226           0 : static void ssmpingd_sendto(struct ssmpingd_sock *ss, const uint8_t *buf,
     227             :                             int len, struct sockaddr_storage to)
     228             : {
     229           0 :         socklen_t tolen = sizeof(to);
     230           0 :         int sent;
     231             : 
     232           0 :         sent = sendto(ss->sock_fd, buf, len, MSG_DONTWAIT,
     233             :                       (struct sockaddr *)&to, tolen);
     234           0 :         if (sent != len) {
     235           0 :                 if (sent < 0) {
     236           0 :                         zlog_warn(
     237             :                                 "%s: sendto() failure to %pSUp,fd=%d len=%d: errno=%d: %s",
     238             :                                 __func__, &to, ss->sock_fd, len, errno,
     239             :                                 safe_strerror(errno));
     240             :                 } else {
     241           0 :                         zlog_warn(
     242             :                                 "%s: sendto() partial to %pSUp, fd=%d len=%d: sent=%d",
     243             :                                 __func__, &to, ss->sock_fd, len, sent);
     244             :                 }
     245             :         }
     246           0 : }
     247             : 
     248           0 : static int ssmpingd_read_msg(struct ssmpingd_sock *ss)
     249             : {
     250           0 :         struct interface *ifp;
     251           0 :         struct sockaddr_storage from;
     252           0 :         struct sockaddr_storage to;
     253           0 :         socklen_t fromlen = sizeof(from);
     254           0 :         socklen_t tolen = sizeof(to);
     255           0 :         ifindex_t ifindex = -1;
     256           0 :         uint8_t buf[1000];
     257           0 :         int len;
     258             : 
     259           0 :         ++ss->requests;
     260             : 
     261           0 :         len = pim_socket_recvfromto(ss->sock_fd, buf, sizeof(buf), &from,
     262             :                                     &fromlen, &to, &tolen, &ifindex);
     263             : 
     264           0 :         if (len < 0) {
     265           0 :                 zlog_warn(
     266             :                         "%s: failure receiving ssmping for source %pPA on fd=%d: errno=%d: %s",
     267             :                         __func__, &ss->source_addr, ss->sock_fd, errno,
     268             :                         safe_strerror(errno));
     269           0 :                 return -1;
     270             :         }
     271             : 
     272           0 :         ifp = if_lookup_by_index(ifindex, ss->pim->vrf->vrf_id);
     273             : 
     274           0 :         if (buf[0] != PIM_SSMPINGD_REQUEST) {
     275           0 :                 zlog_warn(
     276             :                         "%s: bad ssmping type=%d from %pSUp to %pSUp on interface %s ifindex=%d fd=%d src=%pPA",
     277             :                         __func__, buf[0], &from, &to,
     278             :                         ifp ? ifp->name : "<iface?>", ifindex, ss->sock_fd,
     279             :                         &ss->source_addr);
     280           0 :                 return 0;
     281             :         }
     282             : 
     283           0 :         if (PIM_DEBUG_SSMPINGD) {
     284           0 :                 zlog_debug(
     285             :                         "%s: recv ssmping from %pSUp, to %pSUp, on interface %s ifindex=%d fd=%d src=%pPA",
     286             :                         __func__, &from, &to, ifp ? ifp->name : "<iface?>",
     287             :                         ifindex, ss->sock_fd, &ss->source_addr);
     288             :         }
     289             : 
     290           0 :         buf[0] = PIM_SSMPINGD_REPLY;
     291             : 
     292             :         /* unicast reply */
     293           0 :         ssmpingd_sendto(ss, buf, len, from);
     294             : 
     295             :         /* multicast reply */
     296           0 :         memcpy(&from, &ss->pim->ssmpingd_group_addr, sizeof(pim_addr));
     297           0 :         ssmpingd_sendto(ss, buf, len, from);
     298             : 
     299           0 :         return 0;
     300             : }
     301             : 
     302           0 : static void ssmpingd_sock_read(struct thread *t)
     303             : {
     304           0 :         struct ssmpingd_sock *ss;
     305             : 
     306           0 :         ss = THREAD_ARG(t);
     307             : 
     308           0 :         ssmpingd_read_msg(ss);
     309             : 
     310             :         /* Keep reading */
     311           0 :         ssmpingd_read_on(ss);
     312           0 : }
     313             : 
     314           0 : static void ssmpingd_read_on(struct ssmpingd_sock *ss)
     315             : {
     316           0 :         thread_add_read(router->master, ssmpingd_sock_read, ss, ss->sock_fd,
     317             :                         &ss->t_sock_read);
     318           0 : }
     319             : 
     320           0 : static struct ssmpingd_sock *ssmpingd_new(struct pim_instance *pim,
     321             :                                           pim_addr source_addr)
     322             : {
     323           0 :         struct ssmpingd_sock *ss;
     324           0 :         int sock_fd;
     325             : 
     326           0 :         if (!pim->ssmpingd_list) {
     327           0 :                 pim->ssmpingd_list = list_new();
     328           0 :                 pim->ssmpingd_list->del = (void (*)(void *))ssmpingd_free;
     329             :         }
     330             : 
     331           0 :         sock_fd =
     332           0 :                 ssmpingd_socket(source_addr, /* port: */ 4321, /* mTTL: */ 64);
     333           0 :         if (sock_fd < 0) {
     334           0 :                 zlog_warn("%s: ssmpingd_socket() failure for source %pPA",
     335             :                           __func__, &source_addr);
     336           0 :                 return 0;
     337             :         }
     338             : 
     339           0 :         ss = XCALLOC(MTYPE_PIM_SSMPINGD, sizeof(*ss));
     340             : 
     341           0 :         ss->pim = pim;
     342           0 :         ss->sock_fd = sock_fd;
     343           0 :         ss->t_sock_read = NULL;
     344           0 :         ss->source_addr = source_addr;
     345           0 :         ss->creation = pim_time_monotonic_sec();
     346           0 :         ss->requests = 0;
     347             : 
     348           0 :         listnode_add(pim->ssmpingd_list, ss);
     349             : 
     350           0 :         ssmpingd_read_on(ss);
     351             : 
     352           0 :         return ss;
     353             : }
     354             : 
     355           0 : int pim_ssmpingd_start(struct pim_instance *pim, pim_addr source_addr)
     356             : {
     357           0 :         struct ssmpingd_sock *ss;
     358             : 
     359           0 :         ss = ssmpingd_find(pim, source_addr);
     360           0 :         if (ss) {
     361             :                 /* silently ignore request to recreate entry */
     362             :                 return 0;
     363             :         }
     364             : 
     365           0 :         zlog_info("%s: starting ssmpingd for source %pPAs", __func__,
     366             :                   &source_addr);
     367             : 
     368           0 :         ss = ssmpingd_new(pim, source_addr);
     369           0 :         if (!ss) {
     370           0 :                 zlog_warn("%s: ssmpingd_new() failure for source %pPAs",
     371             :                           __func__, &source_addr);
     372           0 :                 return -1;
     373             :         }
     374             : 
     375             :         return 0;
     376             : }
     377             : 
     378           0 : int pim_ssmpingd_stop(struct pim_instance *pim, pim_addr source_addr)
     379             : {
     380           0 :         struct ssmpingd_sock *ss;
     381             : 
     382           0 :         ss = ssmpingd_find(pim, source_addr);
     383           0 :         if (!ss) {
     384           0 :                 zlog_warn("%s: could not find ssmpingd for source %pPAs",
     385             :                           __func__, &source_addr);
     386           0 :                 return -1;
     387             :         }
     388             : 
     389           0 :         zlog_info("%s: stopping ssmpingd for source %pPAs", __func__,
     390             :                   &source_addr);
     391             : 
     392           0 :         ssmpingd_delete(ss);
     393             : 
     394           0 :         return 0;
     395             : }

Generated by: LCOV version v1.16-topotato