diff --git a/autogen.sh b/autogen.sh
index 3c5e1d93..85a6978b 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -1,4 +1,12 @@
 #!/bin/sh
+#
+# Copyright (C) 2010-2017 Red Hat, Inc.  All rights reserved.
+#
+# Author: Fabio M. Di Nitto <fabbione@kronosnet.org>
+#
+# This software licensed under GPL-2.0+, LGPL-2.0+
+#
+
 # Run this to generate all the initial makefiles, etc.
 mkdir -p m4
 autoreconf -i -v && echo Now run ./configure and make
diff --git a/build-aux/update-copyright.sh b/build-aux/update-copyright.sh
index 82f9c4d4..babb25a3 100755
--- a/build-aux/update-copyright.sh
+++ b/build-aux/update-copyright.sh
@@ -1,29 +1,29 @@
-#!/bin/bash
+#!/bin/sh
 #
 # Copyright (C) 2017 Red Hat, Inc.  All rights reserved.
 #
 # Author: Fabio M. Di Nitto <fabbione@kronosnet.org>
 #
 # This software licensed under GPL-2.0+, LGPL-2.0+
 #
 
 # script to update copyright dates across the tree
 
 enddate=$(date +%Y)
 
 input=$(grep -ril -e "Copyright.*Red Hat" |grep -v .swp |grep -v update-copyright)
 for i in $input; do
 	startdate=$(git log --follow "$i" | grep ^Date: | tail -n 1 | awk '{print $6}')
 	if [ "$startdate" != "$enddate" ]; then
 		sed -i -e 's#Copyright (C).*Red Hat#Copyright (C) '$startdate'-'$enddate' Red Hat#g' $i
 	else
 		sed -i -e 's#Copyright (C).*Red Hat#Copyright (C) '$startdate' Red Hat#g' $i
 	fi
 done
 
 input=$(find . -type f |grep -v ".git")
 for i in $input; do
 	if [ -z "$(grep -i "Copyright" $i)" ]; then
 		echo "WARNING: $i appears to be missing Copyright information"
 	fi
 done
diff --git a/init/kronosnetd.default b/init/kronosnetd.default
index 04de6604..63d9638c 100644
--- a/init/kronosnetd.default
+++ b/init/kronosnetd.default
@@ -1,3 +1,11 @@
 # kronosnetd startup options (see man kronosnetd.8)
+#
+# Copyright (C) 2012-2017 Red Hat, Inc.  All rights reserved.
+#
+# Author: Fabio M. Di Nitto <fabbione@kronosnet.org>
+#
+# This software licensed under GPL-2.0+, LGPL-2.0+
+#
+
 
 KNETD_OPTS=""
diff --git a/libknet/libknet_exported_syms b/libknet/libknet_exported_syms
index 9db144b9..89db4b67 100644
--- a/libknet/libknet_exported_syms
+++ b/libknet/libknet_exported_syms
@@ -1,8 +1,15 @@
 # Version and symbol export for libknet.so
+#
+# Copyright (C) 2016-2017 Red Hat, Inc.  All rights reserved.
+#
+# Author: Fabio M. Di Nitto <fabbione@kronosnet.org>
+#
+# This software licensed under GPL-2.0+, LGPL-2.0+
+#
 
 LIBKNET_0.0 {
 	global:
 		knet_*;
 	local:
 		*;
 };
diff --git a/libknet/tests/api-test-coverage b/libknet/tests/api-test-coverage
index 82f6963d..029d3ef9 100755
--- a/libknet/tests/api-test-coverage
+++ b/libknet/tests/api-test-coverage
@@ -1,79 +1,86 @@
 #!/bin/sh
+#
+# Copyright (C) 2016-2017 Red Hat, Inc.  All rights reserved.
+#
+# Author: Fabio M. Di Nitto <fabbione@kronosnet.org>
+#
+# This software licensed under GPL-2.0+, LGPL-2.0+
+#
 
 srcdir="$1"/libknet/tests
 builddir="$2"/libknet/tests
 
 headerapicalls="$(grep knet_ "$srcdir"/../libknet.h | grep -v "^ \*" | grep -v ^struct | grep -v "^[[:space:]]" | grep -v typedef | sed -e 's/(.*//g' -e 's/^const //g' -e 's/\*//g' | awk '{print $2}')"
 
 exportedapicalls="$(nm -B -D "$builddir"/../.libs/libknet.so | grep ' T ' | awk '{print $3}')"
 
 echo "Checking for exported symbols NOT available in header file"
 
 for i in $exportedapicalls; do
 	found=0
 	for x in $headerapicalls; do
 		if [ "$x" = "$i" ]; then
 			found=1
 			break;
 		fi
 	done
 	if [ "$found" = 0 ]; then
 		echo "Symbol $i not found in header file"
 		exit 1
 	fi
 done
 
 echo "Checking for symbols in header file NOT exported by binary lib"
 
 for i in $headerapicalls; do
 	found=0
 	for x in $exportedapicalls; do
 		if [ "$x" = "$i" ]; then
 			found=1
 			break;
 		fi
 	done
 	if [ "$found" = 0 ]; then
 		echo "Symbol $i not found in binary lib"
 		exit 1
 	fi
 done
 
 echo "Checking for tests with memcheck exceptions"
 
 for i in $(grep -l is_memcheck "$srcdir"/*.c | grep -v test-common); do
 	echo "WARNING: $(basename $i) - has memcheck exception enabled"
 done
 
 echo "Checking for tests with helgrind exceptions"
 
 for i in $(grep -l is_helgrind "$srcdir"/*.c | grep -v test-common); do
 	echo "WARNING: $(basename $i) has helgrind exception enabled"
 done
 
 echo "Checking for api test coverage"
 
 numapicalls=0
 found=0
 missing=0
 
 for i in $headerapicalls; do
 	numapicalls=$((numapicalls + 1))
 	if [ -f $srcdir/api_${i}.c ]; then
 		found=$((found + 1))
 	else
 		missing=$((missing + 1))
 		echo "MISSING: $i"
 	fi
 done
 
 echo "Summary"
 echo "-------"
 echo "Found   : $found"
 echo "Missing : $missing"
 echo "Total   : $numapicalls"
 which bc > /dev/null 2>&1 && {
 	coverage=$(echo "scale=3; $found / $numapicalls * 100" | bc -l)
 	echo "Coverage: $coverage%"
 }
 exit 0
diff --git a/libknet/tests/pckt_test.c b/libknet/tests/pckt_test.c
index 0dc32d99..f10d8a72 100644
--- a/libknet/tests/pckt_test.c
+++ b/libknet/tests/pckt_test.c
@@ -1,19 +1,27 @@
+/*
+ * Copyright (C) 2015-2017 Red Hat, Inc.  All rights reserved.
+ *
+ * Author: Fabio M. Di Nitto <fabbione@kronosnet.org>
+ *
+ * This software licensed under GPL-2.0+, LGPL-2.0+
+ */
+
 #include <stdio.h>
 
 #include "onwire.h"
 
 int main(void)
 {
 	printf("\nKronosnet network header size printout:\n\n");
 	printf("KNET_HEADER_ALL_SIZE: %zu\n", KNET_HEADER_ALL_SIZE);
 	printf("KNET_HEADER_SIZE: %zu\n", KNET_HEADER_SIZE);
 	printf("KNET_HEADER_PING_SIZE: %zu (%zu)\n", KNET_HEADER_PING_SIZE, sizeof(struct knet_header_payload_ping));
 	printf("KNET_HEADER_PMTUD_SIZE: %zu (%zu)\n", KNET_HEADER_PMTUD_SIZE, sizeof(struct knet_header_payload_pmtud));
 	printf("KNET_HEADER_DATA_SIZE: %zu (%zu)\n", KNET_HEADER_DATA_SIZE, sizeof(struct knet_header_payload_data));
 	printf("\n");
 	printf("KNET_HOSTINFO_ALL_SIZE: %zu\n", KNET_HOSTINFO_ALL_SIZE);
 	printf("KNET_HOSTINFO_SIZE: %zu\n", KNET_HOSTINFO_SIZE);
 	printf("KNET_HOSTINFO_LINK_STATUS_SIZE: %zu (%zu)\n", KNET_HOSTINFO_LINK_STATUS_SIZE, sizeof(struct knet_hostinfo_payload_link_status));
 
 	return 0;
 }
diff --git a/libknet/transport_common.c b/libknet/transport_common.c
index 5de9acca..2b0887a1 100644
--- a/libknet/transport_common.c
+++ b/libknet/transport_common.c
@@ -1,539 +1,547 @@
+/*
+ * Copyright (C) 2016-2017 Red Hat, Inc.  All rights reserved.
+ *
+ * Author: Fabio M. Di Nitto <fabbione@kronosnet.org>
+ *
+ * This software licensed under GPL-2.0+, LGPL-2.0+
+ */
+
 #include "config.h"
 
 #include <unistd.h>
 #include <string.h>
 #include <errno.h>
 #include <pthread.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 
 #include "libknet.h"
 #include "compat.h"
 #include "host.h"
 #include "link.h"
 #include "logging.h"
 #include "common.h"
 #include "transports.h"
 
 /*
  * reuse Jan Friesse's compat layer as wrapper to drop usage of sendmmsg
  *
  * TODO: kill those wrappers once we work on packet delivery guaranteed
  */
 
 int _recvmmsg(int sockfd, struct knet_mmsghdr *msgvec, unsigned int vlen, unsigned int flags)
 {
 	int savederrno = 0, err = 0;
 	unsigned int i;
 
 	for (i = 0; i < vlen; i++) {
 		err = recvmsg(sockfd, &msgvec[i].msg_hdr, flags);
 		savederrno = errno;
 		if (err >= 0) {
 			msgvec[i].msg_len = err;
 		} else {
 			if ((i > 0) &&
 			    ((errno == EAGAIN) || (errno == EWOULDBLOCK))) {
 				savederrno = 0;
 			}
 			break;
 		}
 	}
 
 	errno = savederrno;
 	return ((i > 0) ? (int)i : err);
 }
 
 int _sendmmsg(int sockfd, struct knet_mmsghdr *msgvec, unsigned int vlen, unsigned int flags)
 {
 	int savederrno = 0, err = 0;
 	unsigned int i;
 
 	for (i = 0; i < vlen; i++) {
 		err = sendmsg(sockfd, &msgvec[i].msg_hdr, flags);
 		savederrno = errno;
 		if (err < 0) {
 			break;
 		}
 	}
 
 	errno = savederrno;
 	return ((i > 0) ? (int)i : err);
 }
 
 int _configure_common_socket(knet_handle_t knet_h, int sock, uint64_t flags, const char *type)
 {
 	int err = 0, savederrno = 0;
 	int value;
 
 	if (_fdset_cloexec(sock)) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSPORT, "Unable to set %s CLOEXEC socket opts: %s",
 			type, strerror(savederrno));
 		goto exit_error;
 	}
 
 	if (_fdset_nonblock(sock)) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSPORT, "Unable to set %s NONBLOCK socket opts: %s",
 			type, strerror(savederrno));
 		goto exit_error;
 	}
 
 	value = KNET_RING_RCVBUFF;
 #ifdef SO_RCVBUFFORCE
 	if (setsockopt(sock, SOL_SOCKET, SO_RCVBUFFORCE, &value, sizeof(value)) < 0) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSPORT, "Unable to set %s receive buffer: %s",
 			type, strerror(savederrno));
 		goto exit_error;
 	}
 #else
 	if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) < 0) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSPORT, "Unable to set %s SO_RECVBUF: %s",
 			type, strerror(savederrno));
 		goto exit_error;
 	}
 #endif
 
 	value = KNET_RING_RCVBUFF;
 #ifdef SO_SNDBUFFORCE
 	if (setsockopt(sock, SOL_SOCKET, SO_SNDBUFFORCE, &value, sizeof(value)) < 0) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSPORT, "Unable to set %s send buffer: %s",
 			type, strerror(savederrno));
 		goto exit_error;
 	}
 #else
 	if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) < 0) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSPORT, "Unable to set %s SO_SNDBUF: %s",
 			type, strerror(savederrno));
 		goto exit_error;
 	}
 #endif
 
 #ifdef SO_PRIORITY
 	if (flags & KNET_LINK_FLAG_TRAFFICHIPRIO) {
 		value = 6; /* TC_PRIO_INTERACTIVE */
 		if (setsockopt(sock, SOL_SOCKET, SO_PRIORITY, &value, sizeof(value)) < 0) {
 			savederrno = errno;
 			err = -1;
 			log_err(knet_h, KNET_SUB_TRANSPORT, "Unable to set %s priority: %s",
 				type, strerror(savederrno));
 			goto exit_error;
 		}
 	}
 #endif
 #if defined(IP_TOS) && defined(IPTOS_LOWDELAY)
 	if (flags & KNET_LINK_FLAG_TRAFFICHIPRIO) {
 		value = IPTOS_LOWDELAY;
 		if (setsockopt(sock, IPPROTO_IP, IP_TOS, &value, sizeof(value)) < 0) {
 			savederrno = errno;
 			err = -1;
 			log_err(knet_h, KNET_SUB_TRANSPORT, "Unable to set %s priority: %s",
 				type, strerror(savederrno));
 			goto exit_error;
 		}
 	}
 #endif
 
 exit_error:
 	errno = savederrno;
 	return err;
 }
 
 int _configure_transport_socket(knet_handle_t knet_h, int sock, struct sockaddr_storage *address, uint64_t flags, const char *type)
 {
 	int err = 0, savederrno = 0;
 	int value;
 
 	if (_configure_common_socket(knet_h, sock, flags, type) < 0) {
 		savederrno = errno;
 		err = -1;
 		goto exit_error;
 	}
 
 #ifdef IP_FREEBIND
 	value = 1;
 	if (setsockopt(sock, SOL_IP, IP_FREEBIND, &value, sizeof(value)) <0) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSPORT, "Unable to set FREEBIND on %s socket: %s",
 			type, strerror(savederrno));
 		goto exit_error;
 	}
 #endif
 #ifdef IP_BINDANY /* BSD */
 	value = 1;
 	if (setsockopt(sock, IPPROTO_IP, IP_BINDANY, &value, sizeof(value)) <0) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSPORT, "Unable to set BINDANY on %s socket: %s",
 			type, strerror(savederrno));
 		goto exit_error;
 	}
 #endif
 
 	if (address->ss_family == AF_INET6) {
 		value = 1;
 		if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
 			       &value, sizeof(value)) < 0) {
 			savederrno = errno;
 			err = -1;
 			log_err(knet_h, KNET_SUB_TRANSPORT, "Unable to set %s IPv6 only: %s",
 				type, strerror(savederrno));
 			goto exit_error;
 
 		}
 #ifdef IPV6_MTU_DISCOVER
 		value = IPV6_PMTUDISC_PROBE;
 		if (setsockopt(sock, SOL_IPV6, IPV6_MTU_DISCOVER, &value, sizeof(value)) <0) {
 			savederrno = errno;
 			err = -1;
 			log_err(knet_h, KNET_SUB_TRANSPORT, "Unable to set PMTUDISC on %s socket: %s",
 				type, strerror(savederrno));
 			goto exit_error;
 		}
 #else
 		value = 1;
 		if (setsockopt(sock, IPPROTO_IPV6, IPV6_DONTFRAG, &value, sizeof(value)) <0) {
 			savederrno = errno;
 			err = -1;
 			log_err(knet_h, KNET_SUB_TRANSPORT, "Unable to set DONTFRAG on %s socket: %s",
 				type, strerror(savederrno));
 			goto exit_error;
 		}
 #endif
 	} else {
 #ifdef IP_MTU_DISCOVER
 		value = IP_PMTUDISC_PROBE;
 		if (setsockopt(sock, SOL_IP, IP_MTU_DISCOVER, &value, sizeof(value)) <0) {
 			savederrno = errno;
 			err = -1;
 			log_err(knet_h, KNET_SUB_TRANSPORT, "Unable to set PMTUDISC on %s socket: %s",
 				type, strerror(savederrno));
 			goto exit_error;
 		}
 #else
 		value = 1;
 		if (setsockopt(sock, IPPROTO_IP, IP_DONTFRAG, &value, sizeof(value)) <0) {
 			savederrno = errno;
 			err = -1;
 			log_err(knet_h, KNET_SUB_TRANSPORT, "Unable to set DONTFRAG on %s socket: %s",
 				type, strerror(savederrno));
 			goto exit_error;
 		}
 #endif
 	}
 
 	value = 1;
 	if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)) < 0) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSPORT, "Unable to set %s reuseaddr: %s",
 			type, strerror(savederrno));
 		goto exit_error;
 	}
 
 exit_error:
 	errno = savederrno;
 	return err;
 }
 
 int _init_socketpair(knet_handle_t knet_h, int *sock)
 {
 	int err = 0, savederrno = 0;
 	int i;
 
 	if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sock) != 0) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_HANDLE, "Unable to initialize socketpair: %s",
 			strerror(savederrno));
 		goto exit_fail;
 	}
 
 	for (i = 0; i < 2; i++) {
 		if (_configure_common_socket(knet_h, sock[i], 0, "local socketpair") < 0) {
 			savederrno = errno;
 			err = -1;
 			goto exit_fail;
 		}
 	}
 
 exit_fail:
 	errno = savederrno;
 	return err;
 }
 
 void _close_socketpair(knet_handle_t knet_h, int *sock)
 {
 	int i;
 
 	for (i = 0; i < 2; i++) {
 		if (sock[i]) {
 			close(sock[i]);
 			sock[i] = 0;
 		}
 	}
 }
 
 /*
  * must be called with global read lock
  *
  * return -1 on error
  * return 0 if fd is invalid
  * return 1 if fd is valid
  */
 int _is_valid_fd(knet_handle_t knet_h, int sockfd)
 {
 	int ret = 0;
 
 	if (sockfd < 0) {
 		errno = EINVAL;
 		return -1;
 	}
 
 	if (sockfd > KNET_MAX_FDS) {
 		errno = EINVAL;
 		return -1;
 	}
 
 	if (knet_h->knet_transport_fd_tracker[sockfd].transport >= KNET_MAX_TRANSPORTS) {
 		ret = 0;
 	} else {
 		ret = 1;
 	}
 
 	return ret;
 }
 
 /*
  * must be called with global write lock
  */
 
 int _set_fd_tracker(knet_handle_t knet_h, int sockfd, uint8_t transport, uint8_t data_type, void *data)
 {
 	if (sockfd < 0) {
 		errno = EINVAL;
 		return -1;
 	}
 
 	if (sockfd > KNET_MAX_FDS) {
 		errno = EINVAL;
 		return -1;
 	}
 
 	knet_h->knet_transport_fd_tracker[sockfd].transport = transport;
 	knet_h->knet_transport_fd_tracker[sockfd].data_type = data_type;
 	knet_h->knet_transport_fd_tracker[sockfd].data = data;
 
 	return 0;
 }
 
 /*
  * public api
  */
 
 int knet_handle_get_transport_list(knet_handle_t knet_h,
 				   struct knet_transport_info *transport_list,
 				   size_t *transport_list_entries)
 {
 	int err = 0, savederrno = 0;
 	int i, count;
 
 	if (!knet_h) {
 		errno = EINVAL;
 		return -1;
 	}
 
 	if (!transport_list) {
 		errno = EINVAL;
 		return -1;
 	}
 
 	if (!transport_list_entries) {
 		errno = EINVAL;
 		return -1;
 	}
 
 	savederrno = pthread_rwlock_rdlock(&knet_h->global_rwlock);
 	if (savederrno) {
 		log_err(knet_h, KNET_SUB_HANDLE, "Unable to get read lock: %s",
 			strerror(savederrno));
 		errno = savederrno;
 		return -1;
 	}
 
 	count = 0;
 
 	/*
 	 * we could potentially build this struct
 	 * at knet_handle_new init time, but
 	 * let's keep it dynamic in case at somepoint
 	 * we need to init transports dynamically
 	 * at runtime vs init time.
 	 */
 
 	for (i=0; i<KNET_MAX_TRANSPORTS; i++) {
 		if (knet_h->transport_ops[i]) {
 			transport_list[count].name = knet_h->transport_ops[i]->transport_name;
 			transport_list[count].id = knet_h->transport_ops[i]->transport_id;
 			count++;
 		}
 	}
 
 	*transport_list_entries = count;
 
 	pthread_rwlock_unlock(&knet_h->global_rwlock);
 
 	return err;
 }
 
 const char *knet_handle_get_transport_name_by_id(knet_handle_t knet_h, uint8_t transport)
 {
 	int savederrno = 0;
 	const char *name = NULL;
 
 	if (!knet_h) {
 		errno = EINVAL;
 		return name;
 	}
 
 	if (transport >= KNET_MAX_TRANSPORTS) {
 		errno = EINVAL;
 		return name;
 	}
 
 	savederrno = pthread_rwlock_rdlock(&knet_h->global_rwlock);
 	if (savederrno) {
 		log_err(knet_h, KNET_SUB_HANDLE, "Unable to get read lock: %s",
 			strerror(savederrno));
 		errno = savederrno;
 		return name;
 	}
 
 	if (knet_h->transport_ops[transport]) {
 		name = knet_h->transport_ops[transport]->transport_name;
 	} else {
 		savederrno = ENOENT;
 	}
 
 	pthread_rwlock_unlock(&knet_h->global_rwlock);
 	errno = savederrno;
 	return name;
 }
 
 uint8_t knet_handle_get_transport_id_by_name(knet_handle_t knet_h, const char *name)
 {
 	int savederrno = 0;
 	uint8_t err = KNET_MAX_TRANSPORTS;
 	int i;
 
 	if (!knet_h) {
 		errno = EINVAL;
 		return err;
 	}
 
 	if (!name) {
 		errno = EINVAL;
 		return err;
 	}
 
 	savederrno = pthread_rwlock_rdlock(&knet_h->global_rwlock);
 	if (savederrno) {
 		log_err(knet_h, KNET_SUB_HANDLE, "Unable to get read lock: %s",
 			strerror(savederrno));
 		errno = savederrno;
 		return err;
 	}
 
 	for (i=0; i<KNET_MAX_TRANSPORTS; i++) {
 		if (knet_h->transport_ops[i]) {
 			if (!strcmp(knet_h->transport_ops[i]->transport_name, name)) {
 				err = knet_h->transport_ops[i]->transport_id;
 				break;
 			}
 		}
 	}
 
 	if (err == KNET_MAX_TRANSPORTS) {
 		savederrno = EINVAL;
 	}
 
 	pthread_rwlock_unlock(&knet_h->global_rwlock);
 	errno = savederrno;
 	return err;
 }
 
 int knet_handle_set_transport_reconnect_interval(knet_handle_t knet_h, uint32_t msecs)
 {
 	int savederrno = 0;
 
 	if (!knet_h) {
 		errno = EINVAL;
 		return -1;
 	}
 
 	if (!msecs) {
 		errno = EINVAL;
 		return -1;
 	}
 
 	if (msecs < 1000) {
 		log_warn(knet_h, KNET_SUB_HANDLE, "reconnect internval below 1 sec (%u msecs) might be too aggressive", msecs);
 	}
 
 	if (msecs > 60000) {
 		log_warn(knet_h, KNET_SUB_HANDLE, "reconnect internval above 1 minute (%u msecs) could cause long delays in network convergiance", msecs);
 	}
 
 	savederrno = pthread_rwlock_wrlock(&knet_h->global_rwlock);
 	if (savederrno) {
 		log_err(knet_h, KNET_SUB_HANDLE, "Unable to get read lock: %s",
 			strerror(savederrno));
 		errno = savederrno;
 		return -1;
 	}
 
 	knet_h->reconnect_int = msecs;
 
 	pthread_rwlock_unlock(&knet_h->global_rwlock);
 	return 0;
 }
 
 int knet_handle_get_transport_reconnect_interval(knet_handle_t knet_h, uint32_t *msecs)
 {
 	int savederrno = 0;
 
 	if (!knet_h) {
 		errno = EINVAL;
 		return -1;
 	}
 
 	if (!msecs) {
 		errno = EINVAL;
 		return -1;
 	}
 
 	savederrno = pthread_rwlock_rdlock(&knet_h->global_rwlock);
 	if (savederrno) {
 		log_err(knet_h, KNET_SUB_HANDLE, "Unable to get read lock: %s",
 			strerror(savederrno));
 		errno = savederrno;
 		return -1;
 	}
 
 	*msecs = knet_h->reconnect_int;
 
 	pthread_rwlock_unlock(&knet_h->global_rwlock);
 	return 0;
 }
