feat: Waku v2 bridge

Issue #12610
This commit is contained in:
Michal Iskierko
2023-11-12 13:29:38 +01:00
parent 56e7bd01ca
commit 6d31343205
6716 changed files with 1982502 additions and 5891 deletions

1
vendor/github.com/pion/transport/vnet/.gitignore generated vendored Normal file
View File

@@ -0,0 +1 @@
*.sw[poe]

239
vendor/github.com/pion/transport/vnet/README.md generated vendored Normal file
View File

@@ -0,0 +1,239 @@
# 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 (Netork Interface Controller)
[1]: Net implments 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>> (vnet.Interface) (net.Addr)
|
| * +-----------+ 1 * +-----------+ 1 * +------+
+------>o--| :Router |<>------|:Interface |<>------|:Addr |
NIC +-----------+ +-----------+ +------+
<<interface>> (vnet.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 vnet.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)
1. Create child routers and add to its parent (forms a tree, don't create a loop!)
1. Add instances of Net to each routers
1. Call Stop(), or Stop(), on the top router, which propages all other routers
#### Example: WAN with one endpoint (vnet)
```go
import (
"net"
"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 goroutines 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:
```go
type AgentConfig struct {
:
Net: *vnet.Net,
}
type Agent struct {
:
net: *vnet.Net,
}
func NetAgent(config *AgentConfig) *Agent {
if config.Net == nil {
config.Net = vnet.NewNet(nil) // defaults to native operation
}
return &Agent {
:
net: config.Net,
}
}
```
```go
// 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`<br>(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|vnet.Interface||
|net.PacketConn|(use it as-is)||
|net.UDPConn|vnet.UDPConn|Use vnet.UDPPacketConn in your code|
|net.TCPConn|vnet.TCPConn|(TODO)|Use net.Conn in your code|
|net.Dialer|vnet.Dialer|Use a.net.CreateDialer() to create it.<br>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 lantecy / 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
* [Comparing Simulated Packet Loss and RealWorld Network Congestion](https://www.riverbed.com/document/fpo/WhitePaper-Riverbed-SimulatedPacketLoss.pdf)
### Code experiments
* [CIDR and IPMask](https://play.golang.org/p/B7OBhkZqjmj)
* [Test with net.IP](https://play.golang.org/p/AgXd23wKY4W)
* [ListenPacket](https://play.golang.org/p/d4vasbnRimQ)
* [isDottedIP()](https://play.golang.org/p/t4aZ47TgJfO)
* [SplitHostPort](https://play.golang.org/p/JtvurlcMbhn)

283
vendor/github.com/pion/transport/vnet/chunk.go generated vendored Normal file
View File

@@ -0,0 +1,283 @@
package vnet
import (
"fmt"
"net"
"strconv"
"strings"
"sync/atomic"
"time"
)
type tcpFlag uint8
const (
tcpFIN tcpFlag = 0x01
tcpSYN tcpFlag = 0x02
tcpRST tcpFlag = 0x04
tcpPSH tcpFlag = 0x08
tcpACK tcpFlag = 0x10
)
func (f tcpFlag) String() string {
var sa []string
if f&tcpFIN != 0 {
sa = append(sa, "FIN")
}
if f&tcpSYN != 0 {
sa = append(sa, "SYN")
}
if f&tcpRST != 0 {
sa = append(sa, "RST")
}
if f&tcpPSH != 0 {
sa = append(sa, "PSH")
}
if f&tcpACK != 0 {
sa = append(sa, "ACK")
}
return strings.Join(sa, "-")
}
// Generate a base36-encoded unique tag
// See: https://play.golang.org/p/0ZaAID1q-HN
var assignChunkTag = func() func() string { //nolint:gochecknoglobals
var tagCtr uint64
return func() string {
n := atomic.AddUint64(&tagCtr, 1)
return strconv.FormatUint(n, 36)
}
}()
// Chunk represents a packet passed around in the vnet
type Chunk interface {
setTimestamp() time.Time // used by router
getTimestamp() time.Time // used by router
getSourceIP() net.IP // used by router
getDestinationIP() net.IP // used by router
setSourceAddr(address string) error // used by nat
setDestinationAddr(address string) error // used by nat
SourceAddr() net.Addr
DestinationAddr() net.Addr
UserData() []byte
Tag() string
Clone() Chunk
Network() string // returns "udp" or "tcp"
String() string
}
type chunkIP struct {
timestamp time.Time
sourceIP net.IP
destinationIP net.IP
tag string
}
func (c *chunkIP) setTimestamp() time.Time {
c.timestamp = time.Now()
return c.timestamp
}
func (c *chunkIP) getTimestamp() time.Time {
return c.timestamp
}
func (c *chunkIP) getDestinationIP() net.IP {
return c.destinationIP
}
func (c *chunkIP) getSourceIP() net.IP {
return c.sourceIP
}
func (c *chunkIP) Tag() string {
return c.tag
}
type chunkUDP struct {
chunkIP
sourcePort int
destinationPort int
userData []byte
}
func newChunkUDP(srcAddr, dstAddr *net.UDPAddr) *chunkUDP {
return &chunkUDP{
chunkIP: chunkIP{
sourceIP: srcAddr.IP,
destinationIP: dstAddr.IP,
tag: assignChunkTag(),
},
sourcePort: srcAddr.Port,
destinationPort: dstAddr.Port,
}
}
func (c *chunkUDP) SourceAddr() net.Addr {
return &net.UDPAddr{
IP: c.sourceIP,
Port: c.sourcePort,
}
}
func (c *chunkUDP) DestinationAddr() net.Addr {
return &net.UDPAddr{
IP: c.destinationIP,
Port: c.destinationPort,
}
}
func (c *chunkUDP) UserData() []byte {
return c.userData
}
func (c *chunkUDP) Clone() Chunk {
var userData []byte
if c.userData != nil {
userData = make([]byte, len(c.userData))
copy(userData, c.userData)
}
return &chunkUDP{
chunkIP: chunkIP{
timestamp: c.timestamp,
sourceIP: c.sourceIP,
destinationIP: c.destinationIP,
tag: c.tag,
},
sourcePort: c.sourcePort,
destinationPort: c.destinationPort,
userData: userData,
}
}
func (c *chunkUDP) Network() string {
return udpString
}
func (c *chunkUDP) String() string {
src := c.SourceAddr()
dst := c.DestinationAddr()
return fmt.Sprintf("%s chunk %s %s => %s",
src.Network(),
c.tag,
src.String(),
dst.String(),
)
}
func (c *chunkUDP) setSourceAddr(address string) error {
addr, err := net.ResolveUDPAddr(udpString, address)
if err != nil {
return err
}
c.sourceIP = addr.IP
c.sourcePort = addr.Port
return nil
}
func (c *chunkUDP) setDestinationAddr(address string) error {
addr, err := net.ResolveUDPAddr(udpString, address)
if err != nil {
return err
}
c.destinationIP = addr.IP
c.destinationPort = addr.Port
return nil
}
type chunkTCP struct {
chunkIP
sourcePort int
destinationPort int
flags tcpFlag // control bits
userData []byte // only with PSH flag
// seq uint32 // always starts with 0
// ack uint32 // always starts with 0
}
func newChunkTCP(srcAddr, dstAddr *net.TCPAddr, flags tcpFlag) *chunkTCP {
return &chunkTCP{
chunkIP: chunkIP{
sourceIP: srcAddr.IP,
destinationIP: dstAddr.IP,
tag: assignChunkTag(),
},
sourcePort: srcAddr.Port,
destinationPort: dstAddr.Port,
flags: flags,
}
}
func (c *chunkTCP) SourceAddr() net.Addr {
return &net.TCPAddr{
IP: c.sourceIP,
Port: c.sourcePort,
}
}
func (c *chunkTCP) DestinationAddr() net.Addr {
return &net.TCPAddr{
IP: c.destinationIP,
Port: c.destinationPort,
}
}
func (c *chunkTCP) UserData() []byte {
return c.userData
}
func (c *chunkTCP) Clone() Chunk {
userData := make([]byte, len(c.userData))
copy(userData, c.userData)
return &chunkTCP{
chunkIP: chunkIP{
timestamp: c.timestamp,
sourceIP: c.sourceIP,
destinationIP: c.destinationIP,
},
sourcePort: c.sourcePort,
destinationPort: c.destinationPort,
userData: userData,
}
}
func (c *chunkTCP) Network() string {
return "tcp"
}
func (c *chunkTCP) String() string {
src := c.SourceAddr()
dst := c.DestinationAddr()
return fmt.Sprintf("%s %s chunk %s %s => %s",
src.Network(),
c.flags.String(),
c.tag,
src.String(),
dst.String(),
)
}
func (c *chunkTCP) setSourceAddr(address string) error {
addr, err := net.ResolveTCPAddr("tcp", address)
if err != nil {
return err
}
c.sourceIP = addr.IP
c.sourcePort = addr.Port
return nil
}
func (c *chunkTCP) setDestinationAddr(address string) error {
addr, err := net.ResolveTCPAddr("tcp", address)
if err != nil {
return err
}
c.destinationIP = addr.IP
c.destinationPort = addr.Port
return nil
}

65
vendor/github.com/pion/transport/vnet/chunk_queue.go generated vendored Normal file
View File

@@ -0,0 +1,65 @@
package vnet
import (
"sync"
)
type chunkQueue struct {
chunks []Chunk
maxSize int // 0 or negative value: unlimited
maxBytes int // 0 or negative value: unlimited
currentBytes int
mutex sync.RWMutex
}
func newChunkQueue(maxSize int, maxBytes int) *chunkQueue {
return &chunkQueue{
chunks: []Chunk{},
maxSize: maxSize,
maxBytes: maxBytes,
currentBytes: 0,
mutex: sync.RWMutex{},
}
}
func (q *chunkQueue) push(c Chunk) bool {
q.mutex.Lock()
defer q.mutex.Unlock()
if q.maxSize > 0 && len(q.chunks) >= q.maxSize {
return false // dropped
}
if q.maxBytes > 0 && q.currentBytes+len(c.UserData()) >= q.maxBytes {
return false
}
q.currentBytes += len(c.UserData())
q.chunks = append(q.chunks, c)
return true
}
func (q *chunkQueue) pop() (Chunk, bool) {
q.mutex.Lock()
defer q.mutex.Unlock()
if len(q.chunks) == 0 {
return nil, false
}
c := q.chunks[0]
q.chunks = q.chunks[1:]
q.currentBytes -= len(c.UserData())
return c, true
}
func (q *chunkQueue) peek() Chunk {
q.mutex.RLock()
defer q.mutex.RUnlock()
if len(q.chunks) == 0 {
return nil
}
return q.chunks[0]
}

246
vendor/github.com/pion/transport/vnet/conn.go generated vendored Normal file
View File

@@ -0,0 +1,246 @@
package vnet
import (
"errors"
"io"
"math"
"net"
"sync"
"time"
)
const (
maxReadQueueSize = 1024
)
var (
errObsCannotBeNil = errors.New("obs cannot be nil")
errUseClosedNetworkConn = errors.New("use of closed network connection")
errAddrNotUDPAddr = errors.New("addr is not a net.UDPAddr")
errLocAddr = errors.New("something went wrong with locAddr")
errAlreadyClosed = errors.New("already closed")
errNoRemAddr = errors.New("no remAddr defined")
)
// UDPPacketConn is packet-oriented connection for UDP.
type UDPPacketConn interface {
net.PacketConn
Read(b []byte) (int, error)
RemoteAddr() net.Addr
Write(b []byte) (int, error)
}
// vNet implements this
type connObserver interface {
write(c Chunk) error
onClosed(addr net.Addr)
determineSourceIP(locIP, dstIP net.IP) net.IP
}
// UDPConn is the implementation of the Conn and PacketConn interfaces for UDP network connections.
// comatible with net.PacketConn and net.Conn
type UDPConn struct {
locAddr *net.UDPAddr // read-only
remAddr *net.UDPAddr // read-only
obs connObserver // read-only
readCh chan Chunk // thread-safe
closed bool // requires mutex
mu sync.Mutex // to mutex closed flag
readTimer *time.Timer // thread-safe
}
func newUDPConn(locAddr, remAddr *net.UDPAddr, obs connObserver) (*UDPConn, error) {
if obs == nil {
return nil, errObsCannotBeNil
}
return &UDPConn{
locAddr: locAddr,
remAddr: remAddr,
obs: obs,
readCh: make(chan Chunk, maxReadQueueSize),
readTimer: time.NewTimer(time.Duration(math.MaxInt64)),
}, nil
}
// 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 (c *UDPConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
loop:
for {
select {
case chunk, ok := <-c.readCh:
if !ok {
break loop
}
var err error
n := copy(p, chunk.UserData())
addr := chunk.SourceAddr()
if n < len(chunk.UserData()) {
err = io.ErrShortBuffer
}
if c.remAddr != nil {
if addr.String() != c.remAddr.String() {
break // discard (shouldn't happen)
}
}
return n, addr, err
case <-c.readTimer.C:
return 0, nil, &net.OpError{
Op: "read",
Net: c.locAddr.Network(),
Addr: c.locAddr,
Err: newTimeoutError("i/o timeout"),
}
}
}
return 0, nil, &net.OpError{
Op: "read",
Net: c.locAddr.Network(),
Addr: c.locAddr,
Err: errUseClosedNetworkConn,
}
}
// 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 (c *UDPConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
dstAddr, ok := addr.(*net.UDPAddr)
if !ok {
return 0, errAddrNotUDPAddr
}
srcIP := c.obs.determineSourceIP(c.locAddr.IP, dstAddr.IP)
if srcIP == nil {
return 0, errLocAddr
}
srcAddr := &net.UDPAddr{
IP: srcIP,
Port: c.locAddr.Port,
}
chunk := newChunkUDP(srcAddr, dstAddr)
chunk.userData = make([]byte, len(p))
copy(chunk.userData, p)
if err := c.obs.write(chunk); err != nil {
return 0, err
}
return len(p), nil
}
// Close closes the connection.
// Any blocked ReadFrom or WriteTo operations will be unblocked and return errors.
func (c *UDPConn) Close() error {
c.mu.Lock()
defer c.mu.Unlock()
if c.closed {
return errAlreadyClosed
}
c.closed = true
close(c.readCh)
c.obs.onClosed(c.locAddr)
return nil
}
// LocalAddr returns the local network address.
func (c *UDPConn) LocalAddr() net.Addr {
return c.locAddr
}
// 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 (c *UDPConn) SetDeadline(t time.Time) error {
return c.SetReadDeadline(t)
}
// 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 (c *UDPConn) SetReadDeadline(t time.Time) error {
var d time.Duration
var noDeadline time.Time
if t == noDeadline {
d = time.Duration(math.MaxInt64)
} else {
d = time.Until(t)
}
c.readTimer.Reset(d)
return nil
}
// 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 (c *UDPConn) SetWriteDeadline(t time.Time) error {
// Write never blocks.
return nil
}
// 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 (c *UDPConn) Read(b []byte) (int, error) {
n, _, err := c.ReadFrom(b)
return n, err
}
// RemoteAddr returns the remote network address.
func (c *UDPConn) RemoteAddr() net.Addr {
return c.remAddr
}
// 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 (c *UDPConn) Write(b []byte) (int, error) {
if c.remAddr == nil {
return 0, errNoRemAddr
}
return c.WriteTo(b, c.remAddr)
}
func (c *UDPConn) onInboundChunk(chunk Chunk) {
c.mu.Lock()
defer c.mu.Unlock()
if c.closed {
return
}
select {
case c.readCh <- chunk:
default:
}
}

