summaryrefslogtreecommitdiffstats
path: root/lib/sockopt.c
diff options
context:
space:
mode:
authorChris Hall <chris.hall@highwayman.com>2011-08-12 15:06:06 +0100
committerChris Hall <chris.hall@highwayman.com>2011-08-12 15:06:06 +0100
commitcec1fae79110dffa900c0c5f38c3d3b48f5b0db6 (patch)
tree408055322e19098b98766168624f1b96865ac73b /lib/sockopt.c
parent228e06bad624a33090da4a09f32f8fed84a7e15c (diff)
parent7bd8653ef788a6395b07583d6766be8950598342 (diff)
downloadquagga-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.c1145
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 ;
-} ;