Consider the following network setup (which I live with by the way):
[main LAN] <-----------------------------------------------------> [remote datacenter LAN]
(192.168.0.0/16) <-------- leased point-to-point ------------> (192.168.0.0/16)
Both locations also have separate connections to the public Internet with different public IP subnets. However, for this discussion it's not necessary to have different public IP subnets. Under normal circumstances the local LAN and the remote LAN are the same logical LAN via the magic of the leased point to point line.
However, today that p2p connection broke (physically between the two locations, out of our control). This outage lasted several hours, but brought out an interesting use of SSH tunneling for ethernet bridging aka Layer-2 VPN or tunneling. For this to work, you'll need to have at least openSSH 4.3, a somewhat recent linux distro and the bridge-utils package for your distro. This also assumes you have a basic knowledge of IP and the linux command line. I use openSuSE 11.0, but this should work for almost any similar linux.
Let's say for example, the main location has a linux box (router1) with two NICs:
eth0: 1.1.1.1 (the public interface)
eth1: unassigned IP, but connected to your LAN (192.168.0.0/16 in my case)
On the other box, at the remote location (router2) we also have two NICs:
eth0: 2.2.2.2 (the public interface)
eth1: unassigned IP, but connected to your LAN (192.168.0.0/16 in my case)
Both routers should be set with it's public IP gateway as the default route, working DNS, etc. You'll want to enable IP forwarding (consult your specific distro) and in my case, I disabled the distro's firewall. On the remote side (consider it the "server"), you'll need to edit your sshd config to allow remote root logins and tunnels via SSH.
/etc/ssh/sshd_config:
PermitRootLogin yes
PermitTunnel yes
The root login is necessary to allow ssh to create the TAP devices for the bridge. Because of that, you'll also want to add your local side's IPs to /etc/hosts.allow for the sshd process. Now, on the local side (IP 1.1.1.1, which you might consider the client now) you'll want to "su root" and do the following:
ssh -o Tunnel=ethernet -f -w 0:0 2.2.2.2 true
The -o switch sets client options on the command line. We're specifying the tunnel type as ethernet (bridge) as opposed to point-to-point, which it'll do by default (for Layer-3 type VPN routing). The -f switch just forks ssh in the background so we're returned to our "client's" command line and not remote's. Since we've done that, ssh will expect a remote command of some kind, so we'll just run "true", effectively doing nothing. The -w 0:0 switch actually sets up our tap devices on either side as tap0. You can do -w 1:1 for tap1, -w 0:1 for tap0 on one side and tap1 on the other, etc.
On both sides now, you should be able to see via ifconfig -a your eth0, eth1 and tap0 devices. Make sure to call ifconfig with -a, or you'll only see interfaces with defined IPs. Now that the two boxes are connected via the public Internet to each other via SSH, you can finally start to establish the bridge interface. Now we'll use the bridge-utils binary to create a bridge interface called br0:
brctl addbr br0
brctl addif br0 eth1
brctl addif br0 tap0
Then you'll want to bring up all of your interfaces, if they aren't already:
ifconfig eth1 up
ifconfig tap0 up
ifconfig br0 up
Doing so will create the br0 interface, then bridge your eth1 and tap0 together and bring up the interfaces. Don't forget, YOU MUST RUN THE brctl and ifconfig COMANDS ON BOTH SIDES!!! Once you've done this, you can check the remote side to see if it knows about the MAC addresses (from Layer-2) on the local side:
brctl showmacs br0
This will report on the known MAC address from the ARP protocol. Depending on your network, you'll see a few or many. Depending on your setup, you can get a DHCP address on the "other side" of the tunnel now or configure an appropriate IP and ping across as if you were on the same physical broadcast domain!
As a final note, there's always a downside. TCP encapsulated TCP is bad and will put a STRAIN on your hardware. Make sure it's decent for the amount of anticipated traffic and use only as a quick and dirty solution or a temporary measure. The following is good reading for why this is not a long-term, permanent solution:
http://sites.inka.de/~W1011/devel/tcp-tcp.html