diff --git a/libknet/transport_loopback.c b/libknet/transport_loopback.c
index 56cc7cff..a26ce758 100644
--- a/libknet/transport_loopback.c
+++ b/libknet/transport_loopback.c
@@ -1,85 +1,93 @@
+/*
+ * Copyright (C) 2017 Red Hat, Inc.  All rights reserved.
+ *
+ * Author: Christine Caulfield <ccaulfie@redhat.com>
+ *
+ * This software licensed under GPL-2.0+, LGPL-2.0+
+ */
+
 #include "config.h"
 
 #include <string.h>
 #include <unistd.h>
 #include <errno.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <stdlib.h>
 #include <netinet/in.h>
 #include <netinet/ip.h>
 #include <netinet/ip_icmp.h>
 
 #include "libknet.h"
 #include "compat.h"
 #include "host.h"
 #include "link.h"
 #include "logging.h"
 #include "common.h"
 #include "transports.h"
 #include "threads_common.h"
 
 #define KNET_PMTUD_LOOPBACK_OVERHEAD 0
 
 /* This is just a file of empty calls as the actual loopback is in threads_tx.c as a special case
    when receiving a packet from the localhost */
 
 
 static int loopback_transport_link_set_config(knet_handle_t knet_h, struct knet_link *kn_link)
 {
 	return 0;
 }
 
 static int loopback_transport_link_clear_config(knet_handle_t knet_h, struct knet_link *kn_link)
 {
 	return 0;
 }
 
 static int loopback_transport_free(knet_handle_t knet_h)
 {
 	return 0;
 }
 
 static int loopback_transport_init(knet_handle_t knet_h)
 {
 	return 0;
 }
 
 static int loopback_transport_rx_sock_error(knet_handle_t knet_h, int sockfd, int recv_err, int recv_errno)
 {
 	return 0;
 }
 
 static int loopback_transport_tx_sock_error(knet_handle_t knet_h, int sockfd, int recv_err, int recv_errno)
 {
 	return 0;
 }
 
 static int loopback_transport_rx_is_data(knet_handle_t knet_h, int sockfd, struct knet_mmsghdr *msg)
 {
 	return 0;
 }
 
 static int loopback_transport_link_dyn_connect(knet_handle_t knet_h, int sockfd, struct knet_link *kn_link)
 {
 	return 0;
 }
 
 static knet_transport_ops_t loopback_transport_ops = {
 	.transport_name = "LOOPBACK",
 	.transport_id = KNET_TRANSPORT_LOOPBACK,
 	.transport_mtu_overhead = KNET_PMTUD_LOOPBACK_OVERHEAD,
 	.transport_init = loopback_transport_init,
 	.transport_free = loopback_transport_free,
 	.transport_link_set_config = loopback_transport_link_set_config,
 	.transport_link_clear_config = loopback_transport_link_clear_config,
 	.transport_link_dyn_connect = loopback_transport_link_dyn_connect,
 	.transport_rx_sock_error = loopback_transport_rx_sock_error,
 	.transport_tx_sock_error = loopback_transport_tx_sock_error,
 	.transport_rx_is_data = loopback_transport_rx_is_data,
 };
 
 knet_transport_ops_t *get_loopback_transport()
 {
 	return &loopback_transport_ops;
 }
