Added an initiator-only socket implementation which binds ports on demand
authorMartin Willi <martin@revosec.ch>
Wed, 24 Feb 2010 09:58:23 +0000 (10:58 +0100)
committerMartin Willi <martin@revosec.ch>
Fri, 26 Feb 2010 10:44:34 +0000 (11:44 +0100)
configure.in
src/charon/Makefile.am
src/charon/plugins/socket_dynamic/Makefile.am [new file with mode: 0644]
src/charon/plugins/socket_dynamic/socket_dynamic_plugin.c [new file with mode: 0644]
src/charon/plugins/socket_dynamic/socket_dynamic_plugin.h [new file with mode: 0644]
src/charon/plugins/socket_dynamic/socket_dynamic_socket.c [new file with mode: 0644]
src/charon/plugins/socket_dynamic/socket_dynamic_socket.h [new file with mode: 0644]

index 7c48959..8156296 100644 (file)
@@ -116,6 +116,7 @@ ARG_ENABL_SET([kernel-pfroute], [enable the PF_ROUTE kernel interface.])
 ARG_ENABL_SET([kernel-klips],   [enable the KLIPS kernel interface.])
 ARG_DISBL_SET([socket-default], [disable default socket implementation for charon.])
 ARG_ENABL_SET([socket-raw],     [enable raw socket implementation of charon, enforced if pluto is enabled])
+ARG_ENABL_SET([socket-dynamic], [enable dynamic socket implementation for charon])
 ARG_ENABL_SET([nat-transport],  [enable NAT traversal with IPsec transport mode in pluto.])
 ARG_DISBL_SET([vendor-id],      [disable the sending of the strongSwan vendor ID in pluto.])
 ARG_DISBL_SET([xauth-vid],      [disable the sending of the XAUTH vendor ID.])
@@ -238,9 +239,10 @@ fi
 
 if test x$pluto = xtrue; then
        if test x$socket_raw = xfalse; then
-               AC_MSG_NOTICE([Enforcing --enable-socket-raw/--disable-socket-default, as pluto is enabled])
+               AC_MSG_NOTICE([Enforcing --enable-socket-raw, as pluto is enabled])
                socket_default=false
                socket_raw=true
+               socket_dynamic=false
        fi
 fi
 
@@ -812,6 +814,7 @@ AM_CONDITIONAL(USE_KERNEL_PFROUTE, test x$kernel_pfroute = xtrue)
 AM_CONDITIONAL(USE_KERNEL_KLIPS, test x$kernel_klips = xtrue)
 AM_CONDITIONAL(USE_SOCKET_DEFAULT, test x$socket_default = xtrue)
 AM_CONDITIONAL(USE_SOCKET_RAW, test x$socket_raw = xtrue)
+AM_CONDITIONAL(USE_SOCKET_DYNAMIC, test x$socket_dynamic = xtrue)
 
 dnl other options
 dnl =============