#!/bin/bash
# prereqs:
# remote host's sshd_config must have "PermitRootLogin=no", "AllowUsers user", and "PermitTunnel=yes"
# "tunctl", in debians it is found in uml-utils, redhats another (dont remember but "yum provides tunctl" must tell)
# remote user must be able to sudo-as-root
# can opt by routing as in this case or soft bridge with brctl and you get full remote ethernet segment membership
# that last i think i'll implement later as an option
# other stuff to do is error checking, etcetc, this is just as came from the oven
userhost='user@host'
sshflags='-Ap 2020 -i /path/to/some/authkey'
vpn='10.0.0.0/24'
rnet=192.168.40.0/24
# START VPN
if [ "$1" == "start" ]; then
echo setting up local tap ...
ltap=$(tunctl -b)
ifconfig $ltap ${vpn%%?/*}2/${vpn##*/} up
echo setting remote configuration and enabling root login ...
rtap="ssh $sshflags $userhost sudo 'bash -c \"rtap=\\\$(tunctl -b); echo \\\$rtap; ifconfig \\\$rtap ${vpn%%?/*}1/${vpn##*/} up; iptables -A FORWARD -i \\\$rtap -j ACCEPT; iptables -A FORWARD -o \\\$rtap -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -s ${vpn%%?/*}2 -j SNAT --to \\\$(ip r | grep $rnet | sed \\\"s/^.*src \\\(.*\\\$\\\)/\1/g\\\"); sed -i -e \\\"s/\\\(PermitRootLogin\\\).*\\\$/\1 without-password/g\\\" -e \\\"s/\\\(AllowUsers.*\\\)\\\$/\1 root/g\\\" /etc/ssh/sshd_config; /usr/sbin/sshd -t\"'"
rtap=$(sh -c "$rtap")
echo setting up local routes ...
# since my ISP sucks with transparent filters (i can't opt for another where i live), i'll just use my work net as gateway
ip r a $(ip r | grep default | sed "s/default/${userhost##*@}/")
ip r c default via ${vpn%%?/*}1 dev $ltap
echo bringing up the tunnel and disabling root login ...
ssh $sshflags -f -w ${ltap##tap}:${rtap##tap} -o Tunnel=ethernet -o ControlMaster=yes -o ControlPath=/root/.ssh/vpn-$userhost-l$ltap-r$rtap root@${userhost##*@} bash -c "\"sed -i -e 's/\(PermitRootLogin\).*\$/\1 no/g' -e 's/\(AllowUsers.*\) root\$/\1/g' /etc/ssh/sshd_config; /usr/sbin/sshd -t\""
echo connected.
# STOP VPN
elif [ "$1" == "stop" ]; then
echo searching control socket and determining configuration ...
controlpath=$(echo /root/.ssh/vpn-$userhost*)
ltap=${controlpath%%-rtap*} && ltap=tap${ltap##*-ltap}
rtap=${controlpath##*rtap} && rtap=tap${rtap%%-*}
echo bringing the tunnel down ...
ssh $sshflags -o ControlPath=$controlpath -O exit $userhost
echo restoring local routes ...
ip r c default $(ip r | grep ${userhost##*@} | sed "s/${userhost##*@}\(.*$\)/\1/g")
ip r d ${userhost##*@}
echo restoring remote configuration ...
sh -c "ssh $sshflags $userhost sudo 'bash -c \"tunctl -d $rtap; iptables -D FORWARD -i $rtap -j ACCEPT; iptables -D FORWARD -o $rtap -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -s ${vpn%%?/*}2 -j SNAT --to \$(ip r | grep $rnet | sed \"s/^.*src \(.*\$\)/\1/g\")\"'"
echo deleting local tap ...
tunctl -d $ltap
echo disconnected.
fi
i just wanted to ask, what happens to the bridges in case the connection got lost and gets reestablished for example by using "autossh" instead of "ssh"-command, i.e. "autossh -o Tunnel=ethernet -f -w 0:0 2.2.2.2 true". does it work, or do i have to set the bridges everytime i reconnect?
the client-side is in an area, where the connection is quite unreliable.
thx