2017-12-29 04:16:20 +00:00
package yggdrasil
// This part does most of the work to handle packets to/from yourself
// It also manages crypto and dht info
2018-01-26 23:30:51 +00:00
// TODO clean up old/unused code, maybe improve comments on whatever is left
2017-12-29 04:16:20 +00:00
// Send:
// Receive a packet from the tun
// Look up session (if none exists, trigger a search)
// Hand off to session (which encrypts, etc)
// Session will pass it back to router.out, which hands it off to the self peer
// The self peer triggers a lookup to find which peer to send to next
// And then passes it to that's peer's peer.out function
// The peer.out function sends it over the wire to the matching peer
// Recv:
// A packet comes in off the wire, and goes to a peer.handlePacket
// The peer does a lookup, sees no better peer than the self
// Hands it to the self peer.out, which passes it to router.in
// If it's dht/seach/etc. traffic, the router passes it to that part
// If it's an encapsulated IPv6 packet, the router looks up the session for it
// The packet is passed to the session, which decrypts it, router.recvPacket
// The router then runs some sanity checks before passing it to the tun
2018-06-12 22:50:08 +00:00
import (
2018-11-07 10:16:46 +00:00
"bytes"
2018-06-12 22:50:08 +00:00
"time"
2018-01-04 22:37:51 +00:00
2018-06-12 22:50:08 +00:00
"golang.org/x/net/icmp"
"golang.org/x/net/ipv6"
)
2017-12-29 04:16:20 +00:00
2018-12-14 17:35:02 +00:00
type adapter struct {
tunDevice
}
2018-06-10 23:03:28 +00:00
// The router struct has channels to/from the tun/tap device and a self peer (0), which is how messages are passed between this node and the peers/switch layer.
// The router's mainLoop goroutine is responsible for managing all information related to the dht, searches, and crypto sessions.
2017-12-29 04:16:20 +00:00
type router struct {
2018-11-05 16:40:47 +00:00
core * Core
addr address
2018-11-05 23:59:41 +00:00
subnet subnet
2018-11-21 04:04:18 +00:00
in <- chan [ ] byte // packets we received from the network, link to peer's "out"
out func ( [ ] byte ) // packets we're sending to the network, link to peer's "in"
toRecv chan router_recvPacket // packets to handle via recvPacket()
2018-12-14 17:35:02 +00:00
tun tunDevice // TUN/TAP adapter
2018-11-21 04:04:18 +00:00
recv chan <- [ ] byte // place where the tun pulls received packets from
send <- chan [ ] byte // place where the tun puts outgoing packets
reset chan struct { } // signal that coords changed (re-init sessions/dht)
admin chan func ( ) // pass a lambda for the admin socket to query stuff
2018-11-05 16:40:47 +00:00
cryptokey cryptokey
2017-12-29 04:16:20 +00:00
}
2018-11-21 04:04:18 +00:00
// Packet and session info, used to check that the packet matches a valid IP range or CKR prefix before sending to the tun.
type router_recvPacket struct {
bs [ ] byte
sinfo * sessionInfo
}
2018-06-10 23:03:28 +00:00
// Initializes the router struct, which includes setting up channels to/from the tun/tap.
2017-12-29 04:16:20 +00:00
func ( r * router ) init ( core * Core ) {
2018-01-04 22:37:51 +00:00
r . core = core
r . addr = * address_addrForNodeID ( & r . core . dht . nodeID )
2018-11-05 23:59:41 +00:00
r . subnet = * address_subnetForNodeID ( & r . core . dht . nodeID )
2018-06-08 23:42:56 +00:00
in := make ( chan [ ] byte , 32 ) // TODO something better than this...
2018-10-21 22:20:14 +00:00
p := r . core . peers . newPeer ( & r . core . boxPub , & r . core . sigPub , & boxSharedKey { } , "(self)" )
2018-01-26 23:30:51 +00:00
p . out = func ( packet [ ] byte ) {
// This is to make very sure it never blocks
2018-02-07 23:48:30 +00:00
select {
case in <- packet :
return
default :
util_putBytes ( packet )
2018-01-26 23:30:51 +00:00
}
}
2018-01-04 22:37:51 +00:00
r . in = in
2018-06-06 22:44:10 +00:00
r . out = func ( packet [ ] byte ) { p . handlePacket ( packet ) } // The caller is responsible for go-ing if it needs to not block
2018-11-21 04:04:18 +00:00
r . toRecv = make ( chan router_recvPacket , 32 )
2018-02-04 00:44:28 +00:00
recv := make ( chan [ ] byte , 32 )
send := make ( chan [ ] byte , 32 )
2018-01-04 22:37:51 +00:00
r . recv = recv
r . send = send
r . reset = make ( chan struct { } , 1 )
2018-11-21 04:04:18 +00:00
r . admin = make ( chan func ( ) , 32 )
2018-11-05 16:40:47 +00:00
r . cryptokey . init ( r . core )
2018-12-14 18:08:13 +00:00
r . tun . init ( r . core , send , recv )
2018-05-27 21:13:37 +00:00
}
2018-06-10 23:03:28 +00:00
// Starts the mainLoop goroutine.
2018-05-27 21:13:37 +00:00
func ( r * router ) start ( ) error {
r . core . log . Println ( "Starting router" )
2018-01-04 22:37:51 +00:00
go r . mainLoop ( )
2018-05-27 21:13:37 +00:00
return nil
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Takes traffic from the tun/tap and passes it to router.send, or from r.in and handles incoming traffic.
// Also adds new peer info to the DHT.
// Also resets the DHT and sesssions in the event of a coord change.
// Also does periodic maintenance stuff.
2017-12-29 04:16:20 +00:00
func ( r * router ) mainLoop ( ) {
2018-01-04 22:37:51 +00:00
ticker := time . NewTicker ( time . Second )
defer ticker . Stop ( )
for {
select {
2018-11-21 04:04:18 +00:00
case rp := <- r . toRecv :
r . recvPacket ( rp . bs , rp . sinfo )
2018-01-04 22:37:51 +00:00
case p := <- r . in :
r . handleIn ( p )
case p := <- r . send :
r . sendPacket ( p )
case info := <- r . core . dht . peers :
2018-10-28 20:04:44 +00:00
now := time . Now ( )
oldInfo , isIn := r . core . dht . table [ * info . getNodeID ( ) ]
2018-10-20 19:48:07 +00:00
r . core . dht . insert ( info )
2018-10-28 20:04:44 +00:00
if isIn && now . Sub ( oldInfo . recv ) < 45 * time . Second {
info . recv = oldInfo . recv
}
2018-01-04 22:37:51 +00:00
case <- r . reset :
r . core . sessions . resetInits ( )
2018-05-16 04:57:00 +00:00
r . core . dht . reset ( )
2018-01-04 22:37:51 +00:00
case <- ticker . C :
{
// Any periodic maintenance stuff goes here
2018-06-07 04:10:33 +00:00
r . core . switchTable . doMaintenance ( )
2018-01-04 22:37:51 +00:00
r . core . dht . doMaintenance ( )
2018-06-22 01:31:30 +00:00
r . core . sessions . cleanup ( )
2018-01-04 22:37:51 +00:00
util_getBytes ( ) // To slowly drain things
}
2018-01-21 18:55:45 +00:00
case f := <- r . admin :
f ( )
2018-01-04 22:37:51 +00:00
}
}
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Checks a packet's to/from address to make sure it's in the allowed range.
// If a session to the destination exists, gets the session and passes the packet to it.
// If no session exists, it triggers (or continues) a search.
// If the session hasn't responded recently, it triggers a ping or search to keep things alive or deal with broken coords *relatively* quickly.
// It also deals with oversized packets if there are MTU issues by calling into icmpv6.go to spoof PacketTooBig traffic, or DestinationUnreachable if the other side has their tun/tap disabled.
2017-12-29 04:16:20 +00:00
func ( r * router ) sendPacket ( bs [ ] byte ) {
2018-01-04 22:37:51 +00:00
var sourceAddr address
2018-11-07 10:04:31 +00:00
var destAddr address
var destSnet subnet
2018-11-07 10:16:46 +00:00
var destPubKey * boxPubKey
2018-11-07 10:04:31 +00:00
var destNodeID * NodeID
2018-11-06 20:49:19 +00:00
var addrlen int
if bs [ 0 ] & 0xf0 == 0x60 {
2018-11-07 10:29:08 +00:00
// Check if we have a fully-sized header
if len ( bs ) < 40 {
panic ( "Tried to send a packet shorter than an IPv6 header..." )
}
2018-11-06 20:49:19 +00:00
// IPv6 address
addrlen = 16
copy ( sourceAddr [ : addrlen ] , bs [ 8 : ] )
2018-11-07 10:04:31 +00:00
copy ( destAddr [ : addrlen ] , bs [ 24 : ] )
copy ( destSnet [ : addrlen / 2 ] , bs [ 24 : ] )
2018-11-06 20:49:19 +00:00
} else if bs [ 0 ] & 0xf0 == 0x40 {
2018-11-07 10:29:08 +00:00
// Check if we have a fully-sized header
if len ( bs ) < 20 {
panic ( "Tried to send a packet shorter than an IPv4 header..." )
}
2018-11-06 20:49:19 +00:00
// IPv4 address
addrlen = 4
copy ( sourceAddr [ : addrlen ] , bs [ 12 : ] )
2018-11-07 10:04:31 +00:00
copy ( destAddr [ : addrlen ] , bs [ 16 : ] )
2018-11-06 20:49:19 +00:00
} else {
2018-11-06 22:57:53 +00:00
// Unknown address length
2018-11-06 20:49:19 +00:00
return
}
if ! r . cryptokey . isValidSource ( sourceAddr , addrlen ) {
2018-11-07 10:29:08 +00:00
// The packet had a source address that doesn't belong to us or our
// configured crypto-key routing source subnets
2018-11-05 23:59:41 +00:00
return
}
2018-11-07 10:04:31 +00:00
if ! destAddr . isValid ( ) && ! destSnet . isValid ( ) {
2018-11-07 10:29:08 +00:00
// The addresses didn't match valid Yggdrasil node addresses so let's see
// whether it matches a crypto-key routing range instead
2018-11-07 10:04:31 +00:00
if key , err := r . cryptokey . getPublicKeyForAddress ( destAddr , addrlen ) ; err == nil {
2018-11-07 10:29:08 +00:00
// A public key was found, get the node ID for the search
2018-11-07 10:16:46 +00:00
destPubKey = & key
destNodeID = getNodeID ( destPubKey )
2018-11-07 10:29:08 +00:00
// Do a quick check to ensure that the node ID refers to a vaild Yggdrasil
// address or subnet - this might be superfluous
2018-11-07 10:04:31 +00:00
addr := * address_addrForNodeID ( destNodeID )
copy ( destAddr [ : ] , addr [ : ] )
copy ( destSnet [ : ] , addr [ : ] )
if ! destAddr . isValid ( ) && ! destSnet . isValid ( ) {
2018-11-05 22:39:30 +00:00
return
}
} else {
2018-11-07 10:29:08 +00:00
// No public key was found in the CKR table so we've exhausted our options
2018-11-05 22:39:30 +00:00
return
}
2018-01-04 22:37:51 +00:00
}
doSearch := func ( packet [ ] byte ) {
var nodeID , mask * NodeID
2018-11-07 10:04:31 +00:00
switch {
case destNodeID != nil :
// We already know the full node ID, probably because it's from a CKR
// route in which the public key is known ahead of time
nodeID = destNodeID
var m NodeID
for i := range m {
m [ i ] = 0xFF
}
mask = & m
case destAddr . isValid ( ) :
// We don't know the full node ID - try and use the address to generate
// a truncated node ID
nodeID , mask = destAddr . getNodeIDandMask ( )
case destSnet . isValid ( ) :
// We don't know the full node ID - try and use the subnet to generate
// a truncated node ID
nodeID , mask = destSnet . getNodeIDandMask ( )
default :
return
2018-01-04 22:37:51 +00:00
}
sinfo , isIn := r . core . searches . searches [ * nodeID ]
if ! isIn {
2018-06-02 04:34:21 +00:00
sinfo = r . core . searches . newIterSearch ( nodeID , mask )
2018-01-04 22:37:51 +00:00
}
if packet != nil {
sinfo . packet = packet
}
2018-06-02 04:34:21 +00:00
r . core . searches . continueSearch ( sinfo )
2018-01-04 22:37:51 +00:00
}
var sinfo * sessionInfo
var isIn bool
2018-11-07 10:04:31 +00:00
if destAddr . isValid ( ) {
sinfo , isIn = r . core . sessions . getByTheirAddr ( & destAddr )
2018-01-04 22:37:51 +00:00
}
2018-11-07 10:04:31 +00:00
if destSnet . isValid ( ) {
sinfo , isIn = r . core . sessions . getByTheirSubnet ( & destSnet )
2018-01-04 22:37:51 +00:00
}
switch {
case ! isIn || ! sinfo . init :
// No or unintiialized session, so we need to search first
doSearch ( bs )
case time . Since ( sinfo . time ) > 6 * time . Second :
2018-04-22 20:31:30 +00:00
if sinfo . time . Before ( sinfo . pingTime ) && time . Since ( sinfo . pingTime ) > 6 * time . Second {
// We haven't heard from the dest in a while
// We tried pinging but didn't get a response
// They may have changed coords
// Try searching to discover new coords
// Note that search spam is throttled internally
doSearch ( nil )
} else {
// We haven't heard about the dest in a while
now := time . Now ( )
if ! sinfo . time . Before ( sinfo . pingTime ) {
// Update pingTime to start the clock for searches (above)
sinfo . pingTime = now
}
if time . Since ( sinfo . pingSend ) > time . Second {
// Send at most 1 ping per second
sinfo . pingSend = now
r . core . sessions . sendPingPong ( sinfo , false )
}
}
fallthrough // Also send the packet
2018-01-04 22:37:51 +00:00
default :
2018-11-07 10:16:46 +00:00
// If we know the public key ahead of time (i.e. a CKR route) then check
// if the session perm pub key matches before we send the packet to it
if destPubKey != nil {
if ! bytes . Equal ( ( * destPubKey ) [ : ] , sinfo . theirPermPub [ : ] ) {
return
}
}
2018-05-18 17:56:33 +00:00
// Drop packets if the session MTU is 0 - this means that one or other
// side probably has their TUN adapter disabled
if sinfo . getMTU ( ) == 0 {
// Don't continue - drop the packet
return
}
2018-02-14 22:59:24 +00:00
// Generate an ICMPv6 Packet Too Big for packets larger than session MTU
2018-02-14 14:08:40 +00:00
if len ( bs ) > int ( sinfo . getMTU ( ) ) {
2018-02-14 22:59:24 +00:00
// Get the size of the oversized payload, up to a max of 900 bytes
window := 900
if int ( sinfo . getMTU ( ) ) < window {
window = int ( sinfo . getMTU ( ) )
}
// Create the Packet Too Big response
ptb := & icmp . PacketTooBig {
2018-02-15 13:38:54 +00:00
MTU : int ( sinfo . getMTU ( ) ) ,
2018-02-14 22:59:24 +00:00
Data : bs [ : window ] ,
}
// Create the ICMPv6 response from it
2018-12-14 17:35:02 +00:00
icmpv6Buf , err := r . tun . icmpv6 . create_icmpv6_tun (
2018-03-10 22:31:36 +00:00
bs [ 8 : 24 ] , bs [ 24 : 40 ] ,
ipv6 . ICMPTypePacketTooBig , 0 , ptb )
2018-02-14 22:59:24 +00:00
if err == nil {
r . recv <- icmpv6Buf
}
// Don't continue - drop the packet
return
2018-02-14 14:08:40 +00:00
}
2018-11-07 10:16:46 +00:00
2018-02-27 00:12:28 +00:00
sinfo . send <- bs
2018-01-04 22:37:51 +00:00
}
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Called for incoming traffic by the session worker for that connection.
// Checks that the IP address is correct (matches the session) and passes the packet to the tun/tap.
2018-11-05 22:58:58 +00:00
func ( r * router ) recvPacket ( bs [ ] byte , sinfo * sessionInfo ) {
2018-01-26 23:30:51 +00:00
// Note: called directly by the session worker, not the router goroutine
2018-01-04 22:37:51 +00:00
if len ( bs ) < 24 {
2018-01-19 23:33:04 +00:00
util_putBytes ( bs )
2018-01-04 22:37:51 +00:00
return
}
2018-11-06 20:49:19 +00:00
var sourceAddr address
2018-11-06 19:23:20 +00:00
var dest address
2018-11-06 20:49:19 +00:00
var snet subnet
var addrlen int
if bs [ 0 ] & 0xf0 == 0x60 {
// IPv6 address
addrlen = 16
copy ( sourceAddr [ : addrlen ] , bs [ 8 : ] )
copy ( dest [ : addrlen ] , bs [ 24 : ] )
2018-11-11 06:00:47 +00:00
copy ( snet [ : addrlen / 2 ] , bs [ 8 : ] )
2018-11-06 20:49:19 +00:00
} else if bs [ 0 ] & 0xf0 == 0x40 {
// IPv4 address
addrlen = 4
copy ( sourceAddr [ : addrlen ] , bs [ 12 : ] )
copy ( dest [ : addrlen ] , bs [ 16 : ] )
} else {
2018-11-06 22:57:53 +00:00
// Unknown address length
2018-11-06 20:49:19 +00:00
return
}
2018-11-07 10:29:08 +00:00
// Check that the packet is destined for either our Yggdrasil address or
// subnet, or that it matches one of the crypto-key routing source routes
2018-11-06 20:49:19 +00:00
if ! r . cryptokey . isValidSource ( dest , addrlen ) {
2018-11-06 19:23:20 +00:00
util_putBytes ( bs )
return
}
2018-11-07 10:29:08 +00:00
// See whether the packet they sent should have originated from this session
2018-01-19 23:33:04 +00:00
switch {
2018-11-06 20:49:19 +00:00
case sourceAddr . isValid ( ) && sourceAddr == sinfo . theirAddr :
2018-11-05 22:58:58 +00:00
case snet . isValid ( ) && snet == sinfo . theirSubnet :
2018-01-19 23:33:04 +00:00
default :
2018-11-06 20:49:19 +00:00
key , err := r . cryptokey . getPublicKeyForAddress ( sourceAddr , addrlen )
2018-11-05 22:58:58 +00:00
if err != nil || key != sinfo . theirPermPub {
util_putBytes ( bs )
return
}
2018-01-04 22:37:51 +00:00
}
//go func() { r.recv<-bs }()
r . recv <- bs
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Checks incoming traffic type and passes it to the appropriate handler.
2017-12-29 04:16:20 +00:00
func ( r * router ) handleIn ( packet [ ] byte ) {
2018-01-04 22:37:51 +00:00
pType , pTypeLen := wire_decode_uint64 ( packet )
if pTypeLen == 0 {
return
}
switch pType {
case wire_Traffic :
r . handleTraffic ( packet )
case wire_ProtocolTraffic :
r . handleProto ( packet )
2018-06-12 22:50:08 +00:00
default :
2018-01-04 22:37:51 +00:00
}
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Handles incoming traffic, i.e. encapuslated ordinary IPv6 packets.
// Passes them to the crypto session worker to be decrypted and sent to the tun/tap.
2017-12-29 04:16:20 +00:00
func ( r * router ) handleTraffic ( packet [ ] byte ) {
2018-01-04 22:37:51 +00:00
defer util_putBytes ( packet )
p := wire_trafficPacket { }
if ! p . decode ( packet ) {
return
}
2018-06-02 20:21:05 +00:00
sinfo , isIn := r . core . sessions . getSessionForHandle ( & p . Handle )
2018-01-04 22:37:51 +00:00
if ! isIn {
return
}
2018-02-27 00:12:28 +00:00
sinfo . recv <- & p
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Handles protocol traffic by decrypting it, checking its type, and passing it to the appropriate handler for that traffic type.
2017-12-29 04:16:20 +00:00
func ( r * router ) handleProto ( packet [ ] byte ) {
2018-01-04 22:37:51 +00:00
// First parse the packet
p := wire_protoTrafficPacket { }
if ! p . decode ( packet ) {
return
}
// Now try to open the payload
var sharedKey * boxSharedKey
2018-06-02 20:21:05 +00:00
if p . ToKey == r . core . boxPub {
2018-01-04 22:37:51 +00:00
// Try to open using our permanent key
2018-06-02 20:21:05 +00:00
sharedKey = r . core . sessions . getSharedKey ( & r . core . boxPriv , & p . FromKey )
2018-01-04 22:37:51 +00:00
} else {
return
}
2018-06-02 20:21:05 +00:00
bs , isOK := boxOpen ( sharedKey , p . Payload , & p . Nonce )
2018-01-04 22:37:51 +00:00
if ! isOK {
return
}
// Now do something with the bytes in bs...
// send dht messages to dht, sessionRefresh to sessions, data to tun...
// For data, should check that key and IP match...
bsType , bsTypeLen := wire_decode_uint64 ( bs )
if bsTypeLen == 0 {
return
}
switch bsType {
case wire_SessionPing :
2018-06-02 20:21:05 +00:00
r . handlePing ( bs , & p . FromKey )
2018-01-04 22:37:51 +00:00
case wire_SessionPong :
2018-06-02 20:21:05 +00:00
r . handlePong ( bs , & p . FromKey )
2018-01-04 22:37:51 +00:00
case wire_DHTLookupRequest :
2018-06-02 20:21:05 +00:00
r . handleDHTReq ( bs , & p . FromKey )
2018-01-04 22:37:51 +00:00
case wire_DHTLookupResponse :
2018-06-02 20:21:05 +00:00
r . handleDHTRes ( bs , & p . FromKey )
2018-06-10 23:03:28 +00:00
default :
util_putBytes ( packet )
2018-01-04 22:37:51 +00:00
}
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Decodes session pings from wire format and passes them to sessions.handlePing where they either create or update a session.
2017-12-29 04:16:20 +00:00
func ( r * router ) handlePing ( bs [ ] byte , fromKey * boxPubKey ) {
2018-01-04 22:37:51 +00:00
ping := sessionPing { }
if ! ping . decode ( bs ) {
return
}
2018-06-02 21:19:42 +00:00
ping . SendPermPub = * fromKey
2018-01-04 22:37:51 +00:00
r . core . sessions . handlePing ( & ping )
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Handles session pongs (which are really pings with an extra flag to prevent acknowledgement).
2017-12-29 04:16:20 +00:00
func ( r * router ) handlePong ( bs [ ] byte , fromKey * boxPubKey ) {
2018-01-04 22:37:51 +00:00
r . handlePing ( bs , fromKey )
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Decodes dht requests and passes them to dht.handleReq to trigger a lookup/response.
2017-12-29 04:16:20 +00:00
func ( r * router ) handleDHTReq ( bs [ ] byte , fromKey * boxPubKey ) {
2018-01-04 22:37:51 +00:00
req := dhtReq { }
if ! req . decode ( bs ) {
return
}
2018-06-02 21:19:42 +00:00
req . Key = * fromKey
2018-01-04 22:37:51 +00:00
r . core . dht . handleReq ( & req )
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Decodes dht responses and passes them to dht.handleRes to update the DHT table and further pass them to the search code (if applicable).
2017-12-29 04:16:20 +00:00
func ( r * router ) handleDHTRes ( bs [ ] byte , fromKey * boxPubKey ) {
2018-01-04 22:37:51 +00:00
res := dhtRes { }
if ! res . decode ( bs ) {
return
}
2018-06-02 21:19:42 +00:00
res . Key = * fromKey
2018-01-04 22:37:51 +00:00
r . core . dht . handleRes ( & res )
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Passed a function to call.
// This will send the function to r.admin and block until it finishes.
// It's used by the admin socket to ask the router mainLoop goroutine about information in the session or dht structs, which cannot be read safely from outside that goroutine.
2018-01-21 18:55:45 +00:00
func ( r * router ) doAdmin ( f func ( ) ) {
// Pass this a function that needs to be run by the router's main goroutine
// It will pass the function to the router and wait for the router to finish
done := make ( chan struct { } )
newF := func ( ) {
f ( )
close ( done )
}
r . admin <- newF
<- done
}