2019-09-01 19:01:33 -05:00
// Package util contains miscellaneous utilities used by yggdrasil.
// In particular, this includes a crypto worker pool, Cancellation machinery, and a sync.Pool used to reuse []byte.
2018-12-14 20:49:18 -06:00
package util
2017-12-28 22:16:20 -06:00
// These are misc. utility functions that didn't really fit anywhere else
2019-08-05 00:30:12 +01:00
import (
"runtime"
"strconv"
"strings"
"time"
)
2018-01-04 22:37:51 +00:00
2019-09-01 18:53:45 -05:00
// Yield just executes runtime.Gosched(), and is included so we don't need to explicitly import runtime elsewhere.
2018-12-14 20:49:18 -06:00
func Yield ( ) {
2018-01-04 22:37:51 +00:00
runtime . Gosched ( )
2017-12-28 22:16:20 -06:00
}
2019-09-01 18:53:45 -05:00
// LockThread executes runtime.LockOSThread(), and is included so we don't need to explicitly import runtime elsewhere.
2018-12-14 20:49:18 -06:00
func LockThread ( ) {
2018-01-04 22:37:51 +00:00
runtime . LockOSThread ( )
2017-12-28 22:16:20 -06:00
}
2019-09-01 18:53:45 -05:00
// UnlockThread executes runtime.UnlockOSThread(), and is included so we don't need to explicitly import runtime elsewhere.
2018-12-14 20:49:18 -06:00
func UnlockThread ( ) {
2018-01-04 22:37:51 +00:00
runtime . UnlockOSThread ( )
2017-12-28 22:16:20 -06:00
}
2019-09-01 18:53:45 -05:00
// 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.
2019-08-04 14:18:59 -05:00
func ResizeBytes ( bs [ ] byte , length int ) [ ] byte {
if cap ( bs ) >= length {
return bs [ : length ]
}
2020-09-27 09:42:46 -04:00
return make ( [ ] byte , length )
2017-12-28 22:16:20 -06:00
}
2019-02-02 22:18:55 -06:00
2019-09-01 18:53:45 -05:00
// TimerStop stops a timer and makes sure the channel is drained, returns true if the timer was stopped before firing.
2019-02-02 22:18:55 -06:00
func TimerStop ( t * time . Timer ) bool {
2019-04-26 22:42:05 -05:00
stopped := t . Stop ( )
select {
case <- t . C :
default :
2019-02-02 22:18:55 -06:00
}
2019-04-26 22:42:05 -05:00
return stopped
2019-02-02 22:18:55 -06:00
}
2019-02-26 21:07:56 -06:00
2019-09-01 18:53:45 -05:00
// 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.
// 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).
2021-05-08 08:35:58 -05:00
func FuncTimeout ( timeout time . Duration , f func ( ) ) bool {
2019-02-26 21:07:56 -06:00
success := make ( chan struct { } )
go func ( ) {
defer close ( success )
f ( )
} ( )
timer := time . NewTimer ( timeout )
defer TimerStop ( timer )
select {
case <- success :
return true
case <- timer . C :
return false
}
}
2019-03-04 18:41:32 +00:00
2019-09-01 18:53:45 -05:00
// Difference loops over two strings and returns the elements of A which do not appear in B.
// This is somewhat useful when needing to determine which elements of a configuration file have changed.
2019-03-04 18:41:32 +00:00
func Difference ( a , b [ ] string ) [ ] string {
ab := [ ] string { }
mb := map [ string ] bool { }
for _ , x := range b {
mb [ x ] = true
}
for _ , x := range a {
2019-03-08 10:26:46 +00:00
if ! mb [ x ] {
2019-03-04 18:41:32 +00:00
ab = append ( ab , x )
}
}
return ab
}
2019-08-05 00:30:12 +01:00
// DecodeCoordString decodes a string representing coordinates in [1 2 3] format
2019-08-05 10:17:19 +01:00
// and returns a []uint64.
2019-08-05 00:30:12 +01:00
func DecodeCoordString ( in string ) ( out [ ] uint64 ) {
s := strings . Trim ( in , "[]" )
t := strings . Split ( s , " " )
for _ , a := range t {
if u , err := strconv . ParseUint ( a , 0 , 64 ) ; err == nil {
out = append ( out , u )
}
}
return out
}
2019-08-06 19:25:55 -05:00
2019-09-01 18:53:45 -05:00
// GetFlowKey takes an IP packet as an argument and returns some information about the traffic flow.
2019-08-06 19:25:55 -05:00
// 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.
// The FlowKey is then used internally by Yggdrasil for congestion control.
func GetFlowKey ( bs [ ] byte ) uint64 {
// Work out the flowkey - this is used to determine which switch queue
// traffic will be pushed to in the event of congestion
var flowkey uint64
// Get the IP protocol version from the packet
switch bs [ 0 ] & 0xf0 {
case 0x40 : // IPv4 packet
2019-12-11 15:24:43 +02:00
ihl := ( bs [ 0 ] & 0x0f ) * 4 // whole IPv4 header length (min 20)
// 8 is minimum UDP packet length
if ihl >= 20 && len ( bs ) - int ( ihl ) >= 8 {
switch bs [ 9 ] /* protocol */ {
case 0x06 /* TCP */ , 0x11 /* UDP */ , 0x84 /* SCTP */ :
2019-08-06 19:25:55 -05:00
flowkey = uint64 ( bs [ 9 ] ) << 32 /* proto */ |
uint64 ( bs [ ihl + 0 ] ) << 24 | uint64 ( bs [ ihl + 1 ] ) << 16 /* sport */ |
uint64 ( bs [ ihl + 2 ] ) << 8 | uint64 ( bs [ ihl + 3 ] ) /* dport */
}
}
case 0x60 : // IPv6 packet
// Check if the flowlabel was specified in the packet header
flowkey = uint64 ( bs [ 1 ] & 0x0f ) << 16 | uint64 ( bs [ 2 ] ) << 8 | uint64 ( bs [ 3 ] )
// If the flowlabel isn't present, make protokey from proto | sport | dport
// if the packet meets minimum UDP packet length
if flowkey == 0 && len ( bs ) >= 48 {
2019-12-11 15:24:43 +02:00
switch bs [ 9 ] /* protocol */ {
case 0x06 /* TCP */ , 0x11 /* UDP */ , 0x84 /* SCTP */ :
2019-08-06 19:25:55 -05:00
flowkey = uint64 ( bs [ 6 ] ) << 32 /* proto */ |
uint64 ( bs [ 40 ] ) << 24 | uint64 ( bs [ 41 ] ) << 16 /* sport */ |
uint64 ( bs [ 42 ] ) << 8 | uint64 ( bs [ 43 ] ) /* dport */
}
}
}
return flowkey
}