diff --git a/libknet/transport_sctp.c b/libknet/transport_sctp.c
index 150e642a..2fed9183 100644
--- a/libknet/transport_sctp.c
+++ b/libknet/transport_sctp.c
@@ -1,1440 +1,1448 @@
+/*
+ * Copyright (C) 2016-2017 Red Hat, Inc.  All rights reserved.
+ *
+ * Author: Christine Caulfield <ccaulfie@redhat.com>
+ *
+ * This software licensed under GPL-2.0+, LGPL-2.0+
+ */
+
 #include "config.h"
 
 #include <string.h>
 #include <unistd.h>
 #include <errno.h>
 #include <pthread.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <stdlib.h>
 
 #include "compat.h"
 #include "host.h"
 #include "links.h"
 #include "logging.h"
 #include "common.h"
 #include "transports.h"
 #include "threads_common.h"
 
 #ifdef HAVE_NETINET_SCTP_H
 #include <netinet/sctp.h>
 
 /*
  * https://en.wikipedia.org/wiki/SCTP_packet_structure
  */
 #define KNET_PMTUD_SCTP_OVERHEAD_COMMON 12
 #define KNET_PMTUD_SCTP_OVERHEAD_DATA_CHUNK 16
 #define KNET_PMTUD_SCTP_OVERHEAD KNET_PMTUD_SCTP_OVERHEAD_COMMON + KNET_PMTUD_SCTP_OVERHEAD_DATA_CHUNK
 
 typedef struct sctp_handle_info {
 	struct knet_list_head listen_links_list;
 	struct knet_list_head connect_links_list;
 	int connect_epollfd;
 	int connectsockfd[2];
 	int listen_epollfd;
 	int listensockfd[2];
 	pthread_t connect_thread;
 	pthread_t listen_thread;
 } sctp_handle_info_t;
 
 /*
  * use by fd_tracker data type
  */
 #define SCTP_NO_LINK_INFO       0
 #define SCTP_LISTENER_LINK_INFO 1
 #define SCTP_ACCEPTED_LINK_INFO 2
 #define SCTP_CONNECT_LINK_INFO  3
 
 /*
  * this value is per listener
  */
 #define MAX_ACCEPTED_SOCKS 256
 
 typedef struct sctp_listen_link_info {
 	struct knet_list_head list;
 	int listen_sock;
 	int accepted_socks[MAX_ACCEPTED_SOCKS];
 	struct sockaddr_storage src_address;
 	int on_listener_epoll;
 	int on_rx_epoll;
 } sctp_listen_link_info_t;
 
 typedef struct sctp_accepted_link_info {
 	char mread_buf[KNET_DATABUFSIZE];
 	ssize_t mread_len;
 	sctp_listen_link_info_t *link_info;
 } sctp_accepted_link_info_t ;
 
 typedef struct sctp_connect_link_info {
 	struct knet_list_head list;
 	sctp_listen_link_info_t *listener;
 	struct knet_link *link;
 	struct sockaddr_storage dst_address;
 	int connect_sock;
 	int on_connected_epoll;
 	int on_rx_epoll;
 	int close_sock;
 } sctp_connect_link_info_t;
 
 /*
  * socket handling functions
  *
  * those functions do NOT perform locking. locking
  * should be handled in the right context from callers
  */
 
 /*
  * sockets are removed from rx_epoll from callers
  * see also error handling functions
  */
 static int _close_connect_socket(knet_handle_t knet_h, struct knet_link *kn_link)
 {
 	int err = 0, savederrno = 0;
 	sctp_connect_link_info_t *info = kn_link->transport_link;
 	sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP];
 	struct epoll_event ev;
 
 	if (info->on_connected_epoll) {
 		memset(&ev, 0, sizeof(struct epoll_event));
 		ev.events = EPOLLOUT;
 		ev.data.fd = info->connect_sock;
 		if (epoll_ctl(handle_info->connect_epollfd, EPOLL_CTL_DEL, info->connect_sock, &ev)) {
 			savederrno = errno;
 			err = -1;
 			log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to remove connected socket from the epoll pool: %s",
 				strerror(errno));
 			goto exit_error;
 		}
 		info->on_connected_epoll = 0;
 	}
 
 exit_error:
 	if (info->connect_sock != -1) {
 		if (_set_fd_tracker(knet_h, info->connect_sock, KNET_MAX_TRANSPORTS, SCTP_NO_LINK_INFO, NULL) < 0) {
 			savederrno = errno;
 			err = -1;
 			log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to set fd tracker: %s",
 				strerror(savederrno));
 			goto exit_error;
 		}
 		close(info->connect_sock);
 		info->connect_sock = -1;
 	}
 
 	errno = savederrno;
 	return err;
 }
 
 static int _enable_sctp_notifications(knet_handle_t knet_h, int sock, const char *type)
 {
 	int err = 0, savederrno = 0;
 	struct sctp_event_subscribe events;
 
 	memset(&events, 0, sizeof (events));
 	events.sctp_data_io_event = 1;
 	events.sctp_association_event = 1;
 	events.sctp_send_failure_event = 1;
 	events.sctp_address_event = 1;
 	events.sctp_peer_error_event = 1;
 	events.sctp_shutdown_event = 1;
 	if (setsockopt(sock, IPPROTO_SCTP, SCTP_EVENTS, &events, sizeof (events)) < 0) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to enable %s events: %s",
 			type, strerror(savederrno));
 	}
 
 	errno = savederrno;
 	return err;
 }
 
 static int _configure_sctp_socket(knet_handle_t knet_h, int sock, struct sockaddr_storage *address, uint64_t flags, const char *type)
 {
 	int err = 0, savederrno = 0;
 	int value;
 	int level;
 
 #ifdef SOL_SCTP
 	level = SOL_SCTP;
 #else
 	level = IPPROTO_SCTP;
 #endif
 
 	if (_configure_transport_socket(knet_h, sock, address, flags, type) < 0) {
 		savederrno = errno;
 		err = -1;
 		goto exit_error;
 	}
 
 	value = 1;
 	if (setsockopt(sock, level, SCTP_NODELAY, &value, sizeof(value)) < 0) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSPORT, "Unable to set sctp nodelay: %s",
 			strerror(savederrno));
 		goto exit_error;
 	}
 
 	if (_enable_sctp_notifications(knet_h, sock, type) < 0) {
 		savederrno = errno;
 		err = -1;
 	}
 
 exit_error:
 	errno = savederrno;
 	return err;
 }
 
 static int _reconnect_socket(knet_handle_t knet_h, struct knet_link *kn_link)
 {
 	int err = 0, savederrno = 0;
 	sctp_connect_link_info_t *info = kn_link->transport_link;
 	sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP];
 	struct epoll_event ev;
 
 	if (connect(info->connect_sock, (struct sockaddr *)&kn_link->dst_addr, sockaddr_len(&kn_link->dst_addr)) < 0) {
 		if ((errno != EALREADY) && (errno != EINPROGRESS) && (errno != EISCONN)) {
 			savederrno = errno;
 			err = -1;
 			log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to connect SCTP socket %d: %s",
 				info->connect_sock, strerror(savederrno));
 			goto exit_error;
 		}
 	}
 
 	if (!info->on_connected_epoll) {
 		memset(&ev, 0, sizeof(struct epoll_event));
 		ev.events = EPOLLOUT;
 		ev.data.fd = info->connect_sock;
 		if (epoll_ctl(handle_info->connect_epollfd, EPOLL_CTL_ADD, info->connect_sock, &ev)) {
 			savederrno = errno;
 			err = -1;
 			log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to add send/recv to epoll pool: %s",
 				strerror(savederrno));
 			goto exit_error;
 		}
 		info->on_connected_epoll = 1;
 	}
 
 exit_error:
 	errno = savederrno;
 	return err;
 }
 
 static int _create_connect_socket(knet_handle_t knet_h, struct knet_link *kn_link)
 {
 	int err = 0, savederrno = 0;
 	sctp_connect_link_info_t *info = kn_link->transport_link;
 	sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP];
 	struct epoll_event ev;
 	int connect_sock;
 
 	connect_sock = socket(kn_link->dst_addr.ss_family, SOCK_STREAM, IPPROTO_SCTP);
 	if (connect_sock < 0) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to create send/recv socket: %s",
 			strerror(savederrno));
 		goto exit_error;
 	}
 
 	if (_configure_sctp_socket(knet_h, connect_sock, &kn_link->dst_addr, kn_link->flags, "SCTP connect") < 0) {
 		savederrno = errno;
 		err = -1;
 		goto exit_error;
 	}
 
 	if (_set_fd_tracker(knet_h, connect_sock, KNET_TRANSPORT_SCTP, SCTP_CONNECT_LINK_INFO, info) < 0) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to set fd tracker: %s",
 			strerror(savederrno));
 		goto exit_error;
 	}
 
 	info->connect_sock = connect_sock;
 	info->close_sock = 0;
 	if (_reconnect_socket(knet_h, kn_link) < 0) {
 		savederrno = errno;
 		err = -1;
 		goto exit_error;
 	}
 
 exit_error:
 	if (err) {
 		if (info->on_connected_epoll) {
 			epoll_ctl(handle_info->connect_epollfd, EPOLL_CTL_DEL, connect_sock, &ev);
 		}
 		if (connect_sock >= 0) {
 			close(connect_sock);
 		}
 	}
 	errno = savederrno;
 	return err;
 }
 
 static int sctp_transport_tx_sock_error(knet_handle_t knet_h, int sockfd, int recv_err, int recv_errno)
 {
 	sctp_connect_link_info_t *connect_info = knet_h->knet_transport_fd_tracker[sockfd].data;
 	sctp_accepted_link_info_t *accepted_info = knet_h->knet_transport_fd_tracker[sockfd].data;
 	sctp_listen_link_info_t *listen_info;
 
 	if (recv_err < 0) {
 		switch (knet_h->knet_transport_fd_tracker[sockfd].data_type) {
 			case SCTP_CONNECT_LINK_INFO:
 				if (connect_info->link->transport_connected == 0) {
 					return -1;
 				}
 				break;
 			case SCTP_ACCEPTED_LINK_INFO:
 				listen_info = accepted_info->link_info;
 				if (listen_info->listen_sock != sockfd) {
 					if (listen_info->on_rx_epoll == 0) {
 						return -1;
 					}
 				}
 				break;
 		}
 		if (recv_errno == EAGAIN) {
 #ifdef DEBUG
 			log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Sock: %d is overloaded. Slowing TX down", sockfd);
 #endif
 			/* Don't hold onto the lock while sleeping */
 			pthread_rwlock_unlock(&knet_h->global_rwlock);
 			usleep(KNET_THREADS_TIMERES / 16);
 			pthread_rwlock_rdlock(&knet_h->global_rwlock);
 			return 1;
 		}
 		return -1;
 	}
 	return 0;
 }
 
 /*
  * socket error management functions
  *
  * both called with global read lock.
  *
  * NOTE: we need to remove the fd from the epoll as soon as possible
  *       even before we notify the respective thread to take care of it
  *       because scheduling can make it so that this thread will overload
  *       and the threads supposed to take care of the error will never
  *       be able to take action.
  *       we CANNOT handle FDs here diretly (close/reconnect/etc) due
  *       to locking context. We need to delegate that to their respective
  *       management threads within global write lock.
  *
  * this function is called from:
  * - RX thread with recv_err <= 0 directly on recvmmsg error
  * - transport_rx_is_data when msg_len == 0 (recv_err = 1)
  * - transport_rx_is_data on notification (recv_err = 2)
  *
  * basically this small abouse of recv_err is to detect notifications
  * generated by sockets created by listen().
  */
 static int sctp_transport_rx_sock_error(knet_handle_t knet_h, int sockfd, int recv_err, int recv_errno)
 {
 	struct epoll_event ev;
 	sctp_connect_link_info_t *connect_info = knet_h->knet_transport_fd_tracker[sockfd].data;
 	sctp_accepted_link_info_t *accepted_info = knet_h->knet_transport_fd_tracker[sockfd].data;
 	sctp_listen_link_info_t *listen_info;
 	sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP];
 
 	switch (knet_h->knet_transport_fd_tracker[sockfd].data_type) {
 		case SCTP_CONNECT_LINK_INFO:
 			/*
 			 * all connect link have notifications enabled
 			 * and we accept only data from notification and
 			 * generic recvmmsg errors.
 			 *
 			 * Errors generated by msg_len 0 can be ignored because
 			 * they follow a notification (double notification)
 			 */
 			if (recv_err != 1) {
 				connect_info->link->transport_connected = 0;
 				if (connect_info->on_rx_epoll) {
 					memset(&ev, 0, sizeof(struct epoll_event));
 					ev.events = EPOLLIN;
 					ev.data.fd = sockfd;
 					if (epoll_ctl(knet_h->recv_from_links_epollfd, EPOLL_CTL_DEL, sockfd, &ev)) {
 						log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to remove EOFed socket from epoll pool: %s",
 						strerror(errno));
 						return -1;
 					}
 					connect_info->on_rx_epoll = 0;
 				}
 				log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Notifying connect thread that sockfd %d received an error", sockfd);
 				if (sendto(handle_info->connectsockfd[1], &sockfd, sizeof(int), MSG_DONTWAIT | MSG_NOSIGNAL, NULL, 0) != sizeof(int)) {
 					log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to notify connect thread: %s", strerror(errno));
 				}
 			}
 			break;
 		case SCTP_ACCEPTED_LINK_INFO:
 			listen_info = accepted_info->link_info;
 			if (listen_info->listen_sock != sockfd) {
 				if (recv_err != 1) {
 					if (listen_info->on_rx_epoll) {
 						memset(&ev, 0, sizeof(struct epoll_event));
 						ev.events = EPOLLIN;
 						ev.data.fd = sockfd;
 						if (epoll_ctl(knet_h->recv_from_links_epollfd, EPOLL_CTL_DEL, sockfd, &ev)) {
 							log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to remove EOFed socket from epoll pool: %s",
 							strerror(errno));
 							return -1;
 						}
 						listen_info->on_rx_epoll = 0;
 					}
 					log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Notifying listen thread that sockfd %d received an error", sockfd);
 					if (sendto(handle_info->listensockfd[1], &sockfd, sizeof(int), MSG_DONTWAIT | MSG_NOSIGNAL, NULL, 0) != sizeof(int)) {
 						log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to notify listen thread: %s", strerror(errno));
 					}
 				}
 			} else {
 				/*
 				 * this means the listen() socket has generated
 				 * a notification. now what? :-)
 				 */
 				log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Received stray notification for listen() socket %d", sockfd);
 			}
 			break;
 		default:
 			log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Received unknown notification? %d", sockfd);
 			break;
 	}
 	/*
 	 * Under RX pressure we need to give time to IPC to pick up the message
 	 */
 
 	/* Don't hold onto the lock while sleeping */
 	pthread_rwlock_unlock(&knet_h->global_rwlock);
 	usleep(KNET_THREADS_TIMERES / 2);
 	pthread_rwlock_rdlock(&knet_h->global_rwlock);
 	return 0;
 }
 
 /*
  * NOTE: sctp_transport_rx_is_data is called with global rdlock
  *       delegate any FD error management to sctp_transport_rx_sock_error
  *       and keep this code to parsing incoming data only
  */
 static int sctp_transport_rx_is_data(knet_handle_t knet_h, int sockfd, struct knet_mmsghdr *msg)
 {
 	size_t i;
 	struct iovec *iov = msg->msg_hdr.msg_iov;
 	size_t iovlen = msg->msg_hdr.msg_iovlen;
 	struct sctp_assoc_change *sac;
 	union sctp_notification  *snp;
 	sctp_accepted_link_info_t *info = knet_h->knet_transport_fd_tracker[sockfd].data;
 
 	if (!(msg->msg_hdr.msg_flags & MSG_NOTIFICATION)) {
 		if (msg->msg_len == 0) {
 			log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "received 0 bytes len packet: %d", sockfd);
 			/*
 			 * NOTE: with event notification enabled, we receive error twice:
 			 *       1) from the event notification
 			 *       2) followed by a 0 byte msg_len
 			 *
 			 * This is generally not a problem if not for causing extra
 			 * handling for the same issue. Should we drop notifications
 			 * and keep the code generic (handle all errors via msg_len = 0)
 			 * or keep the duplication as safety measure, or drop msg_len = 0
 			 * handling (what about sockets without events enabled?)
 			 */
 			sctp_transport_rx_sock_error(knet_h, sockfd, 1, 0);
 			return 1;
 		}
 		/*
 		 * missing MSG_EOR has to be treated as a short read
 		 * from the socket and we need to fill in the mread buf
 		 * while we wait for MSG_EOR
 		 */
 		if (!(msg->msg_hdr.msg_flags & MSG_EOR)) {
 			/*
 			 * copy the incoming data into mread_buf + mread_len (incremental)
 			 * and increase mread_len
 			 */
 			memmove(info->mread_buf + info->mread_len, iov->iov_base, msg->msg_len);
 			info->mread_len = info->mread_len + msg->msg_len;
 			return 0;
 		}
 		/*
 		 * got EOR.
 		 * if mread_len is > 0 we are completing a packet from short reads
 		 * complete reassembling the packet in mread_buf, copy it back in the iov
 		 * and set the iov/msg len numbers (size) correctly
 		 */
 		if (info->mread_len) {
 			/*
 			 * add last fragment to mread_buf
 			 */
 			memmove(info->mread_buf + info->mread_len, iov->iov_base, msg->msg_len);
 			info->mread_len = info->mread_len + msg->msg_len;
 			/*
 			 * move all back into the iovec
 			 */
 			memmove(iov->iov_base, info->mread_buf, info->mread_len);
 			msg->msg_len = info->mread_len;
 			info->mread_len = 0;
 		}
 		return 2;
 	}
 
 	if (!(msg->msg_hdr.msg_flags & MSG_EOR)) {
 		return 1;
 	}
 
 	for (i=0; i< iovlen; i++) {
 		snp = iov[i].iov_base;
 
 		switch (snp->sn_header.sn_type) {
 			case SCTP_ASSOC_CHANGE:
 				log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "[event] sctp assoc change");
 				sac = &snp->sn_assoc_change;
 				if (sac->sac_state == SCTP_COMM_LOST) {
 					log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "[event] sctp assoc change: comm_lost");
 					sctp_transport_rx_sock_error(knet_h, sockfd, 2, 0);
 				}
 				break;
 			case SCTP_SHUTDOWN_EVENT:
 				log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "[event] sctp shutdown event");
 				sctp_transport_rx_sock_error(knet_h, sockfd, 2, 0);
 				break;
 			case SCTP_SEND_FAILED:
 				log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "[event] sctp send failed");
 				break;
 			case SCTP_PEER_ADDR_CHANGE:
 				log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "[event] sctp peer addr change");
 				break;
 			case SCTP_REMOTE_ERROR:
 				log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "[event] sctp remote error");
 				break;
 			default:
 				log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "[event] unknown sctp event type: %hu\n", snp->sn_header.sn_type);
 				break;
 		}
 	}
 	return 0;
 }
 
 /*
  * connect / outgoing socket management thread
  */
 
 /*
  * _handle_connected_sctp* are called with a global write lock
  * from the connect_thread
  */
 static void _handle_connected_sctp(knet_handle_t knet_h, int connect_sock)
 {
 	int err;
 	struct epoll_event ev;
 	unsigned int status, len = sizeof(status);
 	sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP];
 	sctp_connect_link_info_t *info = knet_h->knet_transport_fd_tracker[connect_sock].data;
 	struct knet_link *kn_link = info->link;
 
 	err = getsockopt(connect_sock, SOL_SOCKET, SO_ERROR, &status, &len);
 	if (err) {
 		log_err(knet_h, KNET_SUB_TRANSP_SCTP, "SCTP getsockopt() on connecting socket %d failed: %s",
 			connect_sock, strerror(errno));
 		return;
 	}
 
 	if (info->close_sock) {
 		if (_close_connect_socket(knet_h, kn_link) < 0) {
 			log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to close sock %d from _handle_connected_sctp: %s", connect_sock, strerror(errno));
 			return;
 		}
 		info->close_sock = 0;
 		if (_create_connect_socket(knet_h, kn_link) < 0) {
 			log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to recreate connecting sock! %s", strerror(errno));
 			return;
 		}
 	}
 
 	if (status) {
 		log_info(knet_h, KNET_SUB_TRANSP_SCTP, "SCTP connect on %d to %s port %s failed: %s",
 			 connect_sock, kn_link->status.dst_ipaddr, kn_link->status.dst_port,
 			 strerror(status));
 
 		/*
 		 * No need to create a new socket if connect failed,
 		 * just retry connect
 		 */
 		_reconnect_socket(knet_h, info->link);
 		return;
 	}
 
 	/*
 	 * Connected - Remove us from the connect epoll
 	 */
 	memset(&ev, 0, sizeof(struct epoll_event));
 	ev.events = EPOLLOUT;
 	ev.data.fd = connect_sock;
 	if (epoll_ctl(handle_info->connect_epollfd, EPOLL_CTL_DEL, connect_sock, &ev)) {
 		log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to remove connected socket %d from epoll pool: %s",
 			connect_sock, strerror(errno));
 	}
 	info->on_connected_epoll = 0;
 
 	kn_link->transport_connected = 1;
 	kn_link->outsock = info->connect_sock;
 
 	memset(&ev, 0, sizeof(struct epoll_event));
 	ev.events = EPOLLIN;
 	ev.data.fd = connect_sock;
 	if (epoll_ctl(knet_h->recv_from_links_epollfd, EPOLL_CTL_ADD, connect_sock, &ev)) {
 		log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to add connected socket to epoll pool: %s",
 			strerror(errno));
 	}
 	info->on_rx_epoll = 1;
 
 	log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "SCTP handler fd %d now connected to %s port %s",
 		  connect_sock,
 		  kn_link->status.dst_ipaddr, kn_link->status.dst_port);
 }
 
 static void _handle_connected_sctp_errors(knet_handle_t knet_h)
 {
 	int sockfd = -1;
 	sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP];
 	sctp_connect_link_info_t *info;
 
 	if (recv(handle_info->connectsockfd[0], &sockfd, sizeof(int), MSG_DONTWAIT | MSG_NOSIGNAL) != sizeof(int)) {
 		log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Short read on connectsockfd");
 		return;
 	}
 
 	if (_is_valid_fd(knet_h, sockfd) < 1) {
 		log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Received stray notification for connected socket fd error");
 		return;
 	}
 
 	log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Processing connected error on socket: %d", sockfd);
 
 	info = knet_h->knet_transport_fd_tracker[sockfd].data;
 
 	info->close_sock = 1;
 	info->link->transport_connected = 0;
 	_reconnect_socket(knet_h, info->link);
 }
 
 static void *_sctp_connect_thread(void *data)
 {
 	int savederrno;
 	int i, nev;
 	knet_handle_t knet_h = (knet_handle_t) data;
 	sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP];
 	struct epoll_event events[KNET_EPOLL_MAX_EVENTS];
 
 	while (!shutdown_in_progress(knet_h)) {
 		nev = epoll_wait(handle_info->connect_epollfd, events, KNET_EPOLL_MAX_EVENTS, -1);
 
 		if (nev < 0) {
 			log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "SCTP connect handler EPOLL ERROR: %s",
 				  strerror(errno));
 			continue;
 		}
 
 		/*
 		 * Sort out which FD has a connection
 		 */
 		savederrno = pthread_rwlock_wrlock(&knet_h->global_rwlock);
 		if (savederrno) {
 			log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to get write lock: %s",
 				strerror(savederrno));
 			continue;
 		}
 
 		/*
 		 * minor optimization: deduplicate events
 		 *
 		 * in some cases we can receive multiple notifcations
 		 * of the same FD having issues or need handling.
 		 * It's enough to process it once even tho it's safe
 		 * to handle them multiple times.
 		 */
 		for (i = 0; i < nev; i++) {
 			if (events[i].data.fd == handle_info->connectsockfd[0]) {
 				log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Received notification from rx_error for connected socket");
 				_handle_connected_sctp_errors(knet_h);
 			} else {
 				if (_is_valid_fd(knet_h, events[i].data.fd) == 1) {
 					_handle_connected_sctp(knet_h, events[i].data.fd);
 				} else {
 					log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Received stray notification for dead fd %d\n", events[i].data.fd);
 				}
 			}
 		}
 		pthread_rwlock_unlock(&knet_h->global_rwlock);
 		/*
 		 * this thread can generate events for itself.
 		 * we need to sleep in between loops to allow other threads
 		 * to be scheduled
 		 */
 		usleep(knet_h->reconnect_int * 1000);
 	}
 	return NULL;
 }
 
 /*
  * listen/incoming connections management thread
  */
 
 /*
  * Listener received a new connection
  * called with a write lock from main thread
  */
 static void _handle_incoming_sctp(knet_handle_t knet_h, int listen_sock)
 {
 	int err = 0, savederrno = 0;
 	int new_fd;
 	int i = -1;
 	sctp_listen_link_info_t *info = knet_h->knet_transport_fd_tracker[listen_sock].data;
 	struct epoll_event ev;
 	struct sockaddr_storage ss;
 	socklen_t sock_len = sizeof(ss);
 	char addr_str[KNET_MAX_HOST_LEN];
 	char port_str[KNET_MAX_PORT_LEN];
 	sctp_accepted_link_info_t *accept_info = NULL;
 
 	new_fd = accept(listen_sock, (struct sockaddr *)&ss, &sock_len);
 	if (new_fd < 0) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Incoming: accept error: %s", strerror(errno));
 		goto exit_error;
 	}
 
 	if (knet_addrtostr(&ss, sizeof(ss),
 			   addr_str, KNET_MAX_HOST_LEN,
 			   port_str, KNET_MAX_PORT_LEN) < 0) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Incoming: unable to gather socket info");
 		goto exit_error;
 	}
 
 	log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Incoming: received connection from: %s port: %s",
 						addr_str, port_str);
 
 	/*
 	 * Keep a track of all accepted FDs
 	 */
 	for (i=0; i<MAX_ACCEPTED_SOCKS; i++) {
 		if (info->accepted_socks[i] == -1) {
 			info->accepted_socks[i] = new_fd;
 			break;
 		}
 	}
 
 	if (i == MAX_ACCEPTED_SOCKS) {
 		errno = EBUSY;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Incoming: too many connections!");
 		goto exit_error;
 	}
 
 	if (_configure_common_socket(knet_h, new_fd, 0, "SCTP incoming") < 0) { /* Inherit flags from listener? */
 		savederrno = errno;
 		err = -1;
 		goto exit_error;
 	}
 
 	if (_enable_sctp_notifications(knet_h, new_fd, "Incoming connection") < 0) {
 		savederrno = errno;
 		err = -1;
 		goto exit_error;
 	}
 
 	accept_info = malloc(sizeof(sctp_accepted_link_info_t));
 	if (!accept_info) {
 		savederrno = errno;
 		err = -1;
 		goto exit_error;
 	}
 	memset(accept_info, 0, sizeof(sctp_accepted_link_info_t));
 
 	accept_info->link_info = info;
 
 	if (_set_fd_tracker(knet_h, new_fd, KNET_TRANSPORT_SCTP, SCTP_ACCEPTED_LINK_INFO, accept_info) < 0) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to set fd tracker: %s",
 			strerror(errno));
 		goto exit_error;
 	}
 
 	memset(&ev, 0, sizeof(struct epoll_event));
 	ev.events = EPOLLIN;
 	ev.data.fd = new_fd;
 	if (epoll_ctl(knet_h->recv_from_links_epollfd, EPOLL_CTL_ADD, new_fd, &ev)) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Incoming: unable to add accepted socket %d to epoll pool: %s",
 			new_fd, strerror(errno));
 		goto exit_error;
 	}
 	info->on_rx_epoll = 1;
 
 	log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Incoming: accepted new fd %d for %s/%s (listen fd: %d). index: %d",
 		  new_fd, addr_str, port_str, info->listen_sock, i);
 
 exit_error:
 	if (err) {
 		if ((i >= 0) || (i < MAX_ACCEPTED_SOCKS)) {
 			info->accepted_socks[i] = -1;
 		}
 		_set_fd_tracker(knet_h, new_fd, KNET_MAX_TRANSPORTS, SCTP_NO_LINK_INFO, NULL);
 		free(accept_info);
 		close(new_fd);
 	}
 	errno = savederrno;
 	return;
 }
 
 /*
  * Listen thread received a notification of a bad socket that needs closing
  * called with a write lock from main thread
  */
 static void _handle_listen_sctp_errors(knet_handle_t knet_h)
 {
 	int sockfd = -1;
 	sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP];
 	sctp_accepted_link_info_t *accept_info;
 	sctp_listen_link_info_t *info;
 	struct knet_host *host;
 	int link_idx;
 	int i;
 
 	if (recv(handle_info->listensockfd[0], &sockfd, sizeof(int), MSG_DONTWAIT | MSG_NOSIGNAL) != sizeof(int)) {
 		log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Short read on listensockfd");
 		return;
 	}
 
 	if (_is_valid_fd(knet_h, sockfd) < 1) {
 		log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Received stray notification for listen socket fd error");
 		return;
 	}
 
 	log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Processing listen error on socket: %d", sockfd);
 
 	accept_info = knet_h->knet_transport_fd_tracker[sockfd].data;
 	info = accept_info->link_info;
 
 	/*
 	 * clear all links using this accepted socket as
 	 * outbound dynamically connected socket
 	 */
 
 	for (host = knet_h->host_head; host != NULL; host = host->next) {
 		for (link_idx = 0; link_idx < KNET_MAX_LINK; link_idx++) {
 			if ((host->link[link_idx].dynamic == KNET_LINK_DYNIP) &&
 			    (host->link[link_idx].outsock == sockfd)) {
 				log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Found dynamic connection on host %d link %d (%d)",
 					  host->host_id, link_idx, sockfd);
 				host->link[link_idx].status.dynconnected = 0;
 				host->link[link_idx].transport_connected = 0;
 				host->link[link_idx].outsock = 0;
 				memset(&host->link[link_idx].dst_addr, 0, sizeof(struct sockaddr_storage));
 			}
 		}
 	}
 
 	for (i=0; i<MAX_ACCEPTED_SOCKS; i++) {
 		if (sockfd == info->accepted_socks[i]) {
 			log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Closing accepted socket %d", sockfd);
 			_set_fd_tracker(knet_h, sockfd, KNET_MAX_TRANSPORTS, SCTP_NO_LINK_INFO, NULL);
 			info->accepted_socks[i] = -1;
 			free(accept_info);
 			close(sockfd);
 		}
 	}
 }
 
 static void *_sctp_listen_thread(void *data)
 {
 	int savederrno;
 	int i, nev;
 	knet_handle_t knet_h = (knet_handle_t) data;
 	sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP];
 	struct epoll_event events[KNET_EPOLL_MAX_EVENTS];
 
 	while (!shutdown_in_progress(knet_h)) {
 		nev = epoll_wait(handle_info->listen_epollfd, events, KNET_EPOLL_MAX_EVENTS, -1);
 
 		if (nev < 0) {
 			log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "SCTP listen handler EPOLL ERROR: %s",
 				  strerror(errno));
 			continue;
 		}
 
 		savederrno = pthread_rwlock_wrlock(&knet_h->global_rwlock);
 		if (savederrno) {
 			log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to get write lock: %s",
 				strerror(savederrno));
 			continue;
 		}
 		/*
 		 * Sort out which FD has an incoming connection
 		 */
 		for (i = 0; i < nev; i++) {
 			if (events[i].data.fd == handle_info->listensockfd[0]) {
 				log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Received notification from rx_error for listener/accepted socket");
 				_handle_listen_sctp_errors(knet_h);
 			} else {
 				if (_is_valid_fd(knet_h, events[i].data.fd) == 1) {
 					_handle_incoming_sctp(knet_h, events[i].data.fd);
 				} else {
 					log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Received listen notification from invalid socket");
 				}
 			}
 
 		}
 		pthread_rwlock_unlock(&knet_h->global_rwlock);
 	}
 	return NULL;
 }
 
 /*
  * sctp_link_listener_start/stop are called in global write lock
  * context from set_config and clear_config.
  */
 static sctp_listen_link_info_t *sctp_link_listener_start(knet_handle_t knet_h, struct knet_link *kn_link)
 {
 	int err = 0, savederrno = 0;
 	int listen_sock = -1;
 	struct epoll_event ev;
 	sctp_listen_link_info_t *info = NULL;
 	sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP];
 
 	/*
 	 * Only allocate a new listener if src address is different
 	 */
 	knet_list_for_each_entry(info, &handle_info->listen_links_list, list) {
 		if (memcmp(&info->src_address, &kn_link->src_addr, sizeof(struct sockaddr_storage)) == 0) {
 			return info;
 		}
 	}
 
 	info = malloc(sizeof(sctp_listen_link_info_t));
 	if (!info) {
 		err = -1;
 		goto exit_error;
 	}
 
 	memset(info, 0, sizeof(sctp_listen_link_info_t));
 
 	memset(info->accepted_socks, -1, sizeof(info->accepted_socks));
 	memmove(&info->src_address, &kn_link->src_addr, sizeof(struct sockaddr_storage));
 
 	listen_sock = socket(kn_link->src_addr.ss_family, SOCK_STREAM, IPPROTO_SCTP);
 	if (listen_sock < 0) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to create listener socket: %s",
 			strerror(savederrno));
 		goto exit_error;
 	}
 
 	if (_configure_sctp_socket(knet_h, listen_sock, &kn_link->src_addr, kn_link->flags, "SCTP listener") < 0) {
 		savederrno = errno;
 		err = -1;
 		goto exit_error;
 	}
 
 	if (bind(listen_sock, (struct sockaddr *)&kn_link->src_addr, sockaddr_len(&kn_link->src_addr)) < 0) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to bind listener socket: %s",
 			strerror(savederrno));
 		goto exit_error;
 	}
 
 	if (listen(listen_sock, 5) < 0) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to listen on listener socket: %s",
 			strerror(savederrno));
 		goto exit_error;
 	}
 
 	if (_set_fd_tracker(knet_h, listen_sock, KNET_TRANSPORT_SCTP, SCTP_LISTENER_LINK_INFO, info) < 0) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to set fd tracker: %s",
 			strerror(savederrno));
 		goto exit_error;
 	}
 
 	memset(&ev, 0, sizeof(struct epoll_event));
 	ev.events = EPOLLIN;
 	ev.data.fd = listen_sock;
 	if (epoll_ctl(handle_info->listen_epollfd, EPOLL_CTL_ADD, listen_sock, &ev)) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to add listener to epoll pool: %s",
 			strerror(savederrno));
 		goto exit_error;
 	}
 	info->on_listener_epoll = 1;
 
 	info->listen_sock = listen_sock;
 	knet_list_add(&info->list, &handle_info->listen_links_list);
 
 	log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Listening on fd %d for %s:%s", listen_sock, kn_link->status.src_ipaddr, kn_link->status.src_port);
 
 exit_error:
 	if (err) {
 		if (info->on_listener_epoll) {
 			epoll_ctl(handle_info->listen_epollfd, EPOLL_CTL_DEL, listen_sock, &ev);
 		}
 		if (listen_sock >= 0) {
 			close(listen_sock);
 		}
 		if (info) {
 			free(info);
 			info = NULL;
 		}
 	}
 	errno = savederrno;
 	return info;
 }
 
 static int sctp_link_listener_stop(knet_handle_t knet_h, struct knet_link *kn_link)
 {
 	int err = 0, savederrno = 0;
 	int found = 0, i;
 	struct knet_host *host;
 	int link_idx;
 	sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP];
 	sctp_connect_link_info_t *this_link_info = kn_link->transport_link;
 	sctp_listen_link_info_t *info = this_link_info->listener;
 	sctp_connect_link_info_t *link_info;
 	struct epoll_event ev;
 
 	for (host = knet_h->host_head; host != NULL; host = host->next) {
 		for (link_idx = 0; link_idx < KNET_MAX_LINK; link_idx++) {
 			if (&host->link[link_idx] == kn_link)
 				continue;
 
 			link_info = host->link[link_idx].transport_link;
 			if ((link_info) &&
 			    (link_info->listener == info) &&
 			    (host->link[link_idx].status.enabled == 1)) {
 				found = 1;
 				break;
 			}
 		}
 	}
 
 	if (found) {
 		this_link_info->listener = NULL;
 		log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "SCTP listener socket %d still in use", info->listen_sock);
 		savederrno = EBUSY;
 		err = -1;
 		goto exit_error;
 	}
 
 	if (info->on_listener_epoll) {
 		memset(&ev, 0, sizeof(struct epoll_event));
 		ev.events = EPOLLIN;
 		ev.data.fd = info->listen_sock;
 		if (epoll_ctl(handle_info->listen_epollfd, EPOLL_CTL_DEL, info->listen_sock, &ev)) {
 			savederrno = errno;
 			err = -1;
 			log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to remove listener to epoll pool: %s",
 				strerror(savederrno));
 			goto exit_error;
 		}
 		info->on_listener_epoll = 0;
 	}
 
 	if (_set_fd_tracker(knet_h, info->listen_sock, KNET_MAX_TRANSPORTS, SCTP_NO_LINK_INFO, NULL) < 0) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to set fd tracker: %s",
 			strerror(savederrno));
 		goto exit_error;
 	}
 
 	close(info->listen_sock);
 
 	for (i=0; i< MAX_ACCEPTED_SOCKS; i++) {
 		if (info->accepted_socks[i] > -1) {
 			memset(&ev, 0, sizeof(struct epoll_event));
 			ev.events = EPOLLIN;
 			ev.data.fd = info->accepted_socks[i];
 			if (epoll_ctl(knet_h->recv_from_links_epollfd, EPOLL_CTL_DEL, info->accepted_socks[i], &ev)) {
 				log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to remove EOFed socket from epoll pool: %s",
 					strerror(errno));
 			}
 			info->on_rx_epoll = 0;
 			free(knet_h->knet_transport_fd_tracker[info->accepted_socks[i]].data);
 			close(info->accepted_socks[i]);
 			if (_set_fd_tracker(knet_h, info->accepted_socks[i], KNET_MAX_TRANSPORTS, SCTP_NO_LINK_INFO, NULL) < 0) {
 				savederrno = errno;
 				err = -1;
 				log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to set fd tracker: %s",
 					strerror(savederrno));
 				goto exit_error;
 			}
 			info->accepted_socks[i] = -1;
 		}
 	}
 
 	knet_list_del(&info->list);
 	free(info);
 	this_link_info->listener = NULL;
 
 exit_error:
 	errno = savederrno;
 	return err;
 }
 
 /*
  * Links config/clear. Both called with global wrlock from link_set_config/clear_config
  */
 static int sctp_transport_link_set_config(knet_handle_t knet_h, struct knet_link *kn_link)
 {
 	int savederrno = 0, err = 0;
 	sctp_connect_link_info_t *info;
 	sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP];
 
 	info = malloc(sizeof(sctp_connect_link_info_t));
 	if (!info) {
 		goto exit_error;
 	}
 
 	memset(info, 0, sizeof(sctp_connect_link_info_t));
 
 	kn_link->transport_link = info;
 	info->link = kn_link;
 
 	memmove(&info->dst_address, &kn_link->dst_addr, sizeof(struct sockaddr_storage));
 	info->on_connected_epoll = 0;
 	info->connect_sock = -1;
 
 	info->listener = sctp_link_listener_start(knet_h, kn_link);
 	if (!info->listener) {
 		savederrno = errno;
 		err = -1;
 		goto exit_error;
 	}
 
 	if (kn_link->dynamic == KNET_LINK_STATIC) {
 		if (_create_connect_socket(knet_h, kn_link) < 0) {
 			savederrno = errno;
 			err = -1;
 			goto exit_error;
 		}
 		kn_link->outsock = info->connect_sock;
 	}
 
 	knet_list_add(&info->list, &handle_info->connect_links_list);
 
 exit_error:
 	if (err) {
 		if (info) {
 			if (info->connect_sock) {
 				close(info->connect_sock);
 			}
 			if (info->listener) {
 				sctp_link_listener_stop(knet_h, kn_link);
 			}
 			kn_link->transport_link = NULL;
 			free(info);
 		}
 	}
 	errno = savederrno;
 	return err;
 }
 
 /*
  * called with global wrlock
  */
 static int sctp_transport_link_clear_config(knet_handle_t knet_h, struct knet_link *kn_link)
 {
 	int err = 0, savederrno = 0;
 	sctp_connect_link_info_t *info;
 	struct epoll_event ev;
 
 	if (!kn_link) {
 		errno = EINVAL;
 		return -1;
 	}
 
 	info = kn_link->transport_link;
 
 	if (!info) {
 		errno = EINVAL;
 		return -1;
 	}
 
 	if ((sctp_link_listener_stop(knet_h, kn_link) <0) && (errno != EBUSY)) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to remove listener trasport: %s",
 			strerror(savederrno));
 		goto exit_error;
 	}
 
 	if (info->on_rx_epoll) {
 		memset(&ev, 0, sizeof(struct epoll_event));
 		ev.events = EPOLLIN;
 		ev.data.fd = info->connect_sock;
 		if (epoll_ctl(knet_h->recv_from_links_epollfd, EPOLL_CTL_DEL, info->connect_sock, &ev)) {
 			savederrno = errno;
 			err = -1;
 			log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to remove connected socket from epoll pool: %s",
 			strerror(savederrno));
 			goto exit_error;
 		}
 		info->on_rx_epoll = 0;
 	}
 
 	if (_close_connect_socket(knet_h, kn_link) < 0) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to close connected socket: %s",
 			strerror(savederrno));
 		goto exit_error;
 	}
 
 	knet_list_del(&info->list);
 
 	free(info);
 	kn_link->transport_link = NULL;
 
 exit_error:
 	errno = savederrno;
 	return err;
 }
 
 /*
  * transport_free and transport_init are
  * called only from knet_handle_new and knet_handle_free.
  * all resources (hosts/links) should have been already freed at this point
  * and they are called in a write locked context, hence they
  * don't need their own locking.
  */
 
 static int sctp_transport_free(knet_handle_t knet_h)
 {
 	sctp_handle_info_t *handle_info;
 	void *thread_status;
 	struct epoll_event ev;
 
 	if (!knet_h->transports[KNET_TRANSPORT_SCTP]) {
 		errno = EINVAL;
 		return -1;
 	}
 
 	handle_info = knet_h->transports[KNET_TRANSPORT_SCTP];
 
 	/*
 	 * keep it here while we debug list usage and such
 	 */
 	if (!knet_list_empty(&handle_info->listen_links_list)) {
 		log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Internal error. listen links list is not empty");
 	}
 	if (!knet_list_empty(&handle_info->connect_links_list)) {
 		log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Internal error. connect links list is not empty");
 	}
 
 	if (handle_info->listen_thread) {
 		pthread_cancel(handle_info->listen_thread);
 		pthread_join(handle_info->listen_thread, &thread_status);
 	}
 
 	if (handle_info->connect_thread) {
 		pthread_cancel(handle_info->connect_thread);
 		pthread_join(handle_info->connect_thread, &thread_status);
 	}
 
 	if (handle_info->listensockfd[0] >= 0) {
 		memset(&ev, 0, sizeof(struct epoll_event));
 		ev.events = EPOLLIN;
 		ev.data.fd = handle_info->listensockfd[0];
 		epoll_ctl(handle_info->listen_epollfd, EPOLL_CTL_DEL, handle_info->listensockfd[0], &ev);
 	}
 
 	if (handle_info->connectsockfd[0] >= 0) {
 		memset(&ev, 0, sizeof(struct epoll_event));
 		ev.events = EPOLLIN;
 		ev.data.fd = handle_info->connectsockfd[0];
 		epoll_ctl(handle_info->connect_epollfd, EPOLL_CTL_DEL, handle_info->connectsockfd[0], &ev);
 	}
 
 	_close_socketpair(knet_h, handle_info->connectsockfd);
 	_close_socketpair(knet_h, handle_info->listensockfd);
 
 	if (handle_info->listen_epollfd >= 0) {
 		close(handle_info->listen_epollfd);
 	}
 
 	if (handle_info->connect_epollfd >= 0) {
 		close(handle_info->connect_epollfd);
 	}
 
 	free(handle_info);
 	knet_h->transports[KNET_TRANSPORT_SCTP] = NULL;
 
 	return 0;
 }
 
 static int sctp_transport_init(knet_handle_t knet_h)
 {
 	int err = 0, savederrno = 0;
 	sctp_handle_info_t *handle_info;
 	struct epoll_event ev;
 
 	if (knet_h->transports[KNET_TRANSPORT_SCTP]) {
 		errno = EEXIST;
 		return -1;
 	}
 
 	handle_info = malloc(sizeof(sctp_handle_info_t));
 	if (!handle_info) {
 		return -1;
 	}
 
 	memset(handle_info, 0,sizeof(sctp_handle_info_t));
 
 	knet_h->transports[KNET_TRANSPORT_SCTP] = handle_info;
 
 	knet_list_init(&handle_info->listen_links_list);
 	knet_list_init(&handle_info->connect_links_list);
 
 	handle_info->listen_epollfd = epoll_create(KNET_EPOLL_MAX_EVENTS + 1);
         if (handle_info->listen_epollfd < 0) {
                 savederrno = errno;
 		err = -1;
                 log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to create epoll listen fd: %s",
                         strerror(savederrno));
                 goto exit_fail;
         }
 
 	if (_fdset_cloexec(handle_info->listen_epollfd)) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to set CLOEXEC on listen_epollfd: %s",
 			strerror(savederrno));
 		goto exit_fail;
 	}
 
 	handle_info->connect_epollfd = epoll_create(KNET_EPOLL_MAX_EVENTS + 1);
         if (handle_info->connect_epollfd < 0) {
                 savederrno = errno;
 		err = -1;
                 log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to create epoll connect fd: %s",
                         strerror(savederrno));
                 goto exit_fail;
         }
 
 	if (_fdset_cloexec(handle_info->connect_epollfd)) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to set CLOEXEC on connect_epollfd: %s",
 			strerror(savederrno));
 		goto exit_fail;
 	}
 
 	if (_init_socketpair(knet_h, handle_info->connectsockfd) < 0) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to init connect socketpair: %s",
 			strerror(savederrno));
 		goto exit_fail;
 	}
 
 	memset(&ev, 0, sizeof(struct epoll_event));
 	ev.events = EPOLLIN;
 	ev.data.fd = handle_info->connectsockfd[0];
 	if (epoll_ctl(handle_info->connect_epollfd, EPOLL_CTL_ADD, handle_info->connectsockfd[0], &ev)) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to add connectsockfd[0] to connect epoll pool: %s",
 			strerror(savederrno));
 		goto exit_fail;
 	}
 
 	if (_init_socketpair(knet_h, handle_info->listensockfd) < 0) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to init listen socketpair: %s",
 			strerror(savederrno));
 		goto exit_fail;
 	}
 
 	memset(&ev, 0, sizeof(struct epoll_event));
 	ev.events = EPOLLIN;
 	ev.data.fd = handle_info->listensockfd[0];
 	if (epoll_ctl(handle_info->listen_epollfd, EPOLL_CTL_ADD, handle_info->listensockfd[0], &ev)) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to add listensockfd[0] to listen epoll pool: %s",
 			strerror(savederrno));
 		goto exit_fail;
 	}
 
 	/*
 	 * Start connect & listener threads
 	 */
 	savederrno = pthread_create(&handle_info->listen_thread, 0, _sctp_listen_thread, (void *) knet_h);
 	if (savederrno) {
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to start sctp listen thread: %s",
 			strerror(savederrno));
 		goto exit_fail;
 	}
 
 	savederrno = pthread_create(&handle_info->connect_thread, 0, _sctp_connect_thread, (void *) knet_h);
 	if (savederrno) {
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to start sctp connect thread: %s",
 			strerror(savederrno));
 		goto exit_fail;
 	}
 
 exit_fail:
 	if (err < 0) {
 		sctp_transport_free(knet_h);
 	}
 	errno = savederrno;
 	return err;
 }
 
 static int sctp_transport_link_dyn_connect(knet_handle_t knet_h, int sockfd, struct knet_link *kn_link)
 {
 	kn_link->outsock = sockfd;
 	kn_link->status.dynconnected = 1;
 	kn_link->transport_connected = 1;
 	return 0;
 }
 
 static knet_transport_ops_t sctp_transport_ops = {
 	.transport_name = "SCTP",
 	.transport_id = KNET_TRANSPORT_SCTP,
 	.transport_mtu_overhead = KNET_PMTUD_SCTP_OVERHEAD,
 	.transport_init = sctp_transport_init,
 	.transport_free = sctp_transport_free,
 	.transport_link_set_config = sctp_transport_link_set_config,
 	.transport_link_clear_config = sctp_transport_link_clear_config,
 	.transport_link_dyn_connect = sctp_transport_link_dyn_connect,
 	.transport_rx_sock_error = sctp_transport_rx_sock_error,
 	.transport_tx_sock_error = sctp_transport_tx_sock_error,
 	.transport_rx_is_data = sctp_transport_rx_is_data,
 };
 
 knet_transport_ops_t *get_sctp_transport()
 {
 
 	return &sctp_transport_ops;
 }
 #else // HAVE_NETINET_SCTP_H
 knet_transport_ops_t *get_sctp_transport()
 {
 	return NULL;
 }
 #endif
