Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F3156066
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
27 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/configure.ac b/configure.ac
index ad51ef45..4dd13e8c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,400 +1,401 @@
#
# Copyright (C) 2010-2015 Red Hat, Inc. All rights reserved.
#
# Authors: Fabio M. Di Nitto <fabbione@kronosnet.org>
# Federico Simoncelli <fsimon@kronosnet.org>
#
# This software licensed under GPL-2.0+, LGPL-2.0+
#
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
#
AC_PREREQ([2.63])
AC_INIT([kronosnet],
m4_esyscmd([build-aux/git-version-gen .tarball-version]),
[devel@lists.kronosnet.org])
AC_USE_SYSTEM_EXTENSIONS
AM_INIT_AUTOMAKE([1.11.1 dist-bzip2 dist-xz color-tests -Wno-portability subdir-objects])
LT_PREREQ([2.2.6])
LT_INIT
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_SRCDIR([kronosnetd/main.c])
AC_CONFIG_HEADERS([config.h])
AC_CANONICAL_HOST
AC_PROG_LIBTOOL
AC_LANG([C])
systemddir=${prefix}/lib/systemd/system
if test "$prefix" = "NONE"; then
prefix="/usr"
if test "$localstatedir" = "\${prefix}/var"; then
localstatedir="/var"
fi
if test "$sysconfdir" = "\${prefix}/etc"; then
sysconfdir="/etc"
fi
if test "$systemddir" = "NONE/lib/systemd/system"; then
systemddir=/lib/systemd/system
fi
if test "$libdir" = "\${exec_prefix}/lib"; then
if test -e /usr/lib64; then
libdir="/usr/lib64"
else
libdir="/usr/lib"
fi
fi
fi
# Checks for programs.
if ! ${MAKE-make} --version /cannot/make/this >/dev/null 2>&1; then
AC_MSG_ERROR(["you don't seem to have GNU make; it is required"])
fi
AC_PROG_AWK
AC_PROG_GREP
AC_PROG_SED
AC_PROG_CPP
AC_PROG_CC
AM_PROG_CC_C_O
AC_PROG_LN_S
AC_PROG_INSTALL
AC_PROG_MAKE_SET
AC_PROG_CXX
AC_PROG_RANLIB
AC_CHECK_PROGS([PUBLICAN], [publican], [:])
AC_CHECK_PROGS([PKGCONFIG], [pkg-config])
AC_ARG_ENABLE([kronosnetd],
[ --enable-kronosnetd : Kronosnetd support ],,
[ enable_kronosnetd="no" ])
AM_CONDITIONAL([BUILD_KRONOSNETD], test x$enable_kronosnetd = xyes)
AC_ARG_ENABLE([libtap],
[ --enable-libtap : libtap support ],,
[ enable_libtap="no" ])
if test "x$enable_kronosnetd" = xyes; then
enable_libtap=yes
fi
AM_CONDITIONAL([BUILD_LIBTAP], test x$enable_libtap = xyes)
AC_ARG_ENABLE([libknet-sctp],
[ --enable-libknet-sctp : libknet SCTP support ],,
[ enable_libknet_sctp="yes" ])
## local helper functions
# this function checks if CC support options passed as
# args. Global CFLAGS are ignored during this test.
cc_supports_flag() {
saveCPPFLAGS="$CPPFLAGS"
CPPFLAGS="$@"
if echo $CC | grep -q clang; then
CPPFLAGS="-Werror $CPPFLAGS"
fi
AC_MSG_CHECKING([whether $CC supports "$@"])
AC_PREPROC_IFELSE([AC_LANG_PROGRAM([])],
[RC=0; AC_MSG_RESULT([yes])],
[RC=1; AC_MSG_RESULT([no])])
CPPFLAGS="$saveCPPFLAGS"
return $RC
}
# helper macro to check libs without adding them to LIBS
check_lib_no_libs() {
lib_no_libs_arg1=$1
shift
lib_no_libs_arg2=$1
shift
lib_no_libs_args=$@
AC_CHECK_LIB([$lib_no_libs_arg1],
[$lib_no_libs_arg2],,,
[$lib_no_libs_args])
LIBS=$ac_check_lib_save_LIBS
}
# Checks for C features
AC_C_INLINE
# Checks for libraries.
AC_CHECK_LIB([pthread], [pthread_create])
AC_CHECK_LIB([m], [ceil])
AC_CHECK_LIB([rt], [clock_gettime])
PKG_CHECK_MODULES([nss],[nss])
# Checks for header files.
AC_CHECK_HEADERS([fcntl.h])
AC_CHECK_HEADERS([stdlib.h])
AC_CHECK_HEADERS([string.h])
AC_CHECK_HEADERS([strings.h])
AC_CHECK_HEADERS([sys/ioctl.h])
AC_CHECK_HEADERS([syslog.h])
AC_CHECK_HEADERS([unistd.h])
AC_CHECK_HEADERS([netinet/in.h])
AC_CHECK_HEADERS([sys/socket.h])
AC_CHECK_HEADERS([arpa/inet.h])
AC_CHECK_HEADERS([netdb.h])
AC_CHECK_HEADERS([limits.h])
AC_CHECK_HEADERS([stdint.h])
AC_CHECK_HEADERS([sys/epoll.h])
if test "x$enable_libknet_sctp" = xyes; then
AC_CHECK_HEADERS([netinet/sctp.h],, AC_MSG_ERROR(["missing required SCTP headers"]))
fi
# Checks for typedefs, structures, and compiler characteristics.
AC_C_INLINE
AC_TYPE_SIZE_T
AC_TYPE_PID_T
AC_TYPE_SSIZE_T
AC_TYPE_UINT8_T
AC_TYPE_UINT16_T
AC_TYPE_UINT32_T
AC_TYPE_UINT64_T
AC_TYPE_INT32_T
# Checks for library functions.
AC_FUNC_ALLOCA
AC_FUNC_FORK
AC_FUNC_MALLOC
AC_FUNC_REALLOC
AC_CHECK_FUNCS([memset])
AC_CHECK_FUNCS([strdup])
AC_CHECK_FUNCS([strerror])
AC_CHECK_FUNCS([dup2])
AC_CHECK_FUNCS([select])
AC_CHECK_FUNCS([socket])
AC_CHECK_FUNCS([inet_ntoa])
AC_CHECK_FUNCS([memmove])
AC_CHECK_FUNCS([strchr])
AC_CHECK_FUNCS([atexit])
AC_CHECK_FUNCS([ftruncate])
AC_CHECK_FUNCS([strrchr])
AC_CHECK_FUNCS([strstr])
AC_CHECK_FUNCS([clock_gettime])
AC_CHECK_FUNCS([strcasecmp])
AC_CHECK_FUNCS([sendmmsg])
AC_CHECK_FUNCS([recvmmsg])
AC_CHECK_FUNCS([kevent])
# if neither sys/epoll.h nor kevent are present, we should fail.
if test "x$ac_cv_header_sys_epoll_h" = xno && test "x$ac_cv_func_kevent" = xno; then
AC_MSG_ERROR([Both epoll and kevent unavailable on this OS])
fi
if test "x$ac_cv_header_sys_epoll_h" = xyes && test "x$ac_cv_func_kevent" = xyes; then
AC_MSG_ERROR([Both epoll and kevent available on this OS, please contact the maintainers to fix the code])
fi
# Check entries in specific structs
AC_CHECK_MEMBER([struct mmsghdr.msg_hdr],
[AC_DEFINE_UNQUOTED([HAVE_MMSGHDR], [1], [struct mmsghdr exists])],
[], [[#include <sys/socket.h>]])
# checks (for kronosnetd)
if test "x$enable_kronosnetd" = xyes; then
AC_CHECK_HEADERS([security/pam_appl.h],
[AC_CHECK_LIB([pam], [pam_start])],
[AC_MSG_ERROR([Unable to find LinuxPAM devel files])])
AC_CHECK_HEADERS([security/pam_misc.h],
[AC_CHECK_LIB([pam_misc], [misc_conv])],
[AC_MSG_ERROR([Unable to find LinuxPAM MISC devel files])])
PKG_CHECK_MODULES([libqb], [libqb])
AC_CHECK_LIB([qb], [qb_log_thread_priority_set],
[have_qb_log_thread_priority_set="yes"],
[have_qb_log_thread_priority_set="no"])
if test "x${have_qb_log_thread_priority_set}" = xyes; then
AC_DEFINE_UNQUOTED([HAVE_QB_LOG_THREAD_PRIORITY_SET], 1, [have qb_log_thread_priority_set])
fi
fi
# local options
AC_ARG_ENABLE([debug],
[ --enable-debug enable debug build. ],
[ default="no" ])
AC_ARG_ENABLE([publicandocs],
[ --enable-publicandocs enable docs build. ],
[ default="no" ])
AC_ARG_WITH([initdefaultdir],
[ --with-initdefaultdir : path to /etc/sysconfig/.. or /etc/default dir. ],
[ INITDEFAULTDIR="$withval" ],
[ INITDEFAULTDIR="$sysconfdir/default" ])
AC_ARG_WITH([initddir],
[ --with-initddir=DIR : path to init script directory. ],
[ INITDDIR="$withval" ],
[ INITDDIR="$sysconfdir/init.d" ])
AC_ARG_WITH([systemddir],
[ --with-systemddir=DIR : path to systemd unit files directory. ],
[ SYSTEMDDIR="$withval" ],
[ SYSTEMDDIR="$systemddir" ])
AC_ARG_WITH([syslogfacility],
[ --with-syslogfacility=FACILITY
default syslog facility. ],
[ SYSLOGFACILITY="$withval" ],
[ SYSLOGFACILITY="LOG_DAEMON" ])
AC_ARG_WITH([sysloglevel],
[ --with-sysloglevel=LEVEL
default syslog level. ],
[ SYSLOGLEVEL="$withval" ],
[ SYSLOGLEVEL="LOG_INFO" ])
AC_ARG_WITH([defaultadmgroup],
[ --with-defaultadmgroup=GROUP
define PAM group. Users part of this group will be
allowed to configure kronosnet. Others will only
receive read-only rights. ],
[ DEFAULTADMGROUP="$withval" ],
[ DEFAULTADMGROUP="kronosnetadm" ])
## random vars
LOGDIR=${localstatedir}/log/
RUNDIR=${localstatedir}/run/
DEFAULT_CONFIG_DIR=${sysconfdir}/kronosnet
## do subst
AM_CONDITIONAL([BUILD_DOCS], [test "x${enable_publicandocs}" = xyes])
AM_CONDITIONAL([DEBUG], [test "x${enable_debug}" = xyes])
AC_SUBST([DEFAULT_CONFIG_DIR])
AC_SUBST([INITDEFAULTDIR])
AC_SUBST([INITDDIR])
AC_SUBST([SYSTEMDDIR])
AC_SUBST([LOGDIR])
AC_SUBST([DEFAULTADMGROUP])
AC_DEFINE_UNQUOTED([DEFAULT_CONFIG_DIR],
["$(eval echo ${DEFAULT_CONFIG_DIR})"],
[Default config directory])
AC_DEFINE_UNQUOTED([DEFAULT_CONFIG_FILE],
["$(eval echo ${DEFAULT_CONFIG_DIR}/kronosnetd.conf)"],
[Default config file])
AC_DEFINE_UNQUOTED([LOGDIR],
["$(eval echo ${LOGDIR})"],
[Default logging directory])
AC_DEFINE_UNQUOTED([DEFAULT_LOG_FILE],
["$(eval echo ${LOGDIR}/kronosnetd.log)"],
[Default log file])
AC_DEFINE_UNQUOTED([RUNDIR],
["$(eval echo ${RUNDIR})"],
[Default run directory])
AC_DEFINE_UNQUOTED([SYSLOGFACILITY],
[$(eval echo ${SYSLOGFACILITY})],
[Default syslog facility])
AC_DEFINE_UNQUOTED([SYSLOGLEVEL],
[$(eval echo ${SYSLOGLEVEL})],
[Default syslog level])
AC_DEFINE_UNQUOTED([DEFAULTADMGROUP],
["$(eval echo ${DEFAULTADMGROUP})"],
[Default admin group])
## *FLAGS handling
ENV_CFLAGS="$CFLAGS"
ENV_CPPFLAGS="$CPPFLAGS"
ENV_LDFLAGS="$LDFLAGS"
# debug build stuff
if test "x${enable_debug}" = xyes; then
AC_DEFINE_UNQUOTED([DEBUG], [1], [Compiling Debugging code])
OPT_CFLAGS="-O0"
else
OPT_CFLAGS="-O3"
fi
# gdb flags
if test "x${GCC}" = xyes; then
GDB_FLAGS="-ggdb3"
else
GDB_FLAGS="-g"
fi
# extra warnings
EXTRA_WARNINGS=""
WARNLIST="
all
shadow
missing-prototypes
missing-declarations
strict-prototypes
declaration-after-statement
pointer-arith
write-strings
cast-align
bad-function-cast
missing-format-attribute
format=2
format-security
format-nonliteral
no-long-long
unsigned-char
gnu89-inline
no-strict-aliasing
error
address
cpp
overflow
parentheses
sequence-point
switch
uninitialized
unused-but-set-variable
unused-function
unused-result
unused-value
unused-variable
"
for j in $WARNLIST; do
if cc_supports_flag -W$j; then
EXTRA_WARNINGS="$EXTRA_WARNINGS -W$j";
fi
done
CFLAGS="$ENV_CFLAGS $lt_prog_compiler_pic $OPT_CFLAGS $GDB_FLAGS \
$EXTRA_WARNINGS $WERROR_CFLAGS"
CPPFLAGS="$ENV_CPPFLAGS"
LDFLAGS="$ENV_LDFLAGS $lt_prog_compiler_pic -Wl,--as-needed"
AC_CONFIG_FILES([
Makefile
init/Makefile
libtap/Makefile
libtap/libtap.pc
kronosnetd/Makefile
kronosnetd/kronosnetd.logrotate
libknet/Makefile
libknet/libknet.pc
libknet/tests/Makefile
docs/Makefile
poc-code/Makefile
poc-code/iov-hash/Makefile
poc-code/access-list/Makefile
+ poc-code/sctp-defrag-bug/Makefile
])
AC_OUTPUT
diff --git a/poc-code/Makefile.am b/poc-code/Makefile.am
index 4b12510e..e0986523 100644
--- a/poc-code/Makefile.am
+++ b/poc-code/Makefile.am
@@ -1,13 +1,13 @@
#
# Copyright (C) 2016 Red Hat, Inc. All rights reserved.
#
# Author: Fabio M. Di Nitto <fabbione@kronosnet.org>
#
# This software licensed under GPL-2.0+, LGPL-2.0+
#
MAINTAINERCLEANFILES = Makefile.in
include $(top_srcdir)/build-aux/check.mk
-SUBDIRS = access-list iov-hash
+SUBDIRS = access-list iov-hash sctp-defrag-bug
diff --git a/poc-code/sctp-defrag-bug/Makefile.am b/poc-code/sctp-defrag-bug/Makefile.am
new file mode 100644
index 00000000..72617ee3
--- /dev/null
+++ b/poc-code/sctp-defrag-bug/Makefile.am
@@ -0,0 +1,26 @@
+#
+# 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+
+#
+
+MAINTAINERCLEANFILES = Makefile.in
+
+include $(top_srcdir)/build-aux/check.mk
+
+AM_CPPFLAGS = -I$(top_srcdir)/libknet
+
+# override global LIBS that pulls in lots of craft we don't need here
+LIBS =
+
+noinst_PROGRAMS = client server
+
+noinst_HEADERS = common.h
+
+server_SOURCES = server.c \
+ common.c
+
+client_SOURCES = client.c \
+ common.c
diff --git a/poc-code/sctp-defrag-bug/client.c b/poc-code/sctp-defrag-bug/client.c
new file mode 100644
index 00000000..919084e8
--- /dev/null
+++ b/poc-code/sctp-defrag-bug/client.c
@@ -0,0 +1,198 @@
+#include "config.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#ifdef HAVE_NETINET_SCTP_H
+#include <netinet/sctp.h>
+#include "common.h"
+
+int main(int argc, char **argv)
+{
+ int err = 0;
+ int rv;
+ char defport[8] = "50000";
+ char *address = NULL, *port = NULL;
+
+ struct sockaddr_storage ss;
+ int sock;
+
+ int rx_epoll;
+ struct epoll_event ev;
+ struct epoll_event events[32];
+ int i, nev;
+
+ struct mmsghdr msg_in[256];
+
+ struct mmsghdr msg_out[256];
+ struct iovec iov_out[256];
+
+ int sent_msgs;
+
+ while ((rv = getopt(argc, argv, "a:p:")) != EOF) {
+ switch(rv) {
+ case 'a':
+ address = optarg;
+ break;
+ case 'p':
+ port = optarg;
+ break;
+ default:
+ fprintf(stderr, "Unknown option\n");
+ return -1;
+ break;
+ }
+ }
+
+ /*
+ * Setup RX buffers
+ */
+ memset(&msg_in, 0, sizeof(struct mmsghdr));
+ if (setup_rx_buffers(msg_in) < 0) {
+ return -1;
+ }
+
+ /*
+ * setup TX buffers
+ */
+ for (i = 0; i < 256; i++) {
+ iov_out[i].iov_base = (void *)malloc(65536);
+ if (!iov_out[i].iov_base) {
+ fprintf(stderr, "Unable to malloc RX buffers(%d): %s\n",
+ errno, strerror(errno));
+ return -1;
+ }
+ memset(iov_out[i].iov_base, 0, 65536);
+ iov_out[i].iov_len = 65536;
+ }
+
+ rx_epoll = epoll_create(32);
+ if (rx_epoll < 0) {
+ fprintf(stderr, "Unable to create rx_epoll (%d): %s\n",
+ errno, strerror(errno));
+ return -1;
+ }
+
+ /*
+ * setup SCTP client socket
+ */
+
+ if (!address) {
+ fprintf(stderr, "No server address specified (use -a <ipaddress>).\n");
+ fprintf(stderr, "Scanning the internet for SCTP servers (this might take a while).\n");
+ while (1) {
+ fprintf(stderr, "...");
+ sleep(1);
+ }
+ }
+
+ if (!port) {
+ port = defport;
+ }
+
+ if (strtoaddr(address, port, &ss, sizeof(struct sockaddr_storage)) < 0) {
+ return -1;
+ }
+
+ sock = socket(ss.ss_family, SOCK_STREAM, IPPROTO_SCTP);
+ if (sock < 0) {
+ fprintf(stderr, "unable to create socket (%d): %s\n",
+ errno, strerror(errno));
+ return -1;
+ }
+
+ if (setup_sctp_common_sock_opts(sock, &ss) < 0) {
+ fprintf(stderr, "Unable to set socket options\n");
+ goto out;
+ }
+
+ if (connect(sock, (struct sockaddr *)&ss, sizeof(struct sockaddr_storage)) < 0) {
+ if ((errno != EALREADY) && (errno != EINPROGRESS) && (errno != EISCONN)) {
+ fprintf(stderr, "Unable to connect to server: (%d): %s\n",
+ errno, strerror(errno));
+ return -1;
+ }
+ }
+
+ /*
+ * i am supposed to check SO_ERROR to see if it's connected, but this is a PoC
+ * and I am lazy
+ */
+
+ sleep(1);
+
+ memset(&ev, 0, sizeof(struct epoll_event));
+ ev.events = EPOLLIN;
+ ev.data.fd = sock;
+ if (epoll_ctl(rx_epoll, EPOLL_CTL_ADD, sock, &ev) < 0) {
+ fprintf(stderr, "Unable to add listen socket to epoll (%d): %s\n",
+ errno, strerror(errno));
+ goto out;
+ }
+
+ /*
+ * main loop
+ */
+
+ while(1) {
+ nev = epoll_wait(rx_epoll, events, 32, 0);
+ if (nev < 0) {
+ fprintf(stderr, "SCTP listen handler EPOLL ERROR (%d): %s\n",
+ errno, strerror(errno));
+ } else {
+ for (i = 0; i < nev; i++) {
+ if (events[i].data.fd == sock) {
+ get_incoming_data(events[i].data.fd, msg_in);
+ }
+ }
+ }
+
+ sleep(1);
+
+ memset(&msg_out, 0, sizeof(msg_out));
+ for (i = 0; i < 256; i++) {
+ memset(&msg_out[i].msg_hdr, 0, sizeof(struct msghdr));
+ msg_out[i].msg_hdr.msg_name = &ss;
+ msg_out[i].msg_hdr.msg_namelen = sizeof(struct sockaddr_storage);
+ msg_out[i].msg_hdr.msg_iov = &iov_out[i];
+ msg_out[i].msg_hdr.msg_iovlen = 1;
+ }
+
+ sent_msgs = sendmmsg(sock, msg_out, 256, MSG_DONTWAIT | MSG_NOSIGNAL);
+ if (sent_msgs <= 0) {
+ fprintf(stderr, "Error sending msgs (%d): %s\n",
+ errno, strerror(errno));
+ }
+ if (sent_msgs != 256) {
+ fprintf(stderr, "Unable to send all 256 messages at once (sent: %d)\n",
+ sent_msgs);
+ }
+ fprintf(stderr, "sent %d messages\n", sent_msgs);
+ }
+
+out:
+ if (sock >= 0) {
+ close(sock);
+ }
+ if (rx_epoll >= 0) {
+ close(rx_epoll);
+ }
+
+ return err;
+}
+#else
+int main(void)
+{
+ printf("SCTP unsupported in this build\n");
+ errno = EINVAL;
+ return -1;
+}
+#endif
diff --git a/poc-code/sctp-defrag-bug/common.c b/poc-code/sctp-defrag-bug/common.c
new file mode 100644
index 00000000..905f351a
--- /dev/null
+++ b/poc-code/sctp-defrag-bug/common.c
@@ -0,0 +1,312 @@
+#include "config.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#ifdef HAVE_NETINET_SCTP_H
+#include <netinet/sctp.h>
+#include "common.h"
+
+int strtoaddr(const char *host, const char *port, struct sockaddr_storage *ss, socklen_t sslen)
+{
+ int err;
+ struct addrinfo hints;
+ struct addrinfo *result = NULL;
+
+ if (!host) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!port) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!ss) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!sslen) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ memset(&hints, 0, sizeof(struct addrinfo));
+
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
+
+ err = getaddrinfo(host, port, &hints, &result);
+
+ if (!err) {
+ memmove(ss, result->ai_addr,
+ (sslen < result->ai_addrlen) ? sslen : result->ai_addrlen);
+
+ freeaddrinfo(result);
+ }
+
+ return err;
+}
+
+int _fdset_cloexec(int fd)
+{
+ int fdflags;
+
+ fdflags = fcntl(fd, F_GETFD, 0);
+ if (fdflags < 0)
+ return -1;
+
+ fdflags |= FD_CLOEXEC;
+
+ if (fcntl(fd, F_SETFD, fdflags) < 0)
+ return -1;
+
+ return 0;
+}
+
+int _fdset_nonblock(int fd)
+{
+ int fdflags;
+
+ fdflags = fcntl(fd, F_GETFL, 0);
+ if (fdflags < 0)
+ return -1;
+
+ fdflags |= O_NONBLOCK;
+
+ if (fcntl(fd, F_SETFL, fdflags) < 0)
+ return -1;
+
+ return 0;
+}
+
+int setup_sctp_common_sock_opts(int sock, struct sockaddr_storage *ss)
+{
+ struct sctp_event_subscribe events;
+ int value;
+
+ if (_fdset_cloexec(sock)) {
+ fprintf(stderr, "unable to set CLOEXEC socket opts (%d): %s\n",
+ errno, strerror(errno));
+ return -1;
+ }
+
+ if (_fdset_nonblock(sock)) {
+ fprintf(stderr, "unable to set NONBLOCK socket opts (%d): %s\n",
+ errno, strerror(errno));
+ return -1;
+ }
+
+ value = 8388608;
+ if (setsockopt(sock, SOL_SOCKET, SO_RCVBUFFORCE, &value, sizeof(value)) < 0) {
+ fprintf(stderr, "Unable to set receive buffer (%d): %s\n",
+ errno, strerror(errno));
+ return -1;
+ }
+
+ value = 8388608;
+ if (setsockopt(sock, SOL_SOCKET, SO_SNDBUFFORCE, &value, sizeof(value)) < 0) {
+ fprintf(stderr, "Unable to set send buffer (%d): %s\n",
+ errno, strerror(errno));
+ return -1;
+ }
+
+ if (ss->ss_family == AF_INET6) {
+ value = IPV6_PMTUDISC_PROBE;
+ if (setsockopt(sock, SOL_IPV6, IPV6_MTU_DISCOVER, &value, sizeof(value)) <0) {
+ fprintf(stderr, "Unable to set PMTUDISC (%d): %s\n",
+ errno, strerror(errno));
+ return -1;
+ }
+ } else {
+ value = IP_PMTUDISC_PROBE;
+ if (setsockopt(sock, SOL_IP, IP_MTU_DISCOVER, &value, sizeof(value)) <0) {
+ fprintf(stderr, "Unable to set PMTUDISC (%d): %s\n",
+ errno, strerror(errno));
+ return -1;
+ }
+ }
+
+ value = 1;
+ if (setsockopt(sock, SOL_SCTP, SCTP_NODELAY, &value, sizeof(value)) < 0) {
+ fprintf(stderr, "Unable to set SCTP_NODELAY (%d): %s\n",
+ errno, strerror(errno));
+ return -1;
+ }
+
+#if 0 /* workaround */
+ value = 1;
+ if (setsockopt(sock, SOL_SCTP, SCTP_DISABLE_FRAGMENTS, &value, sizeof(value)) < 0) {
+ fprintf(stderr, "Unable to set SCTP_DISABLE_FRAGMENTS (%d): %s\n",
+ errno, strerror(errno));
+ return -1;
+ }
+#endif
+
+ 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) {
+ fprintf(stderr, "Unable to configure SCTP notifications (%d): %s\n",
+ errno, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+int setup_sctp_server_sock_opts(int sock, struct sockaddr_storage *ss)
+{
+ int value;
+
+ if (setup_sctp_common_sock_opts(sock, ss) < 0) {
+ return -1;
+ }
+
+ value = 1;
+ if (setsockopt(sock, SOL_IP, IP_FREEBIND, &value, sizeof(value)) <0) {
+ fprintf(stderr, "Unable to set FREEBIND (%d): %s\n",
+ errno, strerror(errno));
+ return -1;
+ }
+
+ if (ss->ss_family == AF_INET6) {
+ value = 1;
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
+ &value, sizeof(value)) < 0) {
+ fprintf(stderr, "Unable to set IPv6 only (%d): %s\n",
+ errno, strerror(errno));
+ return -1;
+ }
+ }
+
+ value = 1;
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)) < 0) {
+ fprintf(stderr, "Unable to set REUSEADDR (%d): %s\n",
+ errno, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static void parse_incoming_data(struct mmsghdr msg)
+{
+ int i;
+ struct iovec *iov = msg.msg_hdr.msg_iov;
+ size_t iovlen = msg.msg_hdr.msg_iovlen;
+ union sctp_notification *snp;
+
+ if (!(msg.msg_hdr.msg_flags & MSG_NOTIFICATION)) {
+ if (msg.msg_len == 0) {
+ fprintf(stderr, "Received 0bytes packet\n");
+ exit(-1);
+ }
+ /* check pckt len here */
+ if (msg.msg_len != 65536) {
+ fprintf(stderr, "KABOOM: %d\n", msg.msg_len);
+ exit(-1);
+ }
+ return;
+ }
+
+ if (!(msg.msg_hdr.msg_flags & MSG_EOR)) {
+ fprintf(stderr, "[event] end of notifications\n");
+ return;
+ }
+
+ /* got a notification */
+ for (i=0; i< iovlen; i++) {
+ snp = iov[i].iov_base;
+
+ switch (snp->sn_header.sn_type) {
+ case SCTP_ASSOC_CHANGE:
+ fprintf(stderr, "[event] sctp assoc change\n");
+ break;
+ case SCTP_SHUTDOWN_EVENT:
+ fprintf(stderr, "[event] sctp shutdown event\n");
+ break;
+ case SCTP_SEND_FAILED:
+ fprintf(stderr, "[event] sctp send failed\n");
+ break;
+ case SCTP_PEER_ADDR_CHANGE:
+ fprintf(stderr, "[event] sctp peer addr change\n");
+ break;
+ case SCTP_REMOTE_ERROR:
+ fprintf(stderr, "[event] sctp remote error\n");
+ break;
+ default:
+ fprintf(stderr, "[event] unknown sctp event type: %hu\n", snp->sn_header.sn_type);
+ exit(-1);
+ break;
+ }
+ }
+ return;
+}
+
+void get_incoming_data(int sock, struct mmsghdr *msg)
+{
+ int i, msg_recv;
+
+ msg_recv = recvmmsg(sock, msg, 256, MSG_DONTWAIT | MSG_NOSIGNAL, NULL);
+
+ if (msg_recv <= 0) {
+ fprintf(stderr, "Error message received from recvmmsg (%d): %s\n",
+ errno, strerror(errno));
+ exit(-1);
+ }
+
+ fprintf(stderr, "Received: %d messages\n", msg_recv);
+
+ for (i = 0; i < msg_recv; i++) {
+ parse_incoming_data(msg[i]);
+ }
+}
+
+
+int setup_rx_buffers(struct mmsghdr *msg)
+{
+ int i;
+ struct sockaddr_storage addr[256];
+ struct iovec iov_in[256];
+
+ if (!msg) {
+ return -1;
+ }
+
+ /*
+ * Setup buffers
+ */
+ for (i = 0; i < 256; i++) {
+ iov_in[i].iov_base = (void *)malloc(65536);
+ if (!iov_in[i].iov_base) {
+ fprintf(stderr, "Unable to malloc RX buffers(%d): %s\n",
+ errno, strerror(errno));
+ return -1;
+ }
+ memset(iov_in[i].iov_base, 0, 65536);
+ iov_in[i].iov_len = 65536;
+ memset(&msg[i].msg_hdr, 0, sizeof(struct msghdr));
+ msg[i].msg_hdr.msg_name = &addr[i];
+ msg[i].msg_hdr.msg_namelen = sizeof(struct sockaddr_storage);
+ msg[i].msg_hdr.msg_iov = &iov_in[i];
+ msg[i].msg_hdr.msg_iovlen = 1;
+ }
+ return 0;
+}
+#endif
diff --git a/poc-code/sctp-defrag-bug/common.h b/poc-code/sctp-defrag-bug/common.h
new file mode 100644
index 00000000..fc82d8cf
--- /dev/null
+++ b/poc-code/sctp-defrag-bug/common.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2017 Red Hat, Inc. All rights reserved.
+ *
+ * Authors: Fabio M. Di Nitto <fabbione@kronosnet.org>
+ *
+ * This software licensed under GPL-2.0+, LGPL-2.0+
+ */
+
+#ifndef __COMMON_H__
+#define __COMMON_H__
+
+int strtoaddr(const char *host, const char *port, struct sockaddr_storage *ss, socklen_t sslen);
+int _fdset_cloexec(int fd);
+int _fdset_nonblock(int fd);
+int setup_sctp_common_sock_opts(int sock, struct sockaddr_storage *ss);
+int setup_sctp_server_sock_opts(int sock, struct sockaddr_storage *ss);
+void get_incoming_data(int sock, struct mmsghdr *msg);
+int setup_rx_buffers(struct mmsghdr *msg);
+
+#endif
diff --git a/poc-code/sctp-defrag-bug/server.c b/poc-code/sctp-defrag-bug/server.c
new file mode 100644
index 00000000..ac79f3d6
--- /dev/null
+++ b/poc-code/sctp-defrag-bug/server.c
@@ -0,0 +1,168 @@
+#include "config.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#ifdef HAVE_NETINET_SCTP_H
+#include <netinet/sctp.h>
+#include "common.h"
+
+int main(int argc, char **argv)
+{
+ int err = 0;
+ int rv;
+ char defaddr[8] = "0.0.0.0";
+ char defport[8] = "50000";
+ char *address = NULL, *port = NULL;
+
+ struct sockaddr_storage ss, newss;
+ int sock, newsock;
+ socklen_t sock_len = sizeof(ss);
+
+ int rx_epoll;
+ struct epoll_event ev;
+ struct epoll_event events[32];
+ int i, nev;
+
+ struct mmsghdr msg[256];
+
+ while ((rv = getopt(argc, argv, "a:p:")) != EOF) {
+ switch(rv) {
+ case 'a':
+ address = optarg;
+ break;
+ case 'p':
+ port = optarg;
+ break;
+ default:
+ fprintf(stderr, "Unknown option\n");
+ return -1;
+ break;
+ }
+ }
+
+ memset(&msg, 0, sizeof(msg));
+ if (setup_rx_buffers(msg) < 0) {
+ return -1;
+ }
+
+ rx_epoll = epoll_create(32);
+ if (rx_epoll < 0) {
+ fprintf(stderr, "Unable to create rx_epoll (%d): %s\n",
+ errno, strerror(errno));
+ return -1;
+ }
+
+ /*
+ * setup SCTP server socket
+ */
+
+ if (!address) {
+ address = defaddr;
+ }
+ if (!port) {
+ port = defport;
+ }
+
+ if (strtoaddr(address, port, &ss, sizeof(struct sockaddr_storage)) < 0) {
+ return -1;
+ }
+
+ sock = socket(ss.ss_family, SOCK_STREAM, IPPROTO_SCTP);
+ if (sock < 0) {
+ fprintf(stderr, "unable to create socket (%d): %s\n",
+ errno, strerror(errno));
+ return -1;
+ }
+
+ if (setup_sctp_server_sock_opts(sock, &ss) < 0) {
+ fprintf(stderr, "Unable to set socket options\n");
+ goto out;
+ }
+
+ if (bind(sock, (struct sockaddr *)&ss, sizeof(struct sockaddr_storage)) < 0) {
+ fprintf(stderr, "Unable to bind socket (%d): %s\n",
+ errno, strerror(errno));
+ goto out;
+ }
+
+ if (listen(sock, 5) < 0) {
+ fprintf(stderr, "Unable to listen socket (%d): %s\n",
+ errno, strerror(errno));
+ goto out;
+ }
+
+ memset(&ev, 0, sizeof(struct epoll_event));
+ ev.events = EPOLLIN;
+ ev.data.fd = sock;
+ if (epoll_ctl(rx_epoll, EPOLL_CTL_ADD, sock, &ev) < 0) {
+ fprintf(stderr, "Unable to add listen socket to epoll (%d): %s\n",
+ errno, strerror(errno));
+ goto out;
+ }
+
+ /*
+ * main loop
+ */
+
+ while(1) {
+ nev = epoll_wait(rx_epoll, events, 32, -1);
+ if (nev < 0) {
+ fprintf(stderr, "SCTP listen handler EPOLL ERROR (%d): %s\n",
+ errno, strerror(errno));
+ }
+
+ for (i = 0; i < nev; i++) {
+ if (events[i].data.fd == sock) {
+ newsock = accept(sock, (struct sockaddr *)&newss, &sock_len);
+ if (newsock < 0) {
+ fprintf(stderr, "Error accepting connection (%d): %s\n",
+ errno, strerror(errno));
+ continue;
+ }
+ if (setup_sctp_common_sock_opts(newsock, &newss) < 0) {
+ fprintf(stderr, "Error setting sockopts\n");
+ close(newsock);
+ continue;
+ }
+ memset(&ev, 0, sizeof(struct epoll_event));
+ ev.events = EPOLLIN;
+ ev.data.fd = newsock;
+ if (epoll_ctl(rx_epoll, EPOLL_CTL_ADD, newsock, &ev) < 0) {
+ fprintf(stderr, "Unable to add accept new connection (%d): %s\n",
+ errno, strerror(errno));
+ close(newsock);
+ }
+ fprintf(stderr, "Accepted socket: %d\n", newsock);
+ } else {
+ get_incoming_data(events[i].data.fd, msg);
+ }
+ }
+ }
+
+out:
+ if (sock >= 0) {
+ close(sock);
+ }
+ if (rx_epoll >= 0) {
+ close(rx_epoll);
+ }
+
+ return err;
+}
+#else
+int main(void)
+{
+ printf("SCTP unsupported in this build\n");
+ errno = EINVAL;
+ return -1;
+}
+#endif
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Feb 27, 3:29 AM (1 d, 10 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1466109
Default Alt Text
(27 KB)
Attached To
Mode
rK kronosnet
Attached
Detach File
Event Timeline
Log In to Comment