136
vendor/github.com/pion/transport/vnet/conn_map.go generated vendored Normal file
View File

@@ -0,0 +1,136 @@
package vnet
import (
"errors"
"net"
"sync"
)
var (
errAddressAlreadyInUse = errors.New("address already in use")
errNoSuchUDPConn = errors.New("no such UDPConn")
errCannotRemoveUnspecifiedIP = errors.New("cannot remove unspecified IP by the specified IP")
)
type udpConnMap struct {
portMap map[int][]*UDPConn
mutex sync.RWMutex
}
func newUDPConnMap() *udpConnMap {
return &udpConnMap{
portMap: map[int][]*UDPConn{},
}
}
func (m *udpConnMap) insert(conn *UDPConn) error {
m.mutex.Lock()
defer m.mutex.Unlock()
udpAddr := conn.LocalAddr().(*net.UDPAddr)
// check if the port has a listener
conns, ok := m.portMap[udpAddr.Port]
if ok {
if udpAddr.IP.IsUnspecified() {
return errAddressAlreadyInUse
}
for _, conn := range conns {
laddr := conn.LocalAddr().(*net.UDPAddr)
if laddr.IP.IsUnspecified() || laddr.IP.Equal(udpAddr.IP) {
return errAddressAlreadyInUse
}
}
conns = append(conns, conn)
} else {
conns = []*UDPConn{conn}
}
m.portMap[udpAddr.Port] = conns
return nil
}
func (m *udpConnMap) find(addr net.Addr) (*UDPConn, bool) {
m.mutex.Lock() // could be RLock, but we have delete() op
defer m.mutex.Unlock()
udpAddr := addr.(*net.UDPAddr)
if conns, ok := m.portMap[udpAddr.Port]; ok {
if udpAddr.IP.IsUnspecified() {
// pick the first one appears in the iteration
if len(conns) == 0 {
// This can't happen!
delete(m.portMap, udpAddr.Port)
return nil, false
}
return conns[0], true
}
for _, conn := range conns {
laddr := conn.LocalAddr().(*net.UDPAddr)
if laddr.IP.IsUnspecified() || laddr.IP.Equal(udpAddr.IP) {
return conn, ok
}
}
}
return nil, false
}
func (m *udpConnMap) delete(addr net.Addr) error {
m.mutex.Lock()
defer m.mutex.Unlock()
udpAddr := addr.(*net.UDPAddr)
conns, ok := m.portMap[udpAddr.Port]
if !ok {
return errNoSuchUDPConn
}
if udpAddr.IP.IsUnspecified() {
// remove all from this port
delete(m.portMap, udpAddr.Port)
return nil
}
newConns := []*UDPConn{}
for _, conn := range conns {
laddr := conn.LocalAddr().(*net.UDPAddr)
if laddr.IP.IsUnspecified() {
// This can't happen!
return errCannotRemoveUnspecifiedIP
}
if laddr.IP.Equal(udpAddr.IP) {
continue
}
newConns = append(newConns, conn)
}
if len(newConns) == 0 {
delete(m.portMap, udpAddr.Port)
} else {
m.portMap[udpAddr.Port] = newConns
}
return nil
}
// size returns the number of UDPConns (UDP listeners)
func (m *udpConnMap) size() int {
m.mutex.RLock()
defer m.mutex.RUnlock()
n := 0
for _, conns := range m.portMap {
n += len(conns)
}
return n
}