diff --git a/libknet/transport_udp.c b/libknet/transport_udp.c
index 2c0a81cc..d5dd7058 100644
--- a/libknet/transport_udp.c
+++ b/libknet/transport_udp.c
@@ -1,408 +1,416 @@
+/*
+ * Copyright (C) 2016-2017 Red Hat, Inc.  All rights reserved.
+ *
+ * Author: Christine Caulfield <ccaulfie@redhat.com>
+ *
+ * This software licensed under GPL-2.0+, LGPL-2.0+
+ */
+
 #include "config.h"
 
 #include <string.h>
 #include <unistd.h>
 #include <errno.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <stdlib.h>
 #include <netinet/in.h>
 #include <netinet/ip.h>
 #include <netinet/ip_icmp.h>
 #if defined (IP_RECVERR) || defined (IPV6_RECVERR)
 #include <linux/errqueue.h>
 #endif
 
 #include "libknet.h"
 #include "compat.h"
 #include "host.h"
 #include "link.h"
 #include "logging.h"
 #include "common.h"
 #include "transports.h"
 #include "threads_common.h"
 
 #define KNET_PMTUD_UDP_OVERHEAD 8
 
 typedef struct udp_handle_info {
 	struct knet_list_head links_list;
 } udp_handle_info_t;
 
 typedef struct udp_link_info {
 	struct knet_list_head list;
 	struct sockaddr_storage local_address;
 	int socket_fd;
 	int on_epoll;
 } udp_link_info_t;
 
 static int udp_transport_link_set_config(knet_handle_t knet_h, struct knet_link *kn_link)
 {
 	int err = 0, savederrno = 0;
 	int sock = -1;
 	struct epoll_event ev;
 	udp_link_info_t *info;
 	udp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_UDP];
 #if defined (IP_RECVERR) || defined (IPV6_RECVERR)
 	int value;
 #endif
 
 	/*
 	 * Only allocate a new link if the local address is different
 	 */
 	knet_list_for_each_entry(info, &handle_info->links_list, list) {
 		if (memcmp(&info->local_address, &kn_link->src_addr, sizeof(struct sockaddr_storage)) == 0) {
 			log_debug(knet_h, KNET_SUB_TRANSP_UDP, "Re-using existing UDP socket for new link");
 			kn_link->outsock = info->socket_fd;
 			kn_link->transport_link = info;
 			kn_link->transport_connected = 1;
 			return 0;
 		}
 	}
 
 	info = malloc(sizeof(udp_link_info_t));
 	if (!info) {
 		err = -1;
 		goto exit_error;
 	}
 
 	sock = socket(kn_link->src_addr.ss_family, SOCK_DGRAM, 0);
 	if (sock < 0) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_LISTENER, "Unable to create listener socket: %s",
 			strerror(savederrno));
 		goto exit_error;
 	}
 
 	if (_configure_transport_socket(knet_h, sock, &kn_link->src_addr, kn_link->flags, "UDP") < 0) {
 		savederrno = errno;
 		err = -1;
 		goto exit_error;
 	}
 
 #ifdef IP_RECVERR
 	if (kn_link->src_addr.ss_family == AF_INET) {
 		value = 1;
 		if (setsockopt(sock, SOL_IP, IP_RECVERR, &value, sizeof(value)) <0) {
 			savederrno = errno;
 			err = -1;
 			log_err(knet_h, KNET_SUB_TRANSP_UDP, "Unable to set RECVERR on socket: %s",
 				strerror(savederrno));
 			goto exit_error;
 		}
 	}
 #endif
 #ifdef IPV6_RECVERR
 	if (kn_link->src_addr.ss_family == AF_INET6) {
 		value = 1;
 		if (setsockopt(sock, SOL_IPV6, IPV6_RECVERR, &value, sizeof(value)) <0) {
 			savederrno = errno;
 			err = -1;
 			log_err(knet_h, KNET_SUB_TRANSP_UDP, "Unable to set RECVERR on socket: %s",
 				strerror(savederrno));
 			goto exit_error;
 		}
 	}
 #endif
 
 	if (bind(sock, (struct sockaddr *)&kn_link->src_addr, sockaddr_len(&kn_link->src_addr))) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSP_UDP, "Unable to bind listener socket: %s",
 			strerror(savederrno));
 		goto exit_error;
 	}
 
 	memset(&ev, 0, sizeof(struct epoll_event));
 	ev.events = EPOLLIN;
 	ev.data.fd = sock;
 
 	if (epoll_ctl(knet_h->recv_from_links_epollfd, EPOLL_CTL_ADD, sock, &ev)) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSP_UDP, "Unable to add listener to epoll pool: %s",
 			strerror(savederrno));
 		goto exit_error;
 	}
 
 	info->on_epoll = 1;
 
 	if (_set_fd_tracker(knet_h, sock, KNET_TRANSPORT_UDP, 0, info) < 0) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSP_UDP, "Unable to set fd tracker: %s",
 			strerror(savederrno));
 		goto exit_error;
 	}
 
 	memmove(&info->local_address, &kn_link->src_addr, sizeof(struct sockaddr_storage));
 	info->socket_fd = sock;
 	knet_list_add(&info->list, &handle_info->links_list);
 
 	kn_link->outsock = sock;
 	kn_link->transport_link = info;
 	kn_link->transport_connected = 1;
 
 exit_error:
 	if (err) {
 		if (info) {
 			if (info->on_epoll) {
 				epoll_ctl(knet_h->recv_from_links_epollfd, EPOLL_CTL_DEL, sock, &ev);
 			}
 			free(info);
 		}
 		if (sock >= 0) {
 			close(sock);
 		}
 	}
 	errno = savederrno;
 	return err;
 }
 
 static int udp_transport_link_clear_config(knet_handle_t knet_h, struct knet_link *kn_link)
 {
 	int err = 0, savederrno = 0;
 	int found = 0;
 	struct knet_host *host;
 	int link_idx;
 	udp_link_info_t *info = kn_link->transport_link;
 	struct epoll_event ev;
 
 	for (host = knet_h->host_head; host != NULL; host = host->next) {
 		for (link_idx = 0; link_idx < KNET_MAX_LINK; link_idx++) {
 			if (&host->link[link_idx] == kn_link)
 				continue;
 
 			if ((host->link[link_idx].transport_link == info) &&
 			    (host->link[link_idx].status.enabled == 1)) {
 				found = 1;
 				break;
 			}
 		}
 	}
 
 	if (found) {
 		log_debug(knet_h, KNET_SUB_TRANSP_UDP, "UDP socket %d still in use", info->socket_fd);
 		savederrno = EBUSY;
 		err = -1;
 		goto exit_error;
 	}
 
 	if (info->on_epoll) {
 		memset(&ev, 0, sizeof(struct epoll_event));
 		ev.events = EPOLLIN;
 		ev.data.fd = info->socket_fd;
 
 		if (epoll_ctl(knet_h->recv_from_links_epollfd, EPOLL_CTL_DEL, info->socket_fd, &ev) < 0) {
 			savederrno = errno;
 			err = -1;
 			log_err(knet_h, KNET_SUB_TRANSP_UDP, "Unable to remove UDP socket from epoll poll: %s",
 				strerror(errno));
 			goto exit_error;
 		}
 		info->on_epoll = 0;
 	}
 
 	if (_set_fd_tracker(knet_h, info->socket_fd, KNET_MAX_TRANSPORTS, 0, NULL) < 0) {
 		savederrno = errno;
 		err = -1;
 		log_err(knet_h, KNET_SUB_TRANSP_UDP, "Unable to set fd tracker: %s",
 			strerror(savederrno));
 		goto exit_error;
 	}
 
 	close(info->socket_fd);
 	knet_list_del(&info->list);
 	free(kn_link->transport_link);
 
 exit_error:
 	errno = savederrno;
 	return err;
 }
 
 static int udp_transport_free(knet_handle_t knet_h)
 {
 	udp_handle_info_t *handle_info;
 
 	if (!knet_h->transports[KNET_TRANSPORT_UDP]) {
 		errno = EINVAL;
 		return -1;
 	}
 
 	handle_info = knet_h->transports[KNET_TRANSPORT_UDP];
 
 	/*
 	 * keep it here while we debug list usage and such
 	 */
 	if (!knet_list_empty(&handle_info->links_list)) {
 		log_err(knet_h, KNET_SUB_TRANSP_UDP, "Internal error. handle list is not empty");
 		return -1;
 	}
 
 	free(handle_info);
 
 	knet_h->transports[KNET_TRANSPORT_UDP] = NULL;
 
 	return 0;
 }
 
 static int udp_transport_init(knet_handle_t knet_h)
 {
 	udp_handle_info_t *handle_info;
 
 	if (knet_h->transports[KNET_TRANSPORT_UDP]) {
 		errno = EEXIST;
 		return -1;
 	}
 
 	handle_info = malloc(sizeof(udp_handle_info_t));
 	if (!handle_info) {
 		return -1;
 	}
 
 	memset(handle_info, 0, sizeof(udp_handle_info_t));
 
 	knet_h->transports[KNET_TRANSPORT_UDP] = handle_info;
 
 	knet_list_init(&handle_info->links_list);
 
 	return 0;
 }
 
 #if defined (IP_RECVERR) || defined (IPV6_RECVERR)
 static int read_errs_from_sock(knet_handle_t knet_h, int sockfd)
 {
 	int err = 0, savederrno = 0;
 	int got_err = 0;
 	char buffer[1024];
 	struct iovec iov;
 	struct msghdr msg;
 	struct cmsghdr *cmsg;
 	struct sock_extended_err *sock_err;
 	struct icmphdr icmph;
 	struct sockaddr_storage remote;
 	struct sockaddr_storage *origin;
 	char addr_str[KNET_MAX_HOST_LEN];
 	char port_str[KNET_MAX_PORT_LEN];
 
 	iov.iov_base = &icmph;
 	iov.iov_len = sizeof(icmph);
 	msg.msg_name = (void*)&remote;
 	msg.msg_namelen = sizeof(remote);
 	msg.msg_iov = &iov;
 	msg.msg_iovlen = 1;
 	msg.msg_flags = 0;
 	msg.msg_control = buffer;
 	msg.msg_controllen = sizeof(buffer);
 
 	for (;;) {
 		err = recvmsg(sockfd, &msg, MSG_ERRQUEUE);
 		savederrno = errno;
 		if (err < 0) {
 			if (!got_err) {
 				errno = savederrno;
 				return -1;
 			} else {
 				return 0;
 			}
 		}
 		got_err = 1;
 		for (cmsg = CMSG_FIRSTHDR(&msg);cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
 			if (((cmsg->cmsg_level == SOL_IP) && (cmsg->cmsg_type == IP_RECVERR)) ||
 			    ((cmsg->cmsg_level == SOL_IPV6 && (cmsg->cmsg_type == IPV6_RECVERR)))) {
 				sock_err = (struct sock_extended_err*)(void *)CMSG_DATA(cmsg);
 				if (sock_err) {
 					switch (sock_err->ee_origin) {
 						case 0: /* no origin */
 						case 1: /* local source (EMSGSIZE) */
 							/*
 							 * those errors are way too noisy
 							 */
 							break;
 						case 2: /* ICMP */
 						case 3: /* ICMP6 */
 							origin = (struct sockaddr_storage *)(void *)SO_EE_OFFENDER(sock_err);
 							if (knet_addrtostr(origin, sizeof(origin),
 									   addr_str, KNET_MAX_HOST_LEN,
 									   port_str, KNET_MAX_PORT_LEN) < 0) {
 								log_debug(knet_h, KNET_SUB_TRANSP_UDP, "Received ICMP error from unknown source: %s", strerror(sock_err->ee_errno));
 
 							} else {
 								log_debug(knet_h, KNET_SUB_TRANSP_UDP, "Received ICMP error from %s: %s", addr_str, strerror(sock_err->ee_errno));
 							}
 							break;
 					}
 				} else {
 					log_debug(knet_h, KNET_SUB_TRANSP_UDP, "No data in MSG_ERRQUEUE");
 				}
 			}
 		}
 	}
 }
 #else
 static int read_errs_from_sock(knet_handle_t knet_h, int sockfd)
 {
 	return 0;
 }
 #endif
 
 static int udp_transport_rx_sock_error(knet_handle_t knet_h, int sockfd, int recv_err, int recv_errno)
 {
 	if (recv_errno == EAGAIN) {
 		read_errs_from_sock(knet_h, sockfd);
 	}
 	return 0;
 }
 
 static int udp_transport_tx_sock_error(knet_handle_t knet_h, int sockfd, int recv_err, int recv_errno)
 {
 	if (recv_err < 0) {
 		if (recv_errno == EMSGSIZE) {
 			return 0;
 		}
 		if ((recv_errno == ENOBUFS) || (recv_errno == EAGAIN)) {
 #ifdef DEBUG
 			log_debug(knet_h, KNET_SUB_TRANSP_UDP, "Sock: %d is overloaded. Slowing TX down", sockfd);
 #endif
 			usleep(KNET_THREADS_TIMERES / 16);
 		} else {
 			read_errs_from_sock(knet_h, sockfd);
 		}
 		return 1;
 	}
 
 	return 0;
 }
 
 static int udp_transport_rx_is_data(knet_handle_t knet_h, int sockfd, struct knet_mmsghdr *msg)
 {
 	if (msg->msg_len == 0)
 		return 0;
 
 	return 2;
 }
 
 static int udp_transport_link_dyn_connect(knet_handle_t knet_h, int sockfd, struct knet_link *kn_link)
 {
 	kn_link->status.dynconnected = 1;
 	return 0;
 }
 
 static knet_transport_ops_t udp_transport_ops = {
 	.transport_name = "UDP",
 	.transport_id = KNET_TRANSPORT_UDP,
 	.transport_mtu_overhead = KNET_PMTUD_UDP_OVERHEAD,
 	.transport_init = udp_transport_init,
 	.transport_free = udp_transport_free,
 	.transport_link_set_config = udp_transport_link_set_config,
 	.transport_link_clear_config = udp_transport_link_clear_config,
 	.transport_link_dyn_connect = udp_transport_link_dyn_connect,
 	.transport_rx_sock_error = udp_transport_rx_sock_error,
 	.transport_tx_sock_error = udp_transport_tx_sock_error,
 	.transport_rx_is_data = udp_transport_rx_is_data,
 };
 
 knet_transport_ops_t *get_udp_transport()
 {
 	return &udp_transport_ops;
 }
