vnet

package
v0.0.0-...-bff1185 Latest Latest
Warning

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

Go to latest
Published: Nov 15, 2023 License: MIT Imports: 15 Imported by: 0

README

vnet

A virtual network layer for pion.

Overview

Goals
  • To make NAT traversal tests easy.
  • To emulate packet impairment at application level for testing.
  • To monitor packets at specified arbitrary interfaces.
Features
  • Configurable virtual LAN and WAN
  • Virtually hosted ICE servers
Virtual network components
Top View
                           ......................................
                           :         Virtual Network (vnet)     :
                           :                                    :
   +-------+ *         1 +----+         +--------+              :
   | :App  |------------>|:Net|--o<-----|:Router |              :
   +-------+             +----+         |        |              :
   +-----------+ *     1 +----+         |        |              :
   |:STUNServer|-------->|:Net|--o<-----|        |              :
   +-----------+         +----+         |        |              :
   +-----------+ *     1 +----+         |        |              :
   |:TURNServer|-------->|:Net|--o<-----|        |              :
   +-----------+         +----+ [1]     |        |              :
                           :          1 |        | 1  <<has>>   :
                           :      +---<>|        |<>----+ [2]   :
                           :      |     +--------+      |       :
                         To form  |      *|             v 0..1  :
                   a subnet tree  |       o [3]      +-----+    :
                           :      |       ^          |:NAT |    :
                           :      |       |          +-----+    :
                           :      +-------+                     :
                           ......................................
    Note:
        o: NIC (Network Interface Controller)
      [1]: Net implements NIC interface.
      [2]: Root router has no NAT. All child routers have a NAT always.
      [3]: Router implements NIC interface for accesses from the
           parent router.
Net

Net provides 3 interfaces:

  • Configuration API (direct)
  • Network API via Net (equivalent to net.Xxx())
  • Router access via NIC interface
                   (Pion module/app, ICE servers, etc.)
                             +-----------+
                             |   :App    |
                             +-----------+
                                 * | 
                                   | <<uses>>
                                 1 v
   +---------+ 1           * +-----------+ 1    * +-----------+ 1    * +------+
 ..| :Router |----+------>o--|   :Net    |<>------|:Interface |<>------|:Addr |
   +---------+    |      NIC +-----------+        +-----------+        +------+
                  | <<interface>>             (transport.Interface)   (net.Addr)
                  |
                  |        * +-----------+ 1    * +-----------+ 1    * +------+
                  +------>o--|  :Router  |<>------|:Interface |<>------|:Addr |
                         NIC +-----------+        +-----------+        +------+
                    <<interface>>            (transport.Interface)    (net.Addr)

The instance of Net will be the one passed around the project. Net class has public methods for configuration and for application use.

Implementation

Design Policy
  • Each pion package should have config object which has Net (of type transport.Net) property.
    • Just like how we distribute LoggerFactory throughout the pion project.
  • DNS => a simple dictionary (global)?
  • Each Net has routing capability (a goroutine)
  • Use interface provided net package as much as possible
  • Routers are connected in a tree structure (no loop is allowed)
    • To simplify routing
    • Easy to control / monitor (stats, etc)
  • Root router has no NAT (== Internet / WAN)
  • Non-root router has a NAT always
  • When a Net is instantiated, it will automatically add lo0 and eth0 interface, and lo0 will have one IP address, 127.0.0.1. (this is not used in pion/ice, however)
  • When a Net is added to a router, the router automatically assign an IP address for eth0 interface.
    • For simplicity
  • User data won't fragment, but optionally drop chunk larger than MTU
  • IPv6 is not supported