75
vendor/github.com/pion/transport/vnet/delay_filter.go generated vendored Normal file
View File

@@ -0,0 +1,75 @@
package vnet
import (
"context"
"time"
)
// DelayFilter delays outgoing packets by the given delay. Run must be called
// before any packets will be forwarded.
type DelayFilter struct {
NIC
delay time.Duration
push chan struct{}
queue *chunkQueue
}
type timedChunk struct {
Chunk
deadline time.Time
}
// NewDelayFilter creates a new DelayFilter with the given nic and delay.
func NewDelayFilter(nic NIC, delay time.Duration) (*DelayFilter, error) {
return &DelayFilter{
NIC: nic,
delay: delay,
push: make(chan struct{}),
queue: newChunkQueue(0, 0),
}, nil
}
func (f *DelayFilter) onInboundChunk(c Chunk) {
f.queue.push(timedChunk{
Chunk: c,
deadline: time.Now().Add(f.delay),
})
f.push <- struct{}{}
}
// 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.
func (f *DelayFilter) Run(ctx context.Context) {
timer := time.NewTimer(0)
for {
select {
case <-ctx.Done():
return
case <-f.push:
next := f.queue.peek().(timedChunk)
if !timer.Stop() {
<-timer.C
}
timer.Reset(time.Until(next.deadline))
case now := <-timer.C:
next := f.queue.peek()
if next == nil {
timer.Reset(time.Minute)
continue
}
if n, ok := next.(timedChunk); ok && n.deadline.Before(now) {
f.queue.pop() // ignore result because we already got and casted it from peek
f.NIC.onInboundChunk(n.Chunk)
}
next = f.queue.peek()
if next == nil {
timer.Reset(time.Minute)
continue
}
if n, ok := next.(timedChunk); ok {
timer.Reset(time.Until(n.deadline))
}
}
}
}

19
vendor/github.com/pion/transport/vnet/errors.go generated vendored Normal file
View File

@@ -0,0 +1,19 @@
package vnet
type timeoutError struct {
msg string
}
func newTimeoutError(msg string) error {
return &timeoutError{
msg: msg,
}
}
func (e *timeoutError) Error() string {
return e.msg
}
func (e *timeoutError) Timeout() bool {
return true
}

40
vendor/github.com/pion/transport/vnet/interface.go generated vendored Normal file
View File

@@ -0,0 +1,40 @@
package vnet
import (
"errors"
"net"
)
var errNoAddressAssigned = errors.New("no address assigned")
// See: https://play.golang.org/p/nBO9KGYEziv
// InterfaceBase ...
type InterfaceBase net.Interface
// Interface ...
type Interface struct {
InterfaceBase
addrs []net.Addr
}
// NewInterface ...
func NewInterface(ifc net.Interface) *Interface {
return &Interface{
InterfaceBase: InterfaceBase(ifc),
addrs: nil,
}
}
// AddAddr ...
func (ifc *Interface) AddAddr(addr net.Addr) {
ifc.addrs = append(ifc.addrs, addr)
}
// Addrs ...
func (ifc *Interface) Addrs() ([]net.Addr, error) {
if len(ifc.addrs) == 0 {
return nil, errNoAddressAssigned
}
return ifc.addrs, nil
}

33
vendor/github.com/pion/transport/vnet/loss_filter.go generated vendored Normal file
View File

@@ -0,0 +1,33 @@
package vnet
import (
"math/rand"
"time"
)
// LossFilter is a wrapper around NICs, that drops some of the packets passed to
// onInboundChunk
type LossFilter struct {
NIC
chance int
}
// 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.
func NewLossFilter(nic NIC, chance int) (*LossFilter, error) {
f := &LossFilter{
NIC: nic,
chance: chance,
}
rand.Seed(time.Now().UTC().UnixNano())
return f, nil
}
func (f *LossFilter) onInboundChunk(c Chunk) {
if rand.Intn(100) < f.chance { //nolint:gosec
return
}
f.NIC.onInboundChunk(c)
}

338
vendor/github.com/pion/transport/vnet/nat.go generated vendored Normal file
View File

@@ -0,0 +1,338 @@
package vnet
import (
"errors"
"fmt"
"net"
"sync"
"time"
"github.com/pion/logging"
)
var (
errNATRequriesMapping = errors.New("1:1 NAT requires more than one mapping")
errMismatchLengthIP = errors.New("length mismtach between mappedIPs and localIPs")
errNonUDPTranslationNotSupported = errors.New("non-udp translation is not supported yet")
errNoAssociatedLocalAddress = errors.New("no associated local address")
errNoNATBindingFound = errors.New("no NAT binding found")
errHasNoPermission = errors.New("has no permission")
)
// 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
type EndpointDependencyType uint8
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
)
// NATMode defines basic behavior of the NAT
type NATMode uint8
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
)
const (
defaultNATMappingLifeTime = 30 * time.Second
)
// NATType has a set of parameters that define the behavior of NAT.
type NATType struct {
Mode NATMode
MappingBehavior EndpointDependencyType
FilteringBehavior EndpointDependencyType
Hairpining bool // Not implemented yet
PortPreservation bool // Not implemented yet
MappingLifeTime time.Duration
}
type natConfig struct {
name string
natType NATType
mappedIPs []net.IP // mapped IPv4
localIPs []net.IP // local IPv4, required only when the mode is NATModeNAT1To1
loggerFactory logging.LoggerFactory
}
type mapping struct {
proto string // "udp" or "tcp"
local string // "<local-ip>:<local-port>"
mapped string // "<mapped-ip>:<mapped-port>"
bound string // key: "[<remote-ip>[:<remote-port>]]"
filters map[string]struct{} // key: "[<remote-ip>[:<remote-port>]]"
expires time.Time // time to expire
}
type networkAddressTranslator struct {
name string
natType NATType
mappedIPs []net.IP // mapped IPv4
localIPs []net.IP // local IPv4, required only when the mode is NATModeNAT1To1
outboundMap map[string]*mapping // key: "<proto>:<local-ip>:<local-port>[:remote-ip[:remote-port]]
inboundMap map[string]*mapping // key: "<proto>:<mapped-ip>:<mapped-port>"
udpPortCounter int
mutex sync.RWMutex
log logging.LeveledLogger
}
func newNAT(config *natConfig) (*networkAddressTranslator, error) {
natType := config.natType
if natType.Mode == NATModeNAT1To1 {
// 1:1 NAT behavior
natType.MappingBehavior = EndpointIndependent
natType.FilteringBehavior = EndpointIndependent
natType.PortPreservation = true
natType.MappingLifeTime = 0
if len(config.mappedIPs) == 0 {
return nil, errNATRequriesMapping
}
if len(config.mappedIPs) != len(config.localIPs) {
return nil, errMismatchLengthIP
}
} else {
// Normal (NAPT) behavior
natType.Mode = NATModeNormal
if natType.MappingLifeTime == 0 {
natType.MappingLifeTime = defaultNATMappingLifeTime
}
}
return &networkAddressTranslator{
name: config.name,
natType: natType,
mappedIPs: config.mappedIPs,
localIPs: config.localIPs,
outboundMap: map[string]*mapping{},
inboundMap: map[string]*mapping{},
log: config.loggerFactory.NewLogger("vnet"),
}, nil
}
func (n *networkAddressTranslator) getPairedMappedIP(locIP net.IP) net.IP {
for i, ip := range n.localIPs {
if ip.Equal(locIP) {
return n.mappedIPs[i]
}
}
return nil
}
func (n *networkAddressTranslator) getPairedLocalIP(mappedIP net.IP) net.IP {
for i, ip := range n.mappedIPs {
if ip.Equal(mappedIP) {
return n.localIPs[i]
}
}
return nil
}
func (n *networkAddressTranslator) translateOutbound(from Chunk) (Chunk, error) {
n.mutex.Lock()
defer n.mutex.Unlock()
to := from.Clone()
if from.Network() == udpString {
if n.natType.Mode == NATModeNAT1To1 {
// 1:1 NAT behavior
srcAddr := from.SourceAddr().(*net.UDPAddr)
srcIP := n.getPairedMappedIP(srcAddr.IP)
if srcIP == nil {
n.log.Debugf("[%s] drop outbound chunk %s with not route", n.name, from.String())
return nil, nil // silently discard
}
srcPort := srcAddr.Port
if err := to.setSourceAddr(fmt.Sprintf("%s:%d", srcIP.String(), srcPort)); err != nil {
return nil, err
}
} else {
// Normal (NAPT) behavior
var bound, filterKey string
switch n.natType.MappingBehavior {
case EndpointIndependent:
bound = ""
case EndpointAddrDependent:
bound = from.getDestinationIP().String()
case EndpointAddrPortDependent:
bound = from.DestinationAddr().String()
}
switch n.natType.FilteringBehavior {
case EndpointIndependent:
filterKey = ""
case EndpointAddrDependent:
filterKey = from.getDestinationIP().String()
case EndpointAddrPortDependent:
filterKey = from.DestinationAddr().String()
}
oKey := fmt.Sprintf("udp:%s:%s", from.SourceAddr().String(), bound)
m := n.findOutboundMapping(oKey)
if m == nil {
// Create a new mapping
mappedPort := 0xC000 + n.udpPortCounter
n.udpPortCounter++
m = &mapping{
proto: from.SourceAddr().Network(),
local: from.SourceAddr().String(),
bound: bound,
mapped: fmt.Sprintf("%s:%d", n.mappedIPs[0].String(), mappedPort),
filters: map[string]struct{}{},
expires: time.Now().Add(n.natType.MappingLifeTime),
}
n.outboundMap[oKey] = m
iKey := fmt.Sprintf("udp:%s", m.mapped)
n.log.Debugf("[%s] created a new NAT binding oKey=%s iKey=%s\n",
n.name,
oKey,
iKey)
m.filters[filterKey] = struct{}{}
n.log.Debugf("[%s] permit access from %s to %s\n", n.name, filterKey, m.mapped)
n.inboundMap[iKey] = m
} else if _, ok := m.filters[filterKey]; !ok {
n.log.Debugf("[%s] permit access from %s to %s\n", n.name, filterKey, m.mapped)
m.filters[filterKey] = struct{}{}
}
if err := to.setSourceAddr(m.mapped); err != nil {
return nil, err
}
}
n.log.Debugf("[%s] translate outbound chunk from %s to %s", n.name, from.String(), to.String())
return to, nil
}
return nil, errNonUDPTranslationNotSupported
}
func (n *networkAddressTranslator) translateInbound(from Chunk) (Chunk, error) {
n.mutex.Lock()
defer n.mutex.Unlock()
to := from.Clone()
if from.Network() == udpString {
if n.natType.Mode == NATModeNAT1To1 {
// 1:1 NAT behavior
dstAddr := from.DestinationAddr().(*net.UDPAddr)
dstIP := n.getPairedLocalIP(dstAddr.IP)
if dstIP == nil {
return nil, fmt.Errorf("drop %s as %w", from.String(), errNoAssociatedLocalAddress)
}
dstPort := from.DestinationAddr().(*net.UDPAddr).Port
if err := to.setDestinationAddr(fmt.Sprintf("%s:%d", dstIP, dstPort)); err != nil {
return nil, err
}
} else {
// Normal (NAPT) behavior
iKey := fmt.Sprintf("udp:%s", from.DestinationAddr().String())
m := n.findInboundMapping(iKey)
if m == nil {
return nil, fmt.Errorf("drop %s as %w", from.String(), errNoNATBindingFound)
}
var filterKey string
switch n.natType.FilteringBehavior {
case EndpointIndependent:
filterKey = ""
case EndpointAddrDependent:
filterKey = from.getSourceIP().String()
case EndpointAddrPortDependent:
filterKey = from.SourceAddr().String()
}
if _, ok := m.filters[filterKey]; !ok {
return nil, fmt.Errorf("drop %s as the remote %s %w", from.String(), filterKey, errHasNoPermission)
}
// See RFC 4847 Section 4.3. Mapping Refresh
// a) Inbound refresh may be useful for applications with no outgoing
// UDP traffic. However, allowing inbound refresh may allow an
// external attacker or misbehaving application to keep a mapping
// alive indefinitely. This may be a security risk. Also, if the
// process is repeated with different ports, over time, it could
// use up all the ports on the NAT.
if err := to.setDestinationAddr(m.local); err != nil {
return nil, err
}
}
n.log.Debugf("[%s] translate inbound chunk from %s to %s", n.name, from.String(), to.String())
return to, nil
}
return nil, errNonUDPTranslationNotSupported
}
// caller must hold the mutex
func (n *networkAddressTranslator) findOutboundMapping(oKey string) *mapping {
now := time.Now()
m, ok := n.outboundMap[oKey]
if ok {
// check if this mapping is expired
if now.After(m.expires) {
n.removeMapping(m)
m = nil // expired
} else {
m.expires = time.Now().Add(n.natType.MappingLifeTime)
}
}
return m
}
// caller must hold the mutex
func (n *networkAddressTranslator) findInboundMapping(iKey string) *mapping {
now := time.Now()
m, ok := n.inboundMap[iKey]
if !ok {
return nil
}
// check if this mapping is expired
if now.After(m.expires) {
n.removeMapping(m)
return nil
}
return m
}
// caller must hold the mutex
func (n *networkAddressTranslator) removeMapping(m *mapping) {
oKey := fmt.Sprintf("%s:%s:%s", m.proto, m.local, m.bound)
iKey := fmt.Sprintf("%s:%s", m.proto, m.mapped)
delete(n.outboundMap, oKey)
delete(n.inboundMap, iKey)
}

