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.