]>
git.proxmox.com Git - mirror_frr.git/blob - lib/sockopt.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* setsockopt functions
3 * Copyright (C) 1999 Kunihiro Ishiguro
10 #include "sockunion.h"
11 #include "lib_errors.h"
13 #if (defined(__FreeBSD__) \
14 && ((__FreeBSD_version >= 500022 && __FreeBSD_version < 700000) \
15 || (__FreeBSD_version < 500000 && __FreeBSD_version >= 440000))) \
16 || (defined(__NetBSD__) && defined(__NetBSD_Version__) \
17 && __NetBSD_Version__ >= 106010000) \
18 || defined(__OpenBSD__) || defined(__APPLE__) \
19 || defined(__DragonFly__) || defined(__sun)
20 #define HAVE_BSD_STRUCT_IP_MREQ_HACK
23 void setsockopt_so_recvbuf(int sock
, int size
)
27 while (setsockopt(sock
, SOL_SOCKET
, SO_RCVBUF
, &size
, sizeof(size
))
32 flog_err(EC_LIB_SOCKET
,
33 "%s: fd %d: SO_RCVBUF set to %d (requested %d)",
34 __func__
, sock
, size
, orig_req
);
37 void setsockopt_so_sendbuf(const int sock
, int size
)
41 while (setsockopt(sock
, SOL_SOCKET
, SO_SNDBUF
, &size
, sizeof(size
))
46 flog_err(EC_LIB_SOCKET
,
47 "%s: fd %d: SO_SNDBUF set to %d (requested %d)",
48 __func__
, sock
, size
, orig_req
);
51 int getsockopt_so_sendbuf(const int sock
)
54 socklen_t optlen
= sizeof(optval
);
55 int ret
= getsockopt(sock
, SOL_SOCKET
, SO_SNDBUF
, (char *)&optval
,
58 flog_err_sys(EC_LIB_SYSTEM_CALL
,
59 "fd %d: can't getsockopt SO_SNDBUF: %d (%s)", sock
,
60 errno
, safe_strerror(errno
));
66 int getsockopt_so_recvbuf(const int sock
)
69 socklen_t optlen
= sizeof(optval
);
70 int ret
= getsockopt(sock
, SOL_SOCKET
, SO_RCVBUF
, (char *)&optval
,
73 flog_err_sys(EC_LIB_SYSTEM_CALL
,
74 "fd %d: can't getsockopt SO_RCVBUF: %d (%s)", sock
,
75 errno
, safe_strerror(errno
));
81 static void *getsockopt_cmsg_data(struct msghdr
*msgh
, int level
, int type
)
85 for (cmsg
= CMSG_FIRSTHDR(msgh
); cmsg
!= NULL
;
86 cmsg
= CMSG_NXTHDR(msgh
, cmsg
))
87 if (cmsg
->cmsg_level
== level
&& cmsg
->cmsg_type
== type
)
88 return CMSG_DATA(cmsg
);
93 /* Set IPv6 packet info to the socket. */
94 int setsockopt_ipv6_pktinfo(int sock
, int val
)
98 #ifdef IPV6_RECVPKTINFO /*2292bis-01*/
99 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_RECVPKTINFO
, &val
,
102 flog_err(EC_LIB_SOCKET
,
103 "can't setsockopt IPV6_RECVPKTINFO : %s",
104 safe_strerror(errno
));
106 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_PKTINFO
, &val
, sizeof(val
));
108 flog_err(EC_LIB_SOCKET
, "can't setsockopt IPV6_PKTINFO : %s",
109 safe_strerror(errno
));
110 #endif /* IANA_IPV6 */
114 /* Set multicast hops val to the socket. */
115 int setsockopt_ipv6_multicast_hops(int sock
, int val
)
119 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_MULTICAST_HOPS
, &val
,
122 flog_err(EC_LIB_SOCKET
, "can't setsockopt IPV6_MULTICAST_HOPS");
126 /* Set multicast hops val to the socket. */
127 int setsockopt_ipv6_unicast_hops(int sock
, int val
)
131 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_UNICAST_HOPS
, &val
,
134 flog_err(EC_LIB_SOCKET
, "can't setsockopt IPV6_UNICAST_HOPS");
138 int setsockopt_ipv6_hoplimit(int sock
, int val
)
142 #ifdef IPV6_RECVHOPLIMIT /*2292bis-01*/
143 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_RECVHOPLIMIT
, &val
,
146 flog_err(EC_LIB_SOCKET
, "can't setsockopt IPV6_RECVHOPLIMIT");
148 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_HOPLIMIT
, &val
, sizeof(val
));
150 flog_err(EC_LIB_SOCKET
, "can't setsockopt IPV6_HOPLIMIT");
155 /* Set multicast loop zero to the socket. */
156 int setsockopt_ipv6_multicast_loop(int sock
, int val
)
160 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, &val
,
163 flog_err(EC_LIB_SOCKET
, "can't setsockopt IPV6_MULTICAST_LOOP");
167 static int getsockopt_ipv6_ifindex(struct msghdr
*msgh
)
169 struct in6_pktinfo
*pktinfo
;
171 pktinfo
= getsockopt_cmsg_data(msgh
, IPPROTO_IPV6
, IPV6_PKTINFO
);
173 return pktinfo
->ipi6_ifindex
;
176 int setsockopt_ipv6_tclass(int sock
, int tclass
)
180 #ifdef IPV6_TCLASS /* RFC3542 */
181 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_TCLASS
, &tclass
,
184 flog_err(EC_LIB_SOCKET
,
185 "Can't set IPV6_TCLASS option for fd %d to %#x: %s",
186 sock
, tclass
, safe_strerror(errno
));
192 * Process multicast socket options for IPv4 in an OS-dependent manner.
193 * Supported options are IP_{ADD,DROP}_MEMBERSHIP.
195 * Many operating systems have a limit on the number of groups that
196 * can be joined per socket (where each group and local address
197 * counts). This impacts OSPF, which joins groups on each interface
198 * using a single socket. The limit is typically 20, derived from the
199 * original BSD multicast implementation. Some systems have
200 * mechanisms for increasing this limit.
202 * In many 4.4BSD-derived systems, multicast group operations are not
203 * allowed on interfaces that are not UP. Thus, a previous attempt to
204 * leave the group may have failed, leaving it still joined, and we
205 * drop/join quietly to recover. This may not be necessary, but aims to
206 * defend against unknown behavior in that we will still return an error
207 * if the second join fails. It is not clear how other systems
208 * (e.g. Linux, Solaris) behave when leaving groups on down interfaces,
209 * but this behavior should not be harmful if they behave the same way,
210 * allow leaves, or implicitly leave all groups joined to down interfaces.
212 int setsockopt_ipv4_multicast(int sock
, int optname
, struct in_addr if_addr
,
213 unsigned int mcast_addr
, ifindex_t ifindex
)
217 struct sockaddr_in
*si
;
219 memset(&gr
, 0, sizeof(gr
));
220 si
= (struct sockaddr_in
*)&gr
.gr_group
;
221 gr
.gr_interface
= ifindex
;
222 si
->sin_family
= AF_INET
;
223 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
224 si
->sin_len
= sizeof(struct sockaddr_in
);
225 #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
226 si
->sin_addr
.s_addr
= mcast_addr
;
227 ret
= setsockopt(sock
, IPPROTO_IP
,
228 (optname
== IP_ADD_MEMBERSHIP
) ? MCAST_JOIN_GROUP
230 (void *)&gr
, sizeof(gr
));
231 if ((ret
< 0) && (optname
== IP_ADD_MEMBERSHIP
)
232 && (errno
== EADDRINUSE
)) {
233 setsockopt(sock
, IPPROTO_IP
, MCAST_LEAVE_GROUP
, (void *)&gr
,
235 ret
= setsockopt(sock
, IPPROTO_IP
, MCAST_JOIN_GROUP
,
236 (void *)&gr
, sizeof(gr
));
240 #elif defined(HAVE_STRUCT_IP_MREQN_IMR_IFINDEX) && !defined(__FreeBSD__)
241 struct ip_mreqn mreqn
;
244 assert(optname
== IP_ADD_MEMBERSHIP
|| optname
== IP_DROP_MEMBERSHIP
);
245 memset(&mreqn
, 0, sizeof(mreqn
));
247 mreqn
.imr_multiaddr
.s_addr
= mcast_addr
;
248 mreqn
.imr_ifindex
= ifindex
;
250 ret
= setsockopt(sock
, IPPROTO_IP
, optname
, (void *)&mreqn
,
252 if ((ret
< 0) && (optname
== IP_ADD_MEMBERSHIP
)
253 && (errno
== EADDRINUSE
)) {
254 /* see above: handle possible problem when interface comes back
257 "setsockopt_ipv4_multicast attempting to drop and re-add (fd %d, mcast %pI4, ifindex %u)",
258 sock
, &mreqn
.imr_multiaddr
, ifindex
);
259 setsockopt(sock
, IPPROTO_IP
, IP_DROP_MEMBERSHIP
, (void *)&mreqn
,
261 ret
= setsockopt(sock
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
,
262 (void *)&mreqn
, sizeof(mreqn
));
266 /* Example defines for another OS, boilerplate off other code in this
267 function, AND handle optname as per other sections for consistency !! */
268 /* #elif defined(BOGON_NIX) && EXAMPLE_VERSION_CODE > -100000 */
269 /* Add your favourite OS here! */
271 #elif defined(HAVE_BSD_STRUCT_IP_MREQ_HACK) /* #if OS_TYPE */
272 /* standard BSD API */
277 assert(optname
== IP_ADD_MEMBERSHIP
|| optname
== IP_DROP_MEMBERSHIP
);
280 memset(&mreq
, 0, sizeof(mreq
));
281 mreq
.imr_multiaddr
.s_addr
= mcast_addr
;
282 #if !defined __OpenBSD__
283 mreq
.imr_interface
.s_addr
= htonl(ifindex
);
285 mreq
.imr_interface
.s_addr
= if_addr
.s_addr
;
288 ret
= setsockopt(sock
, IPPROTO_IP
, optname
, (void *)&mreq
,
290 if ((ret
< 0) && (optname
== IP_ADD_MEMBERSHIP
)
291 && (errno
== EADDRINUSE
)) {
292 /* see above: handle possible problem when interface comes back
295 "setsockopt_ipv4_multicast attempting to drop and re-add (fd %d, mcast %pI4, ifindex %u)",
296 sock
, &mreq
.imr_multiaddr
, ifindex
);
297 setsockopt(sock
, IPPROTO_IP
, IP_DROP_MEMBERSHIP
, (void *)&mreq
,
299 ret
= setsockopt(sock
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
,
300 (void *)&mreq
, sizeof(mreq
));
305 #error "Unsupported multicast API"
306 #endif /* #if OS_TYPE */
310 * Set IP_MULTICAST_IF socket option in an OS-dependent manner.
312 int setsockopt_ipv4_multicast_if(int sock
, struct in_addr if_addr
,
316 #ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
317 struct ip_mreqn mreqn
;
318 memset(&mreqn
, 0, sizeof(mreqn
));
320 mreqn
.imr_ifindex
= ifindex
;
321 return setsockopt(sock
, IPPROTO_IP
, IP_MULTICAST_IF
, (void *)&mreqn
,
324 /* Example defines for another OS, boilerplate off other code in this
326 /* #elif defined(BOGON_NIX) && EXAMPLE_VERSION_CODE > -100000 */
327 /* Add your favourite OS here! */
328 #elif defined(HAVE_BSD_STRUCT_IP_MREQ_HACK)
331 #if !defined __OpenBSD__
332 m
.s_addr
= htonl(ifindex
);
334 m
.s_addr
= if_addr
.s_addr
;
337 return setsockopt(sock
, IPPROTO_IP
, IP_MULTICAST_IF
, (void *)&m
,
340 #error "Unsupported multicast API"
344 int setsockopt_ipv4_multicast_loop(int sock
, uint8_t val
)
348 ret
= setsockopt(sock
, IPPROTO_IP
, IP_MULTICAST_LOOP
, (void *)&val
,
351 flog_err(EC_LIB_SOCKET
, "can't setsockopt IP_MULTICAST_LOOP");
356 static int setsockopt_ipv4_ifindex(int sock
, ifindex_t val
)
360 #if defined(IP_PKTINFO)
361 ret
= setsockopt(sock
, IPPROTO_IP
, IP_PKTINFO
, &val
, sizeof(val
));
363 flog_err(EC_LIB_SOCKET
,
364 "Can't set IP_PKTINFO option for fd %d to %d: %s",
365 sock
, val
, safe_strerror(errno
));
366 #elif defined(IP_RECVIF)
367 ret
= setsockopt(sock
, IPPROTO_IP
, IP_RECVIF
, &val
, sizeof(val
));
369 flog_err(EC_LIB_SOCKET
,
370 "Can't set IP_RECVIF option for fd %d to %d: %s", sock
,
371 val
, safe_strerror(errno
));
373 #warning "Neither IP_PKTINFO nor IP_RECVIF is available."
374 #warning "Will not be able to receive link info."
375 #warning "Things might be seriously broken.."
376 /* XXX Does this ever happen? Should there be a zlog_warn message here?
383 int setsockopt_ipv4_tos(int sock
, int tos
)
387 ret
= setsockopt(sock
, IPPROTO_IP
, IP_TOS
, &tos
, sizeof(tos
));
389 flog_err(EC_LIB_SOCKET
,
390 "Can't set IP_TOS option for fd %d to %#x: %s", sock
,
391 tos
, safe_strerror(errno
));
396 int setsockopt_ifindex(int af
, int sock
, ifindex_t val
)
402 ret
= setsockopt_ipv4_ifindex(sock
, val
);
405 ret
= setsockopt_ipv6_pktinfo(sock
, val
);
408 flog_err(EC_LIB_DEVELOPMENT
,
409 "setsockopt_ifindex: unknown address family %d", af
);
415 * Requires: msgh is not NULL and points to a valid struct msghdr, which
416 * may or may not have control data about the incoming interface.
418 * Returns the interface index (small integer >= 1) if it can be
419 * determined, or else 0.
421 static ifindex_t
getsockopt_ipv4_ifindex(struct msghdr
*msgh
)
425 #if defined(IP_PKTINFO)
426 /* Linux pktinfo based ifindex retrieval */
427 struct in_pktinfo
*pktinfo
;
429 pktinfo
= (struct in_pktinfo
*)getsockopt_cmsg_data(msgh
, IPPROTO_IP
,
432 /* getsockopt_ifindex() will forward this, being 0 "not found" */
436 ifindex
= pktinfo
->ipi_ifindex
;
438 #elif defined(IP_RECVIF)
440 /* retrieval based on IP_RECVIF */
442 /* BSD systems use a sockaddr_dl as the control message payload. */
443 struct sockaddr_dl
*sdl
;
446 sdl
= (struct sockaddr_dl
*)getsockopt_cmsg_data(msgh
, IPPROTO_IP
,
449 ifindex
= sdl
->sdl_index
;
455 * Neither IP_PKTINFO nor IP_RECVIF defined - warn at compile time.
456 * XXX Decide if this is a core service, or if daemons have to cope.
457 * Since Solaris 8 and OpenBSD seem not to provide it, it seems that
458 * daemons have to cope.
460 #warning "getsockopt_ipv4_ifindex: Neither IP_PKTINFO nor IP_RECVIF defined."
461 #warning "Some daemons may fail to operate correctly!"
464 #endif /* IP_PKTINFO */
469 /* return ifindex, 0 if none found */
470 ifindex_t
getsockopt_ifindex(int af
, struct msghdr
*msgh
)
474 return (getsockopt_ipv4_ifindex(msgh
));
476 return (getsockopt_ipv6_ifindex(msgh
));
478 flog_err(EC_LIB_DEVELOPMENT
,
479 "getsockopt_ifindex: unknown address family %d", af
);
484 /* swab iph between order system uses for IP_HDRINCL and host order */
485 void sockopt_iphdrincl_swab_htosys(struct ip
*iph
)
487 /* BSD and derived take iph in network order, except for
490 #ifndef HAVE_IP_HDRINCL_BSD_ORDER
491 iph
->ip_len
= htons(iph
->ip_len
);
492 iph
->ip_off
= htons(iph
->ip_off
);
493 #endif /* HAVE_IP_HDRINCL_BSD_ORDER */
495 iph
->ip_id
= htons(iph
->ip_id
);
498 void sockopt_iphdrincl_swab_systoh(struct ip
*iph
)
500 #ifndef HAVE_IP_HDRINCL_BSD_ORDER
501 iph
->ip_len
= ntohs(iph
->ip_len
);
502 iph
->ip_off
= ntohs(iph
->ip_off
);
503 #endif /* HAVE_IP_HDRINCL_BSD_ORDER */
505 iph
->ip_id
= ntohs(iph
->ip_id
);
508 int sockopt_tcp_rtt(int sock
)
512 socklen_t len
= sizeof(ti
);
514 if (getsockopt(sock
, IPPROTO_TCP
, TCP_INFO
, &ti
, &len
) != 0)
517 return ti
.tcpi_rtt
/ 1000;
523 int sockopt_tcp_signature_ext(int sock
, union sockunion
*su
, uint16_t prefixlen
,
524 const char *password
)
526 #ifndef HAVE_DECL_TCP_MD5SIG
528 * We have been asked to enable MD5 auth for an address, but our
529 * platform doesn't support that
534 #ifndef TCP_MD5SIG_EXT
536 * We have been asked to enable MD5 auth for a prefix, but our platform
537 * doesn't support that
543 #if HAVE_DECL_TCP_MD5SIG
546 int optname
= TCP_MD5SIG
;
549 * XXX Need to do PF_KEY operation here to add/remove an SA entry,
550 * and add/remove an SP entry for this peer's packet flows also.
552 int md5sig
= password
&& *password
? 1 : 0;
554 int keylen
= password
? strlen(password
) : 0;
555 struct tcp_md5sig md5sig
;
556 union sockunion
*su2
, *susock
;
558 /* Figure out whether the socket and the sockunion are the same family..
559 * adding AF_INET to AF_INET6 needs to be v4 mapped, you'd think..
561 if (!(susock
= sockunion_getsockname(sock
)))
564 if (susock
->sa
.sa_family
== su
->sa
.sa_family
)
570 if (su2
->sa
.sa_family
== AF_INET
) {
571 sockunion_free(susock
);
575 /* If this does not work, then all users of this sockopt will
577 * differentiate between IPv4 and IPv6, and keep separate
581 * Sadly, it doesn't seem to work at present. It's unknown
583 * this is a bug or not.
585 if (su2
->sa
.sa_family
== AF_INET6
586 && su
->sa
.sa_family
== AF_INET
) {
587 su2
->sin6
.sin6_family
= AF_INET6
;
588 /* V4Map the address */
589 memset(&su2
->sin6
.sin6_addr
, 0,
590 sizeof(struct in6_addr
));
591 su2
->sin6
.sin6_addr
.s6_addr32
[2] = htonl(0xffff);
592 memcpy(&su2
->sin6
.sin6_addr
.s6_addr32
[3],
593 &su
->sin
.sin_addr
, 4);
597 memset(&md5sig
, 0, sizeof(md5sig
));
598 memcpy(&md5sig
.tcpm_addr
, su2
, sizeof(*su2
));
600 md5sig
.tcpm_keylen
= keylen
;
602 memcpy(md5sig
.tcpm_key
, password
, keylen
);
603 sockunion_free(susock
);
606 * Handle support for MD5 signatures on prefixes, if available and
607 * requested. Technically the #ifdef check below is not needed because
608 * if prefixlen > 0 and we don't have support for this feature we would
609 * have already returned by now, but leaving it there to be explicit.
611 #ifdef TCP_MD5SIG_EXT
613 md5sig
.tcpm_prefixlen
= prefixlen
;
614 md5sig
.tcpm_flags
= TCP_MD5SIG_FLAG_PREFIX
;
615 optname
= TCP_MD5SIG_EXT
;
617 #endif /* TCP_MD5SIG_EXT */
619 #endif /* GNU_LINUX */
621 ret
= setsockopt(sock
, IPPROTO_TCP
, optname
, &md5sig
, sizeof(md5sig
));
628 "sockopt_tcp_signature: setsockopt(%d): %s",
629 sock
, safe_strerror(errno
));
632 #endif /* HAVE_TCP_MD5SIG */
635 * Making compiler happy. If we get to this point we probably
636 * have done something really really wrong.
641 int sockopt_tcp_signature(int sock
, union sockunion
*su
, const char *password
)
643 return sockopt_tcp_signature_ext(sock
, su
, 0, password
);
646 /* set TCP mss value to socket */
647 int sockopt_tcp_mss_set(int sock
, int tcp_maxseg
)
650 socklen_t tcp_maxseg_len
= sizeof(tcp_maxseg
);
652 ret
= setsockopt(sock
, IPPROTO_TCP
, TCP_MAXSEG
, &tcp_maxseg
,
655 flog_err_sys(EC_LIB_SYSTEM_CALL
,
656 "%s failed: setsockopt(%d): %s", __func__
, sock
,
657 safe_strerror(errno
));
663 /* get TCP mss value synced by socket */
664 int sockopt_tcp_mss_get(int sock
)
668 socklen_t tcp_maxseg_len
= sizeof(tcp_maxseg
);
670 ret
= getsockopt(sock
, IPPROTO_TCP
, TCP_MAXSEG
, &tcp_maxseg
,
673 flog_err_sys(EC_LIB_SYSTEM_CALL
,
674 "%s failed: getsockopt(%d): %s", __func__
, sock
,
675 safe_strerror(errno
));
682 int setsockopt_tcp_keepalive(int sock
, uint16_t keepalive_idle
,
683 uint16_t keepalive_intvl
,
684 uint16_t keepalive_probes
)
688 if (setsockopt(sock
, SOL_SOCKET
, SO_KEEPALIVE
, &val
, sizeof(val
)) < 0) {
689 flog_err_sys(EC_LIB_SYSTEM_CALL
,
690 "%s failed: setsockopt SO_KEEPALIVE (%d): %s",
691 __func__
, sock
, safe_strerror(errno
));
695 #if defined __OpenBSD__
698 /* Send first probe after keepalive_idle seconds */
699 val
= keepalive_idle
;
700 if (setsockopt(sock
, IPPROTO_TCP
, TCP_KEEPIDLE
, &val
, sizeof(val
)) <
702 flog_err_sys(EC_LIB_SYSTEM_CALL
,
703 "%s failed: setsockopt TCP_KEEPIDLE (%d): %s",
704 __func__
, sock
, safe_strerror(errno
));
708 /* Set interval between two probes */
709 val
= keepalive_intvl
;
710 if (setsockopt(sock
, IPPROTO_TCP
, TCP_KEEPINTVL
, &val
, sizeof(val
)) <
712 flog_err_sys(EC_LIB_SYSTEM_CALL
,
713 "%s failed: setsockopt TCP_KEEPINTVL (%d): %s",
714 __func__
, sock
, safe_strerror(errno
));
718 /* Set maximum probes */
719 val
= keepalive_probes
;
720 if (setsockopt(sock
, IPPROTO_TCP
, TCP_KEEPCNT
, &val
, sizeof(val
)) < 0) {
721 flog_err_sys(EC_LIB_SYSTEM_CALL
,
722 "%s failed: setsockopt TCP_KEEPCNT (%d): %s",
723 __func__
, sock
, safe_strerror(errno
));