summaryrefslogtreecommitdiffstats
path: root/lib/sockunion.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sockunion.c')
-rw-r--r--lib/sockunion.c1289
1 files changed, 810 insertions, 479 deletions
diff --git a/lib/sockunion.c b/lib/sockunion.c
index a5382a72..b34d7047 100644
--- a/lib/sockunion.c
+++ b/lib/sockunion.c
@@ -16,7 +16,7 @@
* You should have received a copy of the GNU General Public License
* along with GNU Zebra; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
+ * 02111-1307, USA.
*/
#include <zebra.h>
@@ -28,6 +28,8 @@
#include "str.h"
#include "log.h"
+#include "symtab.h"
+
#ifndef HAVE_INET_ATON
int
inet_aton (const char *cp, struct in_addr *inaddr)
@@ -95,13 +97,13 @@ inet_ntop (int family, const void *addrptr, char *strptr, size_t len)
{
unsigned char *p = (unsigned char *) addrptr;
- if (family == AF_INET)
+ if (family == AF_INET)
{
char temp[INET_ADDRSTRLEN];
snprintf(temp, sizeof(temp), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
- if (strlen(temp) >= len)
+ if (strlen(temp) >= len)
{
errno = ENOSPC;
return NULL;
@@ -115,460 +117,563 @@ inet_ntop (int family, const void *addrptr, char *strptr, size_t len)
}
#endif /* ! HAVE_INET_NTOP */
-const char *
-inet_sutop (union sockunion *su, char *str)
+/*------------------------------------------------------------------------------
+ * Set the sockunion size (sin_len or sin6_len), if required.
+ *
+ * NB: POSIX does not require this and Stevens et al say that even where it
+ * is supported, the application need not worry about it.
+ *
+ * However... the code as found does this.
+ *
+ * TODO: is it *really* necessary to set sin_len or sin6_len ??
+ *
+ * Returns: the sockunion size
+ */
+inline static int
+sockunion_sin_len(sockunion su)
+{
+ return
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ su->sin.sin_len =
+#endif
+ sizeof(struct sockaddr_in);
+} ;
+
+#ifdef HAVE_IPV6
+inline static int
+sockunion_sin6_len(sockunion su)
+{
+ return
+#ifdef SIN6_LEN
+ su->sin6.sin6_len =
+#endif
+ sizeof(struct sockaddr_in6);
+} ;
+#endif
+
+/*------------------------------------------------------------------------------
+ * Set the address family for the given sockunion.
+ *
+ * If sin_len or sin6_len entry is present, fill that in too.
+ *
+ * Assumes the address family is valid !
+ *
+ * Returns: 0
+ */
+inline static int
+sockunion_set_family(sockunion su, sa_family_t family)
+{
+ su->sa.sa_family = family ;
+
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ if (family == AF_INET)
+ sockunion_sin_len(su) ;
+#endif
+#if defined(HAVE_IPV6) && defined(SIN6_LEN)
+ if (family == AF_INET6)
+ sockunion_sin6_len(su) ;
+#endif
+
+ return 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set the given sockunion address to "any"
+ */
+static void
+sockunion_set_addr_any(sockunion su)
{
switch (su->sa.sa_family)
- {
+ {
case AF_INET:
- inet_ntop (AF_INET, &su->sin.sin_addr, str, INET_ADDRSTRLEN);
- break;
+ su->sin.sin_addr.s_addr = htonl (INADDR_ANY);
+ return ;
+
#ifdef HAVE_IPV6
case AF_INET6:
- inet_ntop (AF_INET6, &su->sin6.sin6_addr, str, INET6_ADDRSTRLEN);
- break;
-#endif /* HAVE_IPV6 */
- }
- return str;
-}
+# if defined(LINUX_IPV6) || defined(NRL)
+ memset (&su->sin6.sin6_addr, 0, sizeof (struct in6_addr));
+# else
+ su->sin6.sin6_addr = in6addr_any;
+# endif /* LINUX_IPV6 || defined(NRL) */
+ return ;
+#endif
-int
-str2sockunion (const char *str, union sockunion *su)
-{
- int ret;
+ default:
+ return ;
+ } ;
+} ;
- memset (su, 0, sizeof (union sockunion));
+/*------------------------------------------------------------------------------
+ * Set the port number in the given sockunion.
+ *
+ * For good measure, set the size (if that's required) and return same.
+ */
+extern int
+sockunion_set_port(sockunion su, in_port_t port)
+{
+ switch (su->sa.sa_family)
+ {
+ case AF_INET:
+ su->sin.sin_port = htons(port) ;
+ return sockunion_sin_len(su) ;
- ret = inet_pton (AF_INET, str, &su->sin.sin_addr);
- if (ret > 0) /* Valid IPv4 address format. */
- {
- su->sin.sin_family = AF_INET;
-#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
- su->sin.sin_len = sizeof(struct sockaddr_in);
-#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
- return 0;
- }
#ifdef HAVE_IPV6
- ret = inet_pton (AF_INET6, str, &su->sin6.sin6_addr);
- if (ret > 0) /* Valid IPv6 address format. */
- {
- su->sin6.sin6_family = AF_INET6;
-#ifdef SIN6_LEN
- su->sin6.sin6_len = sizeof(struct sockaddr_in6);
-#endif /* SIN6_LEN */
- return 0;
- }
-#endif /* HAVE_IPV6 */
- return -1;
-}
+ case AF_INET6:
+ su->sin6.sin6_port = htons(port) ;
+ return sockunion_sin6_len(su) ;
+#endif
-const char *
-sockunion2str (union sockunion *su, char *buf, size_t len)
+ default:
+ return 0 ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Initialise a new sockunion -- for the given address family (if any)
+ *
+ * Allocates a sockunion if required.
+ *
+ * Result is set "any".
+ *
+ * Advice is to zeroize sockaddr_in6, in particular.
+ */
+extern sockunion
+sockunion_init_new(sockunion su, sa_family_t family)
{
- if (su->sa.sa_family == AF_INET)
- return inet_ntop (AF_INET, &su->sin.sin_addr, buf, len);
+ if (su == NULL)
+ su = XCALLOC(MTYPE_SOCKUNION, sizeof(union sockunion)) ;
+ else
+ memset(su, 0, sizeof(union sockunion)) ;
+
+ if (family != AF_UNSPEC)
+ sockunion_set_family(su, family) ;
+ else
+ confirm(AF_UNSPEC == 0) ;
+
+ return su ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get the length of the address in the given sockunion.
+ *
+ * Returns zero if AF_UNSPEC or not any known address family.
+ */
+extern int
+sockunion_get_len(sockunion su)
+{
+ switch (su->sa.sa_family)
+ {
+ case AF_INET:
+ return sizeof(struct sockaddr_in) ;
+
#ifdef HAVE_IPV6
- else if (su->sa.sa_family == AF_INET6)
- return inet_ntop (AF_INET6, &su->sin6.sin6_addr, buf, len);
-#endif /* HAVE_IPV6 */
- return NULL;
-}
+ case AF_INET6:
+ return sizeof(struct sockaddr_in6) ;
+#endif
-union sockunion *
-sockunion_str2su (const char *str)
+ default:
+ return 0 ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * From the given string, fill in the given sockunion.
+ *
+ * Returns: 0 => OK -- sockunion filled in
+ * -1 => not a valid address (or not a known address family)
+ */
+int
+str2sockunion (const char *str, union sockunion *su)
{
int ret;
- union sockunion *su;
- su = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion));
+ assert(su != NULL) ;
+
+ sockunion_init_new(su, AF_UNSPEC) ;
ret = inet_pton (AF_INET, str, &su->sin.sin_addr);
if (ret > 0) /* Valid IPv4 address format. */
- {
- su->sin.sin_family = AF_INET;
-#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
- su->sin.sin_len = sizeof(struct sockaddr_in);
-#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
- return su;
- }
+ return sockunion_set_family(su, AF_INET) ;
+
#ifdef HAVE_IPV6
ret = inet_pton (AF_INET6, str, &su->sin6.sin6_addr);
if (ret > 0) /* Valid IPv6 address format. */
- {
- su->sin6.sin6_family = AF_INET6;
-#ifdef SIN6_LEN
- su->sin6.sin6_len = sizeof(struct sockaddr_in6);
-#endif /* SIN6_LEN */
- return su;
- }
+ return sockunion_set_family(su, AF_INET6) ;
#endif /* HAVE_IPV6 */
- XFREE (MTYPE_SOCKUNION, su);
- return NULL;
+ return -1;
}
-char *
-sockunion_su2str (union sockunion *su)
+/*------------------------------------------------------------------------------
+ * Construct string for sockunion IP address.
+ *
+ * Requires buffer of at least SU_ADDRSTRLEN characters.
+ */
+const char *
+sockunion2str (union sockunion *su, char *buf, size_t size)
{
- char str[SU_ADDRSTRLEN];
+ assert(size >= SU_ADDRSTRLEN) ;
switch (su->sa.sa_family)
- {
+ {
case AF_INET:
- inet_ntop (AF_INET, &su->sin.sin_addr, str, sizeof (str));
+ inet_ntop (AF_INET, &su->sin.sin_addr, buf, size);
break;
#ifdef HAVE_IPV6
case AF_INET6:
- inet_ntop (AF_INET6, &su->sin6.sin6_addr, str, sizeof (str));
+ inet_ntop (AF_INET6, &su->sin6.sin6_addr, buf, size);
break;
#endif /* HAVE_IPV6 */
- }
- return XSTRDUP (MTYPE_TMP, str);
+ default:
+ snprintf (buf, size, "?af=%d?", (int)su->sa.sa_family) ;
+ } ;
+
+ return buf;
}
-/* Convert IPv4 compatible IPv6 address to IPv4 address. */
-static void
-sockunion_normalise_mapped (union sockunion *su)
+/*------------------------------------------------------------------------------
+ * Fill in and return a sockunion_string
+ */
+extern sockunion_string_t
+sutoa(sockunion su)
{
- struct sockaddr_in sin;
-
-#ifdef HAVE_IPV6
- if (su->sa.sa_family == AF_INET6
- && IN6_IS_ADDR_V4MAPPED (&su->sin6.sin6_addr))
- {
- memset (&sin, 0, sizeof (struct sockaddr_in));
- sin.sin_family = AF_INET;
- sin.sin_port = su->sin6.sin6_port;
- memcpy (&sin.sin_addr, ((char *)&su->sin6.sin6_addr) + 12, 4);
- memcpy (su, &sin, sizeof (struct sockaddr_in));
- }
-#endif /* HAVE_IPV6 */
-}
+ sockunion_string_t sus ;
-/* Return socket of sockunion. */
-int
-sockunion_socket (union sockunion *su)
+ sockunion2str(su, sus.str, sizeof(sus.str)) ;
+ return sus ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * From the given string, construct and fill in a sockunion.
+ *
+ * Returns: NULL => not a valid address (or not a known address family)
+ * otherwise is address of new sockunion.
+ *
+ * NB: the caller is responsible for freeing the sockunion created.
+ */
+union sockunion *
+sockunion_str2su (const char *str)
{
- int sock;
+ union sockunion *su;
- sock = socket (su->sa.sa_family, SOCK_STREAM, 0);
- if (sock < 0)
- {
- zlog (NULL, LOG_WARNING, "Can't make socket : %s", safe_strerror (errno));
- return -1;
- }
+ su = XMALLOC (MTYPE_SOCKUNION, sizeof(union sockunion));
- return sock;
+ if (str2sockunion (str, su) != 0)
+ XFREE (MTYPE_SOCKUNION, su); /* sets su = NULL */
+
+ return su ;
}
-/* Return accepted new socket file descriptor. */
-int
-sockunion_accept (int sock, union sockunion *su)
+/*------------------------------------------------------------------------------
+ * Convert given sockunion to string, and return a new piece of memory
+ * containing same.
+ *
+ * It is the callers responsibility to free the memory in due course.
+ */
+extern char *
+sockunion_su2str (union sockunion *su, enum MTYPE type)
{
- socklen_t len;
- int client_sock;
-
- len = sizeof (union sockunion);
- client_sock = accept (sock, (struct sockaddr *) su, &len);
-
- sockunion_normalise_mapped (su);
- return client_sock;
+ return XSTRDUP (type, sutoa(su).str) ;
}
-/* Return sizeof union sockunion. */
-static int
-sockunion_sizeof (union sockunion *su)
+/*------------------------------------------------------------------------------
+ * If have an IPv6 mapped IPv4 address, convert it to an IPv4 address.
+ */
+extern void
+sockunion_unmap_ipv4 (sockunion su)
{
- int ret;
-
- ret = 0;
- switch (su->sa.sa_family)
- {
- case AF_INET:
- ret = sizeof (struct sockaddr_in);
- break;
#ifdef HAVE_IPV6
- case AF_INET6:
- ret = sizeof (struct sockaddr_in6);
- break;
-#endif /* AF_INET6 */
+ if ( (sockunion_family(su) == AF_INET6)
+ && IN6_IS_ADDR_V4MAPPED (&su->sin6.sin6_addr) )
+ {
+ union sockunion sux[1] ;
+
+ sockunion_init_new(sux, AF_INET) ;
+ memcpy (&sux->sin.sin_addr, &su->sin6.sin6_addr.s6_addr[12], 4) ;
+ sux->sin.sin_port = su->sin6.sin6_port ;
+ memcpy (su, sux, sizeof(*sux)) ;
+ confirm(sizeof(*su) == sizeof(*sux)) ;
}
- return ret;
+#endif /* HAVE_IPV6 */
}
-/* return sockunion structure : this function should be revised. */
-static char *
-sockunion_log (union sockunion *su)
+/*------------------------------------------------------------------------------
+ * If have an IPv4 address, convert it to an IPv6 mapped IPv4 address.
+ */
+extern void
+sockunion_map_ipv4 (sockunion su)
{
- static char buf[SU_ADDRSTRLEN];
-
- switch (su->sa.sa_family)
- {
- case AF_INET:
- snprintf (buf, SU_ADDRSTRLEN, "%s", inet_ntoa (su->sin.sin_addr));
- break;
#ifdef HAVE_IPV6
- case AF_INET6:
- snprintf (buf, SU_ADDRSTRLEN, "%s",
- inet_ntop (AF_INET6, &(su->sin6.sin6_addr), buf, SU_ADDRSTRLEN));
- break;
-#endif /* HAVE_IPV6 */
- default:
- snprintf (buf, SU_ADDRSTRLEN, "af_unknown %d ", su->sa.sa_family);
- break;
+ if (sockunion_family(su) == AF_INET)
+ {
+ union sockunion sux[1] ;
+
+ sockunion_init_new(sux, AF_INET6) ;
+ memset (&sux->sin6.sin6_addr.s6_addr[10], 0xFF, 2) ;
+ memcpy (&sux->sin6.sin6_addr.s6_addr[12], &su->sin.sin_addr, 4) ;
+ sux->sin6.sin6_port = su->sin.sin_port ;
+ memcpy (su, sux, sizeof(*sux)) ;
+ confirm(sizeof(*su) == sizeof(*sux)) ;
}
- return (XSTRDUP (MTYPE_TMP, buf));
+#endif /* HAVE_IPV6 */
}
-/* sockunion_connect returns
- -1 : error occured
- 0 : connect success
- 1 : connect is in progress */
-enum connect_result
-sockunion_connect (int fd, union sockunion *peersu, unsigned short port,
- unsigned int ifindex)
+/*------------------------------------------------------------------------------
+ * Return accepted new socket file descriptor.
+ *
+ * The following errors should be ignored:
+ *
+ * EAGAIN, EWOULDBLOCK or ECONNABORTED -- connection aborted before got
+ * around to it (or not ready, anyway).
+ *
+ * EINTR -- the usual suspect.
+ *
+ * Sets the given sockunion to the result of the accept(), converting any
+ * IPv6 mapped IPv4 addresses to IPv4 form. (Hiding the family for the socket.)
+ *
+ * Returns: >= 0 -- OK, this is the fd (socket)
+ * -1 -- error -- not one of the above
+ * -2 -- error -- one of the above
+ */
+extern int
+sockunion_accept (int sock_fd, union sockunion *su)
{
- int ret;
- int val;
- union sockunion su;
-
- memcpy (&su, peersu, sizeof (union sockunion));
-
- switch (su.sa.sa_family)
- {
- case AF_INET:
- su.sin.sin_port = port;
- break;
-#ifdef HAVE_IPV6
- case AF_INET6:
- su.sin6.sin6_port = port;
-#ifdef KAME
- if (IN6_IS_ADDR_LINKLOCAL(&su.sin6.sin6_addr) && ifindex)
- {
-#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
- /* su.sin6.sin6_scope_id = ifindex; */
-#ifdef MUSICA
- su.sin6.sin6_scope_id = ifindex;
-#endif
-#endif /* HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID */
-#ifndef MUSICA
- SET_IN6_LINKLOCAL_IFINDEX (su.sin6.sin6_addr, ifindex);
-#endif
- }
-#endif /* KAME */
- break;
-#endif /* HAVE_IPV6 */
- }
-
- /* Make socket non-block. */
- val = fcntl (fd, F_GETFL, 0);
- fcntl (fd, F_SETFL, val|O_NONBLOCK);
+ socklen_t len;
+ int new_fd, err ;
- /* Call connect function. */
- ret = connect (fd, (struct sockaddr *) &su, sockunion_sizeof (&su));
+ len = sizeof(*su);
+ memset(su, 0, len) ;
+ new_fd = accept(sock_fd, &su->sa, &len) ;
- /* Immediate success */
- if (ret == 0)
+ if (new_fd >= 0)
{
- fcntl (fd, F_SETFL, val);
- return connect_success;
- }
+ sockunion_unmap_ipv4(su);
+ return new_fd ; /* OK -- got socket */
+ } ;
+
+ err = errno ;
+ return ( (err == EAGAIN)
+ || (err == EWOULDBLOCK)
+ || (err == ECONNABORTED)
+ || (err == EINTR) ) ? -2 : -1 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Make socket for given family, type and protocol
+ *
+ * Returns: -1 : failed -- see errno
+ * otherwise : socket
+ *
+ * Logs a LOG_ERR message if fails.
+ */
+extern int
+sockunion_socket(sockunion su, int type, int protocol)
+{
+ int sock_fd ;
+ int err ;
- /* If connect is in progress then return 1 else it's real error. */
- if (ret < 0)
- {
- if (errno != EINPROGRESS)
- {
- zlog_info ("can't connect to %s fd %d : %s",
- sockunion_log (&su), fd, safe_strerror (errno));
- return connect_error;
- }
- }
+ sock_fd = socket(sockunion_family(su), type, protocol);
- fcntl (fd, F_SETFL, val);
+ if (sock_fd >= 0)
+ return sock_fd ;
- return connect_in_progress;
+ err = errno ;
+ zlog_err("Cannot make socket family=%d, type=%d, protocol=%d: %s",
+ (int)sockunion_family(su), type, protocol, errtoa(err, 0).str) ;
+ errno = err ;
+ return -1;
}
-/* Make socket from sockunion union. */
-int
-sockunion_stream_socket (union sockunion *su)
+/*------------------------------------------------------------------------------
+ * Make socket for family from given sockunion, type=SOCK_STREAM, protocol=0.
+ *
+ * Returns: -1 : failed -- see errno
+ * otherwise : socket
+ *
+ * Logs a LOG_ERR message if fails.
+ */
+extern int
+sockunion_stream_socket (sockunion su)
{
- int sock;
-
if (su->sa.sa_family == 0)
su->sa.sa_family = AF_INET_UNION;
- sock = socket (su->sa.sa_family, SOCK_STREAM, 0);
-
- if (sock < 0)
- zlog (NULL, LOG_WARNING, "can't make socket sockunion_stream_socket");
-
- return sock;
+ return sockunion_socket (su, SOCK_STREAM, 0);
}
-/* Bind socket to specified address. */
-int
-sockunion_bind (int sock, union sockunion *su, unsigned short port,
- union sockunion *su_addr)
+/*------------------------------------------------------------------------------
+ * Initiate a connection
+ *
+ * Reports EINPROGRESS as success.
+ *
+ * TODO: discover how the ifindex thing is supposed to work !!
+ *
+ * Returns: 0 : OK (so far so good)
+ * < 0 : failed -- see errno
+ *
+ * Logs a LOG_INFO message if fails.
+ */
+extern int
+sockunion_connect(int sock_fd, union sockunion* peer_su, unsigned short port,
+ unsigned int ifindex)
{
- int size = 0;
- int ret;
+ union sockunion su ;
+ int ret, err ;
+ int sa_len ;
+
+ memcpy(&su, peer_su, sizeof(union sockunion)) ;
+
+ sa_len = sockunion_set_port(&su, port) ;
- if (su->sa.sa_family == AF_INET)
- {
- size = sizeof (struct sockaddr_in);
- su->sin.sin_port = htons (port);
-#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
- su->sin.sin_len = size;
-#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
- if (su_addr == NULL)
- su->sin.sin_addr.s_addr = htonl (INADDR_ANY);
- }
#ifdef HAVE_IPV6
- else if (su->sa.sa_family == AF_INET6)
+# ifdef KAME
+ if (su.sa.sa_family == AF_INET6)
{
- size = sizeof (struct sockaddr_in6);
- su->sin6.sin6_port = htons (port);
-#ifdef SIN6_LEN
- su->sin6.sin6_len = size;
-#endif /* SIN6_LEN */
- if (su_addr == NULL)
+ if (IN6_IS_ADDR_LINKLOCAL(&su.sin6.sin6_addr) && ifindex)
{
-#if defined(LINUX_IPV6) || defined(NRL)
- memset (&su->sin6.sin6_addr, 0, sizeof (struct in6_addr));
-#else
- su->sin6.sin6_addr = in6addr_any;
-#endif /* LINUX_IPV6 */
+# ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
+ /* su.sin6.sin6_scope_id = ifindex; */
+# ifdef MUSICA
+ su.sin6.sin6_scope_id = ifindex;
+# endif
+# endif /* HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID */
+# ifndef MUSICA
+ SET_IN6_LINKLOCAL_IFINDEX (su.sin6.sin6_addr, ifindex);
+# endif
}
- }
+ } ;
+# endif /* KAME */
#endif /* HAVE_IPV6 */
-
- ret = bind (sock, (struct sockaddr *)su, size);
- if (ret < 0)
- zlog (NULL, LOG_WARNING, "can't bind socket : %s", safe_strerror (errno));
+ ret = connect(sock_fd, &su.sa, sa_len) ;
+ err = (ret >= 0) ? 0 : errno ;
- return ret;
-}
+ if ((err == 0) || (err == EINPROGRESS))
+ return 0 ; /* instant success or EINPROGRESS as expected */
-int
-sockopt_reuseaddr (int sock)
-{
- int ret;
- int on = 1;
+ zlog_info("cannot connect to %s port %d socket %d: %s",
+ sutoa(&su).str, port, sock_fd, errtoa(err, 0).str) ;
+ errno = err ;
- ret = setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
- (void *) &on, sizeof (on));
- if (ret < 0)
- {
- zlog (NULL, LOG_WARNING, "can't set sockopt SO_REUSEADDR to socket %d", sock);
- return -1;
- }
- return 0;
-}
+ return ret ;
+} ;
-#ifdef SO_REUSEPORT
-int
-sockopt_reuseport (int sock)
+/*------------------------------------------------------------------------------
+ * Start listening on given socket
+ *
+ * Returns: >= 0 : OK (so far so good)
+ * < 0 : failed -- see errno
+ *
+ * Logs a LOG_ERR message if fails.
+ */
+extern int
+sockunion_listen(int sock_fd, int backlog)
{
- int ret;
- int on = 1;
+ int ret ;
+
+ ret = listen(sock_fd, backlog) ;
- ret = setsockopt (sock, SOL_SOCKET, SO_REUSEPORT,
- (void *) &on, sizeof (on));
if (ret < 0)
{
- zlog (NULL, LOG_WARNING, "can't set sockopt SO_REUSEPORT to socket %d", sock);
- return -1;
- }
- return 0;
-}
-#else
-int
-sockopt_reuseport (int sock)
-{
- return 0;
-}
-#endif /* 0 */
+ int err = errno ;
+ zlog_err("cannot listen on socket %d: %s", sock_fd, errtoa(err, 0).str) ;
+ errno = err ;
+ } ;
-int
-sockopt_ttl (int family, int sock, int ttl)
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Bind socket to address/port.
+ *
+ * If the 'any' parameter is true, sets the given sockunion to INADDR_ANY or
+ * the *socket* address family equivalent.
+ *
+ * Sets the given port into the sockunion su.
+ *
+ * For good measure, sets sin_len or family equivalent if required.
+ *
+ * If not 'any', and the given su does not have the same address family as the
+ * socket, then attempts to convert the su to the same family as the socket,
+ * by mapping or unmapping IPv4.
+ *
+ * Performs bind() and logs a LOG_WARNING message if fails.
+ *
+ * Returns: >= 0 => OK
+ * < 0 => failed -- see errno
+ */
+extern int
+sockunion_bind(int sock_fd, sockunion su, unsigned short port, bool any)
{
- int ret;
+ int sa_len ;
+ int ret ;
+ int sock_family ;
-#ifdef IP_TTL
- if (family == AF_INET)
+ sock_family = sockunion_getsockfamily(sock_fd) ;
+
+ if (any)
{
- ret = setsockopt (sock, IPPROTO_IP, IP_TTL,
- (void *) &ttl, sizeof (int));
- if (ret < 0)
- {
- zlog (NULL, LOG_WARNING, "can't set sockopt IP_TTL %d to socket %d", ttl, sock);
- return -1;
- }
- return 0;
+ /* Create an "any" -- of same family as the socket */
+ sockunion_init_new(su, sock_family) ;
+ sockunion_set_addr_any(su) ;
}
-#endif /* IP_TTL */
-#ifdef HAVE_IPV6
- if (family == AF_INET6)
+ else
{
- ret = setsockopt (sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
- (void *) &ttl, sizeof (int));
- if (ret < 0)
- {
- zlog (NULL, LOG_WARNING, "can't set sockopt IPV6_UNICAST_HOPS %d to socket %d",
- ttl, sock);
- return -1;
- }
- return 0;
- }
-#endif /* HAVE_IPV6 */
- return 0;
-}
-
-int
-sockopt_cork (int sock, int onoff)
-{
-#ifdef TCP_CORK
- return setsockopt (sock, IPPROTO_TCP, TCP_CORK, &onoff, sizeof(onoff));
-#else
- return 0;
+ /* Want to bind to a specific address.
+ *
+ * We provide bind with an address which matches the address family of
+ * the *socket*.
+ *
+ * If the socket is AF_INET, address may be AF_INET, or an AF_INET6
+ * *provided* it is an IPv4 mapped address.
+ *
+ * If the socket is AF_INET6, address may be AF_INET or AF_NET6, and
+ * will map any IPv4 address.
+ *
+ * If we don't HAVE_IPV6, or we don't recognise an address family,
+ * then do nothing and let bind() return some sort of error.
+ */
+#ifdef HAVE_IPV6
+ if (sock_family != sockunion_family(su))
+ {
+ switch (sock_family)
+ {
+ case AF_INET:
+ sockunion_unmap_ipv4(su) ; /* unmap if AF_INET6 mapped IPv4 */
+ break ;
+
+ case AF_INET6:
+ sockunion_map_ipv4(su) ; /* map if AF_INET */
+ break ;
+
+ default:
+ break ;
+ } ;
+ } ;
#endif
-}
+ } ;
-int
-sockopt_minttl (int family, int sock, int minttl)
-{
-#ifdef IP_MINTTL
- if (family == AF_INET)
- {
- int ret = setsockopt (sock, IPPROTO_IP, IP_MINTTL, &minttl, sizeof(minttl));
- if (ret < 0)
- zlog (NULL, LOG_WARNING,
- "can't set sockopt IP_MINTTL to %d on socket %d: %s",
- minttl, sock, safe_strerror (errno));
- return ret;
- }
-#endif /* IP_MINTTL */
-#ifdef IPV6_MINHOPCNT
- if (family == AF_INET6)
+ sa_len = sockunion_set_port(su, port) ;
+
+ ret = bind (sock_fd, &su->sa, sa_len);
+ if (ret < 0)
{
- int ret = setsockopt (sock, IPPROTO_IPV6, IPV6_MINHOPCNT, &minttl, sizeof(minttl));
- if (ret < 0)
- zlog (NULL, LOG_WARNING,
- "can't set sockopt IPV6_MINHOPCNT to %d on socket %d: %s",
- minttl, sock, safe_strerror (errno));
- return ret;
- }
-#endif
+ int err = errno ;
+ zlog_warn("cannot bind to %s port %d socket %d: %s",
+ sutoa(su).str, port, sock_fd, errtoa(err, 0).str) ;
+ errno = err ;
+ } ;
- errno = EOPNOTSUPP;
- return -1;
+ return ret;
}
-/* If same family and same prefix return 1. */
-int
+/*------------------------------------------------------------------------------
+ * If same (known) family and same prefix return 1, otherwise return 0.
+ *
+ * Returns 0 if same family, but not a known family.
+ */
+extern int
sockunion_same (union sockunion *su1, union sockunion *su2)
{
int ret = 0;
@@ -577,126 +682,174 @@ sockunion_same (union sockunion *su1, union sockunion *su2)
return 0;
switch (su1->sa.sa_family)
- {
+ {
case AF_INET:
- ret = memcmp (&su1->sin.sin_addr, &su2->sin.sin_addr,
- sizeof (struct in_addr));
- break;
+ return (su1->sin.sin_addr.s_addr == su2->sin.sin_addr.s_addr) ;
+
#ifdef HAVE_IPV6
case AF_INET6:
ret = memcmp (&su1->sin6.sin6_addr, &su2->sin6.sin6_addr,
- sizeof (struct in6_addr));
- break;
+ sizeof (struct in6_addr));
+ return (ret == 0) ;
#endif /* HAVE_IPV6 */
- }
- if (ret == 0)
- return 1;
- else
- return 0;
-}
-/* After TCP connection is established. Get local address and port. */
-union sockunion *
-sockunion_getsockname (int fd)
+ default:
+ return 0 ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get local (getsockname) or remote (getpeername) address and port.
+ *
+ * Returns: >= 0 == the address family (AF_UNSPEC if fd sock_fd < 0)
+ * < 0 => failed -- see errno
+ *
+ * If "unmap": if address is an IPv4 mapped IPv6 address, returns AF_INET.
+ *
+ * NB: returns EAFNOSUPPORT if don't recognise the address family.
+ *
+ * Logs a LOG_ERR message if fails in getsockname/getpeername.
+ */
+static int
+sockunion_get_name(int sock_fd, union sockunion* su, bool local, bool unmap)
{
- int ret;
- socklen_t len;
+ int ret ;
+ socklen_t len ;
union
{
- struct sockaddr sa;
- struct sockaddr_in sin;
-#ifdef HAVE_IPV6
- struct sockaddr_in6 sin6;
-#endif /* HAVE_IPV6 */
+ union sockunion su ;
char tmp_buffer[128];
- } name;
- union sockunion *su;
+ } name ;
+
+ memset(su, 0, sizeof(union sockunion)) ;
+
+ confirm(AF_UNSPEC == 0) ;
- memset (&name, 0, sizeof name);
- len = sizeof name;
+ if (sock_fd < 0)
+ return AF_UNSPEC ;
+
+ len = sizeof(name) ;
+ memset(&name, 0, len);
+
+ if (local)
+ ret = getsockname(sock_fd, &name.su.sa, &len) ;
+ else
+ ret = getpeername(sock_fd, &name.su.sa, &len) ;
- ret = getsockname (fd, (struct sockaddr *)&name, &len);
if (ret < 0)
{
- zlog_warn ("Can't get local address and port by getsockname: %s",
- safe_strerror (errno));
- return NULL;
+ int err = errno ;
+ zlog_err("failed in %s for socket %d: %s",
+ local ? "getsockname" : "getpeername",
+ sock_fd, errtoa(err, 0).str) ;
+ errno = err ;
}
-
- if (name.sa.sa_family == AF_INET)
+ else
{
- su = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion));
- memcpy (su, &name, sizeof (struct sockaddr_in));
- return su;
- }
+ ret = name.su.sa.sa_family ;
+
+ switch (ret)
+ {
+ case AF_INET:
+ su->sin = name.su.sin ;
+ break ;
+
#ifdef HAVE_IPV6
- if (name.sa.sa_family == AF_INET6)
- {
- su = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion));
- memcpy (su, &name, sizeof (struct sockaddr_in6));
- sockunion_normalise_mapped (su);
- return su;
- }
+ case AF_INET6:
+ su->sin6 = name.su.sin6 ;
+ if (unmap)
+ sockunion_unmap_ipv4(su) ;
+ break ;
#endif /* HAVE_IPV6 */
- return NULL;
-}
-/* After TCP connection is established. Get remote address and port. */
-union sockunion *
-sockunion_getpeername (int fd)
+ default:
+ errno = EAFNOSUPPORT ;
+ ret = -1 ;
+ } ;
+ } ;
+
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get the address family the given socket is set to.
+ *
+ * Returns: >= 0 == the address family (AF_UNSPEC if fd sock_fd < 0)
+ * < 0 => failed -- see errno
+ *
+ * NB: gets the actual address family -- does NOT look for mapped IPv4.
+ */
+extern int
+sockunion_getsockfamily(int sock_fd)
{
- int ret;
- socklen_t len;
- union
- {
- struct sockaddr sa;
- struct sockaddr_in sin;
-#ifdef HAVE_IPV6
- struct sockaddr_in6 sin6;
-#endif /* HAVE_IPV6 */
- char tmp_buffer[128];
- } name;
- union sockunion *su;
+ union sockunion su[1] ;
+ int ret ;
- memset (&name, 0, sizeof name);
- len = sizeof name;
- ret = getpeername (fd, (struct sockaddr *)&name, &len);
- if (ret < 0)
- {
- zlog (NULL, LOG_WARNING, "Can't get remote address and port: %s",
- safe_strerror (errno));
- return NULL;
- }
+ ret = sockunion_get_name(sock_fd, su, true, /* true => local */
+ false) ; /* false => don't unmap */
+ return (ret >= 0) ? sockunion_family(su) : ret ;
+} ;
- if (name.sa.sa_family == AF_INET)
- {
- su = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion));
- memcpy (su, &name, sizeof (struct sockaddr_in));
- return su;
- }
-#ifdef HAVE_IPV6
- if (name.sa.sa_family == AF_INET6)
- {
- su = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion));
- memcpy (su, &name, sizeof (struct sockaddr_in6));
- sockunion_normalise_mapped (su);
- return su;
- }
-#endif /* HAVE_IPV6 */
- return NULL;
-}
+/*------------------------------------------------------------------------------
+ * Get the address family the given socket's protocol is set to.
+ *
+ * If this is an AF_INET, that's easy.
+ *
+ * If this is an AF_INET6, then needs to look out for IN6_IS_ADDR_V4MAPPED.
+ *
+ * Returns: >= 0 == the address family (AF_UNSPEC if fd sock_fd < 0)
+ * < 0 => failed -- see errno
+ *
+ * NB: gets the underlying address family -- ie: looks for mapped IPv4.
+ */
+extern int
+sockunion_getprotofamily(int sock_fd)
+{
+ union sockunion su[1] ;
+ int ret ;
+
+ ret = sockunion_get_name(sock_fd, su, true, /* true => local */
+ true) ; /* true => unmap */
+ return (ret >= 0) ? sockunion_family(su) : ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get local address and port -- ie getsockname(), except unmaps IPv4 mapped.
+ *
+ * See: sockunion_get_name()
+ */
+extern int
+sockunion_getsockname(int sock_fd, sockunion su_local)
+{
+ return sockunion_get_name(sock_fd, su_local, true, /* true => local */
+ true) ; /* true => unmap */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get remote address and port -- ie getpeername(), except unmaps IPv4 mapped.
+ *
+ * See: sockunion_get_name()
+ */
+extern int
+sockunion_getpeername (int sock_fd, sockunion su_remote)
+{
+ return sockunion_get_name(sock_fd, su_remote, false, /* false => remote */
+ true) ; /* true => unmap */
+} ;
-/* Print sockunion structure */
+/*------------------------------------------------------------------------------
+ * Print sockunion structure to stdout
+ */
static void __attribute__ ((unused))
sockunion_print (union sockunion *su)
{
if (su == NULL)
return;
- switch (su->sa.sa_family)
+ switch (su->sa.sa_family)
{
case AF_INET:
- printf ("%s\n", inet_ntoa (su->sin.sin_addr));
+ printf ("%s\n", safe_inet_ntoa (su->sin.sin_addr));
break;
#ifdef HAVE_IPV6
case AF_INET6:
@@ -725,27 +878,15 @@ sockunion_print (union sockunion *su)
}
}
-#ifdef HAVE_IPV6
-static int
-in6addr_cmp (struct in6_addr *addr1, struct in6_addr *addr2)
-{
- unsigned int i;
- u_char *p1, *p2;
-
- p1 = (u_char *)addr1;
- p2 = (u_char *)addr2;
-
- for (i = 0; i < sizeof (struct in6_addr); i++)
- {
- if (p1[i] > p2[i])
- return 1;
- else if (p1[i] < p2[i])
- return -1;
- }
- return 0;
-}
-#endif /* HAVE_IPV6 */
-
+/*------------------------------------------------------------------------------
+ * Compare two sockunion values
+ *
+ * Compares family values first, then the body of the address.
+ *
+ * Returns: +1 => su1 > su2
+ * 0 => su1 == su2 (or same, but unknown, family)
+ * -1 => su1 < su2
+ */
int
sockunion_cmp (union sockunion *su1, union sockunion *su2)
{
@@ -754,23 +895,33 @@ sockunion_cmp (union sockunion *su1, union sockunion *su2)
if (su1->sa.sa_family < su2->sa.sa_family)
return -1;
- if (su1->sa.sa_family == AF_INET)
- {
- if (ntohl (su1->sin.sin_addr.s_addr) == ntohl (su2->sin.sin_addr.s_addr))
- return 0;
- if (ntohl (su1->sin.sin_addr.s_addr) > ntohl (su2->sin.sin_addr.s_addr))
- return 1;
+ switch (su1->sa.sa_family)
+ {
+ case AF_INET:
+ if (su1->sin.sin_addr.s_addr == su2->sin.sin_addr.s_addr)
+ return 0;
+ if (ntohl(su1->sin.sin_addr.s_addr) > ntohl(su2->sin.sin_addr.s_addr))
+ return +1;
else
return -1;
- }
+
#ifdef HAVE_IPV6
- if (su1->sa.sa_family == AF_INET6)
- return in6addr_cmp (&su1->sin6.sin6_addr, &su2->sin6.sin6_addr);
+ case AF_INET6:
+ return memcmp(&su1->sin6.sin6_addr, &su2->sin6.sin6_addr,
+ sizeof(struct in6_addr)) ;
#endif /* HAVE_IPV6 */
- return 0;
+
+ default:
+ return 0 ;
+ } ;
}
-/* Duplicate sockunion. */
+/*------------------------------------------------------------------------------
+ * Create copy of existing sockunion.
+ *
+ * It is the caller's responsibility to free the sockunion at some point --see
+ * sockunion_free()
+ */
union sockunion *
sockunion_dup (union sockunion *su)
{
@@ -779,8 +930,188 @@ sockunion_dup (union sockunion *su)
return dup;
}
+/*------------------------------------------------------------------------------
+ * Copy one sockunion to another
+ */
+extern void
+sockunion_copy (sockunion dst, sockunion src)
+{
+ memcpy (dst, src, sizeof(*dst)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Free given sockunion (if any).
+ */
void
sockunion_free (union sockunion *su)
{
- XFREE (MTYPE_SOCKUNION, su);
+ if (su != NULL)
+ XFREE (MTYPE_SOCKUNION, su);
+}
+
+/*==============================================================================
+ * Sockunion reference utilities
+ */
+
+/*------------------------------------------------------------------------------
+ * Set sockunion from given prefix -- allocate new sockunion, if required.
+ *
+ * It is the caller's responsibility to free the sockunion at some point.
+ * (See sockunion_free() or sockunion_unset().)
+ *
+ * For unknown family, returns an empty sockunion of that family.
+ */
+extern sockunion
+sockunion_new_prefix(sockunion su, struct prefix* p)
+{
+ sa_family_t family ;
+
+ family = (p != NULL) ? p->family : 0 ;
+
+ su = sockunion_init_new(su, family) ;
+
+ switch (family)
+ {
+ case 0:
+ break ;
+
+ case AF_INET:
+ su->sin.sin_addr = p->u.prefix4 ;
+ break ;
+
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ su->sin6.sin6_addr = p->u.prefix6 ;
+ break ;
+#endif
+
+ default:
+ break ;
+ } ;
+
+ return su ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Create new sockunion from given sockaddr -- taking only the address part
+ *
+ * It is the caller's responsibility to free the sockunion at some point.
+ * (See sockunion_free() or sockunion_unset().)
+ *
+ * For unknown family, returns an empty sockunion of that family.
+ */
+extern sockunion
+sockunion_new_sockaddr(sockunion su, struct sockaddr* sa)
+{
+ sa_family_t family ;
+
+ family = (sa != NULL) ? sa->sa_family : AF_UNSPEC ;
+
+ su = sockunion_init_new(su, family) ;
+
+ switch (family)
+ {
+ case AF_UNSPEC:
+ break ;
+
+ case AF_INET:
+ su->sin.sin_addr = ((struct sockaddr_in*)sa)->sin_addr ;
+ break ;
+
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ su->sin6.sin6_addr = ((struct sockaddr_in6*)sa)->sin6_addr ;
+ break ;
+#endif
+
+ default:
+ break ;
+ } ;
+
+ return su ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Unset pointer to sockunion -- free any sockunion referenced
+ *
+ * Does nothing if there is no sockunion
+ */
+extern void
+sockunion_unset(sockunion* p_su)
+{
+ if (*p_su != NULL)
+ XFREE(MTYPE_SOCKUNION, *p_su) ; /* sets *p_su NULL */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set pointer to sockunion (if any)
+ *
+ * Frees any existing sockunion at the destination.
+ *
+ * NB: copies the source pointer -- so must be clear about responsibility
+ * for the sockunion.
+ */
+extern void
+sockunion_set(sockunion* p_dst, sockunion su)
+{
+ sockunion_unset(p_dst) ;
+ *p_dst = su ;
}
+
+/*------------------------------------------------------------------------------
+ * Set pointer to a *copy* of the given sockunion
+ *
+ * Frees any existing sockunion at the destination.
+ *
+ * NB: copies the source pointer -- so must be clear about responsibility
+ * for the sockunion structure.
+ */
+extern void
+sockunion_set_dup(sockunion* p_dst, sockunion su)
+{
+ sockunion_set(p_dst, sockunion_dup(su)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set pointer to sockunion (if any) and unset source pointer.
+ *
+ * Frees any existing sockunion at the destination.
+ *
+ * NB: responsibility for the sockunion passes to the destination.
+ */
+extern void
+sockunion_set_mov(sockunion* p_dst, sockunion* p_src)
+{
+ sockunion_unset(p_dst) ;
+ *p_dst = *p_src ;
+ *p_src = NULL ;
+} ;
+
+/*==============================================================================
+ * Symbol Table Hash function -- for symbols whose name is an address.
+ */
+extern void
+sockunion_symbol_hash(symbol_hash p_hash, const void* name)
+{
+ const union sockunion* su = name ;
+
+ switch (su->sa.sa_family)
+ {
+ case AF_INET:
+ confirm(sizeof(p_hash->hash) == sizeof(su->sin.sin_addr.s_addr)) ;
+ p_hash->hash = su->sin.sin_addr.s_addr ;
+ p_hash->name = (const void*)&su->sin.sin_addr.s_addr ;
+ p_hash->name_len = sizeof(su->sin.sin_addr.s_addr) ;
+ p_hash->name_copy_len = sizeof(su->sin.sin_addr.s_addr) ;
+ break ;
+
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ symbol_hash_bytes(p_hash, (const void*)&su->sin6.sin6_addr,
+ sizeof(su->sin6.sin6_addr)) ;
+ break ;
+#endif /* HAVE_IPV6 */
+ default:
+ zabort("Unknown address family") ;
+ } ;
+} ;