Documentation
¶
Overview ¶
Package netsim provides a simple network simulation framework that developers can use to write integration tests.
Usage and Features ¶
The NewStack function creates a new, simulated network stack using a given IP address. You can invoke usual functions on the stack, such as:
- DialContext - Listen - ListenPacket
These functions return simulated net.Conn, net.Listener, and net.PacketConn respectively.
When a connection sends data, the data is wrapped inside a [*Packet] emitted on the channel returned by [*Stack.Output]. The *Link type allows connecting two *Stack such that they can send [*Packet] to each other. To send a [*Packet] to a *Stack, you need to post the packet on the channel returned by [*Stack.Input]. You don't need to use a *Link as long as you correctly forward packets. In fact, for simulating complex censorship scenarios, you probably want to write custom code to forward or drop [*Packet]. In the future, there will be subpackages of netsim providing this functionality.
Subpackages of this package contain extensions. For example, the netsim/simpki package code helps to simulate a PKI.
The implementation of net.Conn, net.Listener, and net.PacketConn are [*TCPConn], [*UDPConn], and [*UDPListener]. These types, which can also be created manually, are tiny wrappers around [*Port], which contains most of the common implementation code. These types are public to enable writing more complex tests (e.g., the sending of unexpected TCP flags).
The errors returned by these types are the same syscall.Errno the standard library and the kernel would generate in similar cases (we use the x/sys repository to pull system-dependent error values).
This package contains comprehensive examples showing how to use it.
Design Documents ¶
This package is experimental and has no design documents for now.
Example (BlockpageTransparentProxy) ¶
This example shows how to use netsim to simulate transparent proxying of HTTP requests to serve blockpages.
package main
import (
"fmt"
"io"
"log"
"net/http"
"net/netip"
"github.com/rbmk-project/rbmk/pkg/x/netsim"
"github.com/rbmk-project/rbmk/pkg/x/netsim/censor"
)
func main() {
// Create scenario
scenario := netsim.NewScenario("testdata")
defer scenario.Close()
// Create blockpage server
blockpage := scenario.MustNewBlockpageStack()
scenario.Attach(blockpage)
// Create target website
scenario.Attach(scenario.MustNewExampleComStack())
// Configure DNAT to send blocked traffic to blockpage server
scenario.Router().AddFilter(censor.NewDNatter(
netip.MustParseAddr("193.206.158.22"), // source addr
netip.MustParseAddrPort("93.184.216.34:80"), // target dest epnt
netip.MustParseAddrPort("10.10.34.35:80"), // repl dest epnt
))
// Create client stack
clientStack := scenario.MustNewClientStack()
scenario.Attach(clientStack)
// Create the HTTP client
clientTxp := scenario.NewHTTPTransport(clientStack)
defer clientTxp.CloseIdleConnections()
clientHTTP := &http.Client{Transport: clientTxp}
// Get the response body.
resp, err := clientHTTP.Get("http://93.184.216.34/")
if err != nil {
log.Fatal(err)
}
if resp.StatusCode != http.StatusForbidden {
log.Fatalf("HTTP request failed: %d", resp.StatusCode)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
// Print the response body
fmt.Printf("%s", string(body))
}
Output: Access to this website has been blocked by network policy.
Example (CensorDNS) ¶
This example shows how to use netsim to simulate GFW-style DNS censorship, where poisoned responses are injected before the legitimate response arrives. The example demonstrates:
1. how to configure DNS poisoning using a database 2. how to collect multiple DNS responses using dnscore 3. the expected order of responses (poisoned then legitimate)
This example DOES NOT show how to validate responses using [dnscore] since that is outside its specific scope.
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/miekg/dns"
"github.com/rbmk-project/rbmk/pkg/dns/dnscore"
"github.com/rbmk-project/rbmk/pkg/x/netsim"
"github.com/rbmk-project/rbmk/pkg/x/netsim/censor"
netsimdns "github.com/rbmk-project/rbmk/pkg/x/netsim/dns"
)
func main() {
// Create a new scenario using the given directory to cache
// the certificates used by the simulated PKI
scenario := netsim.NewScenario("testdata")
defer scenario.Close()
// Create server stack emulating dns.google (8.8.8.8).
//
// This includes:
//
// 1. creating, attaching, and enabling routing for a server stack
//
// 2. registering the proper domain names and addresses
//
// 3. updating the PKI database to include the server's certificate
scenario.Attach(scenario.MustNewGoogleDNSStack())
// Configure DNS poisoning happening on the scenario router
// thus modeling the typical behaviour of the GFW.
censorDB := netsimdns.NewDatabase()
censorDB.AddAddresses([]string{"dns.google"}, []string{"10.0.0.1"})
scenario.Router().AddFilter(censor.NewDNSPoisoner(censorDB))
// Create and attach the client stack.
clientStack := scenario.MustNewClientStack()
scenario.Attach(clientStack)
// Create a context with a watchdog timeout.
//
// In real measurements this would typically be controlled by
// --wait-duplicates or natural timing of other operations like
// TCP/TLS handshakes and fetching related web pages.
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
// Create DNS query for dns.google A record
query, err := dnscore.NewQuery("dns.google.", dns.TypeA)
if err != nil {
log.Fatal(err)
}
// Configure transport to use our simulated network
txp := &dnscore.Transport{}
txp.DialContext = clientStack.DialContext
// Query 8.8.8.8 over UDP and collect responses
serverAddr := &dnscore.ServerAddr{
Protocol: dnscore.ProtocolUDP,
Address: "8.8.8.8:53",
}
results := txp.QueryWithDuplicates(ctx, serverAddr, query)
// Print responses as they arrive.
//
// We expect:
//
// 1. poisoned response (10.0.0.1) from router
//
// 2. legitimate response (8.8.8.8) from server
//
// After two responses, we cancel the context. In production,
// we will stop after a timeout or perform other operations and
// then check whether there are more addresses to measure.
var count int
for result := range results {
if err := result.Err; err != nil {
// Errors here typically are caused by us closing
// the connection and, anyway, for this test we only
// care about seeing the duplicate responses.
break
}
for _, ans := range result.Msg.Answer {
if a, ok := ans.(*dns.A); ok {
fmt.Printf("%s\n", a.A.String())
}
}
count++
if count >= 2 {
cancel()
}
}
}
Output: 10.0.0.1 8.8.8.8
Example (DnsOverHTTPS) ¶
This example shows how to use netsim to simulate a DNS server that listens for incoming requests over HTTPS.
package main
import (
"context"
"crypto/tls"
"fmt"
"log"
"net/http"
"time"
"github.com/miekg/dns"
"github.com/rbmk-project/rbmk/pkg/dns/dnscore"
"github.com/rbmk-project/rbmk/pkg/x/netsim"
)
func main() {
// Create a new scenario using the given directory to cache
// the certificates used by the simulated PKI
scenario := netsim.NewScenario("testdata")
defer scenario.Close()
// Create server stack emulating dns.google.
//
// This includes:
//
// 1. creating, attaching, and enabling routing for a server stack
//
// 2. registering the proper domain names and addresses
//
// 3. updating the PKI database to include the server's certificate
scenario.Attach(scenario.MustNewGoogleDNSStack())
// Create and attach the client stack.
clientStack := scenario.MustNewClientStack()
scenario.Attach(clientStack)
// Create a context with a watchdog timeout.
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
// Create the dnscore transport and the server address
txp := &dnscore.Transport{
HTTPClient: &http.Client{
Transport: &http.Transport{
DialContext: clientStack.DialContext,
TLSClientConfig: &tls.Config{
RootCAs: scenario.RootCAs(),
ServerName: "dns.google",
},
},
},
}
serverAddr := dnscore.NewServerAddr(
dnscore.ProtocolDoH, "https://8.8.8.8/dns-query")
// Create the query to send
query, err := dnscore.NewQuery("dns.google", dns.TypeA)
if err != nil {
log.Fatal(err)
}
// Perform the DNS round trip
resp, err := txp.Query(ctx, serverAddr, query)
if err != nil {
log.Fatal(err)
}
// Print the responses
for _, ans := range resp.Answer {
if a, ok := ans.(*dns.A); ok {
fmt.Printf("%s\n", a.A.String())
}
}
}
Output: 8.8.8.8
Example (DnsOverTCP) ¶
This example shows how to use netsim to simulate a DNS server that listens for incoming requests over TCP.
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/miekg/dns"
"github.com/rbmk-project/rbmk/pkg/x/netsim"
)
func main() {
// Create a new scenario using the given directory to cache
// the certificates used by the simulated PKI
scenario := netsim.NewScenario("testdata")
defer scenario.Close()
// Create server stack emulating dns.google.
//
// This includes:
//
// 1. creating, attaching, and enabling routing for a server stack
//
// 2. registering the proper domain names and addresses
//
// 3. updating the PKI database to include the server's certificate
scenario.Attach(scenario.MustNewGoogleDNSStack())
// Create and attach the client stack.
clientStack := scenario.MustNewClientStack()
scenario.Attach(clientStack)
// Create a context with a watchdog timeout.
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
// Create the client connection with the DNS server.
conn, err := clientStack.DialContext(ctx, "tcp", "8.8.8.8:53")
if err != nil {
log.Fatal(err)
}
defer conn.Close()
// Create the query to send
query := new(dns.Msg)
query.Id = dns.Id()
query.RecursionDesired = true
query.Question = []dns.Question{{
Name: "dns.google.",
Qtype: dns.TypeA,
Qclass: dns.ClassINET,
}}
// Perform the DNS round trip
clientDNS := &dns.Client{Net: "tcp"}
resp, _, err := clientDNS.ExchangeWithConnContext(ctx, query, &dns.Conn{Conn: conn})
if err != nil {
log.Fatal(err)
}
// Print the responses
for _, ans := range resp.Answer {
if a, ok := ans.(*dns.A); ok {
fmt.Printf("%s\n", a.A.String())
}
}
}
Output: 8.8.8.8
Example (DnsOverTLS) ¶
This example shows how to use netsim to simulate a DNS server that listens for incoming requests over TLS.
package main
import (
"context"
"crypto/tls"
"fmt"
"log"
"time"
"github.com/miekg/dns"
"github.com/rbmk-project/rbmk/pkg/x/netsim"
)
func main() {
// Create a new scenario using the given directory to cache
// the certificates used by the simulated PKI
scenario := netsim.NewScenario("testdata")
defer scenario.Close()
// Create server stack emulating dns.google.
//
// This includes:
//
// 1. creating, attaching, and enabling routing for a server stack
//
// 2. registering the proper domain names and addresses
//
// 3. updating the PKI database to include the server's certificate
scenario.Attach(scenario.MustNewGoogleDNSStack())
// Create and attach the client stack.
clientStack := scenario.MustNewClientStack()
scenario.Attach(clientStack)
// Create a context with a watchdog timeout.
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
// Create the client connection with the DNS server.
conn, err := clientStack.DialContext(ctx, "tcp", "8.8.8.8:853")
if err != nil {
log.Fatal(err)
}
defer conn.Close()
tconn := tls.Client(conn, &tls.Config{
RootCAs: scenario.RootCAs(),
NextProtos: []string{"dot"},
ServerName: "dns.google",
})
defer tconn.Close()
if err := tconn.HandshakeContext(ctx); err != nil {
log.Fatal(err)
}
// Create the query to send
query := new(dns.Msg)
query.Id = dns.Id()
query.RecursionDesired = true
query.Question = []dns.Question{{
Name: "dns.google.",
Qtype: dns.TypeA,
Qclass: dns.ClassINET,
}}
// Perform the DNS round trip
clientDNS := &dns.Client{Net: "tcp-tls"}
resp, _, err := clientDNS.ExchangeWithConnContext(ctx, query, &dns.Conn{Conn: tconn})
if err != nil {
log.Fatal(err)
}
// Print the responses
for _, ans := range resp.Answer {
if a, ok := ans.(*dns.A); ok {
fmt.Printf("%s\n", a.A.String())
}
}
}
Output: 8.8.8.8
Example (DnsOverUDP) ¶
This example shows how to use netsim to simulate a DNS server that listens for incoming requests over UDP.
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/miekg/dns"
"github.com/rbmk-project/rbmk/pkg/x/netsim"
)
func main() {
// Create a new scenario using the given directory to cache
// the certificates used by the simulated PKI
scenario := netsim.NewScenario("testdata")
defer scenario.Close()
// Create server stack emulating dns.google.
//
// This includes:
//
// 1. creating, attaching, and enabling routing for a server stack
//
// 2. registering the proper domain names and addresses
//
// 3. updating the PKI database to include the server's certificate
scenario.Attach(scenario.MustNewGoogleDNSStack())
// Create and attach the client stack.
clientStack := scenario.MustNewClientStack()
scenario.Attach(clientStack)
// Create a context with a watchdog timeout.
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
// Create the client connection with the DNS server.
conn, err := clientStack.DialContext(ctx, "udp", "8.8.8.8:53")
if err != nil {
log.Fatal(err)
}
defer conn.Close()
// Create the query to send
query := new(dns.Msg)
query.Id = dns.Id()
query.RecursionDesired = true
query.Question = []dns.Question{{
Name: "dns.google.",
Qtype: dns.TypeA,
Qclass: dns.ClassINET,
}}
// Perform the DNS round trip
clientDNS := &dns.Client{}
resp, _, err := clientDNS.ExchangeWithConnContext(ctx, query, &dns.Conn{Conn: conn})
if err != nil {
log.Fatal(err)
}
// Print the responses
for _, ans := range resp.Answer {
if a, ok := ans.(*dns.A); ok {
fmt.Printf("%s\n", a.A.String())
}
}
}
Output: 8.8.8.8
Example (Http) ¶
This example shows how to use netsim to simulate an HTTP server that listens for incoming cleartext requests.
package main
import (
"fmt"
"io"
"log"
"net/http"
"github.com/rbmk-project/rbmk/pkg/x/netsim"
)
func main() {
// Create a new scenario using the given directory to cache
// the certificates used by the simulated PKI
scenario := netsim.NewScenario("testdata")
defer scenario.Close()
// Create server stack emulating www.example.com.
//
// This includes:
//
// 1. creating, attaching, and enabling routing for a server stack
//
// 2. registering the proper domain names and addresses
//
// 3. updating the PKI database to include the server's certificate
scenario.Attach(scenario.MustNewExampleComStack())
// Create and attach the client stack.
clientStack := scenario.MustNewClientStack()
scenario.Attach(clientStack)
// Create the HTTP client
clientTxp := scenario.NewHTTPTransport(clientStack)
defer clientTxp.CloseIdleConnections()
clientHTTP := &http.Client{Transport: clientTxp}
// Get the response body.
resp, err := clientHTTP.Get("http://93.184.216.34/")
if err != nil {
log.Fatal(err)
}
if resp.StatusCode != http.StatusOK {
log.Fatalf("HTTP request failed: %d", resp.StatusCode)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
// Print the response body
fmt.Printf("%s", string(body))
}
Output: Example Web Server.
Example (Https) ¶
This example shows how to use netsim to simulate an HTTPS server that listens for incoming encrypted requests.
package main
import (
"fmt"
"io"
"log"
"net/http"
"github.com/rbmk-project/rbmk/pkg/x/netsim"
)
func main() {
// Create a new scenario using the given directory to cache
// the certificates used by the simulated PKI
scenario := netsim.NewScenario("testdata")
defer scenario.Close()
// Create server stack emulating dns.google.
//
// This includes:
//
// 1. creating, attaching, and enabling routing for a server stack
//
// 2. registering the proper domain names and addresses
//
// 3. updating the PKI database to include the server's certificate
scenario.Attach(scenario.MustNewGoogleDNSStack())
// Create and attach the client stack.
clientStack := scenario.MustNewClientStack()
scenario.Attach(clientStack)
// Create the HTTP client
clientTxp := scenario.NewHTTPTransport(clientStack)
defer clientTxp.CloseIdleConnections()
clientHTTP := &http.Client{Transport: clientTxp}
// Get the response body.
resp, err := clientHTTP.Get("https://8.8.8.8/")
if err != nil {
log.Fatal(err)
}
if resp.StatusCode != http.StatusOK {
log.Fatalf("HTTP request failed: %d", resp.StatusCode)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
// Print the response body
fmt.Printf("%s", string(body))
}
Output: Google Public DNS server.
Example (Router) ¶
This example shows how to use a router to simulate a network topology consisting of a client and multiple servers.
package main
import (
"fmt"
"io"
"log"
"net/http"
"time"
"github.com/rbmk-project/rbmk/pkg/x/netsim"
"github.com/rbmk-project/rbmk/pkg/x/netsim/geolink"
)
func main() {
// Create a new scenario using the given directory to cache
// the certificates used by the simulated PKI
scenario := netsim.NewScenario("testdata")
defer scenario.Close()
// Create server stack emulating dns.google.
//
// This includes:
//
// 1. creating, attaching, and enabling routing for a server stack
//
// 2. registering the proper domain names and addresses
//
// 3. updating the PKI database to include the server's certificate
scenario.Attach(scenario.MustNewGoogleDNSStack())
// Create server stack emulating www.example.com.
scenario.Attach(scenario.MustNewExampleComStack())
// Create the client stack, build a geographic point-to-point link
// and attach the scenario router to the other end of the link.
clientStack := scenario.MustNewClientStack()
linkDev := geolink.Extend(clientStack, &geolink.Config{
Delay: 10 * time.Millisecond,
Log: true,
})
scenario.Attach(linkDev)
// Create the HTTP client
clientTxp := scenario.NewHTTPTransport(clientStack)
defer clientTxp.CloseIdleConnections()
clientHTTP := &http.Client{Transport: clientTxp}
// Get the response body.
resp, err := clientHTTP.Get("https://www.example.com/")
if err != nil {
log.Fatal(err)
}
if resp.StatusCode != http.StatusOK {
log.Fatalf("HTTP request failed: %d", resp.StatusCode)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
// Print the response body
fmt.Printf("%s", string(body))
}
Output: Example Web Server.
Example (Tls) ¶
This example shows how to use netsim to simulate a TLS server that listens for incoming encrypted requests.
package main
import (
"context"
"crypto/tls"
"fmt"
"log"
"time"
"github.com/rbmk-project/rbmk/pkg/x/netsim"
)
func main() {
// Create a new scenario using the given directory to cache
// the certificates used by the simulated PKI
scenario := netsim.NewScenario("testdata")
defer scenario.Close()
// Create server stack emulating dns.google.
//
// This includes:
//
// 1. creating, attaching, and enabling routing for a server stack
//
// 2. registering the proper domain names and addresses
//
// 3. updating the PKI database to include the server's certificate
scenario.Attach(scenario.MustNewGoogleDNSStack())
// Create and attach the client stack.
clientStack := scenario.MustNewClientStack()
scenario.Attach(clientStack)
// Create a context with a watchdog timeout.
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
// Connect to the server
conn, err := clientStack.DialContext(ctx, "tcp", "8.8.8.8:443")
if err != nil {
log.Fatal(err)
}
defer conn.Close()
// Perform the TLS handshake
tconn := tls.Client(conn, &tls.Config{
RootCAs: scenario.RootCAs(),
ServerName: "dns.google",
})
defer tconn.Close()
if err := tconn.HandshakeContext(ctx); err != nil {
log.Fatal(err)
}
// Print the handshake result
fmt.Printf("%v", err)
}
Output: <nil>
Example (TlsBlackholing) ¶
This example shows how to use netsim to simulate SNI-based TLS blocking using connection blackholing.
package main
import (
"fmt"
"net/http"
"net/netip"
"time"
"github.com/rbmk-project/rbmk/pkg/x/netsim"
"github.com/rbmk-project/rbmk/pkg/x/netsim/censor"
)
func main() {
// Create a new scenario using the given directory to cache
// the certificates used by the simulated PKI
scenario := netsim.NewScenario("testdata")
defer scenario.Close()
// Create server stack emulating dns.google.
scenario.Attach(scenario.MustNewGoogleDNSStack())
// Configure blackholing on the scenario router targeting
// connections where the SNI matches "dns.google"
scenario.Router().AddFilter(censor.NewBlackholer(
300*time.Second, // residual censorship duration
netip.AddrPort{}, // match any endpoint
[]byte("dns.google"), // match SNI
))
// Create and attach the client stack.
clientStack := scenario.MustNewClientStack()
scenario.Attach(clientStack)
// Create the HTTP client with a short timeout
clientTxp := scenario.NewHTTPTransport(clientStack)
defer clientTxp.CloseIdleConnections()
clientHTTP := &http.Client{
Transport: clientTxp,
Timeout: 200 * time.Millisecond, // short timeout for testing
}
// Attempt the HTTPS request, which should time out
_, err := clientHTTP.Get("https://dns.google/")
fmt.Printf("err: %v\n", err)
}
Output: err: Get "https://dns.google/": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
Example (TlsRSTInjection) ¶
This example shows how to use netsim to simulate SNI-based TLS blocking using RST injection.
package main
import (
"fmt"
"net/http"
"net/netip"
"github.com/rbmk-project/rbmk/pkg/x/netsim"
"github.com/rbmk-project/rbmk/pkg/x/netsim/censor"
)
func main() {
// Create a new scenario using the given directory to cache
// the certificates used by the simulated PKI
scenario := netsim.NewScenario("testdata")
defer scenario.Close()
// Create server stack emulating dns.google.
//
// This includes:
//
// 1. creating, attaching, and enabling routing for a server stack
//
// 2. registering the proper domain names and addresses
//
// 3. updating the PKI database to include the server's certificate
scenario.Attach(scenario.MustNewGoogleDNSStack())
// Configure RST injection on the scenario router targeting
// connections where the SNI matches "dns.google"
scenario.Router().AddFilter(censor.NewTCPResetter(
netip.AddrPort{}, // match any endpoint
[]byte("dns.google"), // match SNI
))
// Create and attach the client stack.
clientStack := scenario.MustNewClientStack()
scenario.Attach(clientStack)
// Create the HTTP client
clientTxp := scenario.NewHTTPTransport(clientStack)
defer clientTxp.CloseIdleConnections()
clientHTTP := &http.Client{Transport: clientTxp}
// Attempt the HTTPS request, which should fail due to RST
_, err := clientHTTP.Get("https://dns.google/")
fmt.Printf("err: %v\n", err)
}
Output: err: Get "https://dns.google/": connection reset by peer
Index ¶
- Variables
- func NewDNSHTTPHandler(dd dns.Database) http.Handler
- type DNSHandler
- type Link
- type Scenario
- func (s *Scenario) Attach(dev packet.NetworkDevice)
- func (s *Scenario) Close() error
- func (s *Scenario) DNSHandler() DNSHandler
- func (s *Scenario) MustNewBlockpageStack() *Stack
- func (s *Scenario) MustNewClientStack() *Stack
- func (s *Scenario) MustNewExampleComStack() *Stack
- func (s *Scenario) MustNewGoogleDNSStack() *Stack
- func (s *Scenario) MustNewStack(config *StackConfig) *Stack
- func (s *Scenario) NewHTTPTransport(stack *Stack) *http.Transport
- func (s *Scenario) RootCAs() *x509.CertPool
- func (s *Scenario) Router() *router.Router
- type Stack
- type StackConfig
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var NewLink = link.New
NewLink is an alias for link.New.
var NewStack = netstack.New
NewStack is an alias for netstack.New.
Functions ¶
func NewDNSHTTPHandler ¶
NewDNSHTTPHandler returns an http.Handler handling DNS-over-HTTPS.
Types ¶
type Scenario ¶
type Scenario struct {
// contains filtered or unexported fields
}
Scenario manages network simulation components using a star topology, where all stacks are connected through a central router.
This means:
1. Each stack is connected only to the central router;
2. The router forwards packets between stacks.
func NewScenario ¶
NewScenario creates a new network simulation scenario.
The cacheDir caches simulated-PKI-related data.
func (*Scenario) Attach ¶
func (s *Scenario) Attach(dev packet.NetworkDevice)
Attach connects a device to the scenario's central router to read packets from the device, and sets up the route to that return packets correctly reach the device.
The common case is to attach a *Stack but other cases are also possible. Suppose a *Stack is linked to a firewall through a link, then you can also attach the firewall to the router.
All network traffic to/from this device will flow through the router.
func (*Scenario) DNSHandler ¶
func (s *Scenario) DNSHandler() DNSHandler
DNSHandler returns the DNSHandler for the scenario. The returned handler will serve queries based on the scenario's DNS database.
func (*Scenario) MustNewBlockpageStack ¶
MustNewBlockpageStack creates a new stack simulating a censorship blockpage server.
It serves a simple warning page on HTTP/HTTPS indicating that the content has been blocked.
func (*Scenario) MustNewClientStack ¶
MustNewClientStack creates a new client stack with standard testing configuration.
We use GARR's (Italian Research & Education Network) public addresses (193.206.158.22 and 2001:760:0:158::22) as default client addresses. These are chosen over documentation ranges (like 192.0.2.0/24) to avoid triggering bogon filters in network simulation scenarios, while still being associated with a public research institution.
The stack uses Google's public DNS addresses as the default resolvers.
func (*Scenario) MustNewExampleComStack ¶
MustNewExampleComStack creates a new stack simulating www.example.com.
func (*Scenario) MustNewGoogleDNSStack ¶
MustNewGoogleDNSStack creates a new stack simulating dns.google.
func (*Scenario) MustNewStack ¶
func (s *Scenario) MustNewStack(config *StackConfig) *Stack
MustNewStack creates a new network stack using the given configuration.
This method panics on error.
This method IS NOT goroutine safe.
func (*Scenario) NewHTTPTransport ¶
NewHTTPTransport creates an *http.Transport configured to use the given stack and the scenario's root CAs.
func (*Scenario) RootCAs ¶
RootCAs returns the *x509.CertPool that clients should use.
type StackConfig ¶
type StackConfig struct {
// Addresses contains the IP addresses for this stack.
//
// The config is invalid if there is not at least one address.
Addresses []string
// ClientResolvers optionally specifies resolvers for client stacks.
ClientResolvers []string
// DNSOverUDPHandler optionally specifies a handler for DNS-over-UDP.
DNSOverUDPHandler DNSHandler
// DNSOverTCPHandler optionally specifies a handler for DNS-over-TCP.
DNSOverTCPHandler DNSHandler
// DNSOverTLSHandler optionally specifies a handler for DNS-over-TLS.
DNSOverTLSHandler DNSHandler
// DomainNames contains the optional domain names associated with this stack.
//
// If there are associated domain names, we will configure the DNS and
// register related certificates for emulating the PKI.
DomainNames []string
// HTTPHandler optionally specifies a handle to use on port 80/tcp.
HTTPHandler http.Handler
// HTTPSHandler optionally specifies a handle to use on port 443/tcp.
HTTPSHandler http.Handler
}
StackConfig contains configuration for creating a new network stack.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package censor implements common internet censorship techniques for testing.
|
Package censor implements common internet censorship techniques for testing. |
|
Package models the distributed DNS database.
|
Package models the distributed DNS database. |
|
Package geolink models a geographic point-to-point link.
|
Package geolink models a geographic point-to-point link. |
|
Package link models a point-to-point network link.
|
Package link models a point-to-point network link. |
|
Package packet contains *Packet and the related definitions.
|
Package packet contains *Packet and the related definitions. |
|
Package router provides network routing capabilities for testing.
|
Package router provides network routing capabilities for testing. |