]> git.proxmox.com Git - mirror_frr.git/blame - ldpd/socket.c
Merge pull request #12798 from donaldsharp/rib_match_multicast
[mirror_frr.git] / ldpd / socket.c
CommitLineData
acddc0ed 1// SPDX-License-Identifier: ISC
8429abe0
RW
2/* $OpenBSD$ */
3
4/*
5 * Copyright (c) 2016 Renato Westphal <renato@openbsd.org>
6 * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
7 * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
8 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
8429abe0
RW
9 */
10
eac6e3f0 11#include <zebra.h>
8429abe0
RW
12
13#include "ldpd.h"
14#include "ldpe.h"
15#include "log.h"
16
eac6e3f0
RW
17#include "lib/log.h"
18#include "privs.h"
19#include "sockopt.h"
20
21extern struct zebra_privs_t ldpd_privs;
22extern struct zebra_privs_t ldpe_privs;
23
8429abe0
RW
24int
25ldp_create_socket(int af, enum socket_type type)
26{
27 int fd, domain, proto;
28 union ldpd_addr addr;
4149ef7c 29 union sockunion local_su;
eac6e3f0 30#ifdef __OpenBSD__
8429abe0 31 int opt;
eac6e3f0 32#endif
8429abe0
RW
33
34 /* create socket */
35 switch (type) {
36 case LDP_SOCKET_DISC:
37 case LDP_SOCKET_EDISC:
38 domain = SOCK_DGRAM;
39 proto = IPPROTO_UDP;
40 break;
41 case LDP_SOCKET_SESSION:
42 domain = SOCK_STREAM;
43 proto = IPPROTO_TCP;
44 break;
45 default:
46 fatalx("ldp_create_socket: unknown socket type");
47 }
eac6e3f0 48 fd = socket(af, domain, proto);
8429abe0
RW
49 if (fd == -1) {
50 log_warn("%s: error creating socket", __func__);
51 return (-1);
52 }
eac6e3f0
RW
53 sock_set_nonblock(fd);
54 sockopt_v6only(af, fd);
8429abe0
RW
55
56 /* bind to a local address/port */
57 switch (type) {
58 case LDP_SOCKET_DISC:
59 /* listen on all addresses */
60 memset(&addr, 0, sizeof(addr));
4149ef7c 61 addr2sa(af, &addr, LDP_PORT, &local_su);
8429abe0
RW
62 break;
63 case LDP_SOCKET_EDISC:
64 case LDP_SOCKET_SESSION:
65 addr = (ldp_af_conf_get(ldpd_conf, af))->trans_addr;
4149ef7c 66 addr2sa(af, &addr, LDP_PORT, &local_su);
eac6e3f0
RW
67 /* ignore any possible error */
68 sock_set_bindany(fd, 1);
8429abe0
RW
69 break;
70 }
0cf6db21 71 frr_with_privs(&ldpd_privs) {
6bb30c2c
DL
72 if (sock_set_reuse(fd, 1) == -1) {
73 close(fd);
74 return (-1);
75 }
76 if (bind(fd, &local_su.sa, sockaddr_len(&local_su.sa)) == -1) {
77 log_warnx("%s: error binding socket: %s", __func__,
78 safe_strerror(errno));
79 close(fd);
80 return (-1);
81 }
8429abe0
RW
82 }
83
84 /* set options */
85 switch (af) {
86 case AF_INET:
87 if (sock_set_ipv4_tos(fd, IPTOS_PREC_INTERNETCONTROL) == -1) {
88 close(fd);
89 return (-1);
90 }
91 if (type == LDP_SOCKET_DISC) {
92 if (sock_set_ipv4_mcast_ttl(fd,
93 IP_DEFAULT_MULTICAST_TTL) == -1) {
94 close(fd);
95 return (-1);
96 }
97 if (sock_set_ipv4_mcast_loop(fd) == -1) {
98 close(fd);
99 return (-1);
100 }
101 }
102 if (type == LDP_SOCKET_DISC || type == LDP_SOCKET_EDISC) {
103 if (sock_set_ipv4_recvif(fd, 1) == -1) {
104 close(fd);
105 return (-1);
106 }
eac6e3f0
RW
107#ifndef MSG_MCAST
108#if defined(HAVE_IP_PKTINFO)
109 if (sock_set_ipv4_pktinfo(fd, 1) == -1) {
110 close(fd);
111 return (-1);
112 }
113#elif defined(HAVE_IP_RECVDSTADDR)
114 if (sock_set_ipv4_recvdstaddr(fd, 1) == -1) {
115 close(fd);
116 return (-1);
117 }
118#else
119#error "Unsupported socket API"
120#endif
121#endif /* MSG_MCAST */
8429abe0
RW
122 }
123 if (type == LDP_SOCKET_SESSION) {
124 if (sock_set_ipv4_ucast_ttl(fd, 255) == -1) {
125 close(fd);
126 return (-1);
127 }
128 }
129 break;
130 case AF_INET6:
131 if (sock_set_ipv6_dscp(fd, IPTOS_PREC_INTERNETCONTROL) == -1) {
132 close(fd);
133 return (-1);
134 }
135 if (type == LDP_SOCKET_DISC) {
136 if (sock_set_ipv6_mcast_loop(fd) == -1) {
137 close(fd);
138 return (-1);
139 }
140 if (sock_set_ipv6_mcast_hops(fd, 255) == -1) {
141 close(fd);
142 return (-1);
143 }
144 if (!(ldpd_conf->ipv6.flags & F_LDPD_AF_NO_GTSM)) {
eac6e3f0
RW
145 /* ignore any possible error */
146 sock_set_ipv6_minhopcount(fd, 255);
8429abe0
RW
147 }
148 }
149 if (type == LDP_SOCKET_DISC || type == LDP_SOCKET_EDISC) {
150 if (sock_set_ipv6_pktinfo(fd, 1) == -1) {
151 close(fd);
152 return (-1);
153 }
154 }
155 if (type == LDP_SOCKET_SESSION) {
156 if (sock_set_ipv6_ucast_hops(fd, 255) == -1) {
157 close(fd);
158 return (-1);
159 }
160 }
161 break;
162 }
163 switch (type) {
164 case LDP_SOCKET_DISC:
165 case LDP_SOCKET_EDISC:
166 sock_set_recvbuf(fd);
167 break;
168 case LDP_SOCKET_SESSION:
169 if (listen(fd, LDP_BACKLOG) == -1)
170 log_warn("%s: error listening on socket", __func__);
171
eac6e3f0 172#ifdef __OpenBSD__
8429abe0
RW
173 opt = 1;
174 if (setsockopt(fd, IPPROTO_TCP, TCP_MD5SIG, &opt,
175 sizeof(opt)) == -1) {
176 if (errno == ENOPROTOOPT) { /* system w/o md5sig */
177 log_warnx("md5sig not available, disabling");
178 sysdep.no_md5sig = 1;
179 } else {
180 close(fd);
181 return (-1);
182 }
183 }
eac6e3f0 184#endif
8429abe0
RW
185 break;
186 }
187
188 return (fd);
189}
190
eac6e3f0
RW
191void
192sock_set_nonblock(int fd)
193{
194 int flags;
195
196 if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
197 fatal("fcntl F_GETFL");
198
199 flags |= O_NONBLOCK;
200
f67de3ee 201 if (fcntl(fd, F_SETFL, flags) == -1)
eac6e3f0
RW
202 fatal("fcntl F_SETFL");
203}
204
205void
206sock_set_cloexec(int fd)
207{
208 int flags;
209
210 if ((flags = fcntl(fd, F_GETFD, 0)) == -1)
211 fatal("fcntl F_GETFD");
212
213 flags |= FD_CLOEXEC;
214
f67de3ee 215 if (fcntl(fd, F_SETFD, flags) == -1)
eac6e3f0
RW
216 fatal("fcntl F_SETFD");
217}
218
8429abe0
RW
219void
220sock_set_recvbuf(int fd)
221{
222 int bsize;
223
224 bsize = 65535;
225 while (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bsize,
226 sizeof(bsize)) == -1)
227 bsize /= 2;
228}
229
230int
231sock_set_reuse(int fd, int enable)
232{
233 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable,
234 sizeof(int)) < 0) {
235 log_warn("%s: error setting SO_REUSEADDR", __func__);
236 return (-1);
237 }
238
239 return (0);
240}
241
242int
243sock_set_bindany(int fd, int enable)
244{
eac6e3f0 245#ifdef HAVE_SO_BINDANY
0cf6db21 246 frr_with_privs(&ldpd_privs) {
01b9e3fd
DL
247 if (setsockopt(fd, SOL_SOCKET, SO_BINDANY, &enable,
248 sizeof(int)) < 0) {
249 log_warn("%s: error setting SO_BINDANY", __func__);
250 return (-1);
251 }
8429abe0 252 }
eac6e3f0
RW
253 return (0);
254#elif defined(HAVE_IP_FREEBIND)
255 if (setsockopt(fd, IPPROTO_IP, IP_FREEBIND, &enable, sizeof(int)) < 0) {
256 log_warn("%s: error setting IP_FREEBIND", __func__);
257 return (-1);
258 }
8429abe0 259 return (0);
1ef00571 260#elif defined(IP_BINDANY)
0cf6db21 261 frr_with_privs(&ldpd_privs) {
1ef00571
RZ
262 if (setsockopt(fd, IPPROTO_IP, IP_BINDANY, &enable, sizeof(int))
263 < 0) {
264 log_warn("%s: error setting IP_BINDANY", __func__);
265 return (-1);
266 }
267 }
c00ba1da 268 return (0);
eac6e3f0 269#else
1ef00571
RZ
270 log_warnx(
271 "%s: missing SO_BINDANY, IP_FREEBIND and IP_BINDANY, unable to bind to a nonlocal IP address",
272 __func__);
eac6e3f0
RW
273 return (-1);
274#endif /* HAVE_SO_BINDANY */
275}
276
277#ifndef __OpenBSD__
278/*
279 * Set MD5 key for the socket, for the given peer address. If the password
280 * is NULL or zero-length, the option will be disabled.
281 */
282int
283sock_set_md5sig(int fd, int af, union ldpd_addr *addr, const char *password)
284{
285 int ret = -1;
286 int save_errno = ENOSYS;
287#if HAVE_DECL_TCP_MD5SIG
288 union sockunion su;
289#endif
290
291 if (fd == -1)
292 return (0);
293#if HAVE_DECL_TCP_MD5SIG
4149ef7c 294 addr2sa(af, addr, 0, &su);
eac6e3f0 295
0cf6db21 296 frr_with_privs(&ldpe_privs) {
6bb30c2c
DL
297 ret = sockopt_tcp_signature(fd, &su, password);
298 save_errno = errno;
eac6e3f0 299 }
eac6e3f0
RW
300#endif /* HAVE_TCP_MD5SIG */
301 if (ret < 0)
302 log_warnx("%s: can't set TCP_MD5SIG option on fd %d: %s",
303 __func__, fd, safe_strerror(save_errno));
304
305 return (ret);
8429abe0 306}
eac6e3f0 307#endif
8429abe0
RW
308
309int
310sock_set_ipv4_tos(int fd, int tos)
311{
c4efd0f4 312 if (setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) {
8429abe0
RW
313 log_warn("%s: error setting IP_TOS to 0x%x", __func__, tos);
314 return (-1);
315 }
316
317 return (0);
318}
319
320int
6cd8093d 321sock_set_ipv4_recvif(int fd, ifindex_t enable)
8429abe0 322{
eac6e3f0 323 return (setsockopt_ifindex(AF_INET, fd, enable));
8429abe0
RW
324}
325
326int
327sock_set_ipv4_minttl(int fd, int ttl)
328{
eac6e3f0 329 return (sockopt_minttl(AF_INET, fd, ttl));
8429abe0
RW
330}
331
332int
333sock_set_ipv4_ucast_ttl(int fd, int ttl)
334{
335 if (setsockopt(fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) < 0) {
336 log_warn("%s: error setting IP_TTL", __func__);
337 return (-1);
338 }
339
340 return (0);
341}
342
343int
344sock_set_ipv4_mcast_ttl(int fd, uint8_t ttl)
345{
346 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
347 (char *)&ttl, sizeof(ttl)) < 0) {
348 log_warn("%s: error setting IP_MULTICAST_TTL to %d",
349 __func__, ttl);
350 return (-1);
351 }
352
353 return (0);
354}
355
eac6e3f0
RW
356#ifndef MSG_MCAST
357#if defined(HAVE_IP_PKTINFO)
358int
359sock_set_ipv4_pktinfo(int fd, int enable)
360{
361 if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &enable,
362 sizeof(enable)) < 0) {
363 log_warn("%s: error setting IP_PKTINFO", __func__);
364 return (-1);
365 }
366
367 return (0);
368}
369#elif defined(HAVE_IP_RECVDSTADDR)
370int
371sock_set_ipv4_recvdstaddr(int fd, int enable)
372{
373 if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &enable,
374 sizeof(enable)) < 0) {
375 log_warn("%s: error setting IP_RECVDSTADDR", __func__);
376 return (-1);
377 }
378
379 return (0);
380}
381#else
382#error "Unsupported socket API"
383#endif
384#endif /* MSG_MCAST */
385
8429abe0
RW
386int
387sock_set_ipv4_mcast(struct iface *iface)
388{
eac6e3f0 389 struct in_addr if_addr;
8429abe0 390
eac6e3f0 391 if_addr.s_addr = if_get_ipv4_addr(iface);
8429abe0 392
eac6e3f0
RW
393 if (setsockopt_ipv4_multicast_if(global.ipv4.ldp_disc_socket,
394 if_addr, iface->ifindex) < 0) {
8429abe0
RW
395 log_warn("%s: error setting IP_MULTICAST_IF, interface %s",
396 __func__, iface->name);
397 return (-1);
398 }
399
400 return (0);
401}
402
403int
404sock_set_ipv4_mcast_loop(int fd)
405{
c5bdb09f 406 return (setsockopt_ipv4_multicast_loop(fd, 0));
8429abe0
RW
407}
408
409int
410sock_set_ipv6_dscp(int fd, int dscp)
411{
412 if (setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &dscp,
413 sizeof(dscp)) < 0) {
414 log_warn("%s: error setting IPV6_TCLASS", __func__);
415 return (-1);
416 }
417
418 return (0);
419}
420
421int
422sock_set_ipv6_pktinfo(int fd, int enable)
423{
424 if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &enable,
425 sizeof(enable)) < 0) {
426 log_warn("%s: error setting IPV6_RECVPKTINFO", __func__);
427 return (-1);
428 }
429
430 return (0);
431}
432
433int
434sock_set_ipv6_minhopcount(int fd, int hoplimit)
435{
eac6e3f0 436 return (sockopt_minttl(AF_INET6, fd, hoplimit));
8429abe0
RW
437}
438
439int
440sock_set_ipv6_ucast_hops(int fd, int hoplimit)
441{
442 if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
443 &hoplimit, sizeof(hoplimit)) < 0) {
444 log_warn("%s: error setting IPV6_UNICAST_HOPS", __func__);
445 return (-1);
446 }
447
448 return (0);
449}
450
451int
452sock_set_ipv6_mcast_hops(int fd, int hoplimit)
453{
454 if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
455 &hoplimit, sizeof(hoplimit)) < 0) {
456 log_warn("%s: error setting IPV6_MULTICAST_HOPS", __func__);
457 return (-1);
458 }
459
460 return (0);
461}
462
463int
464sock_set_ipv6_mcast(struct iface *iface)
465{
466 if (setsockopt(global.ipv6.ldp_disc_socket, IPPROTO_IPV6,
467 IPV6_MULTICAST_IF, &iface->ifindex, sizeof(iface->ifindex)) < 0) {
468 log_warn("%s: error setting IPV6_MULTICAST_IF, interface %s",
469 __func__, iface->name);
470 return (-1);
471 }
472
473 return (0);
474}
475
476int
477sock_set_ipv6_mcast_loop(int fd)
478{
479 unsigned int loop = 0;
480
481 if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
482 &loop, sizeof(loop)) < 0) {
483 log_warn("%s: error setting IPV6_MULTICAST_LOOP", __func__);
484 return (-1);
485 }
486
487 return (0);
488}