diff options
author | Chris Hall <chris.hall@highwayman.com> | 2011-08-12 15:06:06 +0100 |
---|---|---|
committer | Chris Hall <chris.hall@highwayman.com> | 2011-08-12 15:06:06 +0100 |
commit | cec1fae79110dffa900c0c5f38c3d3b48f5b0db6 (patch) | |
tree | 408055322e19098b98766168624f1b96865ac73b /lib/sockopt.c | |
parent | 228e06bad624a33090da4a09f32f8fed84a7e15c (diff) | |
parent | 7bd8653ef788a6395b07583d6766be8950598342 (diff) | |
download | quagga-ex18p.tar.bz2 quagga-ex18p.tar.xz |
Merge branch 'euro_ix' of /git/quagga.euro-ix into pipeworkex18p
Merge with euro_ix branch v0.99.18ex17.
Update version to: 0.99.18ex18p
Of particular note:
* includes support for GTSM:
neighbor ... ttl-security hops X
no neighbor ... ttl-security hops X
where X is 1-254. For usual case of immediately connected
peer, X == 1.
Cannot set ttl-security while ebgp-multihop is set, and
vice-versa.
If underlying O/S does not support GTSM, then will set ttl
as per ebgp-multihop.
In passing, have fixed various bugs in the main Quagga branch.
* initial support for draft-ietf-idr-optional-transitive
Does not yet support "neighbor-complete" flag.
* main Quagga now uses TCP_CORK and permanent non-blocking
Do not beleive TCP_CORK to be necessary for euro_ix code...
which has a different buffering strategy.
The euro_ix code already runs sockets permanently non-blocking.
* various fixes to attribute intern/unintern
Trying to remove memory leaks. Nobody seems convinced that
this has been perfected, yet.
* fixes for ospfd and ospf6d issues.
Up to date with master branch up to:
commit 538cb284864c17de66152a5236db4cd80e3e7639
Merge: 036a6e6 8ced4e8
Author: Paul Jakma <paul@quagga.net>
Date: Fri Jul 29 18:21:50 2011 +0100
Diffstat (limited to 'lib/sockopt.c')
-rw-r--r-- | lib/sockopt.c | 1145 |
1 files changed, 811 insertions, 334 deletions
diff --git a/lib/sockopt.c b/lib/sockopt.c index aa747429..083cafc2 100644 --- a/lib/sockopt.c +++ b/lib/sockopt.c @@ -1,4 +1,4 @@ -/* setsockopt functions +/* Setting and getting socket options -- utility functions. * Copyright (C) 1999 Kunihiro Ishiguro * * This file is part of GNU Zebra. @@ -20,218 +20,585 @@ */ #include <zebra.h> + #include "log.h" #include "sockopt.h" #include "sockunion.h" #include "pthread_safe.h" -int -setsockopt_so_recvbuf (int sock_fd, int size) +/*------------------------------------------------------------------------------ + * Set socket SO_REUSEADDR option + * + * Returns: >= 0 => OK + * < 0 => failed -- see errno + * + * Logs a LOG_WARNING message if fails. + */ +extern int +setsockopt_reuseaddr (int sock_fd) { int ret; + int on = 1; - ret = setsockopt (sock_fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) ; + ret = setsockopt (sock_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); if (ret < 0) { int err = errno ; - zlog_err ("socket %d: cannot setsockopt SO_RCVBUF to %d: %s", - sock_fd, size, errtoa(err, 0).str) ; + zlog_warn ("cannot set sockopt SO_REUSEADDR on socket %d: %s", sock_fd, + errtoa(err, 0).str) ; errno = err ; } ; - return ret; + return ret ; } -int -setsockopt_so_sendbuf (const int sock_fd, int size) +/*------------------------------------------------------------------------------ + * Set socket SO_REUSEPORT option -- if it is locally supported. + * + * Returns: >= 0 => OK -- or not supported + * < 0 => failed -- see errno + * + * Logs a LOG_WARNING message if fails. + */ +extern int +setsockopt_reuseport (int sock_fd) { - int ret ; + int ret; - ret = setsockopt (sock_fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)); +#ifdef SO_REUSEPORT + int on = 1; + ret = setsockopt (sock_fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)); +#else + ret = 0 ; +#endif if (ret < 0) { int err = errno ; - zlog_err ("socket %d: cannot setsockopt SO_SNDBUF to %d: %s", - sock_fd, size, errtoa(err, 0).str) ; + zlog_warn ("cannot set sockopt SO_REUSEPORT on socket %d: %s", sock_fd, + errtoa(err, 0).str) ; errno = err ; } ; - return ret; + return ret ; +} ; + +/*------------------------------------------------------------------------------ + * Set socket SO_BROADCAST option + * + * Returns: >= 0 => OK + * < 0 => failed -- see errno + * + * Logs a LOG_WARNING message if fails. + */ +extern int +setsockopt_broadcast (int sock_fd) +{ + int ret; + int on = 1; + + ret = setsockopt (sock_fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)); + if (ret < 0) + { + int err = errno ; + zlog_warn ("cannot set sockopt SO_BROADCAST on socket %d: %s", sock_fd, + errtoa(err, 0).str) ; + errno = err ; + } + return ret ; } -int -getsockopt_so_sendbuf (const int sock_fd) +/*------------------------------------------------------------------------------ + * Set TCP_CORK, if available. + * + * Returns: >= 0 => OK -- or not supported + * < 0 => failed -- see errno + * + * Logs a LOG_WARNING message if fails. + */ +extern int +setsockopt_cork (int sock_fd, int onoff) { - u_int32_t optval; - socklen_t optlen = sizeof (optval); +#ifdef TCP_CORK + int ret; - int ret = getsockopt (sock_fd, SOL_SOCKET, SO_SNDBUF, &optval, &optlen); + ret = setsockopt (sock_fd, IPPROTO_TCP, TCP_CORK, &onoff, sizeof(onoff)); if (ret < 0) - { - int err = errno ; - zlog_err ("socket %d: cannot getsockopt SO_SNDBUF: %s", + { + int err = errno ; + zlog_warn ("cannot set sockopt TCP_CORK to %d on socket %d: %s", onoff, sock_fd, errtoa(err, 0).str) ; - errno = err ; - return ret; - } - - return optval; + errno = err ; + } + return ret ; +#else + return 0; +#endif } -static void * -getsockopt_cmsg_data (struct msghdr *msgh, int level, int type) +/*------------------------------------------------------------------------------ + * Set IP_TTL/IPV6_UNICAST_HOPS for socket, if available + * + * The ttl given is the maximum number of hops to allow -- so will generally + * be 1 or 255 or some small number. + * + * NB: This code treats any ttl outside the range 1..MAXTTL as MAXTTL. + * + * Returns: >= 0 => OK -- or not supported + * < 0 => failed, see errno. + * + * Logs a LOG_WARNING message if fails. + * + * NB: for AF_INET6 where have: IN6_IS_ADDR_V4MAPPED, there is the question + * of whether to use IPPROTO_IP/IP_TTL or IPPROTO_IPV6/IPV6_UNICAST_HOPS. + * + * Here we try first to use the socket family, and if that fails on + * AF_INET6, then if the protocol family is AF_INET, then tries that. + */ +extern int +setsockopt_ttl (int sock_fd, int ttl) { - struct cmsghdr *cmsg; - void *ptr = NULL; + const char* name ; + int af ; + int ret ; - for (cmsg = ZCMSG_FIRSTHDR(msgh); - cmsg != NULL; - cmsg = CMSG_NXTHDR(msgh, cmsg)) - if (cmsg->cmsg_level == level && cmsg->cmsg_type) - return (ptr = CMSG_DATA(cmsg)); + af = sockunion_getsockfamily(sock_fd) ; + if (af < 0) + return af ; - return NULL; -} + if ((ttl < 1) || (ttl > MAXTTL)) + ttl = MAXTTL ; -#ifdef HAVE_IPV6 -/* Set IPv6 packet info to the socket. */ -int -setsockopt_ipv6_pktinfo (int sock_fd, int val) -{ - int ret; + ret = 0 ; + name = NULL ; -#ifdef IPV6_RECVPKTINFO /*2292bis-01*/ - ret = setsockopt(sock_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(val)); - if (ret < 0) + while (1) { - int err = errno ; - zlog_warn ("cannot setsockopt IPV6_RECVPKTINFO: %s", errtoa(err, 0).str); - errno = err ; + switch (af) + { + case AF_INET: +#ifdef IP_TTL + ret = setsockopt (sock_fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)); + name = "IP_TTL" ; +#endif /* IP_TTL */ + break ; + +#ifdef HAVE_IPV6 + case AF_INET6: + ret = setsockopt (sock_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, + &ttl, sizeof(ttl)); + name = "IPV6_UNICAST_HOPS" ; + + if (ret < 0) + { + af = sockunion_getprotofamily(sock_fd) ; + if (af == AF_INET) + continue ; + } ; + break ; +#endif + + default: /* ignore unknown family */ + break ; + } ; + + break ; } ; -#else /*RFC2292*/ - ret = setsockopt(sock_fd, IPPROTO_IPV6, IPV6_PKTINFO, &val, sizeof(val)); + if (ret < 0) { int err = errno ; - zlog_warn ("cannot setsockopt IPV6_PKTINFO: %s", errtoa(err, 0).str); + zlog_warn("cannot set sockopt %s to %d on socket %d: %s", name, ttl, + sock_fd, errtoa(err, 0).str) ; errno = err ; } ; -#endif /* INIA_IPV6 */ - return ret; -} -/* Set multicast hops val to the socket. */ -int -setsockopt_ipv6_checksum (int sock_fd, int val) + return ret ; +} ; + +/*------------------------------------------------------------------------------ + * Set IP_MINTTL/IPV6_MINHOPCOUNT (GTSM), if available. + * + * The ttl given is the maximum number of hops to allow -- so will generally + * be 1 -- which is the same as IP_TTL/IPV6_UNICAST_HOPS. + * + * NB: to turn off GTSM, need to set ttl = MAXTTL. This code treats any ttl + * outside the range 1..MAXTTL as MAXTTL. + * + * The underlying mechanics want MAX_TTL - (ttl - 1) -- and may not + * accept a value of zero. + * + * Returns: >= 0 => OK + * < 0 => failed or not supported -- see errno + * EOPNOTSUPP if not supported + * + * Logs a LOG_WARNING message if fails (and is supported). + * + * NB: for AF_INET6 where have: IN6_IS_ADDR_V4MAPPED, there is the question + * of whether to use IPPROTO_IP/IP_TTL or IPPROTO_IPV6/IPV6_UNICAST_HOPS. + * + * Here we try first to use the socket family, and if that fails on + * AF_INET6, then if the protocol family is AF_INET, then tries that. + */ +extern int +setsockopt_minttl (int sock_fd, int ttl) { - int ret; + const char* name ; + int af ; + int minttl ; + int ret ; -#ifdef GNU_LINUX - ret = setsockopt(sock_fd, IPPROTO_RAW, IPV6_CHECKSUM, &val, sizeof(val)); + af = sockunion_getsockfamily(sock_fd) ; + if (af < 0) + return af ; + + ret = 0 ; + name = NULL ; + + if ((ttl < 1) || (ttl > MAXTTL)) + ttl = MAXTTL ; + + minttl = MAXTTL - (ttl - 1) ; + + while (1) + { + enum + { +#ifdef IP_MINTTL + have_ip_minttl = true, #else - ret = setsockopt(sock_fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val, sizeof(val)); -#endif /* GNU_LINUX */ + have_ip_minttl = false, +#endif + ip_minttl = IP_MINTTL + 0 + } ; + +#ifdef HAVE_IPV6 + +# ifdef GNU_LINUX + /* The #include to bring in IPV6_MINHOPCOUNT is buried more or less as + * deep as we can get it, because it also redefines a number of things + * that we do not want redefined. + */ + #include <linux/in6.h> +# endif + + enum + { +# ifdef IPV6_MINHOPCOUNT + have_ipv6_minhopcount = true, +# else + have_ipv6_minhopcount = false, +# endif + ipv6_minhopcount = IPV6_MINHOPCOUNT + 0 + } ; +#endif /* HAVE_IPV6 */ + + switch (af) + { + case AF_INET: + name = "IP_MINTTL" ; + if (have_ip_minttl) + ret = setsockopt (sock_fd, IPPROTO_IP, ip_minttl, + &minttl, sizeof(minttl)); + else + { + ret = -1 ; + errno = EOPNOTSUPP ; + } ; + + break ; + +#ifdef HAVE_IPV6 + case AF_INET6: + name = "IPV6_MINHOPCOUNT" ; + if (have_ipv6_minhopcount) + ret = setsockopt (sock_fd, IPPROTO_IPV6, ipv6_minhopcount, + &minttl, sizeof(minttl)); + else + { + ret = -1 ; + errno = EOPNOTSUPP ; + } ; + + if (ret < 0) + { + af = sockunion_getprotofamily(sock_fd) ; + if (af == AF_INET) + continue ; + } ; + + break ; +#endif /* HAVE_IPV6 */ + + default: /* ignore unknown family */ + break ; + } ; + + break ; + } ; + if (ret < 0) { int err = errno ; - zlog_warn ("cannot setsockopt IPV6_CHECKSUM: %s", errtoa(err, 0).str); + zlog_warn("cannot set sockopt %s to %d on socket %d: %s", name, minttl, + sock_fd, errtoa(err, 0).str) ; errno = err ; } ; - return ret; -} -/* Set multicast hops val to the socket. */ -int -setsockopt_ipv6_multicast_hops (int sock_fd, int val) + return ret ; +} ; + +/*------------------------------------------------------------------------------ + * Set TCP MD5 signature socket option, if available. + * + * A NULL password or an empty password both signify unsetting the MD5 + * signature. + * + * Returns: >= 0 => OK (or not supported, but password NULL or empty) + * < 0 => failed or not supported, see errno. + * + * NB: returns EOPNOTSUPP if TCP MD5 is not supported and password is not NULL + * and is not empty. + * + * Logs a LOG_ERR message if fails (and is supported). + */ +extern int +setsockopt_tcp_signature (int sock_fd, sockunion su, const char *password) { - int ret; + int ret ; - ret = setsockopt(sock_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, - sizeof(val)); + if ((password != NULL) && (*password == '\0')) + password = NULL ; + + ret = 0 ; /* so far, so good */ + +#if defined(HAVE_TCP_MD5_LINUX24) && defined(GNU_LINUX) + /* Support for the old Linux 2.4 TCP-MD5 patch, taken from Hasso Tepper's + * version of the Quagga patch (based on work by Rick Payne, and Bruce + * Simpson) + */ +#define TCP_MD5_AUTH 13 +#define TCP_MD5_AUTH_ADD 1 +#define TCP_MD5_AUTH_DEL 2 + struct tcp_rfc2385_cmd { + u_int8_t command; /* Command - Add/Delete */ + u_int32_t address; /* IPV4 address associated */ + u_int8_t keylen; /* MD5 Key len (do NOT assume 0 terminated ascii) */ + void *key; /* MD5 Key */ + } cmd; + struct in_addr *addr = &su->sin.sin_addr; + + cmd.command = (password != NULL ? TCP_MD5_AUTH_ADD : TCP_MD5_AUTH_DEL); + cmd.address = addr->s_addr; + cmd.keylen = (password != NULL ? strlen (password) : 0); + cmd.key = password; + + ret = setsockopt (sock_fd, IPPROTO_TCP, TCP_MD5_AUTH, &cmd, sizeof cmd) ; if (ret < 0) { int err = errno ; - zlog_warn ("cannot setsockopt IPV6_MULTICAST_HOPS: %s", - errtoa(err, 0).str); + zlog_err("sockopt_tcp_signature: setsockopt(%d): %s", + sock_fd, errtoa(err, 0).str) ; errno = err ; + } + +#elif HAVE_DECL_TCP_MD5SIG + +# ifdef GNU_LINUX + struct tcp_md5sig md5sig ; +# ifdef HAVE_IPV6 + int saf ; + union sockunion sux[1] ; +# endif + + /* Testing reveals that in order to set an MD5 password for an AF_INET6 + * socket, the address passed in must be AF_INET6, even if what we are + * dealing with here is an IN6_IS_ADDR_V4MAPPED socket. + */ +# ifdef HAVE_IPV6 + saf = sockunion_getsockfamily(sock_fd) ; + if (saf < 0) + return saf ; + + if ((saf == AF_INET6) && (sockunion_family(su) == AF_INET)) + { + sockunion_copy (sux, su) ; + sockunion_map_ipv4 (sux) ; + su = sux ; /* substitute v4 mapped address */ } ; - return ret; -} +# endif -/* Set multicast hops val to the socket. */ -int -setsockopt_ipv6_unicast_hops (int sock_fd, int val) -{ - int ret; + /* Set address to AF_UNSPEC and key length and everything else to zero, + * then copy in the address and the key. + */ + memset (&md5sig, 0, sizeof (md5sig)) ; + confirm(AF_UNSPEC == 0) ; - ret = setsockopt(sock_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val)); - if (ret < 0) + memcpy (&md5sig.tcpm_addr, &su->sa, sockunion_get_len(su)) ; + + if (password != NULL) { - int err = errno ; - zlog_warn("cannot setsockopt IPV6_UNICAST_HOPS: %s", errtoa(err, 0).str); - errno = err ; + size_t keylen = strlen(password) ; + + if (md5sig.tcpm_keylen <= TCP_MD5SIG_MAXKEYLEN) + { + md5sig.tcpm_keylen = keylen ; + memcpy (md5sig.tcpm_key, password, keylen); + } + else + { + errno = EINVAL ; /* manufactured error */ + ret = -1 ; + } ; } ; - return ret; -} -int -setsockopt_ipv6_hoplimit (int sock_fd, int val) -{ - int ret; +# else + /* + * XXX Need to do PF_KEY operation here to add/remove an SA entry, + * and add/remove an SP entry for this peer's packet flows also. + */ + int md5sig = (password != NULL) ? 1 : 0; + +# endif /* GNU_LINUX */ + + if (ret >= 0) + { + ret = setsockopt(sock_fd, IPPROTO_TCP, TCP_MD5SIG, + &md5sig, sizeof(md5sig)) ; + if (ret < 0) + /* ENOENT is harmless. It is returned when we clear a password where + * one was not previously set. + */ + if ((errno == ENOENT) && (password == NULL)) + ret = 0 ; + } ; + +#else + + /* TCP MD5 is not supported */ + + if (password != NULL) + { + errno = EOPNOTSUPP ; /* manufactured error */ + ret = -1 ; + } ; + +#endif /* !HAVE_TCP_MD5SIG */ -#ifdef IPV6_RECVHOPLIMIT /*2292bis-01*/ - ret = setsockopt (sock_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &val, - sizeof(val)); if (ret < 0) { int err = errno ; - zlog_warn("cannot setsockopt IPV6_RECVHOPLIMIT: %s", errtoa(err, 0).str); + zlog_err("sockopt_tcp_signature: setsockopt(%d): %s", + sock_fd, errtoa(err, 0).str) ; errno = err ; } ; -#else /*RFC2292*/ - ret = setsockopt (sock_fd, IPPROTO_IPV6, IPV6_HOPLIMIT, &val, sizeof(val)); + + return ret ; +} ; + +/*------------------------------------------------------------------------------ + * Set SO_RCVBUF option on socket. + * + * Returns: >= 0 => OK + * < 0 => failed, see errno. + * + * Logs a LOG_ERR message if fails + */ +extern int +setsockopt_so_recvbuf (int sock_fd, int size) +{ + int ret; + + ret = setsockopt (sock_fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) ; if (ret < 0) { int err = errno ; - zlog_warn ("cannot setsockopt IPV6_HOPLIMIT: %s", errtoa(err, 0).str); + zlog_err ("cannot set sockopt SO_RCVBUF to %d on socket %d: %s", + size, sock_fd, errtoa(err, 0).str) ; errno = err ; } ; -#endif + return ret; } -/* Set multicast loop zero to the socket. */ -int -setsockopt_ipv6_multicast_loop (int sock_fd, int val) +/*------------------------------------------------------------------------------ + * Set SO_SNDBUF option on socket. + * + * Returns: >= 0 => OK + * < 0 => failed, see errno. + * + * Logs a LOG_ERR message if fails + */ +extern int +setsockopt_so_sendbuf (int sock_fd, int size) { - int ret; + int ret ; + + ret = setsockopt (sock_fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)); - ret = setsockopt (sock_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, - sizeof (val)); if (ret < 0) { int err = errno ; - zlog_warn ("cannot setsockopt IPV6_MULTICAST_LOOP: %s", - errtoa(err, 0).str); + zlog_err("cannot set sockopt SO_SNDBUF to %d on socket %d: %s", + size, sock_fd, errtoa(err, 0).str) ; errno = err ; } ; + return ret; } -static int -getsockopt_ipv6_ifindex (struct msghdr *msgh) +/*------------------------------------------------------------------------------ + * Get SO_SNDBUF option value from socket. + * + * Returns: >= 0 => OK == value of option + * < 0 => failed, see errno. + * + * Logs a LOG_ERR message if fails + */ +extern int +getsockopt_so_sendbuf (int sock_fd) { - struct in6_pktinfo *pktinfo; + u_int32_t optval; + socklen_t optlen = sizeof (optval); - pktinfo = getsockopt_cmsg_data (msgh, IPPROTO_IPV6, IPV6_PKTINFO); + int ret = getsockopt (sock_fd, SOL_SOCKET, SO_SNDBUF, &optval, &optlen); + if (ret < 0) + { + int err = errno ; + zlog_err ("cannot get sockopt SO_SNDBUF on socket %d: %s", + sock_fd, errtoa(err, 0).str) ; + errno = err ; + return ret; + } - return pktinfo->ipi6_ifindex; + return optval; } -#endif /* HAVE_IPV6 */ +/*------------------------------------------------------------------------------ + * Set IP_TOS option for AF_INET socket. + * + * Returns: >= 0 => OK + * < 0 => failed, see errno. + * + * Logs a LOG_ERR message if fails + */ +extern int +setsockopt_ipv4_tos(int sock_fd, int tos) +{ + int ret; -/* + ret = setsockopt (sock_fd, IPPROTO_IP, IP_TOS, &tos, sizeof (tos)); + if (ret < 0) + { + int err = errno ; + zlog_err("cannot set sockopt IP_TOS option %#x on socket %d: %s", + tos, sock_fd, errtoa(err, 0).str) ; + errno = err ; + } ; + return ret; +} ; + +/*------------------------------------------------------------------------------ * Process multicast socket options for IPv4 in an OS-dependent manner. * Supported options are IP_MULTICAST_IF and IP_{ADD,DROP}_MEMBERSHIP. * @@ -252,7 +619,7 @@ getsockopt_ipv6_ifindex (struct msghdr *msgh) * but this behavior should not be harmful if they behave the same way, * allow leaves, or implicitly leave all groups joined to down interfaces. */ -int +extern int setsockopt_multicast_ipv4(int sock_fd, int optname, struct in_addr if_addr /* required */, @@ -365,27 +732,83 @@ setsockopt_multicast_ipv4(int sock_fd, } +/*============================================================================== + * Set pktinfo and get ifindex etc + */ + +static int setsockopt_ipv4_pktinfo (int sock_fd, int val) ; +static int getsockopt_ipv4_ifindex (struct msghdr *msgh) ; + +#ifdef HAVE_IPV6 +static int getsockopt_ipv6_ifindex (struct msghdr *msgh) ; +#endif + +static void * getsockopt_cmsg_data (struct msghdr *msgh, int level, int type) ; + +/*------------------------------------------------------------------------------ + * Set IP_PKTINFO/IP_RECVIF or IPV6_RECVPKTINFO/IPV6_PKTINFO -- if available. + * + * Returns: >= 0 => OK + * < 0 => failed -- see errno + * + * Logs a LOG_ERR message if fails. + */ +extern int +setsockopt_pktinfo (int af, int sock_fd, int val) +{ + int ret = -1; + + switch (af) + { + case AF_INET: + ret = setsockopt_ipv4_pktinfo (sock_fd, val); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + ret = setsockopt_ipv6_pktinfo (sock_fd, val); + break; +#endif + default: + zlog_warn("setsockopt_ifindex: unknown address family %d", af) ; + ret = -1 ; + errno = EINVAL; + break ; + } + return ret; +} + +/*------------------------------------------------------------------------------ + * Set IP_PKTINFO or IP_RECVIF -- if available. + * + * Returns: >= 0 => OK + * < 0 => failed -- see errno + * + * Logs a LOG_ERR message if fails. + */ static int -setsockopt_ipv4_ifindex (int sock_fd, int val) +setsockopt_ipv4_pktinfo (int sock_fd, int val) { int ret; -#if defined (IP_PKTINFO) - ret = setsockopt (sock_fd, IPPROTO_IP, IP_PKTINFO, &val, sizeof (val)) ; - if (ret < 0) - { - int err = errno ; - zlog_warn ("Can't set IP_PKTINFO option for fd %d to %d: %s", - sock_fd, val, errtoa(err, 0).str); - errno = err ; - } ; -#elif defined (IP_RECVIF) - ret = setsockopt (sock_fd, IPPROTO_IP, IP_RECVIF, &val, sizeof (val)) ; +#if defined(IP_PKTINFO) || defined(IP_RECVIF) + + int opt ; + const char* name ; + +# if defined(IP_PKTINFO) + opt = IP_PKTINFO ; + name = "IP_PKTINFO" ; +# else + opt = IP_RECVIF ; + name = "IP_RECVIF" ; +# endif + + ret = setsockopt (sock_fd, IPPROTO_IP, opt, &val, sizeof (val)) ; if (ret < 0) { int err = errno ; - zlog_warn ("Can't set IP_RECVIF option for fd %d to %d: %s", - sock_fd, val, errtoa(err, 0).str); + zlog_err("cannot set sockopt %s to %d on socket %d: %s", name, + val, sock_fd, errtoa(err, 0).str); errno = err ; } ; #else @@ -393,53 +816,82 @@ setsockopt_ipv4_ifindex (int sock_fd, int val) #warning "Will not be able to receive link info." #warning "Things might be seriously broken.." /* XXX Does this ever happen? Should there be a zlog_warn message here? */ - ret = -1; + ret = -1; + errno = EOPNOTSUPP ; /* manufactured error */ #endif return ret; } -int -setsockopt_ipv4_tos(int sock_fd, int tos) +/*------------------------------------------------------------------------------ + * Set IPV6_RECVPKTINFO (RFC3542) or IPV6_RECVIF (RFC2292) -- if available. + * + * Returns: >= 0 => OK + * < 0 => failed -- see errno + * + * Logs a LOG_ERR message if fails. + */ +#ifdef HAVE_IPV6 +extern int +setsockopt_ipv6_pktinfo (int sock_fd, int val) { int ret; + int opt ; + const char* name ; - ret = setsockopt (sock_fd, IPPROTO_IP, IP_TOS, &tos, sizeof (tos)); +# ifdef IPV6_RECVPKTINFO + opt = IPV6_RECVPKTINFO ; /* RFC3542 == RFC2292-bis */ + name = "IPV6_RECVPKTINFO" ; +# else + opt = IPV6_PKTINFO ; /* RFC2292 */ + name = "IPV6_PKTINFO" ; +# endif + + ret = setsockopt(sock_fd, IPPROTO_IPV6, opt, &val, sizeof(val)); if (ret < 0) { int err = errno ; - zlog_warn ("Can't set IP_TOS option for fd %d to %#x: %s", - sock_fd, tos, errtoa(err, 0).str); + zlog_err("cannot set sockopt %s to %d on socket %d: %s", name, + val, sock_fd, errtoa(err, 0).str); errno = err ; } ; + return ret; } +#endif - -int -setsockopt_ifindex (int af, int sock_fd, int val) +/*------------------------------------------------------------------------------ + * Given a struct msghdr*, extract and return ifindex. + * + * Returns: > 0 => OK == ifindex + * 0 => none found or error + * + * Note: this is badly named, since it is not really a getsockopt() operation, + * but extracting data from a sendmsg/recvmsg struct msghdr. + * + * To have the ifindex returned, need to have setsockopt_pktinfo(). + */ +extern int +getsockopt_ifindex (int af, struct msghdr *msgh) { - int ret = -1; - switch (af) { case AF_INET: - ret = setsockopt_ipv4_ifindex (sock_fd, val); - break; + return (getsockopt_ipv4_ifindex (msgh)); + #ifdef HAVE_IPV6 case AF_INET6: - ret = setsockopt_ipv6_pktinfo (sock_fd, val); - break; + return (getsockopt_ipv6_ifindex (msgh)); #endif + default: - zlog_warn ("setsockopt_ifindex: unknown address family %d", af); - ret = -1 ; - errno = EINVAL; - break ; + zlog_warn ("getsockopt_ifindex: unknown address family %d", af); + return 0 ; } - return ret; } -/* +/*------------------------------------------------------------------------------ + * AF_INET: extract ifindex from struct msghdr, if can + * * Requires: msgh is not NULL and points to a valid struct msghdr, which * may or may not have control data about the incoming interface. * @@ -449,17 +901,17 @@ setsockopt_ifindex (int af, int sock_fd, int val) static int getsockopt_ipv4_ifindex (struct msghdr *msgh) { - /* XXX: initialize to zero? (Always overwritten, so just cosmetic.) */ - int ifindex = -1; + int ifindex ; #if defined(IP_PKTINFO) /* Linux pktinfo based ifindex retrieval */ struct in_pktinfo *pktinfo; - pktinfo = - (struct in_pktinfo *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_PKTINFO); - /* XXX Can pktinfo be NULL? Clean up post 0.98. */ - ifindex = pktinfo->ipi_ifindex; + pktinfo = getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_PKTINFO); + if (pktinfo != NULL) + ifindex = pktinfo->ipi_ifindex ; + else + ifindex = 0 ; #elif defined(IP_RECVIF) @@ -510,34 +962,73 @@ getsockopt_ipv4_ifindex (struct msghdr *msgh) return ifindex; } -/* return ifindex, 0 if none found */ -int -getsockopt_ifindex (int af, struct msghdr *msgh) +/*------------------------------------------------------------------------------ + * AF_INET6: extract ifindex from struct msghdr, if can + * + * Requires: msgh is not NULL and points to a valid struct msghdr, which + * may or may not have control data about the incoming interface. + * + * Returns the interface index (small integer >= 1) if it can be + * determined, or else 0. + */ +#ifdef HAVE_IPV6 +static int +getsockopt_ipv6_ifindex (struct msghdr *msgh) { - int ifindex = 0; + struct in6_pktinfo *pktinfo; - switch (af) - { - case AF_INET: - return (getsockopt_ipv4_ifindex (msgh)); - break; -#ifdef HAVE_IPV6 - case AF_INET6: - return (getsockopt_ipv6_ifindex (msgh)); - break; + pktinfo = getsockopt_cmsg_data (msgh, IPPROTO_IPV6, IPV6_PKTINFO); + + if (pktinfo != NULL) + return pktinfo->ipi6_ifindex; + else + return 0 ; +} #endif - default: - zlog_warn ("getsockopt_ifindex: unknown address family %d", af); - return (ifindex = 0); - } + +/*------------------------------------------------------------------------------ + * Scan msg_control portion of struct msghdr, looking for a cmsg with the given + * level and type. + * + * Requires: msgh is not NULL and points to a valid struct msghdr, which + * may or may not have control data about the incoming interface. + * + * Returns: address of data part of cmsg + * or: NULL => not found + */ +static void * +getsockopt_cmsg_data (struct msghdr *msgh, int level, int type) +{ + struct cmsghdr *cmsg; + + for (cmsg = ZCMSG_FIRSTHDR(msgh); + cmsg != NULL; + cmsg = CMSG_NXTHDR(msgh, cmsg)) + if ((cmsg->cmsg_level == level) && (cmsg->cmsg_type == type)) + return (void*)CMSG_DATA(cmsg); + + return NULL; } -/* swab iph between order system uses for IP_HDRINCL and host order */ -void +/*------------------------------------------------------------------------------ + * swab iph between order system uses for IP_HDRINCL and host order + * + * This is done before handing struct ip to the system. + * + * There are four u_short fields in the IPv4 header: + * + * u_short ip_len -- convert to network order, except as noted below + * u_short ip_id -- convert to network order + * u_short ip_off -- convert to network order, except as noted below + * u_short ip_sum -- which we don't touch -- set by kernel + */ +extern void sockopt_iphdrincl_swab_htosys (struct ip *iph) { - /* BSD and derived take iph in network order, except for - * ip_len and ip_off + /* BSD and derived take iph in network order, except for ip_len and ip_off. + * + * So if *not* BSD-like, then need to convert ip_len and ip_off to network + * order. */ #ifndef HAVE_IP_HDRINCL_BSD_ORDER iph->ip_len = htons(iph->ip_len); @@ -547,7 +1038,12 @@ sockopt_iphdrincl_swab_htosys (struct ip *iph) iph->ip_id = htons(iph->ip_id); } -void +/*------------------------------------------------------------------------------ + * swab iph between order system uses for IP_HDRINCL and host order + * + * This is done after receiving struct ip from the system -- see notes above. + */ +extern void sockopt_iphdrincl_swab_systoh (struct ip *iph) { #ifndef HAVE_IP_HDRINCL_BSD_ORDER @@ -559,190 +1055,171 @@ sockopt_iphdrincl_swab_systoh (struct ip *iph) } /*============================================================================== - * Set TCP MD5 signature socket option. + * IPv6 Stuff + */ +#ifdef HAVE_IPV6 + +/*------------------------------------------------------------------------------ + * Set IPV6_V6ONLY. * * Returns: >= 0 => OK - * < 0 => failed, see errno. + * < 0 => failed -- see errno * - * NB: returns ENOSYS if TCP MD5 is not supported + * Logs a LOG_ERR message if fails. */ -int -sockopt_tcp_signature (int sock_fd, union sockunion *su, const char *password) +extern int +setsockopt_ipv6_v6only(int sock_fd) { -#if defined(HAVE_TCP_MD5_LINUX24) && defined(GNU_LINUX) - int ret ; - - /* Support for the old Linux 2.4 TCP-MD5 patch, taken from Hasso Tepper's - * version of the Quagga patch (based on work by Rick Payne, and Bruce - * Simpson) - */ -#define TCP_MD5_AUTH 13 -#define TCP_MD5_AUTH_ADD 1 -#define TCP_MD5_AUTH_DEL 2 - struct tcp_rfc2385_cmd { - u_int8_t command; /* Command - Add/Delete */ - u_int32_t address; /* IPV4 address associated */ - u_int8_t keylen; /* MD5 Key len (do NOT assume 0 terminated ascii) */ - void *key; /* MD5 Key */ - } cmd; - struct in_addr *addr = &su->sin.sin_addr; - - cmd.command = (password != NULL ? TCP_MD5_AUTH_ADD : TCP_MD5_AUTH_DEL); - cmd.address = addr->s_addr; - cmd.keylen = (password != NULL ? strlen (password) : 0); - cmd.key = password; - - ret = setsockopt (sock_fd, IPPROTO_TCP, TCP_MD5_AUTH, &cmd, sizeof cmd) ; - - return (ret >= 0) ? 0 : -1 ; - -#elif HAVE_DECL_TCP_MD5SIG - int ret, err ; -# ifndef GNU_LINUX - /* - * XXX Need to do PF_KEY operation here to add/remove an SA entry, - * and add/remove an SP entry for this peer's packet flows also. - */ - int md5sig = password && *password ? 1 : 0; -# else - int keylen = password ? strlen (password) : 0 ; - struct tcp_md5sig md5sig ; - union sockunion *su2 ; - union sockunion susock ; + int ret; + int on = 1 ; - /* Figure out whether the socket and the sockunion are the same family.. - * adding AF_INET to AF_INET6 needs to be v4 mapped, you'd think.. - */ - ret = sockunion_getsockname(sock_fd, &susock) ; + ret = setsockopt (sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); if (ret < 0) - return ret ; - - if (susock.sa.sa_family == su->sa.sa_family) - su2 = su ; - else { - /* oops.. */ - su2 = &susock ; - - if (su2->sa.sa_family == AF_INET) - return 0 ; /* TODO: find out what this is doing ?? */ - -# ifdef HAVE_IPV6 - /* If this does not work, then all users of this sockopt will need to - * differentiate between IPv4 and IPv6, and keep separate sockets for - * each. - * - * Sadly, it doesn't seem to work at present. It's unknown whether - * this is a bug or not. - */ - if (su2->sa.sa_family == AF_INET6 - && su->sa.sa_family == AF_INET) - { - su2->sin6.sin6_family = AF_INET6; - /* V4Map the address */ - memset (&su2->sin6.sin6_addr, 0, sizeof (struct in6_addr)); - su2->sin6.sin6_addr.s6_addr[10] = 0xff ; - su2->sin6.sin6_addr.s6_addr[11] = 0xff ; -# ifdef s6_addr32 - su2->sin6.sin6_addr.s6_addr32[3] = su->sin.sin_addr.s_addr ; -# else - memcpy (&su2->sin6.sin6_addr.s6_addr[12], &su->sin.sin_addr, 4); -# endif - } -# endif + int err = errno ; + zlog_err ("cannot set sockopt IPV6_V6ONLY on socket %d: %s", + sock_fd, errtoa(err, 0).str) ; + errno = err ; } + return ret ; +} ; - memset (&md5sig, 0, sizeof (md5sig)); - memcpy (&md5sig.tcpm_addr, su2, sizeof (*su2)); - md5sig.tcpm_keylen = keylen; - if (keylen) - memcpy (md5sig.tcpm_key, password, keylen); - -# endif /* GNU_LINUX */ +/*------------------------------------------------------------------------------ + * Set IPV6_CHECKSUM + * + * Returns: >= 0 => OK + * < 0 => failed -- see errno + * + * Logs a LOG_ERR message if fails. + */ +extern int +setsockopt_ipv6_checksum (int sock_fd, int val) +{ + int ret; - err = 0 ; - ret = setsockopt(sock_fd, IPPROTO_TCP, TCP_MD5SIG, &md5sig, sizeof(md5sig)) ; +#ifdef GNU_LINUX + ret = setsockopt(sock_fd, IPPROTO_RAW, IPV6_CHECKSUM, &val, sizeof(val)); +#else + ret = setsockopt(sock_fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val, sizeof(val)); +#endif /* GNU_LINUX */ if (ret < 0) { - err = errno ; - /* ENOENT is harmless. It is returned when we clear a password for which - one was not previously set. */ - if (err == ENOENT) - err = 0 ; - else - { - zlog_err ("sockopt_tcp_signature: setsockopt(%d): %s", + int err = errno ; + zlog_err("cannot set sockopt IPV6_CHECKSUM to %d on socket %d: %s", val, sock_fd, errtoa(err, 0).str) ; - errno = err ; - } ; - } - - return (err == 0) ? 0 : -1 ; - -#else + errno = err ; + } ; + return ret; +} - /* TCP MD5 is not supported */ +/*------------------------------------------------------------------------------ + * Set unicast hops val to the socket (cf IP_TTL). + * + * Returns: >= 0 => OK + * < 0 => failed -- see errno + * + * Logs a LOG_ERR message if fails. + */ +extern int +setsockopt_ipv6_unicast_hops (int sock_fd, int val) +{ + int ret; - if ((password == NULL) || (*password == '\0')) - return 0 ; /* OK if not required ! */ + ret = setsockopt(sock_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val)); + if (ret < 0) + { + int err = errno ; + zlog_err("cannot set sockopt IPV6_UNICAST_HOPS to %d on socket %d: %s", + val, sock_fd, errtoa(err, 0).str) ; + errno = err ; + } ; + return ret; +} - errno = ENOSYS ; /* manufactured error */ - return -1 ; +/*------------------------------------------------------------------------------ + * Set multicast hops val to the socket. + * + * Returns: >= 0 => OK + * < 0 => failed -- see errno + * + * Logs a LOG_ERR message if fails. + */ +extern int +setsockopt_ipv6_multicast_hops (int sock_fd, int val) +{ + int ret; -#endif /* !HAVE_TCP_MD5SIG */ -} ; + ret = setsockopt(sock_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, + sizeof(val)); + if (ret < 0) + { + int err = errno ; + zlog_err("cannot set sockopt IPV6_MULTICAST_HOPS to %d on socket %d: %s", + val, sock_fd, errtoa(err, 0).str) ; + errno = err ; + } ; + return ret; +} -/*============================================================================== - * Set TTL for socket +/*------------------------------------------------------------------------------ + * Set IPV6_RECVHOPLIMIT option (or IPV6_HOPLIMIT) * * Returns: >= 0 => OK - * < 0 => failed, see errno. + * < 0 => failed -- see errno + * + * Logs a LOG_ERR message if fails. */ -int -sockopt_ttl (int sock_fd, int ttl) +extern int +setsockopt_ipv6_hoplimit (int sock_fd, int val) { - const char* msg ; - int ret ; - int family ; - - ret = 0 ; - msg = NULL ; + int ret; + int opt ; + const char* name ; - family = sockunion_getsockfamily(sock_fd) ; - if (family < 0) - return -1 ; +# ifdef IPV6_RECVHOPLIMIT + opt = IPV6_RECVHOPLIMIT ; /* RFC3542 == RFC2292-bis */ + name = "IPV6_RECVHOPLIMIT" ; +# else + opt = IPV6_HOPLIMIT ; /* RFC2292 */ + name = "IPV6_HOPLIMIT" ; +# endif - switch (family) - { - case AF_INET: -#ifdef IP_TTL - ret = setsockopt (sock_fd, IPPROTO_IP, IP_TTL,(void*)&ttl, sizeof(int)); - msg = "IP_TTL" ; -#endif /* IP_TTL */ - break ; + ret = setsockopt(sock_fd, IPPROTO_IPV6, opt, &val, sizeof(val)); + if (ret < 0) + { + int err = errno ; + zlog_err("cannot set sockopt %s to %d on socket %d: %s", name, + val, sock_fd, errtoa(err, 0).str); + errno = err ; + } ; -#ifdef HAVE_IPV6 - case AF_INET6: - ret = setsockopt (sock_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, - (void*)&ttl, sizeof(int)); - msg = "IPV6_UNICAST_HOPS" ; - break ; -#endif + return ret; +} - default: /* ignore unknown family */ - ret = 0 ; - break ; - } ; +/*------------------------------------------------------------------------------ + * Set IPV6_MULTICAST_LOOP option + * + * Returns: >= 0 => OK + * < 0 => failed -- see errno + * + * Logs a LOG_ERR message if fails. + */ +extern int +setsockopt_ipv6_multicast_loop (int sock_fd, int val) +{ + int ret; - if (ret != 0) + ret = setsockopt (sock_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, + sizeof (val)); + if (ret < 0) { int err = errno ; - zlog (NULL, LOG_WARNING, - "cannot set sockopt %s %d to socket %d: %s", msg, ttl, sock_fd, - errtoa(err, 0).str) ; + zlog_err("cannot set sockopt IPV6_MULTICAST_LOOP to %d on socket %d: %s", + val, sock_fd, errtoa(err, 0).str); errno = err ; - return -1 ; } ; + return ret; +} +#endif /* HAVE_IPV6 */ + - return 0 ; -} ; |