summaryrefslogtreecommitdiffstats
path: root/lib/sockunion.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sockunion.c')
-rw-r--r--lib/sockunion.c314
1 files changed, 200 insertions, 114 deletions
diff --git a/lib/sockunion.c b/lib/sockunion.c
index 4043783c..95626513 100644
--- a/lib/sockunion.c
+++ b/lib/sockunion.c
@@ -139,7 +139,7 @@ sockunion_sin_len(sockunion su)
sizeof(struct sockaddr_in);
} ;
-#if HAVE_IPV6
+#ifdef HAVE_IPV6
inline static int
sockunion_sin6_len(sockunion su)
{
@@ -167,11 +167,11 @@ sockunion_set_family(sockunion su, sa_family_t family)
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
if (family == AF_INET)
- su->sin.sin_len = sizeof(struct sockaddr_in);
+ sockunion_sin_len(sockunion su) ;
#endif
#if defined(HAVE_IPV6) && defined(SIN6_LEN)
if (family == AF_INET6)
- su->sin6.sin6_len = sizeof(struct sockaddr_in6);
+ sockunion_sin6_len(sockunion su) ;
#endif
return 0 ;
@@ -244,7 +244,7 @@ sockunion_init_new(sockunion su, sa_family_t family)
else
memset(su, 0, sizeof(union sockunion)) ;
- if (family != 0)
+ if (family != AF_UNSPEC)
sockunion_set_family(su, family) ;
return su ;
@@ -263,7 +263,7 @@ str2sockunion (const char *str, union sockunion *su)
assert(su != NULL) ;
- sockunion_init_new(su, 0) ;
+ sockunion_init_new(su, AF_UNSPEC) ;
ret = inet_pton (AF_INET, str, &su->sin.sin_addr);
if (ret > 0) /* Valid IPv4 address format. */
@@ -341,23 +341,45 @@ sockunion_su2str (union sockunion *su, enum MTYPE type)
}
/*------------------------------------------------------------------------------
- * Convert IPv4 compatible IPv6 address to IPv4 address.
+ * If have an IPv6 mapped IPv4 address, convert it to an IPv4 address.
*/
-static void
-sockunion_normalise_mapped (union sockunion *su)
+extern void
+sockunion_unmap_ipv4 (union sockunion *su)
{
- struct sockaddr_in sin;
+#ifdef HAVE_IPV6
+ union sockunion sux ;
+ struct sockaddr_in* su_in = &sux.sin ;
+ if ( (su->sa.sa_family == AF_INET6)
+ && IN6_IS_ADDR_V4MAPPED (&su->sin6.sin6_addr) )
+ {
+ sockunion_init_new(&sux, AF_INET) ;
+ memcpy (&su_in->sin_addr, ((char *)&su->sin6.sin6_addr) + 12, 4) ;
+ su_in->sin_port = su->sin6.sin6_port ;
+ memcpy (su, &sux, sizeof(sux)) ;
+ confirm(sizeof(*su) == sizeof(sux)) ;
+ }
+#endif /* HAVE_IPV6 */
+}
+
+/*------------------------------------------------------------------------------
+ * If have an IPv4 address, convert it to an IPv6 mapped IPv4 address.
+ */
+extern void
+sockunion_map_ipv4 (union sockunion *su)
+{
#ifdef HAVE_IPV6
- if (su->sa.sa_family == AF_INET6
- && IN6_IS_ADDR_V4MAPPED (&su->sin6.sin6_addr))
+ union sockunion sux ;
+ struct sockaddr_in6* su_in6 = &sux.sin6 ;
+
+ if (su->sa.sa_family == AF_INET)
{
- 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);
- memset (su, 0, sizeof(union sockunion)) ;
- memcpy (su, &sin, sizeof (struct sockaddr_in));
+ sockunion_init_new(&sux, AF_INET6) ;
+ memset (((char *)&su_in6->sin6_addr) + 10, 0xFF, 2) ;
+ memcpy (((char *)&su_in6->sin6_addr) + 12, &su->sin.sin_addr, 4) ;
+ su_in6->sin6_port = su->sin.sin_port ;
+ memcpy (su, &sux, sizeof(sux)) ;
+ confirm(sizeof(*su) == sizeof(sux)) ;
}
#endif /* HAVE_IPV6 */
}
@@ -372,31 +394,34 @@ sockunion_normalise_mapped (union sockunion *su)
*
* 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
*/
int
-sockunion_accept (int sock, union sockunion *su)
+sockunion_accept (int sock_fd, union sockunion *su)
{
socklen_t len;
- int ret ;
+ int new_fd, err ;
- len = sizeof(union sockunion);
+ len = sizeof(*su);
memset(su, 0, len) ;
- ret = accept(sock, &su->sa, &len) ;
+ new_fd = accept(sock_fd, &su->sa, &len) ;
- if (ret >= 0)
+ if (new_fd >= 0)
{
- sockunion_normalise_mapped(su);
- return ret ; /* OK -- got socket */
+ sockunion_unmap_ipv4(su);
+ return new_fd ; /* OK -- got socket */
} ;
- ret = errno ;
- return ( (ret == EAGAIN)
- || (ret == EWOULDBLOCK)
- || (ret == ECONNABORTED)
- || (ret == EINTR) ) ? -2 : -1 ;
+ err = errno ;
+ return ( (err == EAGAIN)
+ || (err == EWOULDBLOCK)
+ || (err == ECONNABORTED)
+ || (err == EINTR) ) ? -2 : -1 ;
} ;
/*------------------------------------------------------------------------------
@@ -410,18 +435,20 @@ sockunion_accept (int sock, union sockunion *su)
extern int
sockunion_socket(sa_family_t family, int type, int protocol)
{
- int sockfd ;
+ int sock_fd ;
+ int err ;
- sockfd = socket(family, type, protocol);
- if (sockfd < 0)
- {
- zlog (NULL, LOG_WARNING,
- "Can't make socket family=%d, type=%d, protocol=%d : %s",
- (int)family, type, protocol, safe_strerror(errno)) ;
- return -1;
- }
+ sock_fd = socket(family, type, protocol);
+
+ if (sock_fd >= 0)
+ return sock_fd ;
- return sockfd ;
+ err = errno ;
+ zlog (NULL, LOG_WARNING,
+ "Can't make socket family=%d, type=%d, protocol=%d : %s",
+ (int)family, type, protocol, safe_strerror(err)) ;
+ errno = err ;
+ return -1;
}
/*------------------------------------------------------------------------------
@@ -454,12 +481,12 @@ sockunion_stream_socket (union sockunion *su)
* Logs a LOG_INFO message if fails.
*/
extern int
-sockunion_connect(int fd, union sockunion* peer_su, unsigned short port,
+sockunion_connect(int sock_fd, union sockunion* peer_su, unsigned short port,
unsigned int ifindex)
{
char buf[SU_ADDRSTRLEN] ;
union sockunion su ;
- int ret ;
+ int ret, err ;
int sa_len ;
memcpy(&su, peer_su, sizeof(union sockunion)) ;
@@ -486,13 +513,15 @@ sockunion_connect(int fd, union sockunion* peer_su, unsigned short port,
# endif /* KAME */
#endif /* HAVE_IPV6 */
- ret = connect(fd, &su.sa, sa_len) ;
+ ret = connect(sock_fd, &su.sa, sa_len) ;
+ err = (ret >= 0) ? 0 : errno ;
- if ((ret == 0) || (errno == EINPROGRESS))
+ if ((err == 0) || (err == EINPROGRESS))
return 0 ; /* instant success or EINPROGRESS as expected */
- zlog_info("can't connect to %s port %d fd %d : %s",
- sockunion2str(&su, buf, sizeof(buf)), port, fd, safe_strerror(errno)) ;
+ zlog_info("cannot connect to %s port %d socket %d : %s",
+ sockunion2str(&su, buf, sizeof(buf)), port, sock_fd, safe_strerror(err)) ;
+ errno = err ;
return ret ;
} ;
@@ -500,27 +529,26 @@ sockunion_connect(int fd, union sockunion* peer_su, unsigned short port,
/*------------------------------------------------------------------------------
* Start listening on given socket
*
- * 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_WARNING message if fails.
*/
extern int
-sockunion_listen(int fd, int backlog)
+sockunion_listen(int sock_fd, int backlog)
{
- int ret ;
+ int ret, err ;
- ret = listen(fd, backlog) ;
+ ret = listen(sock_fd, backlog) ;
if (ret == 0)
return 0 ;
- zlog (NULL, LOG_WARNING, "can't listen on fd %d : %s",
- fd, safe_strerror(errno)) ;
+ err = errno ;
+ zlog (NULL, LOG_WARNING, "cannot listen on socket %d : %s",
+ sock_fd, safe_strerror(err)) ;
+ errno = err ;
+
return ret ;
} ;
@@ -541,10 +569,11 @@ sockunion_listen(int fd, int backlog)
* < 0 => failed -- see errno
*/
int
-sockunion_bind (int sock, union sockunion *su, unsigned short port, void* any)
+sockunion_bind (int sock_fd, union sockunion *su, unsigned short port,
+ void* any)
{
int sa_len ;
- int ret;
+ int ret ;
char buf[SU_ADDRSTRLEN] ;
if (any == NULL)
@@ -552,10 +581,15 @@ sockunion_bind (int sock, union sockunion *su, unsigned short port, void* any)
sa_len = sockunion_set_port(su, port) ;
- ret = bind (sock, &su->sa, sa_len);
+ ret = bind (sock_fd, &su->sa, sa_len);
if (ret < 0)
- zlog (NULL, LOG_WARNING, "can't bind to %s port %d fd %d : %s",
- sockunion2str(su, buf, sizeof(buf)), port, sock, safe_strerror(ret)) ;
+ {
+ int err = errno ;
+ zlog (NULL, LOG_WARNING, "cannot bind to %s port %d socket %d : %s",
+ sockunion2str(su, buf, sizeof(buf)), port, sock_fd,
+ safe_strerror(err)) ;
+ errno = err ;
+ } ;
return ret;
}
@@ -569,20 +603,23 @@ sockunion_bind (int sock, union sockunion *su, unsigned short port, void* any)
* Logs a LOG_WARNING message if fails.
*/
int
-sockopt_reuseaddr (int sock)
+sockopt_reuseaddr (int sock_fd)
{
int ret;
int on = 1;
- ret = setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
- (void *) &on, sizeof (on));
+ ret = setsockopt (sock_fd, SOL_SOCKET, SO_REUSEADDR, (void *) &on,
+ sizeof (on));
if (ret < 0)
{
+ int err = errno ;
zlog (NULL, LOG_WARNING,
- "can't set sockopt SO_REUSEADDR to socket %d", sock);
- return -1;
- }
- return 0;
+ "cannot set sockopt SO_REUSEADDR to socket %d %s", sock_fd,
+ safe_strerror(err));
+ errno = err ;
+ } ;
+
+ return ret ;
}
/*------------------------------------------------------------------------------
@@ -594,27 +631,34 @@ sockopt_reuseaddr (int sock)
* Logs a LOG_WARNING message if fails.
*/
int
-sockopt_reuseport (int sock)
+sockopt_reuseport (int sock_fd)
{
-#ifdef SO_REUSEPORT
int ret;
+
+#ifdef SO_REUSEPORT
int on = 1;
+ ret = setsockopt (sock_fd, SOL_SOCKET, SO_REUSEPORT,
+ (void *) &on, sizeof (on));
+#else
+ ret = 0 ;
+#endif
- ret = setsockopt (sock, SOL_SOCKET, SO_REUSEPORT,
- (void *) &on, sizeof (on));
if (ret < 0)
{
+ int err = errno ;
zlog (NULL, LOG_WARNING,
- "can't set sockopt SO_REUSEPORT to socket %d", sock);
- return -1;
- }
-#endif
+ "cannot set sockopt SO_REUSEPORT to socket %d: %s", sock_fd,
+ safe_strerror(err));
+ errno = err ;
+ } ;
- return 0;
+ return ret ;
} ;
/*------------------------------------------------------------------------------
* If same family and same prefix return 1.
+ *
+ * Returns 0 if same family, but not a known family.
*/
int
sockunion_same (union sockunion *su1, union sockunion *su2)
@@ -625,7 +669,7 @@ sockunion_same (union sockunion *su1, union sockunion *su2)
return 0;
switch (su1->sa.sa_family)
- {
+ {
case AF_INET:
return (su1->sin.sin_addr.s_addr == su2->sin.sin_addr.s_addr) ;
@@ -633,27 +677,62 @@ sockunion_same (union sockunion *su1, union sockunion *su2)
case AF_INET6:
ret = memcmp (&su1->sin6.sin6_addr, &su2->sin6.sin6_addr,
sizeof (struct in6_addr));
- break;
+ return (ret == 0) ;
#endif /* HAVE_IPV6 */
- }
- if (ret == 0)
- return 1;
- else
- return 0;
-}
+
+ default:
+ return 0 ;
+ } ;
+} ;
/*------------------------------------------------------------------------------
- * After TCP connection is established. Get local or remote address and port.
+ * Get the address family the given socket is set to.
*
- * Returns: 0 => OK
- * != 0 => failed, value = errno
+ * 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)
+{
+ union sockunion su ;
+ int ret ;
+ socklen_t len ;
+
+ if (sock_fd < 0)
+ return AF_UNSPEC ;
+
+ sockunion_init_new(&su, AF_UNSPEC) ;
+ len = sizeof(su) ;
+
+ ret = getsockname(sock_fd, (struct sockaddr *)&su, &len) ;
+ if (ret < 0)
+ {
+ int err = errno ;
+ zlog_warn ("Failed in getsockname for socket %d: %s",
+ sock_fd, safe_strerror(err)) ;
+ errno = err ;
+ return -1 ;
+ } ;
+
+ return su.sa.sa_family ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get local or remote address and port.
+ *
+ * Returns: >= 0 => OK
+ * < 0 => failed (or unknown family) -- see errno
+ *
+ * If address is an IPv4 mapped IPv6 address, returns the IPv4 address.
*
* NB: returns EAFNOSUPPORT if don't recognise the address family.
*/
static int
-sockunion_get_name(int fd, union sockunion* su, int local)
+sockunion_get_name(int sock_fd, union sockunion* su, int local)
{
- int ret;
+ int ret ;
union
{
@@ -671,18 +750,20 @@ sockunion_get_name(int fd, union sockunion* su, int local)
memset(su, 0, sizeof(union sockunion)) ;
if (local)
- ret = getsockname(fd, (struct sockaddr *)&name, &len) ;
+ ret = getsockname(sock_fd, (struct sockaddr *)&name, &len) ;
else
- ret = getpeername(fd, (struct sockaddr *)&name, &len) ;
+ ret = getpeername(sock_fd, (struct sockaddr *)&name, &len) ;
if (ret < 0)
{
- ret = errno ;
- zlog_warn ("Can't get %s address and port: %s",
- local ? "local" : "remote", safe_strerror(ret)) ;
+ int err = errno ;
+ zlog_warn ("Cannot get %s address and port: %s",
+ local ? "local" : "remote", safe_strerror(err)) ;
+ errno = err ;
return ret ;
}
+ ret = 0 ; /* assume all will be well */
switch (name.sa.sa_family)
{
case AF_INET:
@@ -692,43 +773,38 @@ sockunion_get_name(int fd, union sockunion* su, int local)
#ifdef HAVE_IPV6
case AF_INET6:
memcpy(su, &name, sizeof (struct sockaddr_in6)) ;
- sockunion_normalise_mapped(su) ;
+ sockunion_unmap_ipv4(su) ;
break ;
#endif /* HAVE_IPV6 */
default:
- ret = EAFNOSUPPORT ;
+ errno = EAFNOSUPPORT ;
+ ret = -1 ;
} ;
return ret ;
} ;
/*------------------------------------------------------------------------------
- * After TCP connection is established. Get local address and port.
+ * Get local address and port -- ie getsockname().
*
- * Returns: 0 => OK
- * != 0 => failed, value = errno
- *
- * NB: returns EAFNOSUPPORT if don't recognise the socket's address family.
+ * See: sockunion_get_name()
*/
int
-sockunion_getsockname(int fd, union sockunion* su_local)
+sockunion_getsockname(int sock_fd, union sockunion* su_local)
{
- return sockunion_get_name(fd, su_local, 1) ;
+ return sockunion_get_name(sock_fd, su_local, 1) ;
} ;
/*------------------------------------------------------------------------------
- * After TCP connection is established. Get remote address and port.
- *
- * Returns: 0 => OK
- * != 0 => failed, value = errno
+ * Get remote address and port -- ie getpeername().
*
- * NB: returns EAFNOSUPPORT if don't recognise the socket's address family.
+ * See: sockunion_get_name()
*/
int
-sockunion_getpeername (int fd, union sockunion* su_remote)
+sockunion_getpeername (int sock_fd, union sockunion* su_remote)
{
- return sockunion_get_name(fd, su_remote, 0) ;
+ return sockunion_get_name(sock_fd, su_remote, 0) ;
} ;
/*------------------------------------------------------------------------------
@@ -774,6 +850,12 @@ sockunion_print (union sockunion *su)
/*------------------------------------------------------------------------------
* 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)
@@ -837,6 +919,8 @@ sockunion_free (union sockunion *su)
*
* 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)
@@ -870,32 +954,34 @@ sockunion_new_prefix(sockunion su, struct prefix* p)
} ;
/*------------------------------------------------------------------------------
- * Create new sockunion from given sockaddr.
+ * 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 : 0 ;
+ family = (sa != NULL) ? sa->sa_family : AF_UNSPEC ;
su = sockunion_init_new(su, family) ;
switch (family)
{
- case 0:
+ case AF_UNSPEC:
break ;
case AF_INET:
- su->sin = *(struct sockaddr_in*)sa ;
+ su->sin.sin_addr = ((struct sockaddr_in*)sa)->sin_addr ;
break ;
#ifdef HAVE_IPV6
case AF_INET6:
- su->sin6 = *(struct sockaddr_in6*)sa ;
+ su->sin6.sin6_addr = ((struct sockaddr_in6*)sa)->sin6_addr ;
break ;
#endif