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