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