Basic steps for setting up virtual network
  1. Create a root router (WAN)
  2. Create child routers and add to its parent (forms a tree, don't create a loop!)
  3. Add instances of Net to each routers
  4. Call Stop(), or Stop(), on the top router, which propagates all other routers
Example: WAN with one endpoint (vnet)
import (
	"net"

    "github.com/pion/transport"
	"github.com/pion/transport/vnet"
	"github.com/pion/logging"
)

// Create WAN (a root router).
wan, err := vnet.NewRouter(&RouterConfig{
    CIDR:          "0.0.0.0/0",
    LoggerFactory: logging.NewDefaultLoggerFactory(),
})

// Create a network.
// You can specify a static IP for the instance of Net to use. If not specified,
// router will assign an IP address that is contained in the router's CIDR.
nw := vnet.NewNet(&vnet.NetConfig{
    StaticIP: "27.1.2.3",
})

// Add the network to the router.
// The router will assign an IP address to `nw`.
if err = wan.AddNet(nw); err != nil {
    // handle error
}

// Start router.
// This will start internal goroutine to route packets.
// If you set child routers (using AddRouter), the call on the root router
// will start the rest of routers for you.
if err = wan.Start(); err != nil {
    // handle error
}

//
// Your application runs here using `nw`.
//

// Stop the router.
// This will stop all internal Go routines in the router tree.
// (No need to call Stop() on child routers)
if err = wan.Stop(); err != nil {
    // handle error
}
Example of how to pass around the instance of vnet.Net

The instance of vnet.Net wraps a subset of net package to enable operations on the virtual network. Your project must be able to pass the instance to all your routines that do network operation with net package. A typical way is to use a config param to create your instances with the virtual network instance (nw in the above example) like this:

type AgentConfig struct {
    :
    Net:  transport.Net,
}

type Agent struct {
     :
    net:  transport.Net,
}

func NetAgent(config *AgentConfig) *Agent {
    if config.Net == nil {
        config.Net = vnet.NewNet()
    }
    
    return &Agent {
         :
        net: config.Net,
    }
}
// a.net is the instance of vnet.Net class
func (a *Agent) listenUDP(...) error {
    conn, err := a.net.ListenPacket(udpString, ...)
    if err != nil {
        return nil, err
    }
      :
}
Compatibility and Support Status
net
(built-in)
vnet Note
net.Interfaces() a.net.Interfaces()
net.InterfaceByName() a.net.InterfaceByName()
net.ResolveUDPAddr() a.net.ResolveUDPAddr()
net.ListenPacket() a.net.ListenPacket()
net.ListenUDP() a.net.ListenUDP() ListenPacket() is recommended
net.Listen() a.net.Listen() TODO)
net.ListenTCP() (not supported) Listen() would be recommended
net.Dial() a.net.Dial()
net.DialUDP() a.net.DialUDP()
net.DialTCP() (not supported)
net.Interface transport.Interface
net.PacketConn (use it as-is)
net.UDPConn transport.UDPConn
net.TCPConn transport.TCPConn TODO: Use net.Conn in your code
net.Dialer transport.Dialer Use a.net.CreateDialer() to create it.
The use of vnet.Dialer is currently experimental.

a.net is an instance of Net class, and types are defined under the package name vnet

Most of other interface types in net package can be used as is.

Please post a github issue when other types/methods need to be added to vnet/vnet.Net.

TODO / Next Step

  • Implement TCP (TCPConn, Listen)
  • Support of IPv6
  • Write a bunch of examples for building virtual networks.
  • Add network impairment features (on Router)
    • Introduce latency / jitter
    • Packet filtering handler (allow selectively drop packets, etc.)
  • Add statistics data retrieval
    • Total number of packets forward by each router
    • Total number of packet loss
    • Total number of connection failure (TCP)

References

Documentation

Overview

Package vnet provides a virtual network layer for pion

Index

Constants

View Source
const (
	// Bit is a single bit
	Bit = 1
	// KBit is a kilobit
	KBit = 1000 * Bit
	// MBit is a Megabit
	MBit = 1000 * KBit
)

Variables

This section is empty.

Functions

This section is empty.

Types

type Chunk

type Chunk interface {
	SourceAddr() net.Addr
	DestinationAddr() net.Addr
	UserData() []byte
	Tag() string
	Clone() Chunk
	Network() string // returns "udp" or "tcp"
	String() string
	// contains filtered or unexported methods
}

Chunk represents a packet passed around in the vnet

