mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-04-29 16:17:38 +00:00

Since we have autoconf results from a wide swath of target platforms, we can go remove checks that have the same result on all systems. This also removes several "fallback" implementations of functions that, at some point in the history, weren't available on all target platforms. Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
792 lines
17 KiB
C
792 lines
17 KiB
C
/* Socket union related function.
|
|
* Copyright (c) 1997, 98 Kunihiro Ishiguro
|
|
*
|
|
* This file is part of GNU Zebra.
|
|
*
|
|
* GNU Zebra is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2, or (at your option) any
|
|
* later version.
|
|
*
|
|
* GNU Zebra is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include <zebra.h>
|
|
|
|
#include "prefix.h"
|
|
#include "vty.h"
|
|
#include "sockunion.h"
|
|
#include "memory.h"
|
|
#include "str.h"
|
|
#include "log.h"
|
|
#include "jhash.h"
|
|
|
|
DEFINE_MTYPE_STATIC(LIB, SOCKUNION, "Socket union")
|
|
|
|
const char *
|
|
inet_sutop (const union sockunion *su, char *str)
|
|
{
|
|
switch (su->sa.sa_family)
|
|
{
|
|
case AF_INET:
|
|
inet_ntop (AF_INET, &su->sin.sin_addr, str, INET_ADDRSTRLEN);
|
|
break;
|
|
#ifdef HAVE_IPV6
|
|
case AF_INET6:
|
|
inet_ntop (AF_INET6, &su->sin6.sin6_addr, str, INET6_ADDRSTRLEN);
|
|
break;
|
|
#endif /* HAVE_IPV6 */
|
|
}
|
|
return str;
|
|
}
|
|
|
|
int
|
|
str2sockunion (const char *str, union sockunion *su)
|
|
{
|
|
int ret;
|
|
|
|
memset (su, 0, sizeof (union sockunion));
|
|
|
|
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;
|
|
}
|
|
|
|
const char *
|
|
sockunion2str (const union sockunion *su, char *buf, size_t len)
|
|
{
|
|
switch (sockunion_family(su))
|
|
{
|
|
case AF_UNSPEC:
|
|
snprintf (buf, len, "(unspec)");
|
|
return buf;
|
|
case AF_INET:
|
|
return inet_ntop (AF_INET, &su->sin.sin_addr, buf, len);
|
|
#ifdef HAVE_IPV6
|
|
case AF_INET6:
|
|
return inet_ntop (AF_INET6, &su->sin6.sin6_addr, buf, len);
|
|
#endif /* HAVE_IPV6 */
|
|
}
|
|
snprintf (buf, len, "(af %d)", sockunion_family(su));
|
|
return buf;
|
|
}
|
|
|
|
union sockunion *
|
|
sockunion_str2su (const char *str)
|
|
{
|
|
union sockunion *su = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion));
|
|
|
|
if (!str2sockunion (str, su))
|
|
return su;
|
|
|
|
XFREE (MTYPE_SOCKUNION, su);
|
|
return NULL;
|
|
}
|
|
|
|
/* Convert IPv4 compatible IPv6 address to IPv4 address. */
|
|
static void
|
|
sockunion_normalise_mapped (union 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 */
|
|
}
|
|
|
|
/* return sockunion structure : this function should be revised. */
|
|
static const char *
|
|
sockunion_log (const union sockunion *su, char *buf, size_t len)
|
|
{
|
|
switch (su->sa.sa_family)
|
|
{
|
|
case AF_INET:
|
|
return inet_ntop(AF_INET, &su->sin.sin_addr, buf, len);
|
|
|
|
case AF_INET6:
|
|
return inet_ntop(AF_INET6, &(su->sin6.sin6_addr), buf, len);
|
|
break;
|
|
|
|
default:
|
|
snprintf (buf, len, "af_unknown %d ", su->sa.sa_family);
|
|
return buf;
|
|
}
|
|
}
|
|
|
|
/* Return socket of sockunion. */
|
|
int
|
|
sockunion_socket (const union sockunion *su)
|
|
{
|
|
int sock;
|
|
|
|
sock = socket (su->sa.sa_family, SOCK_STREAM, 0);
|
|
if (sock < 0)
|
|
{
|
|
char buf[SU_ADDRSTRLEN];
|
|
zlog (NULL, LOG_WARNING, "Can't make socket for %s : %s",
|
|
sockunion_log (su, buf, SU_ADDRSTRLEN), safe_strerror (errno));
|
|
return -1;
|
|
}
|
|
|
|
return sock;
|
|
}
|
|
|
|
/* Return accepted new socket file descriptor. */
|
|
int
|
|
sockunion_accept (int sock, union sockunion *su)
|
|
{
|
|
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 sizeof union sockunion. */
|
|
static int
|
|
sockunion_sizeof (const union 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 */
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/* sockunion_connect returns
|
|
-1 : error occured
|
|
0 : connect success
|
|
1 : connect is in progress */
|
|
enum connect_result
|
|
sockunion_connect (int fd, const union sockunion *peersu, unsigned short port,
|
|
ifindex_t ifindex)
|
|
{
|
|
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)
|
|
{
|
|
su.sin6.sin6_scope_id = ifindex;
|
|
SET_IN6_LINKLOCAL_IFINDEX (su.sin6.sin6_addr, ifindex);
|
|
}
|
|
#endif /* KAME */
|
|
break;
|
|
#endif /* HAVE_IPV6 */
|
|
}
|
|
|
|
/* Make socket non-block. */
|
|
val = fcntl (fd, F_GETFL, 0);
|
|
fcntl (fd, F_SETFL, val|O_NONBLOCK);
|
|
|
|
/* Call connect function. */
|
|
ret = connect (fd, (struct sockaddr *) &su, sockunion_sizeof (&su));
|
|
|
|
/* Immediate success */
|
|
if (ret == 0)
|
|
{
|
|
fcntl (fd, F_SETFL, val);
|
|
return connect_success;
|
|
}
|
|
|
|
/* If connect is in progress then return 1 else it's real error. */
|
|
if (ret < 0)
|
|
{
|
|
if (errno != EINPROGRESS)
|
|
{
|
|
char str[SU_ADDRSTRLEN];
|
|
zlog_info ("can't connect to %s fd %d : %s",
|
|
sockunion_log (&su, str, sizeof str),
|
|
fd, safe_strerror (errno));
|
|
return connect_error;
|
|
}
|
|
}
|
|
|
|
fcntl (fd, F_SETFL, val);
|
|
|
|
return connect_in_progress;
|
|
}
|
|
|
|
/* Make socket from sockunion union. */
|
|
int
|
|
sockunion_stream_socket (union 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;
|
|
}
|
|
|
|
/* Bind socket to specified address. */
|
|
int
|
|
sockunion_bind (int sock, union sockunion *su, unsigned short port,
|
|
union sockunion *su_addr)
|
|
{
|
|
int size = 0;
|
|
int ret;
|
|
|
|
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)
|
|
sockunion2ip (su) = htonl (INADDR_ANY);
|
|
}
|
|
#ifdef HAVE_IPV6
|
|
else 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)
|
|
{
|
|
#ifdef LINUX_IPV6
|
|
memset (&su->sin6.sin6_addr, 0, sizeof (struct in6_addr));
|
|
#else
|
|
su->sin6.sin6_addr = in6addr_any;
|
|
#endif /* LINUX_IPV6 */
|
|
}
|
|
}
|
|
#endif /* HAVE_IPV6 */
|
|
|
|
|
|
ret = bind (sock, (struct sockaddr *)su, size);
|
|
if (ret < 0)
|
|
{
|
|
char buf[SU_ADDRSTRLEN];
|
|
zlog (NULL, LOG_WARNING, "can't bind socket for %s : %s",
|
|
sockunion_log (su, buf, SU_ADDRSTRLEN), safe_strerror (errno));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
sockopt_reuseaddr (int sock)
|
|
{
|
|
int ret;
|
|
int on = 1;
|
|
|
|
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;
|
|
}
|
|
|
|
#ifdef SO_REUSEPORT
|
|
int
|
|
sockopt_reuseport (int sock)
|
|
{
|
|
int ret;
|
|
int on = 1;
|
|
|
|
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
|
|
sockopt_ttl (int family, int sock, int ttl)
|
|
{
|
|
int ret;
|
|
|
|
#ifdef IP_TTL
|
|
if (family == AF_INET)
|
|
{
|
|
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;
|
|
}
|
|
#endif /* IP_TTL */
|
|
#ifdef HAVE_IPV6
|
|
if (family == AF_INET6)
|
|
{
|
|
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;
|
|
#endif
|
|
}
|
|
|
|
int sockopt_mark_default(int sock, int mark, struct zebra_privs_t *cap)
|
|
{
|
|
#ifdef SO_MARK
|
|
int ret;
|
|
|
|
if ( cap->change (ZPRIVS_RAISE) )
|
|
zlog_err ("routing_socket: Can't raise privileges");
|
|
|
|
ret = setsockopt(sock, SOL_SOCKET, SO_MARK, &mark, sizeof(mark));
|
|
|
|
if ( cap->change (ZPRIVS_LOWER) )
|
|
zlog_err ("routing_socket: Can't lower privileges");
|
|
|
|
return ret;
|
|
#else
|
|
return 0;
|
|
#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_MINHOPCOUNT
|
|
if (family == AF_INET6)
|
|
{
|
|
int ret = setsockopt (sock, IPPROTO_IPV6, IPV6_MINHOPCOUNT, &minttl, sizeof(minttl));
|
|
if (ret < 0)
|
|
zlog (NULL, LOG_WARNING,
|
|
"can't set sockopt IPV6_MINHOPCOUNT to %d on socket %d: %s",
|
|
minttl, sock, safe_strerror (errno));
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
errno = EOPNOTSUPP;
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
sockopt_v6only (int family, int sock)
|
|
{
|
|
int ret, on = 1;
|
|
|
|
#ifdef HAVE_IPV6
|
|
#ifdef IPV6_V6ONLY
|
|
if (family == AF_INET6)
|
|
{
|
|
ret = setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY,
|
|
(void *) &on, sizeof (int));
|
|
if (ret < 0)
|
|
{
|
|
zlog (NULL, LOG_WARNING, "can't set sockopt IPV6_V6ONLY "
|
|
"to socket %d", sock);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif /* IPV6_V6ONLY */
|
|
#endif /* HAVE_IPV6 */
|
|
return 0;
|
|
}
|
|
|
|
/* If same family and same prefix return 1. */
|
|
int
|
|
sockunion_same (const union sockunion *su1, const union sockunion *su2)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (su1->sa.sa_family != su2->sa.sa_family)
|
|
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;
|
|
#ifdef HAVE_IPV6
|
|
case AF_INET6:
|
|
ret = memcmp (&su1->sin6.sin6_addr, &su2->sin6.sin6_addr,
|
|
sizeof (struct in6_addr));
|
|
if ((ret == 0) && IN6_IS_ADDR_LINKLOCAL(&su1->sin6.sin6_addr))
|
|
{
|
|
/* compare interface indices */
|
|
if (su1->sin6.sin6_scope_id && su2->sin6.sin6_scope_id)
|
|
ret = (su1->sin6.sin6_scope_id == su2->sin6.sin6_scope_id) ? 0 : 1;
|
|
}
|
|
break;
|
|
#endif /* HAVE_IPV6 */
|
|
}
|
|
if (ret == 0)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
unsigned int
|
|
sockunion_hash (const union sockunion *su)
|
|
{
|
|
switch (sockunion_family(su))
|
|
{
|
|
case AF_INET:
|
|
return jhash_1word(su->sin.sin_addr.s_addr, 0);
|
|
#ifdef HAVE_IPV6
|
|
case AF_INET6:
|
|
return jhash2(su->sin6.sin6_addr.s6_addr32, ZEBRA_NUM_OF(su->sin6.sin6_addr.s6_addr32), 0);
|
|
#endif /* HAVE_IPV6 */
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
size_t
|
|
family2addrsize(int family)
|
|
{
|
|
switch (family)
|
|
{
|
|
case AF_INET:
|
|
return sizeof(struct in_addr);
|
|
#ifdef HAVE_IPV6
|
|
case AF_INET6:
|
|
return sizeof(struct in6_addr);
|
|
#endif /* HAVE_IPV6 */
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
size_t
|
|
sockunion_get_addrlen(const union sockunion *su)
|
|
{
|
|
return family2addrsize(sockunion_family(su));
|
|
}
|
|
|
|
const u_char *
|
|
sockunion_get_addr(const union sockunion *su)
|
|
{
|
|
switch (sockunion_family(su))
|
|
{
|
|
case AF_INET:
|
|
return (const u_char *) &su->sin.sin_addr.s_addr;
|
|
#ifdef HAVE_IPV6
|
|
case AF_INET6:
|
|
return (const u_char *) &su->sin6.sin6_addr;
|
|
#endif /* HAVE_IPV6 */
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
sockunion_set(union sockunion *su, int family, const u_char *addr, size_t bytes)
|
|
{
|
|
if (family2addrsize(family) != bytes)
|
|
return;
|
|
|
|
sockunion_family(su) = family;
|
|
switch (family)
|
|
{
|
|
case AF_INET:
|
|
memcpy(&su->sin.sin_addr.s_addr, addr, bytes);
|
|
break;
|
|
#ifdef HAVE_IPV6
|
|
case AF_INET6:
|
|
memcpy(&su->sin6.sin6_addr, addr, bytes);
|
|
break;
|
|
#endif /* HAVE_IPV6 */
|
|
}
|
|
}
|
|
|
|
/* After TCP connection is established. Get local address and port. */
|
|
union sockunion *
|
|
sockunion_getsockname (int 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;
|
|
|
|
memset (&name, 0, sizeof name);
|
|
len = sizeof name;
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/* After TCP connection is established. Get remote address and port. */
|
|
union sockunion *
|
|
sockunion_getpeername (int 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;
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/* Print sockunion structure */
|
|
static void __attribute__ ((unused))
|
|
sockunion_print (const union sockunion *su)
|
|
{
|
|
if (su == NULL)
|
|
return;
|
|
|
|
switch (su->sa.sa_family)
|
|
{
|
|
case AF_INET:
|
|
printf ("%s\n", inet_ntoa (su->sin.sin_addr));
|
|
break;
|
|
#ifdef HAVE_IPV6
|
|
case AF_INET6:
|
|
{
|
|
char buf [SU_ADDRSTRLEN];
|
|
|
|
printf ("%s\n", inet_ntop (AF_INET6, &(su->sin6.sin6_addr),
|
|
buf, sizeof (buf)));
|
|
}
|
|
break;
|
|
#endif /* HAVE_IPV6 */
|
|
|
|
#ifdef AF_LINK
|
|
case AF_LINK:
|
|
{
|
|
struct sockaddr_dl *sdl;
|
|
|
|
sdl = (struct sockaddr_dl *)&(su->sa);
|
|
printf ("link#%d\n", sdl->sdl_index);
|
|
}
|
|
break;
|
|
#endif /* AF_LINK */
|
|
default:
|
|
printf ("af_unknown %d\n", su->sa.sa_family);
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifdef HAVE_IPV6
|
|
static int
|
|
in6addr_cmp (const struct in6_addr *addr1, const struct in6_addr *addr2)
|
|
{
|
|
unsigned int i;
|
|
const u_char *p1, *p2;
|
|
|
|
p1 = (const u_char *)addr1;
|
|
p2 = (const 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 */
|
|
|
|
int
|
|
sockunion_cmp (const union sockunion *su1, const union sockunion *su2)
|
|
{
|
|
if (su1->sa.sa_family > su2->sa.sa_family)
|
|
return 1;
|
|
if (su1->sa.sa_family < su2->sa.sa_family)
|
|
return -1;
|
|
|
|
if (su1->sa.sa_family == AF_INET)
|
|
{
|
|
if (ntohl (sockunion2ip (su1)) == ntohl (sockunion2ip (su2)))
|
|
return 0;
|
|
if (ntohl (sockunion2ip (su1)) > ntohl (sockunion2ip (su2)))
|
|
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);
|
|
#endif /* HAVE_IPV6 */
|
|
return 0;
|
|
}
|
|
|
|
/* Duplicate sockunion. */
|
|
union sockunion *
|
|
sockunion_dup (const union sockunion *su)
|
|
{
|
|
union sockunion *dup = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion));
|
|
memcpy (dup, su, sizeof (union sockunion));
|
|
return dup;
|
|
}
|
|
|
|
void
|
|
sockunion_free (union sockunion *su)
|
|
{
|
|
XFREE (MTYPE_SOCKUNION, su);
|
|
}
|
|
|
|
void
|
|
sockunion_init (union sockunion *su)
|
|
{
|
|
memset(su, 0, sizeof(union sockunion));
|
|
}
|