677
vendor/github.com/pion/transport/vnet/net.go generated vendored Normal file
View File

@@ -0,0 +1,677 @@
package vnet
import (
"encoding/binary"
"errors"
"fmt"
"math/rand"
"net"
"strconv"
"strings"
"sync"
)
const (
lo0String = "lo0String"
udpString = "udp"
)
var (
macAddrCounter uint64 = 0xBEEFED910200 //nolint:gochecknoglobals
errNoInterface = errors.New("no interface is available")
errNotFound = errors.New("not found")
errUnexpectedNetwork = errors.New("unexpected network")
errCantAssignRequestedAddr = errors.New("can't assign requested address")
errUnknownNetwork = errors.New("unknown network")
errNoRouterLinked = errors.New("no router linked")
errInvalidPortNumber = errors.New("invalid port number")
errUnexpectedTypeSwitchFailure = errors.New("unexpected type-switch failure")
errBindFailerFor = errors.New("bind failed for")
errEndPortLessThanStart = errors.New("end port is less than the start")
errPortSpaceExhausted = errors.New("port space exhausted")
errVNetDisabled = errors.New("vnet is not enabled")
)
func newMACAddress() net.HardwareAddr {
b := make([]byte, 8)
binary.BigEndian.PutUint64(b, macAddrCounter)
macAddrCounter++
return b[2:]
}
type vNet struct {
interfaces []*Interface // read-only
staticIPs []net.IP // read-only
router *Router // read-only
udpConns *udpConnMap // read-only
mutex sync.RWMutex
}
func (v *vNet) _getInterfaces() ([]*Interface, error) {
if len(v.interfaces) == 0 {
return nil, errNoInterface
}
return v.interfaces, nil
}
func (v *vNet) getInterfaces() ([]*Interface, error) {
v.mutex.RLock()
defer v.mutex.RUnlock()
return v._getInterfaces()
}
// caller must hold the mutex (read)
func (v *vNet) _getInterface(ifName string) (*Interface, error) {
ifs, err := v._getInterfaces()
if err != nil {
return nil, err
}
for _, ifc := range ifs {
if ifc.Name == ifName {
return ifc, nil
}
}
return nil, fmt.Errorf("interface %s %w", ifName, errNotFound)
}
func (v *vNet) getInterface(ifName string) (*Interface, error) {
v.mutex.RLock()
defer v.mutex.RUnlock()
return v._getInterface(ifName)
}
// caller must hold the mutex
func (v *vNet) getAllIPAddrs(ipv6 bool) []net.IP {
ips := []net.IP{}
for _, ifc := range v.interfaces {
addrs, err := ifc.Addrs()
if err != nil {
continue
}
for _, addr := range addrs {
var ip net.IP
if ipNet, ok := addr.(*net.IPNet); ok {
ip = ipNet.IP
} else if ipAddr, ok := addr.(*net.IPAddr); ok {
ip = ipAddr.IP
} else {
continue
}
if !ipv6 {
if ip.To4() != nil {
ips = append(ips, ip)
}
}
}
}
return ips
}
func (v *vNet) setRouter(r *Router) error {
v.mutex.Lock()
defer v.mutex.Unlock()
v.router = r
return nil
}
func (v *vNet) onInboundChunk(c Chunk) {
v.mutex.Lock()
defer v.mutex.Unlock()
if c.Network() == udpString {
if conn, ok := v.udpConns.find(c.DestinationAddr()); ok {
conn.onInboundChunk(c)
}
}
}
// caller must hold the mutex
func (v *vNet) _dialUDP(network string, locAddr, remAddr *net.UDPAddr) (UDPPacketConn, error) {
// validate network
if network != udpString && network != "udp4" {
return nil, fmt.Errorf("%w: %s", errUnexpectedNetwork, network)
}
if locAddr == nil {
locAddr = &net.UDPAddr{
IP: net.IPv4zero,
}
} else if locAddr.IP == nil {
locAddr.IP = net.IPv4zero
}
// validate address. do we have that address?
if !v.hasIPAddr(locAddr.IP) {
return nil, &net.OpError{
Op: "listen",
Net: network,
Addr: locAddr,
Err: fmt.Errorf("bind: %w", errCantAssignRequestedAddr),
}
}
if locAddr.Port == 0 {
// choose randomly from the range between 5000 and 5999
port, err := v.assignPort(locAddr.IP, 5000, 5999)
if err != nil {
return nil, &net.OpError{
Op: "listen",
Net: network,
Addr: locAddr,
Err: err,
}
}
locAddr.Port = port
} else if _, ok := v.udpConns.find(locAddr); ok {
return nil, &net.OpError{
Op: "listen",
Net: network,
Addr: locAddr,
Err: fmt.Errorf("bind: %w", errAddressAlreadyInUse),
}
}
conn, err := newUDPConn(locAddr, remAddr, v)
if err != nil {
return nil, err
}
err = v.udpConns.insert(conn)
if err != nil {
return nil, err
}
return conn, nil
}
func (v *vNet) listenPacket(network string, address string) (UDPPacketConn, error) {
v.mutex.Lock()
defer v.mutex.Unlock()
locAddr, err := v.resolveUDPAddr(network, address)
if err != nil {
return nil, err
}
return v._dialUDP(network, locAddr, nil)
}
func (v *vNet) listenUDP(network string, locAddr *net.UDPAddr) (UDPPacketConn, error) {
v.mutex.Lock()
defer v.mutex.Unlock()
return v._dialUDP(network, locAddr, nil)
}
func (v *vNet) dialUDP(network string, locAddr, remAddr *net.UDPAddr) (UDPPacketConn, error) {
v.mutex.Lock()
defer v.mutex.Unlock()
return v._dialUDP(network, locAddr, remAddr)
}
func (v *vNet) dial(network string, address string) (UDPPacketConn, error) {
v.mutex.Lock()
defer v.mutex.Unlock()
remAddr, err := v.resolveUDPAddr(network, address)
if err != nil {
return nil, err
}
// Determine source address
srcIP := v.determineSourceIP(nil, remAddr.IP)
locAddr := &net.UDPAddr{IP: srcIP, Port: 0}
return v._dialUDP(network, locAddr, remAddr)
}
func (v *vNet) resolveUDPAddr(network, address string) (*net.UDPAddr, error) {
if network != udpString && network != "udp4" {
return nil, fmt.Errorf("%w %s", errUnknownNetwork, network)
}
host, sPort, err := net.SplitHostPort(address)
if err != nil {
return nil, err
}
// Check if host is a domain name
ip := net.ParseIP(host)
if ip == nil {
host = strings.ToLower(host)
if host == "localhost" {
ip = net.IPv4(127, 0, 0, 1)
} else {
// host is a domain name. resolve IP address by the name
if v.router == nil {
return nil, errNoRouterLinked
}
ip, err = v.router.resolver.lookUp(host)
if err != nil {
return nil, err
}
}
}
port, err := strconv.Atoi(sPort)
if err != nil {
return nil, errInvalidPortNumber
}
udpAddr := &net.UDPAddr{
IP: ip,
Port: port,
}
return udpAddr, nil
}
func (v *vNet) write(c Chunk) error {
if c.Network() == udpString {
if udp, ok := c.(*chunkUDP); ok {
if c.getDestinationIP().IsLoopback() {
if conn, ok := v.udpConns.find(udp.DestinationAddr()); ok {
conn.onInboundChunk(udp)
}
return nil
}
} else {
return errUnexpectedTypeSwitchFailure
}
}
if v.router == nil {
return errNoRouterLinked
}
v.router.push(c)
return nil
}
func (v *vNet) onClosed(addr net.Addr) {
if addr.Network() == udpString {
//nolint:errcheck
v.udpConns.delete(addr) // #nosec
}
}
// This method determines the srcIP based on the dstIP when locIP
// is any IP address ("0.0.0.0" or "::"). If locIP is a non-any addr,
// this method simply returns locIP.
// caller must hold the mutex
func (v *vNet) determineSourceIP(locIP, dstIP net.IP) net.IP {
if locIP != nil && !locIP.IsUnspecified() {
return locIP
}
var srcIP net.IP
if dstIP.IsLoopback() {
srcIP = net.ParseIP("127.0.0.1")
} else {
ifc, err2 := v._getInterface("eth0")
if err2 != nil {
return nil
}
addrs, err2 := ifc.Addrs()
if err2 != nil {
return nil
}
if len(addrs) == 0 {
return nil
}
var findIPv4 bool
if locIP != nil {
findIPv4 = (locIP.To4() != nil)
} else {
findIPv4 = (dstIP.To4() != nil)
}
for _, addr := range addrs {
ip := addr.(*net.IPNet).IP
if findIPv4 {
if ip.To4() != nil {
srcIP = ip
break
}
} else {
if ip.To4() == nil {
srcIP = ip
break
}
}
}
}
return srcIP
}
// caller must hold the mutex
func (v *vNet) hasIPAddr(ip net.IP) bool { //nolint:gocognit
for _, ifc := range v.interfaces {
if addrs, err := ifc.Addrs(); err == nil {
for _, addr := range addrs {
var locIP net.IP
if ipNet, ok := addr.(*net.IPNet); ok {
locIP = ipNet.IP
} else if ipAddr, ok := addr.(*net.IPAddr); ok {
locIP = ipAddr.IP
} else {
continue
}
switch ip.String() {
case "0.0.0.0":
if locIP.To4() != nil {
return true
}
case "::":
if locIP.To4() == nil {
return true
}
default:
if locIP.Equal(ip) {
return true
}
}
}
}
}
return false
}
// caller must hold the mutex
func (v *vNet) allocateLocalAddr(ip net.IP, port int) error {
// gather local IP addresses to bind
var ips []net.IP
if ip.IsUnspecified() {
ips = v.getAllIPAddrs(ip.To4() == nil)
} else if v.hasIPAddr(ip) {
ips = []net.IP{ip}
}
if len(ips) == 0 {
return fmt.Errorf("%w %s", errBindFailerFor, ip.String())
}
// check if all these transport addresses are not in use
for _, ip2 := range ips {
addr := &net.UDPAddr{
IP: ip2,
Port: port,
}
if _, ok := v.udpConns.find(addr); ok {
return &net.OpError{
Op: "bind",
Net: udpString,
Addr: addr,
Err: fmt.Errorf("bind: %w", errAddressAlreadyInUse),
}
}
}
return nil
}
// caller must hold the mutex
func (v *vNet) assignPort(ip net.IP, start, end int) (int, error) {
// choose randomly from the range between start and end (inclusive)
if end < start {
return -1, errEndPortLessThanStart
}
space := end + 1 - start
offset := rand.Intn(space) //nolint:gosec
for i := 0; i < space; i++ {
port := ((offset + i) % space) + start
err := v.allocateLocalAddr(ip, port)
if err == nil {
return port, nil
}
}
return -1, errPortSpaceExhausted
}
// NetConfig is a bag of configuration parameters passed to NewNet().
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
}
// Net represents a local network stack euivalent to a set of layers from NIC
// up to the transport (UDP / TCP) layer.
type Net struct {
v *vNet
ifs []*Interface
}
// NewNet creates an instance of Net.
// If config is nil, the virtual network is disabled. (uses corresponding
// net.Xxxx() operations.
// 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 NewNet(config *NetConfig) *Net {
if config == nil {
ifs := []*Interface{}
if orgIfs, err := net.Interfaces(); err == nil {
for _, orgIfc := range orgIfs {
ifc := NewInterface(orgIfc)
if addrs, err := orgIfc.Addrs(); err == nil {
for _, addr := range addrs {
ifc.AddAddr(addr)
}
}
ifs = append(ifs, ifc)
}
}
return &Net{ifs: ifs}
}
lo0 := NewInterface(net.Interface{
Index: 1,
MTU: 16384,
Name: lo0String,
HardwareAddr: nil,
Flags: net.FlagUp | net.FlagLoopback | net.FlagMulticast,
})
lo0.AddAddr(&net.IPNet{
IP: net.ParseIP("127.0.0.1"),
Mask: net.CIDRMask(8, 32),
})
eth0 := NewInterface(net.Interface{
Index: 2,
MTU: 1500,
Name: "eth0",
HardwareAddr: newMACAddress(),
Flags: net.FlagUp | net.FlagMulticast,
})
var staticIPs []net.IP
for _, ipStr := range config.StaticIPs {
if ip := net.ParseIP(ipStr); ip != nil {
staticIPs = append(staticIPs, ip)
}
}
if len(config.StaticIP) > 0 {
if ip := net.ParseIP(config.StaticIP); ip != nil {
staticIPs = append(staticIPs, ip)
}
}
v := &vNet{
interfaces: []*Interface{lo0, eth0},
staticIPs: staticIPs,
udpConns: newUDPConnMap(),
}
return &Net{
v: v,
}
}
// Interfaces returns a list of the system's network interfaces.
func (n *Net) Interfaces() ([]*Interface, error) {
if n.v == nil {
return n.ifs, nil
}
return n.v.getInterfaces()
}
// InterfaceByName returns the interface specified by name.
func (n *Net) InterfaceByName(name string) (*Interface, error) {
if n.v == nil {
for _, ifc := range n.ifs {
if ifc.Name == name {
return ifc, nil
}
}
return nil, fmt.Errorf("interface %s %w", name, errNotFound)
}
return n.v.getInterface(name)
}
// ListenPacket announces on the local network address.
func (n *Net) ListenPacket(network string, address string) (net.PacketConn, error) {
if n.v == nil {
return net.ListenPacket(network, address)
}
return n.v.listenPacket(network, address)
}
// ListenUDP acts like ListenPacket for UDP networks.
func (n *Net) ListenUDP(network string, locAddr *net.UDPAddr) (UDPPacketConn, error) {
if n.v == nil {
return net.ListenUDP(network, locAddr)
}
return n.v.listenUDP(network, locAddr)
}
// Dial connects to the address on the named network.
func (n *Net) Dial(network, address string) (net.Conn, error) {
if n.v == nil {
return net.Dial(network, address)
}
return n.v.dial(network, address)
}
// CreateDialer creates an instance of vnet.Dialer
func (n *Net) CreateDialer(dialer *net.Dialer) Dialer {
if n.v == nil {
return &vDialer{
dialer: dialer,
}
}
return &vDialer{
dialer: dialer,
v: n.v,
}
}
// DialUDP acts like Dial for UDP networks.
func (n *Net) DialUDP(network string, laddr, raddr *net.UDPAddr) (UDPPacketConn, error) {
if n.v == nil {
return net.DialUDP(network, laddr, raddr)
}
return n.v.dialUDP(network, laddr, raddr)
}
// ResolveUDPAddr returns an address of UDP end point.
func (n *Net) ResolveUDPAddr(network, address string) (*net.UDPAddr, error) {
if n.v == nil {
return net.ResolveUDPAddr(network, address)
}
return n.v.resolveUDPAddr(network, address)
}
func (n *Net) getInterface(ifName string) (*Interface, error) {
if n.v == nil {
return nil, errVNetDisabled
}
return n.v.getInterface(ifName)
}
func (n *Net) setRouter(r *Router) error {
if n.v == nil {
return errVNetDisabled
}
return n.v.setRouter(r)
}
func (n *Net) onInboundChunk(c Chunk) {
if n.v == nil {
return
}
n.v.onInboundChunk(c)
}
func (n *Net) getStaticIPs() []net.IP {
if n.v == nil {
return nil
}
return n.v.staticIPs
}
// IsVirtual tests if the virtual network is enabled.
func (n *Net) IsVirtual() bool {
return n.v != nil
}
// Dialer is identical to net.Dialer excepts that its methods
// (Dial, DialContext) are overridden to use virtual network.
// Use vnet.CreateDialer() to create an instance of this Dialer.
type Dialer interface {
Dial(network, address string) (net.Conn, error)
}
type vDialer struct {
dialer *net.Dialer
v *vNet
}
func (d *vDialer) Dial(network, address string) (net.Conn, error) {
if d.v == nil {
return d.dialer.Dial(network, address)
}
return d.v.dial(network, address)
}

