summaryrefslogtreecommitdiff
path: root/src/ipcpd/udp
diff options
context:
space:
mode:
authorDimitri Staessens <dimitri.staessens@ugent.be>2017-12-18 20:50:04 +0100
committerSander Vrijders <sander.vrijders@ugent.be>2017-12-19 10:27:02 +0100
commite5cc7fbcf165ef9c205b6c136f239f645a8d7830 (patch)
tree334120a6cc6dcbe240076b3957233da337bf7ad2 /src/ipcpd/udp
parentd654c997f142b22e2529dc0fb9fab4f799f7ef56 (diff)
downloadouroboros-e5cc7fbcf165ef9c205b6c136f239f645a8d7830.tar.gz
ouroboros-e5cc7fbcf165ef9c205b6c136f239f645a8d7830.zip
ipcpd: Use the term "layer" and deprecate "shim"
This changes the terminology to use layer instead of DIF and deprecate the word "shim" for the IPCPs that attach to Ethernet LLC and UDP .The terminology has not yet been changed in the variable names etc. This reflects the design choices in Ouroboros to make IPCPs pure resource allocators instead of also providing an "IPC service". The Ouroboros IPCPs that attach to Ethernet and UDP implement the allocator and are thus not really shims. Signed-off-by: Dimitri Staessens <dimitri.staessens@ugent.be> Signed-off-by: Sander Vrijders <sander.vrijders@ugent.be>
Diffstat (limited to 'src/ipcpd/udp')
-rw-r--r--src/ipcpd/udp/CMakeLists.txt60
-rw-r--r--src/ipcpd/udp/main.c1167
2 files changed, 1227 insertions, 0 deletions
diff --git a/src/ipcpd/udp/CMakeLists.txt b/src/ipcpd/udp/CMakeLists.txt
new file mode 100644
index 00000000..20c1b58e
--- /dev/null
+++ b/src/ipcpd/udp/CMakeLists.txt
@@ -0,0 +1,60 @@
+get_filename_component(CURRENT_SOURCE_PARENT_DIR
+ ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY)
+get_filename_component(CURRENT_BINARY_PARENT_DIR
+ ${CMAKE_CURRENT_BINARY_DIR} DIRECTORY)
+
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+include_directories(${CURRENT_SOURCE_PARENT_DIR})
+include_directories(${CURRENT_BINARY_PARENT_DIR})
+
+include_directories(${CMAKE_SOURCE_DIR}/include)
+include_directories(${CMAKE_BINARY_DIR}/include)
+
+set(IPCP_UDP_TARGET ipcpd-udp CACHE INTERNAL "")
+
+set(UDP_SOURCES
+ # Add source files here
+ ${CMAKE_CURRENT_SOURCE_DIR}/main.c)
+
+add_executable(ipcpd-udp ${UDP_SOURCES} ${IPCP_SOURCES})
+target_link_libraries(ipcpd-udp LINK_PUBLIC ouroboros-dev)
+
+# Find the nsupdate executable
+find_program(NSUPDATE_EXECUTABLE
+ NAMES nsupdate
+ DOC "The nsupdate tool that enables DDNS")
+
+# Find the nslookup executable
+find_program(NSLOOKUP_EXECUTABLE
+ NAMES nslookup
+ DOC "The nslookup tool that resolves DNS names")
+
+mark_as_advanced(NSLOOKUP_EXECUTABLE NSUPDATE_EXECUTABLE)
+
+if (NSLOOKUP_EXECUTABLE AND NSUPDATE_EXECUTABLE)
+ set(DISABLE_DDNS FALSE CACHE BOOL "Disable DDNS support")
+ if (NOT DISABLE_DNS)
+ message(STATUS "DDNS support enabled")
+ set(HAVE_DDNS TRUE CACHE INTERNAL "")
+ else ()
+ message(STATUS "DDNS support disabled by user")
+ unset(HAVE_DDNS CACHE)
+ endif ()
+else ()
+ if (NSLOOKUP_EXECUTABLE)
+ message(STATUS "Install nsupdate to enable DDNS support")
+ elseif (NSUPDATE_EXECUTABLE)
+ message(STATUS "Install nslookup to enable DDNS support")
+ else ()
+ message(STATUS "Install nslookup and nsupdate to enable DDNS support")
+ endif ()
+endif ()
+
+include(AddCompileFlags)
+if (CMAKE_BUILD_TYPE MATCHES "Debug*")
+ add_compile_flags(ipcpd-udp -DCONFIG_OUROBOROS_DEBUG)
+endif ()
+
+install(TARGETS ipcpd-udp RUNTIME DESTINATION sbin)
diff --git a/src/ipcpd/udp/main.c b/src/ipcpd/udp/main.c
new file mode 100644
index 00000000..c54e6624
--- /dev/null
+++ b/src/ipcpd/udp/main.c
@@ -0,0 +1,1167 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2017
+ *
+ * IPC process over UDP
+ *
+ * Dimitri Staessens <dimitri.staessens@ugent.be>
+ * Sander Vrijders <sander.vrijders@ugent.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#define _POSIX_C_SOURCE 200112L
+
+#include "config.h"
+
+#define OUROBOROS_PREFIX "ipcpd/udp"
+
+#include <ouroboros/hash.h>
+#include <ouroboros/list.h>
+#include <ouroboros/utils.h>
+#include <ouroboros/dev.h>
+#include <ouroboros/ipcp-dev.h>
+#include <ouroboros/fqueue.h>
+#include <ouroboros/errno.h>
+#include <ouroboros/logs.h>
+
+#include "ipcp.h"
+#include "shim-data.h"
+
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+
+#define FLOW_REQ 1
+#define FLOW_REPLY 2
+
+#define THIS_TYPE IPCP_UDP
+#define LISTEN_PORT htons(0x0D1F)
+#define SHIM_UDP_BUF_SIZE 256
+#define SHIM_UDP_MSG_SIZE 256
+#define SHIM_UDP_MAX_SDU_SIZE 8980
+#define DNS_TTL 86400
+#define FD_UPDATE_TIMEOUT 100 /* microseconds */
+
+#define local_ip (udp_data.s_saddr.sin_addr.s_addr)
+
+#define UDP_MAX_PORTS 0xFFFF
+
+struct mgmt_msg {
+ uint16_t src_udp_port;
+ uint16_t dst_udp_port;
+ uint8_t code;
+ uint8_t qoscube;
+ uint8_t response;
+} __attribute__((packed));
+
+struct uf {
+ int udp;
+ int skfd;
+};
+
+struct {
+ struct shim_data * shim_data;
+
+ uint32_t ip_addr;
+ uint32_t dns_addr;
+ /* listen server */
+ struct sockaddr_in s_saddr;
+ int s_fd;
+
+ fset_t * np1_flows;
+ fqueue_t * fq;
+ fd_set flow_fd_s;
+ /* bidir mappings of (n - 1) file descriptor to (n) flow descriptor */
+ int uf_to_fd[FD_SETSIZE];
+ struct uf fd_to_uf[SYS_MAX_FLOWS];
+ pthread_rwlock_t flows_lock;
+
+ pthread_t sduloop;
+ pthread_t handler;
+ pthread_t sdu_reader;
+
+ bool fd_set_mod;
+ pthread_cond_t fd_set_cond;
+ pthread_mutex_t fd_set_lock;
+} udp_data;
+
+static int udp_data_init(void)
+{
+ int i;
+
+ for (i = 0; i < FD_SETSIZE; ++i)
+ udp_data.uf_to_fd[i] = -1;
+
+ for (i = 0; i < SYS_MAX_FLOWS; ++i) {
+ udp_data.fd_to_uf[i].skfd = -1;
+ udp_data.fd_to_uf[i].udp = -1;
+ }
+
+ FD_ZERO(&udp_data.flow_fd_s);
+
+ udp_data.np1_flows = fset_create();
+ if (udp_data.np1_flows == NULL)
+ return -ENOMEM;
+
+ udp_data.fq = fqueue_create();
+ if (udp_data.fq == NULL) {
+ fset_destroy(udp_data.np1_flows);
+ return -ENOMEM;
+ }
+
+ udp_data.shim_data = shim_data_create();
+ if (udp_data.shim_data == NULL) {
+ fqueue_destroy(udp_data.fq);
+ fset_destroy(udp_data.np1_flows);
+ return -ENOMEM;
+ }
+
+ pthread_rwlock_init(&udp_data.flows_lock, NULL);
+ pthread_cond_init(&udp_data.fd_set_cond, NULL);
+ pthread_mutex_init(&udp_data.fd_set_lock, NULL);
+
+ return 0;
+}
+
+static void udp_data_fini(void)
+{
+ fset_destroy(udp_data.np1_flows);
+ fqueue_destroy(udp_data.fq);
+
+ shim_data_destroy(udp_data.shim_data);
+
+ pthread_rwlock_destroy(&udp_data.flows_lock);
+ pthread_mutex_destroy(&udp_data.fd_set_lock);
+ pthread_cond_destroy(&udp_data.fd_set_cond);
+}
+
+static void set_fd(int fd)
+{
+ pthread_mutex_lock(&udp_data.fd_set_lock);
+
+ udp_data.fd_set_mod = true;
+ FD_SET(fd, &udp_data.flow_fd_s);
+
+ while (udp_data.fd_set_mod)
+ pthread_cond_wait(&udp_data.fd_set_cond, &udp_data.fd_set_lock);
+
+ pthread_mutex_unlock(&udp_data.fd_set_lock);
+}
+
+static void clr_fd(int fd)
+{
+ pthread_mutex_lock(&udp_data.fd_set_lock);
+
+ udp_data.fd_set_mod = true;
+ FD_CLR(fd, &udp_data.flow_fd_s);
+
+ while (udp_data.fd_set_mod)
+ pthread_cond_wait(&udp_data.fd_set_cond, &udp_data.fd_set_lock);
+
+ pthread_mutex_unlock(&udp_data.fd_set_lock);
+}
+
+static int send_shim_udp_msg(uint8_t * buf,
+ size_t len,
+ uint32_t dst_ip_addr)
+{
+ struct sockaddr_in r_saddr;
+
+ memset((char *)&r_saddr, 0, sizeof(r_saddr));
+ r_saddr.sin_family = AF_INET;
+ r_saddr.sin_addr.s_addr = dst_ip_addr;
+ r_saddr.sin_port = LISTEN_PORT;
+
+ if (sendto(udp_data.s_fd, buf, len, 0,
+ (struct sockaddr *) &r_saddr,
+ sizeof(r_saddr)) == -1) {
+ log_err("Failed to send message.");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int ipcp_udp_port_alloc(uint32_t dst_ip_addr,
+ uint16_t src_udp_port,
+ const uint8_t * dst,
+ qoscube_t cube)
+{
+ uint8_t * buf;
+ struct mgmt_msg * msg;
+ size_t len;
+ int ret;
+
+ len = sizeof(*msg) + ipcp_dir_hash_len();
+
+ buf = malloc(len);
+ if (buf == NULL)
+ return -1;
+
+ msg = (struct mgmt_msg *) buf;
+ msg->code = FLOW_REQ;
+ msg->src_udp_port = src_udp_port;
+ msg->qoscube = cube;
+
+ memcpy(msg + 1, dst, ipcp_dir_hash_len());
+
+ ret = send_shim_udp_msg(buf, len, dst_ip_addr);
+
+ free(buf);
+
+ return ret;
+}
+
+static int ipcp_udp_port_alloc_resp(uint32_t dst_ip_addr,
+ uint16_t src_udp_port,
+ uint16_t dst_udp_port,
+ int response)
+{
+ uint8_t * buf;
+ struct mgmt_msg * msg;
+ int ret;
+
+ buf = malloc(sizeof(*msg));
+ if (buf == NULL)
+ return -1;
+
+ msg = (struct mgmt_msg *) buf;
+ msg->code = FLOW_REPLY;
+ msg->src_udp_port = src_udp_port;
+ msg->dst_udp_port = dst_udp_port;
+ msg->response = response;
+
+ ret = send_shim_udp_msg(buf, sizeof(*msg), dst_ip_addr);
+
+ free(buf);
+
+ return ret;
+}
+
+static int ipcp_udp_port_req(struct sockaddr_in * c_saddr,
+ const uint8_t * dst,
+ qoscube_t cube)
+{
+ struct timespec ts = {0, FD_UPDATE_TIMEOUT * 1000};
+ struct timespec abstime;
+ struct sockaddr_in f_saddr;
+ socklen_t f_saddr_len = sizeof(f_saddr);
+ int skfd;
+ int fd;
+
+ log_dbg("Port request arrived from UDP port %d",
+ ntohs(c_saddr->sin_port));
+
+ if ((skfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
+ log_err("Could not create UDP socket.");
+ return -1;
+ }
+
+ memset((char *) &f_saddr, 0, sizeof(f_saddr));
+ f_saddr.sin_family = AF_INET;
+ f_saddr.sin_addr.s_addr = local_ip;
+ f_saddr.sin_port = 0;
+
+ if (bind(skfd, (struct sockaddr *) &f_saddr, sizeof(f_saddr)) < 0) {
+ log_err("Could not bind to socket.");
+ close(skfd);
+ return -1;
+ }
+
+ if (getsockname(skfd, (struct sockaddr *) &f_saddr, &f_saddr_len) < 0) {
+ log_err("Could not get address from fd.");
+ return -1;
+ }
+
+ /* connect stores the remote address in the file descriptor */
+ if (connect(skfd, (struct sockaddr *) c_saddr, sizeof(*c_saddr)) < 0) {
+ log_err("Could not connect to remote UDP client.");
+ close(skfd);
+ return -1;
+ }
+
+ clock_gettime(PTHREAD_COND_CLOCK, &abstime);
+
+ pthread_mutex_lock(&ipcpi.alloc_lock);
+
+ while (ipcpi.alloc_id != -1 && ipcp_get_state() == IPCP_OPERATIONAL) {
+ ts_add(&abstime, &ts, &abstime);
+ pthread_cond_timedwait(&ipcpi.alloc_cond,
+ &ipcpi.alloc_lock,
+ &abstime);
+ }
+
+ if (ipcp_get_state() != IPCP_OPERATIONAL) {
+ log_dbg("Won't allocate over non-operational IPCP.");
+ pthread_mutex_unlock(&ipcpi.alloc_lock);
+ return -1;
+ }
+
+ /* reply to IRM */
+ fd = ipcp_flow_req_arr(getpid(), dst, ipcp_dir_hash_len(), cube);
+ if (fd < 0) {
+ pthread_mutex_unlock(&ipcpi.alloc_lock);
+ log_err("Could not get new flow from IRMd.");
+ close(skfd);
+ return -1;
+ }
+
+ pthread_rwlock_wrlock(&udp_data.flows_lock);
+
+ udp_data.uf_to_fd[skfd] = fd;
+ udp_data.fd_to_uf[fd].skfd = skfd;
+ udp_data.fd_to_uf[fd].udp = f_saddr.sin_port;
+
+ pthread_rwlock_unlock(&udp_data.flows_lock);
+
+ ipcpi.alloc_id = fd;
+ pthread_cond_broadcast(&ipcpi.alloc_cond);
+
+ pthread_mutex_unlock(&ipcpi.alloc_lock);
+
+ log_dbg("Pending allocation request, fd %d, UDP port (%d, %d).",
+ fd, ntohs(f_saddr.sin_port), ntohs(c_saddr->sin_port));
+
+ return 0;
+}
+
+/* returns the n flow descriptor */
+static int udp_port_to_fd(int udp_port)
+{
+ int i;
+
+ for (i = 0; i < SYS_MAX_FLOWS; ++i)
+ if (udp_data.fd_to_uf[i].udp == udp_port)
+ return i;
+
+ return -1;
+}
+
+static int ipcp_udp_port_alloc_reply(uint16_t src_udp_port,
+ uint16_t dst_udp_port,
+ int response)
+{
+ int fd = -1;
+ int ret = 0;
+ int skfd = -1;
+
+ struct sockaddr_in t_saddr;
+ socklen_t t_saddr_len = sizeof(t_saddr);
+
+ log_dbg("Received reply for flow on udp port %d.",
+ ntohs(dst_udp_port));
+
+ pthread_rwlock_rdlock(&udp_data.flows_lock);
+
+ fd = udp_port_to_fd(dst_udp_port);
+ skfd = udp_data.fd_to_uf[fd].skfd;
+
+ pthread_rwlock_unlock(&udp_data.flows_lock);
+
+ /* get the original address with the LISTEN PORT */
+ if (getpeername(skfd, (struct sockaddr *) &t_saddr, &t_saddr_len) < 0) {
+ log_dbg("Flow with fd %d has no peer.", fd);
+ return -1;
+ }
+
+ /* connect to the flow udp port */
+ t_saddr.sin_port = src_udp_port;
+
+ if (connect(skfd, (struct sockaddr *) &t_saddr, sizeof(t_saddr)) < 0) {
+ close(skfd);
+ return -1;
+ }
+
+ pthread_rwlock_rdlock(&udp_data.flows_lock);
+
+ set_fd(skfd);
+
+ pthread_rwlock_unlock(&udp_data.flows_lock);
+
+ if (ipcp_flow_alloc_reply(fd, response) < 0)
+ return -1;
+
+ log_dbg("Flow allocation completed, UDP ports: (%d, %d).",
+ ntohs(dst_udp_port), ntohs(src_udp_port));
+
+ return ret;
+}
+
+static void * ipcp_udp_listener(void * o)
+{
+ uint8_t buf[SHIM_UDP_MSG_SIZE];
+ ssize_t n = 0;
+ struct sockaddr_in c_saddr;
+ int sfd = udp_data.s_fd;
+
+ (void) o;
+
+ while (true) {
+ struct mgmt_msg * msg = NULL;
+
+ memset(&buf, 0, SHIM_UDP_MSG_SIZE);
+ n = sizeof(c_saddr);
+ n = recvfrom(sfd, buf, SHIM_UDP_MSG_SIZE, 0,
+ (struct sockaddr *) &c_saddr, (unsigned *) &n);
+ if (n < 0)
+ continue;
+
+ /* flow alloc request from other host */
+ if (gethostbyaddr((const char *) &c_saddr.sin_addr.s_addr,
+ sizeof(c_saddr.sin_addr.s_addr), AF_INET)
+ == NULL)
+ continue;
+
+ msg = (struct mgmt_msg *) buf;
+
+ switch (msg->code) {
+ case FLOW_REQ:
+ c_saddr.sin_port = msg->src_udp_port;
+ ipcp_udp_port_req(&c_saddr,
+ (uint8_t *) (msg + 1),
+ msg->qoscube);
+ break;
+ case FLOW_REPLY:
+ ipcp_udp_port_alloc_reply(msg->src_udp_port,
+ msg->dst_udp_port,
+ msg->response);
+ break;
+ default:
+ log_err("Unknown message received %d.", msg->code);
+ continue;
+ }
+
+ c_saddr.sin_port = LISTEN_PORT;
+ }
+
+ return 0;
+}
+
+static void * ipcp_udp_sdu_reader(void * o)
+{
+ ssize_t n;
+ int skfd;
+ int fd;
+ /* FIXME: avoid this copy */
+ char buf[SHIM_UDP_MAX_SDU_SIZE];
+ struct sockaddr_in r_saddr;
+ struct timeval tv = {0, FD_UPDATE_TIMEOUT};
+ fd_set read_fds;
+ int flags;
+
+ (void) o;
+
+ while (true) {
+ pthread_rwlock_rdlock(&udp_data.flows_lock);
+ pthread_mutex_lock(&udp_data.fd_set_lock);
+
+ read_fds = udp_data.flow_fd_s;
+ udp_data.fd_set_mod = false;
+ pthread_cond_broadcast(&udp_data.fd_set_cond);
+
+ pthread_mutex_unlock(&udp_data.fd_set_lock);
+ pthread_rwlock_unlock(&udp_data.flows_lock);
+
+ if (select(FD_SETSIZE, &read_fds, NULL, NULL, &tv) <= 0)
+ continue;
+
+ for (skfd = 0; skfd < FD_SETSIZE; ++skfd) {
+ if (!FD_ISSET(skfd, &read_fds))
+ continue;
+ flags = fcntl(skfd, F_GETFL, 0);
+ fcntl(skfd, F_SETFL, flags | O_NONBLOCK);
+ n = sizeof(r_saddr);
+ if ((n = recvfrom(skfd,
+ &buf,
+ SHIM_UDP_MAX_SDU_SIZE,
+ 0,
+ (struct sockaddr *) &r_saddr,
+ (unsigned *) &n)) <= 0)
+ continue;
+
+ pthread_rwlock_rdlock(&udp_data.flows_lock);
+
+ fd = udp_data.uf_to_fd[skfd];
+
+ pthread_rwlock_unlock(&udp_data.flows_lock);
+
+ flow_write(fd, buf, n);
+ }
+ }
+
+ return (void *) 0;
+}
+
+static void * ipcp_udp_sdu_loop(void * o)
+{
+ int fd;
+ struct shm_du_buff * sdb;
+
+ (void) o;
+
+ while (true) {
+ fevent(udp_data.np1_flows, udp_data.fq, NULL);
+ while ((fd = fqueue_next(udp_data.fq)) >= 0) {
+ if (ipcp_flow_read(fd, &sdb)) {
+ log_err("Bad read from fd %d.", fd);
+ continue;
+ }
+
+ pthread_rwlock_rdlock(&udp_data.flows_lock);
+
+ fd = udp_data.fd_to_uf[fd].skfd;
+
+ pthread_rwlock_unlock(&udp_data.flows_lock);
+
+ pthread_cleanup_push((void (*)(void *)) ipcp_sdb_release,
+ (void *) sdb);
+
+ if (send(fd, shm_du_buff_head(sdb),
+ shm_du_buff_tail(sdb) - shm_du_buff_head(sdb),
+ 0) < 0)
+ log_err("Failed to send SDU.");
+
+ pthread_cleanup_pop(true);
+ }
+ }
+
+ return (void *) 1;
+}
+
+static int ipcp_udp_bootstrap(const struct ipcp_config * conf)
+{
+ struct sockaddr_in s_saddr;
+ char ipstr[INET_ADDRSTRLEN];
+ char dnsstr[INET_ADDRSTRLEN];
+ int enable = 1;
+ int fd = -1;
+
+ assert(conf);
+ assert(conf->type == THIS_TYPE);
+
+ if (inet_ntop(AF_INET,
+ &conf->ip_addr,
+ ipstr,
+ INET_ADDRSTRLEN) == NULL) {
+ log_err("Failed to convert IP address");
+ return -1;
+ }
+
+ if (conf->dns_addr != 0) {
+ if (inet_ntop(AF_INET,
+ &conf->dns_addr,
+ dnsstr,
+ INET_ADDRSTRLEN) == NULL) {
+ log_err("Failed to convert DNS address");
+ return -1;
+ }
+#ifndef HAVE_DDNS
+ log_warn("DNS disabled at compile time, address ignored");
+#endif
+ } else {
+ strcpy(dnsstr, "not set");
+ }
+
+ /* UDP listen server */
+ if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
+ log_err("Can't create socket.");
+ goto fail_socket;
+ }
+
+ if (setsockopt(fd,
+ SOL_SOCKET,
+ SO_REUSEADDR,
+ &enable,
+ sizeof(int)) < 0)
+ log_warn("Failed to set SO_REUSEADDR.");
+
+ memset((char *) &s_saddr, 0, sizeof(s_saddr));
+ udp_data.s_saddr.sin_family = AF_INET;
+ udp_data.s_saddr.sin_addr.s_addr = conf->ip_addr;
+ udp_data.s_saddr.sin_port = LISTEN_PORT;
+
+ if (bind(fd,
+ (struct sockaddr *) &udp_data.s_saddr,
+ sizeof(udp_data.s_saddr)) < 0) {
+ log_err("Couldn't bind to %s.", ipstr);
+ goto fail_bind;
+ }
+
+ udp_data.s_fd = fd;
+ udp_data.ip_addr = conf->ip_addr;
+ udp_data.dns_addr = conf->dns_addr;
+
+ FD_CLR(udp_data.s_fd, &udp_data.flow_fd_s);
+
+ ipcp_set_state(IPCP_OPERATIONAL);
+
+ if (pthread_create(&udp_data.handler,
+ NULL,
+ ipcp_udp_listener,
+ NULL)) {
+ ipcp_set_state(IPCP_INIT);
+ goto fail_bind;
+ }
+
+ if (pthread_create(&udp_data.sdu_reader,
+ NULL,
+ ipcp_udp_sdu_reader,
+ NULL)) {
+ ipcp_set_state(IPCP_INIT);
+ goto fail_sdu_reader;
+ }
+
+ if (pthread_create(&udp_data.sduloop,
+ NULL,
+ ipcp_udp_sdu_loop,
+ NULL)) {
+ ipcp_set_state(IPCP_INIT);
+ goto fail_sduloop;
+ }
+
+ log_dbg("Bootstrapped IPCP over UDP with pid %d.", getpid());
+ log_dbg("Bound to IP address %s.", ipstr);
+ log_dbg("DNS server address is %s.", dnsstr);
+
+ return 0;
+
+ fail_sduloop:
+ pthread_cancel(udp_data.sdu_reader);
+ pthread_join(udp_data.sdu_reader, NULL);
+ fail_sdu_reader:
+ pthread_cancel(udp_data.handler);
+ pthread_join(udp_data.handler, NULL);
+ fail_bind:
+ close(fd);
+ fail_socket:
+ return -1;
+}
+
+#ifdef HAVE_DDNS
+/* FIXME: Dependency on nsupdate to be removed in the end */
+/* NOTE: Disgusted with this crap */
+static int ddns_send(char * cmd)
+{
+ pid_t pid = -1;
+ int wstatus;
+ int pipe_fd[2];
+ char * argv[] = {NSUPDATE_EXEC, 0};
+ char * envp[] = {0};
+
+ if (pipe(pipe_fd)) {
+ log_err("Failed to create pipe.");
+ return -1;
+ }
+
+ pid = fork();
+ if (pid == -1) {
+ log_err("Failed to fork.");
+ return -1;
+ }
+
+ if (pid == 0) {
+ close(pipe_fd[1]);
+ dup2(pipe_fd[0], 0);
+ execve(argv[0], &argv[0], envp);
+ }
+
+ close(pipe_fd[0]);
+
+ if (write(pipe_fd[1], cmd, strlen(cmd)) == -1) {
+ log_err("Failed to communicate with nsupdate.");
+ close(pipe_fd[1]);
+ return -1;
+ }
+
+ waitpid(pid, &wstatus, 0);
+ if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0)
+ log_dbg("Succesfully communicated with DNS server.");
+ else
+ log_err("Failed to register with DNS server.");
+
+ close(pipe_fd[1]);
+ return 0;
+}
+
+static uint32_t ddns_resolve(char * name,
+ uint32_t dns_addr)
+{
+ pid_t pid = -1;
+ int wstatus;
+ int pipe_fd[2];
+ char dnsstr[INET_ADDRSTRLEN];
+ char buf[SHIM_UDP_BUF_SIZE];
+ ssize_t count = 0;
+ char * substr = NULL;
+ char * substr2 = NULL;
+ char * addr_str = "Address:";
+ uint32_t ip_addr = 0;
+
+ if (inet_ntop(AF_INET, &dns_addr, dnsstr, INET_ADDRSTRLEN) == NULL)
+ return 0;
+
+ if (pipe(pipe_fd)) {
+ log_err("Failed to create pipe.");
+ return 0;
+ }
+
+ pid = fork();
+ if (pid == -1) {
+ log_err("Failed to fork.");
+ return 0;
+ }
+
+ if (pid == 0) {
+ char * argv[] = {NSLOOKUP_EXEC, name, dnsstr, 0};
+ char * envp[] = {0};
+
+ close(pipe_fd[0]);
+ dup2(pipe_fd[1], 1);
+ execve(argv[0], &argv[0], envp);
+ }
+
+ close(pipe_fd[1]);
+
+ count = read(pipe_fd[0], buf, SHIM_UDP_BUF_SIZE);
+ if (count <= 0) {
+ log_err("Failed to communicate with nslookup.");
+ close(pipe_fd[0]);
+ return 0;
+ }
+
+ close(pipe_fd[0]);
+
+ waitpid(pid, &wstatus, 0);
+ if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0)
+ log_dbg("Succesfully communicated with nslookup.");
+ else
+ log_err("Failed to resolve DNS address.");
+
+ buf[count] = '\0';
+ substr = strtok(buf, "\n");
+ while (substr != NULL) {
+ substr2 = substr;
+ substr = strtok(NULL, "\n");
+ }
+
+ if (substr2 == NULL || strstr(substr2, addr_str) == NULL) {
+ log_err("Failed to resolve DNS address.");
+ return 0;
+ }
+
+ if (inet_pton(AF_INET, substr2 + strlen(addr_str) + 1, &ip_addr) != 1) {
+ log_err("Failed to resolve DNS address.");
+ return 0;
+ }
+
+ return ip_addr;
+}
+#endif
+
+static int ipcp_udp_reg(const uint8_t * hash)
+{
+#ifdef HAVE_DDNS
+ char ipstr[INET_ADDRSTRLEN];
+ char dnsstr[INET_ADDRSTRLEN];
+ char cmd[1000];
+ uint32_t dns_addr;
+ uint32_t ip_addr;
+#endif
+ char hashstr[ipcp_dir_hash_strlen() + 1];
+
+ assert(hash);
+
+ ipcp_hash_str(hashstr, hash);
+
+ if (shim_data_reg_add_entry(udp_data.shim_data, hash)) {
+ log_err("Failed to add " HASH_FMT " to local registry.",
+ HASH_VAL(hash));
+ return -1;
+ }
+
+#ifdef HAVE_DDNS
+ /* register application with DNS server */
+
+ dns_addr = udp_data.dns_addr;
+
+ if (dns_addr != 0) {
+ ip_addr = udp_data.ip_addr;
+
+ if (inet_ntop(AF_INET, &ip_addr,
+ ipstr, INET_ADDRSTRLEN) == NULL) {
+ return -1;
+ }
+
+ if (inet_ntop(AF_INET, &dns_addr,
+ dnsstr, INET_ADDRSTRLEN) == NULL) {
+ return -1;
+ }
+
+ sprintf(cmd, "server %s\nupdate add %s %d A %s\nsend\nquit\n",
+ dnsstr, hashstr, DNS_TTL, ipstr);
+
+ if (ddns_send(cmd)) {
+ shim_data_reg_del_entry(udp_data.shim_data, hash);
+ return -1;
+ }
+ }
+#endif
+ log_dbg("Registered " HASH_FMT ".", HASH_VAL(hash));
+
+ return 0;
+}
+
+static int ipcp_udp_unreg(const uint8_t * hash)
+{
+#ifdef HAVE_DDNS
+ char dnsstr[INET_ADDRSTRLEN];
+ /* max DNS name length + max IP length + max command length */
+ char cmd[100];
+ uint32_t dns_addr;
+#endif
+ char hashstr[ipcp_dir_hash_strlen() + 1];
+
+ assert(hash);
+
+ ipcp_hash_str(hashstr, hash);
+
+#ifdef HAVE_DDNS
+ /* unregister application with DNS server */
+
+ dns_addr = udp_data.dns_addr;
+
+ if (dns_addr != 0) {
+ if (inet_ntop(AF_INET, &dns_addr, dnsstr, INET_ADDRSTRLEN)
+ == NULL) {
+ return -1;
+ }
+ sprintf(cmd, "server %s\nupdate delete %s A\nsend\nquit\n",
+ dnsstr, hashstr);
+
+ ddns_send(cmd);
+ }
+#endif
+
+ shim_data_reg_del_entry(udp_data.shim_data, hash);
+
+ log_dbg("Unregistered " HASH_FMT ".", HASH_VAL(hash));
+
+ return 0;
+}
+
+static int ipcp_udp_query(const uint8_t * hash)
+{
+ uint32_t ip_addr = 0;
+ struct hostent * h;
+#ifdef HAVE_DDNS
+ uint32_t dns_addr = 0;
+#endif
+ char hashstr[ipcp_dir_hash_strlen() + 1];
+
+ assert(hash);
+
+ ipcp_hash_str(hashstr, hash);
+
+ if (shim_data_dir_has(udp_data.shim_data, hash))
+ return 0;
+
+#ifdef HAVE_DDNS
+ dns_addr = udp_data.dns_addr;
+
+ if (dns_addr != 0) {
+ ip_addr = ddns_resolve(hashstr, dns_addr);
+ if (ip_addr == 0) {
+ log_dbg("Could not resolve %s.", hashstr);
+ return -1;
+ }
+ } else {
+#endif
+ h = gethostbyname(hashstr);
+ if (h == NULL) {
+ log_dbg("Could not resolve %s.", hashstr);
+ return -1;
+ }
+
+ ip_addr = *((uint32_t *) (h->h_addr_list[0]));
+#ifdef HAVE_DDNS
+ }
+#endif
+
+ if (shim_data_dir_add_entry(udp_data.shim_data, hash, ip_addr)) {
+ log_err("Failed to add directory entry.");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int ipcp_udp_flow_alloc(int fd,
+ const uint8_t * dst,
+ qoscube_t cube)
+{
+ struct sockaddr_in r_saddr; /* server address */
+ struct sockaddr_in f_saddr; /* flow */
+ socklen_t f_saddr_len = sizeof(f_saddr);
+ int skfd;
+ uint32_t ip_addr = 0;
+
+ log_dbg("Allocating flow to " HASH_FMT ".", HASH_VAL(dst));
+
+ assert(dst);
+
+ if (cube != QOS_CUBE_BE) {
+ log_dbg("Unsupported QoS requested.");
+ return -1;
+ }
+
+ skfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+
+ /* this socket is for the flow */
+ memset((char *) &f_saddr, 0, sizeof(f_saddr));
+ f_saddr.sin_family = AF_INET;
+ f_saddr.sin_addr.s_addr = local_ip;
+ f_saddr.sin_port = 0;
+
+ if (bind(skfd, (struct sockaddr *) &f_saddr, sizeof(f_saddr)) < 0) {
+ close(skfd);
+ return -1;
+ }
+
+ if (getsockname(skfd, (struct sockaddr *) &f_saddr, &f_saddr_len) < 0) {
+ log_err("Could not get address from fd.");
+ close(skfd);
+ return -1;
+ }
+
+ if (!shim_data_dir_has(udp_data.shim_data, dst)) {
+ log_dbg("Could not resolve destination.");
+ close(skfd);
+ return -1;
+ }
+ ip_addr = (uint32_t) shim_data_dir_get_addr(udp_data.shim_data, dst);
+
+ /* connect to server (store the remote IP address in the fd) */
+ memset((char *) &r_saddr, 0, sizeof(r_saddr));
+ r_saddr.sin_family = AF_INET;
+ r_saddr.sin_addr.s_addr = ip_addr;
+ r_saddr.sin_port = LISTEN_PORT;
+
+ if (connect(skfd, (struct sockaddr *) &r_saddr, sizeof(r_saddr)) < 0) {
+ close(skfd);
+ return -1;
+ }
+
+ pthread_rwlock_wrlock(&udp_data.flows_lock);
+
+ udp_data.fd_to_uf[fd].udp = f_saddr.sin_port;
+ udp_data.fd_to_uf[fd].skfd = skfd;
+ udp_data.uf_to_fd[skfd] = fd;
+
+ fset_add(udp_data.np1_flows, fd);
+
+ pthread_rwlock_unlock(&udp_data.flows_lock);
+
+ if (ipcp_udp_port_alloc(ip_addr, f_saddr.sin_port, dst, cube) < 0) {
+ pthread_rwlock_wrlock(&udp_data.flows_lock);
+
+ udp_data.fd_to_uf[fd].udp = -1;
+ udp_data.fd_to_uf[fd].skfd = -1;
+ udp_data.uf_to_fd[skfd] = -1;
+
+ pthread_rwlock_unlock(&udp_data.flows_lock);
+ close(skfd);
+ return -1;
+ }
+
+ log_dbg("Flow pending on fd %d, UDP port %d.",
+ fd, ntohs(f_saddr.sin_port));
+
+ return 0;
+}
+
+static int ipcp_udp_flow_alloc_resp(int fd,
+ int response)
+{
+ struct timespec ts = {0, FD_UPDATE_TIMEOUT * 1000};
+ struct timespec abstime;
+ int skfd = -1;
+ struct sockaddr_in f_saddr;
+ struct sockaddr_in r_saddr;
+ socklen_t len = sizeof(r_saddr);
+
+ if (response)
+ return 0;
+
+ clock_gettime(PTHREAD_COND_CLOCK, &abstime);
+
+ pthread_mutex_lock(&ipcpi.alloc_lock);
+
+ while (ipcpi.alloc_id != fd && ipcp_get_state() == IPCP_OPERATIONAL) {
+ ts_add(&abstime, &ts, &abstime);
+ pthread_cond_timedwait(&ipcpi.alloc_cond,
+ &ipcpi.alloc_lock,
+ &abstime);
+ }
+
+ if (ipcp_get_state() != IPCP_OPERATIONAL) {
+ pthread_mutex_unlock(&ipcpi.alloc_lock);
+ return -1;
+ }
+
+ ipcpi.alloc_id = -1;
+ pthread_cond_broadcast(&ipcpi.alloc_cond);
+
+ pthread_mutex_unlock(&ipcpi.alloc_lock);
+
+ pthread_rwlock_rdlock(&udp_data.flows_lock);
+
+ skfd = udp_data.fd_to_uf[fd].skfd;
+
+ pthread_rwlock_unlock(&udp_data.flows_lock);
+
+ if (getsockname(skfd, (struct sockaddr *) &f_saddr, &len) < 0) {
+ log_dbg("Socket with fd %d has no address.", skfd);
+ return -1;
+ }
+
+ if (getpeername(skfd, (struct sockaddr *) &r_saddr, &len) < 0) {
+ log_dbg("Socket with fd %d has no peer.", skfd);
+ return -1;
+ }
+
+ pthread_rwlock_rdlock(&udp_data.flows_lock);
+
+ set_fd(skfd);
+
+ fset_add(udp_data.np1_flows, fd);
+
+ pthread_rwlock_unlock(&udp_data.flows_lock);
+
+ if (ipcp_udp_port_alloc_resp(r_saddr.sin_addr.s_addr, f_saddr.sin_port,
+ r_saddr.sin_port, response) < 0) {
+ pthread_rwlock_rdlock(&udp_data.flows_lock);
+ clr_fd(skfd);
+ pthread_rwlock_unlock(&udp_data.flows_lock);
+ return -1;
+ }
+
+ log_dbg("Accepted flow, fd %d on UDP port %d.",
+ fd, ntohs(f_saddr.sin_port));
+
+ return 0;
+}
+
+static int ipcp_udp_flow_dealloc(int fd)
+{
+ int skfd = -1;
+
+ ipcp_flow_fini(fd);
+
+ pthread_rwlock_wrlock(&udp_data.flows_lock);
+
+ fset_del(udp_data.np1_flows, fd);
+
+ skfd = udp_data.fd_to_uf[fd].skfd;
+
+ udp_data.uf_to_fd[skfd] = -1;
+ udp_data.fd_to_uf[fd].udp = -1;
+ udp_data.fd_to_uf[fd].skfd = -1;
+
+ close(skfd);
+
+ pthread_rwlock_unlock(&udp_data.flows_lock);
+ pthread_rwlock_rdlock(&udp_data.flows_lock);
+
+ clr_fd(skfd);
+
+ pthread_rwlock_unlock(&udp_data.flows_lock);
+
+ flow_dealloc(fd);
+
+ log_dbg("Flow with fd %d deallocated.", fd);
+
+ return 0;
+}
+
+static struct ipcp_ops udp_ops = {
+ .ipcp_bootstrap = ipcp_udp_bootstrap,
+ .ipcp_enroll = NULL,
+ .ipcp_connect = NULL,
+ .ipcp_disconnect = NULL,
+ .ipcp_reg = ipcp_udp_reg,
+ .ipcp_unreg = ipcp_udp_unreg,
+ .ipcp_query = ipcp_udp_query,
+ .ipcp_flow_alloc = ipcp_udp_flow_alloc,
+ .ipcp_flow_alloc_resp = ipcp_udp_flow_alloc_resp,
+ .ipcp_flow_dealloc = ipcp_udp_flow_dealloc
+};
+
+int main(int argc,
+ char * argv[])
+{
+ if (ipcp_init(argc, argv, &udp_ops) < 0) {
+ ipcp_create_r(getpid(), -1);
+ exit(EXIT_FAILURE);
+ }
+
+ if (udp_data_init() < 0) {
+ log_err("Failed to init udp data.");
+ ipcp_create_r(getpid(), -1);
+ ipcp_fini();
+ exit(EXIT_FAILURE);
+ }
+
+ if (ipcp_boot() < 0) {
+ log_err("Failed to boot IPCP.");
+ ipcp_create_r(getpid(), -1);
+ udp_data_fini();
+ ipcp_fini();
+ exit(EXIT_FAILURE);
+ }
+
+ if (ipcp_create_r(getpid(), 0)) {
+ log_err("Failed to notify IRMd we are initialized.");
+ ipcp_set_state(IPCP_NULL);
+ ipcp_shutdown();
+ udp_data_fini();
+ ipcp_fini();
+ exit(EXIT_FAILURE);
+ }
+
+ ipcp_shutdown();
+
+ if (ipcp_get_state() == IPCP_SHUTDOWN) {
+ pthread_cancel(udp_data.sduloop);
+ pthread_cancel(udp_data.handler);
+ pthread_cancel(udp_data.sdu_reader);
+
+ pthread_join(udp_data.sduloop, NULL);
+ pthread_join(udp_data.handler, NULL);
+ pthread_join(udp_data.sdu_reader, NULL);
+ }
+
+ udp_data_fini();
+
+ ipcp_fini();
+
+ exit(EXIT_SUCCESS);
+}