Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Binding to networking interfaces in ruby

Tags:

ruby

I'm trying to open multiple sockets in a ruby application on different network interfaces in linux. For example lets say I have the interface eth0 with an IP of 192.168.1.2 and the interface wlan0 with the IP address 10.0.0.2. I would like to simultaneously connect to a server with a socket on each interface. I thought that binding to the IP address of these interfaces would work however that doesn't seem to be the case. In wireshark when I bind to the IP of wlan0 I successfully see the SYN packets send with the correct source IP, but wireshark sees them on eth0 and the socket is never opened.

Ruby version: ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux]

Here is my current code. I have also tried the Addrinfo method documented on the ruby-doc page for Socket with the same results.

require 'socket'

ip = "192.168.1.2" # IP of internal interface
port = 8000
server = "" # IP of the server I'm trying to connect to goes here

lhost = Socket.pack_sockaddr_in(0, ip)
rhost = Socket.pack_sockaddr_in(port, server)
socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
socket.bind(lhost)
socket.connect(rhost)

Thank you for any help!

like image 544
hkparker Avatar asked Jun 06 '13 19:06

hkparker


1 Answers

The solution is source based routing

I was able to figure it out, and thought I should leave my answer in case anyone else gets stuck with this problem down the road.

What I needed to do was source based routing. The general idea is to create two routing tables, one that forces traffic on one interface and one that forces traffic on the other, and have an ip rule that uses the appropriate routing table based on the source IP address. I set it up like this:

Creating the tables

First I had to edit /etc/iproute2/rt_tables to add the following lines:

128    multiplex0
129    multiplex1

This created two routing tables with IDs 128 and 129 called multiplex0 and multiplex1.

Adding routes to tables

Next I created rules for these tables as follows:

ip route add default via 10.0.2.2 table multiplex0 
ip route add default via 192.168.1.1 table multiplex1

These commands specify the default gateways to use in the routing tables. My 10.0/16 network had a default gateway of 10.0.2.2 and my 192.168.1/24 network had a default gateway of 192.168.1.1.

What if the two networks had the same default gateway?

I believe you can add dev eth0 (or whatever your interface is) to the above commands to specify an interface if your networks have the same default gateway, though I have not yet tested this. I will make an edit when I learn more.

EDIT: I have tested this and it does indeed work. Both routes can have the same default gateway if you specify the interface in the route.

Source based rules

Next I needed to create rules to use these tables:

ip rule add from 10.0.0.0/16 table multiplex0
ip rule add from 192.168.1.1/24 table multiplex1

These rules say that any packet with a source IP in the 10.0/16 range should route based on the rules in multiplex0, while any packet with a source IP in the 192.168.1/24 range should use multiplex1. When the packets use these tables they are directed to the appropriate gateway and corresponding interface. Viola!

EDIT: It would be more accurate to specify just the IP of the network interface in this step! If both interfaces are on a 192.168.1/24 network for example, it would be necessary.

Flushing the cache

Lastly I issued ip route flush cache to make everything take effect and using the ruby code above I was able to open my two sockets on the correct interfaces to the same publicly route-able host.

like image 108
hkparker Avatar answered Sep 30 '22 13:09

hkparker