Using my Netgraph layer 2
tunnel, I've connected the Foundation to GINX.
hippo:~$ traceroute turnip.foundation.org.za
traceroute to turnip.foundation.org.za (196.7.160.2), 64 hops max, 44 byte packets
1 nortel8600a.switch.ru.ac.za (146.231.128.205) 0.571 ms 0.479 ms 0.460 ms
2 porcupine (146.231.128.213) 0.173 ms 0.193 ms 0.168 ms
3 turnip.foundation.org.za (196.7.160.2) 7.241 ms 7.160 ms 7.118 ms
turnip:~$ traceroute www.imaginet.co.za
traceroute to www.imaginet.co.za (196.15.145.5), 64 hops max, 40 byte packets
1 imaginet.ginx.org.za (192.42.99.237) 20.409 ms 20.432 ms 20.985 ms
2 ns1.imaginet.co.za (196.15.145.5) 23.922 ms 23.526 ms 23.966 ms
Top-to-bottom: GINX route server, GINX switch, Imaginet SDSL bridge,
Foundation Netgraph bridge.
Here's my method for creating a layer 2 tunnel with a few Netgraph
nodes. I Google'd the FreeBSD mailing lists for a while and didn't find
any published solutions to this problem, though a few people hinted that it
should be possible.
One machine (I'll call it the "server") is directly connected to an
Ethernet network that another machine (the "remote" machine) needs to be
part of. Between them can be any number of routed networks.
This requires the Netgraph ether, bridge, ksocket and eiface nodes, so
load the corresponding kernel modules. On the server, create a bridge node,
and attach its link0 hook to the node that represents the server's interface
on the network in question:
# ngctl mkpeer rl0: bridge lower link0
# ngctl name rl0:lower br0
Connect the link1 hook of that bridge to a new ksocket node:
# ngctl mkpeer br0: ksocket link1 inet/dgram/udp
# ngctl name br0:link1 ks0
Then bind the ksocket to a UDP port on the Internet-connected interface,
and instruct it to direct encapsulated packets towards the remote
machine:
# ngctl msg ks0: bind inet/a.b.c.d:nnnn
# ngctl msg ks0: connect inet/e.f.g.h:mmmm
On the remote machine, create a bridge node and a virtual Ethernet interface
using an eiface node:
# ngctl -f - <<EOF
mkpeer bridge dummy link0
name dummy br0
mkpeer br0: eiface link1 ether
name br0:link1 ngeth0
EOF
Create and configure another ksocket:
# ngctl -f - <<EOF
mkpeer br0: ksocket link0 inet/dgram/udp
name br0:link0 ks0
msg ks0: bind inet/e.f.g.h:mmmm
msg ks0: connect inet/a.b.c.d:nnnn
EOF
Now that all the pieces are in place, the remote machine has an ngeth0
interface that is effectively connected to the real network that the
physical interface on the server is actually connected to. Configure its
MAC address to be the same as that of the physical interface so that ARP
will work, then configure an IP address:
# ifconfig ngeth0 lladdr 00:0a:0b:0c:0d:0e
# ifconfig ngeth0 192.168.0.1/24
The remote machine should now have full connectivity to the local
network.
Points that could be improved: If there's an IP address bound on
the server's interface to the local network, it seems that that stops
working when you use that interface's lower hook. Perhaps I should
experiment with using the upper or orphans hooks.... Also, this is probably
not terribly bandwidth efficient: all broadcast traffic (including ARP) will
be carried over the tunnel.
Other ways of doing this: PPTP (using MPD), L2TP (maybe).
Nonetheless, this method has neatly solved my problem. And working with
Netgraph is just like playing with Lego. :)