89
vendor/github.com/pion/transport/vnet/resolver.go generated vendored Normal file
View File

@@ -0,0 +1,89 @@
package vnet
import (
"errors"
"fmt"
"net"
"sync"
"github.com/pion/logging"
)
var (
errHostnameEmpty = errors.New("host name must not be empty")
errFailedtoParseIPAddr = errors.New("failed to parse IP address")
)
type resolverConfig struct {
LoggerFactory logging.LoggerFactory
}
type resolver struct {
parent *resolver // read-only
hosts map[string]net.IP // requires mutex
mutex sync.RWMutex // thread-safe
log logging.LeveledLogger // read-only
}
func newResolver(config *resolverConfig) *resolver {
r := &resolver{
hosts: map[string]net.IP{},
log: config.LoggerFactory.NewLogger("vnet"),
}
if err := r.addHost("localhost", "127.0.0.1"); err != nil {
r.log.Warn("failed to add localhost to resolver")
}
return r
}
func (r *resolver) setParent(parent *resolver) {
r.mutex.Lock()
defer r.mutex.Unlock()
r.parent = parent
}
func (r *resolver) addHost(name string, ipAddr string) error {
r.mutex.Lock()
defer r.mutex.Unlock()
if len(name) == 0 {
return errHostnameEmpty
}
ip := net.ParseIP(ipAddr)
if ip == nil {
return fmt.Errorf("%w \"%s\"", errFailedtoParseIPAddr, ipAddr)
}
r.hosts[name] = ip
return nil
}
func (r *resolver) lookUp(hostName string) (net.IP, error) {
ip := func() net.IP {
r.mutex.RLock()
defer r.mutex.RUnlock()
if ip2, ok := r.hosts[hostName]; ok {
return ip2
}
return nil
}()
if ip != nil {
return ip, nil
}
// mutex must be unlocked before calling into parent resolver
if r.parent != nil {
return r.parent.lookUp(hostName)
}
return nil, &net.DNSError{
Err: "host not found",
Name: hostName,
Server: "vnet resolver",
IsTimeout: false,
IsTemporary: false,
}
}