@@ -908,6 +911,7 @@ AC_OUTPUT(
        src/charon/plugins/kernel_klips/Makefile
        src/charon/plugins/socket_default/Makefile
        src/charon/plugins/socket_raw/Makefile
+       src/charon/plugins/socket_dynamic/Makefile
        src/charon/plugins/smp/Makefile
        src/charon/plugins/sql/Makefile
        src/charon/plugins/medsrv/Makefile
index 000515b..40e3e7c 100644 (file)
@@ -164,6 +164,11 @@ if USE_SOCKET_RAW
   PLUGINS += socket-raw
 endif
 
+if USE_SOCKET_DYNAMIC
+  SUBDIRS += plugins/socket_dynamic
+  PLUGINS += socket-dynamic
+endif
+
 if USE_STROKE
   SUBDIRS += plugins/stroke
   PLUGINS += stroke
diff --git a/src/charon/plugins/socket_dynamic/Makefile.am b/src/charon/plugins/socket_dynamic/Makefile.am
new file mode 100644 (file)
index 0000000..5e12e0e
--- /dev/null
@@ -0,0 +1,11 @@
+
+INCLUDES = -I${linux_headers} -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon
+
+AM_CFLAGS = -rdynamic
+
+plugin_LTLIBRARIES = libstrongswan-socket-dynamic.la
+
+libstrongswan_socket_dynamic_la_SOURCES = \
+       socket_dynamic_plugin.h socket_dynamic_plugin.c \
+       socket_dynamic_socket.h socket_dynamic_socket.c
+libstrongswan_socket_dynamic_la_LDFLAGS = -module -avoid-version
diff --git a/src/charon/plugins/socket_dynamic/socket_dynamic_plugin.c b/src/charon/plugins/socket_dynamic/socket_dynamic_plugin.c
new file mode 100644 (file)
index 0000000..50955dc
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2010 Martin Willi
+ * Copyright (C) 2010 revosec AG
+ *
+ * This program 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 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program 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.
+ */
+
+#include "socket_dynamic_plugin.h"
+
+#include "socket_dynamic_socket.h"
+
+#include <daemon.h>
+
+typedef struct private_socket_dynamic_plugin_t private_socket_dynamic_plugin_t;
+
+/**
+ * Private data of socket plugin
+ */
+struct private_socket_dynamic_plugin_t {
+
+       /**
+        * Implements plugin interface
+        */
+       socket_dynamic_plugin_t public;
+
+       /**
+        * Socket instance.
+        */
+       socket_dynamic_socket_t *socket;
+};
+
+METHOD(plugin_t, destroy, void,
+       private_socket_dynamic_plugin_t *this)
+{
+       charon->socket->remove_socket(charon->socket, &this->socket->socket);
+       this->socket->destroy(this->socket);
+       free(this);
+}
+
+/*
+ * see header file
+ */
+plugin_t *plugin_create()
+{
+       private_socket_dynamic_plugin_t *this;
+
+       INIT(this,
+               .public.plugin.destroy = _destroy,
+               .socket = socket_dynamic_socket_create(),
+       );
+
+       if (!this->socket)
+       {
+               free(this);
+               return NULL;
+       }
+       charon->socket->add_socket(charon->socket, &this->socket->socket);
+
+       return &this->public.plugin;
+}
+
diff --git a/src/charon/plugins/socket_dynamic/socket_dynamic_plugin.h b/src/charon/plugins/socket_dynamic/socket_dynamic_plugin.h
new file mode 100644 (file)
index 0000000..7d3ee31
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2010 Martin Willi
+ * Copyright (C) 2010 revosec AG
+ *
+ * This program 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 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program 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.
+ */
+
+/**
+ * @defgroup socket_dynamic socket_dynamic
+ * @ingroup cplugins
+ *
+ * @defgroup socket_dynamic_plugin socket_dynamic_plugin
+ * @{ @ingroup socket_dynamic
+ */
+
+#ifndef SOCKET_DYNAMIC_PLUGIN_H_
+#define SOCKET_DYNAMIC_PLUGIN_H_
+
+#include <plugins/plugin.h>
+
+typedef struct socket_dynamic_plugin_t socket_dynamic_plugin_t;
+
+/**
+ * Plugin providing a socket that binds ports dynamically.
+ */
+struct socket_dynamic_plugin_t {
+
+       /**
+        * implements plugin interface
+        */
+       plugin_t plugin;
+};
+
+/**
+ * Create a socket_dynamic_plugin instance.
+ */
+plugin_t *plugin_create();
+
+#endif /** SOCKET_DYNAMIC_PLUGIN_H_ @}*/
diff --git a/src/charon/plugins/socket_dynamic/socket_dynamic_socket.c b/src/charon/plugins/socket_dynamic/socket_dynamic_socket.c
new file mode 100644 (file)
index 0000000..e1f34de
--- /dev/null
@@ -0,0 +1,604 @@
+/*
+ * Copyright (C) 2006-2009 Tobias Brunner
+ * Copyright (C) 2006 Daniel Roethlisberger
+ * Copyright (C) 2005-2010 Martin Willi
+ * Copyright (C) 2005 Jan Hutter
+ * Hochschule fuer Technik Rapperswil
+ * Copyright (C) 2010 revosec AG
+ *
+ * This program 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 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program 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.
+ */
+
+/* for struct in6_pktinfo */
+#define _GNU_SOURCE
+
+#include "socket_dynamic_socket.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+
+#include <daemon.h>
+#include <threading/thread.h>
+#include <utils/hashtable.h>
+
+/* Maximum size of a packet */
+#define MAX_PACKET 5000
+
+/* length of non-esp marker */
+#define MARKER_LEN sizeof(u_int32_t)
+
+/* from linux/udp.h */
+#ifndef UDP_ENCAP
+#define UDP_ENCAP 100
+#endif /*UDP_ENCAP*/
+
+#ifndef UDP_ENCAP_ESPINUDP
+#define UDP_ENCAP_ESPINUDP 2
+#endif /*UDP_ENCAP_ESPINUDP*/
+
+/* these are not defined on some platforms */
+#ifndef SOL_IP
+#define SOL_IP IPPROTO_IP
+#endif
+#ifndef SOL_IPV6
+#define SOL_IPV6 IPPROTO_IPV6
+#endif
+#ifndef SOL_UDP
+#define SOL_UDP IPPROTO_UDP
+#endif
+
+/* IPV6_RECVPKTINFO is defined in RFC 3542 which obsoletes RFC 2292 that
+ * previously defined IPV6_PKTINFO */
+#ifndef IPV6_RECVPKTINFO
+#define IPV6_RECVPKTINFO IPV6_PKTINFO
+#endif
+
+typedef struct private_socket_dynamic_socket_t private_socket_dynamic_socket_t;
+typedef struct dynsock_t dynsock_t;
+
+/**
+ * Private data of an socket_t object
+ */
+struct private_socket_dynamic_socket_t {
+
+       /**
+        * public functions
+        */
+       socket_dynamic_socket_t public;
+
+       /**
+        * Hashtable of bound sockets
+        */
+       hashtable_t *sockets;
+
+       /**
+        * Notification pipe to signal receiver
+        */
+       int notify[2];
+};
+
+/**
+ * Struct for a dynamically allocated socket
+ */
+struct dynsock_t {
+
+       /**
+        * File descriptor of socket
+        */
+       int fd;
+
+       /**
+        * Address family
+        */
+       int family;
+
+       /**
+        * Bound source port
+        */
+       u_int16_t port;
+};
+
+/**
+ * Hash function for hashtable
+ */
+static u_int hash(dynsock_t *key)
+{
+       return (key->family << 16) | key->port;
+}
+
+/**
+ * Equals function for hashtable
+ */
+static bool equals(dynsock_t *a, dynsock_t *b)
+{
+       return a->family == b->family && a->port == b->port;
+}
+
+/**
+ * Create a fd_set from all bound sockets
+ */
+static int build_fds(private_socket_dynamic_socket_t *this, fd_set *fds)
+{
+       enumerator_t *enumerator;
+       dynsock_t *key, *value;
+       int maxfd;
+
+       FD_ZERO(fds);
+       FD_SET(this->notify[0], fds);
+       maxfd = this->notify[0];
+
+       enumerator = this->sockets->create_enumerator(this->sockets);
+       while (enumerator->enumerate(enumerator, &key, &value))
+       {
+               FD_SET(value->fd, fds);
+               maxfd = max(maxfd, value->fd);
+       }
+       enumerator->destroy(enumerator);
+
+       return maxfd + 1;
+}
+
+/**
+ * Find the socket select()ed
+ */
+static dynsock_t* scan_fds(private_socket_dynamic_socket_t *this, fd_set *fds)
+{
+       enumerator_t *enumerator;
+       dynsock_t *key, *value, *selected = NULL;
+
+       enumerator = this->sockets->create_enumerator(this->sockets);
+       while (enumerator->enumerate(enumerator, &key, &value))
+       {
+               if (FD_ISSET(value->fd, fds))
+               {
+                       selected = value;
+                       break;
+               }
+       }
+       enumerator->destroy(enumerator);
+       return selected;
+}
+
+/**
+ * Receive a packet from a given socket fd
+ */
+static packet_t *receive_packet(private_socket_dynamic_socket_t *this,
+                                                               dynsock_t *skt)
+{
+       host_t *source = NULL, *dest = NULL;
+       ssize_t len;
+       char buffer[MAX_PACKET];
+       chunk_t data;
+       packet_t *packet;
+       struct msghdr msg;
+       struct cmsghdr *cmsgptr;
+       struct iovec iov;
+       char ancillary[64];
+       union {
+               struct sockaddr_in in4;
+               struct sockaddr_in6 in6;
+       } src;
+
+       msg.msg_name = &src;
+       msg.msg_namelen = sizeof(src);
+       iov.iov_base = buffer;
+       iov.iov_len = sizeof(buffer);
+       msg.msg_iov = &iov;
+       msg.msg_iovlen = 1;
+       msg.msg_control = ancillary;
+       msg.msg_controllen = sizeof(ancillary);
+       msg.msg_flags = 0;
+       len = recvmsg(skt->fd, &msg, 0);
+       if (len < 0)
+       {
+               DBG1(DBG_NET, "error reading socket: %s", strerror(errno));
+               return NULL;
+       }
+       DBG3(DBG_NET, "received packet %b", buffer, len);
+
+       if (len < MARKER_LEN)
+       {
+               DBG3(DBG_NET, "received packet too short (%d bytes)", len);
+               return NULL;
+       }
+
+       /* read ancillary data to get destination address */
+       for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL;
+                cmsgptr = CMSG_NXTHDR(&msg, cmsgptr))
+       {
+               if (cmsgptr->cmsg_len == 0)
+               {
+                       DBG1(DBG_NET, "error reading ancillary data");
+                       return NULL;
+               }
+
+               if (cmsgptr->cmsg_level == SOL_IPV6 &&
+                       cmsgptr->cmsg_type == IPV6_PKTINFO)
+               {
+                       struct in6_pktinfo *pktinfo;
+                       struct sockaddr_in6 dst;
+
+                       pktinfo = (struct in6_pktinfo*)CMSG_DATA(cmsgptr);
+                       memset(&dst, 0, sizeof(dst));
+                       memcpy(&dst.sin6_addr, &pktinfo->ipi6_addr, sizeof(dst.sin6_addr));
+                       dst.sin6_family = AF_INET6;
+                       dst.sin6_port = htons(skt->port);
+                       dest = host_create_from_sockaddr((sockaddr_t*)&dst);
+               }
+               if (cmsgptr->cmsg_level == SOL_IP &&
+                       cmsgptr->cmsg_type == IP_PKTINFO)
+               {
+                       struct in_pktinfo *pktinfo;
+                       struct sockaddr_in dst;
+
+                       pktinfo = (struct in_pktinfo*)CMSG_DATA(cmsgptr);
+                       memset(&dst, 0, sizeof(dst));
+                       memcpy(&dst.sin_addr, &pktinfo->ipi_addr, sizeof(dst.sin_addr));
+
+                       dst.sin_family = AF_INET;
+                       dst.sin_port = htons(skt->port);
+                       dest = host_create_from_sockaddr((sockaddr_t*)&dst);
+               }
+               if (dest)
+               {
+                       break;
+               }
+       }
+       if (dest == NULL)
+       {
+               DBG1(DBG_NET, "error reading IP header");
+               return NULL;
+       }
+       source = host_create_from_sockaddr((sockaddr_t*)&src);
+       DBG2(DBG_NET, "received packet: from %#H to %#H", source, dest);
+       data = chunk_create(buffer, len);
+
+       packet = packet_create();
+       packet->set_source(packet, source);
+       packet->set_destination(packet, dest);
+       /* we assume a non-ESP marker if none of the ports is on 500 */
+       if (dest->get_port(dest) != IKEV2_UDP_PORT &&
+               source->get_port(source) != IKEV2_UDP_PORT)
+       {
+               data = chunk_skip(data, MARKER_LEN);
+       }
+       packet->set_data(packet, chunk_clone(data));
+       return packet;
+}
+
+METHOD(socket_t, receiver, status_t,
+       private_socket_dynamic_socket_t *this, packet_t **packet)
+{
+       dynsock_t *selected;
+       packet_t *pkt;
+       bool oldstate;
+       fd_set fds;
+       int maxfd;
+
+       while (TRUE)
+       {
+               maxfd = build_fds(this, &fds);
+
+               DBG2(DBG_NET, "waiting for data on sockets");
+               oldstate = thread_cancelability(TRUE);
+               if (select(maxfd, &fds, NULL, NULL, NULL) <= 0)
+               {
+                       thread_cancelability(oldstate);
+                       return FAILED;
+               }
+               thread_cancelability(oldstate);
+
+               if (FD_ISSET(this->notify[0], &fds))
+               {       /* got notified, read garbage, rebuild fdset */
+                       char buf[1];
+
+                       ignore_result(read(this->notify[0], buf, sizeof(buf)));
+                       DBG2(DBG_NET, "rebuilding fdset due to newly bound ports");
+                       continue;
+               }
+               selected = scan_fds(this, &fds);
+               if (selected)
+               {
+                       break;
+               }
+       }
+       pkt = receive_packet(this, selected);
+       if (pkt)
+       {
+               *packet = pkt;
+               return SUCCESS;
+       }
+       return FAILED;
+}
+
+/**
+ * open a socket to send and receive packets
+ */
+static int open_socket(private_socket_dynamic_socket_t *this,
+                                          int family, u_int16_t port, bool first)
+{
+       int on = TRUE, type = UDP_ENCAP_ESPINUDP;
+       struct sockaddr_storage addr;
+       socklen_t addrlen;
+       u_int sol, pktinfo = 0;
+       int fd;
+
+       memset(&addr, 0, sizeof(addr));
+       /* precalculate constants depending on address family */
+       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);
+                       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);
+                       sol = SOL_IPV6;
+                       pktinfo = IPV6_RECVPKTINFO;
+                       break;
+               }
+               default:
+                       return 0;
+       }
+
+       fd = socket(family, SOCK_DGRAM, IPPROTO_UDP);
+       if (fd < 0)
+       {
+               DBG1(DBG_NET, "could not open socket: %s", strerror(errno));
+               return 0;
+       }
+       if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void*)&on, sizeof(on)) < 0)
+       {
+               DBG1(DBG_NET, "unable to set SO_REUSEADDR on socket: %s", strerror(errno));
+               close(fd);
+               return 0;
+       }
+
+       /* bind the socket */
+       if (bind(fd, (struct sockaddr *)&addr, addrlen) < 0)
+       {
+               DBG1(DBG_NET, "unable to bind socket: %s", strerror(errno));
+               close(fd);
+               return 0;
+       }
+
+       /* get additional packet info on receive */
+       if (setsockopt(fd, sol, pktinfo, &on, sizeof(on)) < 0)
+       {
+               DBG1(DBG_NET, "unable to set IP_PKTINFO on socket: %s", strerror(errno));
+               close(fd);
+               return 0;
+       }
+
+       if (!charon->kernel_interface->bypass_socket(charon->kernel_interface,
+                                                                                                fd, family))
+       {
+               DBG1(DBG_NET, "installing IKE bypass policy failed");
+       }
+
+       /* enable UDP decapsulation globally, only for one socket needed */
+       if (first && setsockopt(fd, SOL_UDP, UDP_ENCAP, &type, sizeof(type)) < 0)
+       {
+               DBG1(DBG_NET, "unable to set UDP_ENCAP: %s", strerror(errno));
+       }
+       return fd;
+}
+
+/**
+ * Find/Create a socket to send from host
+ */
+static dynsock_t *find_socket(private_socket_dynamic_socket_t *this,
+                                                         int family, u_int16_t port)
+{
+       dynsock_t *skt, lookup = {
+               .family = family,
+               .port = port,
+       };
+       char buf[] = {0x01};
+       int fd;
+
+       skt = this->sockets->get(this->sockets, &lookup);
+       if (skt)
+       {
+               return skt;
+       }
+       fd = open_socket(this, family, port,
+                                        this->sockets->get_count(this->sockets));
+       if (!fd)
+       {
+               return NULL;
+       }
+       INIT(skt,
+               .family = family,
+               .port = port,
+               .fd = fd,
+       );
+       this->sockets->put(this->sockets, skt, skt);
+       /* notify receiver thread to reread socket list */
+       ignore_result(write(this->notify[1], buf, sizeof(buf)));
+
+       return skt;
+}
+
+METHOD(socket_t, sender, status_t,
+       private_socket_dynamic_socket_t *this, packet_t *packet)
+{
+       dynsock_t *skt;
+       host_t *src, *dst;
+       int port, family;
+       ssize_t len;
+       chunk_t data, marked;
+       struct msghdr msg;
+       struct cmsghdr *cmsg;
+       struct iovec iov;
+
+       src = packet->get_source(packet);
+       dst = packet->get_destination(packet);
+       family = src->get_family(src);
+       port = src->get_port(src);
+       skt = find_socket(this, family, port);
+       if (!skt)
+       {
+               return FAILED;
+       }
+
+       data = packet->get_data(packet);
+       DBG2(DBG_NET, "sending packet: from %#H to %#H", src, dst);
+
+       /* use non-ESP marker if none of the ports is 500, not for keep alives */
+       if (port != IKEV2_UDP_PORT && dst->get_port(dst) != IKEV2_UDP_PORT &&
+               !(data.len == 1 && data.ptr[0] == 0xFF))
+       {
+               /* add non esp marker to packet */
+               if (data.len > MAX_PACKET - MARKER_LEN)
+               {
+                       DBG1(DBG_NET, "unable to send packet: it's too big (%d bytes)",
+                                data.len);
+                       return FAILED;
+               }
+               marked = chunk_alloc(data.len + MARKER_LEN);
+               memset(marked.ptr, 0, MARKER_LEN);
+               memcpy(marked.ptr + MARKER_LEN, data.ptr, data.len);
+               /* let the packet do the clean up for us */
+               packet->set_data(packet, marked);
+               data = marked;
+       }
+
+       memset(&msg, 0, sizeof(struct msghdr));
+       msg.msg_name = dst->get_sockaddr(dst);;
+       msg.msg_namelen = *dst->get_sockaddr_len(dst);
+       iov.iov_base = data.ptr;
+       iov.iov_len = data.len;
+       msg.msg_iov = &iov;
+       msg.msg_iovlen = 1;
+       msg.msg_flags = 0;
+
+       if (!src->is_anyaddr(src))
+       {
+               if (family == AF_INET)
+               {
+                       struct in_addr *addr;
+                       struct sockaddr_in *sin;
+                       char buf[CMSG_SPACE(sizeof(struct in_pktinfo))];
+                       struct in_pktinfo *pktinfo;
+
+                       msg.msg_control = buf;
+                       msg.msg_controllen = sizeof(buf);
+                       cmsg = CMSG_FIRSTHDR(&msg);
+                       cmsg->cmsg_level = SOL_IP;
+                       cmsg->cmsg_type = IP_PKTINFO;
+                       cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
+                       pktinfo = (struct in_pktinfo*)CMSG_DATA(cmsg);
+                       memset(pktinfo, 0, sizeof(struct in_pktinfo));
+                       addr = &pktinfo->ipi_spec_dst;
+                       sin = (struct sockaddr_in*)src->get_sockaddr(src);
+                       memcpy(addr, &sin->sin_addr, sizeof(struct in_addr));
+               }
+               else
+               {
+                       char buf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
+                       struct in6_pktinfo *pktinfo;
+                       struct sockaddr_in6 *sin;
+
+                       msg.msg_control = buf;
+                       msg.msg_controllen = sizeof(buf);
+                       cmsg = CMSG_FIRSTHDR(&msg);
+                       cmsg->cmsg_level = SOL_IPV6;
+                       cmsg->cmsg_type = IPV6_PKTINFO;
+                       cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+                       pktinfo = (struct in6_pktinfo*)CMSG_DATA(cmsg);
+                       memset(pktinfo, 0, sizeof(struct in6_pktinfo));
+                       sin = (struct sockaddr_in6*)src->get_sockaddr(src);
+                       memcpy(&pktinfo->ipi6_addr, &sin->sin6_addr, sizeof(struct in6_addr));
+               }
+       }
+
+       len = sendmsg(skt->fd, &msg, 0);
+       if (len != data.len)
+       {
+               DBG1(DBG_NET, "error writing to socket: %s", strerror(errno));
+               return FAILED;
+       }
+       return SUCCESS;
+}
+
+METHOD(socket_dynamic_socket_t, destroy, void,
+       private_socket_dynamic_socket_t *this)
+{
+       enumerator_t *enumerator;
+       dynsock_t *key, *value;
+
+       enumerator = this->sockets->create_enumerator(this->sockets);
+       while (enumerator->enumerate(enumerator, &key, &value))
+       {
+               close(value->fd);
+               free(value);
+       }
+       enumerator->destroy(enumerator);
+       this->sockets->destroy(this->sockets);
+
+       close(this->notify[0]);
+       close(this->notify[1]);
+       free(this);
+}
+
+/*
+ * See header for description
+ */
+socket_dynamic_socket_t *socket_dynamic_socket_create()
+{
+       private_socket_dynamic_socket_t *this;
+
+       INIT(this,
+               .public = {
+                       .socket = {
+                               .send = _sender,
+                               .receive = _receiver,
+                       },
+                       .destroy = _destroy,
+               },
+       );
+
+       if (pipe(this->notify) != 0)
+       {
+               DBG1(DBG_NET, "creating notify pipe for dynamic socket failed");
+               free(this);
+               return NULL;
+       }
+
+       this->sockets = hashtable_create((void*)hash, (void*)equals, 8);
+
+       return &this->public;
+}
+
diff --git a/src/charon/plugins/socket_dynamic/socket_dynamic_socket.h b/src/charon/plugins/socket_dynamic/socket_dynamic_socket.h
new file mode 100644 (file)
index 0000000..72551e5
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2010 Martin Willi
+ * Copyright (C) 2010 revosec AG
+ *
+ * This program 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 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program 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.
+ */
+
+/**
+ * @defgroup socket_dynamic_socket socket_dynamic_socket
+ * @{ @ingroup socket_dynamic
+ */
+
+#ifndef SOCKET_DYNAMIC_SOCKET_H_
+#define SOCKET_DYNAMIC_SOCKET_H_
+
+typedef struct socket_dynamic_socket_t socket_dynamic_socket_t;
+
+#include <network/socket.h>
+
+/**
+ * A socket implementation binding to ports on demand as required.
+ */
+struct socket_dynamic_socket_t {
+
+       /**
+        * Implements the socket_t interface.
+        */
+       socket_t socket;
+
+       /**
+        * Destroy a socket_dynamic_socket_t.
+        */
+       void (*destroy)(socket_dynamic_socket_t *this);
+};
+
+/**
+ * Create a socket_dynamic_socket instance.
+ */
+socket_dynamic_socket_t *socket_dynamic_socket_create();
+
+#endif /** SOCKET_DYNAMIC_SOCKET_H_ @}*/