diff options
author | Martin Willi <martin@revosec.ch> | 2013-03-26 17:23:38 +0100 |
---|---|---|
committer | Martin Willi <martin@revosec.ch> | 2013-05-06 15:28:26 +0200 |
commit | d3874008e8a722be522a110afb7249317072f0d9 (patch) | |
tree | cb253b2a8041a9c5d0dd05dd57074d1578d0703c | |
parent | 5e9144a21ff15e38f1a6b89fad4b6b02f443051a (diff) | |
download | strongswan-d3874008e8a722be522a110afb7249317072f0d9.tar.bz2 strongswan-d3874008e8a722be522a110afb7249317072f0d9.tar.xz |
socket-dynamic: when sending from port zero, allocate a free port dynamically
-rw-r--r-- | src/libcharon/plugins/socket_dynamic/socket_dynamic_socket.c | 127 |
1 files changed, 101 insertions, 26 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..b7c73945d 100644 --- a/src/libcharon/plugins/socket_dynamic/socket_dynamic_socket.c +++ b/src/libcharon/plugins/socket_dynamic/socket_dynamic_socket.c @@ -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)); + 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; @@ -597,4 +673,3 @@ socket_dynamic_socket_t *socket_dynamic_socket_create() return &this->public; } - |