diff --git a/libtap/api-test-coverage b/libtap/api-test-coverage
index b2c9c97f..0e544611 100755
--- a/libtap/api-test-coverage
+++ b/libtap/api-test-coverage
@@ -1,42 +1,49 @@
-#!/bin/bash
+#!/bin/sh
+#
+# Copyright (C) 2016-2017 Red Hat, Inc.  All rights reserved.
+#
+# Author: Fabio M. Di Nitto <fabbione@kronosnet.org>
+#
+# This software licensed under GPL-2.0+, LGPL-2.0+
+#
 
 srcdir="$1"/libtap
 builddir="$2"/libtap
 
 headerapicalls="$(grep tap_ "$srcdir"/libtap.h | grep -v "^ \*" | grep -v ^struct | grep -v "^[[:space:]]" | grep -v typedef | sed -e 's/(.*//g' -e 's/^const //g' -e 's/\*//g' | awk '{print $2}')"
 
 exportedapicalls="$(nm -B -D "$builddir"/.libs/libtap.so | grep ' T ' | awk '{print $3}')"
 
 echo "Checking for exported symbols NOT available in header file"
 
 for i in $exportedapicalls; do
 	found=0
 	for x in $headerapicalls; do
 		if [ "$x" = "$i" ]; then
 			found=1
 			break;
 		fi
 	done
 	if [ "$found" = 0 ]; then
 		echo "Symbol $i not found in header file"
 		exit 1
 	fi
 done
 
 echo "Checking for symbols in header file NOT exported by binary lib"
 
 for i in $headerapicalls; do
 	found=0
 	for x in $exportedapicalls; do
 		if [ "$x" = "$i" ]; then
 			found=1
 			break;
 		fi
 	done
 	if [ "$found" = 0 ]; then
 		echo "Symbol $i not found in binary lib"
 		exit 1
 	fi
 done
 
 exit 0
