Commit Graph
64 Commits
Author SHA1 Message Date
Joe TsaiandGitHub 84e8f25c21 net/tstun: rename statististics method (#5852)
Rename StatisticsEnable as SetStatisticsEnabled to be consistent
with other similarly named methods.

Rename StatisticsExtract as ExtractStatistics to follow
the convention where methods start with a verb.
It was originally named with Statistics as a prefix so that
statistics related methods would sort well in godoc,
but that property no longer holds.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-10-06 10:46:09 -07:00
Joe TsaiandGitHub dd045a3767 net/flowtrack: add json tags to Tuple (#5849)
By convention, JSON serialization uses camelCase.
Specify such names on the Tuple type.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-10-05 19:40:49 -07:00
Joe TsaiandGitHub a73c423c8a net/tunstats: add Counts.Add (#5848)
The Counts.Add method merges two Counts together.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-10-05 13:18:08 -07:00
Joe TsaiandGitHub 3af0d4d0f2 logtail: always record timestamps in UTC (#5732)
Upstream optimizations to the Go time package will make
unmarshaling of time.Time 3-6x faster. See:
* https://go.dev/cl/425116
* https://go.dev/cl/425197
* https://go.dev/cl/429862

The last optimization avoids a []byte -> string allocation
if the timestamp string less than than 32B.
Unfortunately, the presence of a timezone breaks that optimization.
Drop recording of timezone as this is non-essential information.

Most of the performance gains is upon unmarshal,
but there is also a slight performance benefit to
not marshaling the timezone as well.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-10-05 12:27:52 -07:00
Joe TsaiandGitHub c321363d2c logtail: support a copy ID (#5851)
The copy ID operates similar to a CC in email where
a message is sent to both the primary ID and also the copy ID.
A given log message is uploaded once, but the log server
records it twice for each ID.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-10-05 12:25:10 -07:00
Joe TsaiandGitHub 24ebf161e8 net/tstun: instrument Wrapper with statistics gathering (#5847)
If Wrapper.StatisticsEnable is enabled,
then per-connection counters are maintained.
If enabled, Wrapper.StatisticsExtract must be periodically called
otherwise there is unbounded memory growth.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-10-05 12:24:30 -07:00
Joe TsaiandGitHub 2934c5114c net/tunstats: new package to track per-connection counters (#5818)
High-level API:

	type Statistics struct { ... }
	type Counts struct { TxPackets, TxBytes, RxPackets, RxBytes uint64 }
	func (*Statistics) UpdateTx([]byte)
	func (*Statistics) UpdateRx([]byte)
	func (*Statistics) Extract() map[flowtrack.Tuple]Counts

The API accepts a []byte instead of a packet.Parsed so that a future
implementation can directly hash the address and port bytes,
which are contiguous in most IP packets.
This will be useful for a custom concurrent-safe hashmap implementation.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-10-04 15:10:33 -07:00
Joe TsaiandGitHub e73657d7aa logpolicy: directly expose the logtail server URL (#5788)
Callers of LogHost often jump through hoops to undo the
loss of information dropped by LogHost (e.g., the HTTP scheme).

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-09-29 13:28:51 -07:00
Joe TsaiandGitHub aa0064db4d logpolicy: add NewWithConfigPath (#5625)
The version.CmdName implementation is buggy such that it does not correctly
identify the binary name if it embeds other go binaries.
For now, add a NewWithConfigPath API that allows the caller to explicitly
specify this information.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-09-13 16:30:40 -07:00
Joe TsaiandGitHub 21cd402204 logtail: do not log when backing off (#5485) 2022-08-30 06:21:03 -07:00
Joe TsaiandGitHub 7e40071571 util/deephash: handle slice edge-cases (#5471)
It is unclear whether the lack of checking nil-ness of slices
was an oversight or a deliberate feature.
Lacking a comment, the assumption is that this was an oversight.

Also, expand the logic to perform cycle detection for recursive slices.
We do this on a per-element basis since a slice is semantically
equivalent to a list of pointers.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-30 00:33:18 -07:00
Joe TsaiandGitHub 9bf13fc3d1 util/deephash: remove getTypeInfo (#5469)
Add a new lookupTypeHasher function that is just a cached front-end
around the makeTypeHasher function.
We do not need to worry about the recursive type cycle issue that
made getTypeInfo more complicated since makeTypeHasher
is not directly recursive. All calls to itself happen lazily
through a sync.Once upon first use.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-27 17:39:51 -07:00
Joe TsaiandGitHub ab7e6f3f11 util/deephash: require pointer in API (#5467)
The entry logic of Hash has extra complexity to make sure
we always have an addressable value on hand.
If not, we heap allocate the input.
For this reason we document that there are performance benefits
to always providing a pointer.
Rather than documenting this, just enforce it through generics.

Also, delete the unused HasherForType function.
It's an interesting use of generics, but not well tested.
We can resurrect it from code history if there's a need for it.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-27 16:08:31 -07:00
Joe TsaiandGitHub c5b1565337 util/deephash: move pointer and interface logic to separate function (#5465)
This helps pprof better identify which Go kinds take the most time
since the kind is always in the function name.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-27 15:51:34 -07:00
Joe TsaiandGitHub d2e2d8438b util/deephash: move map logic to separate function (#5464)
This helps pprof better identify which Go kinds take the most time
since the kind is always in the function name.

There is a minor adjustment where we hash the length of the map
to be more on the cautious side.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-27 15:49:26 -07:00
Joe TsaiandGitHub 23c3831ff9 util/deephash: coalesce struct logic (#5466)
Rather than having two copies []fieldInfo,
just maintain one and perform merging in the same pass.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-27 15:39:46 -07:00
Joe TsaiandGitHub 296b008b9f util/deephash: move array and slice logic to separate function (#5463)
This helps pprof better identify which Go kinds take the most time
since the kind is always in the function name.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-27 15:37:36 -07:00
Joe TsaiandGitHub 31bf3874d6 util/deephash: use unsafe.Pointer instead of reflect.Value (#5459)
Use of reflect.Value.SetXXX panics if the provided argument was
obtained from an unexported struct field.
Instead, pass an unsafe.Pointer around and convert to a
reflect.Value when necessary (i.e., for maps and interfaces).
Converting from unsafe.Pointer to reflect.Value guarantees that
none of the read-only bits will be populated.

When running in race mode, we attach type information to the pointer
so that we can type check every pointer operation.
This also type-checks that direct memory hashing is within
the valid range of a struct value.

We add test cases that previously caused deephash to panic,
but now pass.

Performance:

	name              old time/op    new time/op    delta
	Hash              14.1µs ± 1%    14.1µs ± 1%    ~     (p=0.590 n=10+9)
	HashPacketFilter  2.53µs ± 2%    2.44µs ± 1%  -3.79%  (p=0.000 n=9+10)
	TailcfgNode       1.45µs ± 1%    1.43µs ± 0%  -1.36%  (p=0.000 n=9+9)
	HashArray         318ns ± 2%     318ns ± 2%    ~      (p=0.541 n=10+10)
	HashMapAcyclic    32.9µs ± 1%    31.6µs ± 1%  -4.16%  (p=0.000 n=10+9)

There is a slight performance gain due to the use of unsafe.Pointer
over reflect.Value methods. Also, passing an unsafe.Pointer (1 word)
on the stack is cheaper than passing a reflect.Value (3 words).

Performance gains are diminishing since SHA-256 hashing now dominates the runtime.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-27 12:30:35 -07:00
Joe TsaiandGitHub e0c5ac1f02 util/deephash: add debug printer (#5460)
When built with "deephash_debug", print the set of HashXXX methods.

Example usage:

	$ go test -run=GetTypeHasher/string_slice -tags=deephash_debug
	U64(2)+U64(3)+S("foo")+U64(3)+S("bar")+FIN

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-27 12:14:07 -07:00
Joe TsaiandGitHub 70f9fc8c7a util/deephash: rely on direct memory hashing for primitive kinds (#5457)
Rather than separate functions to hash each kind,
just rely on the fact that these are direct memory hashable,
thus simplifying the code.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-26 20:50:56 -07:00
Joe TsaiandGitHub 531ccca648 util/deephash: delete slow path (#5423)
Every implementation of typeHasherFunc always returns true,
which implies that the slow path is no longer executed.
Delete it.

h.hashValueWithType(v, ti, ...) is deleted as it is equivalent to:
	ti.hasher()(h, v)

h.hashValue(v, ...) is deleted as it is equivalent to:
	ti := getTypeInfo(v.Type())
	ti.hasher()(h, v)

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-26 17:46:22 -07:00
Joe TsaiandGitHub 3fc8683585 util/deephash: expand fast-path capabilities (#5404)
Add support for maps and interfaces to the fast path.
Add cycle-detection to the pointer handling logic.
This logic is mostly copied from the slow path.

A future commit will delete the slow path once
the fast path never falls back to the slow path.

Performance:

	name                 old time/op    new time/op    delta
	Hash-24                18.5µs ± 1%    14.9µs ± 2%  -19.52%  (p=0.000 n=10+10)
	HashPacketFilter-24    2.54µs ± 1%    2.60µs ± 1%   +2.19%  (p=0.000 n=10+10)
	HashMapAcyclic-24      31.6µs ± 1%    30.5µs ± 1%   -3.42%  (p=0.000 n=9+8)
	TailcfgNode-24         1.44µs ± 2%    1.43µs ± 1%     ~     (p=0.171 n=10+10)
	HashArray-24            324ns ± 1%     324ns ± 2%     ~     (p=0.425 n=9+9)

The additional cycle detection logic doesn't incur much slow down
since it only activates if a type is recursive, which does not apply
for any of the types that we care about.

There is a notable performance boost since we switch from the fath path
to the slow path less often. Most notably, a struct with a field that
could not be handled by the fast path would previously cause
the entire struct to go through the slow path.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-24 01:31:01 -07:00
Joe TsaiandGitHub d32700c7b2 util/deephash: specialize for netip.Addr and drop AppendTo support (#5402)
There are 5 types that we care about that implement AppendTo:

	key.DiscoPublic
	key.NodePublic
	netip.Prefix
	netipx.IPRange
	netip.Addr

The key types are thin wrappers around [32]byte and are memory hashable.
The netip.Prefix and netipx.IPRange types are thin wrappers over netip.Addr
and are hashable by default if netip.Addr is hashable.
The netip.Addr type is the only one with a complex structure where
the default behavior of deephash does not hash it correctly due to the presence
of the intern.Value type.

Drop support for AppendTo and instead add specialized hashing for netip.Addr
that would be semantically equivalent to == on the netip.Addr values.

The AppendTo support was already broken prior to this change.
It was fully removed (intentionally or not) in #4870.
It was partially restored in #4858 for the fast path,
but still broken in the slow path.
Just drop support for it altogether.

This does mean we lack any ability for types to self-hash themselves.
In the future we can add support for types that implement:

	interface { DeepHash() Sum }

Test and fuzz cases were added for the relevant types that
used to rely on the AppendTo method.
FuzzAddr has been executed on 1 billion samples without issues.

Signed-off-by: Joe Tsai joetsai@digital-static.net
2022-08-18 22:54:56 -07:00
Joe TsaiandGitHub 03f7e4e577 util/hashx: move from sha256x (#5388) 2022-08-16 13:15:33 -07:00
Joe TsaiandGitHub f061d20c9d util/sha256x: rename Hash as Block512 (#5351)
Rename Hash as Block512 to indicate that this is a general-purpose
hash.Hash for any algorithm that operates on 512-bit block sizes.

While we rename the package as hashx in this commit,
a subsequent commit will move the sha256x package to hashx.
This is done separately to avoid confusing git.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-16 09:49:48 -07:00
Joe TsaiandGitHub 44d62b65d0 util/deephash: move typeIsRecursive and canMemHash to types.go (#5386)
Also, rename canMemHash to typeIsMemHashable to be consistent.
There are zero changes to the semantics.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-16 09:31:19 -07:00
Joe TsaiandGitHub d53eb6fa11 util/deephash: simplify typeIsRecursive (#5385)
Any type that is memory hashable must not be recursive since
there are definitely no pointers involved to make a cycle.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-15 23:29:06 -07:00
Joe TsaiandGitHub 23ec3c104a util/deephash: remove unused stack slice in typeIsRecursive (#5363)
No operation ever reads from this variable.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-15 22:36:08 -07:00
Joe TsaiandGitHub c200229f9e util/deephash: simplify canMemHash (#5384)
Put the t.Size() == 0 check first since this is applicable in all cases.
Drop the last struct field conditional since this is covered by the
sumFieldSize check at the end.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-15 22:22:18 -07:00
Joe TsaiandGitHub 32a1a3d1c0 util/deephash: avoid variadic argument for Update (#5372)
Hashing []any is slow since hashing of interfaces is slow.
Hashing of interfaces is slow since we pessimistically assume
that cycles can occur through them and start cycle tracking.

Drop the variadic signature of Update and fix callers to pass in
an anonymous struct so that we are hashing concrete types
near the root of the value tree.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-15 11:22:28 -07:00
Joe TsaiandGitHub 548fa63e49 util/deephash: use binary encoding of time.Time (#5352)
Formatting a time.Time as RFC3339 is slow.
See https://go.dev/issue/54093

Now that we have efficient hashing of fixed-width integers,
just hash the time.Time as a binary value.

Performance:

	Hash-24                19.0µs ± 1%    18.6µs ± 1%   -2.03%  (p=0.000 n=10+9)
	TailcfgNode-24         1.79µs ± 1%    1.40µs ± 1%  -21.74%  (p=0.000 n=10+9)

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-12 14:42:51 -07:00
Joe TsaiandGitHub 1f7479466e util/deephash: use sha256x (#5339)
Switch deephash to use sha256x.Hash.

We add sha256x.HashString to efficiently hash a string.
It uses unsafe under the hood to convert a string to a []byte.
We also modify sha256x.Hash to export the underlying hash.Hash
for testing purposes so that we can intercept all hash.Hash calls.

Performance:

	name                 old time/op    new time/op    delta
	Hash-24                19.8µs ± 1%    19.2µs ± 1%  -3.01%  (p=0.000 n=10+10)
	HashPacketFilter-24    2.61µs ± 0%    2.53µs ± 1%  -3.01%  (p=0.000 n=8+10)
	HashMapAcyclic-24      31.3µs ± 1%    29.8µs ± 0%  -4.80%  (p=0.000 n=10+9)
	TailcfgNode-24         1.83µs ± 1%    1.82µs ± 2%    ~     (p=0.305 n=10+10)
	HashArray-24            344ns ± 2%     323ns ± 1%  -6.02%  (p=0.000 n=9+10)

The performance gains is not as dramatic as sha256x over sha256 due to:
1. most of the hashing already occurring through the direct memory hashing logic, and
2. what does not go through direct memory hashing is slowed down by reflect.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-11 17:44:09 -07:00
Joe TsaiandGitHub 77a92f326d util/deephash: avoid using sync.Pool for reflect.MapIter (#5333)
In Go 1.19, the reflect.Value.MapRange method uses "function outlining"
so that the allocation of reflect.MapIter is inlinable by the caller.
If the iterator doesn't escape the caller, it can be stack allocated.
See https://go.dev/cl/400675

Performance:

	name               old time/op    new time/op    delta
	HashMapAcyclic-24    31.9µs ± 2%    32.1µs ± 1%   ~     (p=0.075 n=10+10)

	name               old alloc/op   new alloc/op   delta
	HashMapAcyclic-24     0.00B          0.00B        ~     (all equal)

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-11 00:33:40 -07:00
Joe TsaiandGitHub 1c3c6b5382 util/sha256x: make Hash.Sum non-escaping (#5338)
Since Hash is a concrete type, we can make it such that
Sum never escapes the input.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-10 17:31:44 -07:00
Joe TsaiandGitHub 76b0e578c5 util/sha256x: new package (#5337)
The hash.Hash provided by sha256.New is much more efficient
if we always provide it with data a multiple of the block size.
This avoids double-copying of data into the internal block
of sha256.digest.x. Effectively, we are managing a block ourselves
to ensure we only ever call hash.Hash.Write with full blocks.

Performance:

	name    old time/op    new time/op    delta
	Hash    33.5µs ± 1%    20.6µs ± 1%  -38.40%  (p=0.000 n=10+9)

The logic has gone through CPU-hours of fuzzing.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-10 15:49:36 -07:00
Joe TsaiandGitHub 539c5e44c5 util/deephash: always keep values addressable (#5328)
The logic of deephash is both simpler and easier to reason about
if values are always addressable.

In Go, the composite kinds are slices, arrays, maps, structs,
interfaces, pointers, channels, and functions,
where we define "composite" as a Go value that encapsulates
some other Go value (e.g., a map is a collection of key-value entries).

In the cases of pointers and slices, the sub-values are always addressable.

In the cases of arrays and structs, the sub-values are always addressable
if and only if the parent value is addressable.

In the case of maps and interfaces, the sub-values are never addressable.
To make them addressable, we need to copy them onto the heap.

For the purposes of deephash, we do not care about channels and functions.

For all non-composite kinds (e.g., strings and ints), they are only addressable
if obtained from one of the composite kinds that produce addressable values
(i.e., pointers, slices, addressable arrays, and addressable structs).
A non-addressible, non-composite kind can be made addressable by
allocating it on the heap, obtaining a pointer to it, and dereferencing it.

Thus, if we can ensure that values are addressable at the entry points,
and shallow copy sub-values whenever we encounter an interface or map,
then we can ensure that all values are always addressable and
assume such property throughout all the logic.

Performance:

	name                 old time/op    new time/op    delta
	Hash-24                21.5µs ± 1%    19.7µs ± 1%  -8.29%  (p=0.000 n=9+9)
	HashPacketFilter-24    2.61µs ± 1%    2.62µs ± 0%  +0.29%  (p=0.037 n=10+9)
	HashMapAcyclic-24      30.8µs ± 1%    30.9µs ± 1%    ~     (p=0.400 n=9+10)
	TailcfgNode-24         1.84µs ± 1%    1.84µs ± 2%    ~     (p=0.928 n=10+10)
	HashArray-24            324ns ± 2%     332ns ± 2%  +2.45%  (p=0.000 n=10+10)

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-09 22:00:02 -07:00
Joe TsaiandGitHub 622d80c007 util/must: new package (#5307)
The Do function assists in calling functions that must succeed.
It only interacts well with functions that return (T, err).
Signatures with more return arguments are not supported.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-05 12:20:50 -07:00
Joe TsaiandGitHub b1fff4499f tsnet: cleanup resources upon start failure (#5301)
In a partially initialized state, we should cleanup
all prior resources when an error occurs.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-04 16:20:48 -07:00
Joe TsaiandGitHub ddebd30917 tsnet: fix closing of filch buffer (#5299)
It should be safe to initialize multiple Server instances
without any resource leaks what-so-ever.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-04 13:22:56 -07:00
Joe TsaiandGitHub 57275a4912 tsweb: add HTTPError.Header (#5251)
The Header field allows the server to specify specific headers to set.
Example use case: server returns 429 with the "Retry-After" header set.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-01 22:57:04 -07:00
Joe TsaiandGitHub a794963e2f tsweb: mark AccessLogRecord fields as omitempty (#5250)
If the field is the zero value, then avoid serializing the field.
This reduces verbosity in server logs.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-01 21:17:38 -07:00
Joe TsaiandGitHub b905db7a56 cmd/derper: remove support for logtail logging (#5248)
There aren't really any useful logs produced by derper.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-01 14:36:55 -07:00
Joe TsaiandGitHub fe3426b4c7 logpolicy: fix config initialization bug (#5010)
If ConfigFromFile cannot find the configuration file,
we must not initialize it with NewConfig.
Instead, we need it to fail validation so that it eventually writes
a newly constructed configuration file.
Otherwise, new tailscale instances will never be able store a persistent
log config and start with a new config file upon every bootup.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-07-05 18:27:31 -07:00
Joe TsaiandGitHub 0f05b2c13f tsnet: use tailscaled as prefix for state file names (#4816)
Use the "tailscaled" prefix instead of "tsnet" for state file names:
1. It is consistent with the pre-existing {{Dir}}/tailscaled.state file.
2. It makes the file layout of `tsnet` and `tailscaled` identical,
   so that they are compatible with each other.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-06-09 11:46:43 -07:00
Joe TsaiandGitHub 96f73b3894 logtail: do not panic in PrivateID.PublicID (#4815)
It is not idiomatic for Go code to panic for situations that
can be normal. For example, if a server receives PrivateID
from a client, it is normal for the server to call
PrivateID.PublicID to validate that the PublicID matches.
However, doing so would panic prior to this change.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-06-07 17:25:05 -07:00
Joe TsaiandGitHub 741ae9956e tstest/integration/vms: use hujson.Standardize instead of hujson.Unmarshal (#4520)
The hujson package transition to just being a pure AST
parser and formatter for HuJSON and not an unmarshaler.

Thus, parse HuJSON as such, convert it to JSON,
and then use the standard JSON unmarshaler.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-05-06 14:16:10 -07:00
Joe TsaiandGitHub d201d217df go.toolchain.rev: update to go1.18.1 (#4438)
Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-04-18 14:42:58 -07:00
Joe TsaiandGitHub 01adcfa688 tailcfg: add omitempty to all fields of Hostinfo (#4360)
This reduces the noise when marshaling only a subset of this type.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-04-05 13:25:14 -07:00
Joe TsaiandGitHub 0173a50bf0 cmd/derper: add a rate limiter for accepting new connection (#3908)
A large influx of new connections can bring down DERP
since it spins off a new goroutine for each connection,
where each routine may do significant amount of work
(e.g., allocating memory and crunching numbers for TLS crypto).
The momentary spike can cause the process to OOM.

This commit sets the groundwork for limiting connections,
but leaves the limit at infinite by default.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-02-11 12:02:38 -08:00
Joe TsaiandGitHub 3e50a265be go.toolchain.rev: pick up new bufio.Writer API for Go1.18 (#3768)
https://github.com/tailscale/go/commit/25fe91a25c9630a50138a135105af19ae7c7c3e7

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-01-19 12:40:56 -08:00
Joe TsaiandGitHub 66164b9307 api: document API endpoint to manipulate keys (#3544)
The new /keys endpoint allows you to list API and machine auth keys.
You can also create machine auth key.
It currently does not support creating another API key.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2021-12-16 13:50:14 -08:00
Joe TsaiandBrad Fitzpatrick 9af27ba829 cmd/cloner: mangle "go:generate" in cloner.go
The "go generate" command blindly looks for "//go:generate" anywhere
in the file regardless of whether it is truly a comment.
Prevent this false positive in cloner.go by mangling the string
to look less like "//go:generate".

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2021-10-16 17:53:43 -07:00
Joe TsaiandGitHub 2fa004a2a0 cmd/cloner: emit go:generate pragmas (#3082)
Emit a go:generate pragma with the full set of flags passed to cloner.
This allows the user to simply run "go generate" at the location
of the generate file to reproduce the file.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2021-10-14 12:25:55 -07:00
Joe TsaiandGitHub 3f1317e3e5 util/deephash: fix TestArrayAllocs
Unfortunately this test fails on certain architectures.
The problem comes down to inconsistencies in the Go escape analysis
where specific variables are marked as escaping on certain architectures.
The variables escaping to the heap are unfortunately in crypto/sha256,
which makes it impossible to fixthis locally in deephash.

For now, fix the test by compensating for the allocations that
occur from calling sha256.digest.Sum.

See golang/go#48055

Fixes #2727

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2021-08-30 10:47:21 -07:00
Joe TsaiandGitHub 30458c71c8 tstime/rate: deflake TestLongRunningQPS
This test is highly dependent on the accuracy of OS timers.
Reduce the number of failures by decreasing the required
accuracy from 0.999 to 0.995.
Also, switch from repeated time.Sleep to using a time.Ticker
for improved accuracy.

Updates #2727

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2021-08-30 10:46:41 -07:00
Joe TsaiandGitHub 9d0c86b6ec util/deephash: remove unnecessary formatting for structs and slices (#2571)
The index for every struct field or slice element and
the number of fields for the struct is unncessary.

The hashing of Go values is unambiguous because every type (except maps)
encodes in a parsable manner. So long as we know the type information,
we could theoretically decode every value (except for maps).

At a high level:
* numbers are encoded as fixed-width records according to precision.
* strings (and AppendTo output) are encoded with a fixed-width length,
followed by the contents of the buffer.
* slices are prefixed by a fixed-width length, followed by the encoding
of each value. So long as we know the type of each element, we could
theoretically decode each element.
* arrays are encoded just like slices, but elide the length
since it is determined from the Go type.
* maps are encoded first with a byte indicating whether it is a cycle.
If a cycle, it is followed by a fixed-width index for the pointer,
otherwise followed by the SHA-256 hash of its contents. The encoding of maps
is not decodeable, but a SHA-256 hash is sufficient to avoid ambiguities.
* interfaces are encoded first with a byte indicating whether it is nil.
If not nil, it is followed by a fixed-width index for the type,
and then the encoding for the underlying value. Having the type be encoded
first ensures that the value could theoretically be decoded next.
* pointers are encoded first with a byte indicating whether it is
1) nil, 2) a cycle, or 3) newly seen. If a cycle, it is followed by
a fixed-width index for the pointer. If newly seen, it is followed by
the encoding for the pointed-at value.

Removing unnecessary details speeds up hashing:

	name              old time/op    new time/op    delta
	Hash-8              76.0µs ± 1%    55.8µs ± 2%  -26.62%        (p=0.000 n=10+10)
	HashMapAcyclic-8    61.9µs ± 0%    62.0µs ± 0%     ~             (p=0.666 n=9+9)
	TailcfgNode-8       10.2µs ± 1%     7.5µs ± 1%  -26.90%         (p=0.000 n=10+9)
	HashArray-8         1.07µs ± 1%    0.70µs ± 1%  -34.67%         (p=0.000 n=10+9)

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2021-08-03 20:35:57 -07:00
Joe TsaiandGitHub d8fbce7eef util/deephash: hash uint{8,16,32,64} explicitly (#2502)
Instead of hashing the humanly formatted forms of a number,
hash the native machine bits of the integers themselves.

There is a small performance gain for this:
	name              old time/op    new time/op    delta
	Hash-8              75.7µs ± 1%    76.0µs ± 2%    ~            (p=0.315 n=10+9)
	HashMapAcyclic-8    63.1µs ± 3%    61.3µs ± 1%  -2.77%        (p=0.000 n=10+10)
	TailcfgNode-8       10.3µs ± 1%    10.2µs ± 1%  -1.48%        (p=0.000 n=10+10)
	HashArray-8         1.07µs ± 1%    1.05µs ± 1%  -1.79%        (p=0.000 n=10+10)

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2021-08-02 21:44:13 -07:00
Joe TsaiandGitHub 01d4dd331d util/deephash: simplify hasher.hashMap (#2503)
The swapping of bufio.Writer between hasher and mapHasher is subtle.
Just embed a hasher in mapHasher to avoid complexity here.

No notable change in performance:
	name              old time/op    new time/op    delta
	Hash-8              76.7µs ± 1%    77.0µs ± 1%    ~            (p=0.182 n=9+10)
	HashMapAcyclic-8    62.4µs ± 1%    62.5µs ± 1%    ~            (p=0.315 n=10+9)
	TailcfgNode-8       10.3µs ± 1%    10.3µs ± 1%  -0.62%         (p=0.004 n=10+9)
	HashArray-8         1.07µs ± 1%    1.06µs ± 1%  -0.98%          (p=0.001 n=8+9)

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2021-08-02 21:29:14 -07:00
Joe TsaiandGitHub d145c594ad util/deephash: improve cycle detection (#2470)
The previous algorithm used a map of all visited pointers.
The strength of this approach is that it quickly prunes any nodes
that we have ever visited before. The detriment of the approach
is that pruning is heavily dependent on the order that pointers
were visited. This is especially relevant for hashing a map
where map entries are visited in a non-deterministic manner,
which would cause the map hash to be non-deterministic
(which defeats the point of a hash).

This new algorithm uses a stack of all visited pointers,
similar to how github.com/google/go-cmp performs cycle detection.
When we visit a pointer, we push it onto the stack, and when
we leave a pointer, we pop it from the stack.
Before visiting a pointer, we first check whether the pointer exists
anywhere in the stack. If yes, then we prune the node.
The detriment of this approach is that we may hash a node more often
than before since we do not prune as aggressively.

The set of visited pointers up until any node is only the
path of nodes up to that node and not any other pointers
that may have been visited elsewhere. This provides us
deterministic hashing regardless of visit order.
We can now delete hashMapFallback and associated complexity,
which only exists because the previous approach was non-deterministic
in the presence of cycles.

This fixes a failure of the old algorithm where obviously different
values are treated as equal because the pruning was too aggresive.
See https://github.com/tailscale/tailscale/issues/2443#issuecomment-883653534

The new algorithm is slightly slower since it prunes less aggresively:
	name              old time/op    new time/op    delta
	Hash-8              66.1µs ± 1%    68.8µs ± 1%   +4.09%        (p=0.000 n=19+19)
	HashMapAcyclic-8    63.0µs ± 1%    62.5µs ± 1%   -0.76%        (p=0.000 n=18+19)
	TailcfgNode-8       9.79µs ± 2%    9.88µs ± 1%   +0.95%        (p=0.000 n=19+17)
	HashArray-8          643ns ± 1%     653ns ± 1%   +1.64%        (p=0.000 n=19+19)
However, a slower but more correct algorithm seems
more favorable than a faster but incorrect algorithm.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2021-07-22 15:22:48 -07:00
Joe TsaiandGitHub d666bd8533 util/deephash: disambiguate hashing of AppendTo (#2483)
Prepend size to AppendTo output.

Fixes #2443

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2021-07-21 11:29:08 -07:00
Joe TsaiandGitHub 23ad028414 util/deephash: include type as part of hash for interfaces (#2476)
A Go interface may hold any number of different concrete types.
Just because two underlying values hash to the same thing
does not mean the two values are identical if they have different
concrete types. As such, include the type in the hash.
2021-07-21 10:26:04 -07:00
Joe TsaiandGitHub a5fb8e0731 util/deephash: introduce deliberate instability (#2477)
Seed the hash upon first use with the current time.
This ensures that the stability of the hash is bounded within
the lifetime of one program execution.
Hopefully, this prevents future bugs where someone assumes that
this hash is stable.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2021-07-21 09:23:04 -07:00
Joe TsaiandGitHub 81cdd2f26c Merge pull request #2464 from tailscale/dsnet/opaque-hash
util/deephash: make hash type opaque
2021-07-20 12:45:30 -07:00
Joe Tsai 9a0c8bdd20 util/deephash: make hash type opaque
The fact that Hash returns a [sha256.Size]byte leaks details about
the underlying hash implementation. This could very well be any other
hashing algorithm with a possible different block size.

Abstract this implementation detail away by declaring an opaque type
that is comparable. While we are changing the signature of UpdateHash,
rename it to just Update to reduce stutter (e.g., deephash.Update).

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2021-07-20 11:03:25 -07:00