620
vendor/github.com/pion/transport/vnet/router.go generated vendored Normal file
View File

@@ -0,0 +1,620 @@
package vnet
import (
"errors"
"fmt"
"math/rand"
"net"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/pion/logging"
)
const (
defaultRouterQueueSize = 0 // unlimited
)
var (
errInvalidLocalIPinStaticIPs = errors.New("invalid local IP in StaticIPs")
errLocalIPBeyondStaticIPsSubset = errors.New("mapped in StaticIPs is beyond subnet")
errLocalIPNoStaticsIPsAssociated = errors.New("all StaticIPs must have associated local IPs")
errRouterAlreadyStarted = errors.New("router already started")
errRouterAlreadyStopped = errors.New("router already stopped")
errStaticIPisBeyondSubnet = errors.New("static IP is beyond subnet")
errAddressSpaceExhausted = errors.New("address space exhausted")
errNoIPAddrEth0 = errors.New("no IP address is assigned for eth0")
)
// Generate a unique router name
var assignRouterName = func() func() string { //nolint:gochecknoglobals
var routerIDCtr uint64
return func() string {
n := atomic.AddUint64(&routerIDCtr, 1)
return fmt.Sprintf("router%d", n)
}
}()
// 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
}
// NIC is a nework inerface controller that interfaces Router
type NIC interface {
getInterface(ifName string) (*Interface, error)
onInboundChunk(c Chunk)
getStaticIPs() []net.IP
setRouter(r *Router) error
}
// ChunkFilter is a handler users can add to filter chunks.
// If the filter returns false, the packet will be dropped.
type ChunkFilter func(c Chunk) bool
// Router ...
type Router struct {
name string // read-only
interfaces []*Interface // read-only
ipv4Net *net.IPNet // read-only
staticIPs []net.IP // read-only
staticLocalIPs map[string]net.IP // read-only,
lastID byte // requires mutex [x], used to assign the last digit of IPv4 address
queue *chunkQueue // read-only
parent *Router // read-only
children []*Router // read-only
natType *NATType // read-only
nat *networkAddressTranslator // read-only
nics map[string]NIC // read-only
stopFunc func() // requires mutex [x]
resolver *resolver // read-only
chunkFilters []ChunkFilter // requires mutex [x]
minDelay time.Duration // requires mutex [x]
maxJitter time.Duration // requires mutex [x]
mutex sync.RWMutex // thread-safe
pushCh chan struct{} // writer requires mutex
loggerFactory logging.LoggerFactory // read-only
log logging.LeveledLogger // read-only
}
// NewRouter ...
func NewRouter(config *RouterConfig) (*Router, error) {
loggerFactory := config.LoggerFactory
log := loggerFactory.NewLogger("vnet")
_, ipv4Net, err := net.ParseCIDR(config.CIDR)
if err != nil {
return nil, err
}
queueSize := defaultRouterQueueSize
if config.QueueSize > 0 {
queueSize = config.QueueSize
}
// set up network interface, lo0
lo0 := NewInterface(net.Interface{
Index: 1,
MTU: 16384,
Name: lo0String,
HardwareAddr: nil,
Flags: net.FlagUp | net.FlagLoopback | net.FlagMulticast,
})
lo0.AddAddr(&net.IPAddr{IP: net.ParseIP("127.0.0.1"), Zone: ""})
// set up network interface, eth0
eth0 := NewInterface(net.Interface{
Index: 2,
MTU: 1500,
Name: "eth0",
HardwareAddr: newMACAddress(),
Flags: net.FlagUp | net.FlagMulticast,
})
// local host name resolver
resolver := newResolver(&resolverConfig{
LoggerFactory: config.LoggerFactory,
})
name := config.Name
if len(name) == 0 {
name = assignRouterName()
}
var staticIPs []net.IP
staticLocalIPs := map[string]net.IP{}
for _, ipStr := range config.StaticIPs {
ipPair := strings.Split(ipStr, "/")
if ip := net.ParseIP(ipPair[0]); ip != nil {
if len(ipPair) > 1 {
locIP := net.ParseIP(ipPair[1])
if locIP == nil {
return nil, errInvalidLocalIPinStaticIPs
}
if !ipv4Net.Contains(locIP) {
return nil, fmt.Errorf("local IP %s %w", locIP.String(), errLocalIPBeyondStaticIPsSubset)
}
staticLocalIPs[ip.String()] = locIP
}
staticIPs = append(staticIPs, ip)
}
}
if len(config.StaticIP) > 0 {
log.Warn("StaticIP is deprecated. Use StaticIPs instead")
if ip := net.ParseIP(config.StaticIP); ip != nil {
staticIPs = append(staticIPs, ip)
}
}
if nStaticLocal := len(staticLocalIPs); nStaticLocal > 0 {
if nStaticLocal != len(staticIPs) {
return nil, errLocalIPNoStaticsIPsAssociated
}
}
return &Router{
name: name,
interfaces: []*Interface{lo0, eth0},
ipv4Net: ipv4Net,
staticIPs: staticIPs,
staticLocalIPs: staticLocalIPs,
queue: newChunkQueue(queueSize, 0),
natType: config.NATType,
nics: map[string]NIC{},
resolver: resolver,
minDelay: config.MinDelay,
maxJitter: config.MaxJitter,
pushCh: make(chan struct{}, 1),
loggerFactory: loggerFactory,
log: log,
}, nil
}
// caller must hold the mutex
func (r *Router) getInterfaces() ([]*Interface, error) {
if len(r.interfaces) == 0 {
return nil, fmt.Errorf("%w is available", errNoInterface)
}
return r.interfaces, nil
}
func (r *Router) getInterface(ifName string) (*Interface, error) {
r.mutex.RLock()
defer r.mutex.RUnlock()
ifs, err := r.getInterfaces()
if err != nil {
return nil, err
}
for _, ifc := range ifs {
if ifc.Name == ifName {
return ifc, nil
}
}
return nil, fmt.Errorf("interface %s %w", ifName, errNotFound)
}
// Start ...
func (r *Router) Start() error {
r.mutex.Lock()
defer r.mutex.Unlock()
if r.stopFunc != nil {
return errRouterAlreadyStarted
}
cancelCh := make(chan struct{})
go func() {
loop:
for {
d, err := r.processChunks()
if err != nil {
r.log.Errorf("[%s] %s", r.name, err.Error())
break
}
if d <= 0 {
select {
case <-r.pushCh:
case <-cancelCh:
break loop
}
} else {
t := time.NewTimer(d)
select {
case <-t.C:
case <-cancelCh:
break loop
}
}
}
}()
r.stopFunc = func() {
close(cancelCh)
}
for _, child := range r.children {
if err := child.Start(); err != nil {
return err
}
}
return nil
}
// Stop ...
func (r *Router) Stop() error {
r.mutex.Lock()
defer r.mutex.Unlock()
if r.stopFunc == nil {
return errRouterAlreadyStopped
}
for _, router := range r.children {
r.mutex.Unlock()
err := router.Stop()
r.mutex.Lock()
if err != nil {
return err
}
}
r.stopFunc()
r.stopFunc = nil
return nil
}
// caller must hold the mutex
func (r *Router) addNIC(nic NIC) error {
ifc, err := nic.getInterface("eth0")
if err != nil {
return err
}
var ips []net.IP
if ips = nic.getStaticIPs(); len(ips) == 0 {
// assign an IP address
ip, err2 := r.assignIPAddress()
if err2 != nil {
return err2
}
ips = append(ips, ip)
}
for _, ip := range ips {
if !r.ipv4Net.Contains(ip) {
return fmt.Errorf("%w: %s", errStaticIPisBeyondSubnet, r.ipv4Net.String())
}
ifc.AddAddr(&net.IPNet{
IP: ip,
Mask: r.ipv4Net.Mask,
})
r.nics[ip.String()] = nic
}
if err = nic.setRouter(r); err != nil {
return err
}
return nil
}
// AddRouter adds a chile Router.
func (r *Router) AddRouter(router *Router) error {
r.mutex.Lock()
defer r.mutex.Unlock()
// Router is a NIC. Add it as a NIC so that packets are routed to this child
// router.
err := r.addNIC(router)
if err != nil {
return err
}
if err = router.setRouter(r); err != nil {
return err
}
r.children = append(r.children, router)
return nil
}
// 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 (r *Router) AddChildRouter(router *Router) error {
r.mutex.Lock()
defer r.mutex.Unlock()
if err := router.setRouter(r); err != nil {
return err
}
r.children = append(r.children, router)
return nil
}
// AddNet ...
func (r *Router) AddNet(nic NIC) error {
r.mutex.Lock()
defer r.mutex.Unlock()
return r.addNIC(nic)
}
// AddHost adds a mapping of hostname and an IP address to the local resolver.
func (r *Router) AddHost(hostName string, ipAddr string) error {
return r.resolver.addHost(hostName, ipAddr)
}
// 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 (r *Router) AddChunkFilter(filter ChunkFilter) {
r.mutex.Lock()
defer r.mutex.Unlock()
r.chunkFilters = append(r.chunkFilters, filter)
}
// caller should hold the mutex
func (r *Router) assignIPAddress() (net.IP, error) {
// See: https://stackoverflow.com/questions/14915188/ip-address-ending-with-zero
if r.lastID == 0xfe {
return nil, errAddressSpaceExhausted
}
ip := make(net.IP, 4)
copy(ip, r.ipv4Net.IP[:3])
r.lastID++
ip[3] = r.lastID
return ip, nil
}
func (r *Router) push(c Chunk) {
r.mutex.Lock()
defer r.mutex.Unlock()
r.log.Debugf("[%s] route %s", r.name, c.String())
if r.stopFunc != nil {
c.setTimestamp()
if r.queue.push(c) {
select {
case r.pushCh <- struct{}{}:
default:
}
} else {
r.log.Warnf("[%s] queue was full. dropped a chunk", r.name)
}
}
}
func (r *Router) processChunks() (time.Duration, error) {
r.mutex.Lock()
defer r.mutex.Unlock()
// Introduce jitter by delaying the processing of chunks.
if r.maxJitter > 0 {
jitter := time.Duration(rand.Int63n(int64(r.maxJitter))) //nolint:gosec
time.Sleep(jitter)
}
// cutOff
// v min delay
// |<--->|
// +------------:--
// |OOOOOOXXXXX : --> time
// +------------:--
// |<--->| now
// due
enteredAt := time.Now()
cutOff := enteredAt.Add(-r.minDelay)
var d time.Duration // the next sleep duration
for {
d = 0
c := r.queue.peek()
if c == nil {
break // no more chunk in the queue
}
// check timestamp to find if the chunk is due
if c.getTimestamp().After(cutOff) {
// There is one or more chunk in the queue but none of them are due.
// Calculate the next sleep duration here.
nextExpire := c.getTimestamp().Add(r.minDelay)
d = nextExpire.Sub(enteredAt)
break
}
var ok bool
if c, ok = r.queue.pop(); !ok {
break // no more chunk in the queue
}
blocked := false
for i := 0; i < len(r.chunkFilters); i++ {
filter := r.chunkFilters[i]
if !filter(c) {
blocked = true
break
}
}
if blocked {
continue // discard
}
dstIP := c.getDestinationIP()
// check if the desination is in our subnet
if r.ipv4Net.Contains(dstIP) {
// search for the destination NIC
var nic NIC
if nic, ok = r.nics[dstIP.String()]; !ok {
// NIC not found. drop it.
r.log.Debugf("[%s] %s unreachable", r.name, c.String())
continue
}
// found the NIC, forward the chunk to the NIC.
// call to NIC must unlock mutex
r.mutex.Unlock()
nic.onInboundChunk(c)
r.mutex.Lock()
continue
}
// the destination is outside of this subnet
// is this WAN?
if r.parent == nil {
// this WAN. No route for this chunk
r.log.Debugf("[%s] no route found for %s", r.name, c.String())
continue
}
// Pass it to the parent via NAT
toParent, err := r.nat.translateOutbound(c)
if err != nil {
return 0, err
}
if toParent == nil {
continue
}
//nolint:godox
/* FIXME: this implementation would introduce a duplicate packet!
if r.nat.natType.Hairpining {
hairpinned, err := r.nat.translateInbound(toParent)
if err != nil {
r.log.Warnf("[%s] %s", r.name, err.Error())
} else {
go func() {
r.push(hairpinned)
}()
}
}
*/
// call to parent router mutex unlock mutex
r.mutex.Unlock()
r.parent.push(toParent)
r.mutex.Lock()
}
return d, nil
}
// caller must hold the mutex
func (r *Router) setRouter(parent *Router) error {
r.parent = parent
r.resolver.setParent(parent.resolver)
// when this method is called, one or more IP address has already been assigned by
// the parent router.
ifc, err := r.getInterface("eth0")
if err != nil {
return err
}
if len(ifc.addrs) == 0 {
return errNoIPAddrEth0
}
mappedIPs := []net.IP{}
localIPs := []net.IP{}
for _, ifcAddr := range ifc.addrs {
var ip net.IP
switch addr := ifcAddr.(type) {
case *net.IPNet:
ip = addr.IP
case *net.IPAddr: // Do we really need this case?
ip = addr.IP
default:
}
if ip == nil {
continue
}
mappedIPs = append(mappedIPs, ip)
if locIP := r.staticLocalIPs[ip.String()]; locIP != nil {
localIPs = append(localIPs, locIP)
}
}
// Set up NAT here
if r.natType == nil {
r.natType = &NATType{
MappingBehavior: EndpointIndependent,
FilteringBehavior: EndpointAddrPortDependent,
Hairpining: false,
PortPreservation: false,
MappingLifeTime: 30 * time.Second,
}
}
r.nat, err = newNAT(&natConfig{
name: r.name,
natType: *r.natType,
mappedIPs: mappedIPs,
localIPs: localIPs,
loggerFactory: r.loggerFactory,
})
if err != nil {
return err
}
return nil
}
func (r *Router) onInboundChunk(c Chunk) {
fromParent, err := r.nat.translateInbound(c)
if err != nil {
r.log.Warnf("[%s] %s", r.name, err.Error())
return
}
r.push(fromParent)
}
func (r *Router) getStaticIPs() []net.IP {
return r.staticIPs
}

