Coverage Report

Created: 2025-08-28 06:29

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