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