149
vendor/github.com/pion/transport/vnet/tbf.go generated vendored Normal file
View File

@@ -0,0 +1,149 @@
package vnet
import (
"sync"
"time"
)
const (
// Bit is a single bit
Bit = 1
// KBit is a kilobit
KBit = 1000 * Bit
// MBit is a Megabit
MBit = 1000 * KBit
)
// TokenBucketFilter implements a token bucket rate limit algorithm.
type TokenBucketFilter struct {
NIC
currentTokensInBucket int
c chan Chunk
queue *chunkQueue
queueSize int // in bytes
mutex sync.Mutex
rate int
maxBurst int
wg sync.WaitGroup
done chan struct{}
}
// TBFOption is the option type to configure a TokenBucketFilter
type TBFOption func(*TokenBucketFilter) TBFOption
// TBFQueueSizeInBytes sets the max number of bytes waiting in the queue. Can
// only be set in constructor before using the TBF.
func TBFQueueSizeInBytes(bytes int) TBFOption {
return func(t *TokenBucketFilter) TBFOption {
prev := t.queueSize
t.queueSize = bytes
return TBFQueueSizeInBytes(prev)
}
}
// TBFRate sets the bitrate of a TokenBucketFilter
func TBFRate(rate int) TBFOption {
return func(t *TokenBucketFilter) TBFOption {
t.mutex.Lock()
defer t.mutex.Unlock()
previous := t.rate
t.rate = rate
return TBFRate(previous)
}
}
// 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 TBFMaxBurst(size int) TBFOption {
return func(t *TokenBucketFilter) TBFOption {
t.mutex.Lock()
defer t.mutex.Unlock()
previous := t.maxBurst
t.maxBurst = size
return TBFMaxBurst(previous)
}
}
// Set updates a setting on the token bucket filter
func (t *TokenBucketFilter) Set(opts ...TBFOption) (previous TBFOption) {
for _, opt := range opts {
previous = opt(t)
}
return previous
}
// NewTokenBucketFilter creates and starts a new TokenBucketFilter
func NewTokenBucketFilter(n NIC, opts ...TBFOption) (*TokenBucketFilter, error) {
tbf := &TokenBucketFilter{
NIC: n,
currentTokensInBucket: 0,
c: make(chan Chunk),
queue: nil,
queueSize: 50000,
mutex: sync.Mutex{},
rate: 1 * MBit,
maxBurst: 2 * KBit,
wg: sync.WaitGroup{},
done: make(chan struct{}),
}
tbf.Set(opts...)
tbf.queue = newChunkQueue(0, tbf.queueSize)
tbf.wg.Add(1)
go tbf.run()
return tbf, nil
}
func (t *TokenBucketFilter) onInboundChunk(c Chunk) {
t.c <- c
}
func (t *TokenBucketFilter) run() {
defer t.wg.Done()
ticker := time.NewTicker(1 * time.Millisecond)
for {
select {
case <-t.done:
ticker.Stop()
t.drainQueue()
return
case <-ticker.C:
t.mutex.Lock()
if t.currentTokensInBucket < t.maxBurst {
// add (bitrate * S) / 1000 converted to bytes (divide by 8) S
// is the update interval in milliseconds
t.currentTokensInBucket += (t.rate / 1000) / 8
}
t.mutex.Unlock()
t.drainQueue()
case chunk := <-t.c:
t.queue.push(chunk)
t.drainQueue()
}
}
}
func (t *TokenBucketFilter) drainQueue() {
for {
next := t.queue.peek()
if next == nil {
break
}
tokens := len(next.UserData())
if t.currentTokensInBucket < tokens {
break
}
t.queue.pop()
t.NIC.onInboundChunk(next)
t.currentTokensInBucket -= tokens
}
}
// Close closes and stops the token bucket filter queue
func (t *TokenBucketFilter) Close() error {
close(t.done)
t.wg.Wait()
return nil
}