type ChunkFilter

type ChunkFilter func(c Chunk) bool

ChunkFilter is a handler users can add to filter chunks. If the filter returns false, the packet will be dropped.

type DelayFilter

type DelayFilter struct {
	NIC
	// contains filtered or unexported fields
}

DelayFilter delays outgoing packets by the given delay. Run must be called before any packets will be forwarded.

func NewDelayFilter

func NewDelayFilter(nic NIC, delay time.Duration) (*DelayFilter, error)

NewDelayFilter creates a new DelayFilter with the given nic and delay.

func (*DelayFilter) Run

func (f *DelayFilter) Run(ctx context.Context)

Run starts forwarding of packets. Packets will be forwarded if they spent >delay time in the internal queue. Must be called before any packet will be forwarded.

type EndpointDependencyType

type EndpointDependencyType uint8

EndpointDependencyType defines a type of behavioral dependendency on the remote endpoint's IP address or port number. This is used for the two kinds of behaviors:

  • Port mapping behavior
  • Filtering behavior

See: https://tools.ietf.org/html/rfc4787

const (
	// EndpointIndependent means the behavior is independent of the endpoint's address or port
	EndpointIndependent EndpointDependencyType = iota
	// EndpointAddrDependent means the behavior is dependent on the endpoint's address
	EndpointAddrDependent
	// EndpointAddrPortDependent means the behavior is dependent on the endpoint's address and port
	EndpointAddrPortDependent
)

type LossFilter

type LossFilter struct {
	NIC
	// contains filtered or unexported fields
}

LossFilter is a wrapper around NICs, that drops some of the packets passed to onInboundChunk

func NewLossFilter

func NewLossFilter(nic NIC, chance int) (*LossFilter, error)

NewLossFilter creates a new LossFilter that drops every packet with a probability of chance/100. Every packet that is not dropped is passed on to the given NIC.

type NATMode

type NATMode uint8

NATMode defines basic behavior of the NAT

const (
	// NATModeNormal means the NAT behaves as a standard NAPT (RFC 2663).
	NATModeNormal NATMode = iota
	// NATModeNAT1To1 exhibits 1:1 DNAT where the external IP address is statically mapped to
	// a specific local IP address with port number is preserved always between them.
	// When this mode is selected, MappingBehavior, FilteringBehavior, PortPreservation and
	// MappingLifeTime of NATType are ignored.
	NATModeNAT1To1
)

type NATType

type NATType struct {
	Mode              NATMode
	MappingBehavior   EndpointDependencyType
	FilteringBehavior EndpointDependencyType
	Hairpinning       bool // Not implemented yet
	PortPreservation  bool // Not implemented yet
	MappingLifeTime   time.Duration
}

NATType has a set of parameters that define the behavior of NAT.

type NIC

type NIC interface {
	// contains filtered or unexported methods
}

NIC is a network interface controller that interfaces Router

type Net

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

Net represents a local network stack equivalent to a set of layers from NIC up to the transport (UDP / TCP) layer.

func NewNet

func NewNet(config *NetConfig) (*Net, error)

NewNet creates an instance of a virtual network.

By design, it always have lo0 and eth0 interfaces. The lo0 has the address 127.0.0.1 assigned by default. IP address for eth0 will be assigned when this Net is added to a router.

func (*Net) CreateDialer

func (v *Net) CreateDialer(d *net.Dialer) transport.Dialer

CreateDialer creates an instance of vnet.Dialer

func (*Net) Dial

func (v *Net) Dial(network string, address string) (net.Conn, error)

Dial connects to the address on the named network.

func (*Net) DialTCP

func (v *Net) DialTCP(string, *net.TCPAddr, *net.TCPAddr) (transport.TCPConn, error)

DialTCP acts like Dial for TCP networks.

func (*Net) DialUDP

func (v *Net) DialUDP(network string, locAddr, remAddr *net.UDPAddr) (transport.UDPConn, error)

DialUDP acts like Dial for UDP networks.

func (*Net) InterfaceByIndex

