summaryrefslogtreecommitdiff
path: root/src/libcharon/plugins/socket_dynamic/socket_dynamic_socket.c
diff options
context:
space:
mode:
authorYves-Alexis Perez <corsac@debian.org>2013-08-25 15:37:26 +0200
committerYves-Alexis Perez <corsac@debian.org>2013-08-25 15:37:26 +0200
commit6b99c8d9cff7b3e8ae8f3204b99e7ea40f791349 (patch)
tree009fc492961e13860d2a4bc2de8caf2bbe2975e7 /src/libcharon/plugins/socket_dynamic/socket_dynamic_socket.c
parentc83921a2b566aa9d55d8ccc7258f04fca6292ee6 (diff)
downloadvyos-strongswan-6b99c8d9cff7b3e8ae8f3204b99e7ea40f791349.tar.gz
vyos-strongswan-6b99c8d9cff7b3e8ae8f3204b99e7ea40f791349.zip
Imported Upstream version 5.1.0
Diffstat (limited to 'src/libcharon/plugins/socket_dynamic/socket_dynamic_socket.c')
-rw-r--r--src/libcharon/plugins/socket_dynamic/socket_dynamic_socket.c138
1 files changed, 111 insertions, 27 deletions
diff --git a/src/libcharon/plugins/socket_dynamic/socket_dynamic_socket.c b/src/libcharon/plugins/socket_dynamic/socket_dynamic_socket.c
index a5e919348..abbc8bad2 100644
--- a/src/libcharon/plugins/socket_dynamic/socket_dynamic_socket.c
+++ b/src/libcharon/plugins/socket_dynamic/socket_dynamic_socket.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006-2012 Tobias Brunner
+ * Copyright (C) 2006-2013 Tobias Brunner
* Copyright (C) 2006 Daniel Roethlisberger
* Copyright (C) 2005-2010 Martin Willi
* Copyright (C) 2005 Jan Hutter
@@ -326,13 +326,60 @@ METHOD(socket_t, receiver, status_t,
}
/**
+ * Get the port allocated dynamically using bind()
+ */
+static bool get_dynamic_port(int fd, int family, u_int16_t *port)
+{
+ union {
+ struct sockaddr_storage ss;
+ struct sockaddr s;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ } addr;
+ socklen_t addrlen;
+
+ addrlen = sizeof(addr);
+ if (getsockname(fd, &addr.s, &addrlen) != 0)
+ {
+ DBG1(DBG_NET, "unable to getsockname: %s", strerror(errno));
+ return FALSE;
+ }
+ switch (family)
+ {
+ case AF_INET:
+ if (addrlen != sizeof(addr.sin) || addr.sin.sin_family != family)
+ {
+ break;
+ }
+ *port = ntohs(addr.sin.sin_port);
+ return TRUE;
+ case AF_INET6:
+ if (addrlen != sizeof(addr.sin6) || addr.sin6.sin6_family != family)
+ {
+ break;
+ }
+ *port = ntohs(addr.sin6.sin6_port);
+ return TRUE;
+ default:
+ return FALSE;
+ }
+ DBG1(DBG_NET, "received invalid getsockname() result");
+ return FALSE;
+}
+
+/**
* open a socket to send and receive packets
*/
static int open_socket(private_socket_dynamic_socket_t *this,
- int family, u_int16_t port)
+ int family, u_int16_t *port)
{
+ union {
+ struct sockaddr_storage ss;
+ struct sockaddr s;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ } addr;
int on = TRUE;
- struct sockaddr_storage addr;
socklen_t addrlen;
u_int sol, pktinfo = 0;
int fd;
@@ -342,27 +389,21 @@ static int open_socket(private_socket_dynamic_socket_t *this,
switch (family)
{
case AF_INET:
- {
- struct sockaddr_in *sin = (struct sockaddr_in *)&addr;
- sin->sin_family = AF_INET;
- sin->sin_addr.s_addr = INADDR_ANY;
- sin->sin_port = htons(port);
- addrlen = sizeof(struct sockaddr_in);
+ addr.sin.sin_family = AF_INET;
+ addr.sin.sin_addr.s_addr = INADDR_ANY;
+ addr.sin.sin_port = htons(*port);
+ addrlen = sizeof(addr.sin);
sol = SOL_IP;
pktinfo = IP_PKTINFO;
break;
- }
case AF_INET6:
- {
- struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr;
- sin6->sin6_family = AF_INET6;
- memset(&sin6->sin6_addr, 0, sizeof(sin6->sin6_addr));
- sin6->sin6_port = htons(port);
- addrlen = sizeof(struct sockaddr_in6);
+ addr.sin6.sin6_family = AF_INET6;
+ memset(&addr.sin6.sin6_addr, 0, sizeof(addr.sin6.sin6_addr));
+ addr.sin6.sin6_port = htons(*port);
+ addrlen = sizeof(addr.sin6);
sol = SOL_IPV6;
pktinfo = IPV6_RECVPKTINFO;
break;
- }
default:
return 0;
}
@@ -380,13 +421,17 @@ static int open_socket(private_socket_dynamic_socket_t *this,
return 0;
}
- /* bind the socket */
- if (bind(fd, (struct sockaddr *)&addr, addrlen) < 0)
+ if (bind(fd, &addr.s, addrlen) < 0)
{
DBG1(DBG_NET, "unable to bind socket: %s", strerror(errno));
close(fd);
return 0;
}
+ if (*port == 0 && !get_dynamic_port(fd, family, port))
+ {
+ close(fd);
+ return 0;
+ }
/* get additional packet info on receive */
if (setsockopt(fd, sol, pktinfo, &on, sizeof(on)) < 0)
@@ -404,16 +449,41 @@ static int open_socket(private_socket_dynamic_socket_t *this,
/* enable UDP decapsulation on each socket */
if (!hydra->kernel_interface->enable_udp_decap(hydra->kernel_interface,
- fd, family, port))
+ fd, family, *port))
{
DBG1(DBG_NET, "enabling UDP decapsulation for %s on port %d failed",
- family == AF_INET ? "IPv4" : "IPv6", port);
+ family == AF_INET ? "IPv4" : "IPv6", *port);
}
return fd;
}
/**
+ * Get the first usable socket for an address family
+ */
+static dynsock_t *get_any_socket(private_socket_dynamic_socket_t *this,
+ int family)
+{
+ dynsock_t *key, *value, *found = NULL;
+ enumerator_t *enumerator;
+
+ this->lock->read_lock(this->lock);
+ enumerator = this->sockets->create_enumerator(this->sockets);
+ while (enumerator->enumerate(enumerator, &key, &value))
+ {
+ if (value->family == family)
+ {
+ found = value;
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ this->lock->unlock(this->lock);
+
+ return found;
+}
+
+/**
* Find/Create a socket to send from host
*/
static dynsock_t *find_socket(private_socket_dynamic_socket_t *this,
@@ -433,7 +503,15 @@ static dynsock_t *find_socket(private_socket_dynamic_socket_t *this,
{
return skt;
}
- fd = open_socket(this, family, port);
+ if (!port)
+ {
+ skt = get_any_socket(this, family);
+ if (skt)
+ {
+ return skt;
+ }
+ }
+ fd = open_socket(this, family, &port);
if (!fd)
{
return NULL;
@@ -457,7 +535,7 @@ METHOD(socket_t, sender, status_t,
{
dynsock_t *skt;
host_t *src, *dst;
- int port, family;
+ int family;
ssize_t len;
chunk_t data;
struct msghdr msg;
@@ -467,9 +545,7 @@ METHOD(socket_t, sender, status_t,
src = packet->get_source(packet);
dst = packet->get_destination(packet);
family = src->get_family(src);
- port = src->get_port(src);
- port = port ?: CHARON_UDP_PORT;
- skt = find_socket(this, family, port);
+ skt = find_socket(this, family, src->get_port(src));
if (!skt)
{
return FAILED;
@@ -544,6 +620,14 @@ METHOD(socket_t, get_port, u_int16_t,
return 0;
}
+METHOD(socket_t, supported_families, socket_family_t,
+ private_socket_dynamic_socket_t *this)
+{
+ /* we could return only the families of the opened sockets, but it could
+ * be that both families are supported even if no socket is yet open */
+ return SOCKET_FAMILY_BOTH;
+}
+
METHOD(socket_t, destroy, void,
private_socket_dynamic_socket_t *this)
{
@@ -578,6 +662,7 @@ socket_dynamic_socket_t *socket_dynamic_socket_create()
.send = _sender,
.receive = _receiver,
.get_port = _get_port,
+ .supported_families = _supported_families,
.destroy = _destroy,
},
},
@@ -597,4 +682,3 @@ socket_dynamic_socket_t *socket_dynamic_socket_create()
return &this->public;
}
-