diff options
Diffstat (limited to 'src/device.go')
-rw-r--r-- | src/device.go | 309 |
1 files changed, 194 insertions, 115 deletions
diff --git a/src/device.go b/src/device.go index f1c09c6..0317b60 100644 --- a/src/device.go +++ b/src/device.go @@ -9,106 +9,170 @@ import ( ) type Device struct { - isUp AtomicBool // device is (going) up - isClosed AtomicBool // device is closed? (acting as guard) - log *Logger // collection of loggers for levels - idCounter uint // for assigning debug ids to peers - fwMark uint32 - tun struct { - device TUNDevice - mtu int32 - } + isUp AtomicBool // device is (going) up + isClosed AtomicBool // device is closed? (acting as guard) + log *Logger + + // synchronized resources (locks acquired in order) + state struct { mutex deadlock.Mutex changing AtomicBool current bool } - pool struct { - messageBuffers sync.Pool - } + net struct { mutex deadlock.RWMutex bind Bind // bind interface port uint16 // listening port fwmark uint32 // mark value (0 = disabled) } - mutex deadlock.RWMutex - privateKey NoisePrivateKey - publicKey NoisePublicKey - routingTable RoutingTable - indices IndexTable - queue struct { + + noise struct { + mutex deadlock.RWMutex + privateKey NoisePrivateKey + publicKey NoisePublicKey + } + + routing struct { + mutex deadlock.RWMutex + table RoutingTable + } + + peers struct { + mutex deadlock.RWMutex + keyMap map[NoisePublicKey]*Peer + } + + // unprotected / "self-synchronising resources" + + indices IndexTable + mac CookieChecker + + rate struct { + underLoadUntil atomic.Value + limiter Ratelimiter + } + + pool struct { + messageBuffers sync.Pool + } + + queue struct { encryption chan *QueueOutboundElement decryption chan *QueueInboundElement handshake chan QueueHandshakeElement } + signal struct { stop Signal } - underLoadUntil atomic.Value - ratelimiter Ratelimiter - peers map[NoisePublicKey]*Peer - mac CookieChecker + + tun struct { + device TUNDevice + mtu int32 + } } -func deviceUpdateState(device *Device) { +/* Converts the peer into a "zombie", which remains in the peer map, + * but processes no packets and does not exists in the routing table. + * + * Must hold: + * device.peers.mutex : exclusive lock + * device.routing : exclusive lock + */ +func unsafeRemovePeer(device *Device, peer *Peer, key NoisePublicKey) { - // check if state already being updated (guard) + // stop routing and processing of packets - if device.state.changing.Swap(true) { - return + device.routing.table.RemovePeer(peer) + peer.Stop() + + // clean index table + + kp := &peer.keyPairs + kp.mutex.Lock() + + if kp.previous != nil { + device.indices.Delete(kp.previous.localIndex) } - // compare to current state of device + if kp.current != nil { + device.indices.Delete(kp.current.localIndex) + } - device.state.mutex.Lock() + if kp.next != nil { + device.indices.Delete(kp.next.localIndex) + } - newIsUp := device.isUp.Get() + kp.previous = nil + kp.current = nil + kp.next = nil + kp.mutex.Unlock() - if newIsUp == device.state.current { - device.state.mutex.Unlock() - device.state.changing.Set(false) + // remove from peer map + + delete(device.peers.keyMap, key) +} + +func deviceUpdateState(device *Device) { + + // check if state already being updated (guard) + + if device.state.changing.Swap(true) { return } - device.state.mutex.Unlock() + func() { - // change state of device + // compare to current state of device - switch newIsUp { - case true: + device.state.mutex.Lock() + defer device.state.mutex.Unlock() - // start listener + newIsUp := device.isUp.Get() - if err := device.BindUpdate(); err != nil { - device.isUp.Set(false) - break + if newIsUp == device.state.current { + device.state.changing.Set(false) + return } - // start every peer + // change state of device - for _, peer := range device.peers { - peer.Start() - } + switch newIsUp { + case true: + if err := device.BindUpdate(); err != nil { + device.isUp.Set(false) + break + } - case false: + device.peers.mutex.Lock() + defer device.peers.mutex.Unlock() - // stop listening + for _, peer := range device.peers.keyMap { + peer.Start() + } - device.BindClose() + case false: + device.BindClose() - // stop every peer + device.peers.mutex.Lock() + defer device.peers.mutex.Unlock() - for _, peer := range device.peers { - peer.Stop() + for _, peer := range device.peers.keyMap { + println("stopping peer") + peer.Stop() + } } - } - // update state variables - // and check for state change in the mean time + // update state variables + + device.state.current = newIsUp + device.state.changing.Set(false) + }() + + // check for state change in the mean time - device.state.current = newIsUp - device.state.changing.Set(false) deviceUpdateState(device) } @@ -133,18 +197,6 @@ func (device *Device) Down() { deviceUpdateState(device) } -/* Warning: - * The caller must hold the device mutex (write lock) - */ -func removePeerUnsafe(device *Device, key NoisePublicKey) { - peer, ok := device.peers[key] - if !ok { - return - } - device.routingTable.RemovePeer(peer) - delete(device.peers, key) -} - func (device *Device) IsUnderLoad() bool { // check if currently under load @@ -152,54 +204,66 @@ func (device *Device) IsUnderLoad() bool { now := time.Now() underLoad := len(device.queue.handshake) >= UnderLoadQueueSize if underLoad { - device.underLoadUntil.Store(now.Add(time.Second)) + device.rate.underLoadUntil.Store(now.Add(time.Second)) return true } // check if recently under load - until := device.underLoadUntil.Load().(time.Time) + until := device.rate.underLoadUntil.Load().(time.Time) return until.After(now) } func (device *Device) SetPrivateKey(sk NoisePrivateKey) error { - device.mutex.Lock() - defer device.mutex.Unlock() + + // lock required resources + + device.noise.mutex.Lock() + defer device.noise.mutex.Unlock() + + device.routing.mutex.Lock() + defer device.routing.mutex.Unlock() + + device.peers.mutex.Lock() + defer device.peers.mutex.Unlock() + + for _, peer := range device.peers.keyMap { + peer.handshake.mutex.RLock() + defer peer.handshake.mutex.RUnlock() + } // remove peers with matching public keys publicKey := sk.publicKey() - for key, peer := range device.peers { - h := &peer.handshake - h.mutex.RLock() - if h.remoteStatic.Equals(publicKey) { - removePeerUnsafe(device, key) + for key, peer := range device.peers.keyMap { + if peer.handshake.remoteStatic.Equals(publicKey) { + unsafeRemovePeer(device, peer, key) } - h.mutex.RUnlock() } // update key material - device.privateKey = sk - device.publicKey = publicKey + device.noise.privateKey = sk + device.noise.publicKey = publicKey device.mac.Init(publicKey) - // do DH pre-computations + // do static-static DH pre-computations + + rmKey := device.noise.privateKey.IsZero() - rmKey := device.privateKey.IsZero() + for key, peer := range device.peers.keyMap { + + hs := &peer.handshake - for key, peer := range device.peers { - h := &peer.handshake - h.mutex.Lock() if rmKey { - h.precomputedStaticStatic = [NoisePublicKeySize]byte{} + hs.precomputedStaticStatic = [NoisePublicKeySize]byte{} } else { - h.precomputedStaticStatic = device.privateKey.sharedSecret(h.remoteStatic) - if isZero(h.precomputedStaticStatic[:]) { - removePeerUnsafe(device, key) - } + hs.precomputedStaticStatic = device.noise.privateKey.sharedSecret(hs.remoteStatic) + } + + if isZero(hs.precomputedStaticStatic[:]) { + unsafeRemovePeer(device, peer, key) } - h.mutex.Unlock() } return nil @@ -215,21 +279,23 @@ func (device *Device) PutMessageBuffer(msg *[MaxMessageSize]byte) { func NewDevice(tun TUNDevice, logger *Logger) *Device { device := new(Device) - device.mutex.Lock() - defer device.mutex.Unlock() device.isUp.Set(false) device.isClosed.Set(false) device.log = logger - device.peers = make(map[NoisePublicKey]*Peer) device.tun.device = tun + device.peers.keyMap = make(map[NoisePublicKey]*Peer) - device.indices.Init() - device.ratelimiter.Init() + // initialize anti-DoS / anti-scanning features + + device.rate.limiter.Init() + device.rate.underLoadUntil.Store(time.Time{}) - device.routingTable.Reset() - device.underLoadUntil.Store(time.Time{}) + // initialize noise & crypt-key routine + + device.indices.Init() + device.routing.table.Reset() // setup buffer pool @@ -264,36 +330,50 @@ func NewDevice(tun TUNDevice, logger *Logger) *Device { go device.RoutineReadFromTUN() go device.RoutineTUNEventReader() - go device.ratelimiter.RoutineGarbageCollector(device.signal.stop) + go device.rate.limiter.RoutineGarbageCollector(device.signal.stop) return device } func (device *Device) LookupPeer(pk NoisePublicKey) *Peer { - device.mutex.RLock() - defer device.mutex.RUnlock() - return device.peers[pk] + device.peers.mutex.RLock() + defer device.peers.mutex.RUnlock() + + return device.peers.keyMap[pk] } func (device *Device) RemovePeer(key NoisePublicKey) { - device.mutex.Lock() - defer device.mutex.Unlock() - removePeerUnsafe(device, key) + device.noise.mutex.Lock() + defer device.noise.mutex.Unlock() + + device.routing.mutex.Lock() + defer device.routing.mutex.Unlock() + + device.peers.mutex.Lock() + defer device.peers.mutex.Unlock() + + // stop peer and remove from routing + + peer, ok := device.peers.keyMap[key] + if ok { + unsafeRemovePeer(device, peer, key) + } } func (device *Device) RemoveAllPeers() { - device.mutex.Lock() - defer device.mutex.Unlock() - for key, peer := range device.peers { - peer.Stop() - peer, ok := device.peers[key] - if !ok { - return - } - device.routingTable.RemovePeer(peer) - delete(device.peers, key) + device.routing.mutex.Lock() + defer device.routing.mutex.Unlock() + + device.peers.mutex.Lock() + defer device.peers.mutex.Unlock() + + for key, peer := range device.peers.keyMap { + println("rm", peer.String()) + unsafeRemovePeer(device, peer, key) } + + device.peers.keyMap = make(map[NoisePublicKey]*Peer) } func (device *Device) Close() { @@ -305,7 +385,6 @@ func (device *Device) Close() { device.tun.device.Close() device.BindClose() device.isUp.Set(false) - println("remove") device.RemoveAllPeers() device.log.Info.Println("Interface closed") } |