├── man ├── Makefile.am └── udpproxy.8 ├── Makefile.am ├── src ├── Makefile.am ├── proxy.h ├── cksum.c ├── log.c ├── state.c ├── splay.h └── proxy.c ├── .gitignore ├── configure.ac └── README.md /man/Makefile.am: -------------------------------------------------------------------------------- 1 | man_MANS = udpproxy.8 2 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = src man 2 | dist_doc_DATA = README.md 3 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | bin_PROGRAMS = udpproxy 2 | udpproxy_SOURCES = proxy.c proxy.h log.c state.c cksum.c 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.o 3 | *.lo 4 | *.a 5 | *.la 6 | /build*/ 7 | 8 | # autotools stuff 9 | /m4/lt*.m4 10 | /m4/libtool.m4 11 | /aclocal.m4 12 | /ltmain.sh 13 | /install-sh 14 | /config.guess 15 | /config.h.in 16 | /config.sub 17 | /autom4te.cache/ 18 | /compile 19 | /configure 20 | /depcomp 21 | /missing 22 | /test-driver 23 | 24 | # automake 25 | /Makefile.in 26 | /src/Makefile.in 27 | /man/Makefile.in 28 | /ar-lib 29 | 30 | # cscope 31 | /cscope.* 32 | /GPATH 33 | /GRTAGS 34 | /GSYMS 35 | /GTAGS 36 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # -*- Autoconf -*- 2 | # Process this file with autoconf to produce a configure script. 3 | 4 | AC_PREREQ(2.61) 5 | AC_INIT(udpproxy, 0.2, bernat@luffy.cx) 6 | AM_INIT_AUTOMAKE([foreign -Wall -Werror]) 7 | AM_MAINTAINER_MODE 8 | m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES(yes)]) 9 | AC_CONFIG_SRCDIR([src/proxy.c]) 10 | AC_CONFIG_HEADER([config.h]) 11 | 12 | # Checks for programs. 13 | AC_PROG_CC 14 | 15 | # Checks for libraries. 16 | AC_CHECK_LIB([nfnetlink], [nfnl_fd], [], 17 | [AC_MSG_NOTICE([*** without nfnetlink library, only client support will be enabled]) 18 | AC_DEFINE([CLIENT_ONLY], 1, [Only compile client support])]) 19 | AC_CHECK_LIB([netfilter_queue], [nfq_open], [], 20 | [AC_MSG_NOTICE([*** without netfilter-queue, only client support will be enabled]) 21 | AC_DEFINE([CLIENT_ONLY], 1, [Only compile client support])]) 22 | AC_CHECK_LIB([event], [event_set], [], 23 | [AC_MSG_ERROR([*** libevent was not found])]) 24 | 25 | # Checks for header files. 26 | AC_CHECK_HEADERS([sys/tree.h]) 27 | 28 | # Checks for typedefs, structures, and compiler characteristics. 29 | AC_C_CONST 30 | 31 | # Checks for library functions. 32 | 33 | AC_DEFINE([NFQ_PACKET_BUFFER_SIZE],4096,[Define the size of NFQ buffer]) 34 | 35 | AC_CONFIG_FILES([Makefile 36 | man/Makefile 37 | src/Makefile]) 38 | AC_OUTPUT 39 | -------------------------------------------------------------------------------- /src/proxy.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008 Vincent Bernat 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef _PROXY_H 18 | #define _PROXY_H 1 19 | 20 | #ifdef HAVE_CONFIG_H 21 | #include 22 | #endif 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #define UDP_UNCONFIRMED_TTL 2 30 | #define UDP_CONFIRMED_TTL 30 31 | 32 | /* log.c */ 33 | void log_init(int); 34 | void log_warn(const char *, ...); 35 | #define LLOG_WARN(x,...) log_warn("%s: " x, __FUNCTION__, ##__VA_ARGS__) 36 | void log_warnx(const char *, ...); 37 | #define LLOG_WARNX(x,...) log_warnx("%s: " x, __FUNCTION__, ##__VA_ARGS__) 38 | void log_info(const char *, ...); 39 | #define LLOG_INFO(x,...) log_info("%s: " x, __FUNCTION__, ##__VA_ARGS__) 40 | void log_debug(const char *, ...); 41 | #define LLOG_DEBUG(x,...) log_debug("%s: " x, __FUNCTION__, ##__VA_ARGS__) 42 | void fatal(const char *); 43 | void fatalx(const char *); 44 | 45 | /* cksum.c */ 46 | u_int16_t cksum(unsigned char *, int); 47 | 48 | /* state.c */ 49 | struct state { 50 | struct in_addr source; 51 | struct in_addr destination; 52 | u_int16_t sport; 53 | u_int16_t dport; 54 | int socket; 55 | struct event ev; 56 | time_t lastchange; 57 | long int count; 58 | }; 59 | struct states; /* Opaque type */ 60 | 61 | struct states* state_initialize(void); 62 | void state_destroy(struct states *); 63 | struct state* state_get_or_create(struct states *, struct state *, 64 | void (*)(int, short, void *)); 65 | struct state* state_get(struct states *, struct state *); 66 | void state_expire(struct states *, int, int); 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Introduction 2 | ============ 3 | 4 | udpproxy allows to proxy UDP flows using [Netfilter queuing 5 | capabilities][1]. This is not just an UDP relay: the destination does 6 | not need to be known in advance. First, flows to be proxied need to be 7 | selected with the help of iptables: 8 | 9 | For example: 10 | 11 | # iptables -I OUTPUT -p udp --dport 161 \ 12 | --destination 172.16.100.0/23 -j NFQUEUE --queue-num 10 13 | 14 | Then, udpproxy is launched and will relay the packets sent to queue 15 | 10: 16 | 17 | # udpproxy -e "ssh somehost ./udpproxy" -q 10 18 | 19 | The remote udpproxy does not have to run as root. It only uses 20 | unpriviledged operations. 21 | 22 | udpproxy does not handle fragmentation at all. If you use programs 23 | that sends large packets, you should create a dummy interface with 24 | a large MTU and route packets to this interface. udpproxy will then 25 | receives the packets unfragmented and forward them to the remote proxy 26 | which will relies on operating system to handle fragmentation. 27 | 28 | [1]: http://www.netfilter.org/projects/libnetfilter_queue/index.html 29 | 30 | Installation 31 | ============ 32 | 33 | You can get udpproxy from the git repository: 34 | 35 | $ git clone git://git.luffy.cx/udpproxy.git 36 | 37 | udpproxy uses autotools. So, you should get ready with: 38 | 39 | $ autoreconf -i 40 | $ ./configure 41 | $ make 42 | $ sudo make install 43 | 44 | You need [libevent][4] and [libnetfilter_queue][5]. If you don't have 45 | the latest one, only client-side operations will be allowed. 46 | 47 | [4]: http://monkey.org/~provos/libevent/ 48 | [5]: http://www.netfilter.org/projects/libnetfilter_queue/index.html 49 | 50 | Usage 51 | ===== 52 | 53 | You can get help with: 54 | 55 | $ udpproxy -h 56 | 57 | A simple invocation will start udpproxy stating the command to invoke 58 | the remote udpproxy with `-e` switch and selecting the right Netfilter 59 | queue with `-q` switch: 60 | 61 | $ udpproxy -e "ssh somehost ./udpproxy" -q 10 62 | 63 | On server-side, udpproxy needs root privileges. However, the remote 64 | one does not need to and usually, you want to run ssh command as an 65 | unprivileged user. You can use `-u` and `-g` switches for this: 66 | 67 | # udpproxy -u 1000 -g 100 -e "ssh somehost ./udpproxy" -q 10 68 | 69 | Or, with the help of the shell: 70 | 71 | $ sudo udpproxy -u $(id -u) -g $(id -g) -e "ssh somehost ./udpproxy" -q 10 72 | -------------------------------------------------------------------------------- /src/cksum.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1994 3 | * The Regents of the University of California. All rights reserved. 4 | * 5 | * Redistribution and use in source and binary form, with or without 6 | * modifications, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. All advertising materials mentioning features or use of this software 14 | * must display the following acknowledgement: 15 | * This product includes software developed by the University of 16 | * California, Berkeley and its contributors. 17 | * 4. Neither the name of the University nor the names of its contributors 18 | * may be used to endorse or promote products derived from this software 19 | * without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND 22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSITUTE GOODS 27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 | * SUCH DAMAGE. 32 | */ 33 | 34 | #include 35 | 36 | u_int16_t 37 | cksum(unsigned char *ip, int len) 38 | { 39 | u_int32_t sum = 0; /* assume 32 bit long, 16 bit short */ 40 | 41 | while(len > 1){ 42 | sum += *((u_int16_t*) ip++); 43 | if(sum & 0x80000000) /* if high order bit set, fold */ 44 | sum = (sum & 0xFFFF) + (sum >> 16); 45 | len -= 2; 46 | } 47 | 48 | if(len) /* take care of left over byte */ 49 | sum += (u_int16_t)*ip; 50 | 51 | while(sum>>16) 52 | sum = (sum & 0xFFFF) + (sum >> 16); 53 | 54 | return (u_int16_t)(~sum & 0xFFFF); 55 | } 56 | 57 | /* taken from TCP/IP Illustrated Vol. 2(1995) by Gary R. Wright and W. Richard 58 | Stevens. Page 236. Adapted. */ 59 | -------------------------------------------------------------------------------- /man/udpproxy.8: -------------------------------------------------------------------------------- 1 | .\" Copyright (c) 2008 Vincent Bernat 2 | .\" 3 | .\" Permission to use, copy, modify, and distribute this software for any 4 | .\" purpose with or without fee is hereby granted, provided that the above 5 | .\" copyright notice and this permission notice appear in all copies. 6 | .\" 7 | .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | .\" 15 | .Dd $Mdocdate: August 9 2008 $ 16 | .Dt UDPPROXY 8 17 | .Os 18 | .Sh NAME 19 | .Nm udpproxy 20 | .Nd UDP proxy using Netfilter netlink-queue 21 | .Sh SYNOPSIS 22 | .Nm 23 | .Op Fl d 24 | .Op Fl q Ar queue number 25 | .Op Fl e Ar remote command 26 | .Op Fl u Ar uid 27 | .Op Fl g Ar gid 28 | .Sh DESCRIPTION 29 | .Nm 30 | is a daemon intercepting UDP packets with the help of 31 | .Em Netfilter 32 | netlink-queue support. Packets are then sent to another process to be 33 | processed. This process can be a local process or a remote process via 34 | .Xr ssh 1 . 35 | .Pp 36 | .Nm 37 | needs to receive packets from Netfilter using 38 | .Em NFQUEUE 39 | target. You need to setup a rule like this: 40 | .Bd -literal -offset indent 41 | iptables -A OUTPUT -p udp --dport 161 \\ 42 | --destination 172.16.100.0/23 -j NFQUEUE --queue-num 10 43 | .Ed 44 | .Pp 45 | All packets matching the rule will be sent to 46 | .Nm 47 | that will send them to the remote process that will act as a proxy. 48 | .Pp 49 | The options are as follows: 50 | .Bl -tag -width Ds 51 | .It Fl d 52 | Enable debug. 53 | If this option is specified, 54 | .Nm 55 | will output debug messages to 56 | .Em stderr . 57 | .It Fl q Ar queue number 58 | Specify the queue number to listen to. In the example above, this is 59 | 10. Without this option, 60 | .Nm 61 | will act as the remote proxy and will wait for packets to be sent on 62 | its standard input. 63 | .It Fl e Ar remote command 64 | Specify how to invoke the remote process. If this option is not 65 | specified, 66 | .Nm 67 | will invoke itself. You can use 68 | .Xr ssh 1 : 69 | .Bd -literal -offset indent 70 | udpproxy -e "ssh remotehost udpproxy" 71 | .Ed 72 | .It Fl u Ar uid 73 | Invoke the remote process as the given UID. 74 | .Nm 75 | needs root 76 | privileges to receives packets from 77 | .Em Netfilter 78 | and to send back packets when receiving answers. The remote 79 | .Nm 80 | does not need any privilege. 81 | .It Fl g Ar gid 82 | Invoke the remote process as the given GID. 83 | .El 84 | .Sh SEE ALSO 85 | .Xr ssh 1 86 | .Sh AUTHOR 87 | .An -nosplit 88 | The 89 | .Nm 90 | program was written by 91 | .An Vincent Bernat Aq bernat@luffy.cx . 92 | -------------------------------------------------------------------------------- /src/log.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: log.c,v 1.11 2007/12/07 17:17:00 reyk Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 2003, 2004 Henning Brauer 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 | * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 | * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #define _GNU_SOURCE 1 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | void log_init(int); 31 | void log_warn(const char *, ...); 32 | void log_warnx(const char *, ...); 33 | void log_info(const char *, ...); 34 | void log_debug(const char *, ...); 35 | void fatal(const char *); 36 | void fatalx(const char *); 37 | 38 | int debug; 39 | 40 | void vlog(int, const char *, va_list); 41 | void logit(int, const char *, ...); 42 | 43 | 44 | void 45 | log_init(int n_debug) 46 | { 47 | debug = n_debug; 48 | 49 | tzset(); 50 | } 51 | 52 | 53 | void 54 | logit(int pri, const char *fmt, ...) 55 | { 56 | va_list ap; 57 | 58 | va_start(ap, fmt); 59 | vlog(pri, fmt, ap); 60 | va_end(ap); 61 | } 62 | 63 | void 64 | vlog(int pri, const char *fmt, va_list ap) 65 | { 66 | char *nfmt; 67 | 68 | /* best effort in out of mem situations */ 69 | if (asprintf(&nfmt, "%s\n", fmt) == -1) { 70 | vfprintf(stderr, fmt, ap); 71 | fprintf(stderr, "\n"); 72 | } else { 73 | vfprintf(stderr, nfmt, ap); 74 | free(nfmt); 75 | } 76 | fflush(stderr); 77 | } 78 | 79 | 80 | void 81 | log_warn(const char *emsg, ...) 82 | { 83 | char *nfmt; 84 | va_list ap; 85 | 86 | /* best effort to even work in out of memory situations */ 87 | if (emsg == NULL) 88 | logit(LOG_CRIT, "%s", strerror(errno)); 89 | else { 90 | va_start(ap, emsg); 91 | 92 | if (asprintf(&nfmt, "%s: %s", emsg, strerror(errno)) == -1) { 93 | /* we tried it... */ 94 | vlog(LOG_CRIT, emsg, ap); 95 | logit(LOG_CRIT, "%s", strerror(errno)); 96 | } else { 97 | vlog(LOG_CRIT, nfmt, ap); 98 | free(nfmt); 99 | } 100 | va_end(ap); 101 | } 102 | } 103 | 104 | void 105 | log_warnx(const char *emsg, ...) 106 | { 107 | va_list ap; 108 | 109 | va_start(ap, emsg); 110 | vlog(LOG_CRIT, emsg, ap); 111 | va_end(ap); 112 | } 113 | 114 | void 115 | log_info(const char *emsg, ...) 116 | { 117 | va_list ap; 118 | 119 | va_start(ap, emsg); 120 | vlog(LOG_INFO, emsg, ap); 121 | va_end(ap); 122 | } 123 | 124 | void 125 | log_debug(const char *emsg, ...) 126 | { 127 | va_list ap; 128 | 129 | if (debug > 0) { 130 | va_start(ap, emsg); 131 | vlog(LOG_DEBUG, emsg, ap); 132 | va_end(ap); 133 | } 134 | } 135 | 136 | void 137 | fatal(const char *emsg) 138 | { 139 | if (emsg == NULL) 140 | logit(LOG_CRIT, "fatal: %s", strerror(errno)); 141 | else 142 | if (errno) 143 | logit(LOG_CRIT, "fatal: %s: %s", 144 | emsg, strerror(errno)); 145 | else 146 | logit(LOG_CRIT, "fatal: %s", emsg); 147 | 148 | exit(1); 149 | } 150 | 151 | void 152 | fatalx(const char *emsg) 153 | { 154 | errno = 0; 155 | fatal(emsg); 156 | } 157 | -------------------------------------------------------------------------------- /src/state.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008 Vincent Bernat 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "proxy.h" 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #if HAVE_SYS_TREE_H 24 | #include 25 | #else 26 | #include "splay.h" 27 | #endif 28 | 29 | struct statenode { 30 | SPLAY_ENTRY(statenode) entry; 31 | struct state state; 32 | }; 33 | 34 | int state_compare(struct statenode *, struct statenode *); 35 | SPLAY_HEAD(states, statenode); /* Define struct states */ 36 | SPLAY_PROTOTYPE(states, statenode, entry, state_compare); 37 | 38 | SPLAY_GENERATE(states, statenode, entry, state_compare); 39 | 40 | int 41 | state_compare(struct statenode *s1, struct statenode *s2) 42 | { 43 | int rc; 44 | if ((rc = s1->state.sport - s2->state.sport) == 0) 45 | if ((rc = s1->state.destination.s_addr - 46 | s2->state.destination.s_addr) == 0) 47 | if ((rc = s1->state.dport - s2->state.dport) == 0) 48 | rc = s1->state.source.s_addr - s2->state.source.s_addr; 49 | return rc; 50 | } 51 | 52 | struct states* 53 | state_initialize() 54 | { 55 | struct states *new, empty = SPLAY_INITIALIZER(statenode); 56 | if ((new = (struct states *)malloc(sizeof(struct states))) == NULL) { 57 | LLOG_WARN("unable to allocate states tree"); 58 | return NULL; 59 | } 60 | memcpy(new, &empty, sizeof(struct states)); 61 | return new; 62 | } 63 | 64 | void 65 | state_destroy(struct states *s) 66 | { 67 | struct statenode *var, *nxt; 68 | for (var = SPLAY_MIN(states, s); var != NULL; var = nxt) { 69 | nxt = SPLAY_NEXT(states, s, var); 70 | SPLAY_REMOVE(states, s, var); 71 | close(var->state.socket); 72 | free(var); 73 | } 74 | } 75 | 76 | struct state* 77 | state_get_or_create(struct states *ss, struct state *s, 78 | void (*callback)(int, short, void *)) 79 | { 80 | struct statenode find, *res; 81 | struct sockaddr_in dest; 82 | int opt; 83 | 84 | memcpy(&find.state, s, sizeof(struct state)); 85 | if ((res = SPLAY_FIND(states, ss, &find)) != NULL) 86 | return &res->state; 87 | 88 | LLOG_DEBUG("create new state"); 89 | if ((res = (struct statenode *) 90 | calloc(1, sizeof(struct statenode))) == NULL) { 91 | LLOG_WARN("not enough memory"); 92 | return NULL; 93 | } 94 | 95 | memcpy(&res->state, s, sizeof(struct state)); 96 | if ((res->state.socket = socket(AF_INET, 97 | SOCK_DGRAM, 0)) == -1) { 98 | LLOG_WARN("unable to allocate socket"); 99 | free(res); 100 | return NULL; 101 | } 102 | 103 | opt = IP_PMTUDISC_DONT; 104 | setsockopt(res->state.socket, IPPROTO_IP, IP_MTU_DISCOVER, &opt, sizeof(opt)); 105 | 106 | memset(&dest, 0, sizeof(struct sockaddr_in)); 107 | dest.sin_family = AF_INET; 108 | dest.sin_port = htons(res->state.dport); 109 | memcpy(&dest.sin_addr, &res->state.destination, 110 | sizeof(struct in_addr)); 111 | if (connect(res->state.socket, (struct sockaddr *)&dest, 112 | sizeof(struct sockaddr_in)) == -1) { 113 | LLOG_WARN("unable to connect to remote host"); 114 | close(res->state.socket); 115 | free(res); 116 | return NULL; 117 | } 118 | event_set(&res->state.ev, res->state.socket, 119 | EV_READ | EV_PERSIST, callback, &res->state); 120 | if (event_add(&res->state.ev, NULL) == -1) 121 | fatal("unable to set event for UDP socket"); 122 | 123 | SPLAY_INSERT(states, ss, res); 124 | 125 | return &res->state; 126 | } 127 | 128 | struct state* 129 | state_get(struct states *ss, struct state *s) 130 | { 131 | struct statenode *res, find; 132 | memcpy(&find.state, s, sizeof(struct state)); 133 | if ((res = SPLAY_FIND(states, ss, &find)) != NULL) 134 | return &res->state; 135 | return NULL; 136 | } 137 | 138 | void 139 | state_expire(struct states *ss, int delay1, int delay2) 140 | { 141 | struct statenode *var, *nxt; 142 | int i = 0; 143 | time_t cur = time(NULL); 144 | for (var = SPLAY_MIN(states, ss); var != NULL; var = nxt) { 145 | nxt = SPLAY_NEXT(states, ss, var); 146 | if (((var->state.count < 2) && 147 | ((cur - var->state.lastchange) > delay1)) || 148 | ((var->state.count > 1) && 149 | ((cur - var->state.lastchange) > delay2))) { 150 | event_del(&var->state.ev); 151 | close(var->state.socket); 152 | SPLAY_REMOVE(states, ss, var); 153 | free(var); 154 | i++; 155 | } 156 | } 157 | if (i > 0) 158 | LLOG_DEBUG("%d states have been expired", i); 159 | } 160 | -------------------------------------------------------------------------------- /src/splay.h: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: tree.h,v 1.11 2008/05/11 22:19:09 millert Exp $ */ 2 | /* 3 | * Copyright 2002 Niels Provos 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #ifndef _SPLAY_H_ 28 | #define _SPLAY_H_ 29 | 30 | /* 31 | * A splay tree is a self-organizing data structure. Every operation 32 | * on the tree causes a splay to happen. The splay moves the requested 33 | * node to the root of the tree and partly rebalances it. 34 | * 35 | * This has the benefit that request locality causes faster lookups as 36 | * the requested nodes move to the top of the tree. On the other hand, 37 | * every lookup causes memory writes. 38 | * 39 | * The Balance Theorem bounds the total access time for m operations 40 | * and n inserts on an initially empty tree as O((m + n)lg n). The 41 | * amortized cost for a sequence of m accesses to a splay tree is O(lg n); 42 | */ 43 | 44 | #define SPLAY_HEAD(name, type) \ 45 | struct name { \ 46 | struct type *sph_root; /* root of the tree */ \ 47 | } 48 | 49 | #define SPLAY_INITIALIZER(root) \ 50 | { NULL } 51 | 52 | #define SPLAY_INIT(root) do { \ 53 | (root)->sph_root = NULL; \ 54 | } while (0) 55 | 56 | #define SPLAY_ENTRY(type) \ 57 | struct { \ 58 | struct type *spe_left; /* left element */ \ 59 | struct type *spe_right; /* right element */ \ 60 | } 61 | 62 | #define SPLAY_LEFT(elm, field) (elm)->field.spe_left 63 | #define SPLAY_RIGHT(elm, field) (elm)->field.spe_right 64 | #define SPLAY_ROOT(head) (head)->sph_root 65 | #define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) 66 | 67 | /* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ 68 | #define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ 69 | SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ 70 | SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ 71 | (head)->sph_root = tmp; \ 72 | } while (0) 73 | 74 | #define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ 75 | SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ 76 | SPLAY_LEFT(tmp, field) = (head)->sph_root; \ 77 | (head)->sph_root = tmp; \ 78 | } while (0) 79 | 80 | #define SPLAY_LINKLEFT(head, tmp, field) do { \ 81 | SPLAY_LEFT(tmp, field) = (head)->sph_root; \ 82 | tmp = (head)->sph_root; \ 83 | (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ 84 | } while (0) 85 | 86 | #define SPLAY_LINKRIGHT(head, tmp, field) do { \ 87 | SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ 88 | tmp = (head)->sph_root; \ 89 | (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ 90 | } while (0) 91 | 92 | #define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ 93 | SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ 94 | SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ 95 | SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ 96 | SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ 97 | } while (0) 98 | 99 | /* Generates prototypes and inline functions */ 100 | 101 | #define SPLAY_PROTOTYPE(name, type, field, cmp) \ 102 | void name##_SPLAY(struct name *, struct type *); \ 103 | void name##_SPLAY_MINMAX(struct name *, int); \ 104 | struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ 105 | struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ 106 | \ 107 | /* Finds the node with the same key as elm */ \ 108 | static __inline struct type * \ 109 | name##_SPLAY_FIND(struct name *head, struct type *elm) \ 110 | { \ 111 | if (SPLAY_EMPTY(head)) \ 112 | return(NULL); \ 113 | name##_SPLAY(head, elm); \ 114 | if ((cmp)(elm, (head)->sph_root) == 0) \ 115 | return (head->sph_root); \ 116 | return (NULL); \ 117 | } \ 118 | \ 119 | static __inline struct type * \ 120 | name##_SPLAY_NEXT(struct name *head, struct type *elm) \ 121 | { \ 122 | name##_SPLAY(head, elm); \ 123 | if (SPLAY_RIGHT(elm, field) != NULL) { \ 124 | elm = SPLAY_RIGHT(elm, field); \ 125 | while (SPLAY_LEFT(elm, field) != NULL) { \ 126 | elm = SPLAY_LEFT(elm, field); \ 127 | } \ 128 | } else \ 129 | elm = NULL; \ 130 | return (elm); \ 131 | } \ 132 | \ 133 | static __inline struct type * \ 134 | name##_SPLAY_MIN_MAX(struct name *head, int val) \ 135 | { \ 136 | name##_SPLAY_MINMAX(head, val); \ 137 | return (SPLAY_ROOT(head)); \ 138 | } 139 | 140 | /* Main splay operation. 141 | * Moves node close to the key of elm to top 142 | */ 143 | #define SPLAY_GENERATE(name, type, field, cmp) \ 144 | struct type * \ 145 | name##_SPLAY_INSERT(struct name *head, struct type *elm) \ 146 | { \ 147 | if (SPLAY_EMPTY(head)) { \ 148 | SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ 149 | } else { \ 150 | int __comp; \ 151 | name##_SPLAY(head, elm); \ 152 | __comp = (cmp)(elm, (head)->sph_root); \ 153 | if(__comp < 0) { \ 154 | SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ 155 | SPLAY_RIGHT(elm, field) = (head)->sph_root; \ 156 | SPLAY_LEFT((head)->sph_root, field) = NULL; \ 157 | } else if (__comp > 0) { \ 158 | SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ 159 | SPLAY_LEFT(elm, field) = (head)->sph_root; \ 160 | SPLAY_RIGHT((head)->sph_root, field) = NULL; \ 161 | } else \ 162 | return ((head)->sph_root); \ 163 | } \ 164 | (head)->sph_root = (elm); \ 165 | return (NULL); \ 166 | } \ 167 | \ 168 | struct type * \ 169 | name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ 170 | { \ 171 | struct type *__tmp; \ 172 | if (SPLAY_EMPTY(head)) \ 173 | return (NULL); \ 174 | name##_SPLAY(head, elm); \ 175 | if ((cmp)(elm, (head)->sph_root) == 0) { \ 176 | if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ 177 | (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ 178 | } else { \ 179 | __tmp = SPLAY_RIGHT((head)->sph_root, field); \ 180 | (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ 181 | name##_SPLAY(head, elm); \ 182 | SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ 183 | } \ 184 | return (elm); \ 185 | } \ 186 | return (NULL); \ 187 | } \ 188 | \ 189 | void \ 190 | name##_SPLAY(struct name *head, struct type *elm) \ 191 | { \ 192 | struct type __node, *__left, *__right, *__tmp; \ 193 | int __comp; \ 194 | \ 195 | SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ 196 | __left = __right = &__node; \ 197 | \ 198 | while ((__comp = (cmp)(elm, (head)->sph_root))) { \ 199 | if (__comp < 0) { \ 200 | __tmp = SPLAY_LEFT((head)->sph_root, field); \ 201 | if (__tmp == NULL) \ 202 | break; \ 203 | if ((cmp)(elm, __tmp) < 0){ \ 204 | SPLAY_ROTATE_RIGHT(head, __tmp, field); \ 205 | if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ 206 | break; \ 207 | } \ 208 | SPLAY_LINKLEFT(head, __right, field); \ 209 | } else if (__comp > 0) { \ 210 | __tmp = SPLAY_RIGHT((head)->sph_root, field); \ 211 | if (__tmp == NULL) \ 212 | break; \ 213 | if ((cmp)(elm, __tmp) > 0){ \ 214 | SPLAY_ROTATE_LEFT(head, __tmp, field); \ 215 | if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ 216 | break; \ 217 | } \ 218 | SPLAY_LINKRIGHT(head, __left, field); \ 219 | } \ 220 | } \ 221 | SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ 222 | } \ 223 | \ 224 | /* Splay with either the minimum or the maximum element \ 225 | * Used to find minimum or maximum element in tree. \ 226 | */ \ 227 | void name##_SPLAY_MINMAX(struct name *head, int __comp) \ 228 | { \ 229 | struct type __node, *__left, *__right, *__tmp; \ 230 | \ 231 | SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ 232 | __left = __right = &__node; \ 233 | \ 234 | while (1) { \ 235 | if (__comp < 0) { \ 236 | __tmp = SPLAY_LEFT((head)->sph_root, field); \ 237 | if (__tmp == NULL) \ 238 | break; \ 239 | if (__comp < 0){ \ 240 | SPLAY_ROTATE_RIGHT(head, __tmp, field); \ 241 | if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ 242 | break; \ 243 | } \ 244 | SPLAY_LINKLEFT(head, __right, field); \ 245 | } else if (__comp > 0) { \ 246 | __tmp = SPLAY_RIGHT((head)->sph_root, field); \ 247 | if (__tmp == NULL) \ 248 | break; \ 249 | if (__comp > 0) { \ 250 | SPLAY_ROTATE_LEFT(head, __tmp, field); \ 251 | if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ 252 | break; \ 253 | } \ 254 | SPLAY_LINKRIGHT(head, __left, field); \ 255 | } \ 256 | } \ 257 | SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ 258 | } 259 | 260 | #define SPLAY_NEGINF -1 261 | #define SPLAY_INF 1 262 | 263 | #define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) 264 | #define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) 265 | #define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) 266 | #define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) 267 | #define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ 268 | : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) 269 | #define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ 270 | : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) 271 | 272 | #define SPLAY_FOREACH(x, name, head) \ 273 | for ((x) = SPLAY_MIN(name, head); \ 274 | (x) != NULL; \ 275 | (x) = SPLAY_NEXT(name, head, x)) 276 | 277 | #endif /* _SPLAY_H_ */ 278 | -------------------------------------------------------------------------------- /src/proxy.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008 Vincent Bernat 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "proxy.h" 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #if !CLIENT_ONLY 32 | #include 33 | #include 34 | #endif 35 | 36 | 37 | extern const char *__progname; 38 | 39 | void usage(void); 40 | void proxy_shutdown(int, short, void *); 41 | void loop(void); 42 | void udp_incoming(int, short, void *); 43 | #if !CLIENT_ONLY 44 | void nfq_incoming(int, short, void *); 45 | int nfq_process(struct nfq_q_handle *, struct nfgenmsg *, 46 | struct nfq_data *, void *); 47 | int nfq_init(int, struct nfq_handle **, struct nfq_q_handle **, int *); 48 | void nfq_shut(struct nfq_handle *, struct nfq_q_handle *); 49 | int start_remote(const char *, int *, int *, uid_t, uid_t); 50 | #endif 51 | 52 | static void setup_signals(void); 53 | #if !CLIENT_ONLY 54 | static void remote_incoming(int, short, void *); 55 | #endif 56 | static void local_incoming(int, short, void *); 57 | static void expire_states(int, short, void *); 58 | 59 | int keep_running = 1; 60 | 61 | void 62 | usage() 63 | { 64 | extern const char *__progname; 65 | #if !CLIENT_ONLY 66 | fprintf(stderr, "usage: %s [-d] [-q queue] [-e cmd] [ -u uid ] [ -g gid ]\n", __progname); 67 | #else 68 | fprintf(stderr, "usage: %s [-d]\n", __progname); 69 | #endif 70 | exit(1); 71 | } 72 | 73 | static void 74 | expire_states(int fd, short event, void *arg) 75 | { 76 | struct states *udpstates = (struct states *)arg; 77 | static struct timeval tv; 78 | static struct event evexpire; 79 | LLOG_DEBUG("expiring states"); 80 | state_expire(udpstates, UDP_UNCONFIRMED_TTL, UDP_CONFIRMED_TTL); 81 | tv.tv_usec = 0; 82 | tv.tv_sec = 2; 83 | event_set(&evexpire, -1, EV_TIMEOUT, 84 | expire_states, udpstates); 85 | if (event_add(&evexpire, &tv) == -1) 86 | fatal("unable to set timer for state expiration"); 87 | } 88 | 89 | /* Distant process receives an UDP packet */ 90 | void 91 | udp_incoming(int fd, short event, void *arg) 92 | { 93 | struct state *udpstate = (struct state*)arg; 94 | struct iphdr *ip_header; 95 | struct udphdr *udp_header; 96 | char buffer[NFQ_PACKET_BUFFER_SIZE]; 97 | int rv; 98 | 99 | udpstate->count++; 100 | udpstate->lastchange = time(NULL); 101 | if ((rv = read(udpstate->socket, buffer + sizeof(struct iphdr) + 102 | sizeof(struct udphdr), NFQ_PACKET_BUFFER_SIZE - 103 | sizeof(struct iphdr) - sizeof(struct udphdr))) == -1) { 104 | LLOG_WARN("problem while reading"); 105 | return; 106 | } 107 | if (rv == 0) { 108 | /* Remote end was closed, just ignore it, we will let the state 109 | * expire. Not even sure that this can be possible with UDP */ 110 | return; 111 | } 112 | udp_header = (struct udphdr *)(buffer + sizeof(struct iphdr)); 113 | udp_header->source = htons(udpstate->dport); 114 | udp_header->dest = htons(udpstate->sport); 115 | udp_header->len = htons(sizeof(struct udphdr) + rv); 116 | udp_header->check = 0; /* No checksum, as allowed by RFC 768 */ 117 | 118 | ip_header = (struct iphdr *)buffer; 119 | memset(ip_header, 0, sizeof(struct iphdr)); 120 | ip_header->version = IPVERSION; 121 | ip_header->ihl = sizeof(struct iphdr)/4; 122 | ip_header->tot_len = htons(rv + sizeof(struct udphdr) + 123 | sizeof(struct iphdr)); 124 | ip_header->ttl = IPDEFTTL; 125 | ip_header->protocol = IPPROTO_UDP; 126 | memcpy(&ip_header->saddr, &udpstate->destination, 127 | sizeof(struct in_addr)); 128 | memcpy(&ip_header->daddr, &udpstate->source, 129 | sizeof(struct in_addr)); 130 | ip_header->check = cksum((unsigned char *)ip_header, 131 | sizeof(struct iphdr)); 132 | 133 | if (write(STDOUT_FILENO, buffer, rv + 134 | sizeof(struct udphdr) + sizeof(struct iphdr)) == -1) 135 | LLOG_WARN("unable to send back UDP packet"); 136 | } 137 | 138 | #if !CLIENT_ONLY 139 | /* Proxy process receives a packet from the remote end */ 140 | static void 141 | remote_incoming(int fd, short event, void *arg) 142 | { 143 | static char buf[NFQ_PACKET_BUFFER_SIZE]; 144 | static char *n = buf; /* Current pointer */ 145 | static int l = 0; /* Current length */ 146 | static int s = -1; 147 | int rv, opt; 148 | struct iphdr* ip_header; 149 | struct sockaddr_in sin; 150 | 151 | if ((rv = read(fd, n, sizeof(buf) - l)) == -1) { 152 | LLOG_WARN("problem while reading"); 153 | return; 154 | } 155 | if (rv == 0) { 156 | LLOG_WARN("remote pipe was closed"); 157 | close(fd); 158 | keep_running = 0; 159 | return; 160 | } 161 | l += rv; n += rv; 162 | 163 | if ((l < sizeof(struct iphdr)) || 164 | (l < ntohs(((struct iphdr*)buf)->tot_len))) { 165 | /* Too small to analyse, let's continue */ 166 | return; 167 | } 168 | ip_header = (struct iphdr*)buf; 169 | if (l > ip_header->tot_len) { 170 | LLOG_WARNX("packet too large, hope we can restart"); 171 | goto end; 172 | } 173 | if ((s == -1) && ((s = socket(PF_INET, SOCK_RAW, IPPROTO_RAW)) == -1)) { 174 | LLOG_WARN("unable to open raw socket"); 175 | fatalx("cannot continue"); 176 | } 177 | opt = IP_PMTUDISC_DONT; 178 | setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &opt, sizeof(opt)); 179 | sin.sin_port = 0; 180 | sin.sin_family = AF_INET; 181 | memcpy(&sin.sin_addr, &ip_header->daddr, sizeof(struct in_addr)); 182 | if (sendto(s, buf, l, MSG_DONTWAIT, (struct sockaddr *)&sin, 183 | sizeof(struct sockaddr_in)) == -1) { 184 | LLOG_WARN("unable to send packet"); 185 | goto end; 186 | } 187 | 188 | end: 189 | n = buf; 190 | l = 0; 191 | } 192 | #endif 193 | 194 | /* Distant process receives a packet to transmit from the proxy */ 195 | static void 196 | local_incoming(int fd, short event, void *arg) 197 | { 198 | static char buf[NFQ_PACKET_BUFFER_SIZE]; 199 | static char *n = buf; /* Current pointer */ 200 | static int l = 0; /* Current length */ 201 | int rv; 202 | struct iphdr* ip_header; 203 | struct udphdr* udp_header; 204 | struct state *udpstate, find; 205 | struct states *udpstates = (struct states *)arg; 206 | 207 | if ((rv = read(fd, n, sizeof(buf) - l)) == -1) { 208 | LLOG_WARN("problem while reading"); 209 | return; 210 | } 211 | if (rv == 0) { 212 | LLOG_WARN("remote pipe was closed"); 213 | close(fd); 214 | keep_running = 0; 215 | return; 216 | } 217 | l += rv; n += rv; 218 | 219 | if ((l < sizeof(struct iphdr)) || 220 | (l < ntohs(((struct iphdr*)buf)->tot_len))) { 221 | /* Too small to analyse, let's continue */ 222 | return; 223 | } 224 | ip_header = (struct iphdr*)buf; 225 | if (l > ip_header->tot_len) { 226 | LLOG_WARNX("packet too large, hope we can restart"); 227 | goto end; 228 | } 229 | if ((IPVERSION != ip_header->version) || 230 | (IPPROTO_UDP != ip_header->protocol)) { 231 | LLOG_WARNX("non UDPv4 packet received"); 232 | goto end; 233 | } 234 | if (l < ip_header->ihl * 4 + sizeof(struct udphdr)) { 235 | LLOG_WARNX("received too small UDP packet"); 236 | goto end; 237 | } 238 | udp_header = (struct udphdr*)(buf + ip_header->ihl * 4); 239 | 240 | LLOG_DEBUG("received packet from %s:%d", 241 | inet_ntoa(*(struct in_addr*)&ip_header->saddr), 242 | ntohs(udp_header->source)); 243 | LLOG_DEBUG("packet for %s:%d", 244 | inet_ntoa(*(struct in_addr*)&ip_header->daddr), 245 | ntohs(udp_header->dest)); 246 | 247 | /* Get or create and update the current state */ 248 | memcpy(&find.source, &ip_header->saddr, sizeof(struct in_addr)); 249 | memcpy(&find.destination, &ip_header->daddr, sizeof(struct in_addr)); 250 | find.sport = ntohs(udp_header->source); 251 | find.dport = ntohs(udp_header->dest); 252 | if ((udpstate = state_get_or_create(udpstates, &find, 253 | udp_incoming)) == NULL) { 254 | LLOG_WARNX("unable to get UDP state"); 255 | goto end; 256 | } 257 | udpstate->lastchange = time(NULL); 258 | udpstate->count++; 259 | if (send(udpstate->socket, buf + ip_header->ihl * 4 + sizeof(struct udphdr), 260 | l - ip_header->ihl * 4 - sizeof(struct udphdr), MSG_DONTWAIT) == -1) { 261 | LLOG_WARN("unable to send packet from proxy"); 262 | goto end; 263 | } 264 | 265 | end: 266 | n = buf; 267 | l = 0; 268 | } 269 | 270 | #if !CLIENT_ONLY 271 | void 272 | nfq_incoming(int fd, short event, void *arg) 273 | { 274 | char buf[NFQ_PACKET_BUFFER_SIZE]; 275 | int rv; 276 | if ((rv = recv(fd, buf, sizeof(buf), 0)) >= 0) 277 | nfq_handle_packet((struct nfq_handle *)arg, 278 | buf, rv); 279 | else 280 | LLOG_WARN("unable to handle incoming packet"); 281 | } 282 | 283 | int 284 | nfq_process(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, 285 | struct nfq_data *nfdata, void *data) 286 | { 287 | struct nfqnl_msg_packet_hdr *header; 288 | char *payload = NULL; 289 | int payload_length; 290 | int remoteout; 291 | 292 | if (!(header = nfq_get_msg_packet_hdr(nfdata))) { 293 | LLOG_WARNX("unable to get header"); 294 | return -1; 295 | } 296 | if ((payload_length = nfq_get_payload(nfdata, 297 | (unsigned char**)&payload)) == -1) { 298 | LLOG_WARNX("unable to get payload"); 299 | return -1; 300 | } 301 | 302 | /* We now send the packet to the remote end */ 303 | remoteout = *(int*)data; 304 | if (write(remoteout, payload, payload_length) == -1) 305 | LLOG_WARN("unable to send packet to remote end"); 306 | 307 | /* Drop the package, we will pass it through our proxy */ 308 | return nfq_set_verdict(qh, ntohl(header->packet_id), NF_DROP, 0, NULL); 309 | } 310 | 311 | 312 | int 313 | nfq_init(int queue, struct nfq_handle **nfq, struct nfq_q_handle **qh, int *remoteout) 314 | { 315 | LLOG_INFO("Initialize net queue %d", queue); 316 | 317 | if ((*nfq = nfq_open()) == NULL) { 318 | LLOG_WARNX("unable to open nfqueue"); 319 | return -1; 320 | } 321 | 322 | nfq_unbind_pf(*nfq, AF_INET); 323 | if (nfq_bind_pf(*nfq, AF_INET) != 0) { 324 | nfq_close(*nfq); 325 | LLOG_WARNX("unable to bind nfqueue to AF_INET"); 326 | return -1; 327 | } 328 | 329 | if ((*qh = nfq_create_queue(*nfq, queue, &nfq_process, 330 | remoteout)) == NULL) { 331 | nfq_unbind_pf(*nfq, AF_INET); 332 | nfq_close(*nfq); 333 | LLOG_WARNX("unable to create queue %d", queue); 334 | return -1; 335 | } 336 | 337 | if (nfq_set_mode(*qh, NFQNL_COPY_PACKET, 338 | NFQ_PACKET_BUFFER_SIZE) == -1) { 339 | nfq_unbind_pf(*nfq, AF_INET); 340 | nfq_close(*nfq); 341 | LLOG_WARNX("unable to set mode"); 342 | return -1; 343 | } 344 | 345 | return 0; 346 | } 347 | 348 | void 349 | nfq_shut(struct nfq_handle *nfq, struct nfq_q_handle *qh) 350 | { 351 | nfq_destroy_queue(qh); 352 | nfq_unbind_pf(nfq, AF_INET); 353 | nfq_close(nfq); 354 | } 355 | #endif 356 | 357 | void 358 | proxy_shutdown(int fd, short event, void *arg) 359 | { 360 | keep_running = 0; 361 | if (EVENT_SIGNAL(((struct event *)arg)) == SIGCHLD) 362 | LLOG_WARNX("remote process died"); 363 | } 364 | 365 | #if !CLIENT_ONLY 366 | int 367 | start_remote(const char *cmd, int *read, int *write, uid_t uid, uid_t gid) 368 | { 369 | int pipeout[2], pipein[2]; 370 | if ((pipe(pipeout) == -1) || (pipe(pipein) == -1)) { 371 | LLOG_WARN("unable to create pipes"); 372 | return -1; 373 | } 374 | switch (fork()) { 375 | case -1: 376 | LLOG_WARN("unable to fork"); 377 | close(pipeout[0]); 378 | close(pipein[0]); 379 | close(pipeout[1]); 380 | close(pipein[1]); 381 | return -1; 382 | case 0: 383 | /* Child */ 384 | close(pipeout[1]); 385 | close(pipein[0]); 386 | /* Change uid/gid */ 387 | if ((gid > 0) && (setgid(gid) == -1)) 388 | fatal("unable to change uid"); 389 | if ((uid > 0) && (setuid(uid) == -1)) 390 | fatal("unable to change uid"); 391 | /* Plug to stdin/stdout */ 392 | dup2(pipeout[0], STDIN_FILENO); 393 | dup2(pipein[1], STDOUT_FILENO); 394 | execl("/bin/sh", "/bin/sh", "-c", cmd, NULL); 395 | break; 396 | default: 397 | close(pipeout[0]); 398 | close(pipein[1]); 399 | *read = pipein[0]; 400 | *write = pipeout[1]; 401 | if (fcntl(*write, F_SETFL, O_ASYNC) == -1) 402 | LLOG_WARN("unable to set write pipe to non blocking mode"); 403 | return 0; 404 | } 405 | 406 | /* Make the compiler happy */ 407 | return -1; 408 | } 409 | #endif 410 | 411 | static void 412 | setup_signals() 413 | { 414 | static struct event evsigint, evsigterm, evsigchld, evsigpipe; 415 | event_set(&evsigint, SIGINT, EV_SIGNAL | EV_PERSIST, proxy_shutdown, 416 | &evsigint); 417 | if (event_add(&evsigint, NULL) == -1) 418 | fatal("unable to setup sigint signal"); 419 | event_set(&evsigterm, SIGTERM, EV_SIGNAL | EV_PERSIST, proxy_shutdown, 420 | &evsigterm); 421 | if (event_add(&evsigterm, NULL) == -1) 422 | fatal("unable to setup sigterm signal"); 423 | event_set(&evsigchld, SIGCHLD, EV_SIGNAL | EV_PERSIST, proxy_shutdown, 424 | &evsigchld); 425 | if (event_add(&evsigchld, NULL) == -1) 426 | fatal("unable to setup sigchld signal"); 427 | event_set(&evsigpipe, SIGPIPE, EV_SIGNAL | EV_PERSIST, proxy_shutdown, 428 | &evsigpipe); 429 | if (event_add(&evsigpipe, NULL) == -1) 430 | fatal("unable to setup sigpipe signal"); 431 | } 432 | 433 | void 434 | loop() 435 | { 436 | struct timeval tv; 437 | 438 | while (keep_running) { 439 | tv.tv_usec = 0; 440 | tv.tv_sec = 1; 441 | event_loopexit(&tv); 442 | event_dispatch(); 443 | } 444 | } 445 | 446 | int 447 | main(int argc, char **argv) 448 | { 449 | int ch, queue = -1, debug = 0; 450 | char *cmd = NULL; 451 | struct nfq_handle *nfq; 452 | struct nfq_q_handle *qh; 453 | int remotein, remoteout; 454 | uid_t uid = -1, gid = -1; 455 | 456 | struct event evqueue, evrin; 457 | struct states *udpstates; 458 | 459 | while ((ch = getopt(argc, argv, "dq:e:u:g:")) != -1) { 460 | switch (ch) { 461 | case 'd': 462 | debug++; 463 | break; 464 | #if !CLIENT_ONLY 465 | case 'q': 466 | queue = atoi(optarg); 467 | break; 468 | case 'e': 469 | cmd = optarg; 470 | break; 471 | case 'u': 472 | uid = atoi(optarg); 473 | break; 474 | case 'g': 475 | gid = atoi(optarg); 476 | break; 477 | #endif 478 | default: 479 | usage(); 480 | } 481 | } 482 | 483 | log_init(debug); 484 | 485 | if ((cmd == NULL) && (queue != -1)) { 486 | LLOG_INFO("no command given, will call myself"); 487 | cmd = argv[0]; 488 | } 489 | 490 | event_init(); 491 | 492 | #if !CLIENT_ONLY 493 | if (queue != -1) { 494 | /* Packets from remote */ 495 | if (start_remote(cmd, &remotein, &remoteout, 496 | uid, gid) == -1) 497 | fatalx("unable to start remote"); 498 | event_set(&evrin, remotein, EV_READ | EV_PERSIST, 499 | remote_incoming, &evrin); 500 | if (event_add(&evrin, NULL) == -1) 501 | fatal("unable to set event for incoming pipe"); 502 | 503 | /* Packets to be forwarded */ 504 | if (nfq_init(queue, &nfq, &qh, &remoteout) == -1) 505 | fatalx("unable to initialize net queue"); 506 | event_set(&evqueue, nfnl_fd(nfq_nfnlh(nfq)), 507 | EV_READ | EV_PERSIST, nfq_incoming, nfq); 508 | if (event_add(&evqueue, NULL) == -1) 509 | fatal("unable to set event for netfilter queue"); 510 | } else { 511 | #endif 512 | if (fcntl(STDOUT_FILENO, F_SETFL, O_ASYNC) == -1) 513 | LLOG_WARN("unable to set standard output to non blocking mode"); 514 | 515 | /* States expiration */ 516 | udpstates = state_initialize(); 517 | expire_states(-1, 0, udpstates); 518 | 519 | /* Packets from proxy */ 520 | event_set(&evrin, STDIN_FILENO, EV_READ | EV_PERSIST, 521 | local_incoming, udpstates); 522 | if (event_add(&evrin, NULL) == -1) 523 | fatal("unable to set event for incoming pipe"); 524 | #if !CLIENT_ONLY 525 | } 526 | #endif 527 | 528 | setup_signals(); 529 | loop(); 530 | 531 | #if !CLIENT_ONLY 532 | if (queue != -1) { 533 | nfq_shut(nfq, qh); 534 | } 535 | #endif 536 | 537 | LLOG_INFO("shutdown"); 538 | 539 | return 0; 540 | } 541 | --------------------------------------------------------------------------------