diff --git a/libtap/libtap_exported_syms b/libtap/libtap_exported_syms
index 77a3dbb7..a196001d 100644
--- a/libtap/libtap_exported_syms
+++ b/libtap/libtap_exported_syms
@@ -1,6 +1,15 @@
+# Version and symbol export for libtap.so
+#
+# Copyright (C) 2011-2017 Red Hat, Inc.  All rights reserved.
+#
+# Author: Fabio M. Di Nitto <fabbione@kronosnet.org>
+#
+# This software licensed under GPL-2.0+, LGPL-2.0+
+#
+
 Base {
 	global:
 		tap_*;
 	local:
 		*;
 };
diff --git a/poc-code/access-list/ipcheck.c b/poc-code/access-list/ipcheck.c
index c151e440..313092a6 100644
--- a/poc-code/access-list/ipcheck.c
+++ b/poc-code/access-list/ipcheck.c
@@ -1,210 +1,218 @@
+/*
+ * Copyright (C) 2016-2017 Red Hat, Inc.  All rights reserved.
+ *
+ * Author: Christine Caulfield <ccaulfie@redhat.com>
+ *
+ * This software licensed under GPL-2.0+, LGPL-2.0+
+ */
+
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <stdint.h>
 #include <string.h>
 #include <malloc.h>
 #include "ipcheck.h"
 
 struct ip_match_entry {
 	ipcheck_type_t type;
 	ipcheck_acceptreject_t acceptreject;
 	struct sockaddr_storage addr1; /* Actual IP address, mask top or low IP */
 	struct sockaddr_storage addr2; /* high IP address or address bitmask */
 	struct ip_match_entry *next;
 };
 
 
 /* Lists of things to match against. These are dummy structs to provide a quick list head */
 static struct ip_match_entry match_entry_head_v4;
 static struct ip_match_entry match_entry_head_v6;
 
 /*
  * IPv4 See if the address we have matches the current match entry
  *
  */
 static int ip_matches_v4(struct sockaddr_storage *checkip, struct ip_match_entry *match_entry)
 {
 	struct sockaddr_in *ip_to_check;
 	struct sockaddr_in *match1;
 	struct sockaddr_in *match2;
 
 	ip_to_check = (struct sockaddr_in *)checkip;
 	match1 = (struct sockaddr_in *)&match_entry->addr1;
 	match2 = (struct sockaddr_in *)&match_entry->addr2;
 
 	switch(match_entry->type) {
 	case IPCHECK_TYPE_ADDRESS:
 		if (ip_to_check->sin_addr.s_addr == match1->sin_addr.s_addr)
 			return 1;
 		break;
 	case IPCHECK_TYPE_MASK:
 		if ((ip_to_check->sin_addr.s_addr & match2->sin_addr.s_addr) ==
 		    match1->sin_addr.s_addr)
 			return 1;
 		break;
 	case IPCHECK_TYPE_RANGE:
 		if ((ntohl(ip_to_check->sin_addr.s_addr) >= ntohl(match1->sin_addr.s_addr)) &&
 		    (ntohl(ip_to_check->sin_addr.s_addr) <= ntohl(match2->sin_addr.s_addr)))
 			return 1;
 		break;
 
 	}
 	return 0;
 }
 
 /* Compare two IPv6 addresses */
 static int ip6addr_cmp(struct in6_addr *a, struct in6_addr *b)
 {
 	uint64_t a_high, a_low;
 	uint64_t b_high, b_low;
 
 	/* Not sure why '&' doesn't work below, so I used '+' instead which is effectively
 	   the same thing because the bottom 32bits are always zero and the value unsigned */
 	a_high = ((uint64_t)htonl(a->s6_addr32[0]) << 32) + (uint64_t)htonl(a->s6_addr32[1]);
 	a_low  = ((uint64_t)htonl(a->s6_addr32[2]) << 32) + (uint64_t)htonl(a->s6_addr32[3]);
 
 	b_high = ((uint64_t)htonl(b->s6_addr32[0]) << 32) + (uint64_t)htonl(b->s6_addr32[1]);
 	b_low  = ((uint64_t)htonl(b->s6_addr32[2]) << 32) + (uint64_t)htonl(b->s6_addr32[3]);
 
 	if (a_high > b_high)
 		return 1;
 	if (a_high < b_high)
 		return -1;
 
 	if (a_low > b_low)
 		return 1;
 	if (a_low < b_low)
 		return -1;
 
 	return 0;
 }
 
 /*
  * IPv6 See if the address we have matches the current match entry
  *
  */
 static int ip_matches_v6(struct sockaddr_storage *checkip, struct ip_match_entry *match_entry)
 {
 	struct sockaddr_in6 *ip_to_check;
 	struct sockaddr_in6 *match1;
 	struct sockaddr_in6 *match2;
 	int i;
 
 	ip_to_check = (struct sockaddr_in6 *)checkip;
 	match1 = (struct sockaddr_in6 *)&match_entry->addr1;
 	match2 = (struct sockaddr_in6 *)&match_entry->addr2;
 
 	switch(match_entry->type) {
 	case IPCHECK_TYPE_ADDRESS:
 		if (!memcmp(ip_to_check->sin6_addr.s6_addr32, match1->sin6_addr.s6_addr32, sizeof(struct in6_addr)))
 			return 1;
 		break;
 
 	case IPCHECK_TYPE_MASK:
 		/*
 		 * Note that this little loop will quit early if there is a non-match so the
 		 * comparison might look backwards compared to the IPv4 one
 		 */
 		for (i=sizeof(struct in6_addr)/4-1; i>=0; i--) {
 			if ((ip_to_check->sin6_addr.s6_addr32[i] & match2->sin6_addr.s6_addr32[i]) !=
 			    match1->sin6_addr.s6_addr32[i])
 				return 0;
 		}
 		return 1;
 	case IPCHECK_TYPE_RANGE:
 		if ((ip6addr_cmp(&ip_to_check->sin6_addr, &match1->sin6_addr) >= 0) &&
 		    (ip6addr_cmp(&ip_to_check->sin6_addr, &match2->sin6_addr) <= 0))
 			return 1;
 		break;
 	}
 	return 0;
 }
 
 
 /*
  * YOU ARE HERE
  */
 int ipcheck_validate(struct sockaddr_storage *checkip)
 {
 	struct ip_match_entry *match_entry;
 	int (*match_fn)(struct sockaddr_storage *checkip, struct ip_match_entry *match_entry);
 
 	if (checkip->ss_family == AF_INET){
 		match_entry = match_entry_head_v4.next;
 		match_fn = ip_matches_v4;
 	} else {
 		match_entry = match_entry_head_v6.next;
 		match_fn = ip_matches_v6;
 	}
 	while (match_entry) {
 		if (match_fn(checkip, match_entry)) {
 			if (match_entry->acceptreject == IPCHECK_ACCEPT)
 				return 1;
 			else
 				return 0;
 		}
 		match_entry = match_entry->next;
 	}
 	return 0; /* Default reject */
 }
 
 /*
  * Routines to manuipulate the lists
  */
 
 void ipcheck_clear(void)
 {
 	struct ip_match_entry *match_entry;
 	struct ip_match_entry *next_match_entry;
 
 	match_entry = match_entry_head_v4.next;
 	while (match_entry) {
 		next_match_entry = match_entry->next;
 		free(match_entry);
 		match_entry = next_match_entry;
 	}
 
 	match_entry = match_entry_head_v6.next;
 	while (match_entry) {
 		next_match_entry = match_entry->next;
 		free(match_entry);
 		match_entry = next_match_entry;
 	}
 }
 
 int ipcheck_addip(struct sockaddr_storage *ip1, struct sockaddr_storage *ip2,
 		  ipcheck_type_t type, ipcheck_acceptreject_t acceptreject)
 {
 	struct ip_match_entry *match_entry;
 	struct ip_match_entry *new_match_entry;
 
 	if (type == IPCHECK_TYPE_RANGE &&
 	    (ip1->ss_family != ip2->ss_family))
 		return -1;
 
 	if (ip1->ss_family == AF_INET){
 		match_entry = &match_entry_head_v4;
 	} else {
 		match_entry = &match_entry_head_v6;
 	}
 
 
 	new_match_entry = malloc(sizeof(struct ip_match_entry));
 	if (!new_match_entry)
 		return -1;
 
 	memmove(&new_match_entry->addr1, ip1, sizeof(struct sockaddr_storage));
 	memmove(&new_match_entry->addr2, ip2, sizeof(struct sockaddr_storage));
 	new_match_entry->type = type;
 	new_match_entry->acceptreject = acceptreject;
 	new_match_entry->next = NULL;
 
 	/* Find the end of the list */
 	/* is this OK, or should we use a doubly-linked list or bulk-load API call? */
 	while (match_entry->next) {
 		match_entry = match_entry->next;
 	}
 	match_entry->next = new_match_entry;
 
 	return 0;
 }
