Binding to Networking Interfaces in Ruby

Binding to networking interfaces in ruby

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.

Ruby: Binding a listening socket to a specific interface

You will need to get the IP of the network interface you want to listen on and pass it as the first parameter to TCPServer.new. There is no way that I know of to specify the interface by name besides parsing the output of %x(ifconfig <interface> | grep inet).

How to specify network interface with Ruby's Faraday?

I think it even is possible to do that with Faraday and Typhoeus: https://github.com/typhoeus/typhoeus/blob/92d19df7af06034ae28996e097676b01301042f5/lib/typhoeus/adapters/faraday.rb#L139.

ruby scrape with multiple ip addresses

It is definitely possible for a machine or a program to have multiple IP addresses. You can even have multiple network adapters, and tie each of them to different physical connections.

However, it can get really hairy to maintain. The challenge for that is partly in the code, partly in the system maintenance, and partly in the networking required to make that happen.

A better approach that you can take is to design your program so that it can run distributed. As such, you can have several copies of it synchronized and doing the work in parallel. You can then scale it horizontally (build more copies) as required, and over different machines and connections if required.

EDIT: You mentioned that you cannot scale horizontally, and that you prefer to use multiple connections from the same machine.

It's very likely that for this you'll have to go a little bit lower in the network stack, developing yourself the connection through sockets in order to use specific network interfaces.

Check out an introduction to Ruby sockets.

Also, check out these related questions:

  • How does a socket know which network interface controller to use?
  • Binding to networking interfaces in ruby
  • Ruby: Binding a listening socket to a specific interface
  • Can I make ruby send network traffic over a specific iface?

Expose port on all interfaces using Webrick

The answer you're looking for is in the Sinatra config docs: http://sinatrarb.com/configuration.html

Specifically the :bind option. Example: set :bind, '0.0.0.0'

How can you choose the network interface via which UDP broadcast packet is sent

Have you tried bind() yet? Generally speaking, if you socket.bind() to an address (such as 127.0.0.1) then your packets should originate from that address. The loopback, and broadcasts for that matter, might be treated specially but bind() would be my first choice to try.

Networking: Binding a socket on all interfaces when interfaces goes up/down

INADDR_ANY really means any interface, not 'all' interfaces. As such it doesn't matter whether the interface even existed when the socket was bound.



Related Topics



Leave a reply



Submit