Categorygithub.com/ooni/netem
modulepackage
0.0.0-20240208095707-608dcbcd82b8
Repository: https://github.com/ooni/netem.git
Documentation: pkg.go.dev

# README

Netem

alltests GoDoc Coverage Status Slack

Netem allows writing integration tests in Go where networking code uses Gvisor-based networking. Netem also includes primitives to emulate link latency, losses, and internet censorship (null routing, SNI-based blocking, throttling). Using netem, one can easily simulate complex integration testing scenarios involving difficult or adversarial networks.

Install instructions

We currently support go1.20.

To add netem as a dependency, run:

go get -u -v -d github.com/ooni/netem

This command will download netem and update your go.mod and go.sum.

You probably also want to manually force using the Gvisor version we're using in this library with:

go get -u -v -d gvisor.dev/gvisor@COMMIT_HASH

because Gvisor's default branch is not ready to be used with Go tools and go get would misbehave.

When updating Gvisor in this library, make sure you pin to a commit from the go branch, which is the Gvisor branch supporting go tools.

Running tests

go test .

To enable the race detector, run:

go test -race .

Note: we notice that the race detector would be very slow under macOS and many tests will fail; it still seems to be fine under Linux.

Usage

TODO(bassosimone): this section needs to be updated because we have recently removed the stdlib.go file and functionality, since we have much better functionality inside of ooni/probe-cli.

Existing Go code needs to be adjusted to support netem.

Suppose you have this Go code:

func yourCode(ctx context.Context) error {
	addrs, err := net.DefaultResolver.LookupHost(ctx, "www.example.com")
	// ...
}

You need to convert this code to use netem:

func yourCode(ctx context.Context, nn *netem.Net) error {
	addrs, err := nn.LookupHost(ctx, "www.example.com")
	// ...
}

Normally, you would create a netem.Net like this:

nn := &netem.Net{
	Stack: &netem.Stdlib{},
}

Your code will still work as intended. But, now you have the option to replace the Net underlying stack with an userspace TCP/IP network stack, for writing integration tests.

Let us do that. We start by creating a StarTopology:

topology, err := netem.NewStarTopology(&netem.NullLogger{})
if err != nil { /* ... */ }

defer topology.Close()

Then, we use AddHost to add two userspace network stacks to such a topology:

clientStack, err := netem.AddHost(
	"1.2.3.4",            // stack IPv4 address
	"5.4.3.2",            // resolver IPv4 address
	&netem.LinkConfig{},  // link with no delay, losses, or DPI
)
if err != nil { /* ... */ }

serverStack, err := netem.AddHost(
	"5.4.3.2",            // stack IPv4 address
	"5.4.3.2",            // resolver IPv4 address
	&netem.LinkConfig{},  // link with no delay, losses, or DPI
)
if err != nil { /* ... */ }

We now have the following topology:

graph TD
 client[clientStack<br>1.2.3.4]---router{Router}
 server[serverStack<br>5.4.3.2]---router

Now, we can create a DNSServer on 5.4.3.2 as follows:

dnsCfg := netem.NewDNSConfig()
dnsCfg.AddRecord(
	"www.example.com",
	"",                 // empty CNAME
	"5.6.7.8",
)

dnsServer, err := netem.NewDNSServer(
	&netem.NullLogger{},
	serverStack,
	"5.4.3.2",
	dnsCfg,
)
if err != nil { /* ... */ }

Finally, we create a netem.Net as follows:

nn2 := &netem.Net{
	Stack: clientStack,
}

and we can test yourCode as follows:

func TestYourCode(t *testing.T) {
	// ... create nn2 ...
	err := yourCode(context.Background(), nn2)
	if err != nil {
		t.Fatal(err)
	}
}

This test will test your code using the above network stacks and topology.

# Packages

No description provided by the author

# Functions

