chargen2p

package module
v0.0.0-...-b2ce604 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 20, 2021 License: MIT Imports: 9 Imported by: 0

README

2-Phase Character Generator

Go Reference CircleCI

This project is a random data generator that can be used to test network performance (more specifically: throughput).

The original chargen protocol defined in RFC 864 simply says the server will listen for connections and immediately start streaming random data. The UDP version sends one datagram per datagram received.

The server in this project first waits on TCP data and then sends back the same amount. This is a bit safer against write-amplification exploits, and also means we can measure both upload and download, separately. There is no UDP version.

Server Usage

Install chargen2pd on a server, e.g. using the Systemd files in etc/. A Dockerfile is also provided. Since I consider this a spiritual successor of the original chargen, the default is to bind to TCP port 19.

$ go mod download
$ go run ./cmd/chargen2pd -listen-addr tcp:localhost:1919
2006/01/02 15:16:17 Listening for connections on 127.0.0.1:1919...

Client Usage

The testthroughput program sends and receives some data against a server.

$ go mod download
$ go run ./cmd/testthroughput -addr localhost:1919
Uploaded 670040064 bytes in 1.584983235s: 3381.941 Mbps
Downloaded 670040064 bytes in 1.630415568s: 3287.702 Mbps

Protocol

See the code in conn.go.

It surprised me to read that speedtest.net just repeats the same short string over and over. This is trivial for an ISP to compress and still claim plausible deniability.

Security

This is an unauthenticated and unencrypted service. It sets concurrency limits, but if you run this on a public server, you may create significant egress traffic. It is not an echo server, to avoid being used as a relay with a spoofed source address.

The code itself is simple Go and should be fine.

License

This project is licensed under the MIT License.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrNoDataReceived = errors.New("no data received")

ErrNoDataReceived is returned if Conn.Recv saw no data.

Functions

This section is empty.

Types

type Conn

type Conn struct {
	// contains filtered or unexported fields
}

A Conn can receive and send random data.

func Dial

func Dial(ctx context.Context, network, addr string) (*Conn, error)

Dial is a helper to dial and create a new connection.

func NewConn

func NewConn(conn NetConn) *Conn

NewConn wraps a new connection.

func (*Conn) Close

func (c *Conn) Close() error

Close closes the underlying connection.

func (*Conn) Recv

func (c *Conn) Recv(ctx context.Context) (int, time.Duration, time.Duration, error)

Recv receives data until EOF and measures how long it took. Returns the number of bytes received, the time it took, and the time until the first block was received. Note that the later depends on the receiver buffer side, and is just an approximation of latency.

func (*Conn) Send

func (c *Conn) Send(ctx context.Context, n int) (int, time.Duration, error)

Send sends n bytes of data, closes the send-side and measures how long it took. Returns the actual number of bytes written, and the duration.

type MeasureThroughputOpt

type MeasureThroughputOpt func(*measureThroughputOpts) error

A MeasureThroughputOpt can be provided to MeasureThroughput.

func WithDialer

func WithDialer(d NetDialer) MeasureThroughputOpt

func WithMaxBytes

func WithMaxBytes(n int) MeasureThroughputOpt

WithMaxBytes sets a cap on the total number of bytes to send. The hope is to never hit this hard limit, but that the tolerance criterion is fulfilled first. The default is 1 GiB.

func WithMaxDuration

func WithMaxDuration(d time.Duration) MeasureThroughputOpt

WithMaxDuration sets a cap on the duration of a single connection. This should be at most what the connection timeout is on the server. The default is 10s.

func WithMinIterations

func WithMinIterations(n int) MeasureThroughputOpt

WithMinIterations adjusts the number of bytes per iteration so that `WithMaxBytes` is not hit until, at least, this many iterations. The default is 8.

func WithTime

func WithTime(f func() time.Time) MeasureThroughputOpt

func WithTolerance

func WithTolerance(frac float64) MeasureThroughputOpt

WithTolerance sets the tolerated relative error of the last iteration. If the latest send/receive measurement is within this tolerance, measurements stop. A value in [0, 1]. The default is 5%.

type NetConn

type NetConn interface {
	io.Closer
	io.Reader
	io.Writer
	CloseWrite() error
	SetReadDeadline(time.Time) error
	SetWriteDeadline(time.Time) error
}

A NetConn is what we can wrap. `*net.TCPConn` implements this.

type NetDialer

type NetDialer interface {
	DialContext(ctx context.Context, network, address string) (net.Conn, error)
}

type Reporter

type Reporter interface {
	// ServedCharGen2P is called when a connection is closed. Either
	// there is throughput information, or an error.
	ServedCharGen2P(net.Conn, *ThroughputInfo, error)
}

A Reporter hooks into various connection state changes. Implementations must be safe for concurrent use.

type Server

type Server struct {
	// Reporter is an optional result handler. Normally, only the
	// client is interested in the outcome, so this can be nil.
	Reporter Reporter

	// MaxConns is the maximum number of simultaneous connections.
	// This will create a backlog if we're over quota, which pushes
	// back through the kernel to TCP clients. The default is 10.
	MaxConns int

	// ConnContext is a factory function for creating a per-connection
	// context. This is useful to set a timeout.
	ConnContext func(context.Context, net.Conn) context.Context
}

A Server listens for incoming connections and handles processing them.

func (*Server) Serve

func (s *Server) Serve(ctx context.Context, l net.Listener) error

Serve starts accepting connections and forks off connection handlers.

type ThroughputInfo

type ThroughputInfo struct {
	// DialDuration is how long connecting to the remote peer took.
	DialDuration time.Duration

	// NumWrittenBytes is the number of (payload) bytes sent. Protocol
	// headers are not included.
	NumWrittenBytes int

	// WriteDuration is how long it took to send the data, including
	// closing the send endpoint.
	WriteDuration time.Duration

	// NumReadBytes is the number of (payload) bytes
	// received. Protocol headers are not included.
	NumReadBytes int

	// ReadDuration is how long it took to receive the data, counting
	// from after closing the send endpoint.
	ReadDuration time.Duration

	// ReadLatency is how long after closing the send endpoint the
	// first received block was ready. This is a rough estimate of
	// latency, but depends on the internal receive buffer size.
	ReadLatency time.Duration

	// WorstAccuracy describes the worst acceptable relative error for
	// the write and read throughput of the last iteration.
	// `DialDuration` and `FirstReadLatency` are not considered as
	// they normally have much higher variances. A value in [0, 1].
	WorstAccuracy float64
}

A ThroughputInfo contains various measurements from a throughput test.

func MeasureThroughput

func MeasureThroughput(ctx context.Context, network, addr string, opts ...MeasureThroughputOpt) (*ThroughputInfo, error)

MeasureThroughput dials, sends and receives a Conn. Reports summary statistics.

Directories

Path Synopsis
cmd
chargen2pd
Command chargen2pd implements a 2-phase character generator service.
Command chargen2pd implements a 2-phase character generator service.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL