socket-dynamic: when sending from port zero, allocate a free port dynamically
This commit is contained in:
@@ -325,14 +325,61 @@ METHOD(socket_t, receiver, status_t,
|
|||||||
return FAILED;
|
return FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
* open a socket to send and receive packets
|
||||||
*/
|
*/
|
||||||
static int open_socket(private_socket_dynamic_socket_t *this,
|
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;
|
int on = TRUE;
|
||||||
struct sockaddr_storage addr;
|
|
||||||
socklen_t addrlen;
|
socklen_t addrlen;
|
||||||
u_int sol, pktinfo = 0;
|
u_int sol, pktinfo = 0;
|
||||||
int fd;
|
int fd;
|
||||||
@@ -342,27 +389,21 @@ static int open_socket(private_socket_dynamic_socket_t *this,
|
|||||||
switch (family)
|
switch (family)
|
||||||
{
|
{
|
||||||
case AF_INET:
|
case AF_INET:
|
||||||
{
|
addr.sin.sin_family = AF_INET;
|
||||||
struct sockaddr_in *sin = (struct sockaddr_in *)&addr;
|
addr.sin.sin_addr.s_addr = INADDR_ANY;
|
||||||
sin->sin_family = AF_INET;
|
addr.sin.sin_port = htons(*port);
|
||||||
sin->sin_addr.s_addr = INADDR_ANY;
|
addrlen = sizeof(addr.sin);
|
||||||
sin->sin_port = htons(port);
|
|
||||||
addrlen = sizeof(struct sockaddr_in);
|
|
||||||
sol = SOL_IP;
|
sol = SOL_IP;
|
||||||
pktinfo = IP_PKTINFO;
|
pktinfo = IP_PKTINFO;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case AF_INET6:
|
case AF_INET6:
|
||||||
{
|
addr.sin6.sin6_family = AF_INET6;
|
||||||
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr;
|
memset(&addr.sin6.sin6_addr, 0, sizeof(addr.sin6));
|
||||||
sin6->sin6_family = AF_INET6;
|
addr.sin6.sin6_port = htons(*port);
|
||||||
memset(&sin6->sin6_addr, 0, sizeof(sin6->sin6_addr));
|
addrlen = sizeof(addr.sin6);
|
||||||
sin6->sin6_port = htons(port);
|
|
||||||
addrlen = sizeof(struct sockaddr_in6);
|
|
||||||
sol = SOL_IPV6;
|
sol = SOL_IPV6;
|
||||||
pktinfo = IPV6_RECVPKTINFO;
|
pktinfo = IPV6_RECVPKTINFO;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -380,13 +421,17 @@ static int open_socket(private_socket_dynamic_socket_t *this,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* bind the socket */
|
if (bind(fd, &addr.s, addrlen) < 0)
|
||||||
if (bind(fd, (struct sockaddr *)&addr, addrlen) < 0)
|
|
||||||
{
|
{
|
||||||
DBG1(DBG_NET, "unable to bind socket: %s", strerror(errno));
|
DBG1(DBG_NET, "unable to bind socket: %s", strerror(errno));
|
||||||
close(fd);
|
close(fd);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
if (*port == 0 && !get_dynamic_port(fd, family, port))
|
||||||
|
{
|
||||||
|
close(fd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* get additional packet info on receive */
|
/* get additional packet info on receive */
|
||||||
if (setsockopt(fd, sol, pktinfo, &on, sizeof(on)) < 0)
|
if (setsockopt(fd, sol, pktinfo, &on, sizeof(on)) < 0)
|
||||||
@@ -404,15 +449,40 @@ static int open_socket(private_socket_dynamic_socket_t *this,
|
|||||||
|
|
||||||
/* enable UDP decapsulation on each socket */
|
/* enable UDP decapsulation on each socket */
|
||||||
if (!hydra->kernel_interface->enable_udp_decap(hydra->kernel_interface,
|
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",
|
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;
|
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
|
* Find/Create a socket to send from host
|
||||||
*/
|
*/
|
||||||
@@ -433,7 +503,15 @@ static dynsock_t *find_socket(private_socket_dynamic_socket_t *this,
|
|||||||
{
|
{
|
||||||
return skt;
|
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)
|
if (!fd)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -457,7 +535,7 @@ METHOD(socket_t, sender, status_t,
|
|||||||
{
|
{
|
||||||
dynsock_t *skt;
|
dynsock_t *skt;
|
||||||
host_t *src, *dst;
|
host_t *src, *dst;
|
||||||
int port, family;
|
int family;
|
||||||
ssize_t len;
|
ssize_t len;
|
||||||
chunk_t data;
|
chunk_t data;
|
||||||
struct msghdr msg;
|
struct msghdr msg;
|
||||||
@@ -467,9 +545,7 @@ METHOD(socket_t, sender, status_t,
|
|||||||
src = packet->get_source(packet);
|
src = packet->get_source(packet);
|
||||||
dst = packet->get_destination(packet);
|
dst = packet->get_destination(packet);
|
||||||
family = src->get_family(src);
|
family = src->get_family(src);
|
||||||
port = src->get_port(src);
|
skt = find_socket(this, family, src->get_port(src));
|
||||||
port = port ?: CHARON_UDP_PORT;
|
|
||||||
skt = find_socket(this, family, port);
|
|
||||||
if (!skt)
|
if (!skt)
|
||||||
{
|
{
|
||||||
return FAILED;
|
return FAILED;
|
||||||
@@ -597,4 +673,3 @@ socket_dynamic_socket_t *socket_dynamic_socket_create()
|
|||||||
|
|
||||||
return &this->public;
|
return &this->public;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user