DissectPacket parses a packet TCP/IP layers.
DNSParseResponse parses a [dns.Msg] into a getaddrinfo response.
DNSRoundTrip performs a DNS round trip using a given [UnderlyingNetwork].
DNSServerRoundTrip responds to a raw DNS query with a raw DNS response.
DPIFormatHTTPResponse formats an HTTP response for a blockpage.
ExtractTLSServerName takes in input bytes read from the network, attempts to determine whether this is a TLS Handshale message, and if it is a ClientHello, and, if affirmative, attempts to extract the server name.
FindTLSServerNameExtension returns the first ServerName extension in case of success or false in case of failure.
LinkFwdFast is the fast implementation of frames forwarding.
LinkFwdFull is a full implementation of link forwarding that deals with delays, packet losses, and DPI.
LinkFwdWithDelay is an implementation of link forwarding that only delays packets without losses and deep packet inspection.
Must0 panics in case of error.
Must1 panics in case of error otherwise returns the first value.
Must2 panics in case of error otherwise returns the two values.
NewCA creates a new certification authority.
MustNewCA is like [NewCA] but uses a custom [time.Now] func.
MustNewPPPTopology creates a [PPPTopology].
MustNewStarTopology constructs a new, empty [StarTopology] consisting of a [Router] sitting in the middle.
NewDNSConfig constructs a [DNSConfig] instance.
NewDNSRequestA creates a new A request.
NewDNSServer creates a new [DNSServer] instance.
NewDPIEngine creates a new [DPIEngine] instance.
NewFrame constructs a [Frame] for the given [Payload].
NewHTTPTransport creates a new [http.Transport] using an [UnderlyingNetwork].
NewLink creates a new [Link] instance and spawns goroutines for forwarding traffic between the left and the right [LinkNIC].
NewPCAPDumper creates a new [PCAPDumper].
NewRouter creates a new [Router] instance.
NewRouterPort creates a new [RouterPort] for a given [Router].
NewStaticReadableNIC constructs a new [StaticReadableNIC] instance.
NewStaticWriteableNIC constructs a new [StaticWriteableNIC] instance.
NewUNetStack constructs a new [UNetStack] instance.
RunNDT0Client runs the NDT0 client nettest using the given server endpoint address and [UnderlyingNetwork].
RunNDT0Server runs the NDT0 server.
UnmarshalTLSExtensions unmarshals the extensions.
UnmarshalTLSHandshakeMsg unmarshals an Handshake message.
UnmarshalTLSRecordHeader unmarshals a RecordHeader.
UnmarshalTLSServerNameExtension unmarshals the server name from the bytes that consist of the extension value.

# Constants

DPIDirectionClientToServer is the direction from the client to the server.
DPIDirectionServerToClient is the direction from the server to the client.
FrameFlagDrop tells the link that the frame should be dropped rather than forwarded, to emulate a loss occurring on the link while the frame was in flight.
FrameFlagSpoof tells the router it should send the spoofed frames inside the [Frame] Spoofed field.
NDT0CSVHeader is the header for the CSV records returned by the [NDT0PerformanceSample.CSVRecord] function.

# Variables

ErrDissectNetwork indicates that we do not support the packet's network protocol.
ErrDissectShortPacket indicates the packet is too short.
ErrDissectTransport indicates that we do not support the packet's transport protocol.
ErrDNSNoAnswer is returned when the server response does not contain any answer for the original query (i.e., no IPv4 addresses).
ErrDNSNoSuchHost is returned in case of NXDOMAIN.
ErrDNSServerMisbehaving is the error we return for cases different from NXDOMAIN.
ErrDuplicateAddr indicates that an address has already been added to a topology.
ErrNoPacket indicates there's no packet ready.
ErrNotIPAddress indicates that a string is not a serialized IP address.
ErrPacketDropped indicates that a packet was dropped.
ErrStackClosed indicates the network stack has been closed.
ErrTLSParse is the error returned in case there is a TLS parse error.

# Structs

