May 30 2013
Simple UDP proxy
After a colleague with a PhD in Networking and myself spent the best part of a day trying to NAT UDP syslog packets without success (the Destination-NAT half is fine, but Source-NAT eludes: the external system still sees the internal IP), I decided to change tack and solve the problem by handling packets in user-space.
The code at http://www.brokestream.com/udp_redirect.html works, but two instances need to be chained together via an intermediate port if the source port as seen by the external system needs to differ from that of the proxy host.
With that in mind, I updated the code as follows:
/* http://www.brokestream.com/udp_redirect.html Build: gcc -o udp_redirect udp_redirect.c udp_redirect.c Version 2013-05-30 Copyright (C) 2007 Ivan Tikhonov Copyright (C) 2013 Stuart Shelton, HP Autonomy This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Ivan Tikhonov, kefeer@brokestream.com This source has been modified to support sending data to the destination IP address from a different source-ip to the listen-ip, to enable proxying on multi-homed hosts Stuart Shelton, stuart.shelton@hp.com */ #include <stdio.h> #include <stdlib.h> #include <sys/socket.h> #include <netinet/in.h> int bindsocket( char* ip, int port ); int main( int argc, char* argv[] ); int bindsocket( char* ip, int port ) { int fd; struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr( ip ); addr.sin_port = htons( port ); fd = socket( PF_INET, SOCK_DGRAM, IPPROTO_IP ); if( -1 == bind( fd, (struct sockaddr*)&addr, sizeof( addr ) ) ) { fprintf( stderr, "Cannot bind address (%s:%d)\n", ip, port ); exit( 1 ); } return fd; } int main( int argc, char* argv[] ) { int i, listen, output; char *inip, *inpt, *srcip, *dstip, *dstpt; struct sockaddr_in src; struct sockaddr_in dst; struct sockaddr_in ret; if( 3 != argc && 5 != argc && 6 != argc ) { fprintf( stderr, "Usage: %s <listen-ip> <listen-port> [[source-ip] <destination-ip> <destination-port>]\n", argv[ 0 ] ); exit( 1 ); } i = 1; inip = argv[ i++ ]; /* 1 */ inpt = argv[ i++ ]; /* 2 */ if( 6 == argc ) srcip = argv[ i++ ]; /* 3 */ if( 3 != argc ) { dstip = argv[ i++ ]; /* 3 or 4 */ dstpt = argv[ i++ ]; /* 4 or 5 */ } listen = bindsocket( inip, atoi( inpt ) ); if( 6 == argc ) { output = bindsocket( srcip, atoi( inpt ) ); } else { output = listen; } if( 3 != argc ) { dst.sin_family = AF_INET; dst.sin_addr.s_addr = inet_addr( dstip ); dst.sin_port = htons( atoi( dstpt ) ); } ret.sin_addr.s_addr = 0; while( 1 ) { char buffer[65535]; unsigned int size = sizeof( src ); int length = recvfrom( listen, buffer, sizeof( buffer ), 0, (struct sockaddr*)&src, &size ); if( length <= 0 ) continue; if( 3 == argc ) { /* echo, without tracking return packets */ sendto( listen, buffer, length, 0, (struct sockaddr*)&src, size ); } else if( ( src.sin_addr.s_addr == dst.sin_addr.s_addr ) && ( src.sin_port == dst.sin_port ) ) { /* If we receive a return packet back from our destination ... */ if( ret.sin_addr.s_addr ) /* ... and we've previously remembered having sent packets to this location, then return them to the original sender */ sendto( output, buffer, length, 0, (struct sockaddr*)&ret, sizeof( ret ) ); } else { sendto( output, buffer, length, 0, (struct sockaddr*)&dst, sizeof( dst ) ); /* Remeber original sender to direct return packets towards */ ret = src; } } }
The above source can be downloaded from http://files.stuart.shelton.me/unix/udp-proxy.c.
Stuart
16th August 2013 @ 12:27 am
Now also on github: https://github.com/srcshelton/udp-proxy
William Teder
24th December 2013 @ 4:45 pm
Awesome! I forked the project on GitHub, and am working to add support for multiple backend nodes, read from a file. First time programming in C, we’ll see how it goes. Thanks for the start!
Stuart
27th April 2014 @ 6:11 pm
… so, apparently, I managed to add a few extraneous characters when I pasted in the source above… oops.
On the positive side, this led me to running the code through LLVM/clang’s static analyser – so it’s now slightly shinier 😉