document address, crypto, and util

This commit is contained in:
Arceliar 2019-09-01 18:53:45 -05:00
parent 903a8921fc
commit cd99d04bd4
6 changed files with 104 additions and 40 deletions

View File

@ -2,21 +2,21 @@ package address
import "github.com/yggdrasil-network/yggdrasil-go/src/crypto" import "github.com/yggdrasil-network/yggdrasil-go/src/crypto"
// address represents an IPv6 address in the yggdrasil address range. // Address represents an IPv6 address in the yggdrasil address range.
type Address [16]byte type Address [16]byte
// subnet represents an IPv6 /64 subnet in the yggdrasil subnet range. // Subnet represents an IPv6 /64 subnet in the yggdrasil subnet range.
type Subnet [8]byte type Subnet [8]byte
// address_prefix is the prefix used for all addresses and subnets in the network. // GetPrefix returns the address prefix used by yggdrasil.
// The current implementation requires this to be a muliple of 8 bits + 7 bits. // The current implementation requires this to be a muliple of 8 bits + 7 bits.
// The 8th bit of the last byte is used to signal nodes (0) or /64 prefixes (1). // The 8th bit of the last byte is used to signal nodes (0) or /64 prefixes (1).
// Nodes that configure this differently will be unable to communicate with eachother, though routing and the DHT machinery *should* still work. // Nodes that configure this differently will be unable to communicate with eachother using IP packets, though routing and the DHT machinery *should* still work.
func GetPrefix() [1]byte { func GetPrefix() [1]byte {
return [...]byte{0x02} return [...]byte{0x02}
} }
// isValid returns true if an address falls within the range used by nodes in the network. // IsValid returns true if an address falls within the range used by nodes in the network.
func (a *Address) IsValid() bool { func (a *Address) IsValid() bool {
prefix := GetPrefix() prefix := GetPrefix()
for idx := range prefix { for idx := range prefix {
@ -27,7 +27,7 @@ func (a *Address) IsValid() bool {
return true return true
} }
// isValid returns true if a prefix falls within the range usable by the network. // IsValid returns true if a prefix falls within the range usable by the network.
func (s *Subnet) IsValid() bool { func (s *Subnet) IsValid() bool {
prefix := GetPrefix() prefix := GetPrefix()
l := len(prefix) l := len(prefix)
@ -39,8 +39,8 @@ func (s *Subnet) IsValid() bool {
return (*s)[l-1] == prefix[l-1]|0x01 return (*s)[l-1] == prefix[l-1]|0x01
} }
// address_addrForNodeID takes a *NodeID as an argument and returns an *address. // AddrForNodeID takes a *NodeID as an argument and returns an *Address.
// This subnet begins with the address prefix, with the last bit set to 0 to indicate an address. // This address begins with the contents of GetPrefix(), with the last bit set to 0 to indicate an address.
// The following 8 bits are set to the number of leading 1 bits in the NodeID. // The following 8 bits are set to the number of leading 1 bits in the NodeID.
// The NodeID, excluding the leading 1 bits and the first leading 0 bit, is truncated to the appropriate length and makes up the remainder of the address. // The NodeID, excluding the leading 1 bits and the first leading 0 bit, is truncated to the appropriate length and makes up the remainder of the address.
func AddrForNodeID(nid *crypto.NodeID) *Address { func AddrForNodeID(nid *crypto.NodeID) *Address {
@ -80,7 +80,7 @@ func AddrForNodeID(nid *crypto.NodeID) *Address {
return &addr return &addr
} }
// address_subnetForNodeID takes a *NodeID as an argument and returns a *subnet. // SubnetForNodeID takes a *NodeID as an argument and returns an *Address.
// This subnet begins with the address prefix, with the last bit set to 1 to indicate a prefix. // This subnet begins with the address prefix, with the last bit set to 1 to indicate a prefix.
// The following 8 bits are set to the number of leading 1 bits in the NodeID. // The following 8 bits are set to the number of leading 1 bits in the NodeID.
// The NodeID, excluding the leading 1 bits and the first leading 0 bit, is truncated to the appropriate length and makes up the remainder of the subnet. // The NodeID, excluding the leading 1 bits and the first leading 0 bit, is truncated to the appropriate length and makes up the remainder of the subnet.
@ -96,10 +96,10 @@ func SubnetForNodeID(nid *crypto.NodeID) *Subnet {
return &snet return &snet
} }
// getNodeIDandMask returns two *NodeID. // GetNodeIDandMask returns two *NodeID.
// The first is a NodeID with all the bits known from the address set to their correct values. // The first is a NodeID with all the bits known from the Address set to their correct values.
// The second is a bitmask with 1 bit set for each bit that was known from the address. // The second is a bitmask with 1 bit set for each bit that was known from the Address.
// This is used to look up NodeIDs in the DHT and tell if they match an address. // This is used to look up NodeIDs in the DHT and tell if they match an Address.
func (a *Address) GetNodeIDandMask() (*crypto.NodeID, *crypto.NodeID) { func (a *Address) GetNodeIDandMask() (*crypto.NodeID, *crypto.NodeID) {
// Mask is a bitmask to mark the bits visible from the address // Mask is a bitmask to mark the bits visible from the address
// This means truncated leading 1s, first leading 0, and visible part of addr // This means truncated leading 1s, first leading 0, and visible part of addr
@ -126,10 +126,10 @@ func (a *Address) GetNodeIDandMask() (*crypto.NodeID, *crypto.NodeID) {
return &nid, &mask return &nid, &mask
} }
// getNodeIDandMask returns two *NodeID. // GetNodeIDandMask returns two *NodeID.
// The first is a NodeID with all the bits known from the address set to their correct values. // The first is a NodeID with all the bits known from the Subnet set to their correct values.
// The second is a bitmask with 1 bit set for each bit that was known from the subnet. // The second is a bitmask with 1 bit set for each bit that was known from the Subnet.
// This is used to look up NodeIDs in the DHT and tell if they match a subnet. // This is used to look up NodeIDs in the DHT and tell if they match a Subnet.
func (s *Subnet) GetNodeIDandMask() (*crypto.NodeID, *crypto.NodeID) { func (s *Subnet) GetNodeIDandMask() (*crypto.NodeID, *crypto.NodeID) {
// As with the address version, but visible parts of the subnet prefix instead // As with the address version, but visible parts of the subnet prefix instead
var nid crypto.NodeID var nid crypto.NodeID

View File

@ -26,12 +26,21 @@ import (
// NodeID and TreeID // NodeID and TreeID
// NodeIDLen is the length (in bytes) of a NodeID.
const NodeIDLen = sha512.Size const NodeIDLen = sha512.Size
// TreeIDLen is the length (in bytes) of a TreeID.
const TreeIDLen = sha512.Size const TreeIDLen = sha512.Size
// handleLen is the length (in bytes) of a Handle.
const handleLen = 8 const handleLen = 8
// NodeID is how a yggdrasil node is identified in the DHT, and is used to derive IPv6 addresses and subnets in the main executable. It is a sha512sum hash of the node's BoxPubKey
type NodeID [NodeIDLen]byte type NodeID [NodeIDLen]byte
// TreeID is how a yggdrasil node is identified in the root selection algorithm used to construct the spanning tree.
type TreeID [TreeIDLen]byte type TreeID [TreeIDLen]byte
type Handle [handleLen]byte type Handle [handleLen]byte
func (n *NodeID) String() string { func (n *NodeID) String() string {
@ -69,16 +78,19 @@ func (n *NodeID) PrefixLength() int {
return len return len
} }
// GetNodeID returns the NodeID associated with a BoxPubKey.
func GetNodeID(pub *BoxPubKey) *NodeID { func GetNodeID(pub *BoxPubKey) *NodeID {
h := sha512.Sum512(pub[:]) h := sha512.Sum512(pub[:])
return (*NodeID)(&h) return (*NodeID)(&h)
} }
// GetTreeID returns the TreeID associated with a BoxPubKey
func GetTreeID(pub *SigPubKey) *TreeID { func GetTreeID(pub *SigPubKey) *TreeID {
h := sha512.Sum512(pub[:]) h := sha512.Sum512(pub[:])
return (*TreeID)(&h) return (*TreeID)(&h)
} }
// NewHandle returns a new (cryptographically random) Handle, used by the session code to identify which session an incoming packet is associated with.
func NewHandle() *Handle { func NewHandle() *Handle {
var h Handle var h Handle
_, err := rand.Read(h[:]) _, err := rand.Read(h[:])
@ -92,14 +104,25 @@ func NewHandle() *Handle {
// Signatures // Signatures
// SigPubKeyLen is the length of a SigPubKey in bytes.
const SigPubKeyLen = ed25519.PublicKeySize const SigPubKeyLen = ed25519.PublicKeySize
// SigPrivKeyLen is the length of a SigPrivKey in bytes.
const SigPrivKeyLen = ed25519.PrivateKeySize const SigPrivKeyLen = ed25519.PrivateKeySize
// SigLen is the length of SigBytes.
const SigLen = ed25519.SignatureSize const SigLen = ed25519.SignatureSize
// SigPubKey is a public ed25519 signing key.
type SigPubKey [SigPubKeyLen]byte type SigPubKey [SigPubKeyLen]byte
// SigPrivKey is a private ed25519 signing key.
type SigPrivKey [SigPrivKeyLen]byte type SigPrivKey [SigPrivKeyLen]byte
// SigBytes is an ed25519 signature.
type SigBytes [SigLen]byte type SigBytes [SigLen]byte
// NewSigKeys generates a public/private ed25519 key pair.
func NewSigKeys() (*SigPubKey, *SigPrivKey) { func NewSigKeys() (*SigPubKey, *SigPrivKey) {
var pub SigPubKey var pub SigPubKey
var priv SigPrivKey var priv SigPrivKey
@ -112,6 +135,7 @@ func NewSigKeys() (*SigPubKey, *SigPrivKey) {
return &pub, &priv return &pub, &priv
} }
// Sign returns the SigBytes signing a message.
func Sign(priv *SigPrivKey, msg []byte) *SigBytes { func Sign(priv *SigPrivKey, msg []byte) *SigBytes {
var sig SigBytes var sig SigBytes
sigSlice := ed25519.Sign(priv[:], msg) sigSlice := ed25519.Sign(priv[:], msg)
@ -119,12 +143,14 @@ func Sign(priv *SigPrivKey, msg []byte) *SigBytes {
return &sig return &sig
} }
// Verify returns true if the provided signature matches the key and message.
func Verify(pub *SigPubKey, msg []byte, sig *SigBytes) bool { func Verify(pub *SigPubKey, msg []byte, sig *SigBytes) bool {
// Should sig be an array instead of a slice?... // Should sig be an array instead of a slice?...
// It's fixed size, but // It's fixed size, but
return ed25519.Verify(pub[:], msg, sig[:]) return ed25519.Verify(pub[:], msg, sig[:])
} }
// Public returns the SigPubKey associated with this SigPrivKey.
func (p SigPrivKey) Public() SigPubKey { func (p SigPrivKey) Public() SigPubKey {
priv := make(ed25519.PrivateKey, ed25519.PrivateKeySize) priv := make(ed25519.PrivateKey, ed25519.PrivateKeySize)
copy(priv[:], p[:]) copy(priv[:], p[:])
@ -138,17 +164,34 @@ func (p SigPrivKey) Public() SigPubKey {
// NaCl-like crypto "box" (curve25519+xsalsa20+poly1305) // NaCl-like crypto "box" (curve25519+xsalsa20+poly1305)
// BoxPubKeyLen is the length of a BoxPubKey in bytes.
const BoxPubKeyLen = 32 const BoxPubKeyLen = 32
// BoxPrivKeyLen is the length of a BoxPrivKey in bytes.
const BoxPrivKeyLen = 32 const BoxPrivKeyLen = 32
// BoxSharedKeyLen is the length of a BoxSharedKey in bytes.
const BoxSharedKeyLen = 32 const BoxSharedKeyLen = 32
// BoxNonceLen is the length of a BoxNonce in bytes.
const BoxNonceLen = 24 const BoxNonceLen = 24
// BoxOverhead is the length of the overhead from boxing something.
const BoxOverhead = box.Overhead const BoxOverhead = box.Overhead
// BoxPubKey is a NaCl-like "box" public key (curve25519+xsalsa20+poly1305).
type BoxPubKey [BoxPubKeyLen]byte type BoxPubKey [BoxPubKeyLen]byte
// BoxPrivKey is a NaCl-like "box" private key (curve25519+xsalsa20+poly1305).
type BoxPrivKey [BoxPrivKeyLen]byte type BoxPrivKey [BoxPrivKeyLen]byte
// BoxSharedKey is a NaCl-like "box" shared key (curve25519+xsalsa20+poly1305).
type BoxSharedKey [BoxSharedKeyLen]byte type BoxSharedKey [BoxSharedKeyLen]byte
// BoxNonce is the nonce used in NaCl-like crypto "box" operations (curve25519+xsalsa20+poly1305), and must not be reused for different messages encrypted using the same BoxSharedKey.
type BoxNonce [BoxNonceLen]byte type BoxNonce [BoxNonceLen]byte
// NewBoxKeys generates a new pair of public/private crypto box keys.
func NewBoxKeys() (*BoxPubKey, *BoxPrivKey) { func NewBoxKeys() (*BoxPubKey, *BoxPrivKey) {
pubBytes, privBytes, err := box.GenerateKey(rand.Reader) pubBytes, privBytes, err := box.GenerateKey(rand.Reader)
if err != nil { if err != nil {
@ -159,6 +202,7 @@ func NewBoxKeys() (*BoxPubKey, *BoxPrivKey) {
return pub, priv return pub, priv
} }
// GetSharedKey returns the shared key derived from your private key and the destination's public key.
func GetSharedKey(myPrivKey *BoxPrivKey, func GetSharedKey(myPrivKey *BoxPrivKey,
othersPubKey *BoxPubKey) *BoxSharedKey { othersPubKey *BoxPubKey) *BoxSharedKey {
var shared [BoxSharedKeyLen]byte var shared [BoxSharedKeyLen]byte
@ -168,6 +212,7 @@ func GetSharedKey(myPrivKey *BoxPrivKey,
return (*BoxSharedKey)(&shared) return (*BoxSharedKey)(&shared)
} }
// BoxOpen returns a message and true if it successfull opens a crypto box using the provided shared key and nonce.
func BoxOpen(shared *BoxSharedKey, func BoxOpen(shared *BoxSharedKey,
boxed []byte, boxed []byte,
nonce *BoxNonce) ([]byte, bool) { nonce *BoxNonce) ([]byte, bool) {
@ -178,6 +223,9 @@ func BoxOpen(shared *BoxSharedKey,
return unboxed, success return unboxed, success
} }
// BoxSeal seals a crypto box using the provided shared key, returning the box and the nonce needed to decrypt it.
// If nonce is nil, a random BoxNonce will be used and returned.
// If nonce is non-nil, then nonce.Increment() will be called before using it, and the incremented BoxNonce is what is returned.
func BoxSeal(shared *BoxSharedKey, unboxed []byte, nonce *BoxNonce) ([]byte, *BoxNonce) { func BoxSeal(shared *BoxSharedKey, unboxed []byte, nonce *BoxNonce) ([]byte, *BoxNonce) {
if nonce == nil { if nonce == nil {
nonce = NewBoxNonce() nonce = NewBoxNonce()
@ -190,6 +238,7 @@ func BoxSeal(shared *BoxSharedKey, unboxed []byte, nonce *BoxNonce) ([]byte, *Bo
return boxed, nonce return boxed, nonce
} }
// NewBoxNonce generates a (cryptographically) random BoxNonce.
func NewBoxNonce() *BoxNonce { func NewBoxNonce() *BoxNonce {
var nonce BoxNonce var nonce BoxNonce
_, err := rand.Read(nonce[:]) _, err := rand.Read(nonce[:])
@ -204,6 +253,7 @@ func NewBoxNonce() *BoxNonce {
return &nonce return &nonce
} }
// Increment adds 2 to a BoxNonce, which is useful if one node intends to send only with odd BoxNonce values, and the other only with even BoxNonce values.
func (n *BoxNonce) Increment() { func (n *BoxNonce) Increment() {
oldNonce := *n oldNonce := *n
n[len(n)-1] += 2 n[len(n)-1] += 2
@ -214,6 +264,7 @@ func (n *BoxNonce) Increment() {
} }
} }
// Public returns the BoxPubKey associated with this BoxPrivKey.
func (p BoxPrivKey) Public() BoxPubKey { func (p BoxPrivKey) Public() BoxPubKey {
var boxPub [BoxPubKeyLen]byte var boxPub [BoxPubKeyLen]byte
var boxPriv [BoxPrivKeyLen]byte var boxPriv [BoxPrivKeyLen]byte
@ -222,9 +273,9 @@ func (p BoxPrivKey) Public() BoxPubKey {
return boxPub return boxPub
} }
// Used to subtract one nonce from another, staying in the range +- 64. // Minus is the result of subtracting the provided BoNonce from this BoxNonce, bounded at +- 64.
// This is used by the nonce progression machinery to advance the bitmask of recently received packets (indexed by nonce), or to check the appropriate bit of the bitmask. // It's primarily used to determine if a new BoxNonce is higher than the last known BoxNonce from a crypto session, and by how much.
// It's basically part of the machinery that prevents replays and duplicate packets. // This is used in the machinery that makes sure replayed packets can't keep a session open indefinitely or stuck using old/bad information about a node.
func (n *BoxNonce) Minus(m *BoxNonce) int64 { func (n *BoxNonce) Minus(m *BoxNonce) int64 {
diff := int64(0) diff := int64(0)
for idx := range n { for idx := range n {

View File

@ -8,12 +8,14 @@ func init() {
debug.SetGCPercent(25) debug.SetGCPercent(25)
} }
// On mobile, just return a nil slice. // GetBytes always returns a nil slice on mobile platforms.
func GetBytes() []byte { func GetBytes() []byte {
return nil return nil
} }
// On mobile, don't do anything. // PutBytes does literally nothing on mobile platforms.
// This is done rather than keeping a free list of bytes on platforms with memory constraints.
// It's needed to help keep memory usage low enough to fall under the limits set for e.g. iOS NEPacketTunnelProvider apps.
func PutBytes(bs []byte) { func PutBytes(bs []byte) {
return return
} }

View File

@ -7,12 +7,12 @@ import "sync"
// This is used to buffer recently used slices of bytes, to prevent allocations in the hot loops. // This is used to buffer recently used slices of bytes, to prevent allocations in the hot loops.
var byteStore = sync.Pool{New: func() interface{} { return []byte(nil) }} var byteStore = sync.Pool{New: func() interface{} { return []byte(nil) }}
// Gets an empty slice from the byte store. // GetBytes returns a 0-length (possibly nil) slice of bytes from a free list, so it may have a larger capacity.
func GetBytes() []byte { func GetBytes() []byte {
return byteStore.Get().([]byte)[:0] return byteStore.Get().([]byte)[:0]
} }
// Puts a slice in the store. // PutBytes stores a slice in a free list, where it can potentially be reused to prevent future allocations.
func PutBytes(bs []byte) { func PutBytes(bs []byte) {
byteStore.Put(bs) byteStore.Put(bs)
} }

View File

@ -7,15 +7,22 @@ import (
"time" "time"
) )
// Cancellation is used to signal when things should shut down, such as signaling anything associated with a Conn to exit.
// This is and is similar to a context, but with an error to specify the reason for the cancellation.
type Cancellation interface { type Cancellation interface {
Finished() <-chan struct{} Finished() <-chan struct{} // Finished returns a channel which will be closed when Cancellation.Cancel is first called.
Cancel(error) error Cancel(error) error // Cancel closes the channel returned by Finished and sets the error returned by error, or else returns the existing error if the Cancellation has already run.
Error() error Error() error // Error returns the error provided to Cancel, or nil if no error has been provided.
} }
// CancellationFinalized is an error returned if a cancellation object was garbage collected and the finalizer was run.
// If you ever see this, then you're probably doing something wrong with your code.
var CancellationFinalized = errors.New("finalizer called") var CancellationFinalized = errors.New("finalizer called")
// CancellationTimeoutError is used when a CancellationWithTimeout or CancellationWithDeadline is cancelled due to said timeout.
var CancellationTimeoutError = errors.New("timeout") var CancellationTimeoutError = errors.New("timeout")
// CancellationFinalizer is set as a finalizer when creating a new cancellation with NewCancellation(), and generally shouldn't be needed by the user, but is included in case other implementations of the same interface want to make use of it.
func CancellationFinalizer(c Cancellation) { func CancellationFinalizer(c Cancellation) {
c.Cancel(CancellationFinalized) c.Cancel(CancellationFinalized)
} }
@ -27,6 +34,7 @@ type cancellation struct {
done bool done bool
} }
// NewCancellation returns a pointer to a struct satisfying the Cancellation interface.
func NewCancellation() Cancellation { func NewCancellation() Cancellation {
c := cancellation{ c := cancellation{
cancel: make(chan struct{}), cancel: make(chan struct{}),
@ -35,10 +43,12 @@ func NewCancellation() Cancellation {
return &c return &c
} }
// Finished returns a channel which will be closed when Cancellation.Cancel is first called.
func (c *cancellation) Finished() <-chan struct{} { func (c *cancellation) Finished() <-chan struct{} {
return c.cancel return c.cancel
} }
// Cancel closes the channel returned by Finished and sets the error returned by error, or else returns the existing error if the Cancellation has already run.
func (c *cancellation) Cancel(err error) error { func (c *cancellation) Cancel(err error) error {
c.mutex.Lock() c.mutex.Lock()
defer c.mutex.Unlock() defer c.mutex.Unlock()
@ -52,6 +62,7 @@ func (c *cancellation) Cancel(err error) error {
} }
} }
// Error returns the error provided to Cancel, or nil if no error has been provided.
func (c *cancellation) Error() error { func (c *cancellation) Error() error {
c.mutex.RLock() c.mutex.RLock()
err := c.err err := c.err
@ -59,6 +70,7 @@ func (c *cancellation) Error() error {
return err return err
} }
// CancellationChild returns a new Cancellation which can be Cancelled independently of the parent, but which will also be Cancelled if the parent is Cancelled first.
func CancellationChild(parent Cancellation) Cancellation { func CancellationChild(parent Cancellation) Cancellation {
child := NewCancellation() child := NewCancellation()
go func() { go func() {
@ -71,6 +83,7 @@ func CancellationChild(parent Cancellation) Cancellation {
return child return child
} }
// CancellationWithTimeout returns a ChildCancellation that will automatically be Cancelled with a CancellationTimeoutError after the timeout.
func CancellationWithTimeout(parent Cancellation, timeout time.Duration) Cancellation { func CancellationWithTimeout(parent Cancellation, timeout time.Duration) Cancellation {
child := CancellationChild(parent) child := CancellationChild(parent)
go func() { go func() {
@ -85,6 +98,7 @@ func CancellationWithTimeout(parent Cancellation, timeout time.Duration) Cancell
return child return child
} }
// CancellationWithTimeout returns a ChildCancellation that will automatically be Cancelled with a CancellationTimeoutError after the specified deadline.
func CancellationWithDeadline(parent Cancellation, deadline time.Time) Cancellation { func CancellationWithDeadline(parent Cancellation, deadline time.Time) Cancellation {
return CancellationWithTimeout(parent, deadline.Sub(time.Now())) return CancellationWithTimeout(parent, deadline.Sub(time.Now()))
} }

View File

@ -9,22 +9,22 @@ import (
"time" "time"
) )
// A wrapper around runtime.Gosched() so it doesn't need to be imported elsewhere. // Yield just executes runtime.Gosched(), and is included so we don't need to explicitly import runtime elsewhere.
func Yield() { func Yield() {
runtime.Gosched() runtime.Gosched()
} }
// A wrapper around runtime.LockOSThread() so it doesn't need to be imported elsewhere. // LockThread executes runtime.LockOSThread(), and is included so we don't need to explicitly import runtime elsewhere.
func LockThread() { func LockThread() {
runtime.LockOSThread() runtime.LockOSThread()
} }
// A wrapper around runtime.UnlockOSThread() so it doesn't need to be imported elsewhere. // UnlockThread executes runtime.UnlockOSThread(), and is included so we don't need to explicitly import runtime elsewhere.
func UnlockThread() { func UnlockThread() {
runtime.UnlockOSThread() runtime.UnlockOSThread()
} }
// Gets a slice of the appropriate length, reusing existing slice capacity when possible // ResizeBytes returns a slice of the specified length. If the provided slice has sufficient capacity, it will be resized and returned rather than allocating a new slice.
func ResizeBytes(bs []byte, length int) []byte { func ResizeBytes(bs []byte, length int) []byte {
if cap(bs) >= length { if cap(bs) >= length {
return bs[:length] return bs[:length]
@ -33,7 +33,7 @@ func ResizeBytes(bs []byte, length int) []byte {
} }
} }
// This is a workaround to go's broken timer implementation // TimerStop stops a timer and makes sure the channel is drained, returns true if the timer was stopped before firing.
func TimerStop(t *time.Timer) bool { func TimerStop(t *time.Timer) bool {
stopped := t.Stop() stopped := t.Stop()
select { select {
@ -43,10 +43,8 @@ func TimerStop(t *time.Timer) bool {
return stopped return stopped
} }
// Run a blocking function with a timeout. // FuncTimeout runs the provided function in a separate goroutine, and returns true if the function finishes executing before the timeout passes, or false if the timeout passes.
// Returns true if the function returns. // It includes no mechanism to stop the function if the timeout fires, so the user is expected to do so on their own (such as with a Cancellation or a context).
// Returns false if the timer fires.
// The blocked function remains blocked--the caller is responsible for somehow killing it.
func FuncTimeout(f func(), timeout time.Duration) bool { func FuncTimeout(f func(), timeout time.Duration) bool {
success := make(chan struct{}) success := make(chan struct{})
go func() { go func() {
@ -63,9 +61,8 @@ func FuncTimeout(f func(), timeout time.Duration) bool {
} }
} }
// This calculates the difference between two arrays and returns items // Difference loops over two strings and returns the elements of A which do not appear in B.
// that appear in A but not in B - useful somewhat when reconfiguring // This is somewhat useful when needing to determine which elements of a configuration file have changed.
// and working out what configuration items changed
func Difference(a, b []string) []string { func Difference(a, b []string) []string {
ab := []string{} ab := []string{}
mb := map[string]bool{} mb := map[string]bool{}
@ -93,7 +90,7 @@ func DecodeCoordString(in string) (out []uint64) {
return out return out
} }
// GetFlowLabel takes an IP packet as an argument and returns some information about the traffic flow. // GetFlowKey takes an IP packet as an argument and returns some information about the traffic flow.
// For IPv4 packets, this is derived from the source and destination protocol and port numbers. // For IPv4 packets, this is derived from the source and destination protocol and port numbers.
// For IPv6 packets, this is derived from the FlowLabel field of the packet if this was set, otherwise it's handled like IPv4. // For IPv6 packets, this is derived from the FlowLabel field of the packet if this was set, otherwise it's handled like IPv4.
// The FlowKey is then used internally by Yggdrasil for congestion control. // The FlowKey is then used internally by Yggdrasil for congestion control.