librelist archives

« back to archive

Redsocks and FreeBSD

Redsocks and FreeBSD

From:
Andrea Franceschini
Date:
2012-03-02 @ 14:27
Hi All,
        
        I'm using redsocks for a few months now, and I find it
        extremly useful and well done.

        I would like to share some findings I came across during
        my daily use on FreeBSD.

        First of all it happens that I had to use ipfw instead of
        ipf , because the first one works within a VIMAGE jail.

        When using ipfw the generic redirector will work flawless.

        You only need to fwd to the local address which redsocks
        is listening to , the interesting traffic.

        So if your redsocks instance is listening on port 2040 on
        127.0.0.1, and you want to intercept the traffic from
        192.168.0.0/24 to 10.0.0.0/8 you will simply add the following rule:

ipfw add 00300 fwd 127.0.0.1,2040 tcp from 192.168.0.0/24 to 10.0.0.0/8

        the generic redirector simply does a getsockname to get the
        remote address, and it works because of the "magic" ipfw does :)

	I also stepped into some FreeBSD specific issues, one of
	which is that redsocks wouldn't run as daemon.
        
        It turned out that the problem was that the kqueue/kevent
        initalization happened before the fork, and when a process
        fork in FreeBSD the child didn't inherit the kevent set
        by the parent.(I don't know if there's a way to change
        this behaviour...)

        I "solved" it with a (very) dirty hack, basically changing
        the initialization order...
        I copied the hack below just to show the idea:

35:     app_subsys *subsystems[] = {
36:     //      &base_subsys, /* I remove base from the list ..
37:             &redsocks_subsys,
38:             &redudp_subsys,
39:             &dnstc_subsys,
40:     };
...
...
118:      //event_init();  /* old event init position
119:      memset(terminators, 0, sizeof(terminators));
120:
121:      base_subsys.init(); /*init  base subsys, where the fork happens,
"manually"...
122:      event_init();      /* now init
123:

        Another issue I faced was with udp relaying, it seems that
        FreeBSD is a little touchy with the sendmsg function.

        When trying to redsocks an udp connection I got this message:

redudp.c:150 redudp_forward_pkt(...) [x.x.x.x:1234->y.y.y.y:6789]: 
sendmsg: Can't forward packet, dropping it: Socket is already c
onnected

        It seems that the problem is in the redudp_forward_pkt function 
	when it tries to forward to the ss5 udp relay port.

	It build the msg to be sent (via sendmsg) specifying the 
	destination address too (udprelayaddr), this is seen as
	a violation as the destination address has already been
	specified in redudp_read_assoc_reply when the socket was
	first created and connected..

	So to avoid the problem I made this little hack:
	
138:        //msg.msg_name = &client->udprelayaddr; /* There's no need of 
these 2 lines as the socket is already connected.
139:        //msg.msg_namelen = sizeof(client->udprelayaddr);
140:        msg.msg_iov = io;
141:        msg.msg_iovlen = SIZEOF_ARRAY(io);

        Another thing that might be useful, even if not FreeBSD
        specific is this one:
        I couldn't find a way to use a particular source ip for the
        relayed connection.
        I solved adding a new key in the configuration calling it
        bind_ip.

        This is the diff:

-- ../redsocks.old/redsocks.c	2011-10-10 19:49:53.000000000 +0200
+++ redsocks.c	2012-03-02 12:52:24.000000000 +0100
@@ -63,6 +63,7 @@
 	{ .key = "local_port", .type = pt_uint16 },
 	{ .key = "ip",         .type = pt_in_addr },
 	{ .key = "port",       .type = pt_uint16 },
+	{ .key = "bind_ip",    .type = pt_in_addr },
 	{ .key = "type",       .type = pt_pchar },
 	{ .key = "login",      .type = pt_pchar },
 	{ .key = "password",   .type = pt_pchar },
@@ -90,6 +91,9 @@
 	instance->config.bindaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
 	instance->config.relayaddr.sin_family = AF_INET;
 	instance->config.relayaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+	instance->config.localaddr.sin_family = AF_INET;
+	instance->config.localaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+	instance->config.localaddr.sin_port=0;
 
 	for (parser_entry *entry = §ion->entries[0]; entry->key; entry++)
 		entry->addr =
@@ -97,6 +101,7 @@
 			(strcmp(entry->key, "local_port") == 0) ? 
(void*)&instance->config.bindaddr.sin_port :
 			(strcmp(entry->key, "ip") == 0)         ? 
(void*)&instance->config.relayaddr.sin_addr :
 			(strcmp(entry->key, "port") == 0)       ? 
(void*)&instance->config.relayaddr.sin_port :
+			(strcmp(entry->key, "bind_ip") == 0)    ? 
(void*)&instance->config.localaddr.sin_addr :
 			(strcmp(entry->key, "type") == 0)       ? (void*)&instance->config.type :
 			(strcmp(entry->key, "login") == 0)      ? (void*)&instance->config.login :
 			(strcmp(entry->key, "password") == 0)   ? (void*)&instance->config.password :
@@ -523,7 +528,7 @@
 
 void redsocks_connect_relay(redsocks_client *client)
 {
-	client->relay = red_connect_relay(&client->instance->config.relayaddr,
+	client->relay = red_connect_relay(&client->instance->config.relayaddr, 
&client->instance->config.localaddr,
 			                          redsocks_relay_connected, 
redsocks_event_error, client);
 	if (!client->relay) {
 		redsocks_log_errno(client, LOG_ERR, "red_connect_relay");


	Almost the same has been done for redudp.c

	Please notice that none of this aims to be
	complete nor accurate, these are just
	fast and dirty hacks that made things work
	better for me, my hope, in sharing them, is
	to be useful to someone.

	Thanks again to Leonid for this amazing piece
	of software.

Bye,

Andrea