back to topotato report
topotato coverage report
Current view: top level - pimd - pim_sock.c (source / functions) Hit Total Coverage
Test: test_pim6_basic.py::PIM6Basic Lines: 96 129 74.4 %
Date: 2023-02-24 18:38:38 Functions: 10 10 100.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             : 
      22             : #include <sys/types.h>
      23             : #include <sys/socket.h>
      24             : #include <netinet/in.h>
      25             : #include <netinet/igmp.h>
      26             : #include <arpa/inet.h>
      27             : #include <unistd.h>
      28             : #include <netdb.h>
      29             : #include <errno.h>
      30             : 
      31             : #include "log.h"
      32             : #include "privs.h"
      33             : #include "if.h"
      34             : #include "vrf.h"
      35             : #include "sockopt.h"
      36             : #include "lib_errors.h"
      37             : #include "network.h"
      38             : 
      39             : #include "pimd.h"
      40             : #include "pim_instance.h"
      41             : #include "pim_mroute.h"
      42             : #include "pim_iface.h"
      43             : #include "pim_sock.h"
      44             : #include "pim_str.h"
      45             : 
      46             : #if PIM_IPV == 4
      47             : #define setsockopt_iptos setsockopt_ipv4_tos
      48             : #define setsockopt_multicast_loop setsockopt_ipv4_multicast_loop
      49             : #else
      50             : #define setsockopt_iptos setsockopt_ipv6_tclass
      51             : #define setsockopt_multicast_loop setsockopt_ipv6_multicast_loop
      52             : #endif
      53             : 
      54           4 : int pim_socket_raw(int protocol)
      55             : {
      56           4 :         int fd;
      57             : 
      58           4 :         frr_with_privs(&pimd_privs) {
      59           4 :                 fd = socket(PIM_AF, SOCK_RAW, protocol);
      60             :         }
      61             : 
      62           4 :         if (fd < 0) {
      63           0 :                 zlog_warn("Could not create raw socket: errno=%d: %s", errno,
      64             :                           safe_strerror(errno));
      65           0 :                 return PIM_SOCK_ERR_SOCKET;
      66             :         }
      67             : 
      68             :         return fd;
      69             : }
      70             : 
      71           4 : void pim_socket_ip_hdr(int fd)
      72             : {
      73           8 :         frr_with_privs(&pimd_privs) {
      74             : #if PIM_IPV == 4
      75             :                 const int on = 1;
      76             : 
      77             :                 if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)))
      78             :                         zlog_err("%s: Could not turn on IP_HDRINCL option: %m",
      79             :                                  __func__);
      80             : #endif
      81             :         }
      82           4 : }
      83             : 
      84             : /*
      85             :  * Given a socket and a interface,
      86             :  * Bind that socket to that interface
      87             :  */
      88           4 : int pim_socket_bind(int fd, struct interface *ifp)
      89             : {
      90           4 :         int ret = 0;
      91             : 
      92             : #ifdef SO_BINDTODEVICE
      93           4 :         frr_with_privs(&pimd_privs) {
      94           4 :                 ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifp->name,
      95           4 :                                  strlen(ifp->name));
      96             :         }
      97             : #endif
      98           4 :         return ret;
      99             : }
     100             : 
     101             : #if PIM_IPV == 4
     102             : static inline int pim_setsockopt(int protocol, int fd, struct interface *ifp)
     103             : {
     104             :         int one = 1;
     105             :         int ttl = 1;
     106             : 
     107             : #if defined(HAVE_IP_PKTINFO)
     108             :         /* Linux and Solaris IP_PKTINFO */
     109             :         if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)))
     110             :                 zlog_warn("Could not set PKTINFO on socket fd=%d: %m", fd);
     111             : #elif defined(HAVE_IP_RECVDSTADDR)
     112             :         /* BSD IP_RECVDSTADDR */
     113             :         if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &one, sizeof(one)))
     114             :                 zlog_warn("Could not set IP_RECVDSTADDR on socket fd=%d: %m",
     115             :                           fd);
     116             : #else
     117             :         flog_err(
     118             :                 EC_LIB_DEVELOPMENT,
     119             :                 "Missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()");
     120             :         close(fd);
     121             :         return PIM_SOCK_ERR_DSTADDR;
     122             : #endif
     123             : 
     124             :         /* Set router alert (RFC 2113) for all IGMP messages (RFC
     125             :          * 3376 4. Message Formats)*/
     126             :         if (protocol == IPPROTO_IGMP) {
     127             :                 uint8_t ra[4];
     128             : 
     129             :                 ra[0] = 148;
     130             :                 ra[1] = 4;
     131             :                 ra[2] = 0;
     132             :                 ra[3] = 0;
     133             :                 if (setsockopt(fd, IPPROTO_IP, IP_OPTIONS, ra, 4)) {
     134             :                         zlog_warn(
     135             :                                 "Could not set Router Alert Option on socket fd=%d: %m",
     136             :                                 fd);
     137             :                         close(fd);
     138             :                         return PIM_SOCK_ERR_RA;
     139             :                 }
     140             :         }
     141             : 
     142             :         if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl))) {
     143             :                 zlog_warn("Could not set multicast TTL=%d on socket fd=%d: %m",
     144             :                           ttl, fd);
     145             :                 close(fd);
     146             :                 return PIM_SOCK_ERR_TTL;
     147             :         }
     148             : 
     149             :         if (setsockopt_ipv4_multicast_if(fd, PIMADDR_ANY, ifp->ifindex)) {
     150             :                 zlog_warn(
     151             :                         "Could not set Outgoing Interface Option on socket fd=%d: %m",
     152             :                         fd);
     153             :                 close(fd);
     154             :                 return PIM_SOCK_ERR_IFACE;
     155             :         }
     156             : 
     157             :         return 0;
     158             : }
     159             : #else /* PIM_IPV != 4 */
     160           4 : static inline int pim_setsockopt(int protocol, int fd, struct interface *ifp)
     161             : {
     162           4 :         int ttl = 1;
     163           4 :         struct ipv6_mreq mreq = {};
     164             : 
     165           4 :         setsockopt_ipv6_pktinfo(fd, 1);
     166           4 :         setsockopt_ipv6_multicast_hops(fd, ttl);
     167             : 
     168           4 :         mreq.ipv6mr_interface = ifp->ifindex;
     169           4 :         if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &mreq,
     170             :                        sizeof(mreq))) {
     171           0 :                 zlog_warn(
     172             :                         "Could not set Outgoing Interface Option on socket fd=%d: %m",
     173             :                         fd);
     174           0 :                 close(fd);
     175           0 :                 return PIM_SOCK_ERR_IFACE;
     176             :         }
     177             : 
     178             :         return 0;
     179             : }
     180             : #endif
     181             : 
     182           2 : int pim_reg_sock(void)
     183             : {
     184           2 :         int fd;
     185           2 :         long flags;
     186             : 
     187           2 :         frr_with_privs (&pimd_privs) {
     188           2 :                 fd = socket(PIM_AF, SOCK_RAW, PIM_PROTO_REG);
     189             :         }
     190             : 
     191           2 :         if (fd < 0) {
     192           0 :                 zlog_warn("Could not create raw socket: errno=%d: %s", errno,
     193             :                           safe_strerror(errno));
     194           0 :                 return PIM_SOCK_ERR_SOCKET;
     195             :         }
     196             : 
     197           2 :         if (sockopt_reuseaddr(fd)) {
     198           0 :                 close(fd);
     199           0 :                 return PIM_SOCK_ERR_REUSE;
     200             :         }
     201             : 
     202           2 :         flags = fcntl(fd, F_GETFL, 0);
     203           2 :         if (flags < 0) {
     204           0 :                 zlog_warn(
     205             :                         "Could not get fcntl(F_GETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
     206             :                         fd, errno, safe_strerror(errno));
     207           0 :                 close(fd);
     208           0 :                 return PIM_SOCK_ERR_NONBLOCK_GETFL;
     209             :         }
     210             : 
     211           2 :         if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
     212           0 :                 zlog_warn(
     213             :                         "Could not set fcntl(F_SETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
     214             :                         fd, errno, safe_strerror(errno));
     215           0 :                 close(fd);
     216           0 :                 return PIM_SOCK_ERR_NONBLOCK_SETFL;
     217             :         }
     218             : 
     219             :         return fd;
     220             : }
     221             : 
     222           4 : int pim_socket_mcast(int protocol, pim_addr ifaddr, struct interface *ifp,
     223             :                      uint8_t loop)
     224             : {
     225           4 :         int fd;
     226           4 :         int ret;
     227             : 
     228           4 :         fd = pim_socket_raw(protocol);
     229           4 :         if (fd < 0) {
     230           0 :                 zlog_warn("Could not create multicast socket: errno=%d: %s",
     231             :                           errno, safe_strerror(errno));
     232           0 :                 return PIM_SOCK_ERR_SOCKET;
     233             :         }
     234             : 
     235             :         /* XXX: if SO_BINDTODEVICE isn't available, use IP_PKTINFO / IP_RECVIF
     236             :          * to emulate behaviour?  Or change to only use 1 socket for all
     237             :          * interfaces? */
     238           4 :         ret = pim_socket_bind(fd, ifp);
     239           4 :         if (ret) {
     240           0 :                 close(fd);
     241           0 :                 zlog_warn("Could not set fd: %d for interface: %s to device",
     242             :                           fd, ifp->name);
     243           0 :                 return PIM_SOCK_ERR_BIND;
     244             :         }
     245             : 
     246           4 :         set_nonblocking(fd);
     247           4 :         sockopt_reuseaddr(fd);
     248           4 :         setsockopt_so_recvbuf(fd, 8 * 1024 * 1024);
     249             : 
     250           4 :         ret = pim_setsockopt(protocol, fd, ifp);
     251           4 :         if (ret) {
     252           0 :                 zlog_warn("pim_setsockopt failed for interface: %s to device ",
     253             :                           ifp->name);
     254           0 :                 return ret;
     255             :         }
     256             : 
     257             :         /* leftover common sockopts */
     258           4 :         if (setsockopt_multicast_loop(fd, loop)) {
     259           0 :                 zlog_warn(
     260             :                         "Could not %s Multicast Loopback Option on socket fd=%d: %m",
     261             :                         loop ? "enable" : "disable", fd);
     262           0 :                 close(fd);
     263           0 :                 return PIM_SOCK_ERR_LOOP;
     264             :         }
     265             : 
     266             :         /* Set Tx socket DSCP byte */
     267           4 :         if (setsockopt_iptos(fd, IPTOS_PREC_INTERNETCONTROL))
     268           0 :                 zlog_warn("can't set sockopt IP[V6]_TOS to socket %d: %m", fd);
     269             : 
     270             :         return fd;
     271             : }
     272             : 
     273           4 : int pim_socket_join(int fd, pim_addr group, pim_addr ifaddr, ifindex_t ifindex,
     274             :                     struct pim_interface *pim_ifp)
     275             : {
     276           4 :         int ret;
     277             : 
     278             : #if PIM_IPV == 4
     279             :         ret = setsockopt_ipv4_multicast(fd, IP_ADD_MEMBERSHIP, ifaddr,
     280             :                                         group.s_addr, ifindex);
     281             : #else
     282           4 :         struct ipv6_mreq opt;
     283             : 
     284           4 :         memcpy(&opt.ipv6mr_multiaddr, &group, 16);
     285           4 :         opt.ipv6mr_interface = ifindex;
     286           4 :         ret = setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &opt, sizeof(opt));
     287             : #endif
     288             : 
     289           4 :         pim_ifp->igmp_ifstat_joins_sent++;
     290             : 
     291           4 :         if (ret) {
     292           0 :                 flog_err(
     293             :                         EC_LIB_SOCKET,
     294             :                         "Failure socket joining fd=%d group %pPAs on interface address %pPAs: %m",
     295             :                         fd, &group, &ifaddr);
     296           0 :                 pim_ifp->igmp_ifstat_joins_failed++;
     297           0 :                 return ret;
     298             :         }
     299             : 
     300           4 :         if (PIM_DEBUG_TRACE)
     301           4 :                 zlog_debug(
     302             :                         "Socket fd=%d joined group %pPAs on interface address %pPAs",
     303             :                         fd, &group, &ifaddr);
     304             :         return ret;
     305             : }
     306             : 
     307             : #if PIM_IPV == 4
     308             : static void cmsg_getdstaddr(struct msghdr *mh, struct sockaddr_storage *dst,
     309             :                             ifindex_t *ifindex)
     310             : {
     311             :         struct cmsghdr *cmsg;
     312             :         struct sockaddr_in *dst4 = (struct sockaddr_in *)dst;
     313             : 
     314             :         for (cmsg = CMSG_FIRSTHDR(mh); cmsg != NULL;
     315             :              cmsg = CMSG_NXTHDR(mh, cmsg)) {
     316             : #ifdef HAVE_IP_PKTINFO
     317             :                 if ((cmsg->cmsg_level == IPPROTO_IP) &&
     318             :                     (cmsg->cmsg_type == IP_PKTINFO)) {
     319             :                         struct in_pktinfo *i;
     320             : 
     321             :                         i = (struct in_pktinfo *)CMSG_DATA(cmsg);
     322             :                         if (dst4)
     323             :                                 dst4->sin_addr = i->ipi_addr;
     324             :                         if (ifindex)
     325             :                                 *ifindex = i->ipi_ifindex;
     326             : 
     327             :                         break;
     328             :                 }
     329             : #endif
     330             : 
     331             : #ifdef HAVE_IP_RECVDSTADDR
     332             :                 if ((cmsg->cmsg_level == IPPROTO_IP) &&
     333             :                     (cmsg->cmsg_type == IP_RECVDSTADDR)) {
     334             :                         struct in_addr *i = (struct in_addr *)CMSG_DATA(cmsg);
     335             : 
     336             :                         if (dst4)
     337             :                                 dst4->sin_addr = *i;
     338             : 
     339             :                         break;
     340             :                 }
     341             : #endif
     342             : 
     343             : #if defined(HAVE_IP_RECVIF) && defined(CMSG_IFINDEX)
     344             :                 if (cmsg->cmsg_type == IP_RECVIF)
     345             :                         if (ifindex)
     346             :                                 *ifindex = CMSG_IFINDEX(cmsg);
     347             : #endif
     348             :         }
     349             : }
     350             : #else  /* PIM_IPV != 4 */
     351          11 : static void cmsg_getdstaddr(struct msghdr *mh, struct sockaddr_storage *dst,
     352             :                             ifindex_t *ifindex)
     353             : {
     354          11 :         struct cmsghdr *cmsg;
     355          11 :         struct sockaddr_in6 *dst6 = (struct sockaddr_in6 *)dst;
     356             : 
     357          11 :         for (cmsg = CMSG_FIRSTHDR(mh); cmsg != NULL;
     358          11 :              cmsg = CMSG_NXTHDR(mh, cmsg)) {
     359          11 :                 if ((cmsg->cmsg_level == IPPROTO_IPV6) &&
     360             :                     (cmsg->cmsg_type == IPV6_PKTINFO)) {
     361          11 :                         struct in6_pktinfo *i;
     362             : 
     363          11 :                         i = (struct in6_pktinfo *)CMSG_DATA(cmsg);
     364             : 
     365          11 :                         if (dst6)
     366          11 :                                 dst6->sin6_addr = i->ipi6_addr;
     367          11 :                         if (ifindex)
     368          11 :                                 *ifindex = i->ipi6_ifindex;
     369             :                         break;
     370             :                 }
     371             :         }
     372          11 : }
     373             : #endif /* PIM_IPV != 4 */
     374             : 
     375          19 : int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len,
     376             :                           struct sockaddr_storage *from, socklen_t *fromlen,
     377             :                           struct sockaddr_storage *to, socklen_t *tolen,
     378             :                           ifindex_t *ifindex)
     379             : {
     380          19 :         struct msghdr msgh;
     381          19 :         struct iovec iov;
     382          19 :         char cbuf[1000];
     383          19 :         int err;
     384             : 
     385             :         /*
     386             :          * IP_PKTINFO / IP_RECVDSTADDR don't yield sin_port.
     387             :          * Use getsockname() to get sin_port.
     388             :          */
     389          19 :         if (to) {
     390          19 :                 socklen_t to_len = sizeof(*to);
     391             : 
     392          19 :                 pim_socket_getsockname(fd, (struct sockaddr *)to, &to_len);
     393             : 
     394          19 :                 if (tolen)
     395          19 :                         *tolen = sizeof(*to);
     396             :         }
     397             : 
     398          19 :         memset(&msgh, 0, sizeof(msgh));
     399          19 :         iov.iov_base = buf;
     400          19 :         iov.iov_len = len;
     401          19 :         msgh.msg_control = cbuf;
     402          19 :         msgh.msg_controllen = sizeof(cbuf);
     403          19 :         msgh.msg_name = from;
     404          19 :         msgh.msg_namelen = fromlen ? *fromlen : 0;
     405          19 :         msgh.msg_iov = &iov;
     406          19 :         msgh.msg_iovlen = 1;
     407          19 :         msgh.msg_flags = 0;
     408             : 
     409          19 :         err = recvmsg(fd, &msgh, 0);
     410          19 :         if (err < 0)
     411             :                 return err;
     412             : 
     413          11 :         if (fromlen)
     414          11 :                 *fromlen = msgh.msg_namelen;
     415             : 
     416          11 :         cmsg_getdstaddr(&msgh, to, ifindex);
     417             : 
     418          11 :         return err; /* len */
     419             : }
     420             : 
     421          19 : int pim_socket_getsockname(int fd, struct sockaddr *name, socklen_t *namelen)
     422             : {
     423          19 :         if (getsockname(fd, name, namelen)) {
     424           0 :                 int e = errno;
     425           0 :                 zlog_warn(
     426             :                         "Could not get Socket Name for socket fd=%d: errno=%d: %s",
     427             :                         fd, errno, safe_strerror(errno));
     428           0 :                 errno = e;
     429           0 :                 return PIM_SOCK_ERR_NAME;
     430             :         }
     431             : 
     432             :         return PIM_SOCK_ERR_NONE;
     433             : }

Generated by: LCOV version v1.16-topotato