Coverage Report

Created: 2026-01-25 06:17

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