]>
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
17 * along with GNU Zebra; see the file COPYING. If not, write to the Free
18 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
30 #include "sockunion.h"
33 setsockopt_so_recvbuf (int sock
, int size
)
37 while (setsockopt(sock
, SOL_SOCKET
, SO_RCVBUF
, &size
, sizeof (size
)) == -1)
41 zlog_warn ("%s: fd %d: SO_RCVBUF set to %d (requested %d)", __func__
, sock
,
46 setsockopt_so_sendbuf (const int sock
, int size
)
50 while (setsockopt(sock
, SOL_SOCKET
, SO_SNDBUF
, &size
, sizeof (size
)) == -1)
54 zlog_warn ("%s: fd %d: SO_SNDBUF set to %d (requested %d)", __func__
, sock
,
59 getsockopt_so_sendbuf (const int sock
)
62 socklen_t optlen
= sizeof (optval
);
63 int ret
= getsockopt (sock
, SOL_SOCKET
, SO_SNDBUF
,
64 (char *)&optval
, &optlen
);
67 zlog_err ("fd %d: can't getsockopt SO_SNDBUF: %d (%s)",
68 sock
, errno
, safe_strerror (errno
));
75 getsockopt_cmsg_data (struct msghdr
*msgh
, int level
, int type
)
80 for (cmsg
= ZCMSG_FIRSTHDR(msgh
);
82 cmsg
= CMSG_NXTHDR(msgh
, cmsg
))
83 if (cmsg
->cmsg_level
== level
&& cmsg
->cmsg_type
)
84 return (ptr
= CMSG_DATA(cmsg
));
89 /* Set IPv6 packet info to the socket. */
91 setsockopt_ipv6_pktinfo (int sock
, int val
)
95 #ifdef IPV6_RECVPKTINFO /*2292bis-01*/
96 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_RECVPKTINFO
, &val
, sizeof(val
));
98 zlog_warn ("can't setsockopt IPV6_RECVPKTINFO : %s", safe_strerror (errno
));
100 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_PKTINFO
, &val
, sizeof(val
));
102 zlog_warn ("can't setsockopt IPV6_PKTINFO : %s", safe_strerror (errno
));
103 #endif /* INIA_IPV6 */
107 /* Set multicast hops val to the socket. */
109 setsockopt_ipv6_checksum (int sock
, int val
)
114 ret
= setsockopt(sock
, IPPROTO_RAW
, IPV6_CHECKSUM
, &val
, sizeof(val
));
116 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_CHECKSUM
, &val
, sizeof(val
));
117 #endif /* GNU_LINUX */
119 zlog_warn ("can't setsockopt IPV6_CHECKSUM");
123 /* Set multicast hops val to the socket. */
125 setsockopt_ipv6_multicast_hops (int sock
, int val
)
129 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_MULTICAST_HOPS
, &val
, sizeof(val
));
131 zlog_warn ("can't setsockopt IPV6_MULTICAST_HOPS");
135 /* Set multicast hops val to the socket. */
137 setsockopt_ipv6_unicast_hops (int sock
, int val
)
141 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_UNICAST_HOPS
, &val
, sizeof(val
));
143 zlog_warn ("can't setsockopt IPV6_UNICAST_HOPS");
148 setsockopt_ipv6_hoplimit (int sock
, int val
)
152 #ifdef IPV6_RECVHOPLIMIT /*2292bis-01*/
153 ret
= setsockopt (sock
, IPPROTO_IPV6
, IPV6_RECVHOPLIMIT
, &val
, sizeof(val
));
155 zlog_warn ("can't setsockopt IPV6_RECVHOPLIMIT");
157 ret
= setsockopt (sock
, IPPROTO_IPV6
, IPV6_HOPLIMIT
, &val
, sizeof(val
));
159 zlog_warn ("can't setsockopt IPV6_HOPLIMIT");
164 /* Set multicast loop zero to the socket. */
166 setsockopt_ipv6_multicast_loop (int sock
, int val
)
170 ret
= setsockopt (sock
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, &val
,
173 zlog_warn ("can't setsockopt IPV6_MULTICAST_LOOP");
178 getsockopt_ipv6_ifindex (struct msghdr
*msgh
)
180 struct in6_pktinfo
*pktinfo
;
182 pktinfo
= getsockopt_cmsg_data (msgh
, IPPROTO_IPV6
, IPV6_PKTINFO
);
184 return pktinfo
->ipi6_ifindex
;
188 setsockopt_ipv6_tclass(int sock
, int tclass
)
192 #ifdef IPV6_TCLASS /* RFC3542 */
193 ret
= setsockopt (sock
, IPPROTO_IPV6
, IPV6_TCLASS
, &tclass
, sizeof (tclass
));
195 zlog_warn ("Can't set IPV6_TCLASS option for fd %d to %#x: %s",
196 sock
, tclass
, safe_strerror(errno
));
202 * Process multicast socket options for IPv4 in an OS-dependent manner.
203 * Supported options are IP_{ADD,DROP}_MEMBERSHIP.
205 * Many operating systems have a limit on the number of groups that
206 * can be joined per socket (where each group and local address
207 * counts). This impacts OSPF, which joins groups on each interface
208 * using a single socket. The limit is typically 20, derived from the
209 * original BSD multicast implementation. Some systems have
210 * mechanisms for increasing this limit.
212 * In many 4.4BSD-derived systems, multicast group operations are not
213 * allowed on interfaces that are not UP. Thus, a previous attempt to
214 * leave the group may have failed, leaving it still joined, and we
215 * drop/join quietly to recover. This may not be necessary, but aims to
216 * defend against unknown behavior in that we will still return an error
217 * if the second join fails. It is not clear how other systems
218 * (e.g. Linux, Solaris) behave when leaving groups on down interfaces,
219 * but this behavior should not be harmful if they behave the same way,
220 * allow leaves, or implicitly leave all groups joined to down interfaces.
223 setsockopt_ipv4_multicast(int sock
,
225 struct in_addr if_addr
,
226 unsigned int mcast_addr
,
231 struct sockaddr_in
*si
;
233 memset (&gr
, 0, sizeof(gr
));
234 si
= (struct sockaddr_in
*)&gr
.gr_group
;
235 gr
.gr_interface
= ifindex
;
236 si
->sin_family
= AF_INET
;
237 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
238 si
->sin_len
= sizeof(struct sockaddr_in
);
239 #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
240 si
->sin_addr
.s_addr
= mcast_addr
;
241 ret
= setsockopt(sock
, IPPROTO_IP
, (optname
== IP_ADD_MEMBERSHIP
) ?
242 MCAST_JOIN_GROUP
: MCAST_LEAVE_GROUP
, (void *)&gr
, sizeof(gr
));
243 if ((ret
< 0) && (optname
== IP_ADD_MEMBERSHIP
) && (errno
== EADDRINUSE
))
245 setsockopt(sock
, IPPROTO_IP
, MCAST_LEAVE_GROUP
, (void *)&gr
, sizeof(gr
));
246 ret
= setsockopt(sock
, IPPROTO_IP
, MCAST_JOIN_GROUP
, (void *)&gr
, sizeof(gr
));
250 #elif defined(HAVE_STRUCT_IP_MREQN_IMR_IFINDEX) && !defined(__FreeBSD__)
251 struct ip_mreqn mreqn
;
254 assert(optname
== IP_ADD_MEMBERSHIP
|| optname
== IP_DROP_MEMBERSHIP
);
255 memset (&mreqn
, 0, sizeof(mreqn
));
257 mreqn
.imr_multiaddr
.s_addr
= mcast_addr
;
258 mreqn
.imr_ifindex
= ifindex
;
260 ret
= setsockopt(sock
, IPPROTO_IP
, optname
,
261 (void *)&mreqn
, sizeof(mreqn
));
262 if ((ret
< 0) && (optname
== IP_ADD_MEMBERSHIP
) && (errno
== EADDRINUSE
))
264 /* see above: handle possible problem when interface comes back up */
265 char buf
[1][INET_ADDRSTRLEN
];
266 zlog_info("setsockopt_ipv4_multicast attempting to drop and "
267 "re-add (fd %d, mcast %s, ifindex %u)",
269 inet_ntop(AF_INET
, &mreqn
.imr_multiaddr
,
270 buf
[0], sizeof(buf
[0])), ifindex
);
271 setsockopt(sock
, IPPROTO_IP
, IP_DROP_MEMBERSHIP
,
272 (void *)&mreqn
, sizeof(mreqn
));
273 ret
= setsockopt(sock
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
,
274 (void *)&mreqn
, sizeof(mreqn
));
278 /* Example defines for another OS, boilerplate off other code in this
279 function, AND handle optname as per other sections for consistency !! */
280 /* #elif defined(BOGON_NIX) && EXAMPLE_VERSION_CODE > -100000 */
281 /* Add your favourite OS here! */
283 #elif defined(HAVE_BSD_STRUCT_IP_MREQ_HACK) /* #if OS_TYPE */
284 /* standard BSD API */
289 assert(optname
== IP_ADD_MEMBERSHIP
|| optname
== IP_DROP_MEMBERSHIP
);
292 memset (&mreq
, 0, sizeof(mreq
));
293 mreq
.imr_multiaddr
.s_addr
= mcast_addr
;
294 #if !defined __OpenBSD__
295 mreq
.imr_interface
.s_addr
= htonl (ifindex
);
297 mreq
.imr_interface
.s_addr
= if_addr
.s_addr
;
300 ret
= setsockopt (sock
, IPPROTO_IP
, optname
, (void *)&mreq
, sizeof(mreq
));
301 if ((ret
< 0) && (optname
== IP_ADD_MEMBERSHIP
) && (errno
== EADDRINUSE
))
303 /* see above: handle possible problem when interface comes back up */
304 char buf
[1][INET_ADDRSTRLEN
];
305 zlog_info("setsockopt_ipv4_multicast attempting to drop and "
306 "re-add (fd %d, mcast %s, ifindex %u)",
308 inet_ntop(AF_INET
, &mreq
.imr_multiaddr
,
309 buf
[0], sizeof(buf
[0])), ifindex
);
310 setsockopt (sock
, IPPROTO_IP
, IP_DROP_MEMBERSHIP
,
311 (void *)&mreq
, sizeof(mreq
));
312 ret
= setsockopt (sock
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
,
313 (void *)&mreq
, sizeof(mreq
));
318 #error "Unsupported multicast API"
319 #endif /* #if OS_TYPE */
324 * Set IP_MULTICAST_IF socket option in an OS-dependent manner.
327 setsockopt_ipv4_multicast_if(int sock
, struct in_addr if_addr
,
331 #ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
332 struct ip_mreqn mreqn
;
333 memset (&mreqn
, 0, sizeof(mreqn
));
335 mreqn
.imr_ifindex
= ifindex
;
336 return setsockopt(sock
, IPPROTO_IP
, IP_MULTICAST_IF
, (void *)&mreqn
, sizeof(mreqn
));
338 /* Example defines for another OS, boilerplate off other code in this
340 /* #elif defined(BOGON_NIX) && EXAMPLE_VERSION_CODE > -100000 */
341 /* Add your favourite OS here! */
342 #elif defined(HAVE_BSD_STRUCT_IP_MREQ_HACK)
345 #if !defined __OpenBSD__
346 m
.s_addr
= htonl (ifindex
);
348 m
.s_addr
= if_addr
.s_addr
;
351 return setsockopt (sock
, IPPROTO_IP
, IP_MULTICAST_IF
, (void *)&m
, sizeof(m
));
352 #elif defined(SUNOS_5)
353 char ifname
[IF_NAMESIZE
];
354 struct ifaddrs
*ifa
, *ifap
;
355 struct in_addr ifaddr
;
357 if (if_indextoname(ifindex
, ifname
) == NULL
)
360 if (getifaddrs(&ifa
) != 0)
363 for (ifap
= ifa
; ifap
!= NULL
; ifap
= ifap
->ifa_next
)
365 struct sockaddr_in
*sa
;
367 if (strcmp(ifap
->ifa_name
, ifname
) != 0)
369 if (ifap
->ifa_addr
->sa_family
!= AF_INET
)
371 sa
= (struct sockaddr_in
*)ifap
->ifa_addr
;
372 memcpy(&ifaddr
, &sa
->sin_addr
, sizeof(ifaddr
));
377 if (!ifap
) /* This means we did not find an IP */
380 return setsockopt(sock
, IPPROTO_IP
, IP_MULTICAST_IF
, (void *)&ifaddr
, sizeof(ifaddr
));
382 #error "Unsupported multicast API"
387 setsockopt_ipv4_multicast_loop (int sock
, u_char val
)
391 ret
= setsockopt (sock
, IPPROTO_IP
, IP_MULTICAST_LOOP
, (void *) &val
,
394 zlog_warn ("can't setsockopt IP_MULTICAST_LOOP");
400 setsockopt_ipv4_ifindex (int sock
, ifindex_t val
)
404 #if defined (IP_PKTINFO)
405 if ((ret
= setsockopt (sock
, IPPROTO_IP
, IP_PKTINFO
, &val
, sizeof (val
))) < 0)
406 zlog_warn ("Can't set IP_PKTINFO option for fd %d to %d: %s",
407 sock
,val
,safe_strerror(errno
));
408 #elif defined (IP_RECVIF)
409 if ((ret
= setsockopt (sock
, IPPROTO_IP
, IP_RECVIF
, &val
, sizeof (val
))) < 0)
410 zlog_warn ("Can't set IP_RECVIF option for fd %d to %d: %s",
411 sock
,val
,safe_strerror(errno
));
413 #warning "Neither IP_PKTINFO nor IP_RECVIF is available."
414 #warning "Will not be able to receive link info."
415 #warning "Things might be seriously broken.."
416 /* XXX Does this ever happen? Should there be a zlog_warn message here? */
423 setsockopt_ipv4_tos(int sock
, int tos
)
427 ret
= setsockopt (sock
, IPPROTO_IP
, IP_TOS
, &tos
, sizeof (tos
));
429 zlog_warn ("Can't set IP_TOS option for fd %d to %#x: %s",
430 sock
, tos
, safe_strerror(errno
));
436 setsockopt_ifindex (int af
, int sock
, ifindex_t val
)
443 ret
= setsockopt_ipv4_ifindex (sock
, val
);
446 ret
= setsockopt_ipv6_pktinfo (sock
, val
);
449 zlog_warn ("setsockopt_ifindex: unknown address family %d", af
);
455 * Requires: msgh is not NULL and points to a valid struct msghdr, which
456 * may or may not have control data about the incoming interface.
458 * Returns the interface index (small integer >= 1) if it can be
459 * determined, or else 0.
462 getsockopt_ipv4_ifindex (struct msghdr
*msgh
)
464 /* XXX: initialize to zero? (Always overwritten, so just cosmetic.) */
465 ifindex_t ifindex
= -1;
467 #if defined(IP_PKTINFO)
468 /* Linux pktinfo based ifindex retrieval */
469 struct in_pktinfo
*pktinfo
;
472 (struct in_pktinfo
*)getsockopt_cmsg_data (msgh
, IPPROTO_IP
, IP_PKTINFO
);
473 /* XXX Can pktinfo be NULL? Clean up post 0.98. */
474 ifindex
= pktinfo
->ipi_ifindex
;
476 #elif defined(IP_RECVIF)
478 /* retrieval based on IP_RECVIF */
481 /* BSD systems use a sockaddr_dl as the control message payload. */
482 struct sockaddr_dl
*sdl
;
484 /* SUNOS_5 uses an integer with the index. */
485 ifindex_t
*ifindex_p
;
491 (struct sockaddr_dl
*)getsockopt_cmsg_data (msgh
, IPPROTO_IP
, IP_RECVIF
);
493 ifindex
= sdl
->sdl_index
;
498 * Solaris. On Solaris 8, IP_RECVIF is defined, but the call to
499 * enable it fails with errno=99, and the struct msghdr has
502 ifindex_p
= (uint_t
*)getsockopt_cmsg_data (msgh
, IPPROTO_IP
, IP_RECVIF
);
503 if (ifindex_p
!= NULL
)
504 ifindex
= *ifindex_p
;
511 * Neither IP_PKTINFO nor IP_RECVIF defined - warn at compile time.
512 * XXX Decide if this is a core service, or if daemons have to cope.
513 * Since Solaris 8 and OpenBSD seem not to provide it, it seems that
514 * daemons have to cope.
516 #warning "getsockopt_ipv4_ifindex: Neither IP_PKTINFO nor IP_RECVIF defined."
517 #warning "Some daemons may fail to operate correctly!"
520 #endif /* IP_PKTINFO */
525 /* return ifindex, 0 if none found */
527 getsockopt_ifindex (int af
, struct msghdr
*msgh
)
532 return (getsockopt_ipv4_ifindex (msgh
));
535 return (getsockopt_ipv6_ifindex (msgh
));
538 zlog_warn ("getsockopt_ifindex: unknown address family %d", af
);
543 /* swab iph between order system uses for IP_HDRINCL and host order */
545 sockopt_iphdrincl_swab_htosys (struct ip
*iph
)
547 /* BSD and derived take iph in network order, except for
550 #ifndef HAVE_IP_HDRINCL_BSD_ORDER
551 iph
->ip_len
= htons(iph
->ip_len
);
552 iph
->ip_off
= htons(iph
->ip_off
);
553 #endif /* HAVE_IP_HDRINCL_BSD_ORDER */
555 iph
->ip_id
= htons(iph
->ip_id
);
559 sockopt_iphdrincl_swab_systoh (struct ip
*iph
)
561 #ifndef HAVE_IP_HDRINCL_BSD_ORDER
562 iph
->ip_len
= ntohs(iph
->ip_len
);
563 iph
->ip_off
= ntohs(iph
->ip_off
);
564 #endif /* HAVE_IP_HDRINCL_BSD_ORDER */
566 iph
->ip_id
= ntohs(iph
->ip_id
);
570 sockopt_tcp_rtt (int sock
)
574 socklen_t len
= sizeof(ti
);
576 if (getsockopt (sock
, IPPROTO_TCP
, TCP_INFO
, &ti
, &len
) != 0)
579 return ti
.tcpi_rtt
/ 1000;
586 sockopt_tcp_signature (int sock
, union sockunion
*su
, const char *password
)
588 #if defined(HAVE_TCP_MD5_LINUX24) && defined(GNU_LINUX)
589 /* Support for the old Linux 2.4 TCP-MD5 patch, taken from Hasso Tepper's
590 * version of the Quagga patch (based on work by Rick Payne, and Bruce
593 #define TCP_MD5_AUTH 13
594 #define TCP_MD5_AUTH_ADD 1
595 #define TCP_MD5_AUTH_DEL 2
596 struct tcp_rfc2385_cmd
{
597 u_int8_t command
; /* Command - Add/Delete */
598 u_int32_t address
; /* IPV4 address associated */
599 u_int8_t keylen
; /* MD5 Key len (do NOT assume 0 terminated ascii) */
600 void *key
; /* MD5 Key */
602 struct in_addr
*addr
= &su
->sin
.sin_addr
;
604 cmd
.command
= (password
!= NULL
? TCP_MD5_AUTH_ADD
: TCP_MD5_AUTH_DEL
);
605 cmd
.address
= addr
->s_addr
;
606 cmd
.keylen
= (password
!= NULL
? strlen (password
) : 0);
609 return setsockopt (sock
, IPPROTO_TCP
, TCP_MD5_AUTH
, &cmd
, sizeof cmd
);
611 #elif HAVE_DECL_TCP_MD5SIG
615 * XXX Need to do PF_KEY operation here to add/remove an SA entry,
616 * and add/remove an SP entry for this peer's packet flows also.
618 int md5sig
= password
&& *password
? 1 : 0;
620 int keylen
= password
? strlen (password
) : 0;
621 struct tcp_md5sig md5sig
;
622 union sockunion
*su2
, *susock
;
624 /* Figure out whether the socket and the sockunion are the same family..
625 * adding AF_INET to AF_INET6 needs to be v4 mapped, you'd think..
627 if (!(susock
= sockunion_getsockname (sock
)))
630 if (susock
->sa
.sa_family
== su
->sa
.sa_family
)
637 if (su2
->sa
.sa_family
== AF_INET
)
639 sockunion_free (susock
);
643 /* If this does not work, then all users of this sockopt will need to
644 * differentiate between IPv4 and IPv6, and keep seperate sockets for
647 * Sadly, it doesn't seem to work at present. It's unknown whether
648 * this is a bug or not.
650 if (su2
->sa
.sa_family
== AF_INET6
651 && su
->sa
.sa_family
== AF_INET
)
653 su2
->sin6
.sin6_family
= AF_INET6
;
654 /* V4Map the address */
655 memset (&su2
->sin6
.sin6_addr
, 0, sizeof (struct in6_addr
));
656 su2
->sin6
.sin6_addr
.s6_addr32
[2] = htonl(0xffff);
657 memcpy (&su2
->sin6
.sin6_addr
.s6_addr32
[3], &su
->sin
.sin_addr
, 4);
661 memset (&md5sig
, 0, sizeof (md5sig
));
662 memcpy (&md5sig
.tcpm_addr
, su2
, sizeof (*su2
));
663 md5sig
.tcpm_keylen
= keylen
;
665 memcpy (md5sig
.tcpm_key
, password
, keylen
);
666 sockunion_free (susock
);
667 #endif /* GNU_LINUX */
668 if ((ret
= setsockopt (sock
, IPPROTO_TCP
, TCP_MD5SIG
, &md5sig
, sizeof md5sig
)) < 0)
670 /* ENOENT is harmless. It is returned when we clear a password for which
671 one was not previously set. */
675 zlog_err ("sockopt_tcp_signature: setsockopt(%d): %s",
676 sock
, safe_strerror(errno
));
679 #else /* HAVE_TCP_MD5SIG */
681 #endif /* !HAVE_TCP_MD5SIG */