func (v *Net) InterfaceByIndex(index int) (*transport.Interface, error)

InterfaceByIndex returns the interface specified by index.

On Solaris, it returns one of the logical network interfaces sharing the logical data link; for more precision use InterfaceByName.

func (*Net) InterfaceByName

func (v *Net) InterfaceByName(ifName string) (*transport.Interface, error)

InterfaceByName returns the interface specified by name.

func (*Net) Interfaces

func (v *Net) Interfaces() ([]*transport.Interface, error)

Interfaces returns a list of the system's network interfaces.

func (*Net) ListenPacket

func (v *Net) ListenPacket(network string, address string) (net.PacketConn, error)

ListenPacket announces on the local network address.

func (*Net) ListenTCP

func (v *Net) ListenTCP(string, *net.TCPAddr) (transport.TCPListener, error)

ListenTCP acts like Listen for TCP networks.

func (*Net) ListenUDP

func (v *Net) ListenUDP(network string, locAddr *net.UDPAddr) (transport.UDPConn, error)

ListenUDP acts like ListenPacket for UDP networks.

func (*Net) ResolveIPAddr

func (v *Net) ResolveIPAddr(_, address string) (*net.IPAddr, error)

ResolveIPAddr returns an address of IP end point.

func (*Net) ResolveTCPAddr

func (v *Net) ResolveTCPAddr(network, address string) (*net.TCPAddr, error)

ResolveTCPAddr returns an address of TCP end point.

func (*Net) ResolveUDPAddr

func (v *Net) ResolveUDPAddr(network, address string) (*net.UDPAddr, error)

ResolveUDPAddr returns an address of UDP end point.

type NetConfig

type NetConfig struct {
	// StaticIPs is an array of static IP addresses to be assigned for this Net.
	// If no static IP address is given, the router will automatically assign
	// an IP address.
	StaticIPs []string

	// StaticIP is deprecated. Use StaticIPs.
	StaticIP string
}

NetConfig is a bag of configuration parameters passed to NewNet().

type Router

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

Router ...

func NewRouter

func NewRouter(config *RouterConfig) (*Router, error)

NewRouter ...

func (*Router) AddChildRouter

func (r *Router) AddChildRouter(router *Router) error

AddChildRouter is like AddRouter, but does not add the child routers NIC to the parent. This has to be done manually by calling AddNet, which allows to use a wrapper around the subrouters NIC. AddNet MUST be called before AddChildRouter.

func (*Router) AddChunkFilter

func (r *Router) AddChunkFilter(filter ChunkFilter)

AddChunkFilter adds a filter for chunks traversing this router. You may add more than one filter. The filters are called in the order of this method call. If a chunk is dropped by a filter, subsequent filter will not receive the chunk.

func (*Router) AddHost

func (r *Router) AddHost(hostName string, ipAddr string) error

AddHost adds a mapping of hostname and an IP address to the local resolver.

func (*Router) AddNet

func (r *Router) AddNet(nic NIC) error

AddNet ...

func (*Router) AddRouter

func (r *Router) AddRouter(router *Router) error

AddRouter adds a child Router.

func (*Router) Start

func (r *Router) Start() error

Start ...

func (*Router) Stop

func (r *Router) Stop() error

Stop ...

type RouterConfig

type RouterConfig struct {
	// Name of router. If not specified, a unique name will be assigned.
	Name string
	// CIDR notation, like "192.0.2.0/24"
	CIDR string
	// StaticIPs is an array of static IP addresses to be assigned for this router.
	// If no static IP address is given, the router will automatically assign
	// an IP address.
	// This will be ignored if this router is the root.
	StaticIPs []string
	// StaticIP is deprecated. Use StaticIPs.
	StaticIP string
	// Internal queue size
	QueueSize int
	// Effective only when this router has a parent router
	NATType *NATType
	// Minimum Delay
	MinDelay time.Duration
	// Max Jitter
	MaxJitter time.Duration
	// Logger factory
	LoggerFactory logging.LoggerFactory
}

RouterConfig ...

type TBFOption

type TBFOption func(*TokenBucketFilter) TBFOption

