lib/connections: Add KCP support (fixes #804)

GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/3489
This commit is contained in:
Audrius Butkevicius
2017-03-07 12:44:16 +00:00
committed by Jakob Borg
parent 151004d645
commit 0da0774ce4
181 changed files with 30946 additions and 106 deletions

21
vendor/github.com/AudriusButkevicius/pfilter/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2016 Audrius Butkevicius
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

95
vendor/github.com/AudriusButkevicius/pfilter/conn.go generated vendored Normal file
View File

@@ -0,0 +1,95 @@
package pfilter
import (
"net"
"sync/atomic"
"time"
)
type FilteredConn struct {
source *PacketFilter
priority int
recvBuffer chan packet
filter Filter
deadline atomic.Value
closed chan struct{}
}
// LocalAddr returns the local address
func (r *FilteredConn) LocalAddr() net.Addr {
return r.source.LocalAddr()
}
// SetReadDeadline sets a read deadline
func (r *FilteredConn) SetReadDeadline(t time.Time) error {
r.deadline.Store(t)
return nil
}
// SetWriteDeadline sets a write deadline
func (r *FilteredConn) SetWriteDeadline(t time.Time) error {
return r.source.SetWriteDeadline(t)
}
// SetDeadline sets a read and a write deadline
func (r *FilteredConn) SetDeadline(t time.Time) error {
r.SetReadDeadline(t)
return r.SetWriteDeadline(t)
}
// WriteTo writes bytes to the given address
func (r *FilteredConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
select {
case <-r.closed:
return 0, errClosed
default:
}
if r.filter != nil {
r.filter.Outgoing(b, addr)
}
return r.source.WriteTo(b, addr)
}
// ReadFrom reads from the filtered connection
func (r *FilteredConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
select {
case <-r.closed:
return 0, nil, errClosed
default:
}
var timeout <-chan time.Time
if deadline, ok := r.deadline.Load().(time.Time); ok && !deadline.IsZero() {
timer := time.NewTimer(deadline.Sub(time.Now()))
timeout = timer.C
defer timer.Stop()
}
select {
case <-timeout:
return 0, nil, &timeoutError{}
case pkt := <-r.recvBuffer:
copy(b[:pkt.n], pkt.buf)
bufPool.Put(pkt.buf[:maxPacketSize])
return pkt.n, pkt.addr, pkt.err
case <-r.closed:
return 0, nil, errClosed
}
}
// Close closes the filtered connection, removing it's filters
func (r *FilteredConn) Close() error {
select {
case <-r.closed:
return errClosed
default:
}
close(r.closed)
r.source.removeConn(r)
return nil
}

134
vendor/github.com/AudriusButkevicius/pfilter/filter.go generated vendored Normal file
View File

@@ -0,0 +1,134 @@
package pfilter
import (
"net"
"sort"
"sync"
"sync/atomic"
)
// Filter object receives all data sent out on the Outgoing callback,
// and is expected to decide if it wants to receive the packet or not via
// the Receive callback
type Filter interface {
Outgoing([]byte, net.Addr)
ClaimIncoming([]byte, net.Addr) bool
}
// NewPacketFilter creates a packet filter object wrapping the given packet
// connection.
func NewPacketFilter(conn net.PacketConn) *PacketFilter {
d := &PacketFilter{
PacketConn: conn,
}
return d
}
// PacketFilter embeds a net.PacketConn to perform the filtering.
type PacketFilter struct {
net.PacketConn
conns []*FilteredConn
mut sync.Mutex
dropped uint64
overflow uint64
}
// NewConn returns a new net.PacketConn object which filters packets based
// on the provided filter. If filter is nil, the connection will receive all
// packets. Priority decides which connection gets the ability to claim the packet.
func (d *PacketFilter) NewConn(priority int, filter Filter) net.PacketConn {
conn := &FilteredConn{
priority: priority,
source: d,
recvBuffer: make(chan packet, 256),
filter: filter,
closed: make(chan struct{}),
}
d.mut.Lock()
d.conns = append(d.conns, conn)
sort.Sort(filteredConnList(d.conns))
d.mut.Unlock()
return conn
}
func (d *PacketFilter) removeConn(r *FilteredConn) {
d.mut.Lock()
for i, conn := range d.conns {
if conn == r {
copy(d.conns[i:], d.conns[i+1:])
d.conns[len(d.conns)-1] = nil
d.conns = d.conns[:len(d.conns)-1]
break
}
}
d.mut.Unlock()
}
// NumberOfConns returns the number of currently active virtual connections
func (d *PacketFilter) NumberOfConns() int {
d.mut.Lock()
n := len(d.conns)
d.mut.Unlock()
return n
}
// Dropped returns number of packets dropped due to nobody claiming them.
func (d *PacketFilter) Dropped() uint64 {
return atomic.LoadUint64(&d.dropped)
}
// Overflow returns number of packets were dropped due to receive buffers being
// full.
func (d *PacketFilter) Overflow() uint64 {
return atomic.LoadUint64(&d.overflow)
}
// Start starts the packet filter.
func (d *PacketFilter) Start() {
go d.loop()
}
func (d *PacketFilter) loop() {
var buf []byte
next:
for {
buf = bufPool.Get().([]byte)
n, addr, err := d.ReadFrom(buf)
pkt := packet{
n: n,
addr: addr,
err: err,
buf: buf[:n],
}
d.mut.Lock()
conns := d.conns
d.mut.Unlock()
if err != nil {
for _, conn := range conns {
select {
case conn.recvBuffer <- pkt:
default:
atomic.AddUint64(&d.overflow, 1)
}
}
return
}
for _, conn := range conns {
if conn.filter == nil || conn.filter.ClaimIncoming(pkt.buf, pkt.addr) {
select {
case conn.recvBuffer <- pkt:
default:
atomic.AddUint64(&d.overflow, 1)
}
goto next
}
}
atomic.AddUint64(&d.dropped, 1)
}
}

36
vendor/github.com/AudriusButkevicius/pfilter/misc.go generated vendored Normal file
View File

@@ -0,0 +1,36 @@
package pfilter
import (
"fmt"
"net"
"sync"
)
var (
maxPacketSize = 1500
bufPool = sync.Pool{
New: func() interface{} {
return make([]byte, maxPacketSize)
},
}
errClosed = fmt.Errorf("use of closed network connection")
)
type timeoutError struct{}
func (e *timeoutError) Error() string { return "i/o timeout" }
func (e *timeoutError) Timeout() bool { return true }
func (e *timeoutError) Temporary() bool { return true }
type filteredConnList []*FilteredConn
func (r filteredConnList) Len() int { return len(r) }
func (r filteredConnList) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
func (r filteredConnList) Less(i, j int) bool { return r[i].priority < r[j].priority }
type packet struct {
n int
addr net.Addr
err error
buf []byte
}