215
vendor/github.com/pion/transport/vnet/udpproxy.go generated vendored Normal file
View File

@@ -0,0 +1,215 @@
package vnet
import (
"context"
"net"
"sync"
"time"
)
// 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 |
// : | | : +---------+ +---------+ +--------+
// : | | ............................:
// : +--------+ :
// ...............................................
type UDPProxy struct {
// The router bind to.
router *Router
// Each vnet source, bind to a real socket to server.
// key is real server addr, which is net.Addr
// value is *aUDPProxyWorker
workers sync.Map
// For each endpoint, we never know when to start and stop proxy,
// so we stop the endpoint when timeout.
timeout time.Duration
// For utest, to mock the target real server.
// Optional, use the address of received client packet.
mockRealServerAddr *net.UDPAddr
}
// 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 NewProxy(router *Router) (*UDPProxy, error) {
v := &UDPProxy{router: router, timeout: 2 * time.Minute}
return v, nil
}
// Close the proxy, stop all workers.
func (v *UDPProxy) Close() error {
v.workers.Range(func(key, value interface{}) bool {
_ = value.(*aUDPProxyWorker).Close()
return true
})
return nil
}
// Proxy starts a worker for server, ignore if already started.
func (v *UDPProxy) Proxy(client *Net, server *net.UDPAddr) error {
// Note that even if the worker exists, it's also ok to create a same worker,
// because the router will use the last one, and the real server will see a address
// change event after we switch to the next worker.
if _, ok := v.workers.Load(server.String()); ok {
// nolint:godox // TODO: Need to restart the stopped worker?
return nil
}
// Not exists, create a new one.
worker := &aUDPProxyWorker{
router: v.router, mockRealServerAddr: v.mockRealServerAddr,
}
// Create context for cleanup.
var ctx context.Context
ctx, worker.ctxDisposeCancel = context.WithCancel(context.Background())
v.workers.Store(server.String(), worker)
return worker.Proxy(ctx, client, server)
}
// A proxy worker for a specified proxy server.
type aUDPProxyWorker struct {
router *Router
mockRealServerAddr *net.UDPAddr
// Each vnet source, bind to a real socket to server.
// key is vnet client addr, which is net.Addr
// value is *net.UDPConn
endpoints sync.Map
// For cleanup.
ctxDisposeCancel context.CancelFunc
wg sync.WaitGroup
}
func (v *aUDPProxyWorker) Close() error {
// Notify all goroutines to dispose.
v.ctxDisposeCancel()
// Wait for all goroutines quit.
v.wg.Wait()
return nil
}
func (v *aUDPProxyWorker) Proxy(ctx context.Context, client *Net, serverAddr *net.UDPAddr) error { // nolint:gocognit
// Create vnet for real server by serverAddr.
nw := NewNet(&NetConfig{
StaticIP: serverAddr.IP.String(),
})
if err := v.router.AddNet(nw); err != nil {
return err
}
// We must create a "same" vnet.UDPConn as the net.UDPConn,
// which has the same ip:port, to copy packets between them.
vnetSocket, err := nw.ListenUDP("udp4", serverAddr)
if err != nil {
return err
}
// User stop proxy, we should close the socket.
go func() {
<-ctx.Done()
_ = vnetSocket.Close()
}()
// Got new vnet client, start a new endpoint.
findEndpointBy := func(addr net.Addr) (*net.UDPConn, error) {
// Exists binding.
if value, ok := v.endpoints.Load(addr.String()); ok {
// Exists endpoint, reuse it.
return value.(*net.UDPConn), nil
}
// The real server we proxy to, for utest to mock it.
realAddr := serverAddr
if v.mockRealServerAddr != nil {
realAddr = v.mockRealServerAddr
}
// Got new vnet client, create new endpoint.
realSocket, err := net.DialUDP("udp4", nil, realAddr)
if err != nil {
return nil, err
}
// User stop proxy, we should close the socket.
go func() {
<-ctx.Done()
_ = realSocket.Close()
}()
// Bind address.
v.endpoints.Store(addr.String(), realSocket)
// Got packet from real serverAddr, we should proxy it to vnet.
v.wg.Add(1)
go func(vnetClientAddr net.Addr) {
defer v.wg.Done()
buf := make([]byte, 1500)
for {
n, _, err := realSocket.ReadFrom(buf)
if err != nil {
return
}
if n <= 0 {
continue // Drop packet
}
if _, err := vnetSocket.WriteTo(buf[:n], vnetClientAddr); err != nil {
return
}
}
}(addr)
return realSocket, nil
}
// Start a proxy goroutine.
v.wg.Add(1)
go func() {
defer v.wg.Done()
buf := make([]byte, 1500)
for {
n, addr, err := vnetSocket.ReadFrom(buf)
if err != nil {
return
}
if n <= 0 || addr == nil {
continue // Drop packet
}
realSocket, err := findEndpointBy(addr)
if err != nil {
continue // Drop packet.
}
if _, err := realSocket.Write(buf[:n]); err != nil {
return
}
}
}()
return nil
}

View File

@@ -0,0 +1,44 @@
package vnet
import (
"fmt"
"net"
)
// Deliver directly send packet to vnet or real-server.
// For example, we can use this API to simulate the REPLAY ATTACK.
func (v *UDPProxy) Deliver(sourceAddr, destAddr net.Addr, b []byte) (nn int, err error) {
v.workers.Range(func(key, value interface{}) bool {
if nn, err = value.(*aUDPProxyWorker).Deliver(sourceAddr, destAddr, b); err != nil {
return false // Fail, abort.
} else if nn == len(b) {
return false // Done.
}
return true // Deliver by next worker.
})
return
}
func (v *aUDPProxyWorker) Deliver(sourceAddr, destAddr net.Addr, b []byte) (nn int, err error) {
addr, ok := sourceAddr.(*net.UDPAddr)
if !ok {
return 0, fmt.Errorf("invalid addr %v", sourceAddr) // nolint:goerr113
}
// nolint:godox // TODO: Support deliver packet from real server to vnet.
// If packet is from vnet, proxy to real server.
var realSocket *net.UDPConn
if value, ok := v.endpoints.Load(addr.String()); !ok {
return 0, nil
} else { // nolint:golint
realSocket = value.(*net.UDPConn)
}
// Send to real server.
if _, err := realSocket.Write(b); err != nil {
return 0, err
}
return len(b), nil
}

2
vendor/github.com/pion/transport/vnet/vnet.go generated vendored Normal file
View File

@@ -0,0 +1,2 @@
// Package vnet provides a virtual network layer for pion
package vnet