├── NEWS ├── debian ├── compat ├── docs ├── pepsal.dirs ├── source │ └── format ├── changelog ├── pepsal.default ├── pepsal.service ├── control ├── rules ├── pepsal.preinst ├── pepsal.prerm ├── pepsal.postinst ├── copyright ├── pepsal.postrm └── pepsal.init ├── Makefile.am ├── src ├── stamp-h1 ├── Makefile.am ├── pepqueue.c ├── pepbuf.c ├── pepsal.1 ├── syntab.c ├── hashtable.c └── pep.c ├── ltmain.sh ├── conf ├── syslog.conf ├── logrotate.conf ├── start.ini ├── pepsal.conf ├── pepsal-arguments └── pepsal-iptables ├── .gitignore ├── bootstrap ├── iptables-config-example.sh ├── ChangeLog ├── autogen.sh ├── AUTHORS ├── include ├── atomic.h ├── pepbuf.h ├── pepqueue.h ├── pepsal.h ├── syntab.h ├── pepdefs.h ├── hashtable_private.h ├── hashtable.h └── list.h ├── configure.ac ├── README ├── INSTALL └── COPYING /NEWS: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = src 2 | -------------------------------------------------------------------------------- /debian/docs: -------------------------------------------------------------------------------- 1 | NEWS 2 | README 3 | -------------------------------------------------------------------------------- /debian/pepsal.dirs: -------------------------------------------------------------------------------- 1 | /usr/bin 2 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /src/stamp-h1: -------------------------------------------------------------------------------- 1 | timestamp for config.h 2 | -------------------------------------------------------------------------------- /ltmain.sh: -------------------------------------------------------------------------------- 1 | /usr/share/libtool/build-aux/ltmain.sh -------------------------------------------------------------------------------- /conf/syslog.conf: -------------------------------------------------------------------------------- 1 | :programname, isequal, "pepsal" -/var/log/pepsal/pepsal.log 2 | -------------------------------------------------------------------------------- /conf/logrotate.conf: -------------------------------------------------------------------------------- 1 | /var/log/pepsal/pepsal.log { 2 | weekly 3 | rotate 4 4 | missingok 5 | notifempty 6 | compress 7 | } 8 | -------------------------------------------------------------------------------- /conf/start.ini: -------------------------------------------------------------------------------- 1 | /usr/bin/pepsal -d ${debug} ${fastopen} -p ${port} -a ${ip_address} -c ${max_conns} -g ${gcc_interval} -l ${log_file} -t ${pending_lifetime} 2 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | pepsal (1.8.8) trusty; urgency=low 2 | 3 | * Initial release 4 | 5 | -- PEPSal Maintainers Thu, 20 Oct 2016 14:27:52 +0200 6 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | AM_CFLAGS = -I$(top_srcdir)/include 2 | 3 | bin_PROGRAMS = pepsal 4 | pepsal_SOURCES= pep.c hashtable.c pepbuf.c pepqueue.c syntab.c 5 | man_MANS = pepsal.1 6 | EXTRA_DIST = $(man_MANS) 7 | -------------------------------------------------------------------------------- /debian/pepsal.default: -------------------------------------------------------------------------------- 1 | # Defaults for pepsal initscript 2 | # sourced by /etc/init.d/pepsal 3 | # installed at /etc/default/pepsal by the maintainer scripts 4 | 5 | # 6 | # This is a POSIX shell fragment 7 | # 8 | 9 | # Additional options that are passed to the Daemon. 10 | DAEMON_OPTS="" 11 | -------------------------------------------------------------------------------- /debian/pepsal.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Startup for PEPSal 3 | After=remote-fs.target 4 | After=systemd-journald-dev-log.socket 5 | 6 | [Service] 7 | Type=forking 8 | ExecStartPre=/etc/pepsal/pepsal-arguments 9 | ExecStart=/bin/sh -ac ". /etc/pepsal/.pepsal.conf ; sh /etc/pepsal/start.ini" 10 | 11 | [Install] 12 | WantedBy=multi-user.target 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # C extentions 2 | *.so 3 | *.o 4 | 5 | # Autotools 6 | Makefile 7 | Makefile.in 8 | autom4te.cache 9 | aclocal.m4 10 | compile 11 | configure 12 | depcomp 13 | install-sh 14 | missing 15 | stamp-h1 16 | 17 | .deps 18 | config.log 19 | config.status 20 | ltmain.sh 21 | config.h 22 | config.h.in 23 | 24 | src/pepsal 25 | debian/pepsal 26 | debian/files 27 | debian/tmp 28 | *.substvars 29 | *.debhelper* 30 | *.libs 31 | *.deb 32 | *.dsc 33 | *.tar.gz 34 | -------------------------------------------------------------------------------- /conf/pepsal.conf: -------------------------------------------------------------------------------- 1 | # Port to which PEPSal listens 2 | port="5000" 3 | # IP address to bind PEPSal 4 | ip_address="0.0.0.0" 5 | # Maximum nomber of connections (between 128 and 4096) 6 | max_conns="2112" 7 | # Garbage collector interval (seconds) 8 | gcc_interval="54000" 9 | # Enable TCP FastOpen 10 | fastopen="false" 11 | # Enable debug logs 12 | debug="true" 13 | # Pending connection lifetime (seconds) 14 | pending_lifetime="18000" 15 | # Log file 16 | log_file="/var/log/pepsal/connections.log" 17 | 18 | -------------------------------------------------------------------------------- /bootstrap: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -x 4 | 5 | case `uname -s` in 6 | Darwin) 7 | LIBTOOLIZE=glibtoolize 8 | ;; 9 | *) 10 | LIBTOOLIZE=libtoolize 11 | ;; 12 | esac 13 | 14 | # Remove autoconf 2.5x's cache directory 15 | rm -rf autom4te*.cache 16 | 17 | aclocal -I . || exit 1 18 | autoheader || exit 1 19 | ${LIBTOOLIZE} --force --copy || exit 1 20 | automake --foreign --add-missing --copy || exit 1 21 | autoconf || exit 1 22 | -------------------------------------------------------------------------------- /iptables-config-example.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "8192 2100000 8400000" >/proc/sys/net/ipv4/tcp_mem 4 | echo "8192 2100000 8400000" >/proc/sys/net/ipv4/tcp_rmem 5 | echo "8192 2100000 8400000" >/proc/sys/net/ipv4/tcp_wmem 6 | 7 | CLIENTS_IFACE="eth0" 8 | 9 | ip rule add fwmark 1 lookup 100 10 | ip route add local 0.0.0.0/0 dev lo table 100 11 | 12 | # Careful, as this will erase any other preexisting entries 13 | iptables -t mangle -F 14 | iptables -t mangle -A PREROUTING -i $CLIENTS_IFACE -p tcp -j TPROXY --on-port 5000 --tproxy-mark 1 15 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: pepsal 2 | Section: misc 3 | Priority: optional 4 | Maintainer: PEPSal Maintainers 5 | Build-Depends: debhelper (>= 8.0.0), dh-systemd, 6 | iptables, iproute2 7 | Standards-Version: 3.9.4 8 | 9 | Package: pepsal 10 | Architecture: any 11 | Section: misc 12 | Depends: ${shlibs:Depends}, ${misc:Depends}, iptables, 13 | iproute2, lsb-base (>= 3.2-14), rsyslog, logrotate 14 | Description: PEPSal Performance Enhancing Proxy 15 | PEPSal is an open-source TCP Performance Enhancing Proxy (PEP) 16 | for satellite links. 17 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 1.0.0 : Several bugfixes, rewritten most of the code, removed obsolete ip_queue interface, autotools added 2 | 1.0.1 : Double initial bind for TCP socket. New POSIX signal management 3 | support. 4 | 1.0.2 : Added socket timeout options for non-blocking sockets. 5 | 6 | 1.2.0 : Fixed instability in http connections. Added timeout for sleeping 7 | sockets. Two separate poll for reading/writing 8 | 9 | 1.2.1 : small bugfix: netfilter_queue libraries unbind function return value discarded 10 | 11 | 1.2.2 : Added -a option to bind to a single address, memory leak bugfix (all thanks to Wojtek Sawasciuk) 12 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Script to generate all required files for `configure` when 3 | # starting from a fresh repository checkout 4 | 5 | run() 6 | { 7 | echo -n "Running $1... " 8 | $@ >/dev/null 2>&1 9 | if [ $? -eq 0 ] ; then 10 | echo "done" 11 | else 12 | echo "failed" 13 | echo "Running $1 again with errors unmasked:" 14 | $@ 15 | exit 1 16 | fi 17 | } 18 | 19 | rm -f config.cache 20 | rm -f config.log 21 | 22 | run aclocal 23 | run libtoolize --force 24 | run autoconf 25 | run autoheader 26 | run automake --add-missing 27 | 28 | chmod +x ./configure 29 | ./configure --enable-fail-on-warning $@ 30 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 2 | PEPsal: A TCP Performance Enhancing Proxy for satellite links 3 | http://www.sourceforge.net/projects/pepsal 4 | -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 5 | 6 | Idea and Design : Carlo Caini , 7 | Rosario Firrincieli 8 | Daniele Lacamera 9 | 10 | Authors : Daniele Lacamera , Dan Kruchinin 11 | 12 | Co-Author : Sergio Ammirata 13 | 14 | 15 | Please Refer to COPYING for license. 16 | 17 | 18 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # -*- makefile -*- 3 | 4 | # Uncomment this to turn on verbose mode. 5 | #export DH_VERBOSE=1 6 | 7 | override_dh_install: 8 | dh_install 9 | 10 | # Configuration scripts 11 | install -D -m 0644 conf/pepsal.conf $(CURDIR)/debian/pepsal/etc/pepsal/pepsal.conf 12 | install -D -m 0755 conf/pepsal-arguments $(CURDIR)/debian/pepsal/etc/pepsal/pepsal-arguments 13 | install -D -m 0644 conf/start.ini $(CURDIR)/debian/pepsal/etc/pepsal/start.ini 14 | install -D -m 0755 conf/pepsal-iptables $(CURDIR)/debian/pepsal/usr/bin/pepsal-iptables 15 | 16 | # Syslog 17 | install -D -m 0644 conf/syslog.conf $(CURDIR)/debian/pepsal/etc/rsyslog.d/pepsal.conf 18 | install -D -m 0644 conf/logrotate.conf $(CURDIR)/debian/pepsal/etc/logrotate.d/pepsal.conf 19 | 20 | %: 21 | dh $@ --with=systemd 22 | -------------------------------------------------------------------------------- /include/atomic.h: -------------------------------------------------------------------------------- 1 | #ifndef __ATOMIC_H 2 | #define __ATOMIC_H 3 | 4 | #ifndef __GNUC__ 5 | #error "Atomic operations require GCC compiler!" 6 | #endif /* !GCC */ 7 | 8 | typedef struct __atomic { 9 | volatile int val; 10 | } atomic_t; 11 | 12 | #define atomic_read(a) ((a)->val) 13 | #define atomic_set(a, b) ((a)->val = b) 14 | 15 | static inline int atomic_inc(atomic_t *a) 16 | { 17 | return __sync_fetch_and_add(&a->val, 1); 18 | } 19 | 20 | static inline int atomic_dec(atomic_t *a) 21 | { 22 | return __sync_fetch_and_sub(&a->val, 1); 23 | } 24 | 25 | static inline int atomic_and(atomic_t *a, int mask) 26 | { 27 | return __sync_fetch_and_and(&a->val, mask); 28 | } 29 | 30 | static inline int atomic_or(atomic_t *a, int mask) 31 | { 32 | return __sync_fetch_and_or(&a->val, mask); 33 | } 34 | 35 | #endif /* __ATOMIC_H */ 36 | -------------------------------------------------------------------------------- /debian/pepsal.preinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # preinst script for pepsal 3 | # 4 | # see: dh_installdeb(1) 5 | 6 | set -e 7 | 8 | # summary of how this script can be called: 9 | # * `install' 10 | # * `install' 11 | # * `upgrade' 12 | # * `abort-upgrade' 13 | # for details, see http://www.debian.org/doc/debian-policy/ or 14 | # the debian-policy package 15 | 16 | 17 | case "$1" in 18 | install|upgrade) 19 | ;; 20 | 21 | abort-upgrade) 22 | ;; 23 | 24 | *) 25 | echo "preinst called with unknown argument \`$1'" >&2 26 | exit 1 27 | ;; 28 | esac 29 | 30 | # dh_installdeb will replace this with shell code automatically 31 | # generated by other debhelper scripts. 32 | 33 | #DEBHELPER# 34 | 35 | exit 0 36 | -------------------------------------------------------------------------------- /debian/pepsal.prerm: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # prerm script for pepsal 3 | # 4 | # see: dh_installdeb(1) 5 | 6 | set -e 7 | 8 | # summary of how this script can be called: 9 | # * `remove' 10 | # * `upgrade' 11 | # * `failed-upgrade' 12 | # * `remove' `in-favour' 13 | # * `deconfigure' `in-favour' 14 | # `removing' 15 | # 16 | # for details, see http://www.debian.org/doc/debian-policy/ or 17 | # the debian-policy package 18 | 19 | 20 | case "$1" in 21 | remove|upgrade|deconfigure) 22 | ;; 23 | 24 | failed-upgrade) 25 | ;; 26 | 27 | *) 28 | echo "prerm called with unknown argument \`$1'" >&2 29 | exit 1 30 | ;; 31 | esac 32 | 33 | # dh_installdeb will replace this with shell code automatically 34 | # generated by other debhelper scripts. 35 | 36 | #DEBHELPER# 37 | 38 | exit 0 39 | -------------------------------------------------------------------------------- /include/pepbuf.h: -------------------------------------------------------------------------------- 1 | /* PEPsal : A Performance Enhancing Proxy for Satellite Links 2 | * 3 | * Copyleft Daniele Lacamera 2005 4 | * Copyleft Dan Kruchining 2010 5 | * See AUTHORS and COPYING before using this software. 6 | * 7 | * 8 | * 9 | */ 10 | 11 | #ifndef __PEPBUF_H 12 | #define __PEPBUF_H 13 | 14 | #include 15 | #include "pepdefs.h" 16 | 17 | struct pep_buffer { 18 | void *space; 19 | char *r_pos; 20 | char *w_pos; 21 | size_t rbytes; 22 | size_t space_left; 23 | size_t total_size; 24 | }; 25 | 26 | #define pepbuf_empty(pbuf) ((pbuf)->rbytes == 0) 27 | #define pepbuf_full(pbuf) ((pbuf)->space_left == 0) 28 | #define pepbuf_initialized(pbuf) ((pbuf)->space != NULL) 29 | 30 | #define PEPBUF_RPOS(pbuf) (pbuf)->r_pos 31 | #define PEPBUF_WPOS(pbuf) (pbuf)->w_pos 32 | #define PEPBUF_SPACE_LEFT(pbuf) (pbuf)->space_left 33 | #define PEPBUF_SPACE_FILLED(pbuf) (pbuf)->rbytes 34 | 35 | int pepbuf_init(struct pep_buffer *pbuf); 36 | void pepbuf_deinit(struct pep_buffer *pbuf); 37 | void pepbuf_update_rpos(struct pep_buffer *pbuf, ssize_t rb); 38 | void pepbuf_update_wpos(struct pep_buffer *pbuf, ssize_t wb); 39 | 40 | #endif /* __PEPBUF_H */ 41 | -------------------------------------------------------------------------------- /include/pepqueue.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * PEPsal : A Performance Enhancing Proxy for Satellite Links 4 | * 5 | * Copyleft Dan Kruchinin 2010 6 | * See AUTHORS and COPYING before using this software. 7 | * 8 | * 9 | * 10 | */ 11 | 12 | #ifndef __PEPQUEUE_H 13 | #define __PEPQUEUE_H 14 | 15 | #include 16 | #include "pepdefs.h" 17 | #include "list.h" 18 | 19 | struct pep_queue { 20 | struct list_head queue; 21 | int num_items; 22 | pthread_mutex_t mutex; 23 | pthread_cond_t condvar; 24 | }; 25 | 26 | #define PEPQUEUE_LOCK(pq) pthread_mutex_lock(&(pq)->mutex) 27 | #define PEPQUEUE_UNLOCK(pq) pthread_mutex_unlock(&(pq)->mutex) 28 | 29 | #define PEPQUEUE_WAKEUP_WAITERS(pq) pthread_cond_signal(&(pq)->condvar) 30 | #define PEPQUEUE_WAIT(pq) pthread_cond_wait(&(pq)->condvar, &(pq)->mutex) 31 | 32 | int pepqueue_init(struct pep_queue *pq); 33 | void pepqueue_enqueue(struct pep_queue *pq, struct pep_proxy *endp); 34 | void pepqueue_enqueue_list(struct pep_queue *pq, 35 | struct list_head *list, int num_items); 36 | struct pep_proxy *pepqueue_dequeue(struct pep_queue *pq); 37 | void pepqueue_dequeue_list(struct pep_queue *pq, struct list_head *list); 38 | 39 | #endif /* __PEPQUEUE_H */ 40 | -------------------------------------------------------------------------------- /debian/pepsal.postinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # postinst script for pepsal 3 | # 4 | # see: dh_installdeb(1) 5 | 6 | set -e 7 | 8 | # summary of how this script can be called: 9 | # * `configure' 10 | # * `abort-upgrade' 11 | # * `abort-remove' `in-favour' 12 | # 13 | # * `abort-remove' 14 | # * `abort-deconfigure' `in-favour' 15 | # `removing' 16 | # 17 | # for details, see http://www.debian.org/doc/debian-policy/ or 18 | # the debian-policy package 19 | 20 | restart_syslog() 21 | { 22 | service rsyslog restart 23 | } 24 | 25 | case "$1" in 26 | configure) 27 | chmod +x /etc/pepsal/pepsal-arguments 28 | restart_syslog 29 | ;; 30 | 31 | abort-upgrade|abort-remove|abort-deconfigure) 32 | ;; 33 | 34 | *) 35 | echo "postinst called with unknown argument \`$1'" >&2 36 | exit 1 37 | ;; 38 | esac 39 | 40 | # dh_installdeb will replace this with shell code automatically 41 | # generated by other debhelper scripts. 42 | 43 | #DEBHELPER# 44 | 45 | exit 0 46 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: pepsal 3 | Source: 4 | 5 | Files: * 6 | Copyright: Daniele Lacamera 7 | Dan Kruchinin 8 | Sergio Ammirata 9 | License: GPL-2.0+ 10 | 11 | Files: debian/* 12 | Copyright: 2016 PEPSal Maintainers 13 | License: GPL-2.0+ 14 | 15 | License: GPL-2.0+ 16 | This package is free software; you can redistribute it and/or modify 17 | it under the terms of the GNU General Public License as published by 18 | the Free Software Foundation; either version 2 of the License, or 19 | (at your option) any later version. 20 | . 21 | This package is distributed in the hope that it will be useful, 22 | but WITHOUT ANY WARRANTY; without even the implied warranty of 23 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 | GNU General Public License for more details. 25 | . 26 | You should have received a copy of the GNU General Public License 27 | along with this program. If not, see 28 | . 29 | On Debian systems, the complete text of the GNU General 30 | Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". 31 | -------------------------------------------------------------------------------- /debian/pepsal.postrm: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # postrm script for pepsal 3 | # 4 | # see: dh_installdeb(1) 5 | 6 | set -e 7 | 8 | # summary of how this script can be called: 9 | # * `remove' 10 | # * `purge' 11 | # * `upgrade' 12 | # * `failed-upgrade' 13 | # * `abort-install' 14 | # * `abort-install' 15 | # * `abort-upgrade' 16 | # * `disappear' 17 | # 18 | # for details, see http://www.debian.org/doc/debian-policy/ or 19 | # the debian-policy package 20 | 21 | remove_syslog_conf() 22 | { 23 | EXE_RM="/bin/rm" 24 | SYSLOG_CONF="/etc/rsyslog.d/pepsal.conf" 25 | LOGROTATE_CONF="/etc/logrotate.d/pepsal.conf" 26 | 27 | ${EXE_RM} -f ${SYSLOG_CONF} || true 28 | ${EXE_RM} -f ${LOGROTATE_CONF} || true 29 | 30 | case "$1" in 31 | purge) 32 | remove_syslog_conf 33 | ;; 34 | 35 | remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) 36 | ;; 37 | 38 | *) 39 | echo "postrm called with unknown argument \`$1'" >&2 40 | exit 1 41 | ;; 42 | esac 43 | 44 | # dh_installdeb will replace this with shell code automatically 45 | # generated by other debhelper scripts. 46 | 47 | #DEBHELPER# 48 | 49 | exit 0 50 | -------------------------------------------------------------------------------- /include/pepsal.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * PEPsal : A Performance Enhancing Proxy for Satellite Links 4 | * 5 | * Copyleft Daniele Lacamera 2005-2007 6 | * Copyleft Dan Kruchinin 2010 7 | * See AUTHORS and COPYING before using this software. 8 | * 9 | * 10 | * 11 | */ 12 | 13 | #ifndef __PEPSAL_H 14 | #define __PEPSAL_H 15 | 16 | #include "pepdefs.h" 17 | #include "pepbuf.h" 18 | #include "atomic.h" 19 | #include "list.h" 20 | 21 | enum proxy_status { 22 | PST_CLOSED = 0, 23 | PST_OPEN, 24 | PST_CONNECT, 25 | PST_PENDING, 26 | PST_INVAL, 27 | }; 28 | 29 | /* I/O flags of PEP endpoint */ 30 | #define PEP_IORDONE 0x01 31 | #define PEP_IOWDONE 0x02 32 | #define PEP_IOEOF 0x04 33 | #define PEP_IOERR 0x08 34 | 35 | struct pep_proxy; 36 | 37 | struct pep_endpoint{ 38 | int addr; 39 | unsigned short port; 40 | int fd; 41 | struct pep_buffer buf; 42 | struct pep_proxy *owner; 43 | unsigned short poll_events; 44 | unsigned char iostat; 45 | }; 46 | 47 | #define PROXY_ENDPOINTS 2 48 | 49 | struct pep_proxy { 50 | enum proxy_status status; 51 | struct list_node lnode; 52 | struct list_node qnode; 53 | 54 | union { 55 | struct pep_endpoint endpoints[PROXY_ENDPOINTS]; 56 | struct { 57 | struct pep_endpoint src; 58 | struct pep_endpoint dst; 59 | }; 60 | }; 61 | 62 | time_t syn_time; 63 | time_t last_rxtx; 64 | atomic_t refcnt; 65 | int enqueued; 66 | }; 67 | 68 | #endif /* !__PEPSAL_H */ 69 | -------------------------------------------------------------------------------- /src/pepqueue.c: -------------------------------------------------------------------------------- 1 | /* 2 | * PEPsal : A Performance Enhancing Proxy for Satellite Links 3 | * 4 | * Copyleft Dan Kruchinin 2010 5 | * See AUTHORS and COPYING before using this software. 6 | * 7 | * 8 | * 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | #include "pepsal.h" 15 | #include "pepqueue.h" 16 | 17 | int pepqueue_init(struct pep_queue *pq) 18 | { 19 | list_init_head(&pq->queue); 20 | if (pthread_mutex_init(&pq->mutex, NULL) != 0) { 21 | return -1; 22 | } 23 | if (pthread_cond_init(&pq->condvar, NULL) != 0) { 24 | return -1; 25 | } 26 | 27 | pq->num_items = 0; 28 | return 0; 29 | } 30 | 31 | void pepqueue_enqueue(struct pep_queue *pq, struct pep_proxy *proxy) 32 | { 33 | list_add2tail(&pq->queue, &proxy->qnode); 34 | pq->num_items++; 35 | } 36 | 37 | void pepqueue_enqueue_list(struct pep_queue *pq, 38 | struct list_head *list, int num_items) 39 | { 40 | list_move2tail(&pq->queue, list); 41 | pq->num_items += num_items; 42 | } 43 | 44 | struct pep_proxy *pepqueue_dequeue(struct pep_queue *pq) 45 | { 46 | struct pep_proxy *proxy = NULL; 47 | 48 | if (pq->num_items == 0) { 49 | return NULL; 50 | } 51 | 52 | proxy = list_entry(list_node_first(&pq->queue), 53 | struct pep_proxy, qnode); 54 | list_del(&proxy->qnode); 55 | pq->num_items--; 56 | 57 | return proxy; 58 | } 59 | 60 | void pepqueue_dequeue_list(struct pep_queue *pq, struct list_head *lh) 61 | { 62 | list_move2head(lh, &pq->queue); 63 | pq->num_items = 0; 64 | } 65 | -------------------------------------------------------------------------------- /include/syntab.h: -------------------------------------------------------------------------------- 1 | /* 2 | * PEPsal : A Performance Enhancing Proxy for Satellite Links 3 | * 4 | * Copyleft Daniele Lacamera 2005-2007 5 | * Copyleft Dan Kruchinin 2010 6 | * Copyleft Joaquin Muguerza 2016 7 | * See AUTHORS and COPYING before using this software. 8 | * 9 | * 10 | * 11 | */ 12 | 13 | #ifndef __PEPSAL_SYNTAB_H 14 | #define __PEPSAL_SYNTAB_H 15 | 16 | #include 17 | #include "hashtable.h" 18 | #include "list.h" 19 | #include "pepsal.h" 20 | 21 | struct syn_table{ 22 | struct hashtable *hash; 23 | struct list_head conns; 24 | pthread_rwlock_t lock; 25 | int num_items; 26 | }; 27 | 28 | struct syntab_key { 29 | int addr; 30 | unsigned short port; 31 | }; 32 | 33 | #define GET_SYNTAB() (&syntab) 34 | 35 | #define SYNTAB_LOCK_READ() pthread_rwlock_rdlock(&(GET_SYNTAB())->lock) 36 | #define SYNTAB_LOCK_WRITE() pthread_rwlock_wrlock(&(GET_SYNTAB())->lock) 37 | #define SYNTAB_UNLOCK_READ() pthread_rwlock_unlock(&(GET_SYNTAB())->lock) 38 | #define SYNTAB_UNLOCK_WRITE() pthread_rwlock_unlock(&(GET_SYNTAB())->lock) 39 | 40 | extern struct syn_table syntab; 41 | 42 | #define syntab_foreach_connection(con) \ 43 | list_for_each_entry(&GET_SYNTAB()->conns, con, struct pep_proxy, lnode) 44 | 45 | int syntab_init(int num_conns); 46 | void syntab_format_key(struct pep_proxy *proxy, struct syntab_key *key); 47 | struct pep_proxy *syntab_find(struct syntab_key *key); 48 | int syntab_add(struct pep_proxy *proxy); 49 | void syntab_delete(struct pep_proxy *proxy); 50 | 51 | #endif /* __PEPSAL_SYNTAB_H */ 52 | -------------------------------------------------------------------------------- /conf/pepsal-arguments: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Read configurations variable if it is present 4 | [ -r /etc/pepsal/pepsal.conf ] && . /etc/pepsal/pepsal.conf 5 | 6 | # Remove old file 7 | rm -f /etc/pepsal/.pepsal.conf 8 | 9 | # Save variables to arguments file 10 | if [ -z "$port" ] 11 | then 12 | echo "port=5000" >>/etc/pepsal/.pepsal.conf 13 | else 14 | echo "port=${port}" >>/etc/pepsal/.pepsal.conf 15 | fi 16 | 17 | if [ -z "$ip_address" ] 18 | then 19 | echo "ip_address=0.0.0.0" >>/etc/pepsal/.pepsal.conf 20 | else 21 | echo "ip_address=${ip_address}" >>/etc/pepsal/.pepsal.conf 22 | fi 23 | 24 | if [ -z "$max_conns" ] 25 | then 26 | echo "max_conns=2112" >>/etc/pepsal/.pepsal.conf 27 | else 28 | echo "max_conns=${max_conns}" >>/etc/pepsal/.pepsal.conf 29 | fi 30 | 31 | if [ -z "$gcc_interval" ] 32 | then 33 | echo "gcc_interval=54000" >>/etc/pepsal/.pepsal.conf 34 | else 35 | echo "gcc_interval=${gcc_interval}" >>/etc/pepsal/.pepsal.conf 36 | fi 37 | 38 | if [ -z "$log_file" ] 39 | then 40 | echo "log_file=/var/log/pepsal.log" >>/etc/pepsal/.pepsal.conf 41 | else 42 | echo "log_file=${log_file}" >>/etc/pepsal/.pepsal.conf 43 | fi 44 | 45 | if [ -z "$pending_lifetime" ] 46 | then 47 | echo "pending_lifetime=18000" >>/etc/pepsal/.pepsal.conf 48 | else 49 | echo "pending_lifetime=${pending_lifetime}" >>/etc/pepsal/.pepsal.conf 50 | fi 51 | 52 | if [[ $fastopen = true ]] 53 | then 54 | echo "fastopen=-f" >>/etc/pepsal/.pepsal.conf 55 | else 56 | echo "fastopen=" >>/etc/pepsal/.pepsal.conf 57 | fi 58 | 59 | if [[ $debug = true ]] 60 | then 61 | echo "debug=-f" >>/etc/pepsal/.pepsal.conf 62 | else 63 | echo "debug=" >>/etc/pepsal/.pepsal.conf 64 | fi 65 | -------------------------------------------------------------------------------- /src/pepbuf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * PEPsal : A Performance Enhancing Proxy for Satellite Links 3 | * 4 | * Copyleft Daniele Lacamera 2005-2007 5 | * Copyleft Dan Kruchinin 2010 6 | * See AUTHORS and COPYING before using this software. 7 | * 8 | * 9 | * 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include "pepdefs.h" 17 | #include "pepsal.h" 18 | #include "pepbuf.h" 19 | 20 | int pepbuf_init(struct pep_buffer *pbuf) 21 | { 22 | void *space; 23 | 24 | space = mmap(NULL, PEPBUF_PAGES * PAGE_SIZE, PROT_READ | PROT_WRITE, 25 | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); 26 | if (!space) { 27 | return -1; 28 | } 29 | 30 | pbuf->space = space; 31 | pbuf->r_pos = pbuf->w_pos = pbuf->space; 32 | pbuf->total_size = PEPBUF_PAGES * PAGE_SIZE; 33 | pbuf->space_left = pbuf->total_size; 34 | pbuf->rbytes = 0; 35 | 36 | return 0; 37 | } 38 | 39 | void pepbuf_deinit(struct pep_buffer *pbuf) 40 | { 41 | munmap(pbuf->space, pbuf->total_size); 42 | memset(pbuf, 0, sizeof(*pbuf)); 43 | } 44 | 45 | void pepbuf_update_rpos(struct pep_buffer *pbuf, ssize_t rb) 46 | { 47 | assert((ssize_t)PEPBUF_SPACE_LEFT(pbuf) - rb >= 0); 48 | pbuf->r_pos += rb; 49 | pbuf->rbytes += rb; 50 | pbuf->space_left -= rb; 51 | } 52 | 53 | void pepbuf_update_wpos(struct pep_buffer *pbuf, ssize_t wb) 54 | { 55 | assert(((pbuf->w_pos + wb) - (char *)pbuf->space) <= pbuf->total_size); 56 | 57 | pbuf->w_pos += wb; 58 | pbuf->rbytes -= wb; 59 | if (pbuf->w_pos == pbuf->r_pos) { 60 | pbuf->r_pos = pbuf->w_pos = pbuf->space; 61 | pbuf->space_left = pbuf->total_size; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /include/pepdefs.h: -------------------------------------------------------------------------------- 1 | #ifndef __PEPSDEFS_H 2 | #define __PEPSDEFS_H 3 | 4 | #include 5 | 6 | /* Program name */ 7 | #define PROGRAM_NAME "pepsal" 8 | 9 | /* Minimal and maximal number of simultaneous connections */ 10 | #define PEP_MIN_CONNS 128 11 | #define PEP_MAX_CONNS 4096 12 | 13 | /* Default port number of pepsal listener */ 14 | #define PEP_DEFAULT_PORT 5000 15 | 16 | /* Default receive buffer size of queuer thread */ 17 | #define QUEUER_BUF_SIZE PAGE_SIZE 18 | 19 | /* 20 | * Size of buffer that is used for temporary error messages 21 | * composed by pep_error and pep_warning functions. 22 | */ 23 | #define PEP_ERRBUF_SZ 1024 24 | 25 | /* Queue size of listener thread used for incomming TCP packets */ 26 | #define LISTENER_QUEUE_SIZE 60000 27 | 28 | /* 29 | * Signal number that is sent to poller thread when 30 | * new incomming connection appears 31 | */ 32 | #define POLLER_NEWCONN_SIG SIGUSR1 33 | 34 | /* Number of pages reserved for send/receive buffers */ 35 | #define PEPBUF_PAGES 2 36 | 37 | /* Number of worker threads in pepsal threads pool */ 38 | #define PEPPOOL_THREADS 10 39 | 40 | #define PEPLOGGER_INTERVAL (5 * 60) 41 | 42 | #define PEP_GCC_INTERVAL (15 * 3600) 43 | 44 | #define PEP_PENDING_CONN_LIFETIME (5 * 3600) 45 | 46 | #ifndef offsetof 47 | #define offsetof(type, field) \ 48 | ((size_t)&(((type *)0)->field) - (size_t)((type *)0)) 49 | #endif /* !offsetof */ 50 | 51 | #define container_of(ptr, type, member) \ 52 | (type *)((char *)(ptr) - offsetof(type, member)) 53 | 54 | #if (defined(__cplusplus) || defined(__GNUC__) || defined(__INTEL_COMPILER)) 55 | #define __inline inline 56 | #else /* __cplusplus || __GNUC__ || __INTEL_COMPILER */ 57 | #define __inline 58 | #endif /* !__cplusplus && !__GNUC__ && !__INTEL_COMPILER */ 59 | 60 | #ifndef UNUSED 61 | #if defined(__GNUC__) 62 | #define UNUSED(x) x __attribute__((unused)) 63 | #else /* __GNUC__ */ 64 | #define UNUSED(x) 65 | #endif /* !__GNUC__ */ 66 | #endif /* !UNUSED */ 67 | 68 | #endif /* !_PEPSDEFS_H */ 69 | -------------------------------------------------------------------------------- /conf/pepsal-iptables: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Script to aid the configuration of iptables for use 4 | # with PEPSal 5 | 6 | DEFAULT_TABLE_NUMBER=100 7 | 8 | load-conf() 9 | { 10 | # Load PEPSal conf 11 | /etc/pepsal/pepsal-arguments 12 | [ -r /etc/pepsal/.pepsal.conf ] && . /etc/pepsal/.pepsal.conf 13 | } 14 | 15 | route-table() 16 | { 17 | # Check if lookup table for fwmark 1 exists 18 | TABLE=$(ip rule | grep "from all fwmark 0x1 lookup" | awk '{print $NF}') 19 | if [ -z $TABLE ] 20 | then 21 | TABLE=${DEFAULT_TABLE_NUMBER} 22 | ip rule add fwmark 1 lookup ${TABLE} || error 23 | fi 24 | # Check if default route in table exists 25 | RET=$(ip route list table ${TABLE} | grep default) 26 | if [ "x$RET" = "x" ] 27 | then 28 | ip route add local 0.0.0.0/0 dev lo table ${TABLE} 29 | else 30 | ip route change local 0.0.0.0/0 dev lo table ${TABLE} 31 | fi 32 | } 33 | 34 | addiface-iptables() 35 | { 36 | # Check if the rule exists, otherwise, add it 37 | IFACE=$1 38 | iptables -C PREROUTING -t mangle -i ${IFACE} -p tcp -j TPROXY \ 39 | --on-port ${port} --tproxy-mark 1 2>/dev/null && exit 0 40 | iptables -A PREROUTING -t mangle -i ${IFACE} -p tcp -j TPROXY \ 41 | --on-port ${port} --tproxy-mark 1 || error 42 | } 43 | 44 | deliface-iptables() 45 | { 46 | # Check if the rule exists, otherwise, add it 47 | IFACE=$1 48 | iptables -C PREROUTING -t mangle -i ${IFACE} -p tcp -j TPROXY \ 49 | --on-port ${port} --tproxy-mark 1 2>/dev/null && exit 0 50 | iptables -D PREROUTING -t mangle -i ${IFACE} -p tcp -j TPROXY \ 51 | --on-port ${port} --tproxy-mark 1 || error 52 | } 53 | 54 | error() 55 | { 56 | echo "Error!" >&2 && exit 1 57 | } 58 | 59 | usage() 60 | { 61 | # TODO: add options to add IP addresses (src or dst) 62 | echo "Usage: $0 [-h] (addiface|deliface) [args]" >&2 63 | echo " addiface iface redirect incoming traffic to iface to PEPSal" >&2 64 | echo " deliface iface remove iptables entry related to iface" >&2 65 | exit 1 66 | } 67 | 68 | case "$1" in 69 | addiface) 70 | load-conf 71 | route-table 72 | addiface-iptables $2 73 | ;; 74 | deliface) 75 | load-conf 76 | deliface-iptables $2 77 | ;; 78 | *) 79 | usage 80 | ;; 81 | esac 82 | 83 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Process this file with autoconf to produce a configure script. 4 | 5 | AC_PREREQ(2.59e) 6 | AC_INIT(PEPSaL, 2.0.0) 7 | AC_CONFIG_SRCDIR(src) 8 | AC_CONFIG_HEADER(include/config.h) 9 | AM_INIT_AUTOMAKE 10 | 11 | # 12 | # defines the required versions of libraries 13 | # 14 | 15 | # By default, generate static libraries 16 | #AC_DISABLE_SHARED 17 | #AC_DISABLE_STATIC 18 | 19 | # Checks for programs. 20 | AC_PROG_CC 21 | AC_PROG_MAKE_SET 22 | AC_PROG_RANLIB 23 | 24 | # Checks for libraries 25 | AC_CHECK_LIB([nfnetlink], [nfnl_open],,check_nfnetlink="no") 26 | AC_CHECK_LIB([pthread], [pthread_create],,check_pthread="no") 27 | AC_CHECK_LIB([rt], [timer_create],,check_rt="no") 28 | AC_CHECK_LIB([m], [ceil],,check_m="no") 29 | 30 | if test \ 31 | -o x${check_nfnetlink} = xno \ 32 | -o x${check_pthread} = xno \ 33 | -o x${check_rt} = xno \ 34 | -o x${check_m} = xno \ 35 | ; then 36 | AC_MSG_RESULT([]) 37 | if test x${check_nfqueue} = xno; then 38 | AC_MSG_RESULT([ERROR: libnetfilter-queue required]) 39 | fi 40 | if test x${check_nfnetlink} = xno; then 41 | AC_MSG_RESULT([ERROR: libnfnetlink required]) 42 | fi 43 | if test x${check_pthread} = xno; then 44 | AC_MSG_RESULT([ERROR: libpthread required]) 45 | fi 46 | if test x${check_rt} = xno; then 47 | AC_MSG_RESULT([ERROR: librt required]) 48 | fi 49 | if test x${check_m} = xno; then 50 | AC_MSG_RESULT([ERROR: libm required]) 51 | fi 52 | exit 1 53 | fi 54 | 55 | # Add required libraries to LDFLAGS 56 | [ LDFLAGS="$LDFLAGS -lnfnetlink -lpthread -lrt -lm" ] 57 | 58 | # Checks for header files. 59 | AC_HEADER_DIRENT 60 | AC_HEADER_STDC 61 | AC_HEADER_SYS_WAIT 62 | AC_CHECK_HEADERS([fcntl.h netdb.h netinet/in.h stdlib.h string.h sys/socket.h unistd.h time.h ]) 63 | 64 | # Checks for typedefs, structures, and compiler characteristics. 65 | AC_C_CONST 66 | 67 | # Checks for library functions. 68 | AC_FUNC_FORK 69 | AC_FUNC_MALLOC 70 | AC_CHECK_FUNCS([gethostbyname memset socket timer_create timer_settime]) 71 | 72 | AC_ARG_ENABLE(debug, 73 | AC_HELP_STRING([--enable-debug], [enable debugging: yes|no (default=no)]), 74 | [enable_debug=$enableval], 75 | [enable_debug="no"]) 76 | 77 | AC_ARG_ENABLE(fail_on_warning, 78 | AC_HELP_STRING([--enable-fail-on-warning], 79 | [build fails on warnings if enabled [[default=no]]]), 80 | fail_on_warning=$enableval, 81 | fail_on_warning=no) 82 | if test "x$fail_on_warning" != "xno"; then 83 | WERROR="-Werror" 84 | fi 85 | 86 | if test x${enable_debug} = xno; then 87 | AC_DEFINE([NDEBUG], 1, "Disable assertions") 88 | fi 89 | 90 | AC_SUBST(AM_CFLAGS, "$AM_CFLAGS -g -Wall ${WERROR} foreign") 91 | 92 | 93 | AC_CONFIG_FILES(Makefile 94 | src/Makefile) 95 | AC_OUTPUT 96 | -------------------------------------------------------------------------------- /include/hashtable_private.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2002, 2004 Christopher Clark */ 2 | /* $Id: hashtable_private.h 564 2008-08-01 16:59:25Z mtm $ */ 3 | 4 | #ifndef __HASHTABLE_PRIVATE_CWC22_H__ 5 | #define __HASHTABLE_PRIVATE_CWC22_H__ 6 | 7 | #include "hashtable.h" 8 | 9 | /*****************************************************************************/ 10 | struct entry 11 | { 12 | void *k, *v; 13 | unsigned int h; 14 | struct entry *next; 15 | }; 16 | 17 | struct hashtable { 18 | unsigned int tablelength; 19 | struct entry **table; 20 | unsigned int entrycount; 21 | unsigned int loadlimit; 22 | unsigned int primeindex; 23 | unsigned int (*hashfn) (void *k); 24 | int (*eqfn) (void *k1, void *k2); 25 | }; 26 | 27 | /*****************************************************************************/ 28 | unsigned int 29 | hash(struct hashtable *h, void *k); 30 | 31 | /*****************************************************************************/ 32 | /* indexFor */ 33 | static inline unsigned int 34 | indexFor(unsigned int tablelength, unsigned int hashvalue) { 35 | return (hashvalue % tablelength); 36 | }; 37 | 38 | /* Only works if tablelength == 2^N */ 39 | /*static inline unsigned int 40 | indexFor(unsigned int tablelength, unsigned int hashvalue) 41 | { 42 | return (hashvalue & (tablelength - 1u)); 43 | } 44 | */ 45 | 46 | /*****************************************************************************/ 47 | #define freekey(X) free(X) 48 | /*define freekey(X) ; */ 49 | 50 | 51 | /*****************************************************************************/ 52 | 53 | #endif /* __HASHTABLE_PRIVATE_CWC22_H__*/ 54 | 55 | /* 56 | * Copyright (c) 2002, Christopher Clark 57 | * All rights reserved. 58 | * 59 | * Redistribution and use in source and binary forms, with or without 60 | * modification, are permitted provided that the following conditions 61 | * are met: 62 | * 63 | * * Redistributions of source code must retain the above copyright 64 | * notice, this list of conditions and the following disclaimer. 65 | * 66 | * * Redistributions in binary form must reproduce the above copyright 67 | * notice, this list of conditions and the following disclaimer in the 68 | * documentation and/or other materials provided with the distribution. 69 | * 70 | * * Neither the name of the original author; nor the names of any contributors 71 | * may be used to endorse or promote products derived from this software 72 | * without specific prior written permission. 73 | * 74 | * 75 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 76 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 77 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 78 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 79 | * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 80 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 81 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 82 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 83 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 84 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 85 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 86 | */ 87 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 2 | PEPsal: A TCP Performance Enhancing Proxy for satellite links 3 | -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 4 | 5 | Update 2024/08/29 6 | -=-=-=-=-=-=-=-=- 7 | After many years of inactivity, this repository is now marked as archived. 8 | A more recent branch is currently actively developed and maintained by the 9 | French national space agency (CNES), in compliance with the original license 10 | and goals of the project. 11 | 12 | Please refer to their publicly available repository here: https://github.com/CNES/pepsal/ 13 | 14 | -=-=-=-=-=-=-=-=- 15 | 16 | PEPsal is a multi layer proxy. At the network level, it uses netfilter to intercept those 17 | connections that would involve a satellite links and "steals" the TCP SYN packet in 18 | the three-way handshake phase of a TCP connection, then pretends to be the other side of that 19 | connection, and initiate a new connection to the real endpoint, using a userspace application 20 | that directly copy data between the two sockets. 21 | 22 | The PEPsal is classified as an integrated PEP, since it runs only on a single linux box that 23 | normally forwards packet between two or more networks, and can select which connections have to 24 | Please Refer to COPYING for license. 25 | be enhanced by means of netfilter. 26 | 27 | The application is written in C with the use of shared memory library and netfilter ipqueue 28 | library. It is multi-thread and multi-task. One process, the "queuer", 29 | communicate with the netfilter by the ipqueue library routines. It reads every syn packet in 30 | the queue, annotating the information on the two endpoints in a known zone of the shared memory, 31 | a connection array with a bitmap index for fast access, and then release the packet which 32 | continues its path through the netfilter chain. Just after that, our SYN packet is redirected 33 | by netfilter to tcp port 5000, where a TCP daemon, the "connection manager" 34 | is listening for it. The connecion is accepted by a dedicated "proxy server" 35 | process, which search in shared memory for the instance matching source address and TCP port 36 | of the host which has started the connection. 37 | Once the destination IP address and port have been found in the connection array, a new TCP 38 | connection is attempted to real destination. When both connections are established, the proxy 39 | spawns two little concurrent threads, each one reading from one TCP socket and writing all the 40 | data to the other one. When one of the two connections ends, the other socket is closed and the 41 | proxy process ends. 42 | 43 | PEPsal represents a valid solution for the degraded TCP performances when for example satellite 44 | link are involved. It does not require modifications on content servers or satellite receivers, 45 | it is sufficient to install a computer running PEPsal in a path which all poor performing TCP 46 | connection must pass through. It is designed to follow the advices given in IETF RFC3135, to 47 | implement a simple "TCP split" technique. The aim of PEPsal is to give a free 48 | alternative in the scenario of commercial "black-boxes" acting as performance 49 | enhancing proxies. 50 | 51 | 52 | 53 | Idea and Design : Carlo Caini , 54 | Rosario Firrincieli 55 | Daniele Lacamera 56 | 57 | Author : Daniele Lacamera 58 | 59 | Co-Author : Sergio Ammirata 60 | 61 | 62 | Please Refer to COPYING for license. 63 | 64 | 65 | -------------------------------------------------------------------------------- /src/pepsal.1: -------------------------------------------------------------------------------- 1 | .\" Copyright (c) 2006 Daniele Lacamera 2 | .\" 3 | .\" This is free documentation; you can redistribute it and/or 4 | .\" modify it under the terms of the GNU General Public License as 5 | .\" published by the Free Software Foundation; either version 2 of 6 | .\" the License, or (at your option) any later version. 7 | .\" 8 | .\" The GNU General Public License's references to "object code" 9 | .\" and "executables" are to be interpreted as the output of any 10 | .\" document formatting or typesetting system, including 11 | .\" intermediate and printed output. 12 | .\" 13 | .\" This manual is distributed in the hope that it will be useful, 14 | .\" but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | .\" GNU General Public License for more details. 17 | .\" 18 | .\" You should have received a copy of the GNU General Public 19 | .\" License along with this manual; if not, write to the Free 20 | .\" Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, 21 | .\" USA. 22 | 23 | .TH PEPSAL 1 "July 25, 2006" "PEPSaL Performance Enhancing Proxy" 24 | .SH NAME 25 | pepsal \- Performance Enhancing Proxy for Satellite Links 26 | .SH SYNOPSIS 27 | .B pepsal 28 | [ 29 | .B \-p 30 | .I portnum 31 | ] 32 | [ 33 | .B \-d 34 | ] 35 | [ 36 | .B \-v 37 | ] 38 | [ 39 | .B \-h 40 | ] 41 | [ 42 | .B \-a 43 | .I Address 44 | ] 45 | [ 46 | .B \-q 47 | .I Queuenum 48 | ] 49 | .br 50 | .SH DESCRIPTION 51 | A 52 | \fBPEPSaL\fP 53 | Is a Performance Enhancing Proxy for Satellite links. 54 | It realizes a typical RFC3135 "splitting" Tcp PEP, using the Linux 55 | netfilter package. 56 | 57 | A couple of rules must be added via 58 | .B iptables (8) 59 | in order to redirect all the TCP segments to a local port. 60 | 61 | Moreover, it uses the new NFQUEUE target to spoof the syn packets incoming 62 | from the clients. See the example iptables script for the default configuration. 63 | 64 | 65 | .SH OPTIONS 66 | .TP 67 | .B \-d 68 | Daemon mode: run pepsal in background 69 | .TP 70 | .B \-q "\fIQueuenum\fP" 71 | specify the netfilter queue to be used to spoof SYN packets incoming from the client network. 72 | The default value is "0". 73 | .TP 74 | .B \-a "\fIAddress\fP" 75 | Use the specified Address to bind tcp local socket (default: INADDR_ANY) 76 | .TP 77 | .B \-p "\fIPort\fP" 78 | Use the specified port to bind tcp local socket (default: 5000) 79 | .TP 80 | .B \-h 81 | get a brief help 82 | .TP 83 | .B \-v 84 | verbose mode. Useful for debugging. 85 | .TP 86 | .B \-l "\fILogfile\fP" 87 | Enable logging to local file information about proxies. 88 | .TP 89 | .B \-c "\fIMax_conn\fP" 90 | Set maximum number of simultaneous proxy connections (default: 2112, min: 128, max: 4096) 91 | .TP 92 | .B \-t "\fILifetime\fP" 93 | Set maximum lifetime for proxy connections (default: 5 * 3600 seconds = 5 hours) 94 | .TP 95 | .B \-g "\fGarbageCollectorInterval\fP" 96 | Set time interval between two consecutive run of the garbage collector that terminate closed/expired proxyes. 97 | .TP 98 | .B \-V 99 | show version and exit. 100 | .TP 101 | 102 | .SH BUGS 103 | None ;-) 104 | 105 | .SH SEE ALSO 106 | .BR iptables (8), 107 | .BR libipq (3), 108 | .br 109 | .SH AUTHORS 110 | PEPSaL is a project by Daniele Lacamera . Version "2" improvements added by: Dan Kruchinin . 111 | .br 112 | Idea and Design: Carlo Caini , Rosario Firrincieli and Daniele Lacamera 113 | .br 114 | Development effort and large scale testing environment by Sergio Ammirata 115 | Thanks to: Wojtek Sawasciuk for bugfixes and patches 116 | 117 | 118 | -------------------------------------------------------------------------------- /src/syntab.c: -------------------------------------------------------------------------------- 1 | /* 2 | * PEPsal : A Performance Enhancing Proxy for Satellite Links 3 | * 4 | * Copyleft Dan Kruchinin 2010 5 | * Copyleft Joaquin Muguerza 2016 6 | * See AUTHORS and COPYING before using this software. 7 | * 8 | * 9 | * 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "pepsal.h" 18 | #include "syntab.h" 19 | 20 | struct syn_table syntab; 21 | 22 | /* Bob Jenkin's MIX function */ 23 | #define BJ_MIX(a, b, c) \ 24 | do { \ 25 | a -= b; a -= c; a ^= (c>>13); \ 26 | b -= c; b -= a; b ^= (a<<8); \ 27 | c -= a; c -= b; c ^= (b>>13); \ 28 | a -= b; a -= c; a ^= (c>>12); \ 29 | b -= c; b -= a; b ^= (a<<16); \ 30 | c -= a; c -= b; c ^= (b>>5); \ 31 | a -= b; a -= c; a ^= (c>>3); \ 32 | b -= c; b -= a; b ^= (a<<10); \ 33 | c -= a; c -= b; c ^= (b>>15); \ 34 | } while (0) 35 | 36 | 37 | 38 | static unsigned int syntab_hashfunction(void *k) 39 | { 40 | struct syntab_key *sk = k; 41 | unsigned int a, b, key; 42 | 43 | key = sk->addr; 44 | a = sk->port; 45 | b = 0x9e3779b9; /* the golden ratio */ 46 | 47 | BJ_MIX(a, b, key); 48 | 49 | /* Robert Jenkins' 32 bit integer hash function */ 50 | key = (key + 0x7ed55d16) + (key << 12); 51 | key = (key ^ 0xc761c23c) ^ (key >> 19); 52 | key = (key + 0x165667b1) + (key << 5); 53 | key = (key + 0xd3a2646c) ^ (key << 9); 54 | key = (key + 0xfd7046c5) + (key << 3); 55 | key = (key ^ 0xb55a4f09) ^ (key >> 16); 56 | 57 | return key; 58 | } 59 | 60 | static int __keyeqfn(void *k1, void *k2) 61 | { 62 | return (memcmp(k1, k2, sizeof(struct syntab_key)) == 0); 63 | } 64 | 65 | int syntab_init(int num_conns) 66 | { 67 | int ret, hash_size; 68 | 69 | memset(&syntab, 0, sizeof(syntab)); 70 | hash_size = (num_conns * 25) / 100; /* ~25% of max number of connections */ 71 | syntab.hash = create_hashtable(hash_size, syntab_hashfunction, __keyeqfn); 72 | if (!syntab.hash) { 73 | ret = ENOMEM; 74 | goto err; 75 | } 76 | 77 | ret = pthread_rwlock_init(&syntab.lock, NULL); 78 | if (ret) { 79 | ret = errno; 80 | goto err_destroy_hash; 81 | } 82 | 83 | list_init_head(&syntab.conns); 84 | syntab.num_items = 0; 85 | 86 | return 0; 87 | 88 | err_destroy_hash: 89 | hashtable_destroy(syntab.hash, 0); 90 | err: 91 | errno = ret; 92 | return -1; 93 | } 94 | 95 | static __inline void syntab_make_key(struct syntab_key *key, 96 | int addr, unsigned short port) 97 | { 98 | memset(key, 0, sizeof(*key)); 99 | key->addr = addr; 100 | key->port = port; 101 | } 102 | 103 | void syntab_format_key(struct pep_proxy *proxy, struct syntab_key *key) 104 | { 105 | key->addr = proxy->src.addr; 106 | key->port = proxy->src.port; 107 | } 108 | 109 | struct pep_proxy *syntab_find(struct syntab_key *key) 110 | { 111 | return hashtable_search(syntab.hash, key); 112 | } 113 | 114 | int syntab_add(struct pep_proxy *proxy) 115 | { 116 | struct syntab_key *key; 117 | int ret; 118 | 119 | assert(proxy->status == PST_PENDING); 120 | key = calloc(sizeof(*key), 1); 121 | if (!key) { 122 | errno = ENOMEM; 123 | return -1; 124 | } 125 | 126 | syntab_make_key(key, proxy->src.addr, proxy->src.port); 127 | ret = hashtable_insert(syntab.hash, key, proxy); 128 | if (ret == 0) { 129 | free(key); 130 | return -1; 131 | } 132 | 133 | list_add2tail(&syntab.conns, &proxy->lnode); 134 | syntab.num_items++; 135 | 136 | return 0; 137 | } 138 | 139 | void syntab_delete(struct pep_proxy *proxy) 140 | { 141 | struct syntab_key key; 142 | 143 | syntab_make_key(&key, proxy->src.addr, proxy->src.port); 144 | hashtable_remove(syntab.hash, &key); 145 | list_del(&proxy->lnode); 146 | syntab.num_items--; 147 | } 148 | -------------------------------------------------------------------------------- /debian/pepsal.init: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ### BEGIN INIT INFO 3 | # Provides: pepsal 4 | # Required-Start: $local_fs $network $remote_fs $syslog 5 | # Required-Stop: $local_fs $network $remote_fs $syslog 6 | # Default-Start: 2 3 4 5 7 | # Default-Stop: 0 1 6 8 | # Short-Description: PEPSal Performance Enhancing Proxy 9 | # Description: PEPSal is an open-source TCP Performance Enhancing 10 | # Proxy (PEP) for satellite links. 11 | # 12 | ### END INIT INFO 13 | 14 | # Author: PEPSal Maintainers 15 | 16 | # Do NOT "set -e" 17 | 18 | # Read configuration variable file if it is present 19 | /etc/pepsal/pepsal-arguments 20 | [ -r /etc/pepsal/.pepsal.conf ] && . /etc/pepsal/.pepsal.conf 21 | 22 | 23 | # PATH should only include /usr/* if it runs after the mountnfs.sh script 24 | PATH=/sbin:/usr/sbin:/bin:/usr/bin 25 | DESC="daemon" 26 | NAME=pepsal 27 | DAEMON=/usr/bin/pepsal 28 | DAEMON_ARGS="-d" 29 | PIDFILE=/var/run/$NAME.pid 30 | SCRIPTNAME=/etc/init.d/$NAME 31 | 32 | # Exit if the package is not installed 33 | [ -x "$DAEMON" ] || exit 0 34 | 35 | # Set daemon args 36 | DAEMON_ARGS="${DAEMON_ARGS} ${fastopen} ${debug} -p ${port} -a ${ip_address}"\ 37 | " -c ${max_conns} -g ${gcc_interval} -l ${log_file} -t ${pending_lifetime}" 38 | 39 | # Load the VERBOSE setting and other rcS variables 40 | . /lib/init/vars.sh 41 | 42 | # Define LSB log_* functions. 43 | # Depend on lsb-base (>= 3.2-14) to ensure that this file is present 44 | # and status_of_proc is working. 45 | . /lib/lsb/init-functions 46 | 47 | # 48 | # Function that starts the daemon/service 49 | # 50 | do_start() 51 | { 52 | # Return 53 | # 0 if daemon has been started 54 | # 1 if daemon was already running 55 | # 2 if daemon could not be started 56 | start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ 57 | || return 1 58 | start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \ 59 | $DAEMON_ARGS \ 60 | || return 2 61 | # The above code will not work for interpreted scripts, use the next 62 | # six lines below instead (Ref: #643337, start-stop-daemon(8) ) 63 | #start-stop-daemon --start --quiet --pidfile $PIDFILE --startas $DAEMON \ 64 | # --name $NAME --test > /dev/null \ 65 | # || return 1 66 | #start-stop-daemon --start --quiet --pidfile $PIDFILE --startas $DAEMON \ 67 | # --name $NAME -- $DAEMON_ARGS \ 68 | # || return 2 69 | 70 | # Add code here, if necessary, that waits for the process to be ready 71 | # to handle requests from services started subsequently which depend 72 | # on this one. As a last resort, sleep for some time. 73 | } 74 | 75 | # 76 | # Function that stops the daemon/service 77 | # 78 | do_stop() 79 | { 80 | # Return 81 | # 0 if daemon has been stopped 82 | # 1 if daemon was already stopped 83 | # 2 if daemon could not be stopped 84 | # other if a failure occurred 85 | start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME 86 | RETVAL="$?" 87 | [ "$RETVAL" = 2 ] && return 2 88 | # Wait for children to finish too if this is a daemon that forks 89 | # and if the daemon is only ever run from this initscript. 90 | # If the above conditions are not satisfied then add some other code 91 | # that waits for the process to drop all resources that could be 92 | # needed by services started subsequently. A last resort is to 93 | # sleep for some time. 94 | start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON 95 | [ "$?" = 2 ] && return 2 96 | # Many daemons don't delete their pidfiles when they exit. 97 | rm -f $PIDFILE 98 | return "$RETVAL" 99 | } 100 | 101 | # 102 | # Function that sends a SIGHUP to the daemon/service 103 | # 104 | do_reload() { 105 | # 106 | # If the daemon can reload its configuration without 107 | # restarting (for example, when it is sent a SIGHUP), 108 | # then implement that here. 109 | # 110 | start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME 111 | return 0 112 | } 113 | 114 | case "$1" in 115 | start) 116 | [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" 117 | do_start 118 | case "$?" in 119 | 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 120 | 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; 121 | esac 122 | ;; 123 | stop) 124 | [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" 125 | do_stop 126 | case "$?" in 127 | 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 128 | 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; 129 | esac 130 | ;; 131 | status) 132 | status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? 133 | ;; 134 | #reload|force-reload) 135 | # 136 | # If do_reload() is not implemented then leave this commented out 137 | # and leave 'force-reload' as an alias for 'restart'. 138 | # 139 | #log_daemon_msg "Reloading $DESC" "$NAME" 140 | #do_reload 141 | #log_end_msg $? 142 | #;; 143 | restart|force-reload) 144 | # 145 | # If the "reload" option is implemented then remove the 146 | # 'force-reload' alias 147 | # 148 | log_daemon_msg "Restarting $DESC" "$NAME" 149 | do_stop 150 | case "$?" in 151 | 0|1) 152 | do_start 153 | case "$?" in 154 | 0) log_end_msg 0 ;; 155 | 1) log_end_msg 1 ;; # Old process is still running 156 | *) log_end_msg 1 ;; # Failed to start 157 | esac 158 | ;; 159 | *) 160 | # Failed to stop 161 | log_end_msg 1 162 | ;; 163 | esac 164 | ;; 165 | *) 166 | #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 167 | echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 168 | exit 3 169 | ;; 170 | esac 171 | 172 | : 173 | -------------------------------------------------------------------------------- /include/hashtable.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2002 Christopher Clark */ 2 | /* $Id: hashtable.h 564 2008-08-01 16:59:25Z mtm $ */ 3 | 4 | #ifndef __HASHTABLE_CWC22_H__ 5 | #define __HASHTABLE_CWC22_H__ 6 | 7 | struct hashtable; 8 | 9 | /* Example of use: 10 | * 11 | * struct hashtable *h; 12 | * struct some_key *k; 13 | * struct some_value *v; 14 | * 15 | * static unsigned int hash_from_key_fn( void *k ); 16 | * static int keys_equal_fn ( void *key1, void *key2 ); 17 | * 18 | * h = create_hashtable(16, hash_from_key_fn, keys_equal_fn); 19 | * k = (struct some_key *) malloc(sizeof(struct some_key)); 20 | * v = (struct some_value *) malloc(sizeof(struct some_value)); 21 | * 22 | * (initialise k and v to suitable values) 23 | * 24 | * if (! hashtable_insert(h,k,v) ) 25 | * { exit(-1); } 26 | * 27 | * if (NULL == (found = hashtable_search(h,k) )) 28 | * { printf("not found!"); } 29 | * 30 | * if (NULL == (found = hashtable_remove(h,k) )) 31 | * { printf("Not found\n"); } 32 | * 33 | */ 34 | 35 | /* Macros may be used to define type-safe(r) hashtable access functions, with 36 | * methods specialized to take known key and value types as parameters. 37 | * 38 | * Example: 39 | * 40 | * Insert this at the start of your file: 41 | * 42 | * DEFINE_HASHTABLE_INSERT(insert_some, struct some_key, struct some_value); 43 | * DEFINE_HASHTABLE_SEARCH(search_some, struct some_key, struct some_value); 44 | * DEFINE_HASHTABLE_REMOVE(remove_some, struct some_key, struct some_value); 45 | * 46 | * This defines the functions 'insert_some', 'search_some' and 'remove_some'. 47 | * These operate just like hashtable_insert etc., with the same parameters, 48 | * but their function signatures have 'struct some_key *' rather than 49 | * 'void *', and hence can generate compile time errors if your program is 50 | * supplying incorrect data as a key (and similarly for value). 51 | * 52 | * Note that the hash and key equality functions passed to create_hashtable 53 | * still take 'void *' parameters instead of 'some key *'. This shouldn't be 54 | * a difficult issue as they're only defined and passed once, and the other 55 | * functions will ensure that only valid keys are supplied to them. 56 | * 57 | * The cost for this checking is increased code size and runtime overhead 58 | * - if performance is important, it may be worth switching back to the 59 | * unsafe methods once your program has been debugged with the safe methods. 60 | * This just requires switching to some simple alternative defines - eg: 61 | * #define insert_some hashtable_insert 62 | * 63 | */ 64 | 65 | /***************************************************************************** 66 | * create_hashtable 67 | 68 | * @name create_hashtable 69 | * @param minsize minimum initial size of hashtable 70 | * @param hashfunction function for hashing keys 71 | * @param key_eq_fn function for determining key equality 72 | * @return newly created hashtable or NULL on failure 73 | */ 74 | 75 | struct hashtable * 76 | create_hashtable(unsigned int minsize, 77 | unsigned int (*hashfunction) (void*), 78 | int (*key_eq_fn) (void*,void*)); 79 | 80 | /***************************************************************************** 81 | * hashtable_insert 82 | 83 | * @name hashtable_insert 84 | * @param h the hashtable to insert into 85 | * @param k the key - hashtable claims ownership and will free on removal 86 | * @param v the value - does not claim ownership 87 | * @return non-zero for successful insertion 88 | * 89 | * This function will cause the table to expand if the insertion would take 90 | * the ratio of entries to table size over the maximum load factor. 91 | * 92 | * This function does not check for repeated insertions with a duplicate key. 93 | * The value returned when using a duplicate key is undefined -- when 94 | * the hashtable changes size, the order of retrieval of duplicate key 95 | * entries is reversed. 96 | * If in doubt, remove before insert. 97 | */ 98 | 99 | int 100 | hashtable_insert(struct hashtable *h, void *k, void *v); 101 | 102 | #define DEFINE_HASHTABLE_INSERT(fnname, keytype, valuetype) \ 103 | int fnname (struct hashtable *h, keytype *k, valuetype *v) \ 104 | { \ 105 | return hashtable_insert(h,k,v); \ 106 | } 107 | 108 | /***************************************************************************** 109 | * hashtable_search 110 | 111 | * @name hashtable_search 112 | * @param h the hashtable to search 113 | * @param k the key to search for - does not claim ownership 114 | * @return the value associated with the key, or NULL if none found 115 | */ 116 | 117 | void * 118 | hashtable_search(struct hashtable *h, void *k); 119 | 120 | #define DEFINE_HASHTABLE_SEARCH(fnname, keytype, valuetype) \ 121 | valuetype * fnname (struct hashtable *h, keytype *k) \ 122 | { \ 123 | return (valuetype *) (hashtable_search(h,k)); \ 124 | } 125 | 126 | /***************************************************************************** 127 | * hashtable_remove 128 | 129 | * @name hashtable_remove 130 | * @param h the hashtable to remove the item from 131 | * @param k the key to search for - does not claim ownership 132 | * @return the value associated with the key, or NULL if none found 133 | */ 134 | 135 | void * /* returns value */ 136 | hashtable_remove(struct hashtable *h, void *k); 137 | 138 | #define DEFINE_HASHTABLE_REMOVE(fnname, keytype, valuetype) \ 139 | valuetype * fnname (struct hashtable *h, keytype *k) \ 140 | { \ 141 | return (valuetype *) (hashtable_remove(h,k)); \ 142 | } 143 | 144 | 145 | /***************************************************************************** 146 | * hashtable_count 147 | 148 | * @name hashtable_count 149 | * @param h the hashtable 150 | * @return the number of items stored in the hashtable 151 | */ 152 | unsigned int 153 | hashtable_count(struct hashtable *h); 154 | 155 | 156 | /***************************************************************************** 157 | * hashtable_destroy 158 | 159 | * @name hashtable_destroy 160 | * @param h the hashtable 161 | * @param free_values whether to call 'free' on the remaining values 162 | */ 163 | 164 | void 165 | hashtable_destroy(struct hashtable *h, int free_values); 166 | 167 | #endif /* __HASHTABLE_CWC22_H__ */ 168 | 169 | /* 170 | * Copyright (c) 2002, Christopher Clark 171 | * All rights reserved. 172 | * 173 | * Redistribution and use in source and binary forms, with or without 174 | * modification, are permitted provided that the following conditions 175 | * are met: 176 | * 177 | * * Redistributions of source code must retain the above copyright 178 | * notice, this list of conditions and the following disclaimer. 179 | * 180 | * * Redistributions in binary form must reproduce the above copyright 181 | * notice, this list of conditions and the following disclaimer in the 182 | * documentation and/or other materials provided with the distribution. 183 | * 184 | * * Neither the name of the original author; nor the names of any contributors 185 | * may be used to endorse or promote products derived from this software 186 | * without specific prior written permission. 187 | * 188 | * 189 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 190 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 191 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 192 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 193 | * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 194 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 195 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 196 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 197 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 198 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 199 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 200 | */ 201 | -------------------------------------------------------------------------------- /src/hashtable.c: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2004 Christopher Clark */ 2 | /* $Id: hashtable.c 564 2008-08-01 16:59:25Z mtm $ */ 3 | 4 | #include "hashtable.h" 5 | #include "hashtable_private.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | /* 12 | Credit for primes table: Aaron Krowne 13 | http://br.endernet.org/~akrowne/ 14 | http://planetmath.org/encyclopedia/GoodHashTablePrimes.html 15 | */ 16 | static const unsigned int primes[] = { 17 | 53, 97, 193, 389, 18 | 769, 1543, 3079, 6151, 19 | 12289, 24593, 49157, 98317, 20 | 196613, 393241, 786433, 1572869, 21 | 3145739, 6291469, 12582917, 25165843, 22 | 50331653, 100663319, 201326611, 402653189, 23 | 805306457, 1610612741 24 | }; 25 | const unsigned int prime_table_length = sizeof(primes)/sizeof(primes[0]); 26 | const float max_load_factor = 0.65; 27 | 28 | /*****************************************************************************/ 29 | struct hashtable * 30 | create_hashtable(unsigned int minsize, 31 | unsigned int (*hashf) (void*), 32 | int (*eqf) (void*,void*)) 33 | { 34 | struct hashtable *h; 35 | unsigned int pindex, size = primes[0]; 36 | /* Check requested hashtable isn't too large */ 37 | if (minsize > (1u << 30)) return NULL; 38 | /* Enforce size as prime */ 39 | for (pindex=0; pindex < prime_table_length; pindex++) { 40 | if (primes[pindex] > minsize) { size = primes[pindex]; break; } 41 | } 42 | h = (struct hashtable *)malloc(sizeof(struct hashtable)); 43 | if (NULL == h) return NULL; /*oom*/ 44 | h->table = (struct entry **)malloc(sizeof(struct entry*) * size); 45 | if (NULL == h->table) { free(h); return NULL; } /*oom*/ 46 | memset(h->table, 0, size * sizeof(struct entry *)); 47 | h->tablelength = size; 48 | h->primeindex = pindex; 49 | h->entrycount = 0; 50 | h->hashfn = hashf; 51 | h->eqfn = eqf; 52 | h->loadlimit = (unsigned int) ceil(size * max_load_factor); 53 | return h; 54 | } 55 | 56 | /*****************************************************************************/ 57 | unsigned int 58 | hash(struct hashtable *h, void *k) 59 | { 60 | /* Aim to protect against poor hash functions by adding logic here 61 | * - logic taken from java 1.4 hashtable source */ 62 | unsigned int i = h->hashfn(k); 63 | #if 0 64 | i += ~(i << 9); 65 | i ^= ((i >> 14) | (i << 18)); /* >>> */ 66 | i += (i << 4); 67 | i ^= ((i >> 10) | (i << 22)); /* >>> */ 68 | #endif 69 | return i; 70 | } 71 | 72 | /*****************************************************************************/ 73 | static int 74 | hashtable_expand(struct hashtable *h) 75 | { 76 | /* Double the size of the table to accomodate more entries */ 77 | struct entry **newtable; 78 | struct entry *e; 79 | struct entry **pE; 80 | unsigned int newsize, i, hindex; 81 | /* Check we're not hitting max capacity */ 82 | if (h->primeindex == (prime_table_length - 1)) return 0; 83 | newsize = primes[++(h->primeindex)]; 84 | 85 | newtable = (struct entry **)malloc(sizeof(struct entry*) * newsize); 86 | if (NULL != newtable) 87 | { 88 | memset(newtable, 0, newsize * sizeof(struct entry *)); 89 | /* This algorithm is not 'stable'. ie. it reverses the list 90 | * when it transfers entries between the tables */ 91 | for (i = 0; i < h->tablelength; i++) { 92 | while (NULL != (e = h->table[i])) { 93 | h->table[i] = e->next; 94 | hindex = indexFor(newsize,e->h); 95 | e->next = newtable[hindex]; 96 | newtable[hindex] = e; 97 | } 98 | } 99 | free(h->table); 100 | h->table = newtable; 101 | } 102 | /* Plan B: realloc instead */ 103 | else 104 | { 105 | newtable = (struct entry **) 106 | realloc(h->table, newsize * sizeof(struct entry *)); 107 | if (NULL == newtable) { (h->primeindex)--; return 0; } 108 | h->table = newtable; 109 | memset(newtable[h->tablelength], 0, newsize - h->tablelength); 110 | for (i = 0; i < h->tablelength; i++) { 111 | for (pE = &(newtable[i]), e = *pE; e != NULL; e = *pE) { 112 | hindex = indexFor(newsize,e->h); 113 | if (hindex == i) 114 | { 115 | pE = &(e->next); 116 | } 117 | else 118 | { 119 | *pE = e->next; 120 | e->next = newtable[hindex]; 121 | newtable[hindex] = e; 122 | } 123 | } 124 | } 125 | } 126 | h->tablelength = newsize; 127 | h->loadlimit = (unsigned int) ceil(newsize * max_load_factor); 128 | return -1; 129 | } 130 | 131 | /*****************************************************************************/ 132 | unsigned int 133 | hashtable_count(struct hashtable *h) 134 | { 135 | return h->entrycount; 136 | } 137 | 138 | /*****************************************************************************/ 139 | int 140 | hashtable_insert(struct hashtable *h, void *k, void *v) 141 | { 142 | /* This method allows duplicate keys - but they shouldn't be used */ 143 | unsigned int hindex; 144 | struct entry *e; 145 | if (++(h->entrycount) > h->loadlimit) 146 | { 147 | /* Ignore the return value. If expand fails, we should 148 | * still try cramming just this value into the existing table 149 | * -- we may not have memory for a larger table, but one more 150 | * element may be ok. Next time we insert, we'll try expanding again.*/ 151 | hashtable_expand(h); 152 | } 153 | e = (struct entry *)malloc(sizeof(struct entry)); 154 | if (NULL == e) { --(h->entrycount); return 0; } /*oom*/ 155 | e->h = hash(h,k); 156 | hindex = indexFor(h->tablelength,e->h); 157 | e->k = k; 158 | e->v = v; 159 | e->next = h->table[hindex]; 160 | h->table[hindex] = e; 161 | return -1; 162 | } 163 | 164 | /*****************************************************************************/ 165 | void * /* returns value associated with key */ 166 | hashtable_search(struct hashtable *h, void *k) 167 | { 168 | struct entry *e; 169 | unsigned int hashvalue, hindex; 170 | hashvalue = hash(h,k); 171 | hindex = indexFor(h->tablelength,hashvalue); 172 | e = h->table[hindex]; 173 | while (NULL != e) 174 | { 175 | /* Check hash value to short circuit heavier comparison */ 176 | if ((hashvalue == e->h) && (h->eqfn(k, e->k))) return e->v; 177 | e = e->next; 178 | } 179 | return NULL; 180 | } 181 | 182 | /*****************************************************************************/ 183 | void * /* returns value associated with key */ 184 | hashtable_remove(struct hashtable *h, void *k) 185 | { 186 | /* TODO: consider compacting the table when the load factor drops enough, 187 | * or provide a 'compact' method. */ 188 | 189 | struct entry *e; 190 | struct entry **pE; 191 | void *v; 192 | unsigned int hashvalue, hindex; 193 | 194 | hashvalue = hash(h,k); 195 | hindex = indexFor(h->tablelength,hash(h,k)); 196 | pE = &(h->table[hindex]); 197 | e = *pE; 198 | while (NULL != e) 199 | { 200 | /* Check hash value to short circuit heavier comparison */ 201 | if ((hashvalue == e->h) && (h->eqfn(k, e->k))) 202 | { 203 | *pE = e->next; 204 | h->entrycount--; 205 | v = e->v; 206 | freekey(e->k); 207 | free(e); 208 | return v; 209 | } 210 | pE = &(e->next); 211 | e = e->next; 212 | } 213 | return NULL; 214 | } 215 | 216 | /*****************************************************************************/ 217 | /* destroy */ 218 | void 219 | hashtable_destroy(struct hashtable *h, int free_values) 220 | { 221 | unsigned int i; 222 | struct entry *e, *f; 223 | struct entry **table = h->table; 224 | if (free_values) 225 | { 226 | for (i = 0; i < h->tablelength; i++) 227 | { 228 | e = table[i]; 229 | while (NULL != e) 230 | { f = e; e = e->next; freekey(f->k); free(f->v); free(f); } 231 | } 232 | } 233 | else 234 | { 235 | for (i = 0; i < h->tablelength; i++) 236 | { 237 | e = table[i]; 238 | while (NULL != e) 239 | { f = e; e = e->next; freekey(f->k); free(f); } 240 | } 241 | } 242 | free(h->table); 243 | free(h); 244 | } 245 | 246 | /* 247 | * Copyright (c) 2002, Christopher Clark 248 | * All rights reserved. 249 | * 250 | * Redistribution and use in source and binary forms, with or without 251 | * modification, are permitted provided that the following conditions 252 | * are met: 253 | * 254 | * * Redistributions of source code must retain the above copyright 255 | * notice, this list of conditions and the following disclaimer. 256 | * 257 | * * Redistributions in binary form must reproduce the above copyright 258 | * notice, this list of conditions and the following disclaimer in the 259 | * documentation and/or other materials provided with the distribution. 260 | * 261 | * * Neither the name of the original author; nor the names of any contributors 262 | * may be used to endorse or promote products derived from this software 263 | * without specific prior written permission. 264 | * 265 | * 266 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 267 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 268 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 269 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 270 | * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 271 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 272 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 273 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 274 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 275 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 276 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 277 | */ 278 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | Installation Instructions 2 | ************************* 3 | 4 | Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, 5 | 2006, 2007, 2008, 2009 Free Software Foundation, Inc. 6 | 7 | This file is free documentation; the Free Software Foundation gives 8 | unlimited permission to copy, distribute and modify it. 9 | 10 | Basic Installation 11 | ================== 12 | 13 | Briefly, the shell commands `./configure; make; make install' should 14 | configure, build, and install this package. The following 15 | more-detailed instructions are generic; see the `README' file for 16 | instructions specific to this package. 17 | 18 | The `configure' shell script attempts to guess correct values for 19 | various system-dependent variables used during compilation. It uses 20 | those values to create a `Makefile' in each directory of the package. 21 | It may also create one or more `.h' files containing system-dependent 22 | definitions. Finally, it creates a shell script `config.status' that 23 | you can run in the future to recreate the current configuration, and a 24 | file `config.log' containing compiler output (useful mainly for 25 | debugging `configure'). 26 | 27 | It can also use an optional file (typically called `config.cache' 28 | and enabled with `--cache-file=config.cache' or simply `-C') that saves 29 | the results of its tests to speed up reconfiguring. Caching is 30 | disabled by default to prevent problems with accidental use of stale 31 | cache files. 32 | 33 | If you need to do unusual things to compile the package, please try 34 | to figure out how `configure' could check whether to do them, and mail 35 | diffs or instructions to the address given in the `README' so they can 36 | be considered for the next release. If you are using the cache, and at 37 | some point `config.cache' contains results you don't want to keep, you 38 | may remove or edit it. 39 | 40 | The file `configure.ac' (or `configure.in') is used to create 41 | `configure' by a program called `autoconf'. You need `configure.ac' if 42 | you want to change it or regenerate `configure' using a newer version 43 | of `autoconf'. 44 | 45 | The simplest way to compile this package is: 46 | 47 | 1. `cd' to the directory containing the package's source code and type 48 | `./configure' to configure the package for your system. 49 | 50 | Running `configure' might take a while. While running, it prints 51 | some messages telling which features it is checking for. 52 | 53 | 2. Type `make' to compile the package. 54 | 55 | 3. Optionally, type `make check' to run any self-tests that come with 56 | the package. 57 | 58 | 4. Type `make install' to install the programs and any data files and 59 | documentation. 60 | 61 | 5. You can remove the program binaries and object files from the 62 | source code directory by typing `make clean'. To also remove the 63 | files that `configure' created (so you can compile the package for 64 | a different kind of computer), type `make distclean'. There is 65 | also a `make maintainer-clean' target, but that is intended mainly 66 | for the package's developers. If you use it, you may have to get 67 | all sorts of other programs in order to regenerate files that came 68 | with the distribution. 69 | 70 | 6. Often, you can also type `make uninstall' to remove the installed 71 | files again. 72 | 73 | Compilers and Options 74 | ===================== 75 | 76 | Some systems require unusual options for compilation or linking that 77 | the `configure' script does not know about. Run `./configure --help' 78 | for details on some of the pertinent environment variables. 79 | 80 | You can give `configure' initial values for configuration parameters 81 | by setting variables in the command line or in the environment. Here 82 | is an example: 83 | 84 | ./configure CC=c99 CFLAGS=-g LIBS=-lposix 85 | 86 | *Note Defining Variables::, for more details. 87 | 88 | Compiling For Multiple Architectures 89 | ==================================== 90 | 91 | You can compile the package for more than one kind of computer at the 92 | same time, by placing the object files for each architecture in their 93 | own directory. To do this, you can use GNU `make'. `cd' to the 94 | directory where you want the object files and executables to go and run 95 | the `configure' script. `configure' automatically checks for the 96 | source code in the directory that `configure' is in and in `..'. 97 | 98 | With a non-GNU `make', it is safer to compile the package for one 99 | architecture at a time in the source code directory. After you have 100 | installed the package for one architecture, use `make distclean' before 101 | reconfiguring for another architecture. 102 | 103 | On MacOS X 10.5 and later systems, you can create libraries and 104 | executables that work on multiple system types--known as "fat" or 105 | "universal" binaries--by specifying multiple `-arch' options to the 106 | compiler but only a single `-arch' option to the preprocessor. Like 107 | this: 108 | 109 | ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ 110 | CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ 111 | CPP="gcc -E" CXXCPP="g++ -E" 112 | 113 | This is not guaranteed to produce working output in all cases, you 114 | may have to build one architecture at a time and combine the results 115 | using the `lipo' tool if you have problems. 116 | 117 | Installation Names 118 | ================== 119 | 120 | By default, `make install' installs the package's commands under 121 | `/usr/local/bin', include files under `/usr/local/include', etc. You 122 | can specify an installation prefix other than `/usr/local' by giving 123 | `configure' the option `--prefix=PREFIX'. 124 | 125 | You can specify separate installation prefixes for 126 | architecture-specific files and architecture-independent files. If you 127 | pass the option `--exec-prefix=PREFIX' to `configure', the package uses 128 | PREFIX as the prefix for installing programs and libraries. 129 | Documentation and other data files still use the regular prefix. 130 | 131 | In addition, if you use an unusual directory layout you can give 132 | options like `--bindir=DIR' to specify different values for particular 133 | kinds of files. Run `configure --help' for a list of the directories 134 | you can set and what kinds of files go in them. 135 | 136 | If the package supports it, you can cause programs to be installed 137 | with an extra prefix or suffix on their names by giving `configure' the 138 | option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. 139 | 140 | Optional Features 141 | ================= 142 | 143 | Some packages pay attention to `--enable-FEATURE' options to 144 | `configure', where FEATURE indicates an optional part of the package. 145 | They may also pay attention to `--with-PACKAGE' options, where PACKAGE 146 | is something like `gnu-as' or `x' (for the X Window System). The 147 | `README' should mention any `--enable-' and `--with-' options that the 148 | package recognizes. 149 | 150 | For packages that use the X Window System, `configure' can usually 151 | find the X include and library files automatically, but if it doesn't, 152 | you can use the `configure' options `--x-includes=DIR' and 153 | `--x-libraries=DIR' to specify their locations. 154 | 155 | Particular systems 156 | ================== 157 | 158 | On HP-UX, the default C compiler is not ANSI C compatible. If GNU 159 | CC is not installed, it is recommended to use the following options in 160 | order to use an ANSI C compiler: 161 | 162 | ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" 163 | 164 | and if that doesn't work, install pre-built binaries of GCC for HP-UX. 165 | 166 | On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot 167 | parse its `' header file. The option `-nodtk' can be used as 168 | a workaround. If GNU CC is not installed, it is therefore recommended 169 | to try 170 | 171 | ./configure CC="cc" 172 | 173 | and if that doesn't work, try 174 | 175 | ./configure CC="cc -nodtk" 176 | 177 | On Solaris, don't put `/usr/ucb' early in your `PATH'. This 178 | directory contains several dysfunctional programs; working variants of 179 | these programs are available in `/usr/bin'. So, if you need `/usr/ucb' 180 | in your `PATH', put it _after_ `/usr/bin'. 181 | 182 | On Haiku, software installed for all users goes in `/boot/common', 183 | not `/usr/local'. It is recommended to use the following options: 184 | 185 | ./configure --prefix=/boot/common 186 | 187 | Specifying the System Type 188 | ========================== 189 | 190 | There may be some features `configure' cannot figure out 191 | automatically, but needs to determine by the type of machine the package 192 | will run on. Usually, assuming the package is built to be run on the 193 | _same_ architectures, `configure' can figure that out, but if it prints 194 | a message saying it cannot guess the machine type, give it the 195 | `--build=TYPE' option. TYPE can either be a short name for the system 196 | type, such as `sun4', or a canonical name which has the form: 197 | 198 | CPU-COMPANY-SYSTEM 199 | 200 | where SYSTEM can have one of these forms: 201 | 202 | OS 203 | KERNEL-OS 204 | 205 | See the file `config.sub' for the possible values of each field. If 206 | `config.sub' isn't included in this package, then this package doesn't 207 | need to know the machine type. 208 | 209 | If you are _building_ compiler tools for cross-compiling, you should 210 | use the option `--target=TYPE' to select the type of system they will 211 | produce code for. 212 | 213 | If you want to _use_ a cross compiler, that generates code for a 214 | platform different from the build platform, you should specify the 215 | "host" platform (i.e., that on which the generated programs will 216 | eventually be run) with `--host=TYPE'. 217 | 218 | Sharing Defaults 219 | ================ 220 | 221 | If you want to set default values for `configure' scripts to share, 222 | you can create a site shell script called `config.site' that gives 223 | default values for variables like `CC', `cache_file', and `prefix'. 224 | `configure' looks for `PREFIX/share/config.site' if it exists, then 225 | `PREFIX/etc/config.site' if it exists. Or, you can set the 226 | `CONFIG_SITE' environment variable to the location of the site script. 227 | A warning: not all `configure' scripts look for a site script. 228 | 229 | Defining Variables 230 | ================== 231 | 232 | Variables not defined in a site shell script can be set in the 233 | environment passed to `configure'. However, some packages may run 234 | configure again during the build, and the customized values of these 235 | variables may be lost. In order to avoid this problem, you should set 236 | them in the `configure' command line, using `VAR=value'. For example: 237 | 238 | ./configure CC=/usr/local2/bin/gcc 239 | 240 | causes the specified `gcc' to be used as the C compiler (unless it is 241 | overridden in the site shell script). 242 | 243 | Unfortunately, this technique does not work for `CONFIG_SHELL' due to 244 | an Autoconf bug. Until the bug is fixed you can use this workaround: 245 | 246 | CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash 247 | 248 | `configure' Invocation 249 | ====================== 250 | 251 | `configure' recognizes the following options to control how it 252 | operates. 253 | 254 | `--help' 255 | `-h' 256 | Print a summary of all of the options to `configure', and exit. 257 | 258 | `--help=short' 259 | `--help=recursive' 260 | Print a summary of the options unique to this package's 261 | `configure', and exit. The `short' variant lists options used 262 | only in the top level, while the `recursive' variant lists options 263 | also present in any nested packages. 264 | 265 | `--version' 266 | `-V' 267 | Print the version of Autoconf used to generate the `configure' 268 | script, and exit. 269 | 270 | `--cache-file=FILE' 271 | Enable the cache: use and save the results of the tests in FILE, 272 | traditionally `config.cache'. FILE defaults to `/dev/null' to 273 | disable caching. 274 | 275 | `--config-cache' 276 | `-C' 277 | Alias for `--cache-file=config.cache'. 278 | 279 | `--quiet' 280 | `--silent' 281 | `-q' 282 | Do not print messages saying which checks are being made. To 283 | suppress all normal output, redirect it to `/dev/null' (any error 284 | messages will still be shown). 285 | 286 | `--srcdir=DIR' 287 | Look for the package's source code in directory DIR. Usually 288 | `configure' can determine that directory automatically. 289 | 290 | `--prefix=DIR' 291 | Use DIR as the installation prefix. *Note Installation Names:: 292 | for more details, including other options available for fine-tuning 293 | the installation locations. 294 | 295 | `--no-create' 296 | `-n' 297 | Run the configure checks, but stop before creating any output 298 | files. 299 | 300 | `configure' also accepts some other, not widely useful, options. Run 301 | `configure --help' for more details. 302 | 303 | -------------------------------------------------------------------------------- /include/list.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This program is free software; you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License as published by 4 | * the Free Software Foundation; either version 2 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | * GNU General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program; if not, write to the Free Software 14 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 15 | * 02111-1307, USA. 16 | * 17 | * (c) Copyright 2008 Dan Kruchinin 18 | */ 19 | 20 | #ifndef __LIST_H__ 21 | #define __LIST_H__ 22 | 23 | #include 24 | #include "pepdefs.h" 25 | 26 | /** 27 | * @struct list_node 28 | * @brief List node 29 | */ 30 | struct list_node { 31 | struct list_node *next; 32 | struct list_node *prev; 33 | }; 34 | 35 | /** 36 | * @struct list_head 37 | * @brief List head 38 | * Actually struct list_head is the same as struct list_node, but 39 | * they have different types though. That was done to prevent 40 | * potentional errors(notice that list head is a stub, it's never 41 | * tied with any real data and it's used only to determine where list 42 | * starts and where it ends) 43 | */ 44 | struct list_head { 45 | struct list_node head; /**< Head element of the list */ 46 | }; 47 | 48 | 49 | /** 50 | * @def LIST_DEFINE(name) 51 | * @brief Define and initialize list head with name @a name 52 | * @param name - name of variable 53 | */ 54 | #define LIST_DEFINE(name) \ 55 | struct list_head (name) = LIST_INITIALIZE(name) 56 | 57 | /** 58 | * @def LIST_INITIALIZE 59 | * @brief Initialize list head. 60 | * @param name - list name 61 | */ 62 | #define LIST_INITIALIZE(name) \ 63 | { .head = { &(name).head, &(name).head } } 64 | 65 | #define list_node2head(node) \ 66 | ((struct list_head *)(node)) 67 | 68 | /** 69 | * @fn static __inline void list_init_head(struct list_head *lst) 70 | * @brief Initialize list head 71 | * @param lst - a pointer to list head. 72 | */ 73 | static __inline void list_init_head(struct list_head *lst) 74 | { 75 | lst->head.next = lst->head.prev = &lst->head; 76 | } 77 | 78 | /** 79 | * @fn static __inline void list_init_node(struct list_node *node) 80 | * @brief Initialize list node 81 | * @param node - a pointer to free(unattached from list) node. 82 | */ 83 | static __inline void list_init_node(struct list_node *node) 84 | { 85 | node->next = NULL; 86 | node->prev = NULL; 87 | } 88 | 89 | static __inline int list_node_next_isbound(struct list_node *node) 90 | { 91 | return (node->next != NULL); 92 | } 93 | 94 | static __inline int list_node_prev_isbound(struct list_node *node) 95 | { 96 | return (node->prev != NULL); 97 | } 98 | 99 | #define list_node_is_bound(node) \ 100 | (list_node_next_isbound(node) && list_node_prev_isbound(node)) 101 | 102 | /** 103 | * @def list_entry(lst, nptr) 104 | * @brief Get item that holds @a nptr node 105 | * @param list - A pointer to the list 106 | * @param nptr - A pointer to the node 107 | * @return A pointer to the object given node contains 108 | */ 109 | #define list_entry(node, type, member) \ 110 | container_of(node, type, member) 111 | 112 | /** 113 | * @def list_head(lst) 114 | * @brief Get head of the list 115 | * @param lst - a pointer to struct list_head 116 | * @return A pointer to header struct list_node 117 | */ 118 | #define list_head(lst) \ 119 | (&(lst)->head) 120 | 121 | /** 122 | * @def list_node_first(lst) 123 | * @brief Get list's first node 124 | * @param list - A pointer to the struct list_head 125 | * @return A pointer to the list first node 126 | */ 127 | #define list_node_first(lst) \ 128 | ((lst)->head.next) 129 | 130 | /** 131 | * @def list_node_last(lst) 132 | * @brief Get list's last node 133 | * @param list - A pointer to the struct list_head 134 | * @return A pointer to the list last node 135 | */ 136 | #define list_node_last(lst) \ 137 | ((lst)->head.prev) 138 | 139 | /** 140 | * @def list_add2head(lst, new) 141 | * @brief Add a node @a new to the head of the list 142 | * @param lst - A pointer to the list 143 | * @param new - A pointer to the list node 144 | */ 145 | #define list_add2head(lst, new) \ 146 | list_add_before(list_node_first(lst), new) 147 | 148 | /** 149 | * @def list_add2tail(lst, new) 150 | * @brief Add a node @a new to the tail of the list 151 | * @param lst - A pointer to the list 152 | * @param new - A pointer to node to add 153 | */ 154 | #define list_add2tail(lst, new) \ 155 | list_add_before(list_head(lst), new) 156 | 157 | /** 158 | * @def list_delfromhead(lst) 159 | * @brief Remove first element of the list 160 | * @param lst - A pointer to the list 161 | */ 162 | #define list_delfromhead(lst) \ 163 | list_del(list_node_first(lst)) 164 | 165 | /** 166 | * @def list_delfromtail(lst) 167 | * @brief Remove the last element of the list 168 | * @param list - A pointer to the list 169 | */ 170 | #define list_delfromtail(lst) \ 171 | list_del(list_node_last(lst)) 172 | 173 | /** 174 | * @def list_del(del) 175 | * @brief Remove node @a del from the list 176 | * @param del - A node to remove 177 | */ 178 | #define list_del(del) \ 179 | (list_del_range(del, del)) 180 | 181 | /** 182 | * @def list_add_before(before, new) 183 | * @param brefore - The node before which @a new will be inserted 184 | * @param new - A node to insert 185 | */ 186 | #define list_add_before(before, new) \ 187 | (list_add_range(new, new, (before)->prev, before)) 188 | 189 | /** 190 | * @def list_add_after(after, new) 191 | * @param after - The node after which a new one will be inserted 192 | * @param new - A node to insert 193 | */ 194 | #define list_add_after(after, new) \ 195 | (list_add_range(new, new, (after), (after)->next)) 196 | 197 | /** 198 | * @def list_move2head(to, from) 199 | * @brief Move all nodes from list @a from to the head of list @a to 200 | * @param to - destination list 201 | * @param from - source list 202 | */ 203 | #define list_move2head(to, from) \ 204 | (list_move(list_head(to), list_node_first(to), from)) 205 | 206 | /** 207 | * @def list_move2tail(to, from) 208 | * @brief Move all nodes from list @a from to the tail of list @a to 209 | * @param to - destination list 210 | * @param from - source list 211 | */ 212 | #define list_move2tail(to, from) \ 213 | (list_move(list_node_last(to), list_head(to), from)) 214 | 215 | /** 216 | * @def list_for_each(lst, liter) 217 | * @brief Iterate through each element of the list 218 | * @param lst - A pointer to list head 219 | * @param liter - A pointer to list which will be used for iteration 220 | */ 221 | #define list_for_each(lst, liter) \ 222 | for (liter = list_node_first(lst); \ 223 | (liter) != list_head(lst); (liter) = (liter)->next) 224 | 225 | /** 226 | * @def list_for_each_safe(lst, liter, save) 227 | * @brief Safe iteration through the list @a lst 228 | * 229 | * This iteration wouldn't be broken even if @a liter will be removed 230 | * from the list 231 | * 232 | * @param lst - A pointer to the list head 233 | * @param liter - A pointer to list node which will be used for iteration 234 | * @param save - The same 235 | */ 236 | #define list_for_each_safe(lst, liter, save) \ 237 | for (liter = list_node_first(lst), save = (liter)->next; \ 238 | (liter) != list_head(lst); (liter) = (save), \ 239 | (save) = (liter)->next) 240 | 241 | /** 242 | * @def list_for_each_entry(lst, iter, member) 243 | * @brief Iterate through each list node member 244 | * @param lst - a pointer list head 245 | * @param iter - a pointer to list entry using as iterator 246 | * @param member - name of list node member in the parent structure 247 | */ 248 | #define list_for_each_entry(lst, iter, type, member) \ 249 | for (iter = list_entry(list_node_first(lst), type, member); \ 250 | &iter->member != list_head(lst); \ 251 | iter = list_entry(iter->member.next, type, member)) 252 | 253 | 254 | /** 255 | * @fn static __inline int list_is_empty(list_t *list) 256 | * @brief Determines if list @a list is empty 257 | * @param list - A pointer to list to test 258 | * @return True if list is empty, false otherwise 259 | */ 260 | static __inline int list_is_empty(struct list_head *list) 261 | { 262 | return (list_node_first(list) == list_head(list)); 263 | } 264 | 265 | /** 266 | * @fn static __inline void list_add_range(struct list_node *first, 267 | * struct list_node *last, 268 | * struct list_node *prev, 269 | * struct list_node *next) 270 | * @brief Insert a range of nodes from @a frist to @a last after 271 | * @a prev and before @a next 272 | * @param first - first node of range 273 | * @param last - last node of range 274 | * @param prev - after this node a range will be inserted 275 | * @param next - before this node a range will be inserted 276 | */ 277 | static __inline void list_add_range(struct list_node *first, 278 | struct list_node *last, 279 | struct list_node *prev, 280 | struct list_node *next) 281 | { 282 | first->prev = prev; 283 | last->next = next; 284 | next->prev = last; 285 | prev->next = first; 286 | } 287 | 288 | /* for internal usage */ 289 | static __inline void __list_del_range(struct list_node *first, 290 | struct list_node *last) 291 | { 292 | first->prev->next = last->next; 293 | last->next->prev = first->prev; 294 | } 295 | 296 | /** 297 | * @fn static __inline list_del_range(struct list_node *first, 298 | * struct list_node *last) 299 | * @brief Delete nodes from @a first to @a last from list. 300 | * @param fist - first node to delete 301 | * @param last - last node to delete 302 | */ 303 | static __inline void list_del_range(struct list_node *first, 304 | struct list_node *last) 305 | { 306 | __list_del_range(first, last); 307 | first->prev = NULL; 308 | last->next = NULL; 309 | } 310 | 311 | /** 312 | * @fn static __inline void list_cut_sublist(struct list_node *first, 313 | * struct list_node *last) 314 | * @brief Cut a "sublist" started from @a first and ended with @a last 315 | * 316 | * A @e "sublist" is similar to ordinary list except it hasn't a head. 317 | * In other words it's a cyclic list in which all nodes are equitable. 318 | * 319 | * @param first - From this node sublist will be cutted 320 | * @param last - The last node in the cutted sequence 321 | */ 322 | static __inline void list_cut_sublist(struct list_node *first, 323 | struct list_node *last) 324 | { 325 | __list_del_range(first, last); 326 | first->prev = last; 327 | last->next = first; 328 | } 329 | 330 | /** 331 | * @fn static __inline void list_cut_head(struct list_head *head) 332 | * @brief Cut a head from the list and make a "sublist" 333 | * @param head - List's head that will be cutted. 334 | * @see list_cut_sublist 335 | * @see list_set_head 336 | */ 337 | static __inline void list_cut_head(struct list_head *head) 338 | { 339 | list_cut_sublist(list_node_first(head), list_node_last(head)); 340 | } 341 | 342 | /** 343 | * @fn static __inline void list_cut_head(struct list_head *head) 344 | * @brief Attach a head to the sublist @a cyclist 345 | * @param new_head - A head that will be attached 346 | * @param cyclist - "sublist" 347 | * @see list_cut_sublist 348 | * @see list_set_head 349 | */ 350 | static __inline void list_set_head(struct list_head *new_head, 351 | struct list_node *cyclist) 352 | { 353 | list_add_range(cyclist, cyclist->prev, 354 | list_node_first(new_head), list_node_last(new_head)); 355 | } 356 | 357 | /** 358 | * @fn static __inline void list_move(struct list_node *prev, 359 | * struct list_node *next, 360 | * struct list_head *from) 361 | * @brief Insert nodes of list @a from after @a prev and before @a next 362 | * @param prev - a node after which nodes of @a from will be inserted 363 | * @param next - a node before which nodes of @a from will be inserted 364 | */ 365 | static __inline void list_move(struct list_node *prev, 366 | struct list_node *next, 367 | struct list_head *from) 368 | { 369 | list_add_range(list_node_first(from), list_node_last(from), 370 | prev, next); 371 | list_init_head(from); 372 | } 373 | 374 | #endif /* __LIST_H__ */ 375 | 376 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | This program is Free Software, you can use and distribute it under the terms of the 2 | GNU/General Public License Version 2 reported here. 3 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 | 5 | GNU GENERAL PUBLIC LICENSE 6 | Version 2, June 1991 7 | 8 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 9 | 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 10 | Everyone is permitted to copy and distribute verbatim copies 11 | of this license document, but changing it is not allowed. 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your 16 | freedom to share and change it. By contrast, the GNU General Public 17 | License is intended to guarantee your freedom to share and change free 18 | software--to make sure the software is free for all its users. This 19 | General Public License applies to most of the Free Software 20 | Foundation's software and to any other program whose authors commit to 21 | using it. (Some other Free Software Foundation software is covered by 22 | the GNU Library General Public License instead.) You can apply it to 23 | your programs, too. 24 | 25 | When we speak of free software, we are referring to freedom, not 26 | price. Our General Public Licenses are designed to make sure that you 27 | have the freedom to distribute copies of free software (and charge for 28 | this service if you wish), that you receive source code or can get it 29 | if you want it, that you can change the software or use pieces of it 30 | in new free programs; and that you know you can do these things. 31 | 32 | To protect your rights, we need to make restrictions that forbid 33 | anyone to deny you these rights or to ask you to surrender the rights. 34 | These restrictions translate to certain responsibilities for you if you 35 | distribute copies of the software, or if you modify it. 36 | 37 | For example, if you distribute copies of such a program, whether 38 | gratis or for a fee, you must give the recipients all the rights that 39 | you have. You must make sure that they, too, receive or can get the 40 | source code. And you must show them these terms so they know their 41 | rights. 42 | 43 | We protect your rights with two steps: (1) copyright the software, and 44 | (2) offer you this license which gives you legal permission to copy, 45 | distribute and/or modify the software. 46 | 47 | Also, for each author's protection and ours, we want to make certain 48 | that everyone understands that there is no warranty for this free 49 | software. If the software is modified by someone else and passed on, we 50 | want its recipients to know that what they have is not the original, so 51 | that any problems introduced by others will not reflect on the original 52 | authors' reputations. 53 | 54 | Finally, any free program is threatened constantly by software 55 | patents. We wish to avoid the danger that redistributors of a free 56 | program will individually obtain patent licenses, in effect making the 57 | program proprietary. To prevent this, we have made it clear that any 58 | patent must be licensed for everyone's free use or not licensed at all. 59 | 60 | The precise terms and conditions for copying, distribution and 61 | modification follow. 62 | 63 | GNU GENERAL PUBLIC LICENSE 64 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 65 | 66 | 0. This License applies to any program or other work which contains 67 | a notice placed by the copyright holder saying it may be distributed 68 | under the terms of this General Public License. The "Program", below, 69 | refers to any such program or work, and a "work based on the Program" 70 | means either the Program or any derivative work under copyright law: 71 | that is to say, a work containing the Program or a portion of it, 72 | either verbatim or with modifications and/or translated into another 73 | language. (Hereinafter, translation is included without limitation in 74 | the term "modification".) Each licensee is addressed as "you". 75 | 76 | Activities other than copying, distribution and modification are not 77 | covered by this License; they are outside its scope. The act of 78 | running the Program is not restricted, and the output from the Program 79 | is covered only if its contents constitute a work based on the 80 | Program (independent of having been made by running the Program). 81 | Whether that is true depends on what the Program does. 82 | 83 | 1. You may copy and distribute verbatim copies of the Program's 84 | source code as you receive it, in any medium, provided that you 85 | conspicuously and appropriately publish on each copy an appropriate 86 | copyright notice and disclaimer of warranty; keep intact all the 87 | notices that refer to this License and to the absence of any warranty; 88 | and give any other recipients of the Program a copy of this License 89 | along with the Program. 90 | 91 | You may charge a fee for the physical act of transferring a copy, and 92 | you may at your option offer warranty protection in exchange for a fee. 93 | 94 | 2. You may modify your copy or copies of the Program or any portion 95 | of it, thus forming a work based on the Program, and copy and 96 | distribute such modifications or work under the terms of Section 1 97 | above, provided that you also meet all of these conditions: 98 | 99 | a) You must cause the modified files to carry prominent notices 100 | stating that you changed the files and the date of any change. 101 | 102 | b) You must cause any work that you distribute or publish, that in 103 | whole or in part contains or is derived from the Program or any 104 | part thereof, to be licensed as a whole at no charge to all third 105 | parties under the terms of this License. 106 | 107 | c) If the modified program normally reads commands interactively 108 | when run, you must cause it, when started running for such 109 | interactive use in the most ordinary way, to print or display an 110 | announcement including an appropriate copyright notice and a 111 | notice that there is no warranty (or else, saying that you provide 112 | a warranty) and that users may redistribute the program under 113 | these conditions, and telling the user how to view a copy of this 114 | License. (Exception: if the Program itself is interactive but 115 | does not normally print such an announcement, your work based on 116 | the Program is not required to print an announcement.) 117 | 118 | These requirements apply to the modified work as a whole. If 119 | identifiable sections of that work are not derived from the Program, 120 | and can be reasonably considered independent and separate works in 121 | themselves, then this License, and its terms, do not apply to those 122 | sections when you distribute them as separate works. But when you 123 | distribute the same sections as part of a whole which is a work based 124 | on the Program, the distribution of the whole must be on the terms of 125 | this License, whose permissions for other licensees extend to the 126 | entire whole, and thus to each and every part regardless of who wrote it. 127 | 128 | Thus, it is not the intent of this section to claim rights or contest 129 | your rights to work written entirely by you; rather, the intent is to 130 | exercise the right to control the distribution of derivative or 131 | collective works based on the Program. 132 | 133 | In addition, mere aggregation of another work not based on the Program 134 | with the Program (or with a work based on the Program) on a volume of 135 | a storage or distribution medium does not bring the other work under 136 | the scope of this License. 137 | 138 | 3. You may copy and distribute the Program (or a work based on it, 139 | under Section 2) in object code or executable form under the terms of 140 | Sections 1 and 2 above provided that you also do one of the following: 141 | 142 | a) Accompany it with the complete corresponding machine-readable 143 | source code, which must be distributed under the terms of Sections 144 | 1 and 2 above on a medium customarily used for software interchange; or, 145 | 146 | b) Accompany it with a written offer, valid for at least three 147 | years, to give any third party, for a charge no more than your 148 | cost of physically performing source distribution, a complete 149 | machine-readable copy of the corresponding source code, to be 150 | distributed under the terms of Sections 1 and 2 above on a medium 151 | customarily used for software interchange; or, 152 | 153 | c) Accompany it with the information you received as to the offer 154 | to distribute corresponding source code. (This alternative is 155 | allowed only for noncommercial distribution and only if you 156 | received the program in object code or executable form with such 157 | an offer, in accord with Subsection b above.) 158 | 159 | The source code for a work means the preferred form of the work for 160 | making modifications to it. For an executable work, complete source 161 | code means all the source code for all modules it contains, plus any 162 | associated interface definition files, plus the scripts used to 163 | control compilation and installation of the executable. However, as a 164 | special exception, the source code distributed need not include 165 | anything that is normally distributed (in either source or binary 166 | form) with the major components (compiler, kernel, and so on) of the 167 | operating system on which the executable runs, unless that component 168 | itself accompanies the executable. 169 | 170 | If distribution of executable or object code is made by offering 171 | access to copy from a designated place, then offering equivalent 172 | access to copy the source code from the same place counts as 173 | distribution of the source code, even though third parties are not 174 | compelled to copy the source along with the object code. 175 | 176 | 4. You may not copy, modify, sublicense, or distribute the Program 177 | except as expressly provided under this License. Any attempt 178 | otherwise to copy, modify, sublicense or distribute the Program is 179 | void, and will automatically terminate your rights under this License. 180 | However, parties who have received copies, or rights, from you under 181 | this License will not have their licenses terminated so long as such 182 | parties remain in full compliance. 183 | 184 | 5. You are not required to accept this License, since you have not 185 | signed it. However, nothing else grants you permission to modify or 186 | distribute the Program or its derivative works. These actions are 187 | prohibited by law if you do not accept this License. Therefore, by 188 | modifying or distributing the Program (or any work based on the 189 | Program), you indicate your acceptance of this License to do so, and 190 | all its terms and conditions for copying, distributing or modifying 191 | the Program or works based on it. 192 | 193 | 6. Each time you redistribute the Program (or any work based on the 194 | Program), the recipient automatically receives a license from the 195 | original licensor to copy, distribute or modify the Program subject to 196 | these terms and conditions. You may not impose any further 197 | restrictions on the recipients' exercise of the rights granted herein. 198 | You are not responsible for enforcing compliance by third parties to 199 | this License. 200 | 201 | 7. If, as a consequence of a court judgment or allegation of patent 202 | infringement or for any other reason (not limited to patent issues), 203 | conditions are imposed on you (whether by court order, agreement or 204 | otherwise) that contradict the conditions of this License, they do not 205 | excuse you from the conditions of this License. If you cannot 206 | distribute so as to satisfy simultaneously your obligations under this 207 | License and any other pertinent obligations, then as a consequence you 208 | may not distribute the Program at all. For example, if a patent 209 | license would not permit royalty-free redistribution of the Program by 210 | all those who receive copies directly or indirectly through you, then 211 | the only way you could satisfy both it and this License would be to 212 | refrain entirely from distribution of the Program. 213 | 214 | If any portion of this section is held invalid or unenforceable under 215 | any particular circumstance, the balance of the section is intended to 216 | apply and the section as a whole is intended to apply in other 217 | circumstances. 218 | 219 | It is not the purpose of this section to induce you to infringe any 220 | patents or other property right claims or to contest validity of any 221 | such claims; this section has the sole purpose of protecting the 222 | integrity of the free software distribution system, which is 223 | implemented by public license practices. Many people have made 224 | generous contributions to the wide range of software distributed 225 | through that system in reliance on consistent application of that 226 | system; it is up to the author/donor to decide if he or she is willing 227 | to distribute software through any other system and a licensee cannot 228 | impose that choice. 229 | 230 | This section is intended to make thoroughly clear what is believed to 231 | be a consequence of the rest of this License. 232 | 233 | 8. If the distribution and/or use of the Program is restricted in 234 | certain countries either by patents or by copyrighted interfaces, the 235 | original copyright holder who places the Program under this License 236 | may add an explicit geographical distribution limitation excluding 237 | those countries, so that distribution is permitted only in or among 238 | countries not thus excluded. In such case, this License incorporates 239 | the limitation as if written in the body of this License. 240 | 241 | 9. The Free Software Foundation may publish revised and/or new versions 242 | of the General Public License from time to time. Such new versions will 243 | be similar in spirit to the present version, but may differ in detail to 244 | address new problems or concerns. 245 | 246 | Each version is given a distinguishing version number. If the Program 247 | specifies a version number of this License which applies to it and "any 248 | later version", you have the option of following the terms and conditions 249 | either of that version or of any later version published by the Free 250 | Software Foundation. If the Program does not specify a version number of 251 | this License, you may choose any version ever published by the Free Software 252 | Foundation. 253 | 254 | 10. If you wish to incorporate parts of the Program into other free 255 | programs whose distribution conditions are different, write to the author 256 | to ask for permission. For software which is copyrighted by the Free 257 | Software Foundation, write to the Free Software Foundation; we sometimes 258 | make exceptions for this. Our decision will be guided by the two goals 259 | of preserving the free status of all derivatives of our free software and 260 | of promoting the sharing and reuse of software generally. 261 | 262 | NO WARRANTY 263 | 264 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 265 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 266 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 267 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 268 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 269 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 270 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 271 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 272 | REPAIR OR CORRECTION. 273 | 274 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 275 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 276 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 277 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 278 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 279 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 280 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 281 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 282 | POSSIBILITY OF SUCH DAMAGES. 283 | 284 | END OF TERMS AND CONDITIONS 285 | 286 | How to Apply These Terms to Your New Programs 287 | 288 | If you develop a new program, and you want it to be of the greatest 289 | possible use to the public, the best way to achieve this is to make it 290 | free software which everyone can redistribute and change under these terms. 291 | 292 | To do so, attach the following notices to the program. It is safest 293 | to attach them to the start of each source file to most effectively 294 | convey the exclusion of warranty; and each file should have at least 295 | the "copyright" line and a pointer to where the full notice is found. 296 | 297 | 298 | Copyright (C) 299 | 300 | This program is free software; you can redistribute it and/or modify 301 | it under the terms of the GNU General Public License as published by 302 | the Free Software Foundation; either version 2 of the License, or 303 | (at your option) any later version. 304 | 305 | This program is distributed in the hope that it will be useful, 306 | but WITHOUT ANY WARRANTY; without even the implied warranty of 307 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 308 | GNU General Public License for more details. 309 | 310 | You should have received a copy of the GNU General Public License 311 | along with this program; if not, write to the Free Software 312 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 313 | 314 | 315 | Also add information on how to contact you by electronic and paper mail. 316 | 317 | If the program is interactive, make it output a short notice like this 318 | when it starts in an interactive mode: 319 | 320 | Gnomovision version 69, Copyright (C) year name of author 321 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 322 | This is free software, and you are welcome to redistribute it 323 | under certain conditions; type `show c' for details. 324 | 325 | The hypothetical commands `show w' and `show c' should show the appropriate 326 | parts of the General Public License. Of course, the commands you use may 327 | be called something other than `show w' and `show c'; they could even be 328 | mouse-clicks or menu items--whatever suits your program. 329 | 330 | You should also get your employer (if you work as a programmer) or your 331 | school, if any, to sign a "copyright disclaimer" for the program, if 332 | necessary. Here is a sample; alter the names: 333 | 334 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 335 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 336 | 337 | , 1 April 1989 338 | Ty Coon, President of Vice 339 | 340 | This General Public License does not permit incorporating your program into 341 | proprietary programs. If your program is a subroutine library, you may 342 | consider it more useful to permit linking proprietary applications with the 343 | library. If this is what you want to do, use the GNU Library General 344 | Public License instead of this License. 345 | -------------------------------------------------------------------------------- /src/pep.c: -------------------------------------------------------------------------------- 1 | /* 2 | * PEPsal : A Performance Enhancing Proxy for Satellite Links 3 | * 4 | * Copyleft Daniele Lacamera 2005 5 | * Copyleft Dan Kruchining 2010 6 | * Copyleft Joaquin Muguerza 2016 7 | * See AUTHORS and COPYING before using this software. 8 | * 9 | * 10 | * 11 | */ 12 | 13 | #include "config.h" 14 | #include "pepsal.h" 15 | #include "pepqueue.h" 16 | #include "syntab.h" 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #define __USE_XOPEN_EXTENDED 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | 50 | #include 51 | 52 | /* 53 | * Data structure to fill with packet headers when we 54 | * get a new syn: 55 | * 56 | * struct ipv4_packet 57 | * iph : ip header for the packet 58 | * tcph: tcp header for the segment 59 | * 60 | */ 61 | struct ipv4_packet{ 62 | struct iphdr iph; 63 | struct tcphdr tcph; 64 | }; 65 | 66 | static int DEBUG = 0; 67 | static int background = 0; 68 | static int fastopen = 0; 69 | static int gcc_interval = PEP_GCC_INTERVAL; 70 | static int pending_conn_lifetime = PEP_PENDING_CONN_LIFETIME; 71 | static int portnum = PEP_DEFAULT_PORT; 72 | static int max_conns = (PEP_MIN_CONNS + PEP_MAX_CONNS) / 2; 73 | static char pepsal_ip_addr[20] = "0.0.0.0"; 74 | 75 | /* 76 | * The main aim of this structure is to reduce search time 77 | * of pep_proxy instance corresponding to the returned by poll() 78 | * file descriptor. Typically to find pep_proxy by one of its FDs 79 | * takes linear time(because pep_proxys are arranged into one list), 80 | * but this structure reduce search time to O(1). 81 | * pollfds is an array of file descriptors and events used by poll. 82 | * pdescrs is an array of pointers to corresponding pep_endpoint entries. 83 | * Each Ith FD corresponds to Ith pep_proxy. Both array have the same 84 | * size equal to num_pollfds items. 85 | */ 86 | static struct { 87 | struct pollfd *pollfds; 88 | struct pep_endpoint **endpoints; 89 | int num_pollfds; 90 | } poll_resources; 91 | 92 | /* 93 | * PEP logger dumps all connections in the syn table to 94 | * the file specified by filename every PEPLOGGER_INTERVAL 95 | * seconds. 96 | */ 97 | struct pep_logger { 98 | FILE *file; 99 | timer_t timer; 100 | char *filename; 101 | }; 102 | 103 | /* 104 | * Main queues for connections and work synchronization 105 | * active_queue is used to transfer read/write jobs to 106 | * worker threads from PEP threads pool. After all jobs in 107 | * active_queue are done, they're moved to the ready_queue 108 | * which is used by poller thread. After poller thread wakes up, 109 | * it cheks out all connections from ready_queue, checks theier status, 110 | * updates metainformation and restarts polling loop. 111 | */ 112 | static struct pep_queue active_queue, ready_queue; 113 | static struct pep_logger logger; 114 | 115 | static pthread_t listener; 116 | static pthread_t poller; 117 | static pthread_t timer_sch; 118 | static pthread_t *workers = NULL; 119 | 120 | #define pep_error(fmt, args...) \ 121 | syslog(LOG_ERR, "%s():%d: " fmt " (errno %d)", \ 122 | __FUNCTION__, __LINE__, ##args, errno); \ 123 | __pep_error(__FUNCTION__, __LINE__, fmt, ##args) 124 | 125 | #define pep_warning(fmt, args...) \ 126 | syslog(LOG_WARNING, "%s():%d: " fmt, \ 127 | __FUNCTION__, __LINE__, ##args); \ 128 | __pep_warning(__FUNCTION__, __LINE__, fmt, ##args) 129 | 130 | #define PEP_DEBUG(fmt, args...) \ 131 | if (DEBUG) { \ 132 | fprintf(stderr, "[DEBUG] %s(): " fmt "\n", \ 133 | __FUNCTION__, ##args); \ 134 | syslog(LOG_DEBUG, "%s(): " fmt, __FUNCTION__, \ 135 | ##args); \ 136 | } 137 | 138 | #define PEP_DEBUG_DP(proxy, fmt, args...) \ 139 | if (DEBUG) { \ 140 | char __buf[17]; \ 141 | toip(__buf, (proxy)->src.addr); \ 142 | fprintf(stderr, "[DEBUG] %s(): {%s:%d} " fmt "\n", \ 143 | __FUNCTION__, __buf, (proxy)->src.port, ##args); \ 144 | syslog(LOG_DEBUG, "%s(): {%s:%d} " fmt, __FUNCTION__, \ 145 | __buf, (proxy)->src.port, ##args); \ 146 | } 147 | 148 | static void __pep_error(const char *function, int line, const char *fmt, ...) 149 | { 150 | va_list ap; 151 | char buf[PEP_ERRBUF_SZ]; 152 | int err = errno; 153 | size_t len; 154 | 155 | va_start(ap, fmt); 156 | 157 | len = snprintf(buf, PEP_ERRBUF_SZ, "[ERROR]: "); 158 | len += vsnprintf(buf + len, PEP_ERRBUF_SZ - len, fmt, ap); 159 | if (err && (PEP_ERRBUF_SZ - len) > 1) { 160 | snprintf(buf + len, PEP_ERRBUF_SZ - len, 161 | "\n ERRNO: [%s:%d]", strerror(err), err); 162 | } 163 | 164 | fprintf(stderr, "%s\n AT: %s:%d\n", buf, function, line); 165 | va_end(ap); 166 | closelog(); 167 | exit(EXIT_FAILURE); 168 | } 169 | 170 | static void __pep_warning(const char *function, int line, const char *fmt, ...) 171 | { 172 | va_list ap; 173 | char buf[PEP_ERRBUF_SZ]; 174 | size_t len; 175 | 176 | va_start(ap, fmt); 177 | len = snprintf(buf, PEP_ERRBUF_SZ, "[WARNING]: "); 178 | if (PEP_ERRBUF_SZ - len > 1) { 179 | len += vsnprintf(buf + len, PEP_ERRBUF_SZ - len, fmt, ap); 180 | } 181 | 182 | fprintf(stderr, "%s\n AT: %s:%d\n", buf, function, line); 183 | va_end(ap); 184 | } 185 | 186 | static void usage(char *name) 187 | { 188 | fprintf(stderr,"Usage: %s [-V] [-h] [-v] [-d] [-f]" 189 | " [-a address] [-p port]" 190 | " [-c max_conn] [-l logfile] [-t proxy_lifetime]" 191 | " [-g garbage collector interval]\n", name); 192 | exit(EXIT_SUCCESS); 193 | } 194 | 195 | /* 196 | * Check if error @err is related to nonblocking I/O. 197 | * If it is in a set of nonblocking erros, it may handled 198 | * properly without program termination. 199 | */ 200 | static int nonblocking_err_p(int err) 201 | { 202 | static int nb_errs[] = { 203 | EAGAIN, 204 | EINPROGRESS, 205 | EALREADY, 206 | }; 207 | int i; 208 | 209 | for (i = 0; i < sizeof(nb_errs); i++) { 210 | if (err == nb_errs[i]) 211 | return 1; 212 | } 213 | 214 | return 0; 215 | } 216 | 217 | /* 218 | * Secure routine to translate a hex address in a 219 | * readable ip number: 220 | */ 221 | static void toip(char *ret, int address) 222 | { 223 | int a,b,c,d; 224 | 225 | a = (0xFF000000 & address) >> 24; 226 | b = (0x00FF0000 & address) >> 16; 227 | c = (0x0000FF00 & address) >> 8; 228 | d = 0x000000FF & address; 229 | 230 | snprintf(ret,16,"%d.%d.%d.%d",a,b,c,d); 231 | } 232 | 233 | static char *conn_stat[] = { 234 | "PST_CLOSED", 235 | "PST_OPEN", 236 | "PST_CONNECT", 237 | "PST_PENDING", 238 | }; 239 | 240 | static void logger_fn(void) 241 | { 242 | struct pep_proxy *proxy; 243 | time_t tm; 244 | char ip_src[17], ip_dst[17], timebuf[128]; 245 | int i = 1, len; 246 | 247 | PEP_DEBUG("Logger invoked!"); 248 | SYNTAB_LOCK_READ(); 249 | tm = time(NULL); 250 | ctime_r(&tm, timebuf); 251 | len = strlen(timebuf); 252 | timebuf[len - 1] = ']'; 253 | fprintf(logger.file, "=== [%s ===\n", timebuf); 254 | syntab_foreach_connection(proxy) { 255 | toip(ip_src, proxy->src.addr); 256 | toip(ip_dst, proxy->dst.addr); 257 | fprintf(logger.file, "[%d] Proxy %s:%d <-> %s:%d\n", i++, 258 | ip_src, proxy->src.port, ip_dst, proxy->dst.port); 259 | fprintf(logger.file, " Status: %s\n", conn_stat[proxy->status]); 260 | ctime_r(&proxy->syn_time, timebuf); 261 | fprintf(logger.file, " SYN received: %s", timebuf); 262 | if (proxy->last_rxtx != 0) { 263 | ctime_r(&proxy->last_rxtx, timebuf); 264 | fprintf(logger.file, " Last Rx/Tx activity: %s", timebuf); 265 | } 266 | 267 | } 268 | if (i == 1) { 269 | fprintf(logger.file, " No connections\n"); 270 | } 271 | 272 | SYNTAB_UNLOCK_READ(); 273 | fflush(logger.file); 274 | } 275 | 276 | static void setup_socket(int fd) 277 | { 278 | struct timeval t= { 0, 10000 }; 279 | int flags; 280 | 281 | flags = fcntl(fd, F_GETFL, 0); 282 | fcntl(fd, F_SETFL, flags | O_NONBLOCK); 283 | setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(struct timeval)); 284 | setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &t, sizeof(struct timeval)); 285 | PEP_DEBUG("Socket %d: Setting up timeouts and syncronous mode.", fd); 286 | } 287 | 288 | #define ENDPOINT_POLLEVENTS (POLLIN | POLLHUP | POLLERR | POLLNVAL) 289 | static struct pep_proxy *alloc_proxy(void) 290 | { 291 | struct pep_proxy *proxy = calloc(1, sizeof(*proxy)); 292 | int i; 293 | struct pep_endpoint *endp; 294 | 295 | if (!proxy) { 296 | errno = ENOMEM; 297 | return NULL; 298 | } 299 | 300 | list_init_node(&proxy->lnode); 301 | list_init_node(&proxy->qnode); 302 | proxy->status = PST_INVAL; 303 | atomic_set(&proxy->refcnt, 1); 304 | 305 | for (i = 0; i < PROXY_ENDPOINTS; i++) { 306 | endp = &proxy->endpoints[i]; 307 | endp->fd = -1; 308 | endp->owner = proxy; 309 | endp->iostat = 0; 310 | endp->poll_events = ENDPOINT_POLLEVENTS; 311 | } 312 | 313 | return proxy; 314 | } 315 | 316 | static void free_proxy(struct pep_proxy *proxy) 317 | { 318 | assert(atomic_read(&proxy->refcnt) == 0); 319 | free(proxy); 320 | } 321 | 322 | static inline void pin_proxy(struct pep_proxy *proxy) 323 | { 324 | atomic_inc(&proxy->refcnt); 325 | } 326 | 327 | static inline void unpin_proxy(struct pep_proxy *proxy) 328 | { 329 | if (atomic_dec(&proxy->refcnt) == 1) { 330 | PEP_DEBUG_DP(proxy, "Free proxy"); 331 | free_proxy(proxy); 332 | } 333 | } 334 | 335 | static void destroy_proxy(struct pep_proxy *proxy) 336 | { 337 | int i; 338 | 339 | if (proxy->status == PST_CLOSED) { 340 | goto out; 341 | } 342 | 343 | proxy->status = PST_CLOSED; 344 | PEP_DEBUG_DP(proxy, "Destroy proxy"); 345 | 346 | SYNTAB_LOCK_WRITE(); 347 | syntab_delete(proxy); 348 | proxy->status = PST_CLOSED; 349 | SYNTAB_UNLOCK_WRITE(); 350 | 351 | for (i = 0; i < PROXY_ENDPOINTS; i++) { 352 | if (proxy->endpoints[i].fd >= 0) { 353 | fcntl(proxy->endpoints[i].fd, F_SETFL, O_SYNC); 354 | close(proxy->endpoints[i].fd); 355 | } 356 | if (pepbuf_initialized(&proxy->endpoints[i].buf)) { 357 | pepbuf_deinit(&proxy->endpoints[i].buf); 358 | } 359 | } 360 | 361 | out: 362 | unpin_proxy(proxy); 363 | } 364 | 365 | /* 366 | * Garbage connections collector handler is periodically invoked 367 | * with gcc_interval interval(in seconds) and cleans dead(or garbage) 368 | * connections. 369 | * When PEPsal catches SYN packet from the source endpoint, 370 | * it creates new pep_proxy instance, markes it with PST_PENDING status 371 | * and saves into the SYN table. After some time(actually after ACK is received) 372 | * this proxy shold be 373 | * activated, connection should be established and endpoints set up. 374 | * If everything is going alright, the proxy will be marked with PST_CONNECT 375 | * status. But the client might endup abnormally after SYN is sent. In this case 376 | * PEPsal has no chance to know about it. Thus PEPsal monitors all pending 377 | * connections in SYN table and closes them if a connection hasn't have any 378 | * activity for a long time. 379 | */ 380 | static void garbage_connections_collector(void) 381 | { 382 | struct pep_proxy *proxy; 383 | struct list_node *item, *safe; 384 | time_t t_now, t_diff; 385 | 386 | PEP_DEBUG("Garbage connections collector activated!"); 387 | 388 | SYNTAB_LOCK_WRITE(); 389 | t_now = time(NULL); 390 | list_for_each_safe(&GET_SYNTAB()->conns, item, safe) { 391 | proxy = list_entry(item, struct pep_proxy, lnode); 392 | if (proxy->status != PST_PENDING) { 393 | continue; 394 | } 395 | 396 | t_diff = t_now - proxy->syn_time; 397 | if (t_diff >= pending_conn_lifetime) { 398 | PEP_DEBUG_DP(proxy, "Marked as garbage. Destroying..."); 399 | destroy_proxy(proxy); 400 | } 401 | } 402 | 403 | SYNTAB_UNLOCK_WRITE(); 404 | } 405 | 406 | static ssize_t pep_receive(struct pep_endpoint *endp) 407 | { 408 | int iostat; 409 | ssize_t rb; 410 | 411 | if (endp->iostat & (PEP_IORDONE | PEP_IOERR | PEP_IOEOF) || 412 | pepbuf_full(&endp->buf)) { 413 | return 0; 414 | } 415 | 416 | rb = read(endp->fd, PEPBUF_RPOS(&endp->buf), 417 | PEPBUF_SPACE_LEFT(&endp->buf)); 418 | if (rb < 0) { 419 | if (nonblocking_err_p(errno)) { 420 | endp->iostat |= PEP_IORDONE; 421 | return 0; 422 | } 423 | 424 | endp->iostat |= PEP_IOERR; 425 | return -1; 426 | } 427 | else if (rb == 0) { 428 | endp->iostat |= PEP_IOEOF; 429 | return 0; 430 | } 431 | 432 | pepbuf_update_rpos(&endp->buf, rb); 433 | return rb; 434 | } 435 | 436 | static ssize_t pep_send(struct pep_endpoint *from, int to_fd) 437 | { 438 | ssize_t wb; 439 | 440 | if (from->iostat & (PEP_IOERR | PEP_IOWDONE) || 441 | (pepbuf_empty(&from->buf) && !(from->iostat & PEP_IOEOF))) { 442 | return 0; 443 | } 444 | 445 | wb = write(to_fd, PEPBUF_WPOS(&from->buf), 446 | PEPBUF_SPACE_FILLED(&from->buf)); 447 | if (wb < 0) { 448 | if (nonblocking_err_p(errno)) { 449 | from->iostat |= PEP_IOWDONE; 450 | return 0; 451 | } 452 | 453 | from->iostat |= PEP_IOERR; 454 | return -1; 455 | } 456 | 457 | pepbuf_update_wpos(&from->buf, wb); 458 | return wb; 459 | } 460 | 461 | static void pep_proxy_data(struct pep_endpoint *from, struct pep_endpoint *to) 462 | { 463 | ssize_t rb, wb; 464 | 465 | rb = wb = 1; 466 | while ((wb > 0) || (rb > 0)) { 467 | rb = pep_receive(from); 468 | wb = pep_send(from, to->fd); 469 | } 470 | 471 | if (from->iostat & PEP_IOERR) { 472 | return; 473 | } 474 | 475 | /* 476 | * Receiving buffer has no space or EOF was reached from the peer. 477 | * Stop wait for incomming data on this FD. 478 | */ 479 | if (pepbuf_full(&from->buf) || (from->iostat & PEP_IOEOF)) { 480 | from->poll_events &= ~POLLIN; 481 | } 482 | else if (from->iostat & PEP_IORDONE) { 483 | from->poll_events |= POLLIN; 484 | } 485 | 486 | /* 487 | * All available data was transmitted to the peer 488 | * Stop wait when FD will be ready for write. 489 | */ 490 | if (pepbuf_empty(&from->buf)) { 491 | to->poll_events &= ~POLLOUT; 492 | } 493 | else { /* There exists some data to write. Wait until we can transmit it. */ 494 | to->poll_events |= POLLOUT; 495 | } 496 | } 497 | 498 | static int save_proxy_from_socket(int sockfd, struct sockaddr_in cliaddr) 499 | { 500 | char *buffer; 501 | struct ipv4_packet *ip4; 502 | struct pep_proxy *proxy, *dup; 503 | struct syntab_key key; 504 | int id = 0, ret, added = 0; 505 | struct sockaddr_in orig_dst; 506 | int addrlen = sizeof(orig_dst); 507 | 508 | PEP_DEBUG("Saving new SYN..."); 509 | 510 | proxy = NULL; 511 | proxy = alloc_proxy(); 512 | if (!proxy) { 513 | pep_warning("Failed to allocate new pep_proxy instance! [%s:%d]", 514 | strerror(errno), errno); 515 | ret = -1; 516 | goto err; 517 | } 518 | 519 | /* Socket is bound to original destination */ 520 | if(getsockname(sockfd, (struct sockaddr *) &orig_dst, &addrlen) < 0){ 521 | pep_warning("Failed to get original dest from socket! [%s:%d]", 522 | strerror(errno), errno); 523 | ret = -1; 524 | goto err; 525 | } 526 | 527 | /* Setup source and destination endpoints */ 528 | proxy->src.addr = ntohl(cliaddr.sin_addr.s_addr); 529 | proxy->src.port = ntohs(cliaddr.sin_port); 530 | proxy->dst.addr = ntohl(orig_dst.sin_addr.s_addr); 531 | proxy->dst.port = ntohs(orig_dst.sin_port); 532 | proxy->syn_time = time(NULL); 533 | syntab_format_key(proxy, &key); 534 | 535 | /* Check for duplicate syn, and drop it. 536 | * This happens when RTT is too long and we 537 | * still didn't establish the connection. 538 | */ 539 | SYNTAB_LOCK_WRITE(); 540 | dup = syntab_find(&key); 541 | if (dup != NULL) { 542 | PEP_DEBUG_DP(dup, "Duplicate SYN. Dropping..."); 543 | SYNTAB_UNLOCK_WRITE(); 544 | goto err; 545 | } 546 | 547 | /* add to the table... */ 548 | proxy->status = PST_PENDING; 549 | ret = syntab_add(proxy); 550 | SYNTAB_UNLOCK_WRITE(); 551 | if (ret < 0) { 552 | pep_warning("Failed to insert pep_proxy into a hash table!"); 553 | goto err; 554 | } 555 | 556 | added = 1; 557 | PEP_DEBUG_DP(proxy, "Registered new SYN"); 558 | if (ret < 0) { 559 | pep_warning("nfq_set_verdict to NF_ACCEPT failed! [%s:%d]", 560 | strerror(errno), errno); 561 | goto err; 562 | } 563 | 564 | return ret; 565 | 566 | err: 567 | if (added) { 568 | syntab_delete(proxy); 569 | } 570 | if (proxy != NULL) { 571 | unpin_proxy(proxy); 572 | } 573 | 574 | return ret; 575 | } 576 | 577 | 578 | void *listener_loop(void UNUSED(*unused)) 579 | { 580 | int listenfd, optval, ret, connfd, out_fd; 581 | struct sockaddr_in cliaddr, servaddr, 582 | r_servaddr; 583 | socklen_t len; 584 | struct pep_proxy *proxy; 585 | struct hostent *host; 586 | char ipbuf[17], ipbuf1[17]; 587 | unsigned short r_port, c_port; 588 | struct syntab_key key; 589 | 590 | listenfd = socket(AF_INET, SOCK_STREAM, 0); 591 | if (listenfd < 0) { 592 | pep_error("Failed to create listener socket!"); 593 | } 594 | 595 | PEP_DEBUG("Opened listener socket: %d", listenfd); 596 | memset(&servaddr, 0, sizeof(servaddr)); 597 | 598 | servaddr.sin_family = AF_INET; 599 | servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 600 | servaddr.sin_port = htons(portnum); 601 | 602 | optval = 1; 603 | ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, 604 | &optval, sizeof(optval)); 605 | if (ret < 0) { 606 | pep_error("Failed to set SOL_REUSEADDR option! [RET = %d]", ret); 607 | } 608 | 609 | /* Set socket transparent (able to bind to external address) */ 610 | ret = setsockopt(listenfd, SOL_IP, IP_TRANSPARENT, 611 | &optval, sizeof(optval)); 612 | if (ret < 0) { 613 | pep_error("Failed to set IP_TRANSPARENT option! [RET = %d]", ret); 614 | } 615 | 616 | /* Set TCP_FASTOPEN socket option */ 617 | if (fastopen) { 618 | optval = 5; 619 | ret = setsockopt(listenfd, SOL_TCP, TCP_FASTOPEN, 620 | &optval, sizeof(optval)); 621 | if (ret < 0) { 622 | pep_error("Failed to set TCP_FASTOPEN option! [RET = %d]", ret); 623 | } 624 | } 625 | 626 | ret = bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); 627 | if (ret < 0) { 628 | pep_error("Failed to bind socket! [RET = %d]", ret); 629 | } 630 | 631 | ret = listen(listenfd, LISTENER_QUEUE_SIZE); 632 | if (ret < 0) { 633 | pep_error("Failed to set quesize of listenfd to %d! [RET = %d]", 634 | LISTENER_QUEUE_SIZE, ret); 635 | } 636 | 637 | /* Accept loop */ 638 | PEP_DEBUG("Entering lister main loop..."); 639 | for (;;) { 640 | out_fd = -1; 641 | proxy = NULL; 642 | 643 | len = sizeof(struct sockaddr_in); 644 | connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &len); 645 | if (connfd < 0) { 646 | pep_warning("accept() failed! [Errno: %s, %d]", 647 | strerror(errno), errno); 648 | continue; 649 | } 650 | 651 | /* 652 | * Try to find incomming connection in our SYN table 653 | * It must be already there waiting for activation. 654 | */ 655 | key.addr = ntohl(cliaddr.sin_addr.s_addr); 656 | key.port = ntohs(cliaddr.sin_port); 657 | toip(ipbuf, key.addr); 658 | PEP_DEBUG("New incomming connection: %s:%d", ipbuf, key.port); 659 | 660 | SYNTAB_LOCK_READ(); 661 | proxy = syntab_find(&key); 662 | 663 | /* 664 | * If the proxy is not in the table, add the entry. 665 | */ 666 | if (!proxy) { 667 | SYNTAB_UNLOCK_READ(); 668 | save_proxy_from_socket(connfd, cliaddr); 669 | SYNTAB_LOCK_READ(); 670 | proxy = syntab_find(&key); 671 | } 672 | 673 | /* 674 | * If still can't find key in the table, there is an error. 675 | */ 676 | if (!proxy) { 677 | pep_warning("Can not find the connection in SYN table. " 678 | "Terminating!"); 679 | SYNTAB_UNLOCK_READ(); 680 | goto close_connection; 681 | } 682 | 683 | /* 684 | * The proxy we fetched from the SYN table is in PST_PENDING state. 685 | * Now we're going to setup connection for it and configure endpoints. 686 | * While the proxy is in PST_PENDING state it may be possibly removed 687 | * by the garbage connections collector. Collector is invoked every N 688 | * seconds and removes from SYN table all pending connections 689 | * that were not activated during predefined interval. Thus we have 690 | * to pin our proxy to protect ourself from segfault. 691 | */ 692 | pin_proxy(proxy); 693 | assert(proxy->status == PST_PENDING); 694 | SYNTAB_UNLOCK_READ(); 695 | 696 | toip(ipbuf, proxy->dst.addr); 697 | r_port = proxy->dst.port; 698 | PEP_DEBUG("Connecting to %s:%d...", ipbuf, r_port); 699 | host = gethostbyname(ipbuf); 700 | if (!host) { 701 | pep_warning("Failed to get host %s!", ipbuf); 702 | goto close_connection; 703 | } 704 | 705 | memset(&r_servaddr, 0, sizeof(r_servaddr)); 706 | r_servaddr.sin_family = AF_INET; 707 | r_servaddr.sin_addr.s_addr = ((struct in_addr *)(host->h_addr))->s_addr; 708 | r_servaddr.sin_port = htons(r_port); 709 | 710 | ret = socket(AF_INET, SOCK_STREAM, 0); 711 | if (ret < 0) { 712 | pep_warning("Failed to create socket! [%s:%d]", 713 | strerror(errno), errno); 714 | goto close_connection; 715 | } 716 | 717 | out_fd = ret; 718 | fcntl(out_fd, F_SETFL, O_NONBLOCK); 719 | 720 | /* 721 | * Set outbound endpoint to transparent mode 722 | * (bind to external address) 723 | */ 724 | ret = setsockopt(out_fd, SOL_IP, IP_TRANSPARENT, 725 | &optval, sizeof(optval)); 726 | if (ret < 0) { 727 | pep_error("Failed to set IP_TRANSPARENT option! [RET = %d]", ret); 728 | } 729 | 730 | toip(ipbuf, proxy->src.addr); 731 | toip(ipbuf1, proxy->dst.addr); 732 | 733 | if (fastopen) { 734 | ret = sendto(out_fd, PEPBUF_WPOS(&proxy->src.buf), 0, MSG_FASTOPEN, 735 | (struct sockaddr *)&r_servaddr, sizeof(r_servaddr)); 736 | } 737 | else { 738 | ret = connect(out_fd, (struct sockaddr *)&r_servaddr, 739 | sizeof(r_servaddr)); 740 | } 741 | if ((ret < 0) && !nonblocking_err_p(errno)) { 742 | pep_warning("Failed to connect! [%s:%d]", strerror(errno), errno); 743 | goto close_connection; 744 | } 745 | 746 | proxy->src.fd = connfd; 747 | proxy->dst.fd = out_fd; 748 | if (proxy->status == PST_CLOSED) { 749 | unpin_proxy(proxy); 750 | goto close_connection; 751 | } 752 | 753 | proxy->status = PST_CONNECT; 754 | unpin_proxy(proxy); 755 | PEP_DEBUG("Sending signal to poller [%d, %d]!", connfd, out_fd); 756 | if (pthread_kill(poller, POLLER_NEWCONN_SIG) != 0) { 757 | pep_error("Failed to send %d siganl to poller thread", 758 | POLLER_NEWCONN_SIG); 759 | } 760 | 761 | continue; 762 | 763 | close_connection: 764 | /* 765 | * Ok. Some error occured and we have to properly cleanup 766 | * all resources. Client socket must be closed and server 767 | * socket (if any) as well. Also it would be good if we 768 | * remove pep_proxy instance which caused an error from SYN 769 | * table. 770 | */ 771 | 772 | close(connfd); 773 | if (out_fd >= 0) { 774 | close(out_fd); 775 | } 776 | if (proxy) { 777 | destroy_proxy(proxy); 778 | } 779 | } 780 | 781 | /* Normally this code won't be executed */ 782 | PEP_DEBUG("Exiting..."); 783 | pthread_exit(NULL); 784 | } 785 | 786 | static int prepare_poll_resources(void) 787 | { 788 | struct pep_proxy *proxy; 789 | struct pep_endpoint *endp; 790 | struct pollfd *pfd; 791 | int i, j; 792 | enum proxy_status stat; 793 | 794 | i = 0; 795 | SYNTAB_LOCK_READ(); 796 | syntab_foreach_connection(proxy) { 797 | /* 798 | * Proxy status field may be changed from 799 | * another thread(for example from listener thread) 800 | * So to prevent conflicts we copy it to the @stat 801 | * variable(reads are atomic) and only then compare the copy 802 | * with our patterns. 803 | */ 804 | stat = proxy->status; 805 | if (stat == PST_PENDING || stat == PST_CLOSED) 806 | continue; 807 | 808 | for (j = 0; j < PROXY_ENDPOINTS; j++) { 809 | endp = &proxy->endpoints[j]; 810 | pfd = &poll_resources.pollfds[i]; 811 | pfd->fd = endp->fd; 812 | pfd->events = endp->poll_events; 813 | pfd->revents = 0; 814 | poll_resources.endpoints[i] = endp; 815 | i++; 816 | } 817 | } 818 | 819 | SYNTAB_UNLOCK_READ(); 820 | return i; 821 | } 822 | 823 | /* An empty signal handler. It only needed to interrupt poll() */ 824 | static void poller_sighandler(int signo) 825 | { 826 | PEP_DEBUG("Received signal %d", signo); 827 | } 828 | 829 | static void *poller_loop(void __attribute__((unused)) *unused) 830 | { 831 | int pollret, num_works, i, num_clients, iostat; 832 | struct pep_proxy *proxy; 833 | struct pep_endpoint *endp, *target; 834 | struct pollfd *pollfd; 835 | struct list_node *entry, *safe; 836 | struct list_head local_list; 837 | sigset_t sigset; 838 | struct sigaction sa; 839 | 840 | sigemptyset(&sigset); 841 | sigaddset(&sigset, POLLER_NEWCONN_SIG); 842 | memset(&sa, 0, sizeof(sa)); 843 | sa.sa_handler = poller_sighandler; 844 | sa.sa_mask = sigset; 845 | if (sigaction(POLLER_NEWCONN_SIG, &sa, NULL) < 0) { 846 | pep_error("sigaction() error!"); 847 | } 848 | 849 | sigprocmask(SIG_UNBLOCK, &sigset, NULL); 850 | 851 | for (;;) { 852 | list_init_head(&local_list); 853 | 854 | /* 855 | * At first we prepare file descriptors of all existing 856 | * connections in SYN table for poll. Whilie we're doing 857 | * it some new connection may appear in listener thread. 858 | * When listener accpets new connection and configures proxy 859 | * relevant proxy, it sends the POLLER_NEWCONN_SIG signal 860 | * to the poller thread. Thus while poller is busy with preparing 861 | * descriptors for poll, POLLER_NEWCONN_SIG should be blocked. 862 | * It performs poll() be preperly interrupted and renew descriptors. 863 | */ 864 | sigprocmask(SIG_BLOCK, &sigset, NULL); 865 | num_clients = prepare_poll_resources(); 866 | if (!num_clients) { 867 | sigprocmask(SIG_UNBLOCK, &sigset, NULL); 868 | sigwaitinfo(&sigset, NULL); 869 | continue; 870 | } 871 | 872 | sigprocmask(SIG_UNBLOCK, &sigset, NULL); 873 | pollret = poll(poll_resources.pollfds, num_clients, -1); 874 | if (pollret < 0) { 875 | if (errno == EINTR) { 876 | /* It seems that new client just appered. Renew descriptors. */ 877 | continue; 878 | } 879 | 880 | pep_error("poll() error!"); 881 | } 882 | else if (pollret == 0) { 883 | continue; 884 | } 885 | 886 | num_works = 0; 887 | for (i = 0; i < num_clients; i++) { 888 | pollfd = &poll_resources.pollfds[i]; 889 | if (!pollfd->revents) { 890 | continue; 891 | } 892 | 893 | endp = poll_resources.endpoints[i]; 894 | proxy = endp->owner; 895 | if (proxy->enqueued) { 896 | continue; 897 | } 898 | switch (proxy->status) { 899 | case PST_CONNECT: 900 | { 901 | int ret, connerr, errlen = sizeof(int); 902 | 903 | getsockopt(proxy->dst.fd, SOL_SOCKET, SO_ERROR, 904 | &connerr, &errlen); 905 | if (connerr != 0) { 906 | destroy_proxy(proxy); 907 | break; 908 | } 909 | 910 | ret = pepbuf_init(&proxy->src.buf); 911 | if (ret < 0) { 912 | pep_error("Failed to allocate PEP IN buffer!"); 913 | } 914 | 915 | ret = pepbuf_init(&proxy->dst.buf); 916 | if (ret < 0) { 917 | pepbuf_deinit(&proxy->src.buf); 918 | pep_error("Failed to allocate PEP OUT buffer!"); 919 | } 920 | 921 | proxy->status = PST_OPEN; 922 | setup_socket(proxy->src.fd); 923 | setup_socket(proxy->dst.fd); 924 | } 925 | case PST_OPEN: 926 | { 927 | if (pollfd->revents & (POLLHUP | POLLERR | POLLNVAL)) { 928 | if (proxy->enqueued) { 929 | list_del(&proxy->qnode); 930 | } 931 | 932 | destroy_proxy(proxy); 933 | continue; 934 | } 935 | 936 | if (pollfd->revents & (POLLIN | POLLOUT)) { 937 | list_add2tail(&local_list, &proxy->qnode); 938 | num_works++; 939 | proxy->enqueued = 1; 940 | } 941 | 942 | break; 943 | } 944 | } 945 | } 946 | if (list_is_empty(&local_list)) { 947 | continue; 948 | } 949 | 950 | /* 951 | * Now we're able to give connections with ready I/O status 952 | * to worker threads. Worker threads from PEPsal threads pool 953 | * will preform the I/O according to state of given connection 954 | * and move it back to the ready_queue when I/O job is finished. 955 | * Poller loop will wait until all connections it gave to worker 956 | * threads will be fully handled. 957 | */ 958 | PEPQUEUE_LOCK(&active_queue); 959 | pepqueue_enqueue_list(&active_queue, &local_list, num_works); 960 | 961 | PEPQUEUE_LOCK(&ready_queue); 962 | PEPQUEUE_WAKEUP_WAITERS(&active_queue); 963 | PEPQUEUE_UNLOCK(&active_queue); 964 | 965 | /* Wait until connections are fully handled */ 966 | while (ready_queue.num_items != num_works) { 967 | PEPQUEUE_WAIT(&ready_queue); 968 | } 969 | 970 | list_init_head(&local_list); 971 | pepqueue_dequeue_list(&ready_queue, &local_list); 972 | PEPQUEUE_UNLOCK(&ready_queue); 973 | 974 | /* 975 | * Now it's a time to handle connections after I/O is completed. 976 | * There are only two possible ways to do it: 977 | * 1) Close the connection if an I/O error occured or EOF was reached 978 | * 2) Continue work with connection and renew its I/O status 979 | */ 980 | list_for_each_safe(&local_list, entry, safe) { 981 | proxy = list_entry(entry, struct pep_proxy, qnode); 982 | proxy->enqueued = 0; 983 | for (i = 0; i < PROXY_ENDPOINTS; i++) { 984 | endp = &proxy->endpoints[i]; 985 | iostat = endp->iostat; 986 | if ((iostat & PEP_IOERR) || 987 | ((iostat & PEP_IOEOF) && pepbuf_empty(&endp->buf))) { 988 | list_del(&proxy->qnode); 989 | destroy_proxy(proxy); 990 | break; 991 | } 992 | 993 | endp->iostat &= ~(PEP_IOWDONE | PEP_IORDONE | PEP_IOEOF); 994 | } 995 | } 996 | } 997 | } 998 | 999 | static void *workers_loop(void __attribute__((unused)) *unused) 1000 | { 1001 | struct pep_proxy *proxy; 1002 | struct list_head local_list; 1003 | int ret, ready_items; 1004 | 1005 | PEPQUEUE_LOCK(&active_queue); 1006 | for (;;) { 1007 | list_init_head(&local_list); 1008 | ready_items = 0; 1009 | PEPQUEUE_WAIT(&active_queue); 1010 | 1011 | while (active_queue.num_items > 0) { 1012 | proxy = pepqueue_dequeue(&active_queue); 1013 | PEPQUEUE_UNLOCK(&active_queue); 1014 | 1015 | pep_proxy_data(&proxy->src, &proxy->dst); 1016 | pep_proxy_data(&proxy->dst, &proxy->src); 1017 | 1018 | proxy->last_rxtx = time(NULL); 1019 | list_add2tail(&local_list, &proxy->qnode); 1020 | ready_items++; 1021 | 1022 | PEPQUEUE_LOCK(&active_queue); 1023 | } 1024 | 1025 | PEPQUEUE_LOCK(&ready_queue); 1026 | pepqueue_enqueue_list(&ready_queue, &local_list, ready_items); 1027 | PEPQUEUE_UNLOCK(&ready_queue); 1028 | PEPQUEUE_WAKEUP_WAITERS(&ready_queue); 1029 | } 1030 | } 1031 | 1032 | static void *timer_sch_loop(void __attribute__((unused)) *unused) 1033 | { 1034 | struct timeval last_log_evt_time = {0U, 0U}, last_gc_evt_time = {0U, 0U}, now; 1035 | 1036 | if (logger.filename) { 1037 | PEP_DEBUG("Setting up PEP logger"); 1038 | logger.file = fopen(logger.filename, "w+"); 1039 | if (!logger.file) { 1040 | pep_error("Failed to open log file %s!", logger.filename); 1041 | } 1042 | gettimeofday(&last_log_evt_time, 0); 1043 | gettimeofday(&last_gc_evt_time, 0); 1044 | } 1045 | 1046 | for(;;) { 1047 | gettimeofday(&now, 0); 1048 | if (logger.file && now.tv_sec > last_log_evt_time.tv_sec + PEPLOGGER_INTERVAL) { 1049 | logger_fn(); 1050 | gettimeofday(&last_log_evt_time, 0); 1051 | } 1052 | 1053 | if (now.tv_sec > last_gc_evt_time.tv_sec + gcc_interval) { 1054 | garbage_connections_collector(); 1055 | gettimeofday(&last_gc_evt_time, 0); 1056 | } 1057 | sleep(2); 1058 | } 1059 | } 1060 | 1061 | static void init_pep_threads(void) 1062 | { 1063 | int ret; 1064 | PEP_DEBUG("Creating listener thread"); 1065 | ret = pthread_create(&listener, NULL, listener_loop, NULL); 1066 | if (ret) { 1067 | pep_error("Failed to create the listener thread! [RET = %d]", ret); 1068 | } 1069 | 1070 | PEP_DEBUG("Creating poller thread"); 1071 | ret = pthread_create(&poller, NULL, poller_loop, NULL); 1072 | if (ret < 0) { 1073 | pep_error("Failed to create the poller thread! [RET = %d]", ret); 1074 | } 1075 | PEP_DEBUG("Creating timer_sch thread"); 1076 | ret = pthread_create(&timer_sch, NULL, timer_sch_loop, NULL); 1077 | if (ret < 0) { 1078 | pep_error("Failed to create the timer_sch thread! [RET = %d]", ret); 1079 | } 1080 | } 1081 | 1082 | static void init_pep_queues(void) 1083 | { 1084 | PEP_DEBUG("Initialize PEP queue for active connections..."); 1085 | pepqueue_init(&active_queue); 1086 | 1087 | PEP_DEBUG("Initialize PEP queue for handled connections..."); 1088 | pepqueue_init(&ready_queue); 1089 | } 1090 | 1091 | static void create_threads_pool(int num_threads) 1092 | { 1093 | int ret, i; 1094 | 1095 | workers = calloc(num_threads, sizeof(pthread_t)); 1096 | if (!workers) { 1097 | pep_error("Failed to create threads pool of %d threads!", 1098 | num_threads); 1099 | } 1100 | for (i = 0; i < num_threads; i++) { 1101 | ret = pthread_create(&workers[i], NULL, 1102 | workers_loop, NULL); 1103 | if (ret) { 1104 | pep_error("Failed to create %d thread in pool!", i + 1); 1105 | } 1106 | } 1107 | } 1108 | 1109 | int main(int argc, char *argv[]) 1110 | { 1111 | int c, ret, numfds; 1112 | void *valptr; 1113 | sigset_t sigset; 1114 | 1115 | memset(&logger, 0, sizeof(logger)); 1116 | while (1) { 1117 | int option_index = 0; 1118 | static struct option long_options[] = { 1119 | {"daemon", 1, 0, 'd'}, 1120 | {"verbose", 1, 0, 'v'}, 1121 | {"help", 0, 0, 'h'}, 1122 | {"fastopen", 0, 0, 'f'}, 1123 | {"port", 1, 0, 'p'}, 1124 | {"version", 0, 0, 'V'}, 1125 | {"address", 1, 0, 'a'}, 1126 | {"logfile", 1, 0, 'l'}, 1127 | {"gcc_interval", 1, 0, 'g'}, 1128 | {"plifetime", 1, 0,'t'}, 1129 | {"conns", 1, 0, 'c'}, 1130 | {0, 0, 0, 0} 1131 | }; 1132 | 1133 | c = getopt_long(argc, argv, "dvVhfp:a:l:g:t:c:", 1134 | long_options, &option_index); 1135 | if (c == -1) 1136 | break; 1137 | 1138 | switch (c) { 1139 | case 'd': 1140 | background = 1; 1141 | break; 1142 | case 'v': 1143 | DEBUG = 1; 1144 | break; 1145 | case 'h': 1146 | usage(argv[0]); //implies exit 1147 | break; 1148 | case 'f': 1149 | fastopen = 1; 1150 | break; 1151 | case 'p': 1152 | portnum = atoi(optarg); 1153 | break; 1154 | case 'a': 1155 | strncpy(pepsal_ip_addr, optarg, 19); 1156 | break; 1157 | case 'l': 1158 | logger.filename = optarg; 1159 | break; 1160 | case 't': 1161 | pending_conn_lifetime = atoi(optarg); 1162 | break; 1163 | case 'g': 1164 | gcc_interval = atoi(optarg); 1165 | break; 1166 | case 'c': 1167 | max_conns = atoi(optarg); 1168 | if ((max_conns < PEP_MIN_CONNS) || 1169 | (max_conns > PEP_MAX_CONNS)) { 1170 | usage(argv[0]); 1171 | } 1172 | 1173 | break; 1174 | case 'V': 1175 | printf("PEPSal ver. %s\n", VERSION); 1176 | exit(0); 1177 | } 1178 | } 1179 | openlog(PROGRAM_NAME, LOG_PID, LOG_DAEMON); 1180 | 1181 | if (background) { 1182 | PEP_DEBUG("Daemonizing..."); 1183 | if (daemon(0, 1) < 0) { 1184 | pep_error("daemon() failed!"); 1185 | } 1186 | } 1187 | 1188 | PEP_DEBUG("Init SYN table with %d max connections", max_conns); 1189 | ret = syntab_init(max_conns); 1190 | if (ret < 0) { 1191 | pep_error("Failed to initialize SYN table!"); 1192 | } 1193 | 1194 | poll_resources.num_pollfds = numfds = max_conns * 2; 1195 | poll_resources.pollfds = calloc(numfds, sizeof(struct pollfd)); 1196 | if (!poll_resources.pollfds) { 1197 | pep_error("Failed to allocate %zd bytes for pollfds array!", 1198 | numfds * sizeof(struct pollfd)); 1199 | } 1200 | 1201 | poll_resources.endpoints = calloc(numfds, sizeof(struct pep_endpoint *)); 1202 | if (!poll_resources.endpoints) { 1203 | free(poll_resources.pollfds); 1204 | pep_error("Failed to allocate %zd bytes for pdescrs array!", 1205 | numfds * sizeof(struct pep_endpoint *)); 1206 | } 1207 | 1208 | sigemptyset(&sigset); 1209 | sigaddset(&sigset, POLLER_NEWCONN_SIG); 1210 | sigaddset(&sigset, SIGPIPE); 1211 | sigprocmask(SIG_BLOCK, &sigset, NULL); 1212 | 1213 | init_pep_queues(); 1214 | init_pep_threads(); 1215 | create_threads_pool(PEPPOOL_THREADS); 1216 | 1217 | PEP_DEBUG("Pepsal started..."); 1218 | pthread_join(listener, &valptr); 1219 | pthread_join(poller, &valptr); 1220 | pthread_join(timer_sch, &valptr); 1221 | PEP_DEBUG("exiting...\n"); 1222 | closelog(); 1223 | return 0; 1224 | } 1225 | --------------------------------------------------------------------------------