TBFOption is the option type to configure a TokenBucketFilter

func TBFMaxBurst

func TBFMaxBurst(size int) TBFOption

TBFMaxBurst sets the bucket size of the token bucket filter. This is the maximum size that can instantly leave the filter, if the bucket is full.

func TBFQueueSizeInBytes

func TBFQueueSizeInBytes(bytes int) TBFOption

TBFQueueSizeInBytes sets the max number of bytes waiting in the queue. Can only be set in constructor before using the TBF.

func TBFRate

func TBFRate(rate int) TBFOption

TBFRate sets the bit rate of a TokenBucketFilter

type TokenBucketFilter

type TokenBucketFilter struct {
	NIC
	// contains filtered or unexported fields
}

TokenBucketFilter implements a token bucket rate limit algorithm.

func NewTokenBucketFilter

func NewTokenBucketFilter(n NIC, opts ...TBFOption) (*TokenBucketFilter, error)

NewTokenBucketFilter creates and starts a new TokenBucketFilter

func (*TokenBucketFilter) Close

func (t *TokenBucketFilter) Close() error

Close closes and stops the token bucket filter queue

func (*TokenBucketFilter) Set

func (t *TokenBucketFilter) Set(opts ...TBFOption) (previous TBFOption)

Set updates a setting on the token bucket filter

type UDPConn

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

UDPConn is the implementation of the Conn and PacketConn interfaces for UDP network connections. compatible with net.PacketConn and net.Conn

func (*UDPConn) Close

func (c *UDPConn) Close() error

Close closes the connection. Any blocked ReadFrom or WriteTo operations will be unblocked and return errors.

func (*UDPConn) LocalAddr

func (c *UDPConn) LocalAddr() net.Addr

LocalAddr returns the local network address.

func (*UDPConn) Read

func (c *UDPConn) Read(b []byte) (int, error)

Read reads data from the connection. Read can be made to time out and return an Error with Timeout() == true after a fixed time limit; see SetDeadline and SetReadDeadline.

func (*UDPConn) ReadFrom

func (c *UDPConn) ReadFrom(p []byte) (n int, addr net.Addr, err error)

ReadFrom reads a packet from the connection, copying the payload into p. It returns the number of bytes copied into p and the return address that was on the packet. It returns the number of bytes read (0 <= n <= len(p)) and any error encountered. Callers should always process the n > 0 bytes returned before considering the error err. ReadFrom can be made to time out and return an Error with Timeout() == true after a fixed time limit; see SetDeadline and SetReadDeadline.

func (*UDPConn) ReadFromUDP

func (c *UDPConn) ReadFromUDP(b []byte) (int, *net.UDPAddr, error)

ReadFromUDP acts like ReadFrom but returns a UDPAddr.

func (*UDPConn) ReadMsgUDP

func (c *UDPConn) ReadMsgUDP([]byte, []byte) (n, oobn, flags int, addr *net.UDPAddr, err error)

ReadMsgUDP reads a message from c, copying the payload into b and the associated out-of-band data into oob. It returns the number of bytes copied into b, the number of bytes copied into oob, the flags that were set on the message and the source address of the message.

The packages golang.org/x/net/ipv4 and golang.org/x/net/ipv6 can be used to manipulate IP-level socket options in oob.

func (*UDPConn) RemoteAddr

func (c *UDPConn) RemoteAddr() net.Addr

RemoteAddr returns the remote network address.

func (*UDPConn) SetDeadline

func (c *UDPConn) SetDeadline(t time.Time) error

SetDeadline sets the read and write deadlines associated with the connection. It is equivalent to calling both SetReadDeadline and SetWriteDeadline.

A deadline is an absolute time after which I/O operations fail with a timeout (see type Error) instead of blocking. The deadline applies to all future and pending I/O, not just the immediately following call to ReadFrom or WriteTo. After a deadline has been exceeded, the connection can be refreshed by setting a deadline in the future.

An idle timeout can be implemented by repeatedly extending the deadline after successful ReadFrom or WriteTo calls.