CA is a certification authority.
DissectedPacket is a dissected IP packet.
DNSConfig is the DNS configuration to use.
DNSRecord is a DNS record in the [DNSConfig].
DNSServer is a DNS server.
DPICloseConnectionForServerEndpoint is a [DPIRule] that spoofs a FIN|ACK TCP segment after it sees a given TCP connect attempt.
DPICloseConnectionForString is a [DPIRule] that spoofs a FIN|ACK TCP segment after it sees a given string in the payload.
DPICloseConnectionForTLSSNI is a [DPIRule] that spoofs a FIN|ACK TCP segment after it sees a given TLS SNI.
DPIDropTrafficForServerEndpoint is a [DPIRule] that drops all the traffic towards a given server endpoint.
DPIDropTrafficForString is a [DPIRule] that drops all the traffic after it sees a given string.
DPIDropTrafficForTLSSNI is a [DPIRule] that drops all the traffic after it sees a given TLS SNI.
DPIEngine is a deep packet inspection engine.
DPIPolicy tells the [DPIEngine] which policy to apply to a packet.
DPIResetTrafficForString is a [DPIRule] that spoofs a RST TCP segment after it sees a given string in the payload for a given offending server endpoint.
DPIResetTrafficForTLSSNI is a [DPIRule] that spoofs a RST TCP segment after it sees a given TLS SNI.
DPISpoofBlockpageForString is a [DPIRule] that spoofs a blockpage after it sees a given string in the payload.
DPISpoofDNSResponse is a [DPIRule] that spoofs a DNS response after it sees a given DNS request.
DPIThrottleTrafficForTCPEndpoint is a [DPIRule] that throttles traffic for a given TCP endpoint.
DPIThrottleTrafficForTLSSNI is a [DPIRule] that throttles traffic after it sees a given TLS SNI.
ErrDial contains all the errors occurred during a [DialContext] operation.
Frame contains an IPv4 or IPv6 packet.
Link models a link between a "left" and a "right" NIC.
LinkConfig contains config for creating a [Link].
LinkFwdConfig contains config for frame forwarding algorithms.
MocakbleNIC is a mockable [NIC] implementation.
NDT0PerformanceSample is a performance sample returned by [RunNDT0Client].
Net is a drop-in replacement for the [net] package.
NullLogger is a [netem.Logger] that does not emit logs.
PCAPDumper collects a PCAP trace.
PPPTopology is a point-to-point topology with two network stacks and a [Link] in the middle.
Router routes traffic between [RouterPort]s.
RouterPort is a port of a [Router].
StarTopology is the star network topology: there is a router in the middle and all hosts connect to it.
StaticReadableNIC is a [ReadableNIC] that will return a fixed amount of frames.
StaticWriteableNIC is a [WritableNIC] that collects all the frames it received for you to inspect later.
TLSClientHello is the TLSClientHello message.
TLSExtension is a TLS extension.
TLSHandshakeMsg is the TLSHandshakeMsg message.
TLSRecordHeader is a TLS TLSRecordHeader.
UNetStack is a network stack in user space.

# Interfaces

CertificationAuthority is a TLS certification authority.
DPIRule is a deep packet inspection rule.
FrameReader allows one to read incoming frames.
HTTPUnderlyingNetwork is the [UnderlyingNetwork] used by HTTP code.
LinkFwdRNG is a [LinkFwdFunc] view of a [rand.Rand] abstracted for festability.
LinkNICWrapper allows wrapping [NIC]s used by a [Link] to log packets, collect PCAPs and implement DPI.
Logger is the logger we're using.
NIC is a network interface card with which you can send and receive [Frame]s.
ReadableNIC is the read-only [NIC] used by frame forwarding algorithms.
UDPLikeConn is a net.PacketConn with some extra functions required to convince the QUIC library (lucas-clemente/quic-go) to inflate the receive buffer of the connection.
UnderlyingNetwork replaces for functions in the [net] package.
WriteableNIC is the write-only [NIC] used by frame forwarding algorithms.

# Type aliases

DPIDirection is the direction of packets within a flow according to the [DPIEngine].
LinkFwdFunc is type type of a link forwarding function.