diff --git a/poc-code/access-list/ipcheck.h b/poc-code/access-list/ipcheck.h
index c7b3fee6..8341e69d 100644
--- a/poc-code/access-list/ipcheck.h
+++ b/poc-code/access-list/ipcheck.h
@@ -1,10 +1,16 @@
-
+/*
+ * Copyright (C) 2016-2017 Red Hat, Inc.  All rights reserved.
+ *
+ * Author: Christine Caulfield <ccaulfie@redhat.com>
+ *
+ * This software licensed under GPL-2.0+, LGPL-2.0+
+ */
 
 typedef enum {IPCHECK_TYPE_ADDRESS, IPCHECK_TYPE_MASK, IPCHECK_TYPE_RANGE} ipcheck_type_t;
 typedef	enum {IPCHECK_ACCEPT, IPCHECK_REJECT} ipcheck_acceptreject_t;
 
 int ipcheck_validate(struct sockaddr_storage *checkip);
 
 void ipcheck_clear(void);
 int ipcheck_addip(struct sockaddr_storage *ip1, struct sockaddr_storage *ip2,
 		  ipcheck_type_t type, ipcheck_acceptreject_t acceptreject);
diff --git a/poc-code/access-list/test_ipcheck.c b/poc-code/access-list/test_ipcheck.c
index 520e2aac..ff6e2e51 100644
--- a/poc-code/access-list/test_ipcheck.c
+++ b/poc-code/access-list/test_ipcheck.c
@@ -1,185 +1,193 @@
+/*
+ * Copyright (C) 2016-2017 Red Hat, Inc.  All rights reserved.
+ *
+ * Author: Christine Caulfield <ccaulfie@redhat.com>
+ *
+ * This software licensed under GPL-2.0+, LGPL-2.0+
+ */
+
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <netdb.h>
 #include <malloc.h>
 #include "ipcheck.h"
 
 /* This is a test program .. remember! */
 #define BUFLEN 1024
 
 static int get_ipaddress(char *buf, struct sockaddr_storage *addr)
 {
 	struct addrinfo *info;
 	struct addrinfo hints;
 	int res;
 
 	memset(&hints, 0, sizeof(hints));
 	hints.ai_family = AF_UNSPEC;
 
 	res = getaddrinfo(buf, NULL, &hints, &info);
 	if (!res) {
 		memmove(addr, info->ai_addr, info->ai_addrlen);
 		free(info);
 	}
 	return res;
 }
 
 static int read_address(char *buf, struct sockaddr_storage *addr)
 {
 	return get_ipaddress(buf, addr);
 }
 
 static int read_mask(char *buf, struct sockaddr_storage *addr, struct sockaddr_storage *addr2)
 {
 	char tmpbuf[BUFLEN];
 	char *slash;
 	int ret;
 
 	slash = strchr(buf, '/');
 	if (!slash)
 		return 1;
 
 	strncpy(tmpbuf, buf, slash-buf);
 	tmpbuf[slash-buf] = '\0';
 
 	ret = get_ipaddress(tmpbuf, addr);
         if (ret)
 		return ret;
 
 	ret = get_ipaddress(slash+1, addr2);
         if (ret)
 		return ret;
 
 	return 0;
 }
 
 static int read_range(char *buf, struct sockaddr_storage *addr1, struct sockaddr_storage *addr2)
 {
 	char tmpbuf[BUFLEN];
 	char *hyphen;
 	int ret;
 
 	hyphen = strchr(buf, '-');
 	if (!hyphen)
 		return 1;
 
 	strncpy(tmpbuf, buf, hyphen-buf);
 	tmpbuf[hyphen-buf] = '\0';
 
 	ret = get_ipaddress(tmpbuf, addr1);
         if (ret)
 		return ret;
 
 	ret = get_ipaddress(hyphen+1, addr2);
         if (ret)
 		return ret;
 
 	return 0;
 }
 
 
 static int load_file(void)
 {
 	FILE *filterfile;
 	char filebuf[BUFLEN];
 	int line = 0;
 	int ret;
 	ipcheck_type_t type;
 	ipcheck_acceptreject_t acceptreject;
 	struct sockaddr_storage addr1;
 	struct sockaddr_storage addr2;
 
 	ipcheck_clear();
 
 	filterfile = fopen("test_ipcheck.txt", "r");
 	if (!filterfile) {
 		fprintf(stderr, "Cannot open test_ipcheck.txt\n");
 		return 1;
 	}
 
 	while (fgets(filebuf, sizeof(filebuf), filterfile)) {
 		filebuf[strlen(filebuf)-1] = '\0'; /* remove trailing LF */
 		line++;
 
 		/*
 		 * First char is A (accept) or R (Reject)
 		 */
 		switch(filebuf[0] & 0x5F) {
 		case 'A':
 			acceptreject = IPCHECK_ACCEPT;
 			break;
 		case 'R':
 			acceptreject = IPCHECK_REJECT;
 			break;
 		default:
 			fprintf(stderr, "Unknown record type on line %d: %s\n", line, filebuf);
 			goto next_record;
 		}
 
 		/*
 		 * Second char is the filter type:
 		 * A Address
 		 * M Mask
 		 * R Range
 		 */
 		switch(filebuf[1] & 0x5F) {
 		case 'A':
 			type = IPCHECK_TYPE_ADDRESS;
 			ret = read_address(filebuf+2, &addr1);
 			break;
 		case 'M':
 			type = IPCHECK_TYPE_MASK;
 			ret = read_mask(filebuf+2, &addr1, &addr2);
 			break;
 		case 'R':
 			type = IPCHECK_TYPE_RANGE;
 			ret = read_range(filebuf+2, &addr1, &addr2);
 			break;
 		default:
 			fprintf(stderr, "Unknown filter type on line %d: %s\n", line, filebuf);
 			goto next_record;
 			break;
 		}
 		if (ret) {
 			fprintf(stderr, "Failed to parse address on line %d: %s\n", line, filebuf);
 		}
 		else {
 			ipcheck_addip(&addr1, &addr2, type, acceptreject);
 		}
 	next_record: {} /* empty statement to mollify the compiler */
 	}
 	fclose(filterfile);
 
 	return 0;
 }
 
 int main(int argc, char *argv[])
 {
 	struct sockaddr_storage saddr;
 	int ret;
 	int i;
 
 	if (load_file())
 		return 1;
 
 	for (i=1; i<argc; i++) {
 		ret = get_ipaddress(argv[i], &saddr);
 		if (ret) {
 			fprintf(stderr, "Cannot parse address %s\n", argv[i]);
 		}
 		else {
 			if (ipcheck_validate(&saddr)) {
 				printf("%s is VALID\n", argv[i]);
 			}
 			else {
 				printf("%s is not allowed\n", argv[i]);
 			}
 		}
 	}
 
 	return 0;
 }