A zero value for t means I/O operations will not time out.

func (*UDPConn) SetReadBuffer

func (c *UDPConn) SetReadBuffer(int) error

SetReadBuffer sets the size of the operating system's receive buffer associated with the connection.

func (*UDPConn) SetReadDeadline

func (c *UDPConn) SetReadDeadline(t time.Time) error

SetReadDeadline sets the deadline for future ReadFrom calls and any currently-blocked ReadFrom call. A zero value for t means ReadFrom will not time out.

func (*UDPConn) SetWriteBuffer

func (c *UDPConn) SetWriteBuffer(int) error

SetWriteBuffer sets the size of the operating system's transmit buffer associated with the connection.

func (*UDPConn) SetWriteDeadline

func (c *UDPConn) SetWriteDeadline(time.Time) error

SetWriteDeadline sets the deadline for future WriteTo calls and any currently-blocked WriteTo call. Even if write times out, it may return n > 0, indicating that some of the data was successfully written. A zero value for t means WriteTo will not time out.

func (*UDPConn) Write

func (c *UDPConn) Write(b []byte) (int, error)

Write writes data to the connection. Write can be made to time out and return an Error with Timeout() == true after a fixed time limit; see SetDeadline and SetWriteDeadline.

func (*UDPConn) WriteMsgUDP

func (c *UDPConn) WriteMsgUDP([]byte, []byte, *net.UDPAddr) (n, oobn int, err error)

WriteMsgUDP writes a message to addr via c if c isn't connected, or to c's remote address if c is connected (in which case addr must be nil). The payload is copied from b and the associated out-of-band data is copied from oob. It returns the number of payload and out-of-band bytes written.

The packages golang.org/x/net/ipv4 and golang.org/x/net/ipv6 can be used to manipulate IP-level socket options in oob.

func (*UDPConn) WriteTo

func (c *UDPConn) WriteTo(p []byte, addr net.Addr) (n int, err error)

WriteTo writes a packet with payload p to addr. WriteTo can be made to time out and return an Error with Timeout() == true after a fixed time limit; see SetDeadline and SetWriteDeadline. On packet-oriented connections, write timeouts are rare.

func (*UDPConn) WriteToUDP

func (c *UDPConn) WriteToUDP(b []byte, addr *net.UDPAddr) (int, error)

WriteToUDP acts like WriteTo but takes a UDPAddr.

type UDPProxy

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

UDPProxy is a proxy between real server(net.UDPConn) and vnet.UDPConn.

High level design:

                        ..............................................
                        :         Virtual Network (vnet)             :
                        :                                            :
+-------+ *         1 +----+         +--------+                      :
| :App  |------------>|:Net|--o<-----|:Router |          .............................
+-------+             +----+         |        |          :        UDPProxy           :
                        :            |        |       +----+     +---------+     +---------+     +--------+
                        :            |        |--->o--|:Net|-->o-| vnet.   |-->o-|  net.   |--->-| :Real  |
                        :            |        |       +----+     | UDPConn |     | UDPConn |     | Server |
                        :            |        |          :       +---------+     +---------+     +--------+
                        :            |        |          ............................:
                        :            +--------+                       :
                        ...............................................

func NewProxy

func NewProxy(router *Router) (*UDPProxy, error)

NewProxy create a proxy, the router for this proxy belongs/bind to. If need to proxy for please create a new proxy for each router. For all addresses we proxy, we will create a vnet.Net in this router and proxy all packets.

func (*UDPProxy) Close

func (v *UDPProxy) Close() error

Close the proxy, stop all workers.

func (*UDPProxy) Deliver

func (v *UDPProxy) Deliver(sourceAddr, destAddr net.Addr, b []byte) (nn int, err error)

Deliver directly send packet to vnet or real-server. For example, we can use this API to simulate the REPLAY ATTACK.

func (*UDPProxy) Proxy

func (v *UDPProxy) Proxy(client *Net, server *net.UDPAddr) error

Proxy starts a worker for server, ignore if already started.

Jump to

Keyboard shortcuts

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