]>
git.proxmox.com Git - mirror_frr.git/blob - lib/sockopt.c
1 /* setsockopt functions
2 * Copyright (C) 1999 Kunihiro Ishiguro
4 * This file is part of GNU Zebra.
6 * GNU Zebra is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2, or (at your option) any
11 * GNU Zebra is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
29 #include "sockunion.h"
30 #include "lib_errors.h"
32 void setsockopt_so_recvbuf(int sock
, int size
)
36 while (setsockopt(sock
, SOL_SOCKET
, SO_RCVBUF
, &size
, sizeof(size
))
41 flog_err(EC_LIB_SOCKET
,
42 "%s: fd %d: SO_RCVBUF set to %d (requested %d)",
43 __func__
, sock
, size
, orig_req
);
46 void setsockopt_so_sendbuf(const int sock
, int size
)
50 while (setsockopt(sock
, SOL_SOCKET
, SO_SNDBUF
, &size
, sizeof(size
))
55 flog_err(EC_LIB_SOCKET
,
56 "%s: fd %d: SO_SNDBUF set to %d (requested %d)",
57 __func__
, sock
, size
, orig_req
);
60 int getsockopt_so_sendbuf(const int sock
)
63 socklen_t optlen
= sizeof(optval
);
64 int ret
= getsockopt(sock
, SOL_SOCKET
, SO_SNDBUF
, (char *)&optval
,
67 flog_err_sys(EC_LIB_SYSTEM_CALL
,
68 "fd %d: can't getsockopt SO_SNDBUF: %d (%s)", sock
,
69 errno
, safe_strerror(errno
));
75 int getsockopt_so_recvbuf(const int sock
)
78 socklen_t optlen
= sizeof(optval
);
79 int ret
= getsockopt(sock
, SOL_SOCKET
, SO_RCVBUF
, (char *)&optval
,
82 flog_err_sys(EC_LIB_SYSTEM_CALL
,
83 "fd %d: can't getsockopt SO_RCVBUF: %d (%s)", sock
,
84 errno
, safe_strerror(errno
));
90 static void *getsockopt_cmsg_data(struct msghdr
*msgh
, int level
, int type
)
95 for (cmsg
= CMSG_FIRSTHDR(msgh
); cmsg
!= NULL
;
96 cmsg
= CMSG_NXTHDR(msgh
, cmsg
))
97 if (cmsg
->cmsg_level
== level
&& cmsg
->cmsg_type
== type
)
98 return (ptr
= CMSG_DATA(cmsg
));
103 /* Set IPv6 packet info to the socket. */
104 int setsockopt_ipv6_pktinfo(int sock
, int val
)
108 #ifdef IPV6_RECVPKTINFO /*2292bis-01*/
109 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_RECVPKTINFO
, &val
,
112 flog_err(EC_LIB_SOCKET
,
113 "can't setsockopt IPV6_RECVPKTINFO : %s",
114 safe_strerror(errno
));
116 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_PKTINFO
, &val
, sizeof(val
));
118 flog_err(EC_LIB_SOCKET
, "can't setsockopt IPV6_PKTINFO : %s",
119 safe_strerror(errno
));
120 #endif /* INIA_IPV6 */
124 /* Set multicast hops val to the socket. */
125 int setsockopt_ipv6_checksum(int sock
, int val
)
130 ret
= setsockopt(sock
, IPPROTO_RAW
, IPV6_CHECKSUM
, &val
, sizeof(val
));
132 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_CHECKSUM
, &val
, sizeof(val
));
133 #endif /* GNU_LINUX */
135 flog_err(EC_LIB_SOCKET
, "can't setsockopt IPV6_CHECKSUM");
139 /* Set multicast hops val to the socket. */
140 int setsockopt_ipv6_multicast_hops(int sock
, int val
)
144 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_MULTICAST_HOPS
, &val
,
147 flog_err(EC_LIB_SOCKET
, "can't setsockopt IPV6_MULTICAST_HOPS");
151 /* Set multicast hops val to the socket. */
152 int setsockopt_ipv6_unicast_hops(int sock
, int val
)
156 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_UNICAST_HOPS
, &val
,
159 flog_err(EC_LIB_SOCKET
, "can't setsockopt IPV6_UNICAST_HOPS");
163 int setsockopt_ipv6_hoplimit(int sock
, int val
)
167 #ifdef IPV6_RECVHOPLIMIT /*2292bis-01*/
168 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_RECVHOPLIMIT
, &val
,
171 flog_err(EC_LIB_SOCKET
, "can't setsockopt IPV6_RECVHOPLIMIT");
173 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_HOPLIMIT
, &val
, sizeof(val
));
175 flog_err(EC_LIB_SOCKET
, "can't setsockopt IPV6_HOPLIMIT");
180 /* Set multicast loop zero to the socket. */
181 int setsockopt_ipv6_multicast_loop(int sock
, int val
)
185 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, &val
,
188 flog_err(EC_LIB_SOCKET
, "can't setsockopt IPV6_MULTICAST_LOOP");
192 static int getsockopt_ipv6_ifindex(struct msghdr
*msgh
)
194 struct in6_pktinfo
*pktinfo
;
196 pktinfo
= getsockopt_cmsg_data(msgh
, IPPROTO_IPV6
, IPV6_PKTINFO
);
198 return pktinfo
->ipi6_ifindex
;
201 int setsockopt_ipv6_tclass(int sock
, int tclass
)
205 #ifdef IPV6_TCLASS /* RFC3542 */
206 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_TCLASS
, &tclass
,
209 flog_err(EC_LIB_SOCKET
,
210 "Can't set IPV6_TCLASS option for fd %d to %#x: %s",
211 sock
, tclass
, safe_strerror(errno
));
217 * Process multicast socket options for IPv4 in an OS-dependent manner.
218 * Supported options are IP_{ADD,DROP}_MEMBERSHIP.
220 * Many operating systems have a limit on the number of groups that
221 * can be joined per socket (where each group and local address
222 * counts). This impacts OSPF, which joins groups on each interface
223 * using a single socket. The limit is typically 20, derived from the
224 * original BSD multicast implementation. Some systems have
225 * mechanisms for increasing this limit.
227 * In many 4.4BSD-derived systems, multicast group operations are not
228 * allowed on interfaces that are not UP. Thus, a previous attempt to
229 * leave the group may have failed, leaving it still joined, and we
230 * drop/join quietly to recover. This may not be necessary, but aims to
231 * defend against unknown behavior in that we will still return an error
232 * if the second join fails. It is not clear how other systems
233 * (e.g. Linux, Solaris) behave when leaving groups on down interfaces,
234 * but this behavior should not be harmful if they behave the same way,
235 * allow leaves, or implicitly leave all groups joined to down interfaces.
237 int setsockopt_ipv4_multicast(int sock
, int optname
, struct in_addr if_addr
,
238 unsigned int mcast_addr
, ifindex_t ifindex
)
242 struct sockaddr_in
*si
;
244 memset(&gr
, 0, sizeof(gr
));
245 si
= (struct sockaddr_in
*)&gr
.gr_group
;
246 gr
.gr_interface
= ifindex
;
247 si
->sin_family
= AF_INET
;
248 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
249 si
->sin_len
= sizeof(struct sockaddr_in
);
250 #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
251 si
->sin_addr
.s_addr
= mcast_addr
;
252 ret
= setsockopt(sock
, IPPROTO_IP
,
253 (optname
== IP_ADD_MEMBERSHIP
) ? MCAST_JOIN_GROUP
255 (void *)&gr
, sizeof(gr
));
256 if ((ret
< 0) && (optname
== IP_ADD_MEMBERSHIP
)
257 && (errno
== EADDRINUSE
)) {
258 setsockopt(sock
, IPPROTO_IP
, MCAST_LEAVE_GROUP
, (void *)&gr
,
260 ret
= setsockopt(sock
, IPPROTO_IP
, MCAST_JOIN_GROUP
,
261 (void *)&gr
, sizeof(gr
));
265 #elif defined(HAVE_STRUCT_IP_MREQN_IMR_IFINDEX) && !defined(__FreeBSD__)
266 struct ip_mreqn mreqn
;
269 assert(optname
== IP_ADD_MEMBERSHIP
|| optname
== IP_DROP_MEMBERSHIP
);
270 memset(&mreqn
, 0, sizeof(mreqn
));
272 mreqn
.imr_multiaddr
.s_addr
= mcast_addr
;
273 mreqn
.imr_ifindex
= ifindex
;
275 ret
= setsockopt(sock
, IPPROTO_IP
, optname
, (void *)&mreqn
,
277 if ((ret
< 0) && (optname
== IP_ADD_MEMBERSHIP
)
278 && (errno
== EADDRINUSE
)) {
279 /* see above: handle possible problem when interface comes back
281 char buf
[1][INET_ADDRSTRLEN
];
283 "setsockopt_ipv4_multicast attempting to drop and "
284 "re-add (fd %d, mcast %s, ifindex %u)",
285 sock
, inet_ntop(AF_INET
, &mreqn
.imr_multiaddr
, buf
[0],
288 setsockopt(sock
, IPPROTO_IP
, IP_DROP_MEMBERSHIP
, (void *)&mreqn
,
290 ret
= setsockopt(sock
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
,
291 (void *)&mreqn
, sizeof(mreqn
));
295 /* Example defines for another OS, boilerplate off other code in this
296 function, AND handle optname as per other sections for consistency !! */
297 /* #elif defined(BOGON_NIX) && EXAMPLE_VERSION_CODE > -100000 */
298 /* Add your favourite OS here! */
300 #elif defined(HAVE_BSD_STRUCT_IP_MREQ_HACK) /* #if OS_TYPE */
301 /* standard BSD API */
306 assert(optname
== IP_ADD_MEMBERSHIP
|| optname
== IP_DROP_MEMBERSHIP
);
309 memset(&mreq
, 0, sizeof(mreq
));
310 mreq
.imr_multiaddr
.s_addr
= mcast_addr
;
311 #if !defined __OpenBSD__
312 mreq
.imr_interface
.s_addr
= htonl(ifindex
);
314 mreq
.imr_interface
.s_addr
= if_addr
.s_addr
;
317 ret
= setsockopt(sock
, IPPROTO_IP
, optname
, (void *)&mreq
,
319 if ((ret
< 0) && (optname
== IP_ADD_MEMBERSHIP
)
320 && (errno
== EADDRINUSE
)) {
321 /* see above: handle possible problem when interface comes back
323 char buf
[1][INET_ADDRSTRLEN
];
325 "setsockopt_ipv4_multicast attempting to drop and "
326 "re-add (fd %d, mcast %s, ifindex %u)",
327 sock
, inet_ntop(AF_INET
, &mreq
.imr_multiaddr
, buf
[0],
330 setsockopt(sock
, IPPROTO_IP
, IP_DROP_MEMBERSHIP
, (void *)&mreq
,
332 ret
= setsockopt(sock
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
,
333 (void *)&mreq
, sizeof(mreq
));
338 #error "Unsupported multicast API"
339 #endif /* #if OS_TYPE */
343 * Set IP_MULTICAST_IF socket option in an OS-dependent manner.
345 int setsockopt_ipv4_multicast_if(int sock
, struct in_addr if_addr
,
349 #ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
350 struct ip_mreqn mreqn
;
351 memset(&mreqn
, 0, sizeof(mreqn
));
353 mreqn
.imr_ifindex
= ifindex
;
354 return setsockopt(sock
, IPPROTO_IP
, IP_MULTICAST_IF
, (void *)&mreqn
,
357 /* Example defines for another OS, boilerplate off other code in this
359 /* #elif defined(BOGON_NIX) && EXAMPLE_VERSION_CODE > -100000 */
360 /* Add your favourite OS here! */
361 #elif defined(HAVE_BSD_STRUCT_IP_MREQ_HACK)
364 #if !defined __OpenBSD__
365 m
.s_addr
= htonl(ifindex
);
367 m
.s_addr
= if_addr
.s_addr
;
370 return setsockopt(sock
, IPPROTO_IP
, IP_MULTICAST_IF
, (void *)&m
,
372 #elif defined(SUNOS_5)
373 char ifname
[IF_NAMESIZE
];
374 struct ifaddrs
*ifa
, *ifap
;
375 struct in_addr ifaddr
;
377 if (if_indextoname(ifindex
, ifname
) == NULL
)
380 if (getifaddrs(&ifa
) != 0)
383 for (ifap
= ifa
; ifap
!= NULL
; ifap
= ifap
->ifa_next
) {
384 struct sockaddr_in
*sa
;
386 if (strcmp(ifap
->ifa_name
, ifname
) != 0)
388 if (ifap
->ifa_addr
->sa_family
!= AF_INET
)
390 sa
= (struct sockaddr_in
*)ifap
->ifa_addr
;
391 memcpy(&ifaddr
, &sa
->sin_addr
, sizeof(ifaddr
));
396 if (!ifap
) /* This means we did not find an IP */
399 return setsockopt(sock
, IPPROTO_IP
, IP_MULTICAST_IF
, (void *)&ifaddr
,
402 #error "Unsupported multicast API"
406 int setsockopt_ipv4_multicast_loop(int sock
, uint8_t val
)
410 ret
= setsockopt(sock
, IPPROTO_IP
, IP_MULTICAST_LOOP
, (void *)&val
,
413 flog_err(EC_LIB_SOCKET
, "can't setsockopt IP_MULTICAST_LOOP");
418 static int setsockopt_ipv4_ifindex(int sock
, ifindex_t val
)
422 #if defined(IP_PKTINFO)
423 if ((ret
= setsockopt(sock
, IPPROTO_IP
, IP_PKTINFO
, &val
, sizeof(val
)))
425 flog_err(EC_LIB_SOCKET
,
426 "Can't set IP_PKTINFO option for fd %d to %d: %s",
427 sock
, val
, safe_strerror(errno
));
428 #elif defined(IP_RECVIF)
429 if ((ret
= setsockopt(sock
, IPPROTO_IP
, IP_RECVIF
, &val
, sizeof(val
)))
431 flog_err(EC_LIB_SOCKET
,
432 "Can't set IP_RECVIF option for fd %d to %d: %s", sock
,
433 val
, safe_strerror(errno
));
435 #warning "Neither IP_PKTINFO nor IP_RECVIF is available."
436 #warning "Will not be able to receive link info."
437 #warning "Things might be seriously broken.."
438 /* XXX Does this ever happen? Should there be a zlog_warn message here?
445 int setsockopt_ipv4_tos(int sock
, int tos
)
449 ret
= setsockopt(sock
, IPPROTO_IP
, IP_TOS
, &tos
, sizeof(tos
));
451 flog_err(EC_LIB_SOCKET
,
452 "Can't set IP_TOS option for fd %d to %#x: %s", sock
,
453 tos
, safe_strerror(errno
));
458 int setsockopt_ifindex(int af
, int sock
, ifindex_t val
)
464 ret
= setsockopt_ipv4_ifindex(sock
, val
);
467 ret
= setsockopt_ipv6_pktinfo(sock
, val
);
470 flog_err(EC_LIB_DEVELOPMENT
,
471 "setsockopt_ifindex: unknown address family %d", af
);
477 * Requires: msgh is not NULL and points to a valid struct msghdr, which
478 * may or may not have control data about the incoming interface.
480 * Returns the interface index (small integer >= 1) if it can be
481 * determined, or else 0.
483 static ifindex_t
getsockopt_ipv4_ifindex(struct msghdr
*msgh
)
487 #if defined(IP_PKTINFO)
488 /* Linux pktinfo based ifindex retrieval */
489 struct in_pktinfo
*pktinfo
;
491 pktinfo
= (struct in_pktinfo
*)getsockopt_cmsg_data(msgh
, IPPROTO_IP
,
494 /* getsockopt_ifindex() will forward this, being 0 "not found" */
498 ifindex
= pktinfo
->ipi_ifindex
;
500 #elif defined(IP_RECVIF)
502 /* retrieval based on IP_RECVIF */
505 /* BSD systems use a sockaddr_dl as the control message payload. */
506 struct sockaddr_dl
*sdl
;
508 /* SUNOS_5 uses an integer with the index. */
509 ifindex_t
*ifindex_p
;
514 sdl
= (struct sockaddr_dl
*)getsockopt_cmsg_data(msgh
, IPPROTO_IP
,
517 ifindex
= sdl
->sdl_index
;
522 * Solaris. On Solaris 8, IP_RECVIF is defined, but the call to
523 * enable it fails with errno=99, and the struct msghdr has
526 ifindex_p
= (uint_t
*)getsockopt_cmsg_data(msgh
, IPPROTO_IP
, IP_RECVIF
);
527 if (ifindex_p
!= NULL
)
528 ifindex
= *ifindex_p
;
535 * Neither IP_PKTINFO nor IP_RECVIF defined - warn at compile time.
536 * XXX Decide if this is a core service, or if daemons have to cope.
537 * Since Solaris 8 and OpenBSD seem not to provide it, it seems that
538 * daemons have to cope.
540 #warning "getsockopt_ipv4_ifindex: Neither IP_PKTINFO nor IP_RECVIF defined."
541 #warning "Some daemons may fail to operate correctly!"
544 #endif /* IP_PKTINFO */
549 /* return ifindex, 0 if none found */
550 ifindex_t
getsockopt_ifindex(int af
, struct msghdr
*msgh
)
554 return (getsockopt_ipv4_ifindex(msgh
));
557 return (getsockopt_ipv6_ifindex(msgh
));
560 flog_err(EC_LIB_DEVELOPMENT
,
561 "getsockopt_ifindex: unknown address family %d", af
);
566 /* swab iph between order system uses for IP_HDRINCL and host order */
567 void sockopt_iphdrincl_swab_htosys(struct ip
*iph
)
569 /* BSD and derived take iph in network order, except for
572 #ifndef HAVE_IP_HDRINCL_BSD_ORDER
573 iph
->ip_len
= htons(iph
->ip_len
);
574 iph
->ip_off
= htons(iph
->ip_off
);
575 #endif /* HAVE_IP_HDRINCL_BSD_ORDER */
577 iph
->ip_id
= htons(iph
->ip_id
);
580 void sockopt_iphdrincl_swab_systoh(struct ip
*iph
)
582 #ifndef HAVE_IP_HDRINCL_BSD_ORDER
583 iph
->ip_len
= ntohs(iph
->ip_len
);
584 iph
->ip_off
= ntohs(iph
->ip_off
);
585 #endif /* HAVE_IP_HDRINCL_BSD_ORDER */
587 iph
->ip_id
= ntohs(iph
->ip_id
);
590 int sockopt_tcp_rtt(int sock
)
594 socklen_t len
= sizeof(ti
);
596 if (getsockopt(sock
, IPPROTO_TCP
, TCP_INFO
, &ti
, &len
) != 0)
599 return ti
.tcpi_rtt
/ 1000;
605 int sockopt_tcp_signature_ext(int sock
, union sockunion
*su
, uint16_t prefixlen
,
606 const char *password
)
608 #ifndef HAVE_DECL_TCP_MD5SIG
610 * We have been asked to enable MD5 auth for an address, but our
611 * platform doesn't support that
616 #ifndef TCP_MD5SIG_EXT
618 * We have been asked to enable MD5 auth for a prefix, but our platform
619 * doesn't support that
625 #if HAVE_DECL_TCP_MD5SIG
628 int optname
= TCP_MD5SIG
;
631 * XXX Need to do PF_KEY operation here to add/remove an SA entry,
632 * and add/remove an SP entry for this peer's packet flows also.
634 int md5sig
= password
&& *password
? 1 : 0;
636 int keylen
= password
? strlen(password
) : 0;
637 struct tcp_md5sig md5sig
;
638 union sockunion
*su2
, *susock
;
640 /* Figure out whether the socket and the sockunion are the same family..
641 * adding AF_INET to AF_INET6 needs to be v4 mapped, you'd think..
643 if (!(susock
= sockunion_getsockname(sock
)))
646 if (susock
->sa
.sa_family
== su
->sa
.sa_family
)
652 if (su2
->sa
.sa_family
== AF_INET
) {
653 sockunion_free(susock
);
657 /* If this does not work, then all users of this sockopt will
659 * differentiate between IPv4 and IPv6, and keep seperate
663 * Sadly, it doesn't seem to work at present. It's unknown
665 * this is a bug or not.
667 if (su2
->sa
.sa_family
== AF_INET6
668 && su
->sa
.sa_family
== AF_INET
) {
669 su2
->sin6
.sin6_family
= AF_INET6
;
670 /* V4Map the address */
671 memset(&su2
->sin6
.sin6_addr
, 0,
672 sizeof(struct in6_addr
));
673 su2
->sin6
.sin6_addr
.s6_addr32
[2] = htonl(0xffff);
674 memcpy(&su2
->sin6
.sin6_addr
.s6_addr32
[3],
675 &su
->sin
.sin_addr
, 4);
679 memset(&md5sig
, 0, sizeof(md5sig
));
680 memcpy(&md5sig
.tcpm_addr
, su2
, sizeof(*su2
));
682 md5sig
.tcpm_keylen
= keylen
;
684 memcpy(md5sig
.tcpm_key
, password
, keylen
);
685 sockunion_free(susock
);
688 * Handle support for MD5 signatures on prefixes, if available and
689 * requested. Technically the #ifdef check below is not needed because
690 * if prefixlen > 0 and we don't have support for this feature we would
691 * have already returned by now, but leaving it there to be explicit.
693 #ifdef TCP_MD5SIG_EXT
695 md5sig
.tcpm_prefixlen
= prefixlen
;
696 md5sig
.tcpm_flags
= TCP_MD5SIG_FLAG_PREFIX
;
697 optname
= TCP_MD5SIG_EXT
;
699 #endif /* TCP_MD5SIG_EXT */
701 #endif /* GNU_LINUX */
703 if ((ret
= setsockopt(sock
, IPPROTO_TCP
, optname
, &md5sig
,
706 /* ENOENT is harmless. It is returned when we clear a password
708 one was not previously set. */
714 "sockopt_tcp_signature: setsockopt(%d): %s",
715 sock
, safe_strerror(errno
));
718 #endif /* HAVE_TCP_MD5SIG */
721 * Making compiler happy. If we get to this point we probably
722 * have done something really really wrong.
727 int sockopt_tcp_signature(int sock
, union sockunion
*su
, const char *password
)
729 return sockopt_tcp_signature_ext(sock
, su
, 0, password
);