diff --git a/poc-code/iov-hash/main.c b/poc-code/iov-hash/main.c
index 8f80da8a..145ab9b0 100644
--- a/poc-code/iov-hash/main.c
+++ b/poc-code/iov-hash/main.c
@@ -1,180 +1,188 @@
+/*
+ * Copyright (C) 2016-2017 Red Hat, Inc.  All rights reserved.
+ *
+ * Author: Fabio M. Di Nitto <fabbione@kronosnet.org>
+ *
+ * This software licensed under GPL-2.0+, LGPL-2.0+
+ */
+
 /* Example code to illustrate DES enccryption/decryption using NSS.
  * The example skips the details of obtaining the Key & IV to use, and
  * just uses a hardcoded Key & IV.
  * Note: IV is only needed if Cipher Blocking Chaining (CBC) mode of encryption
  *       is used
  *
  * The recommended approach is to store and transport WRAPPED (encrypted)
  * DES Keys (IVs can be in the clear). However, it is a common (and dangerous)
  * practice to use raw DES Keys. This example shows the use of a RAW key.
  */
 
 #ifdef BUILDCRYPTONSS
 #include <nss.h>
 #include <pk11pub.h>
 #include <prerror.h>
 #include <blapit.h>
 
 /* example Key & IV */
 unsigned char gKey[] = {0xe8, 0xa7, 0x7c, 0xe2, 0x05, 0x63, 0x6a, 0x31};
 unsigned char gIV[] = {0xe4, 0xbb, 0x3b, 0xd3, 0xc3, 0x71, 0x2e, 0x58};
 
 int main(int argc, char **argv)
 {
   CK_MECHANISM_TYPE  hashMech;
   PK11SlotInfo*      slot = NULL;
   PK11SymKey*        SymKey = NULL;
   SECItem            SecParam;
   PK11Context*       HashContext = NULL;
   SECItem            keyItem;
   SECStatus          rv, rv1, rv2;
   unsigned char      buf1[1024], buf2[1024];
   char		     data[1024];
   unsigned int	     i;
   unsigned int       tmp2_outlen;
 
   /* Initialize NSS
  *    * If your application code has already initialized NSS, you can skip it
  *       * here.
  *          * This code uses the simplest of the Init functions, which does not
  *             * require a NSS database to exist
  *                */
   rv = NSS_NoDB_Init(".");
   if (rv != SECSuccess)
   {
     fprintf(stderr, "NSS initialization failed (err %d)\n",
             PR_GetError());
     goto out;
   }
 
   /* choose mechanism: CKM_DES_CBC_PAD, CKM_DES3_ECB, CKM_DES3_CBC..... 
  *    * Note that some mechanisms (*_PAD) imply the padding is handled for you
  *       * by NSS. If you choose something else, then data padding is the
  *          * application's responsibility
  *             */
   hashMech = CKM_SHA_1_HMAC;
   slot = PK11_GetBestSlot(hashMech, NULL);
   /* slot = PK11_GetInternalKeySlot(); is a simpler alternative but in
  *    * theory, it *may not* return the optimal slot for the operation. For
  *       * DES ops, Internal slot is typically the best slot
  *          */
   if (slot == NULL)
   {
     fprintf(stderr, "Unable to find security device (err %d)\n",
             PR_GetError());
     goto out;
   }
 
   /* NSS passes blobs around as SECItems. These contain a pointer to
  *    * data and a length. Turn the raw key into a SECItem. */
   keyItem.type = siBuffer;
   keyItem.data = gKey;
   keyItem.len = sizeof(gKey);
 
   /* Turn the raw key into a key object. We use PK11_OriginUnwrap
  *    * to indicate the key was unwrapped - which is what should be done
  *       * normally anyway - using raw keys isn't a good idea */
   SymKey = PK11_ImportSymKey(slot, hashMech, PK11_OriginUnwrap, CKA_SIGN,
                              &keyItem, NULL);
   if (SymKey == NULL)
   {
     fprintf(stderr, "Failure to import key into NSS (err %d)\n",
             PR_GetError());
     goto out;
   }
 
   SecParam.type = siBuffer;
   SecParam.data = 0;
   SecParam.len = 0;
 
   /* sample data we'll hash */
   strcpy(data, "Hash me!");
   fprintf(stderr, "Clear Data: %s\n", data);
 
   /* ========================= START SECTION ============================= */
   /* If using the the same key and iv over and over, stuff before this     */
   /* section and after this section needs to be done only ONCE             */
 
   /* Create cipher context */
   HashContext = PK11_CreateContextBySymKey(hashMech, CKA_SIGN,
                                           SymKey, &SecParam);
 
   if (!HashContext) {
     fprintf(stderr, "no hash context today?\n");
     goto out;
   }
 
   if (PK11_DigestBegin(HashContext) != SECSuccess) {
     fprintf(stderr, "hash doesn't begin?\n");
     goto out;
   }
 
   rv1 = PK11_DigestOp(HashContext, (unsigned char *)data, strlen(data)+1);
 
   rv2 = PK11_DigestFinal(HashContext, buf2, &tmp2_outlen, SHA1_BLOCK_LENGTH);
 
   PK11_DestroyContext(HashContext, PR_TRUE);
   if (rv1 != SECSuccess || rv2 != SECSuccess)
     goto out;
 
   fprintf(stderr, "Hash Data: ");
   for (i=0; i<tmp2_outlen; i++)
     fprintf(stderr, "%02x ", buf2[i]);
   fprintf(stderr, "\n");
 
   /* =========================== END SECTION ============================= */
 
   /* ========================= START SECTION ============================= */
   /* If using the the same key and iv over and over, stuff before this     */
   /* section and after this section needs to be done only ONCE             */
 
   memset(buf1, 0, sizeof(buf1));
   memset(buf2, 0, sizeof(buf2));
 
   /* Create cipher context */
   HashContext = PK11_CreateContextBySymKey(hashMech, CKA_SIGN,
                                           SymKey, &SecParam);
 
   if (!HashContext) {
     fprintf(stderr, "no hash context today?\n");
     goto out;
   }
 
   if (PK11_DigestBegin(HashContext) != SECSuccess) {
     fprintf(stderr, "hash doesn't begin?\n");
     goto out;
   }
 
   rv1 = PK11_DigestOp(HashContext, (unsigned char *)data, 5);
   rv1 = PK11_DigestOp(HashContext, (unsigned char *)data+5, 4);
 
   rv2 = PK11_DigestFinal(HashContext, buf2, &tmp2_outlen, SHA1_BLOCK_LENGTH);
 
   PK11_DestroyContext(HashContext, PR_TRUE);
   if (rv1 != SECSuccess || rv2 != SECSuccess)
     goto out;
 
   fprintf(stderr, "Hash Data: ");
   for (i=0; i<tmp2_outlen; i++)
     fprintf(stderr, "%02x ", buf2[i]);
   fprintf(stderr, "\n");
 
   /* =========================== END SECTION ============================= */
 
  
 out:
   if (SymKey)
     PK11_FreeSymKey(SymKey);
 
  return 0;
 
 }
 #else
 #include <stdio.h>
 
 int main(void)
 {
 	printf("you need nss build for this PoC to work\n");
 	return 0;
 }
 #endif