├── AUTHORS ├── dccp ├── sandbox │ ├── .gitignore │ ├── runidle.sh │ ├── runloss.sh │ ├── runrate.ssh │ ├── runrtt.sh │ ├── doc.go │ ├── util.go │ ├── TODO │ ├── INPROGRESS │ ├── moment.go │ ├── latency.go │ ├── guzzleplex.go │ ├── clientserver.go │ ├── DONE │ ├── loss_test.go │ ├── rate_test.go │ ├── dccp_test.go │ ├── measure.go │ └── roundtrip_test.go ├── NOTES ├── dial.go ├── OPTIMIZE ├── ccid3 │ ├── err.go │ ├── strober_test.go.disabled │ ├── ccid3.go │ ├── TODO │ ├── util.go │ ├── segmentsize.go │ ├── receiverrate.go │ ├── loss.go │ ├── loss_test.go_ │ ├── options_test.go │ ├── strober.go │ ├── nofeedback.go │ └── senderloss.go ├── mutex.go ├── routine_test.go ├── emit.go ├── checksum_test.go ├── util.go ├── mono.go ├── udplink.go ├── muxmsg.go ├── gauge │ ├── rate.go │ └── reduce.go ├── TODO ├── wire_test.go ├── ctrlc.go ├── reorder.go ├── dccp.go ├── link.go ├── flags.go ├── filetrace.go ├── seqack.go ├── connect_test.go.disabled ├── servicecode.go ├── stack.go ├── chanlink.go ├── headerutil.go ├── backoff.go ├── term.go ├── gen.go ├── listener.go ├── readwrite_test.go ├── err.go ├── addr.go ├── checksum.go ├── env.go ├── timeopt_test.go ├── options.go ├── ccfixed.go ├── string.go ├── conn.go ├── user.go ├── wire.go ├── segment.go ├── mux_test.go ├── pipe.go ├── flow.go ├── label.go ├── timeopt.go ├── amb.go ├── read.go └── header.go ├── cmd ├── ccid3-gentab │ ├── .gitignore │ └── gentab.go └── dccp-inspector │ ├── .gitignore │ ├── TODO │ ├── trip.go │ ├── series.go │ ├── inspector.go │ └── ui_js.go ├── doc ├── kohler06designing.pdf ├── floyd-tcp-friendly.pdf ├── end-to-end-uncertainty-winstein.pdf ├── latency-simulation ├── synthetic-time └── 8.1-Connection-Establishment ├── retransmit ├── REMARK ├── readwrite_test.go ├── read.go ├── write.go ├── conn.go └── header.go ├── .gitignore ├── README.md └── playground └── conntest.go /AUTHORS: -------------------------------------------------------------------------------- 1 | Petar Maymounkov 2 | -------------------------------------------------------------------------------- /dccp/sandbox/.gitignore: -------------------------------------------------------------------------------- 1 | var 2 | -------------------------------------------------------------------------------- /cmd/ccid3-gentab/.gitignore: -------------------------------------------------------------------------------- 1 | gentab 2 | -------------------------------------------------------------------------------- /cmd/dccp-inspector/.gitignore: -------------------------------------------------------------------------------- 1 | dccp-inspector 2 | -------------------------------------------------------------------------------- /doc/kohler06designing.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petar/GoDCCP/HEAD/doc/kohler06designing.pdf -------------------------------------------------------------------------------- /doc/floyd-tcp-friendly.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petar/GoDCCP/HEAD/doc/floyd-tcp-friendly.pdf -------------------------------------------------------------------------------- /dccp/sandbox/runidle.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | go test -test.run=Idle && dccp-inspector var/idle.emit > var/idle.html 3 | -------------------------------------------------------------------------------- /dccp/sandbox/runloss.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | go test -test.run=Loss; dccp-inspector -emits=true var/loss.emit > var/loss.html 3 | -------------------------------------------------------------------------------- /dccp/sandbox/runrate.ssh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | go test -test.run=Rate; dccp-inspector -emits=true var/rate.emit > var/rate.html 3 | -------------------------------------------------------------------------------- /doc/end-to-end-uncertainty-winstein.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petar/GoDCCP/HEAD/doc/end-to-end-uncertainty-winstein.pdf -------------------------------------------------------------------------------- /dccp/sandbox/runrtt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | go test -test.run=RoundtripEstimation; dccp-inspector -emits=true var/rtt.emit > var/rtt.html 3 | -------------------------------------------------------------------------------- /dccp/NOTES: -------------------------------------------------------------------------------- 1 | Upon entrance to CLOSING state the CCID is closed. However, the CLOSING logic 2 | in DCCP ensures that Close packets are sent at a conservative rate. 3 | -------------------------------------------------------------------------------- /retransmit/REMARK: -------------------------------------------------------------------------------- 1 | 2 | * cargo size limitations? 3 | 4 | * change the interfaces of header Write() methods to reduce the number of slice copys in packet 5 | processing 6 | -------------------------------------------------------------------------------- /cmd/dccp-inspector/TODO: -------------------------------------------------------------------------------- 1 | * (THINKING) add dimension labels on time series charts 2 | + unclear what to do when different time series have different dimensions 3 | * add row folding 4 | -------------------------------------------------------------------------------- /dccp/dial.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | import () 8 | -------------------------------------------------------------------------------- /dccp/sandbox/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | This package contains utilities for testing DCCP and CCID in a controlled, simulated 7 | environment. 8 | */ 9 | package sandbox 10 | -------------------------------------------------------------------------------- /dccp/OPTIMIZE: -------------------------------------------------------------------------------- 1 | Idle ticker should use Go's ticker 2 | 3 | Backoff optimizations 4 | Convert backoff/expire mechanisms to work in a polling model, so that all concurrent 5 | backoffs/expires can be handled by a the connection thread thread. 6 | This would enable synthetic time simulation more easily 7 | 8 | Implement BACKOFFS using table lookups? 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | syntax:glob 2 | *.[568ao] 3 | *.ao 4 | *.so 5 | *.pyc 6 | *.swp 7 | *.swo 8 | *.swn 9 | *.emit 10 | ._* 11 | .nfs.* 12 | [568a].out 13 | *~ 14 | *.orig 15 | *.pb.go 16 | core 17 | _obj 18 | _test 19 | src/pkg/Make.deps 20 | _testmain.go 21 | panic 22 | playground/conntest 23 | 24 | syntax:regexp 25 | ^pkg/ 26 | ^src/cmd/(.*)/6?\1$ 27 | ^.*/core.[0-9]*$ 28 | SCRIBBLE 29 | xxx 30 | -------------------------------------------------------------------------------- /dccp/ccid3/err.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package ccid3 6 | 7 | import "errors" 8 | 9 | // TODO: Annotate each error with the circumstances that can cause it 10 | var ( 11 | ErrMissingOption = errors.New("missing option") 12 | ErrNoAck = errors.New("packet is not an ack") 13 | ) 14 | -------------------------------------------------------------------------------- /doc/latency-simulation: -------------------------------------------------------------------------------- 1 | 2 | Repeat: 3 | 4 | INVARIANT: Keep track of the time remaining until the next packet in the pipe should be 5 | released to the reader side 6 | 7 | Block until either timeout is reached, or a new packet has entered the pipe 8 | 9 | If new packet arrived, place all queued packets in chronological order, ascending in 10 | delivery time 11 | 12 | If timeout reached, deliver packet to user read (unblock the read call) 13 | 14 | Repeat forever 15 | -------------------------------------------------------------------------------- /dccp/mutex.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | import ( 8 | "sync" 9 | ) 10 | 11 | type Mutex struct { 12 | sync.Mutex 13 | } 14 | 15 | func (m *Mutex) Lock() { 16 | m.Mutex.Lock() 17 | } 18 | 19 | func (m *Mutex) AssertLocked() { 20 | // TODO: Implement 21 | } 22 | 23 | func (m *Mutex) Unlock() { 24 | m.Mutex.Unlock() 25 | } 26 | -------------------------------------------------------------------------------- /dccp/sandbox/util.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package sandbox 6 | 7 | func NanoToMilli(nano float64) float64 { 8 | return nano / 1e6 9 | } 10 | 11 | func max64(x, y int64) int64 { 12 | if x > y { 13 | return x 14 | } 15 | return y 16 | } 17 | 18 | func min64(x, y int64) int64 { 19 | if x < y { 20 | return x 21 | } 22 | return y 23 | } 24 | -------------------------------------------------------------------------------- /dccp/ccid3/strober_test.go.disabled: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package ccid3 6 | 7 | import ( 8 | "fmt" 9 | "testing" 10 | "github.com/petar/GoDCCP/dccp" 11 | ) 12 | 13 | func TestStrober(t *testing.T) { 14 | var logger dccp.Amb = dccp.NewDetailAmb(?, ?) 15 | var s senderStrober 16 | s.Init(logger, 1024, 1024) 17 | for { 18 | s.Strobe() 19 | fmt.Printf("*") 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /dccp/ccid3/ccid3.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package ccid3 6 | 7 | import ( 8 | "github.com/petar/GoDCCP/dccp" 9 | ) 10 | 11 | type CCID3 struct {} 12 | 13 | func (CCID3) NewSender(env *dccp.Env, amb *dccp.Amb) dccp.SenderCongestionControl { 14 | return newSender(env, amb) 15 | } 16 | 17 | func (CCID3) NewReceiver(env *dccp.Env, amb *dccp.Amb) dccp.ReceiverCongestionControl { 18 | return newReceiver(env, amb) 19 | } 20 | -------------------------------------------------------------------------------- /dccp/routine_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | import ( 8 | "testing" 9 | "time" 10 | ) 11 | 12 | func TestGoJoin(t *testing.T) { 13 | var hello, world bool 14 | NewGoJoin("hello+world", 15 | Go(func() { 16 | hello = true 17 | time.Sleep(time.Second) 18 | }, "hello"), 19 | Go(func() { 20 | world = true 21 | time.Sleep(time.Second/2) 22 | }, "world"), 23 | ).Join() 24 | if !hello || !world { 25 | t.Errorf("goroutines did not complete") 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /dccp/emit.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | import ( 8 | "fmt" 9 | "runtime/debug" 10 | ) 11 | 12 | func (c *Conn) emitCatchSeqNo(h *Header, seqNos ...int64) { 13 | if h == nil { 14 | return 15 | } 16 | for _, seqNo := range seqNos { 17 | if h.SeqNo == seqNo { 18 | c.amb.EC(1, EventCatch, 19 | fmt.Sprintf("Caught SeqNo=%d: %s\n%s", seqNo, h.String(), string(debug.Stack())), 20 | h) 21 | break 22 | } 23 | } 24 | } 25 | 26 | func (c *Conn) emitSetState() { 27 | c.AssertLocked() 28 | c.amb.SetState(c.socket.GetState()) 29 | } 30 | -------------------------------------------------------------------------------- /dccp/sandbox/TODO: -------------------------------------------------------------------------------- 1 | Loss tests 2 | 3 | Rate tests 4 | ensure one-way convergence test converges to rate limit of sandbox line 5 | vary data size 6 | vary roundtrip time 7 | measure response to changes to either 8 | ensure two competing connections behave fairly 9 | 10 | Make an experiment class to unify boilerplate in tests 11 | 12 | Should loss.go's reorder buffer enforce ascending seq no output? 13 | 14 | RTT test, without constant rate constraint, causes division by zero error in rate calculation (in SetRate) 15 | 16 | Ensure behavior stabilizes if both sides remain silent after connect and connection does not close perpetually 17 | 18 | Infrastructre for tests of the form: 19 | "first packet after switched to OPEN state does not produce 'missing rtt report' log" 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | GoDCCP is an implementation of the 4 | [Datagram Congestion Control Protocol](http://read.cs.ucla.edu/dccp) in 5 | [Google Go](http://golang.org). 6 | 7 | GoDCCP is (being) implemented after 8 | [RFC 4340](http://read.cs.ucla.edu/dccp/rfc4340.txt), 9 | [RFC 4341](http://read.cs.ucla.edu/dccp/rfc4341.txt), and 10 | [RFC 4342](http://read.cs.ucla.edu/dccp/rfc4342.txt). 11 | 12 | ## Warning 13 | 14 | GoDCCP is not yet operational. This repository is public in the 15 | event someone is interested to join the development effort. 16 | 17 | ## Installation 18 | 19 | To install, simply run 20 | 21 | goinstall github.com/petar/GoDCCP/dccp 22 | 23 | ## About 24 | 25 | GoDCCP is maintained by [Petar Maymounkov](http://pdos.csail.mit.edu/~petar/). 26 | -------------------------------------------------------------------------------- /dccp/ccid3/TODO: -------------------------------------------------------------------------------- 1 | LONG-TERM 2 | 3 | Idle and application limited periods 4 | (5.1) The allowed sending rate is never reduced to less than the [RFC3390] 5 | initial sending rate as the result of an idle period. If the allowed 6 | sending rate is less than the initial sending rate upon entry to the 7 | idle period, then it will still be less than the initial sending rate 8 | when the idle period is exited. However, if the allowed sending rate 9 | is greater than or equal to the initial sending rate upon entry to 10 | the idle period, then it should not be reduced below the initial 11 | sending rate no matter how long the idle period lasts. 12 | 13 | TimestampEcho option and RTT estimates based on timestamps 14 | 15 | Data Dropped Option 16 | 17 | Slow Receiver Option 18 | -------------------------------------------------------------------------------- /dccp/checksum_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | import ( 8 | "math/rand" 9 | "testing" 10 | ) 11 | 12 | func TestChecksum(t *testing.T) { 13 | // Test even-size checksum 14 | buf := make([]byte, 2+40) 15 | for i := 2; i < len(buf); i++ { 16 | buf[i] = byte(rand.Int()) 17 | } 18 | csumUint16ToBytes( 19 | csumDone(csumSum(buf)), 20 | buf[0:2], 21 | ) 22 | if csumDone(csumSum(buf)) != 0 { 23 | t.Errorf("csum") 24 | } 25 | 26 | // Test odd-size checksum 27 | buf = make([]byte, 41+2) 28 | for i := 2; i < len(buf); i++ { 29 | buf[i] = byte(rand.Int()) 30 | } 31 | csumUint16ToBytes( 32 | csumDone(csumSum(buf)), 33 | buf[0:2], 34 | ) 35 | if csumDone(csumSum(buf)) != 0 { 36 | t.Errorf("csum") 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /dccp/util.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | func min(i, j int) int { 8 | if i < j { 9 | return i 10 | } 11 | return j 12 | } 13 | 14 | func max(i, j int) int { 15 | if i > j { 16 | return i 17 | } 18 | return j 19 | } 20 | 21 | func max64(x, y int64) int64 { 22 | if x > y { 23 | return x 24 | } 25 | return y 26 | } 27 | 28 | func max32(x, y int32) int32 { 29 | if x > y { 30 | return x 31 | } 32 | return y 33 | } 34 | 35 | func min32(x, y int32) int32 { 36 | if x < y { 37 | return x 38 | } 39 | return y 40 | } 41 | 42 | func min64(x, y int64) int64 { 43 | if x < y { 44 | return x 45 | } 46 | return y 47 | } 48 | 49 | func minu32(x, y uint32) uint32 { 50 | if x < y { 51 | return x 52 | } 53 | return y 54 | } 55 | -------------------------------------------------------------------------------- /cmd/ccid3-gentab/gentab.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | // Use this output of this command as the contents of table.go 6 | 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "math" 12 | ) 13 | 14 | // TODO: Implement a compressed version of the table whereby 15 | // repeating values are omitted. 16 | 17 | func main() { 18 | 19 | // Generate qTable 20 | fmt.Printf( 21 | `// THIS FILE IS AUTO-GENERATED 22 | 23 | package ccid3 24 | 25 | var qTable = []struct{ RateInvLo uint32; Q int64 }{ 26 | `) 27 | // j is the loss rate inverse 28 | for j := 1; j < 3000; j++ { 29 | p := 1 / (1+float64(j)) // loss rate 30 | q := (math.Sqrt(2*p/3) + 12*math.Sqrt(3*p/8)*p*(1+32*p*p)) 31 | fmt.Printf("\t{ % 4d, % 5d\t},\n", j, int64(q*1e3)) 32 | } 33 | fmt.Printf( 34 | `} 35 | `) 36 | 37 | } 38 | -------------------------------------------------------------------------------- /dccp/ccid3/util.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package ccid3 6 | 7 | // Some basic utility functions below 8 | 9 | func min(x, y int) int { 10 | if x < y { 11 | return x 12 | } 13 | return y 14 | } 15 | 16 | func minu32(x, y uint32) uint32 { 17 | if x < y { 18 | return x 19 | } 20 | return y 21 | } 22 | 23 | func min64(x, y int64) int64 { 24 | if x < y { 25 | return x 26 | } 27 | return y 28 | } 29 | 30 | func max8(x, y int8) int8 { 31 | if x > y { 32 | return x 33 | } 34 | return y 35 | } 36 | 37 | func max(x, y int) int { 38 | if x > y { 39 | return x 40 | } 41 | return y 42 | } 43 | 44 | func maxu32(x, y uint32) uint32 { 45 | if x > y { 46 | return x 47 | } 48 | return y 49 | } 50 | 51 | func max64(x, y int64) int64 { 52 | if x > y { 53 | return x 54 | } 55 | return y 56 | } 57 | -------------------------------------------------------------------------------- /doc/synthetic-time: -------------------------------------------------------------------------------- 1 | 2 | The key insight in multi-goroutine synthetic time is that between every two 3 | moments of simulated time, exactly all active goroutines must perform exactly 4 | one state-changing (e.g. time query operations don't count) interaction 5 | (functional call) with the runtime. 6 | These interactions would be one of: 7 | sleep 8 | return from blocking exchange with external environment 9 | kill goroutine (can we capture these?) 10 | spawn go-routine 11 | 12 | 13 | Example program for article: 14 | Say F is as follows: 15 | 16 | func F(api TimeAPI) (dur time.Duration) { 17 | d := make(chan int) 18 | c := make(chan time.Time) 19 | go func() { 20 | api.Sleep(2e9) 21 | c <- api.Now() 22 | d <- 1 23 | }() 24 | go func() { 25 | api.Sleep(1e9) 26 | c <- api.Now() 27 | d <- 1 28 | }() 29 | go func() { 30 | t0 := <-c 31 | t1 := <-c 32 | dur = t1.Sub(t0) 33 | d <- 1 34 | }() 35 | <-d 36 | <-d 37 | <-d 38 | return 39 | } 40 | -------------------------------------------------------------------------------- /dccp/mono.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | import ( 8 | "sync" 9 | ) 10 | 11 | // An instance of monotoneTime has the singular function of returning the time Now. 12 | // It has the property that consecutive invokations return strictly increasing numbers. 13 | // This may provide superfluous logic, but we use it for piece of mind. 14 | type monotoneTime struct { 15 | sync.Mutex 16 | env *Env 17 | last int64 18 | } 19 | 20 | func (x *monotoneTime) Init(env *Env) { 21 | x.env = env 22 | x.last = 0 23 | } 24 | 25 | func (x *monotoneTime) Now() int64 { 26 | x.Lock() 27 | defer x.Unlock() 28 | now := x.env.Now() 29 | // TODO: If now - x.last is hugely negative we might want to report some sort of error 30 | if now < x.last { 31 | panic("negative time in mono") 32 | } 33 | x.last = max64(now, x.last) 34 | return x.last 35 | } 36 | -------------------------------------------------------------------------------- /dccp/udplink.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | import ( 8 | "net" 9 | "time" 10 | ) 11 | 12 | // UDPLink binds to a UDP port and acts as a Link. 13 | type UDPLink struct { 14 | c *net.UDPConn 15 | } 16 | 17 | func BindUDPLink(netw string, laddr *net.UDPAddr) (link *UDPLink, err error) { 18 | c, err := net.ListenUDP(netw, laddr) 19 | if err != nil { 20 | return nil, err 21 | } 22 | return &UDPLink{c}, nil 23 | } 24 | 25 | func (u *UDPLink) GetMTU() int { return 1500 } 26 | 27 | func (u *UDPLink) SetReadDeadline(t time.Time) error { 28 | return u.c.SetReadDeadline(t) 29 | } 30 | 31 | func (u *UDPLink) ReadFrom(buf []byte) (n int, addr net.Addr, err error) { 32 | return u.c.ReadFrom(buf) 33 | } 34 | 35 | func (u *UDPLink) WriteTo(buf []byte, addr net.Addr) (n int, err error) { 36 | return u.c.WriteTo(buf, addr) 37 | } 38 | 39 | func (u *UDPLink) Close() error { 40 | return u.c.Close() 41 | } 42 | -------------------------------------------------------------------------------- /dccp/muxmsg.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | // muxMsg{} contains the source and destination labels of a flow. 8 | type muxMsg struct { 9 | Source, Sink *Label 10 | } 11 | 12 | const muxMsgFootprint = 2 * labelFootprint 13 | 14 | // readMuxMsg() decodes a muxMsg{} from wire format 15 | func readMuxMsg(p []byte) (msg *muxMsg, n int, err error) { 16 | source, n0, err := ReadLabel(p) 17 | if err != nil { 18 | return nil, 0, err 19 | } 20 | dest, n1, err := ReadLabel(p[n0:]) 21 | if err != nil { 22 | return nil, 0, err 23 | } 24 | return &muxMsg{source, dest}, n0 + n1, nil 25 | } 26 | 27 | // Write() encodes the muxMsg{} to p@ in wire format 28 | func (msg *muxMsg) Write(p []byte) (n int, err error) { 29 | n0, err := msg.Source.Write(p) 30 | if err != nil { 31 | return 0, err 32 | } 33 | n1, err := msg.Sink.Write(p[n0:]) 34 | if err != nil { 35 | return 0, err 36 | } 37 | return n0 + n1, nil 38 | } 39 | -------------------------------------------------------------------------------- /dccp/gauge/rate.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package gauge 6 | 7 | // TODO: Don't count drops after one side closes 8 | func CalcRates(trips []*Trip) (sendRate float64, receiveRate float64) { 9 | var earliest, latest int64 10 | var sent int64 11 | var received int64 12 | for _, t := range trips { 13 | sent++ 14 | if t.Round { 15 | received++ 16 | } 17 | if earliest == 0 { 18 | earliest = t.Forward[0].Time 19 | } else { 20 | earliest = min64(earliest, t.Forward[0].Time) 21 | } 22 | if latest == 0 { 23 | latest = t.Forward[len(t.Forward)-1].Time 24 | } else { 25 | latest = max64(latest, t.Forward[len(t.Forward)-1].Time) 26 | } 27 | } 28 | d := float64(latest - earliest) / 1e9 29 | 30 | return float64(sent)/d, float64(received)/d 31 | } 32 | 33 | func max64(x, y int64) int64 { 34 | if x > y { 35 | return x 36 | } 37 | return y 38 | } 39 | 40 | func min64(x, y int64) int64 { 41 | if x < y { 42 | return x 43 | } 44 | return y 45 | } 46 | -------------------------------------------------------------------------------- /dccp/TODO: -------------------------------------------------------------------------------- 1 | PRE-RELEASE 2 | 3 | Implement Conn.SetReadExpire 4 | 5 | Address Ack-Sync pair in client connection establishement, marked XXX in code 6 | 7 | Add heartbeat optional function (in ccid? once every so many RTTs?) 8 | 9 | Handshake with 0-latency, no drops should be fast 10 | 11 | POST-RELEASE 12 | 13 | Deal with circular arithmetic of sequence numbers 14 | In theory, long lived connections may wrap around the AckNo/SeqNo space in which case circular 15 | versions of things like maxu64() should be used. This is unlikely to happen however if we are using 16 | 48-bit numbers exclusively 17 | 18 | Update the DCCP Mutex implementation 19 | 20 | LONG-TERM 21 | 22 | Dynamically changing SWAF/SWBF 23 | 24 | Abridged header read/write (no ports, no checksums). Not needed in user-space mode 25 | 26 | Make ccvals int8 27 | 28 | Denial of service considerations (Not needed in the context of the Tonika security stack) 29 | Section 7.5.4: To protect against denial-of-service attacks, DCCP implementations SHOULD impose a 30 | rate limit on DCCP-Syncs sent in response to sequence-invalid packets, such as not more than eight 31 | DCCP-Syncs per second. 32 | -------------------------------------------------------------------------------- /dccp/sandbox/INPROGRESS: -------------------------------------------------------------------------------- 1 | Loss 2 | Why is the server series null sometimes? 3 | Why does c<—s report 20+ percent loss when inspector shows none 4 | 5 | Why does the (roundtrip/loss) measure report ~15% loss server-to-client, when the pipe is configured not 6 | to drop at all. 7 | These are packets dropped due to full send queue. They are Acks that are send in response to 8 | every received DataAck packet. 9 | 10 | Why are they dropped? Send queue should never fill up given that the server-to-client 11 | has fixed speed at same rate as client-to-server send rate and higher rate than 12 | actually transmitted client-to-server traffic. 13 | 14 | Packets that are dropped due to "Slow strobe", i.e. before they even get into the send queue, 15 | are never assigned sequence numbers and therefore they are never captured by the internal 16 | sequence-number-based loss estimation mechanism. 17 | 18 | 19 | ____________________________________ 20 | Produce setRate samples 21 | Test logic append a "show" token to log arguments that pertain to this test and should be displayed in inspector output 22 | 23 | Reducer for actual rate 24 | 25 | ____________________________________ 26 | Should we send NON-data packets immediately, bypassing rate control? 27 | -------------------------------------------------------------------------------- /dccp/wire_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | import ( 8 | "math/rand" 9 | "testing" 10 | ) 11 | 12 | func TestWireEncodeDecode(t *testing.T) { 13 | 14 | buf := make([]byte, 8) 15 | var u8 uint8 16 | var u16 uint16 17 | var u32 uint32 18 | var u64 uint64 19 | 20 | // 1 byte 21 | u8 = uint8(rand.Int31()) 22 | EncodeUint8(u8, buf[0:1]) 23 | if DecodeUint8(buf[0:1]) != u8 { 24 | t.Errorf("E/D 1 byte") 25 | } 26 | 27 | // 2 byte 28 | u16 = uint16(rand.Int31()) 29 | EncodeUint16(u16, buf[0:2]) 30 | if DecodeUint16(buf[0:2]) != u16 { 31 | t.Errorf("E/D 2 byte") 32 | } 33 | 34 | // 3 byte 35 | u32 = uint32(rand.Int31()) 36 | u32 = (u32 << 8) >> 8 37 | EncodeUint24(u32, buf[0:3]) 38 | if DecodeUint24(buf[0:3]) != u32 { 39 | t.Errorf("E/D 3 byte") 40 | } 41 | 42 | // 4 byte 43 | u32 = uint32(rand.Int31()) << 1 44 | EncodeUint32(u32, buf[0:4]) 45 | if DecodeUint32(buf[0:4]) != u32 { 46 | t.Errorf("E/D 4 byte") 47 | } 48 | 49 | // 6 byte 50 | u64 = uint64(rand.Int63()) 51 | u64 = (u64 << (2 * 8)) >> (2 * 8) 52 | EncodeUint48(u64, buf[0:6]) 53 | if DecodeUint48(buf[0:6]) != u64 { 54 | t.Errorf("E/D 6 byte") 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /dccp/ccid3/segmentsize.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package ccid3 6 | 7 | // senderSegmentSize keeps an up-to-date estimate of the Segment Size (SS). 8 | // The current implementation simply uses the MPS (maximum packet size) as SS. 9 | // TODO: Compute SS as the average SS over a few most recent loss intervals, see Section 5.3. 10 | type senderSegmentSize struct { 11 | mps int 12 | } 13 | 14 | const FixedSegmentSize = 2*1500 15 | 16 | // Init resets the object for new use 17 | func (t *senderSegmentSize) Init() { 18 | t.mps = 0 19 | } 20 | 21 | // Sender calls SetMPS to notify this object if the maximum packet size in use 22 | func (t *senderSegmentSize) SetMPS(mps int) { t.mps = mps } 23 | 24 | // SS returns the current estimate of the segment size 25 | func (t *senderSegmentSize) SS() int { 26 | if t.mps <= 0 { 27 | panic("not ready with SS") 28 | } 29 | // TODO: Segment Size should equal maximum packet size minus DCCP header size. 30 | // In other words, it is supposed to reflect the app data size. 31 | // Since we are in user space, we tend to use MPS to mean app data as well. 32 | // It would be nice to set these straight eventually and use more uniform terminology. 33 | return t.mps 34 | } 35 | -------------------------------------------------------------------------------- /dccp/ctrlc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | "os" 11 | "os/signal" 12 | "syscall" 13 | "time" 14 | ) 15 | 16 | // InstallTimeout panics the current process in ns time 17 | func InstallTimeout(ns int64) { 18 | go func() { 19 | k := int(ns / 1e9) 20 | for i := 0; i < k; i++ { 21 | time.Sleep(time.Second) 22 | fmt.Printf("•%d/%d•\n", i, k) 23 | } 24 | //time.Sleep(time.Duration(ns)) 25 | panic("process timeout") 26 | }() 27 | } 28 | 29 | // InstallCtrlCPanic installs a Ctrl-C signal handler that panics 30 | func InstallCtrlCPanic() { 31 | go func() { 32 | //defer SavePanicTrace() 33 | ch := make(chan os.Signal) 34 | signal.Notify(ch, os.Interrupt, os.Kill) 35 | for s := range ch { 36 | log.Printf("ctrl-c/kill interruption: %s\n", s) 37 | panic("ctrl-c/kill") 38 | } 39 | }() 40 | } 41 | 42 | func SavePanicTrace() { 43 | r := recover() 44 | if r == nil { 45 | return 46 | } 47 | // Redirect stderr 48 | file, err := os.Create("panic") 49 | if err != nil { 50 | panic("dumper (no file) " + r.(fmt.Stringer).String()) 51 | } 52 | syscall.Dup2(int(file.Fd()), int(os.Stderr.Fd())) 53 | // TRY: defer func() { file.Close() }() 54 | panic("dumper " + r.(string)) 55 | } 56 | -------------------------------------------------------------------------------- /dccp/reorder.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | // WARN: reorderBuffer applied to all DCCP traffic may introduce significant 8 | // delay in interactive sections like the initial handshake e.g. 9 | // Not in use. Implementation incomplete until XXX addressed. 10 | 11 | // reorderBuffer consumes headers and outputs them in sequence number order. 12 | // The output headers are guaranteed to have increasing sequence numbers. 13 | type reorderBuffer struct { 14 | headers []*Header 15 | lastIssue int64 // Sequence number of past packet released for reading 16 | } 17 | 18 | // Init prepares the reorderBuffer for new use 19 | func (t *reorderBuffer) Init(size int) { 20 | t.headers = make([]*Header, size) 21 | t.lastIssue = 0 22 | } 23 | 24 | func (t *reorderBuffer) PushPop(h *Header) *Header { 25 | // XXX: Must guarantee strictly ascending order 26 | var popSeqNo int64 = SEQNOMAX + 1 27 | var popIndex int 28 | for i, g := range t.headers { 29 | if g == nil { 30 | t.headers[i] = h 31 | return nil 32 | } 33 | // XXX: This must employ circular comparison 34 | if g.SeqNo < popSeqNo { 35 | popIndex = i 36 | popSeqNo = g.SeqNo 37 | } 38 | } 39 | r := t.headers[popIndex] 40 | t.headers[popIndex] = h 41 | return r 42 | } 43 | -------------------------------------------------------------------------------- /dccp/dccp.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | import "net" 8 | 9 | type Stack struct { 10 | mux *Mux 11 | link Link 12 | ccid CCID 13 | } 14 | 15 | // NewStack creates a new connection-handling object. 16 | func NewStack(link Link, ccid CCID) *Stack { 17 | return &Stack{ 18 | mux: NewMux(link), 19 | link: link, 20 | ccid: ccid, 21 | } 22 | } 23 | 24 | // Dial initiates a new connection to the specified Link-layer address. 25 | func (s *Stack) Dial(addr net.Addr, serviceCode uint32) (c SegmentConn, err error) { 26 | bc, err := s.mux.Dial(addr) 27 | if err != nil { 28 | return nil, err 29 | } 30 | hc := NewHeaderConn(bc) 31 | env := NewEnv(nil) 32 | c = NewConnClient(env, NoLogging, hc, 33 | s.ccid.NewSender(env, NoLogging), 34 | s.ccid.NewReceiver(env, NoLogging), 35 | serviceCode) 36 | return c, nil 37 | } 38 | 39 | // Accept blocks until a new connecion is established. It then 40 | // returns the connection. 41 | func (s *Stack) Accept() (c SegmentConn, err error) { 42 | bc, err := s.mux.Accept() 43 | if err != nil { 44 | return nil, err 45 | } 46 | hc := NewHeaderConn(bc) 47 | env := NewEnv(nil) 48 | c = NewConnServer(env, NoLogging, hc, 49 | s.ccid.NewSender(env, NoLogging), 50 | s.ccid.NewReceiver(env, NoLogging)) 51 | return c, nil 52 | } 53 | -------------------------------------------------------------------------------- /dccp/sandbox/moment.go: -------------------------------------------------------------------------------- 1 | package sandbox 2 | 3 | import ( 4 | "math" 5 | ) 6 | 7 | type Moment struct { 8 | sum float64 9 | sumSq float64 10 | min float64 11 | max float64 12 | weight float64 13 | } 14 | 15 | func (x *Moment) Init() { 16 | x.sum, x.sumSq, x.min, x.max, x.weight = 0, 0, math.NaN(), math.NaN(), 0 17 | } 18 | 19 | func (x *Moment) Add(sample float64) { 20 | x.AddWeighted(sample, 1) 21 | } 22 | 23 | func (x *Moment) AddWeighted(sample float64, weight float64) { 24 | x.sum += sample*weight 25 | x.sumSq += sample*sample*weight 26 | x.weight += weight 27 | if math.IsNaN(x.min) || sample < x.min { 28 | x.min = sample 29 | } 30 | if math.IsNaN(x.max) || sample > x.max { 31 | x.max = sample 32 | } 33 | } 34 | 35 | func (x *Moment) Average() float64 { 36 | return x.Moment(1) 37 | } 38 | 39 | func (x *Moment) Variance() float64 { 40 | m1 := x.Moment(1) 41 | return x.Moment(2) - m1*m1 42 | } 43 | 44 | func (x *Moment) StdDev() float64 { 45 | return math.Sqrt(x.Variance()) 46 | } 47 | 48 | func (x *Moment) Min() float64 { 49 | return x.min 50 | } 51 | 52 | func (x *Moment) Max() float64 { 53 | return x.max 54 | } 55 | 56 | func (x *Moment) Moment(k float64) float64 { 57 | switch k { 58 | case 0: 59 | return 1 60 | case 1: 61 | return x.sum / x.weight 62 | case 2: 63 | return x.sumSq / x.weight 64 | } 65 | if math.IsInf(k, 1) { 66 | return x.max 67 | } 68 | panic("not yet supported") 69 | } 70 | -------------------------------------------------------------------------------- /dccp/link.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | import ( 8 | "net" 9 | "time" 10 | ) 11 | 12 | // Note that Link objects return Go package (net, os, and syscall) errors, whereas 13 | // SegmentConn and HeaderConn (being internal to DCCP) return DCCP errors. 14 | // The driver type that implements access to a concrete communication link would be what 15 | // bridges from Go errors to DCCP errors. E.g. this happans in type flow. 16 | 17 | // Link is an abstract interface to a physical connection-less packet layer which sends and 18 | // receives packets 19 | type Link interface { 20 | 21 | // Returns the Maximum Transmission Unit 22 | // Writes smaller than this are guaranteed to be sent whole 23 | GetMTU() int 24 | 25 | // ReadFrom receives the next packet of data. It returns net.Error errors. So in the event 26 | // of a timeout e.g. it will return a net.Error with Timeout() true, rather than an 27 | // ErrTimeout. The latter is used internally to DCCP in types that implement HeaderConn and 28 | // SegmentConn. 29 | ReadFrom(buf []byte) (n int, addr net.Addr, err error) 30 | 31 | // WriteTo sends a packet of data 32 | WriteTo(buf []byte, addr net.Addr) (n int, err error) 33 | 34 | // SetReadDeadline has the same meaning as net.Conn.SetReadDeadline 35 | SetReadDeadline(t time.Time) error 36 | 37 | // Close terminates the link gracefully 38 | Close() error 39 | } 40 | -------------------------------------------------------------------------------- /dccp/flags.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | import ( 8 | "sync" 9 | ) 10 | 11 | // Flags is a general purpose key-value map, which is used inside Amb to allow 12 | // an Amb and all of its refinements to share a collection of debug flags. 13 | type Flags struct { 14 | sync.Mutex 15 | flags map[string]interface{} 16 | } 17 | 18 | // NewFlags creates and initializes a new Flags instance 19 | func NewFlags() *Flags { 20 | x := &Flags{} 21 | x.Init() 22 | return x 23 | } 24 | 25 | // Init clears a Flags instance for fresh use 26 | func (x *Flags) Init() { 27 | x.Lock() 28 | defer x.Unlock() 29 | x.flags = make(map[string]interface{}) 30 | } 31 | 32 | func (x *Flags) Set(key string, value interface{}) { 33 | x.Lock() 34 | defer x.Unlock() 35 | x.flags[key] = value 36 | } 37 | 38 | func (x *Flags) SetUint32(key string, value uint32) { 39 | x.Lock() 40 | defer x.Unlock() 41 | x.flags[key] = value 42 | } 43 | 44 | func (x *Flags) Has(key string) bool { 45 | x.Lock() 46 | defer x.Unlock() 47 | _, ok := x.flags[key] 48 | return ok 49 | } 50 | 51 | func (x *Flags) GetInt64(key string) (value int64, present bool) { 52 | x.Lock() 53 | defer x.Unlock() 54 | v, ok := x.flags[key] 55 | if !ok { 56 | return 0, false 57 | } 58 | return v.(int64), true 59 | } 60 | 61 | func (x *Flags) GetUint32(key string) (value uint32, present bool) { 62 | x.Lock() 63 | defer x.Unlock() 64 | v, ok := x.flags[key] 65 | if !ok { 66 | return 0, false 67 | } 68 | return v.(uint32), true 69 | } 70 | -------------------------------------------------------------------------------- /dccp/filetrace.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | import ( 8 | "fmt" 9 | "runtime" 10 | "encoding/json" 11 | "os" 12 | "sync" 13 | ) 14 | 15 | // FileTraceWriter saves all log entries to a file in JSON format 16 | type FileTraceWriter struct { 17 | sync.Mutex 18 | f *os.File 19 | enc *json.Encoder 20 | dup TraceWriter 21 | } 22 | 23 | // NewFileTraceWriterDup creates a TraceWriter that saves logs in a file and also passes them to dup. 24 | func NewFileTraceWriterDup(filename string, dup TraceWriter) *FileTraceWriter { 25 | os.Remove(filename) 26 | f, err := os.Create(filename) 27 | if err != nil { 28 | panic(fmt.Sprintf("cannot create log file '%s'", filename)) 29 | } 30 | w := &FileTraceWriter{ f:f, enc:json.NewEncoder(f), dup:dup } 31 | runtime.SetFinalizer(w, func(w *FileTraceWriter) { 32 | w.f.Close() 33 | }) 34 | return w 35 | } 36 | 37 | func NewFileTraceWriter(filename string) *FileTraceWriter { 38 | return NewFileTraceWriterDup(filename, nil) 39 | } 40 | 41 | func (t *FileTraceWriter) Write(r *Trace) { 42 | t.Lock() 43 | err := t.enc.Encode(r) 44 | t.Unlock() 45 | if err != nil { 46 | panic(fmt.Sprintf("error encoding log entry (%s)", err)) 47 | } 48 | if t.dup != nil { 49 | t.dup.Write(r) 50 | } 51 | } 52 | 53 | func (t *FileTraceWriter) Sync() error { 54 | if t.dup != nil { 55 | t.dup.Sync() 56 | } 57 | return t.f.Sync() 58 | } 59 | 60 | func (t *FileTraceWriter) Close() error { 61 | if t.dup != nil { 62 | t.dup.Close() 63 | } 64 | return t.f.Close() 65 | } 66 | -------------------------------------------------------------------------------- /retransmit/readwrite_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package retransmit 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "reflect" 11 | "testing" 12 | ) 13 | 14 | var testHeaders = []*header{ 15 | &header{ 16 | Sync: true, 17 | SyncNo: 1234, 18 | 19 | Ack: true, 20 | AckSyncNo: 5678, 21 | AckDataNo: 43218765, 22 | AckMap: []byte{1, 2, 3, 4}, 23 | 24 | Data: true, 25 | DataNo: 333355551, 26 | DataCargo: []byte{1, 1, 2, 2, 3, 3, 4, 4}, 27 | }, 28 | } 29 | 30 | func TestReadWrite(t *testing.T) { 31 | for _, h := range testHeaders { 32 | b, err := h.Write() 33 | if err != nil { 34 | t.Errorf("write error: %s", err) 35 | } 36 | fmt.Println(dumpBytes(b)) 37 | g, err := readHeader(b) 38 | if err != nil { 39 | t.Errorf("read error: %s", err) 40 | } else { 41 | diff(t, "** ", g, h) 42 | } 43 | } 44 | } 45 | 46 | func dumpBytes(bb []byte) string { 47 | var w bytes.Buffer 48 | for _, b := range bb { 49 | fmt.Fprintf(&w, "%02x·", b) 50 | } 51 | return string(w.Bytes()) 52 | } 53 | 54 | func diff(t *testing.T, prefix string, have, want interface{}) { 55 | hv := reflect.ValueOf(have).Elem() 56 | wv := reflect.ValueOf(want).Elem() 57 | if hv.Type() != wv.Type() { 58 | t.Errorf("%s: type mismatch %v vs %v", prefix, hv.Type(), wv.Type()) 59 | } 60 | for i := 0; i < hv.NumField(); i++ { 61 | hf := hv.Field(i).Interface() 62 | wf := wv.Field(i).Interface() 63 | if !reflect.DeepEqual(hf, wf) { 64 | t.Errorf("%s: %s = %v want %v", prefix, hv.Type().Field(i).Name, hf, wf) 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /dccp/seqack.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | // PlaceSeqAck() updates the socket registers upon 8 | // receiving a header from the other side. 9 | func (c *Conn) PlaceSeqAck(h *Header) { 10 | c.AssertLocked() 11 | 12 | // Update GSR 13 | gsr := c.socket.GetGSR() 14 | c.socket.SetGSR(max64(gsr, h.SeqNo)) 15 | 16 | // Update GAR 17 | if h.HasAckNo() { 18 | gar := c.socket.GetGAR() 19 | c.socket.SetGAR(max64(gar, h.AckNo)) 20 | } 21 | } 22 | 23 | const ( 24 | seqAckNormal = iota + 1 25 | seqAckAbnormal 26 | seqAckSyncAck 27 | ) 28 | 29 | func (c *Conn) WriteSeqAck(h *writeHeader) { 30 | c.AssertLocked() 31 | switch h.SeqAckType { 32 | case seqAckNormal: 33 | c.takeSeqAck(&h.Header) 34 | case seqAckAbnormal: 35 | c.takeAbnormalSeqAck(&h.Header, h.InResponseTo) 36 | case seqAckSyncAck: 37 | c.takeSeqAck(&h.Header) 38 | if h.InResponseTo.Type != Sync { 39 | panic("SyncAck without a Sync") 40 | } 41 | h.Header.AckNo = h.InResponseTo.SeqNo 42 | default: 43 | panic("missing seq ack type") 44 | } 45 | } 46 | 47 | func (c *Conn) takeSeqAck(h *Header) *Header { 48 | c.AssertLocked() 49 | 50 | h.SeqNo = max64(c.socket.GetISS(), c.socket.GetGSS() + 1) 51 | c.socket.SetGSS(h.SeqNo) 52 | h.AckNo = c.socket.GetGSR() 53 | 54 | return h 55 | } 56 | 57 | func (c *Conn) takeAbnormalSeqAck(h, inResponseTo *Header) *Header { 58 | c.AssertLocked() 59 | 60 | if inResponseTo.HasAckNo() { 61 | h.SeqNo = inResponseTo.AckNo + 1 62 | } else { 63 | h.SeqNo = 0 64 | } 65 | h.AckNo = inResponseTo.SeqNo 66 | return h 67 | } 68 | -------------------------------------------------------------------------------- /dccp/connect_test.go.disabled: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | import ( 8 | "math/rand" 9 | "testing" 10 | ) 11 | 12 | func TestConnect(t *testing.T) { 13 | 14 | // Install stacks 15 | linka, linkb := NewChanPipe() 16 | newscc := NewFixedRateSenderControlFunc(10) 17 | newrcc := NewFixedRateReceiverControlFunc() 18 | stacka, stackb := NewStack(linka, newscc, newrcc), NewStack(linkb, newscc, newrcc) 19 | 20 | // Establish connection 21 | ca, err := stacka.Dial(nil, 1) 22 | if err != nil { 23 | t.Fatalf("side a dial: %s", err) 24 | } 25 | cb, err := stackb.Accept() 26 | if err != nil { 27 | t.Fatalf("side b accept: %s", err) 28 | } 29 | 30 | // Prepare random block 31 | p := make([]byte, 10) 32 | for i, _ := range p { 33 | p[i] = byte(rand.Int()) 34 | } 35 | 36 | // Write and read the block 37 | err = ca.WriteSegment(p) 38 | if err != nil { 39 | t.Errorf("side a write: %s", err) 40 | } 41 | q, err := cb.ReadSegment() 42 | if err != nil { 43 | t.Errorf("side b read: %s", err) 44 | } 45 | 46 | // Compare 47 | if !byteSlicesEqual(p, q) { 48 | t.Errorf("read and write blocks differ") 49 | } 50 | 51 | // Close connection 52 | err = ca.Close() 53 | if err != nil { 54 | t.Errorf("side a close: %s", err) 55 | } 56 | err = cb.Close() 57 | if err != nil { 58 | t.Errorf("side b close: %s", err) 59 | } 60 | } 61 | 62 | func byteSlicesEqual(a,b []byte) bool { 63 | if len(a) != len(b) { 64 | return false 65 | } 66 | for i, x := range a { 67 | if b[i] != x { 68 | return false 69 | } 70 | } 71 | return true 72 | } 73 | -------------------------------------------------------------------------------- /dccp/servicecode.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | // After '8.1.2. Service Codes' 8 | 9 | func isValidServiceCode(sc uint32) bool { return sc != 4294967295 } 10 | 11 | // isASCIIServiceCodeChar() returns true if c@ is a ServiceCode character 12 | // that can be displayed in ASCII 13 | func isASCIIServiceCodeChar(c byte) bool { 14 | if c == 32 { 15 | return true 16 | } 17 | if c >= 42 && c <= 43 { 18 | return true 19 | } 20 | if c >= 45 && c <= 57 { 21 | return true 22 | } 23 | if c >= 63 && c <= 90 { 24 | return true 25 | } 26 | if c == 95 { 27 | return true 28 | } 29 | if c >= 97 && c <= 122 { 30 | return true 31 | } 32 | return false 33 | } 34 | 35 | func serviceCodeToSlice(u uint32) []byte { 36 | p := make([]byte, 4) 37 | p[0] = byte(u >> 3 * 8) 38 | p[1] = byte((u >> 2 * 8) & 0xff) 39 | p[2] = byte((u >> 1 * 8) & 0xff) 40 | p[3] = byte(u & 0xff) 41 | return p 42 | } 43 | 44 | func sliceToServiceCode(p []byte) uint32 { 45 | var s uint32 46 | s = uint32(p[0]) 47 | s <<= 8 48 | s |= uint32(p[1]) 49 | s <<= 8 50 | s |= uint32(p[2]) 51 | s <<= 8 52 | s |= uint32(p[3]) 53 | return s 54 | } 55 | 56 | // TODO: Implement additional string representations according to '8.1.2. Service Codes' 57 | 58 | func ServiceCodeString(code uint32) string { 59 | return "SC:" + string(serviceCodeToSlice(code)) 60 | } 61 | 62 | func ParseServiceCode(p []byte) (uint32, error) { 63 | if len(p) != 7 { 64 | return 0, ErrSyntax 65 | } 66 | if string(p[:3]) != "SC:" { 67 | return 0, ErrSyntax 68 | } 69 | return sliceToServiceCode(p[3:7]), nil 70 | } 71 | -------------------------------------------------------------------------------- /dccp/stack.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "runtime" 11 | ) 12 | 13 | func Caller() string { 14 | pc, file, line, ok := runtime.Caller(2) 15 | if !ok { 16 | return "" 17 | } 18 | fn := runtime.FuncForPC(pc) 19 | if fn == nil { 20 | return fmt.Sprintf("(%s:%d)", file, line) 21 | } 22 | return fmt.Sprintf("%-61s (%s:%d)", fn.Name(), file, line) 23 | } 24 | 25 | // StackTrace formats the stack trace of the calling goroutine, 26 | // excluding pointer information and including DCCP runtime-specific information, 27 | // in a manner convenient for debugging DCCP 28 | func StackTrace(labels []string, skip int, sfile string, sline int) string { 29 | var w bytes.Buffer 30 | var stk []uintptr = make([]uintptr, 32) // DCCP logic stack should not be deeper than that 31 | n := runtime.Callers(skip+1, stk) 32 | stk = stk[:n] 33 | var utf2byte int 34 | for _, l := range labels { 35 | fmt.Fprintf(&w, "%s·", l) 36 | utf2byte++ 37 | } 38 | for w.Len() < 40 + 4 + utf2byte { 39 | w.WriteRune(' ') 40 | } 41 | fmt.Fprintf(&w, " (%s:%d)\n", sfile, sline) 42 | var external bool 43 | for _, pc := range stk { 44 | f := runtime.FuncForPC(pc) 45 | if f == nil { 46 | break 47 | } 48 | file, line := f.FileLine(pc) 49 | fname, isdccp := TrimFuncName(f.Name()) 50 | if !isdccp { 51 | external = true 52 | } else { 53 | if external { 54 | fmt.Fprintf(&w, " ···· ···· ···· \n") 55 | } 56 | fmt.Fprintf(&w, " %-40s (%s:%d)\n", fname, TrimSourceFile(file), line) 57 | } 58 | } 59 | return string(w.Bytes()) 60 | } 61 | 62 | -------------------------------------------------------------------------------- /dccp/chanlink.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | import ( 8 | "net" 9 | "time" 10 | ) 11 | 12 | // ChanLink treats one side of a channel as an incoming packet link 13 | type ChanLink struct { 14 | Mutex 15 | in, out chan []byte 16 | } 17 | 18 | func NewChanPipe() (p, q *ChanLink) { 19 | c0 := make(chan []byte) 20 | c1 := make(chan []byte) 21 | return &ChanLink{in: c0, out: c1}, &ChanLink{in: c1, out: c0} 22 | } 23 | 24 | func (l *ChanLink) GetMTU() int { 25 | return 1500 26 | } 27 | 28 | func (l *ChanLink) SetReadDeadline(t time.Time) error { 29 | // SetReadDeadline does not apply because ChanLink returns 30 | // an EIO error if no input is available 31 | panic("SetReadDeadline does not apply") 32 | return nil 33 | } 34 | 35 | func (l *ChanLink) ReadFrom(buf []byte) (n int, addr net.Addr, err error) { 36 | l.Lock() 37 | in := l.in 38 | l.Unlock() 39 | if in == nil { 40 | return 0, nil, ErrBad 41 | } 42 | 43 | p, ok := <-in 44 | if !ok { 45 | return 0, nil, ErrIO 46 | } 47 | n = copy(buf, p) 48 | if n != len(p) { 49 | panic("insufficient buf len") 50 | } 51 | return len(p), nil, nil 52 | } 53 | 54 | func (l *ChanLink) WriteTo(buf []byte, addr net.Addr) (n int, err error) { 55 | l.Lock() 56 | out := l.out 57 | l.Unlock() 58 | if out == nil { 59 | return 0, ErrBad 60 | } 61 | 62 | p := make([]byte, len(buf)) 63 | copy(p, buf) 64 | out <- p 65 | return len(buf), nil 66 | } 67 | 68 | func (l *ChanLink) Close() error { 69 | l.Lock() 70 | defer l.Unlock() 71 | 72 | if l.out != nil { 73 | close(l.out) 74 | l.out = nil 75 | } 76 | return nil 77 | } 78 | -------------------------------------------------------------------------------- /dccp/sandbox/latency.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package sandbox 6 | 7 | import ( 8 | "sort" 9 | "github.com/petar/GoDCCP/dccp" 10 | ) 11 | 12 | type latencyQueue struct { 13 | env *dccp.Env 14 | amb *dccp.Amb 15 | queue []*pipeHeader 16 | } 17 | 18 | // Init initializes the queue for initial use 19 | func (x *latencyQueue) Init(env *dccp.Env, amb *dccp.Amb) { 20 | x.env = env 21 | x.amb = amb 22 | x.queue = make([]*pipeHeader, 0) 23 | } 24 | 25 | // Add adds a new item to the queue 26 | func (x *latencyQueue) Add(ph *pipeHeader) { 27 | x.queue = append(x.queue, ph) 28 | sort.Sort(pipeHeaderTimeSort(x.queue)) 29 | } 30 | 31 | // DeleteMin removes the item with lowest timestamp from the queue 32 | func (x *latencyQueue) DeleteMin() *pipeHeader { 33 | if len(x.queue) == 0 { 34 | return nil 35 | } 36 | ph := x.queue[0] 37 | x.queue = x.queue[1:] 38 | return ph 39 | } 40 | 41 | // TimeToMin returns the duration of time from now until the timestamp of the 42 | // earliest item in the queue 43 | func (x *latencyQueue) TimeToMin() (dur int64, present bool) { 44 | if len(x.queue) == 0 { 45 | return 0, false 46 | } 47 | now := x.env.Now() 48 | return max64(0, x.queue[0].DeliverTime - now), true 49 | } 50 | 51 | // pipeHeaderTimeSort sorts a slice of *pipeHeader by timestamp 52 | type pipeHeaderTimeSort []*pipeHeader 53 | 54 | func (t pipeHeaderTimeSort) Len() int { 55 | return len(t) 56 | } 57 | 58 | func (t pipeHeaderTimeSort) Less(i, j int) bool { 59 | return t[i].DeliverTime < t[j].DeliverTime 60 | } 61 | 62 | func (t pipeHeaderTimeSort) Swap(i, j int) { 63 | t[i], t[j] = t[j], t[i] 64 | } 65 | -------------------------------------------------------------------------------- /dccp/sandbox/guzzleplex.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package sandbox 6 | 7 | import ( 8 | "github.com/petar/GoDCCP/dccp" 9 | ) 10 | 11 | // TraceWriterPlex is a dccp.TraceWriter that replicates TraceWriter method invocations to a set of TraceWriters 12 | type TraceWriterPlex struct { 13 | guzzles []dccp.TraceWriter 14 | highlight []string 15 | } 16 | 17 | func NewTraceWriterPlex(guzzles ...dccp.TraceWriter) *TraceWriterPlex { 18 | return &TraceWriterPlex{ 19 | guzzles: guzzles, 20 | } 21 | } 22 | 23 | // HighlightSamples instructs the guzzle to highlight any records carrying samples of the given names 24 | func (t *TraceWriterPlex) HighlightSamples(samples ...string) { 25 | t.highlight = samples 26 | } 27 | 28 | // Add adds an additional guzzle to the plex 29 | func (t *TraceWriterPlex) Add(g dccp.TraceWriter) { 30 | t.guzzles = append(t.guzzles, g) 31 | } 32 | 33 | func (t *TraceWriterPlex) Write(r *dccp.Trace) { 34 | sample, ok := r.Sample() 35 | if ok { 36 | for _, hi := range t.highlight { 37 | if sample.Series == hi { 38 | r.SetHighlight() 39 | break 40 | } 41 | } 42 | } 43 | for _, g := range t.guzzles { 44 | g.Write(r) 45 | } 46 | } 47 | 48 | // Sync syncs all the guzzles in the plex 49 | func (t *TraceWriterPlex) Sync() error { 50 | var err error 51 | for _, g := range t.guzzles { 52 | e := g.Sync() 53 | if err != nil { 54 | err = e 55 | } 56 | } 57 | return err 58 | } 59 | 60 | // Close closes all the guzzles in the plex 61 | func (t *TraceWriterPlex) Close() error { 62 | var err error 63 | for _, g := range t.guzzles { 64 | e := g.Close() 65 | if err != nil { 66 | err = e 67 | } 68 | } 69 | return err 70 | } 71 | -------------------------------------------------------------------------------- /dccp/sandbox/clientserver.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package sandbox 6 | 7 | import ( 8 | "os" 9 | "path" 10 | "github.com/petar/GoDCCP/dccp" 11 | "github.com/petar/GoDCCP/dccp/ccid3" 12 | ) 13 | 14 | // NewEnv creates a dccp.Env for test purposes, whose dccp.TraceWriter writes to a file 15 | // and duplicates all emits to any number of additional guzzles, which are usually used to check 16 | // test conditions. The TraceWriterPlex is returned to facilitate adding further guzzles. 17 | func NewEnv(guzzleFilename string, guzzles ...dccp.TraceWriter) (env *dccp.Env, plex *TraceWriterPlex) { 18 | fileTraceWriter := dccp.NewFileTraceWriter(path.Join(os.Getenv("DCCPLOG"), guzzleFilename + ".emit")) 19 | plex = NewTraceWriterPlex(append(guzzles, fileTraceWriter)...) 20 | return dccp.NewEnv(plex), plex 21 | } 22 | 23 | // NewClientServerPipe creates a sandbox communication pipe and attaches a DCCP client and a DCCP 24 | // server to its endpoints. In addition to sending all emits to a standard DCCP log file, it sends a 25 | // copy of all emits to the dup TraceWriter. 26 | func NewClientServerPipe(env *dccp.Env) (clientConn, serverConn *dccp.Conn, clientToServer, serverToClient *headerHalfPipe) { 27 | llog := dccp.NewAmb("line", env) 28 | hca, hcb, _ := NewPipe(env, llog, "client", "server") 29 | ccid := ccid3.CCID3{} 30 | 31 | clog := dccp.NewAmb("client", env) 32 | clientConn = dccp.NewConnClient(env, clog, hca, ccid.NewSender(env, clog), ccid.NewReceiver(env, clog), 0) 33 | 34 | slog := dccp.NewAmb("server", env) 35 | serverConn = dccp.NewConnServer(env, slog, hcb, ccid.NewSender(env, slog), ccid.NewReceiver(env, slog)) 36 | 37 | return clientConn, serverConn, hca, hcb 38 | } 39 | -------------------------------------------------------------------------------- /dccp/headerutil.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | func (h *Header) HasAckNo() bool { return getAckNoSubheaderSize(h.Type, h.X) > 0 } 8 | 9 | // InitResetHeader() creates a new Reset header 10 | func (h *Header) InitResetHeader(resetCode byte) { 11 | h.Type = Reset 12 | h.X = true 13 | h.ResetCode = resetCode 14 | } 15 | 16 | // InitCloseHeader() creates a new Close header 17 | func (h *Header) InitCloseHeader() { 18 | h.Type = Close 19 | h.X = true 20 | } 21 | 22 | // InitAckHeader() creates a new Ack header 23 | func (h *Header) InitAckHeader() { 24 | h.Type = Ack 25 | h.X = true 26 | } 27 | 28 | // InitDataHeader() creates a new Data header 29 | func (h *Header) InitDataHeader(data []byte) { 30 | h.Type = Data 31 | h.X = true 32 | h.Data = data 33 | } 34 | 35 | // InitDataAckHeader() creates a new DataAck header 36 | func (h *Header) InitDataAckHeader(data []byte) { 37 | h.Type = DataAck 38 | h.X = true 39 | h.Data = data 40 | } 41 | 42 | // InitSyncHeader() creates a new Sync header 43 | func (h *Header) InitSyncHeader() { 44 | h.Type = Sync 45 | h.X = true 46 | } 47 | 48 | // InitSyncAckHeader() creates a new Sync header 49 | func (h *Header) InitSyncAckHeader() { 50 | h.Type = SyncAck 51 | h.X = true 52 | } 53 | 54 | // InitRequestHeader() creates a new Request header 55 | func (h *Header) InitRequestHeader(serviceCode uint32) { 56 | h.Type = Request 57 | h.X = true 58 | h.ServiceCode = serviceCode 59 | } 60 | 61 | // InitResponseHeader() creates a new Response header 62 | func (h *Header) InitResponseHeader(serviceCode uint32) { 63 | h.Type = Response 64 | h.X = true 65 | h.ServiceCode = serviceCode 66 | } 67 | -------------------------------------------------------------------------------- /cmd/dccp-inspector/trip.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "sort" 10 | //"github.com/petar/GoGauge/gauge" 11 | "github.com/petar/GoDCCP/dccp" 12 | dccp_gauge "github.com/petar/GoDCCP/dccp/gauge" 13 | ) 14 | 15 | var ( 16 | printTripSep = &PrintRecord{ 17 | Text: 18 | "——————————————————————————————————————————————" + 19 | "——————————————————————————————————————————————" + 20 | "———————————————————————————————", 21 | } 22 | printHalveSep = &PrintRecord{ 23 | Text: fmt.Sprintf("%s=%s=%s=%s=%s", skipState, skip, skip, skip, skipState), 24 | } 25 | printNop = &PrintRecord{} 26 | ) 27 | 28 | func printTrip(emits []*dccp.LogRecord) { 29 | reducer := dccp_gauge.NewLogReducer() 30 | for _, rec := range emits { 31 | reducer.Write(rec) 32 | } 33 | trips := dccp_gauge.TripMapToSlice(reducer.Trips()) 34 | sort.Sort(TripSeqNoSort(trips)) 35 | 36 | prints := make([]*PrintRecord, 0) 37 | for _, t := range trips { 38 | prints = append(prints, printNop) 39 | for _, r := range t.Forward { 40 | p := printRecord(r) 41 | if p != nil { 42 | prints = append(prints, p) 43 | } 44 | } 45 | prints = append(prints, printHalveSep) 46 | for _, r := range t.Backward { 47 | p := printRecord(r) 48 | if p != nil { 49 | prints = append(prints, p) 50 | } 51 | } 52 | prints = append(prints, printNop) 53 | prints = append(prints, printTripSep) 54 | } 55 | Print(prints, false) 56 | } 57 | 58 | // TripSeqNoSort sorts an array of trips by sequence number 59 | type TripSeqNoSort []*dccp_gauge.Trip 60 | 61 | func (t TripSeqNoSort) Len() int { 62 | return len(t) 63 | } 64 | 65 | func (t TripSeqNoSort) Less(i, j int) bool { 66 | return t[i].SeqNo < t[j].SeqNo 67 | } 68 | 69 | func (t TripSeqNoSort) Swap(i, j int) { 70 | t[i], t[j] = t[j], t[i] 71 | } 72 | -------------------------------------------------------------------------------- /dccp/sandbox/DONE: -------------------------------------------------------------------------------- 1 | Loss 2 | ---- 3 | 4 | Why does the (roundtrip/loss) gauge report ~15% loss server-to-client, when the pipe is configured not 5 | to drop at all. 6 | These are packets dropped due to full send queue. They are Acks that are send in response to 7 | every received DataAck packet. 8 | 9 | ? 10 | 11 | Packets that are dropped due to "Slow strobe", i.e. before they even get into the send queue, 12 | are never assigned sequence numbers and therefore they are never captured by the internal 13 | sequence-number-based loss estimation mechanism. 14 | 15 | Roundtrip 16 | --------- 17 | 18 | Can endpoints estimate RTT? 19 | * Yes. Test included. 20 | 21 | client·sender never gets an Ack or DataAck (during idle periods), which 22 | carries Elapsed option, which is needed for RTT estimate 23 | * See [OptAck] below. 24 | 25 | [OptAck] Should Elapsed options be limited to Ack and DataAck, as opposed to also 26 | other packets that contain acknowledgements? Are there other such packet types? 27 | * Only Ack and DataAck have acks which is needed for Elapsed and, yes, 28 | Elapsed is limitted to those only by design, since the lack of those implies 29 | slow app send speed an no need for good estimates of RTT. 30 | 31 | Need mechanism to ensure Ack sent back to client in presence of Idle? 32 | * No. It's OK if client stays in PARTOPEN. Just means it must 33 | send Acks with any data it sends. RFC Section 8.1.5 for details. 34 | 35 | Ensure that user-side of DCCP client is available for i/o when in 36 | PARTOPEN, but until in PARTOPEN all data is sent in DataAcks. 37 | 38 | Client sends 10 packets in the first second, only 3 of which are received? 39 | No drops are reported. Is there a programmatic delay? 40 | * Sender's rate control switches to 1pps at the start 41 | 42 | Why don't we drop data packets if the data queue is full? 43 | * Because making the user Write wait is the only way to communicate to the user 44 | at what rate to send data. 45 | -------------------------------------------------------------------------------- /dccp/backoff.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | import "io" 8 | 9 | // backOff{} 10 | type backOff struct { 11 | env *Env 12 | sleep int64 // Duration of next sleep interval 13 | lifetime int64 // Total lifetime so far 14 | timeout int64 // Maximum time the backoff mechanism stays alive 15 | backoffFreq int64 // Backoff period. The sleep duration backs off approximately every backoffFreq nanoseconds 16 | lastBackoff int64 // Last time the sleep interval was backed off, relative to the starting time 17 | } 18 | 19 | // newBackOff() creates a new back-off timer whose first wait period is firstSleep 20 | // nanoseconds. Approximately every backoffFreq nanoseconds, the sleep timers backs off 21 | // (increases by a factor of 4/3). The lifetime of the backoff sleep intervals does not 22 | // exceed timeout. 23 | func newBackOff(env *Env, firstSleep, timeout, backoffFreq int64) *backOff { 24 | return &backOff{ 25 | env: env, 26 | sleep: firstSleep, 27 | lifetime: 0, 28 | timeout: timeout, 29 | backoffFreq: backoffFreq, 30 | lastBackoff: 0, 31 | } 32 | } 33 | 34 | // BackoffMin is the minimum time before two firings of the backoff timers 35 | const BackoffMin = 100e6 36 | 37 | // Sleep() blocks for the duration of the next sleep interval in the back-off 38 | // sequence and return nil. If the maximum total sleep time has been reached, 39 | // Sleep() returns os.EOF without sleeping. 40 | func (b *backOff) Sleep() (error, int64) { 41 | if b.lifetime >= b.timeout { 42 | return io.EOF, 0 43 | } 44 | effectiveSleep := max64(BackoffMin, b.sleep) 45 | b.env.Sleep(effectiveSleep) 46 | b.lifetime += effectiveSleep 47 | if b.lifetime-b.lastBackoff >= b.backoffFreq { 48 | b.sleep = (4 * b.sleep) / 3 49 | b.lastBackoff = b.lifetime 50 | } 51 | return nil, b.env.Now() 52 | } 53 | -------------------------------------------------------------------------------- /dccp/term.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | // abortWith() resets the connection with Reset Code resetCode 8 | func (c *Conn) abortWith(resetCode byte) { 9 | c.Lock() 10 | c.setError(ErrAbort) 11 | c.gotoCLOSED() 12 | c.inject(c.generateReset(resetCode)) 13 | c.Unlock() 14 | c.teardownUser() 15 | c.teardownWriteLoop() 16 | } 17 | 18 | // abort() resets the connection with Reset Code 2, "Aborted" 19 | func (c *Conn) abort() { c.abortWith(ResetAborted) } 20 | 21 | func (c *Conn) setError(err error) { 22 | c.AssertLocked() 23 | if c.err != nil { 24 | return 25 | } 26 | c.err = err 27 | } 28 | 29 | func (c *Conn) reset(resetCode byte, err error) { 30 | c.AssertLocked() 31 | c.setError(err) 32 | c.gotoCLOSED() 33 | c.inject(c.generateReset(resetCode)) 34 | c.teardownUser() 35 | c.teardownWriteLoop() 36 | } 37 | 38 | // abortQuietly() aborts the connection immediately without sending Reset packets 39 | func (c *Conn) abortQuietly() { 40 | c.Lock() 41 | c.setError(ErrAbort) 42 | c.gotoCLOSED() 43 | c.Unlock() 44 | c.teardownUser() 45 | c.teardownWriteLoop() 46 | } 47 | 48 | // teardownUser MUST be idempotent. It may be called with or without lock on c. 49 | func (c *Conn) teardownUser() { 50 | c.readAppLk.Lock() 51 | if c.readApp != nil { 52 | close(c.readApp) 53 | c.readApp = nil 54 | } 55 | c.readAppLk.Unlock() 56 | c.writeDataLk.Lock() 57 | if c.writeData != nil { 58 | close(c.writeData) 59 | c.writeData = nil 60 | } 61 | c.writeDataLk.Unlock() 62 | } 63 | 64 | // teardownWriteLoop MUST be idempotent. It may be called with or without lock on c. 65 | func (c *Conn) teardownWriteLoop() { 66 | c.writeNonDataLk.Lock() 67 | defer c.writeNonDataLk.Unlock() 68 | if c.writeNonData != nil { 69 | close(c.writeNonData) 70 | c.writeNonData = nil 71 | } 72 | c.scc.Close() 73 | c.rcc.Close() 74 | } 75 | -------------------------------------------------------------------------------- /retransmit/read.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package retransmit 6 | 7 | import ( 8 | "os" 9 | "github.com/petar/GoDCCP/dccp" 10 | ) 11 | 12 | // readHeader parses a header from its wire representation 13 | func readHeader(buf []byte) (h *header, err os.Error) { 14 | 15 | h = &header{} 16 | k := 0 17 | 18 | // Read type 19 | if len(buf) < 1 { 20 | return nil, dccp.ErrSize 21 | } 22 | t := buf[k] 23 | k++ 24 | if t & AckFlag != 0 { 25 | h.Ack = true 26 | } 27 | if t & SyncFlag != 0 { 28 | h.Sync = true 29 | } 30 | if t & DataFlag != 0 { 31 | h.Data = true 32 | } 33 | 34 | // Read Ack section 35 | if h.Ack { 36 | if len(buf) - k < 2 + 4 + 2 { 37 | return nil, dccp.ErrSize 38 | } 39 | 40 | // Read AckSyncNo 41 | h.AckSyncNo = dccp.Decode2ByteUint(buf[k:k+2]) 42 | k += 2 43 | 44 | // Read AckDataNo 45 | h.AckDataNo = dccp.Decode4ByteUint(buf[k:k+4]) 46 | k += 4 47 | 48 | // Read AckMapLen 49 | ackMapLen := dccp.Decode2ByteUint(buf[k:k+2]) 50 | k += 2 51 | 52 | if len(buf) - k < int(ackMapLen) { 53 | return nil, dccp.ErrSize 54 | } 55 | 56 | // Read AckMap 57 | h.AckMap = buf[k:k+int(ackMapLen)] 58 | k += int(ackMapLen) 59 | } 60 | 61 | // Read Sync section 62 | if h.Sync { 63 | if len(buf) - k < 2 { 64 | return nil, dccp.ErrSize 65 | } 66 | 67 | // Read SyncNo 68 | h.SyncNo = dccp.Decode2ByteUint(buf[k:k+2]) 69 | k += 2 70 | } 71 | 72 | // Read Data section 73 | if h.Data { 74 | if len(buf) - k < 4 + 2 { 75 | return nil, dccp.ErrSize 76 | } 77 | 78 | // Read DataNo 79 | h.DataNo = dccp.Decode4ByteUint(buf[k:k+4]) 80 | k += 4 81 | 82 | // Read DataLen 83 | dataLen := dccp.Decode2ByteUint(buf[k:k+2]) 84 | k += 2 85 | 86 | if len(buf) - k < int(dataLen) { 87 | return nil, dccp.ErrSize 88 | } 89 | 90 | // Read DataCargo 91 | h.DataCargo = buf[k:k+int(dataLen)] 92 | k += int(dataLen) 93 | } 94 | 95 | return h, nil 96 | } 97 | -------------------------------------------------------------------------------- /playground/conntest.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "log" 9 | // "rand" 10 | "time" 11 | . "github.com/petar/GoDCCP/dccp" 12 | ) 13 | 14 | func main() { 15 | //rand.Seed(time.Nanoseconds()) 16 | 17 | InstallCtrlCPanic() 18 | defer SavePanicTrace() 19 | defer time.Sleep(2e9) // Sleep for 1 min after end 20 | 21 | // Install stacks 22 | linka, linkb := NewChanPipe() 23 | newscc := NewFixedRateSenderControlFunc(12) 24 | newrcc := NewFixedRateReceiverControlFunc() 25 | stacka, stackb := NewStack(linka, newscc, newrcc), NewStack(linkb, newscc, newrcc) 26 | 27 | // Establish connection 28 | ca, err := stacka.Dial(nil, 1) 29 | if err != nil { 30 | log.Printf("side a dial: %s", err) 31 | } 32 | cb, err := stackb.Accept() 33 | if err != nil { 34 | log.Printf("side b accept: %s", err) 35 | } 36 | 37 | // Prepare random block 38 | p := []byte("hello world!") 39 | 40 | // Write and read the block 41 | for t := 0; t < 30; t++ { 42 | err = ca.WriteBlock(p) 43 | if err != nil { 44 | log.Printf("side a write: %s", err) 45 | } 46 | q, err := cb.ReadBlock() 47 | if err != nil { 48 | log.Printf("side b read: %s", err) 49 | } 50 | log.Printf("<--%d--|%s|\n", t, string(q)) 51 | 52 | // Compare 53 | if !byteSlicesEqual(p, q) { 54 | log.Printf("read and write blocks differ") 55 | } 56 | 57 | /* 58 | err = cb.WriteBlock(p) 59 | if err != nil { 60 | log.Printf("side b write: %s", err) 61 | } 62 | q, err = ca.ReadBlock() 63 | if err != nil { 64 | log.Printf("side a read: %s", err) 65 | } 66 | log.Printf(">--%d--|%s|\n", t, string(q)) 67 | */ 68 | } 69 | 70 | // Close connection 71 | err = ca.Close() 72 | if err != nil { 73 | log.Printf("side a close: %s", err) 74 | } 75 | err = cb.Close() 76 | if err != nil { 77 | log.Printf("side b close: %s", err) 78 | } 79 | } 80 | 81 | func byteSlicesEqual(a,b []byte) bool { 82 | if len(a) != len(b) { 83 | return false 84 | } 85 | for i, x := range a { 86 | if b[i] != x { 87 | return false 88 | } 89 | } 90 | return true 91 | } 92 | -------------------------------------------------------------------------------- /dccp/sandbox/loss_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package sandbox 6 | 7 | import ( 8 | "fmt" 9 | "testing" 10 | "github.com/petar/GoDCCP/dccp" 11 | "github.com/petar/GoDCCP/dccp/ccid3" 12 | ) 13 | 14 | const ( 15 | lossDuration = 10e9 // Duration of the experiment in ns 16 | lossSendRate = 40 // Fixed sender rate in pps 17 | lossTransmitRate = 20 // Fixed transmission rate of the network in pps 18 | ) 19 | 20 | // TestLoss checks that loss estimation matches actual 21 | func TestLoss(t *testing.T) { 22 | 23 | env, plex := NewEnv("loss") 24 | reducer := NewMeasure(env, t) 25 | plex.Add(reducer) 26 | plex.HighlightSamples(ccid3.LossReceiverEstimateSample) 27 | 28 | clientConn, serverConn, clientToServer, _ := NewClientServerPipe(env) 29 | 30 | payload := []byte{1, 2, 3} 31 | buf := make([]byte, len(payload)) 32 | 33 | // In order to force packet loss, we fix the send rate slightly above the 34 | // the pipeline rate. 35 | clientConn.Amb().Flags().SetUint32("FixRate", lossSendRate) 36 | serverConn.Amb().Flags().SetUint32("FixRate", lossSendRate) 37 | clientToServer.SetWriteRate(1e9, lossTransmitRate) 38 | 39 | cchan := make(chan int, 1) 40 | env.Go(func() { 41 | t0 := env.Now() 42 | for env.Now() - t0 < lossDuration { 43 | err := clientConn.Write(buf) 44 | if err != nil { 45 | break 46 | } 47 | } 48 | clientConn.Close() 49 | close(cchan) 50 | }, "test client") 51 | 52 | schan := make(chan int, 1) 53 | env.Go(func() { 54 | for { 55 | _, err := serverConn.Read() 56 | if err != nil { 57 | break 58 | } 59 | } 60 | close(schan) 61 | }, "test server") 62 | 63 | _, _ = <-cchan 64 | _, _ = <-schan 65 | 66 | fmt.Println(reducer.String()) 67 | 68 | // Shutdown the connections properly 69 | clientConn.Abort() 70 | serverConn.Abort() 71 | env.NewGoJoin("end-of-test", clientConn.Joiner(), serverConn.Joiner()).Join() 72 | dccp.NewAmb("line", env).E(dccp.EventMatch, "Server and client done.") 73 | if err := env.Close(); err != nil { 74 | t.Errorf("error closing runtime (%s)", err) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /retransmit/write.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package retransmit 6 | 7 | import ( 8 | "os" 9 | "github.com/petar/GoDCCP/dccp" 10 | ) 11 | 12 | func (h *header) getFootprintLen() int { 13 | a := 0 14 | if h.Ack { 15 | a = 2 + 2 + 4 + len(h.AckMap) 16 | } 17 | s := 0 18 | if h.Sync { 19 | s = 2 20 | } 21 | d := 0 22 | if h.Data { 23 | d = 4 + 2 + len(h.DataCargo) 24 | } 25 | return 1 + a + s + d 26 | } 27 | 28 | const ( 29 | AckFlag = 1 << iota 30 | SyncFlag 31 | DataFlag 32 | ) 33 | 34 | // Write returns the wire format represenatation of h 35 | func (h *header) Write() (buf []byte, err os.Error) { 36 | buf = make([]byte, h.getFootprintLen()) 37 | k := 0 38 | 39 | // Write type 40 | var t byte = 0 41 | if h.Ack { 42 | t |= AckFlag 43 | } 44 | if h.Sync { 45 | t |= SyncFlag 46 | } 47 | if h.Data { 48 | t |= DataFlag 49 | } 50 | buf[k] = t 51 | k++ 52 | 53 | // Write Ack section 54 | if h.Ack { 55 | // Write SyncAckNo 56 | dccp.Encode2ByteUint(h.AckSyncNo, buf[k:k+2]) 57 | k += 2 58 | 59 | // Write AckDataNo 60 | dccp.Encode4ByteUint(h.AckDataNo, buf[k:k+4]) 61 | k += 4 62 | 63 | // Write AckMapLen 64 | if !dccp.FitsIn2Bytes(uint64(len(h.AckMap))) { 65 | return nil, dccp.ErrOverflow 66 | } 67 | dccp.Encode2ByteUint(uint16(len(h.AckMap)), buf[k:k+2]) 68 | k += 2 69 | 70 | // Write AckMap 71 | copy(buf[k:k+len(h.AckMap)], h.AckMap) 72 | k += len(h.AckMap) 73 | } 74 | 75 | // Write Sync section 76 | if h.Sync { 77 | // Write SyncNo 78 | dccp.Encode2ByteUint(h.SyncNo, buf[k:k+2]) 79 | k += 2 80 | } 81 | 82 | // Write Data section 83 | if h.Data { 84 | // Write DataNo 85 | dccp.Encode4ByteUint(h.DataNo, buf[k:k+4]) 86 | k += 4 87 | 88 | // Write DataLen 89 | if !dccp.FitsIn2Bytes(uint64(len(h.DataCargo))) { 90 | return nil, dccp.ErrSize 91 | } 92 | dccp.Encode2ByteUint(uint16(len(h.DataCargo)), buf[k:k+2]) 93 | k += 2 94 | 95 | // Write DataCargo 96 | copy(buf[k:k+len(h.DataCargo)], h.DataCargo) 97 | k += len(h.DataCargo) 98 | } 99 | 100 | return buf, nil 101 | } 102 | -------------------------------------------------------------------------------- /retransmit/conn.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package retransmit 6 | 7 | import ( 8 | "io" 9 | "os" 10 | "github.com/petar/GoDCCP/dccp" 11 | ) 12 | 13 | func NewRetransmit(bc dccp.BlockConn) io.ReadWriteCloser { 14 | c := &conn{ 15 | bc: bc, 16 | readFirst: 0, 17 | readWin: make([][]byte, RETRANSMIT_WIDTH), 18 | readChan: make(chan []byte), 19 | } 20 | go c.readLoop() 21 | go c.writeLoop() 22 | return c 23 | } 24 | 25 | type conn struct { 26 | bc dccp.BlockConn 27 | 28 | // Read fields 29 | readLock dccp.Mutex 30 | readTail []byte 31 | 32 | // readLoop fields 33 | readFirst uint32 34 | readWin [][]byte 35 | readChan chan []byte 36 | 37 | // Write fields 38 | writeLock dccp.Mutex 39 | writeFirst uint32 40 | writeWin [][]byte 41 | writeChan chan []byte 42 | } 43 | 44 | const RETRANSMIT_WIDTH = 128 // The retransmit window width must be a multiple of 8 45 | 46 | func (c *conn) Read(p []byte) (n int, err os.Error) { 47 | c.readLock.Lock() 48 | defer c.readLock.Unlock() 49 | 50 | for len(p) > 0 { 51 | // Read incoming data 52 | if len(c.readTail) == 0 { 53 | b, ok := <-c.readChan 54 | if !ok { 55 | return n, os.EOF 56 | } 57 | c.readTail = b 58 | } 59 | 60 | // Copy to user buffer 61 | k := copy(p, c.readTail) 62 | c.readTail = c.readTail[k:] 63 | p = p[k:] 64 | n += k 65 | } 66 | 67 | return n, nil 68 | } 69 | 70 | func makeAckMap(win [][]byte, first uint32) []byte { 71 | am := make([]byte, len(win) / 8) 72 | for i, b := range win { 73 | if b == nil { 74 | am[i/8] |= (1 << uint(i % 8)) 75 | } 76 | } 77 | return am 78 | } 79 | 80 | func (c *conn) readLoop() { 81 | for { 82 | b, err := c.bc.ReadBlock() 83 | if err != nil { 84 | break 85 | } 86 | h, err := readHeader(b) 87 | if err != nil { 88 | break 89 | } 90 | 91 | ? 92 | } 93 | ?? 94 | } 95 | 96 | func (c *conn) Write(p []byte) (n int, err os.Error) { 97 | panic("") 98 | } 99 | 100 | func (c *conn) writeLoop() { 101 | panic("") 102 | } 103 | 104 | func (c *conn) Close() os.Error { 105 | panic("") 106 | } 107 | -------------------------------------------------------------------------------- /dccp/gen.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | // Special case seq and ack numbers 8 | 9 | // generateAbnormalReset() generates a new out-of-sync Reset header, according to Section 8.3.1 10 | func (c *Conn) generateAbnormalReset(resetCode byte, inResponseTo *Header) *writeHeader { 11 | h := &writeHeader{} 12 | h.Header.InitResetHeader(resetCode) 13 | h.SeqAckType = seqAckAbnormal 14 | h.InResponseTo = inResponseTo 15 | return h 16 | } 17 | 18 | func (c *Conn) generateSyncAck(inResponseTo *Header) *writeHeader { 19 | h := &writeHeader{} 20 | h.Header.InitSyncAckHeader() 21 | h.SeqAckType = seqAckSyncAck 22 | h.InResponseTo = inResponseTo 23 | return h 24 | } 25 | 26 | // Common case seq and ack numbers 27 | 28 | func (c *Conn) generateReset(resetCode byte) *writeHeader { 29 | h := &writeHeader{} 30 | h.Header.InitResetHeader(resetCode) 31 | h.SeqAckType = seqAckNormal 32 | return h 33 | } 34 | 35 | func (c *Conn) generateSync() *writeHeader { 36 | h := &writeHeader{} 37 | h.Header.InitSyncHeader() 38 | h.SeqAckType = seqAckNormal 39 | return h 40 | } 41 | 42 | func (c *Conn) generateRequest(serviceCode uint32) *writeHeader { 43 | h := &writeHeader{} 44 | h.Header.InitRequestHeader(serviceCode) 45 | h.SeqAckType = seqAckNormal 46 | return h 47 | } 48 | 49 | func (c *Conn) generateResponse(serviceCode uint32) *writeHeader { 50 | h := &writeHeader{} 51 | h.Header.InitResponseHeader(serviceCode) 52 | h.SeqAckType = seqAckNormal 53 | return h 54 | } 55 | 56 | func (c *Conn) generateClose() *writeHeader { 57 | h := &writeHeader{} 58 | h.Header.InitCloseHeader() 59 | h.SeqAckType = seqAckNormal 60 | return h 61 | } 62 | 63 | func (c *Conn) generateAck() *writeHeader { 64 | h := &writeHeader{} 65 | h.Header.InitAckHeader() 66 | h.SeqAckType = seqAckNormal 67 | return h 68 | } 69 | 70 | func (c *Conn) generateData(data []byte) *writeHeader { 71 | h := &writeHeader{} 72 | h.Header.InitDataHeader(data) 73 | h.SeqAckType = seqAckNormal 74 | return h 75 | } 76 | 77 | func (c *Conn) generateDataAck(data []byte) *writeHeader { 78 | h := &writeHeader{} 79 | h.Header.InitDataAckHeader(data) 80 | h.SeqAckType = seqAckNormal 81 | return h 82 | } 83 | -------------------------------------------------------------------------------- /dccp/listener.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | /* 8 | import ( 9 | "net" 10 | ) 11 | 12 | 13 | // Listener{} takes care of listening for incoming connections. 14 | // It handles the LISTEN state. 15 | type Listener struct { 16 | m *Mux 17 | conns []*Conn // List of active connections 18 | // TODO: Lookups in a short array should be fine for now. Hashing? 19 | } 20 | 21 | func Listen(m *Mux) net.Listener { 22 | l := &Listener{ m: m, } 23 | go l.loop() 24 | return l 25 | } 26 | 27 | // loop() reads and processes incoming packets 28 | func (l *Listener) loop() { 29 | for { 30 | h,err := e.readPacket(l.phy, zeroFlowID) 31 | if err != nil { 32 | continue XX // no continue 33 | } 34 | ? 35 | } 36 | } 37 | 38 | // readAndSwitch() reads an incoming packet and sends it over to 39 | // its Conn{} destination if any, or returns it otherwise 40 | func (l *listener) readAndSwitch() (h *Header, err os.Error) { 41 | h, err = read(l.phy) 42 | if err != nil { 43 | return 44 | } 45 | ? 46 | } 47 | 48 | // read() reads the next buffer of data from the link layer 49 | // and tries to parse it into a valid Header{} 50 | func read(r Reader) (*Header, os.Error) { 51 | 52 | // Read packet from physical layer 53 | buf, phyFlowID, err := r.Read() 54 | if err != nil { 55 | return nil, err 56 | } 57 | // Parse generic header 58 | h, err := ReadHeader(buf, zeroPhyFlowID.SourceAddr, zeroPhyFlowID.DestAddr, AnyProto, false) 59 | if err != nil { 60 | return nil, err 61 | } 62 | // Ensure extended SeqNo's 63 | if !h.X { 64 | return nil, ErrUnsupported 65 | } 66 | return h, nil 67 | } 68 | 69 | func arrayEqual(a,b []byte) bool { 70 | if len(a) != len(b) { 71 | return false 72 | } 73 | for i := 0; i < len(a); i++ { 74 | if a[i] != b[i] { 75 | return false 76 | } 77 | } 78 | return true 79 | } 80 | 81 | func (l *Listener) Accept() (c Conn, err os.Error) { 82 | ? //XX 83 | h,err := e.readPacket() 84 | if err != nil { 85 | return nil, err 86 | } 87 | 88 | } 89 | 90 | func (l *Listener) Close() os.Error { 91 | ? //XX 92 | return l.phy.Close(); 93 | } 94 | 95 | func (l *Listener) Addr() net.Addr { return DefaultAddr } 96 | 97 | */ 98 | -------------------------------------------------------------------------------- /dccp/readwrite_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "reflect" 11 | "testing" 12 | ) 13 | 14 | var testHeaders = []*Header{ 15 | &Header{ 16 | SourcePort: 33, 17 | DestPort: 77, 18 | CCVal: 1, 19 | CsCov: CsCov8, 20 | Type: Ack, 21 | X: true, 22 | SeqNo: 0x0000334455667788, 23 | AckNo: 0x0000112233445566, 24 | ServiceCode: 0, 25 | ResetCode: 0, 26 | ResetData: nil, 27 | Options: []*Option{ 28 | &Option{OptionSlowReceiver, nil, true}, 29 | }, 30 | Data: []byte{1, 2, 3, 0, 4, 5, 6, 7, 8, 9}, 31 | }, 32 | } 33 | // Expected wire representation: 34 | // 35 | // Source Port 00·21· 36 | // Dest Port 00·4d· 37 | // Data Offset 07· 38 | // CCVal/CsCov 13· 39 | // Checksum 38·13· 40 | // Res/Type/X 07· 41 | // Reserved 00· 42 | // SeqNo 33·44·55·66·77·88· 43 | // AckNo 00·00·11·22·33·44·55·66· 44 | // Options 01·02·00·00· 45 | // Data 01·02·03·00·04·05·06·07·08·09 46 | 47 | func TestReadWrite(t *testing.T) { 48 | for _, gh := range testHeaders { 49 | hd, err := gh.Write([]byte{1, 2, 3, 4}, []byte{5, 6, 7, 8}, 34, false) 50 | if err != nil { 51 | t.Errorf("write error: %s", err) 52 | } 53 | fmt.Println(dumpBytes(hd)) 54 | gh2, err := ReadHeader(hd, []byte{1, 2, 3, 4}, []byte{5, 6, 7, 8}, 34, false) 55 | if err != nil { 56 | t.Errorf("read error: %s", err) 57 | } else { 58 | diff(t, "** ", gh2, gh) 59 | } 60 | } 61 | } 62 | 63 | func dumpBytes(bb []byte) string { 64 | var w bytes.Buffer 65 | for _, b := range bb { 66 | fmt.Fprintf(&w, "%02x·", b) 67 | } 68 | return string(w.Bytes()) 69 | } 70 | 71 | func diff(t *testing.T, prefix string, have, want interface{}) { 72 | hv := reflect.ValueOf(have).Elem() 73 | wv := reflect.ValueOf(want).Elem() 74 | if hv.Type() != wv.Type() { 75 | t.Errorf("%s: type mismatch %v vs %v", prefix, hv.Type(), wv.Type()) 76 | } 77 | for i := 0; i < hv.NumField(); i++ { 78 | hf := hv.Field(i).Interface() 79 | wf := wv.Field(i).Interface() 80 | if !reflect.DeepEqual(hf, wf) { 81 | t.Errorf("%s: %s = %v want %v", prefix, hv.Type().Field(i).Name, hf, wf) 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /dccp/err.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | // ProtoError is a type that wraps all DCCP-specific errors. 8 | // It is utilized to distinguish these errors from others, using type checks. 9 | type ProtoError string 10 | 11 | func (e ProtoError) Error() string { return string(e) } 12 | 13 | func NewError(s string) error { return ProtoError(s) } 14 | 15 | // TODO: Annotate each error with the circumstances that can cause it 16 | var ( 17 | ErrInvalid = NewError("invalid argument") // Invalid arguments passed to a routine 18 | ErrAlign = NewError("align") 19 | ErrSize = NewError("size") 20 | ErrSemantic = NewError("semantic") 21 | ErrSyntax = NewError("syntax") 22 | ErrNumeric = NewError("numeric") 23 | ErrOption = NewError("option") 24 | ErrOptionsTooBig = NewError("options too big") 25 | ErrOversize = NewError("over size") 26 | ErrCsCov = NewError("cscov") 27 | ErrChecksum = NewError("checksum") 28 | ErrIPFormat = NewError("ip format") 29 | ErrUnknownType = NewError("unknown packet type") 30 | ErrUnsupported = NewError("unsupported") 31 | ErrProto = NewError("protocol error") 32 | ErrDrop = NewError("dropped") 33 | ErrReset = NewError("reset") 34 | ErrTooBig = NewError("too big") 35 | ErrOverflow = NewError("overflow") 36 | ) 37 | 38 | // Connection errors 39 | var ( 40 | ErrEOF = NewError("i/o eof") 41 | ErrAbort = NewError("i/o aborted") 42 | ErrTimeout = NewError("i/o timeout") 43 | ErrBad = NewError("i/o bad connection") 44 | ErrIO = NewError("i/o error") 45 | ) 46 | 47 | // Congestion Control errors/events 48 | 49 | // CongestionReset is sent from Congestion Control to Conn to indicate that 50 | // the connection must be reset. CongestionReset encloses the desired Reset Code. 51 | type CongestionReset byte 52 | 53 | func (ce CongestionReset) Error() string { return "cc-reset(" + resetCodeString(byte(ce)) + ")" } 54 | 55 | func (ce CongestionReset) ResetCode() byte { return byte(ce) } 56 | 57 | func NewCongestionReset(resetCode byte) error { return CongestionReset(resetCode) } 58 | 59 | // CongestionAck is sent from Congestion Control to Conn to advise that an 60 | // Ack packet should be sent to the other side. 61 | var CongestionAck = NewError("cc-ack") 62 | -------------------------------------------------------------------------------- /dccp/ccid3/receiverrate.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package ccid3 6 | 7 | import ( 8 | "github.com/petar/GoDCCP/dccp" 9 | ) 10 | 11 | // receiverRateCalculator keeps track of the data receive rate at the CCID3 receiver, 12 | // and produces Receive Rate options for outgoing feedback packets. 13 | // It's function is specified in RFC 4342, Section 8.3. 14 | // 15 | // XXX: Section 8.1, on the other hand, seems to suggest an alternative 16 | // mechanism for computing receive rate, based on the window counter values 17 | // in CCVal. 18 | type receiverRateCalculator struct { 19 | data0, data1 int 20 | time0, time1 int64 21 | } 22 | 23 | func (r *receiverRateCalculator) Init() { 24 | r.time0, r.time1 = 0, 0 25 | r.data0, r.data1 = 0, 0 26 | } 27 | 28 | // OnRead is called to let the receiverRateCalculator know that data has been received. 29 | // The ccval window counter value is not used in the current rate receiver algorithm 30 | // explicitly. It is used implicitly in that the RTT estimate is based on these values. 31 | func (r *receiverRateCalculator) OnRead(ff *dccp.FeedforwardHeader) { 32 | if ff.Type != dccp.Data && ff.Type != dccp.DataAck { 33 | return 34 | } 35 | if r.time0 == 0 || r.time1 == 0 { 36 | r.time0 = ff.Time 37 | r.time1 = ff.Time 38 | } 39 | r.data0 += ff.DataLen 40 | r.data1 += ff.DataLen 41 | } 42 | 43 | // Flush returns a Receive Rate option and indicates to receiverRateCalculator 44 | // that the next Ack-to-Ack window has begun 45 | func (r *receiverRateCalculator) Flush(rtt int64, timeWrite int64) *ReceiveRateOption { 46 | if r.time0 > timeWrite || r.time1 > timeWrite { 47 | panic("receive rate time") 48 | } 49 | d0 := timeWrite - r.time0 50 | d1 := timeWrite - r.time1 51 | if d0 < d1 { 52 | panic("receive rate period") 53 | } 54 | if d1 < rtt { 55 | return &ReceiveRateOption{rate(r.data0, d0)} 56 | } 57 | rval := rate(r.data0, d0) 58 | r.data0, r.data1 = r.data1, 0 59 | r.time0, r.time1 = r.time1, timeWrite 60 | return &ReceiveRateOption{rval} 61 | } 62 | 63 | // Given data volume in bytes and time duration in nanoseconds, rate returns the 64 | // corresponding rate in bytes per second 65 | func rate(nbytes int, nsec int64) uint32 { 66 | if nbytes < 0 || nsec <= 0 { 67 | panic("receive rate, negative bytes or time") 68 | } 69 | return uint32((int64(nbytes)*1e9)/nsec) 70 | } 71 | -------------------------------------------------------------------------------- /dccp/addr.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | import ( 8 | "errors" 9 | "strconv" 10 | "strings" 11 | ) 12 | 13 | // Addr{} is a general-purpose data type intended for custom link layer addresses. 14 | type Addr struct { 15 | *Label 16 | Port uint16 17 | } 18 | 19 | var ZeroAddr = &Addr{} 20 | 21 | // Network() returns the name of the link address namespace, included to conform to net.Addr 22 | func (addr *Addr) Network() string { return "godccp-addr" } 23 | 24 | // String() returns the string represenation of the link address 25 | func (addr *Addr) String() string { 26 | return addr.Label.String() + ":" + strconv.Itoa(int(addr.Port)) 27 | } 28 | 29 | // Address() is identical to String(), included as a method so that Addr conforms to net.Addr 30 | func (addr *Addr) Address() string { return addr.String() } 31 | 32 | // ParseAddr() parses a link address from s@ in string format 33 | func ParseAddr(s string) (addr *Addr, n int, err error) { 34 | var label *Label 35 | label, n, err = ParseLabel(s) 36 | if err != nil { 37 | return nil, 0, err 38 | } 39 | s = s[n:] 40 | if len(s) == 0 { 41 | return nil, 0, errors.New("link addr missing port") 42 | } 43 | if s[0] != ':' { 44 | return nil, 0, errors.New("link addr expecting ':'") 45 | } 46 | n += 1 47 | s = s[1:] 48 | q := strings.Index(s, " ") 49 | if q >= 0 { 50 | s = s[:q] 51 | n += q 52 | } else { 53 | n += len(s) 54 | } 55 | p, err := strconv.ParseUint(s, 10, 0) 56 | if err != nil { 57 | return nil, 0, err 58 | } 59 | return &Addr{label, uint16(p)}, n, nil 60 | } 61 | 62 | // Read() reads a link address from p@ in wire format 63 | func ReadAddr(p []byte) (addr *Addr, n int, err error) { 64 | var label *Label 65 | label, n, err = ReadLabel(p) 66 | if err != nil { 67 | return nil, 0, err 68 | } 69 | p = p[n:] 70 | if len(p) < 2 { 71 | return nil, 0, errors.New("link addr missing port") 72 | } 73 | return &Addr{label, DecodeUint16(p[0:2])}, n + 2, nil 74 | } 75 | 76 | // Write() writes the link address to p@ in wire format 77 | func (addr *Addr) Write(p []byte) (n int, err error) { 78 | n, err = addr.Label.Write(p) 79 | if err != nil { 80 | return 0, err 81 | } 82 | p = p[n:] 83 | if len(p) < 2 { 84 | return 0, errors.New("link addr can't fit port") 85 | } 86 | EncodeUint16(addr.Port, p[0:2]) 87 | return n + 2, nil 88 | } 89 | -------------------------------------------------------------------------------- /dccp/sandbox/rate_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package sandbox 6 | 7 | import ( 8 | //"fmt" 9 | "testing" 10 | "github.com/petar/GoDCCP/dccp" 11 | ) 12 | 13 | const ( 14 | rateDuration = 10e9 // Duration of rate test 15 | rateInterval = 1e9 16 | ratePacketsPerInterval = 50 17 | ) 18 | 19 | // TestRate tests whether a single connection's one-way client-to-server rate converges to 20 | // limit imposed by connection in that the send rate has to: 21 | // (1) converge and stabilize, and 22 | // (2) the stable rate should 23 | // (2.a) either be closely below the connection limit, 24 | // (2.b) or be closely above the connection limit (and maintain a drop rate below some threshold) 25 | // A two-way test is not necessary as the congestion mechanisms in either direction are completely independent. 26 | // 27 | // NOTE: Pipe currently supports rate simulation in packets per time interval. If we want to test behavior 28 | // under variable packet sizes, we need to implement rate simulation in bytes per interval. 29 | func TestRate(t *testing.T) { 30 | 31 | env, _ := NewEnv("rate") 32 | clientConn, serverConn, clientToServer, _ := NewClientServerPipe(env) 33 | 34 | // Set rate limit on client-to-server connection 35 | clientToServer.SetWriteRate(rateInterval, ratePacketsPerInterval) 36 | 37 | cchan := make(chan int, 1) 38 | mtu := clientConn.GetMTU() 39 | buf := make([]byte, mtu) 40 | env.Go(func() { 41 | t0 := env.Now() 42 | for env.Now() - t0 < rateDuration { 43 | err := clientConn.Write(buf) 44 | if err != nil { 45 | t.Errorf("error writing (%s)", err) 46 | break 47 | } 48 | } 49 | // Close is necessary because otherwise, if no read timeout is in place, the 50 | // server sides hangs forever on Read 51 | clientConn.Close() 52 | close(cchan) 53 | }, "test client") 54 | 55 | schan := make(chan int, 1) 56 | env.Go(func() { 57 | for { 58 | _, err := serverConn.Read() 59 | if err == dccp.ErrEOF { 60 | break 61 | } else if err != nil { 62 | t.Errorf("error reading (%s)", err) 63 | break 64 | } 65 | } 66 | serverConn.Close() 67 | close(schan) 68 | }, "test server") 69 | 70 | _, _ = <-cchan 71 | _, _ = <-schan 72 | 73 | clientConn.Abort() 74 | serverConn.Abort() 75 | 76 | env.NewGoJoin("end-of-test", clientConn.Joiner(), serverConn.Joiner()).Join() 77 | dccp.NewAmb("line", env).E(dccp.EventMatch, "Server and client done.") 78 | if err := env.Close(); err != nil { 79 | t.Errorf("error closing runtime (%s)", err) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /retransmit/header.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package retransmit 6 | 7 | // All sequence numbers are circular integers 8 | 9 | type header struct { 10 | 11 | // --> Sync indicates if the header includes a sync request, whose number is SyncNo 12 | Sync bool 13 | 14 | // The presence of SyncNo indicates that the sender is requesting an ack. 15 | // SyncNo is the sequence number of the sync request, generated by the sender. 16 | SyncNo uint16 17 | 18 | // --> Ack indicates if the header includes an acknowledgement, represented by SyncAckN 19 | Ack bool 20 | 21 | // AckSyncNo is the SyncNo this acknowledgement is in response to 22 | AckSyncNo uint16 23 | 24 | // AckDataNo is the sequence number of the first block in the acknowledgements map. 25 | // This number also indicates that all data blocks with sequence numbers lower 26 | // than AckDataNo have already been received. 27 | AckDataNo uint32 28 | 29 | // AckMap is a bitmap where 1's correspond to blocks that HAVE NOT been received 30 | // TODO: This can be made more space-efficient with something like run-length encoding 31 | // or a Huffman-type (LZ?) thing that favors 0's 32 | AckMap []byte 33 | 34 | // --> Data indicates if the header includes data, represented by DataNo and DataCargo 35 | Data bool 36 | 37 | // DataNo is the sequence Number of the data block, if len(Data) > 0 38 | DataNo uint32 39 | 40 | // DataCargo is the contents of the data block 41 | DataCargo []byte 42 | } 43 | 44 | // Header wire format 45 | // 46 | // +------------+---------------+----------------+----------------+ 47 | // | Type 1byte | Ack Subheader | Sync Subheader | Data Subheader | 48 | // +------------+---------------+----------------+----------------+ 49 | // 50 | // Type-byte format 51 | // 52 | // MSB LSB 53 | // +-+-+-+-+-+-+-+-+ 54 | // | | | | | |D|S|A| 55 | // +-+-+-+-+-+-+-+-+ 56 | // 57 | // Ack Subheader wire format 58 | // 59 | // +------------------+------------------+------------------+----------------+ 60 | // | AckSyncNo 2bytes | AckDataNo 4bytes | AckMapLen 2bytes | ... AckMap ... | 61 | // +------------------+------------------+------------------+----------------+ 62 | // 63 | // Sync Subheader wire format 64 | // 65 | // +----------------+ 66 | // | SyncNo 2 bytes | 67 | // +----------------+ 68 | // 69 | // Data Subheader wire format 70 | // 71 | // +---------------+----------------+--------------+ 72 | // | DataNo 4bytes | DataLen 2bytes | ... Data ... | 73 | // +---------------+----------------+--------------+ 74 | -------------------------------------------------------------------------------- /dccp/ccid3/loss.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package ccid3 6 | 7 | import ( 8 | "math" 9 | "github.com/petar/GoDCCP/dccp" 10 | ) 11 | 12 | // LossSample generates a log sample with loss information 13 | func LossSample(series string, lossRateInv uint32) dccp.Sample { 14 | return dccp.NewSample(series, 100 /float64(lossRateInv), "%") 15 | } 16 | 17 | const ( 18 | LossReceiverEstimateSample = "Loss-Receiver" 19 | ) 20 | 21 | // lossRateCalculator calculates the inverse of the loss event rate as 22 | // specified in Section 5.4, RFC 5348. One instantiation can perform repeated 23 | // calculations using a fixed nInterval parameter. 24 | type lossRateCalculator struct { 25 | nInterval int 26 | w []float64 27 | h []float64 28 | } 29 | 30 | // Init resets the calculator for new use with the given nInterval parameter 31 | func (t *lossRateCalculator) Init(nInterval int) { 32 | t.nInterval = nInterval 33 | t.w = make([]float64, nInterval) 34 | for i, _ := range t.w { 35 | t.w[i] = intervalWeight(i, nInterval) 36 | } 37 | t.h = make([]float64, nInterval) 38 | } 39 | 40 | func intervalWeight(i, nInterval int) float64 { 41 | if i < nInterval/2 { 42 | return 1.0 43 | } 44 | return 2.0 * float64(nInterval-i) / float64(nInterval+2) 45 | } 46 | 47 | // CalcLossEventRateInv computes the inverse of the loss event rate, RFC 5348, Section 5.4. 48 | // NOTE: We currently don't use the alternative algorithm, called History Discounting, 49 | // discussed in RFC 5348, Section 5.5 50 | // TODO: This calculation should be replaced with an entirely integral one. 51 | // TODO: Remove the most recent unfinished interval from the calculation, if too small. Not crucial. 52 | func (t *lossRateCalculator) CalcLossEventRateInv(history []*LossIntervalDetail) uint32 { 53 | 54 | // Prepare a slice with interval lengths 55 | k := min(len(history), t.nInterval) 56 | if k < 2 { 57 | // Too few loss events are reported as UnknownLossEventRateInv which signifies 'no loss' 58 | return UnknownLossEventRateInv 59 | } 60 | h := t.h[:k] 61 | for i := 0; i < k; i++ { 62 | h[i] = float64(history[i].LossInterval.SeqLen()) 63 | } 64 | 65 | // Directly from the RFC 66 | var I_tot0 float64 = 0 67 | var I_tot1 float64 = 0 68 | var W_tot float64 = 0 69 | for i := 0; i < k-1; i++ { 70 | I_tot0 += h[i] * t.w[i] 71 | W_tot += t.w[i] 72 | } 73 | for i := 1; i < k; i++ { 74 | I_tot1 += h[i] * t.w[i-1] 75 | } 76 | I_tot := math.Max(I_tot0, I_tot1) 77 | I_mean := I_tot / W_tot 78 | 79 | if I_mean < 1.0 { 80 | panic("invalid inverse") 81 | } 82 | return uint32(I_mean) 83 | } 84 | -------------------------------------------------------------------------------- /dccp/checksum.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | func csum64to16(sum uint64) uint16 { 8 | // 32+32 to 33 9 | sum = (sum & 0xffffffff) + (sum >> 32) 10 | // 17+16 to 17+c 11 | sum = (sum & 0xffff) + (sum >> 16) 12 | // (1+c)+16 to 16+c 13 | sum = (sum & 0xffff) + (sum >> 16) 14 | // c+16 to 16 15 | sum = (sum & 0xffff) + (sum >> 16) 16 | 17 | return uint16(sum) 18 | } 19 | 20 | func csumBytesToUint16(buf []byte) uint16 { 21 | return (uint16(buf[0]) << 8) | uint16(buf[1]) 22 | } 23 | 24 | func csumUint16ToBytes(u uint16, buf []byte) { 25 | buf[0] = byte(u >> 8) 26 | buf[1] = byte(u & 0xff) 27 | } 28 | 29 | func csumAdd(u, w uint16) uint16 { 30 | sum := uint32(u) + uint32(w) 31 | sum = (sum & 0xffff) + (sum >> 16) 32 | return uint16(sum) 33 | } 34 | 35 | // TODO(petar): This method can be optimized significantly 36 | func csumSum(buf []byte) uint16 { 37 | var sum uint16 38 | l16 := len(buf) >> 1 39 | for i := 0; i < l16; i++ { 40 | sum = csumAdd(sum, csumBytesToUint16(buf[2*i:2*i+2])) 41 | } 42 | if (l16 << 2) < len(buf) { 43 | two := make([]byte, 2) 44 | two[0] = buf[len(buf)-1] 45 | two[1] = 0 46 | sum = csumAdd(sum, csumBytesToUint16(two)) 47 | } 48 | return sum 49 | } 50 | 51 | func csumPartial(sum uint16, buf []byte) uint16 { 52 | return csumAdd(sum, csumSum(buf)) 53 | } 54 | 55 | func csumDone(sum uint16) uint16 { return ^sum } 56 | 57 | // @dccpLen is the length of the DCCP header with options, plus the length of any data 58 | func csumPseudoIP(sourceIP, destIP []byte, protoNo byte, dccpLen int) uint16 { 59 | if len(sourceIP) == 4 { 60 | return csumPseudoIPv4(sourceIP, destIP, protoNo, dccpLen) 61 | } 62 | return csumPseudoIPv6(sourceIP, destIP, protoNo, dccpLen) 63 | } 64 | 65 | func csumPseudoIPv4(sourceIP, destIP []byte, protoNo byte, dccpLen int) uint16 { 66 | if len(sourceIP) != 4 || len(destIP) != 4 { 67 | panic("size") 68 | } 69 | if uint(protoNo)>>8 != 0 { 70 | panic("proto no") 71 | } 72 | if uint16(dccpLen)>>16 != 0 { 73 | panic("len") 74 | } 75 | sum := csumSum(sourceIP) 76 | sum = csumPartial(sum, destIP) 77 | sum = csumAdd(sum, uint16(protoNo)) 78 | sum = csumAdd(sum, uint16(dccpLen)) 79 | return sum 80 | } 81 | 82 | func csumPseudoIPv6(sourceIP, destIP []byte, protoNo byte, dccpLen int) uint16 { 83 | if len(sourceIP) != 16 || len(destIP) != 16 { 84 | panic("size") 85 | } 86 | if uint(protoNo)>>8 != 0 { 87 | panic("proto no") 88 | } 89 | sum := csumSum(sourceIP) 90 | sum = csumPartial(sum, destIP) 91 | sum = csumAdd(sum, uint16(uint32(dccpLen)>>16)) 92 | sum = csumAdd(sum, uint16(uint32(dccpLen)&0xffff)) 93 | sum = csumAdd(sum, uint16(protoNo)) 94 | return sum 95 | } 96 | -------------------------------------------------------------------------------- /dccp/ccid3/loss_test.go_: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package ccid3 6 | 7 | import ( 8 | //"fmt" 9 | //"rand" 10 | "testing" 11 | "github.com/petar/GoDCCP/dccp" 12 | ) 13 | 14 | type ffRateMaker struct { 15 | gap int64 // time between adjacent packets 16 | ppr int64 // packets per rtt 17 | t int64 // time counter 18 | s int64 // sequence number counter 19 | } 20 | 21 | func (rm *ffRateMaker) Init(gap int64, ppr int64) { 22 | rm.gap = gap 23 | rm.ppr = ppr 24 | rm.t = 1e11 25 | rm.s = 20000 26 | } 27 | 28 | func (rm *ffRateMaker) RTT() int64 { return rm.gap * rm.ppr } 29 | 30 | func (rm *ffRateMaker) PPR() int64 { return rm.ppr } 31 | 32 | func (rm *ffRateMaker) Next() *dccp.FeedforwardHeader { 33 | ff := &dccp.FeedforwardHeader{ 34 | Type: dccp.Data, 35 | X: true, 36 | SeqNo: rm.s, 37 | CCVal: 0, 38 | Options: nil, 39 | Time: rm.t, 40 | } 41 | rm.s++ 42 | rm.t += rm.gap 43 | return ff 44 | } 45 | 46 | type lossHistory struct { 47 | h []*LossInterval 48 | } 49 | 50 | func (h *lossHistory) Add(i *LossInterval) { 51 | h.h = append(h.h, i) 52 | } 53 | 54 | func (h *lossHistory) Check(tail []*LossInterval) bool { 55 | if len(tail) > len(h.h) { 56 | return false 57 | } 58 | for i, g := range tail { 59 | j := len(h.h)-1-i 60 | if g.LossLength != h.h[j].LossLength || 61 | g.LosslessLength != h.h[j].LosslessLength || 62 | g.DataLength != h.h[j].DataLength { 63 | return false 64 | } 65 | } 66 | return true 67 | } 68 | 69 | func TestLossEvents(t *testing.T) { 70 | var le lossReceiver 71 | le.Init() 72 | 73 | var rm ffRateMaker 74 | rm.Init(1e10, 10) 75 | 76 | var h lossHistory 77 | le.OnRead(rm.Next(), rm.RTT()) 78 | for q := 0; q < 10; q++ { 79 | for i := 0; i < NDUPACK; i++ { 80 | le.OnRead(rm.Next(), rm.RTT()) 81 | } 82 | 83 | rm.Next() 84 | 85 | for i := 0; i < 3*int(rm.PPR()); i++ { 86 | le.OnRead(rm.Next(), rm.RTT()) 87 | } 88 | 89 | ? 90 | 91 | un := le.currentInterval() 92 | if un == nil { 93 | t.Errorf("expecting non-nil current interval") 94 | } 95 | 96 | expLossLen := uint32(1) 97 | expLosslessLen := uint32(3*int(rm.PPR())-NDUPACK) 98 | expDataLen := expLossLen + expLosslessLen 99 | if un.LossLength != expLossLen || un.LosslessLength != expLosslessLen || un.DataLength != expDataLen { 100 | t.Errorf("expect %d,%d,%d; got %d,%d,%d", 101 | expLossLen, expLosslessLen, expDataLen, 102 | un.LossLength, un.LosslessLength, un.DataLength) 103 | } 104 | // fmt.Printf("%d —— %d == %d\n", un.LossLength, un.LosslessLength, un.DataLength) 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /dccp/ccid3/options_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package ccid3 6 | 7 | import ( 8 | "reflect" 9 | "testing" 10 | ) 11 | 12 | var lossEventRateOptions = []*LossEventRateOption{ 13 | &LossEventRateOption{ 14 | RateInv: 0, 15 | }, 16 | &LossEventRateOption{ 17 | RateInv: UnknownLossEventRateInv, 18 | }, 19 | } 20 | 21 | func TestLossEventRateOption(t *testing.T) { 22 | for _, original := range lossEventRateOptions { 23 | encoded, err := original.Encode() 24 | if err != nil { 25 | t.Fatalf("encoding option (%s)", err) 26 | } 27 | decoded := DecodeLossEventRateOption(encoded) 28 | if decoded == nil { 29 | t.Fatalf("decoding option") 30 | } 31 | if !reflect.DeepEqual(original, decoded) { 32 | t.Fatalf("expecting %v, encountered %v", original, decoded) 33 | } 34 | } 35 | } 36 | 37 | var lossIntervalsOptions = []*LossIntervalsOption{ 38 | &LossIntervalsOption{ 39 | SkipLength: 3, 40 | LossIntervals: nil, 41 | }, 42 | &LossIntervalsOption{ 43 | SkipLength: 0, 44 | LossIntervals: []*LossInterval{}, 45 | }, 46 | &LossIntervalsOption{ 47 | SkipLength: 0, 48 | LossIntervals: []*LossInterval{ 49 | &LossInterval{ 50 | LosslessLength: 10, 51 | LossLength: 5, 52 | DataLength: 15, 53 | ECNNonceEcho: true, 54 | }, 55 | &LossInterval{ 56 | LosslessLength: 0, 57 | LossLength: 1, 58 | DataLength: 1, 59 | ECNNonceEcho: false, 60 | }, 61 | }, 62 | }, 63 | } 64 | 65 | func TestLostIntervalsOption(t *testing.T) { 66 | for _, original := range lossIntervalsOptions { 67 | encoded, err := original.Encode() 68 | if err != nil { 69 | t.Fatalf("encoding option (%s)", err) 70 | } 71 | decoded := DecodeLossIntervalsOption(encoded) 72 | if decoded == nil { 73 | t.Fatalf("decoding option") 74 | } 75 | if !reflect.DeepEqual(original, decoded) { 76 | t.Fatalf("expecting %v, encountered %v", original, decoded) 77 | } 78 | } 79 | } 80 | 81 | var receiveRateOptions = []*ReceiveRateOption{ 82 | &ReceiveRateOption{ 83 | Rate: 0, 84 | }, 85 | &ReceiveRateOption{ 86 | Rate: 15, 87 | }, 88 | } 89 | 90 | func TestReceiveRateOption(t *testing.T) { 91 | for _, original := range receiveRateOptions { 92 | encoded, err := original.Encode() 93 | if err != nil { 94 | t.Fatalf("encoding option (%s)", err) 95 | } 96 | decoded := DecodeReceiveRateOption(encoded) 97 | if decoded == nil { 98 | t.Fatalf("decoding option") 99 | } 100 | if !reflect.DeepEqual(original, decoded) { 101 | t.Fatalf("expecting %v, encountered %v", original, decoded) 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /dccp/env.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | import ( 8 | "sync" 9 | "time" 10 | "github.com/petar/GoGauge/filter" 11 | ) 12 | 13 | // Env encapsulates the runtime environment of a DCCP endpoint. It includes a pluggable 14 | // time interface, in order to allow for use of real as well as synthetic (accelerated) time 15 | // (for testing purposes), as well as a amb interface. 16 | type Env struct { 17 | guzzle TraceWriter 18 | filter *filter.Filter 19 | gojoin *GoJoin 20 | 21 | sync.Mutex 22 | timeZero int64 // Time when execution started 23 | timeLast int64 // Time of last log message 24 | } 25 | 26 | func NewEnv(guzzle TraceWriter) *Env { 27 | now := time.Now().UnixNano() 28 | r := &Env{ 29 | guzzle: guzzle, 30 | filter: filter.NewFilter(), 31 | gojoin: NewGoJoin("Env"), 32 | timeZero: now, 33 | timeLast: now, 34 | } 35 | return r 36 | } 37 | 38 | // Go runs f in a new GoRoutine. The GoRoutine is also added to the GoJoin of the Env. 39 | func (t *Env) Go(f func(), fmt_ string, args_ ...interface{}) { 40 | t.gojoin.Go(f, fmt_, args_...) 41 | } 42 | 43 | func (t *Env) Joiner() Joiner { 44 | return t.gojoin 45 | } 46 | 47 | func (t *Env) NewGoJoin(annotation string, group ...Joiner) *GoJoin { 48 | return NewGoJoin(annotation, group...) 49 | } 50 | 51 | func (t *Env) TraceWriter() TraceWriter { 52 | return t.guzzle 53 | } 54 | 55 | func (t *Env) Filter() *filter.Filter { 56 | return t.filter 57 | } 58 | 59 | func (t *Env) Sync() error { 60 | return t.guzzle.Sync() 61 | } 62 | 63 | func (t *Env) Close() error { 64 | return t.guzzle.Close() 65 | } 66 | 67 | func (t *Env) Now() int64 { 68 | return time.Now().UnixNano() 69 | } 70 | 71 | func (t *Env) Sleep(ns int64) { 72 | time.Sleep(time.Duration(ns)) 73 | } 74 | 75 | func (t *Env) Snap() (sinceZero int64, sinceLast int64) { 76 | t.Lock() 77 | defer t.Unlock() 78 | 79 | logTime := t.Now() 80 | timeLast := t.timeLast 81 | t.timeLast = logTime 82 | return logTime - t.timeZero, logTime - timeLast 83 | } 84 | 85 | // Expire periodically, on every interval duration, checks if the test condition has been met. If 86 | // the condition is met within the timeout period, no further action is taken. Otherwise, the 87 | // onexpire function is invoked. 88 | func (t *Env) Expire(test func()bool, onexpire func(), timeout, interval int64, fmt_ string, args_ ...interface{}) { 89 | t.Go(func() { 90 | k := timeout / interval 91 | if k <= 0 { 92 | panic("frequency too small") 93 | } 94 | for i := int64(0); i < k; i++ { 95 | t.Sleep(interval) 96 | if test() { 97 | return 98 | } 99 | } 100 | onexpire() 101 | }, fmt_, args_...) 102 | } 103 | -------------------------------------------------------------------------------- /dccp/timeopt_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | import ( 8 | "math/rand" 9 | "testing" 10 | ) 11 | 12 | const ( 13 | K = 20 14 | TimestampRange = 2^31 / 10 15 | ElapsedRange = 210010010 16 | ) 17 | 18 | func TestTimestampOption(t *testing.T) { 19 | for i := 0; i < K; i++ { 20 | t0 := uint32(rand.Int31n(TimestampRange)) 21 | delta := uint32(rand.Int31n(ElapsedRange)) 22 | t1 := t0 + delta 23 | opt0 := &TimestampOption{t0} 24 | opt1 := &TimestampOption{t1} 25 | opt0_, err1 := opt0.Encode() 26 | opt1_, err2 := opt1.Encode() 27 | if err1 != nil || err2 != nil { 28 | t.Fatalf("error encoding timestamp option") 29 | } 30 | opt0 = DecodeTimestampOption(opt0_) 31 | opt1 = DecodeTimestampOption(opt1_) 32 | if opt0 == nil || opt1 == nil { 33 | t.Fatalf("decoding timestamp error") 34 | } 35 | t0_ := opt0.Timestamp 36 | t1_ := opt1.Timestamp 37 | delta_ := TenMicroDiff(t0_, t1_) 38 | if delta != delta_ { 39 | t.Errorf("Expecting %d, got %d", delta, delta_) 40 | } 41 | } 42 | } 43 | 44 | func TestElapsedTimeOption(t *testing.T) { 45 | for i := 0; i < K; i++ { 46 | e := uint32(rand.Int31n(TimestampRange)) 47 | opt := &ElapsedTimeOption{e} 48 | opt_, err := opt.Encode() 49 | if err != nil { 50 | t.Fatalf("error encoding elapsed time option") 51 | } 52 | opt = DecodeElapsedTimeOption(opt_) 53 | if opt == nil { 54 | t.Fatalf("error decoding elapsed time option") 55 | } 56 | e_ := opt.Elapsed 57 | if e != e_ { 58 | t.Errorf("Expecting %d, got %d", e, e_) 59 | } 60 | } 61 | } 62 | 63 | func TestTimestampEchoOption(t *testing.T) { 64 | for i := 0; i < K; i++ { 65 | t0 := uint32(rand.Int31n(TimestampRange)) 66 | delta := uint32(rand.Int31n(ElapsedRange)) 67 | t1 := t0 + delta 68 | e0 := uint32(rand.Int31n(TimestampRange)) 69 | e1 := uint32(rand.Int31n(TimestampRange)) 70 | ech0 := &TimestampEchoOption{t0, e0} 71 | ech1 := &TimestampEchoOption{t1, e1} 72 | 73 | ech0_, err1 := ech0.Encode() 74 | ech1_, err2 := ech1.Encode() 75 | if err1 != nil || err2 != nil { 76 | t.Fatalf("encoding timstamp echo option") 77 | } 78 | ech0 = DecodeTimestampEchoOption(ech0_) 79 | ech1 = DecodeTimestampEchoOption(ech1_) 80 | if ech0 == nil || ech1 == nil { 81 | t.Fatalf("decoding timestamp echo option") 82 | } 83 | t0_ := ech0.Timestamp 84 | t1_ := ech1.Timestamp 85 | 86 | delta_ := TenMicroDiff(t0_, t1_) 87 | if delta != delta_ { 88 | t.Errorf("delta: expecting %d, got %d", delta, delta_) 89 | } 90 | 91 | e0_ := ech0.Elapsed 92 | e1_ := ech1.Elapsed 93 | if e0 != e0_ { 94 | t.Errorf("e0: expecting %d, got %d", e0, e0_) 95 | } 96 | if e1 != e1_ { 97 | t.Errorf("e1: expecting %d, got %d", e1, e1_) 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /cmd/dccp-inspector/series.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "container/list" 6 | "fmt" 7 | "io" 8 | "text/template" 9 | "github.com/petar/GoDCCP/dccp" 10 | ) 11 | 12 | // SeriesSweeper consumes log records in time order and outputs 13 | // the embedded time series data in JavaScript array format that 14 | // can be fed into for dygraph. 15 | type SeriesSweeper struct { 16 | series []string 17 | chrono list.List 18 | } 19 | 20 | type sample struct { 21 | Series string 22 | Time float64 23 | Value float64 24 | } 25 | 26 | // Init resets the SeriesSweeper for new user. 27 | func (x *SeriesSweeper) Init() { 28 | x.series = make([]string, 0) 29 | x.chrono.Init() 30 | } 31 | 32 | // Add adds a new log record to the series. It assumes that records are added 33 | // in increasing chronological order 34 | func (x *SeriesSweeper) Add(r *dccp.LogRecord) { 35 | if !r.IsHighlighted() { 36 | return 37 | } 38 | // Check that the argument is a sample 39 | m_, ok := r.Args[dccp.SampleType] 40 | if !ok { 41 | return 42 | } 43 | // Read sample data 44 | m := m_.(map[string]interface{}) 45 | value := m["Value"].(float64) 46 | series := r.LabelString() + m["Series"].(string) 47 | for _, u := range x.series { 48 | if u == series { 49 | goto __SeriesSaved 50 | } 51 | } 52 | x.series = append(x.series, series) 53 | __SeriesSaved: 54 | // Remember the sample 55 | u := &sample{ 56 | Series: series, 57 | Time: float64(r.Time) / 1e6, // Time, X-coordinate, always in milliseconds 58 | Value: value, 59 | } 60 | x.chrono.PushBack(u) 61 | } 62 | 63 | // EncodeData encodes the entire data received so far into a JavaScript array format. 64 | func (x *SeriesSweeper) EncodeData(w io.Writer) error { 65 | var bw *bufio.Writer = bufio.NewWriter(w) 66 | bw.WriteByte(byte('[')) 67 | for e := x.chrono.Front(); e != nil; e = e.Next() { 68 | s := e.Value.(*sample) 69 | bw.WriteByte(byte('[')) 70 | fmt.Fprintf(bw, "%0.3f,", s.Time) 71 | for i, series := range x.series { 72 | if series == s.Series { 73 | fmt.Fprintf(bw, "%0.3f", s.Value) 74 | } else { 75 | bw.WriteString("null") 76 | } 77 | if i < len(x.series)-1 { 78 | bw.WriteByte(byte(',')) 79 | } 80 | } 81 | bw.WriteByte(']') 82 | if e.Next() != nil { 83 | bw.WriteByte(byte(',')) 84 | } 85 | bw.WriteByte('\n') 86 | } 87 | bw.WriteByte(byte(']')) 88 | return bw.Flush() 89 | } 90 | 91 | // EncodeHeader encodes the names of the time series in a JavaScript array format. 92 | func (x *SeriesSweeper) EncodeHeader(w io.Writer) error { 93 | var bw *bufio.Writer = bufio.NewWriter(w) 94 | bw.WriteString("[\"Time\",") 95 | for i, series := range x.series { 96 | bw.WriteByte(byte('"')) 97 | template.JSEscape(bw, []byte(series)) 98 | bw.WriteByte(byte('"')) 99 | if i < len(x.series)-1 { 100 | bw.WriteByte(byte(',')) 101 | } 102 | } 103 | bw.WriteByte(byte(']')) 104 | return bw.Flush() 105 | } 106 | -------------------------------------------------------------------------------- /dccp/options.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | type Option struct { 8 | Type byte 9 | Data []byte 10 | Mandatory bool 11 | } 12 | 13 | const ( 14 | OptionPadding = 0 15 | OptionMandatory = 1 16 | OptionSlowReceiver = 2 17 | OptionChangeL = 32 18 | OptionConfirmL = 33 19 | OptionChangeR = 34 20 | OptionConfirmR = 35 21 | OptionInitCookie = 36 22 | OptionNDPCount = 37 23 | OptionAckVectorNonce0 = 38 24 | OptionAckVectorNonce1 = 39 25 | OptionDataDropped = 40 26 | OptionTimestamp = 41 27 | OptionTimestampEcho = 42 28 | OptionElapsedTime = 43 29 | OptionDataChecksum = 44 30 | // Reserved 45 to 127 31 | // CCID-specific 128 to 255 32 | ) 33 | 34 | func isOptionReserved(optionType byte) bool { 35 | return (optionType >= 3 && optionType <= 31) || 36 | (optionType >= 45 && optionType <= 127) 37 | } 38 | 39 | func isOptionCCIDSpecific(optionType byte) bool { 40 | return optionType >= 128 && optionType <= 255 41 | } 42 | 43 | func isOptionCCIDSenderToReceiver(optionType byte) bool { 44 | return (optionType >= 38 && optionType <= 43) || (optionType >= 128 && optionType <= 191) 45 | } 46 | 47 | func validateCCIDSenderToReceiver(opts []*Option) bool { 48 | for _, o := range opts { 49 | if !isOptionCCIDSenderToReceiver(o.Type) { 50 | return false 51 | } 52 | } 53 | return true 54 | } 55 | 56 | func filterCCIDSenderToReceiverOptions(opts []*Option) []*Option { 57 | r := make([]*Option, len(opts)) 58 | k := 0 59 | for _, o := range opts { 60 | if isOptionCCIDSenderToReceiver(o.Type) { 61 | r[k] = o 62 | k++ 63 | } 64 | } 65 | return r[:k] 66 | } 67 | 68 | func isOptionCCIDReceiverToSender(optionType byte) bool { 69 | return (optionType >= 38 && optionType <= 43) || (optionType >= 192 && optionType <= 255) 70 | } 71 | 72 | func validateCCIDReceiverToSender(opts []*Option) bool { 73 | for _, o := range opts { 74 | if !isOptionCCIDReceiverToSender(o.Type) { 75 | return false 76 | } 77 | } 78 | return true 79 | } 80 | 81 | func filterCCIDReceiverToSenderOptions(opts []*Option) []*Option { 82 | r := make([]*Option, len(opts)) 83 | k := 0 84 | for _, o := range opts { 85 | if isOptionCCIDReceiverToSender(o.Type) { 86 | r[k] = o 87 | k++ 88 | } 89 | } 90 | return r[:k] 91 | } 92 | 93 | func isOptionSingleByte(optionType byte) bool { 94 | return optionType >= 0 && optionType <= 31 95 | } 96 | 97 | func isOptionValidForType(optionType, Type byte) bool { 98 | if Type != Data { 99 | return true 100 | } 101 | switch optionType { 102 | case OptionPadding, 103 | OptionSlowReceiver, 104 | OptionNDPCount, 105 | OptionTimestamp, 106 | OptionTimestampEcho, 107 | OptionDataChecksum: 108 | return true 109 | default: 110 | return false 111 | } 112 | panic("unreach") 113 | } 114 | -------------------------------------------------------------------------------- /dccp/ccfixed.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | type CCFixed struct { 8 | 9 | } 10 | 11 | func (CCFixed) NewSender(env *Env, amb *Amb) SenderCongestionControl { 12 | return newFixedRateSenderControl(env, 1e9) // one packet per second. sendsPerSecond 13 | } 14 | 15 | func (CCFixed) NewReceiver(env *Env, amb *Amb) ReceiverCongestionControl { 16 | return newFixedRateReceiverControl(env) 17 | } 18 | 19 | // ---> Fixed-rate HC-Sender Congestion Control 20 | 21 | type fixedRateSenderControl struct { 22 | env *Env 23 | Mutex 24 | every int64 // Strobe every every nanoseconds 25 | strobeRead chan int 26 | strobeWrite chan int 27 | } 28 | 29 | func newFixedRateSenderControl(env *Env, every int64) *fixedRateSenderControl { 30 | strobe := make(chan int) 31 | return &fixedRateSenderControl{env: env, every: every, strobeRead: strobe, strobeWrite: strobe} 32 | } 33 | 34 | func (scc *fixedRateSenderControl) Open() { 35 | scc.env.Go(func() { 36 | for { 37 | scc.Lock() 38 | if scc.strobeWrite == nil { 39 | scc.Unlock() 40 | break 41 | } 42 | scc.strobeWrite <- 1 43 | scc.Unlock() 44 | scc.env.Sleep(scc.every) 45 | } 46 | }, "fixedRateSenderControl") 47 | } 48 | 49 | const CCID_FIXED = 0xf 50 | 51 | func (scc *fixedRateSenderControl) GetID() byte { return CCID_FIXED } 52 | 53 | func (scc *fixedRateSenderControl) GetCCMPS() int32 { return 1e9 } 54 | 55 | func (scc *fixedRateSenderControl) GetRTT() int64 { return RoundtripDefault } 56 | 57 | func (scc *fixedRateSenderControl) OnWrite(ph *PreHeader) (ccval int8, options []*Option) { 58 | return 0, nil 59 | } 60 | 61 | func (scc *fixedRateSenderControl) OnRead(fb *FeedbackHeader) error { return nil } 62 | 63 | func (scc *fixedRateSenderControl) OnIdle(now int64) error { return nil } 64 | 65 | func (scc *fixedRateSenderControl) Strobe() { 66 | <-scc.strobeRead 67 | } 68 | 69 | func (scc *fixedRateSenderControl) SetHeartbeat(interval int64) { 70 | } 71 | 72 | func (scc *fixedRateSenderControl) Close() { 73 | scc.Lock() 74 | defer scc.Unlock() 75 | if scc.strobeWrite != nil { 76 | close(scc.strobeWrite) 77 | scc.strobeWrite = nil 78 | } 79 | } 80 | 81 | // ---> Fixed-rate HC-Receiver Congestion Control 82 | 83 | type fixedRateReceiverControl struct{ 84 | env *Env 85 | } 86 | 87 | func newFixedRateReceiverControl(env *Env) *fixedRateReceiverControl { 88 | return &fixedRateReceiverControl{ env: env } 89 | } 90 | 91 | func (rcc *fixedRateReceiverControl) Open() {} 92 | 93 | func (rcc *fixedRateReceiverControl) GetID() byte { return CCID_FIXED } 94 | 95 | func (rcc *fixedRateReceiverControl) OnWrite(ph *PreHeader) (options []*Option) { return nil } 96 | 97 | func (rcc *fixedRateReceiverControl) OnRead(ff *FeedforwardHeader) error { return nil } 98 | 99 | func (rcc *fixedRateReceiverControl) OnIdle(now int64) error { return nil } 100 | 101 | func (rcc *fixedRateReceiverControl) Close() {} 102 | -------------------------------------------------------------------------------- /cmd/dccp-inspector/inspector.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "encoding/json" 9 | "flag" 10 | "fmt" 11 | "io" 12 | "os" 13 | "sort" 14 | //"github.com/petar/GoGauge/gauge" 15 | "github.com/petar/GoDCCP/dccp" 16 | dccp_gauge "github.com/petar/GoDCCP/dccp/gauge" 17 | ) 18 | 19 | var ( 20 | flagReport *string = flag.String("report", "basic", "Report types: basic, trip") 21 | flagEmits *bool = flag.Bool("emits", true, "Include emits with stack trace logs") 22 | ) 23 | 24 | func usage() { 25 | fmt.Printf("%s [optional_flags] log_file\n", os.Args[0]) 26 | flag.PrintDefaults() 27 | os.Exit(1) 28 | } 29 | 30 | func main() { 31 | flag.Parse() 32 | 33 | // First non-flag argument is log file name 34 | nonflags := flag.Args() 35 | if len(nonflags) == 0 { 36 | usage() 37 | } 38 | 39 | // Open and decode log file 40 | logFile, err := os.Open(nonflags[0]) 41 | if err != nil { 42 | fmt.Fprintf(os.Stderr, "Error opening log (%s)\n", err) 43 | os.Exit(1) 44 | } 45 | defer logFile.Close() 46 | logDec := json.NewDecoder(logFile) 47 | 48 | // Raw log entries will go into emits 49 | var emits []*dccp.LogRecord = make([]*dccp.LogRecord, 0) 50 | for { 51 | rec := &dccp.LogRecord{} 52 | if err = logDec.Decode(rec); err != nil { 53 | break 54 | } 55 | emits = append(emits, rec) 56 | } 57 | fmt.Fprintf(os.Stderr, "Read %d records.\n", len(emits)) 58 | if err != io.EOF { 59 | fmt.Fprintf(os.Stderr, "Terminated unexpectedly (%s).\n", err) 60 | } 61 | 62 | // Fork to desired reducer 63 | switch *flagReport { 64 | case "basic": 65 | htmlBasic(emits, *flagEmits) 66 | case "trip": 67 | printTrip(emits) 68 | } 69 | 70 | printStats(emits) 71 | } 72 | 73 | func printStats(emits []*dccp.LogRecord) { 74 | sort.Sort(LogRecordTimeSort(emits)) 75 | reducer := dccp_gauge.NewLogReducer() 76 | for _, rec := range emits { 77 | reducer.Write(rec) 78 | } 79 | trips := dccp_gauge.TripMapToSlice(reducer.Trips()) 80 | sort.Sort(TripSeqNoSort(trips)) 81 | sr, rr := dccp_gauge.CalcRates(trips) 82 | fmt.Fprintf(os.Stderr, "Send rate: %g pkt/sec, Receive rate: %g pkt/sec\n", sr, rr) 83 | } 84 | 85 | // LogRecordTimeSort sorts LogRecord records by timestamp 86 | type LogRecordTimeSort []*dccp.LogRecord 87 | 88 | func (t LogRecordTimeSort) Len() int { 89 | return len(t) 90 | } 91 | 92 | func (t LogRecordTimeSort) Less(i, j int) bool { 93 | return t[i].Time < t[j].Time 94 | } 95 | 96 | func (t LogRecordTimeSort) Swap(i, j int) { 97 | t[i], t[j] = t[j], t[i] 98 | } 99 | 100 | // TODO: Not used any more; Remove 101 | func printBasic(emits []*dccp.LogRecord) { 102 | prints := make([]*PrintRecord, 0) 103 | for _, t := range emits { 104 | var p *PrintRecord = printRecord(t) 105 | if p != nil { 106 | prints = append(prints, p) 107 | } 108 | } 109 | Print(prints, true) 110 | } 111 | 112 | func htmlBasic(emits []*dccp.LogRecord, includeEmits bool) { 113 | lps := make([]*logPipe, 0) 114 | for _, t := range emits { 115 | p := pipeEmit(t) 116 | if p != nil { 117 | lps = append(lps, p) 118 | } 119 | } 120 | htmlize(lps, true, includeEmits) 121 | } 122 | -------------------------------------------------------------------------------- /dccp/string.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "strconv" 11 | ) 12 | 13 | func (h *Header) String() string { 14 | x := 0 15 | if h.X { 16 | x = 1 17 | } 18 | var w bytes.Buffer 19 | switch h.Type { 20 | case Request: 21 | fmt.Fprintf(&w, "T:%s X:%d SeqNo:%d SC:%d ··· SP:%2d DP:%2d", 22 | typeString(h.Type), x, h.SeqNo, h.ServiceCode, 23 | h.SourcePort, h.DestPort) 24 | case Response: 25 | fmt.Fprintf(&w, "T:%s X:%d SeqNo:%d AckNo:%d SC:%d ··· SP:%2d DP:%2d", 26 | typeString(h.Type), x, h.SeqNo, h.AckNo, h.ServiceCode, 27 | h.SourcePort, h.DestPort) 28 | case Data: 29 | fmt.Fprintf(&w, "T:%s X:%d SeqNo:%d ··· #D:%d", 30 | typeString(h.Type), x, h.SeqNo, 31 | len(h.Data)) 32 | case Ack: 33 | fmt.Fprintf(&w, "T:%s X:%d SeqNo:%d AckNo:%d", 34 | typeString(h.Type), x, h.SeqNo, h.AckNo) 35 | case DataAck: 36 | fmt.Fprintf(&w, "T:%s X:%d SeqNo:%d AckNo:%d ··· #D:%d", 37 | typeString(h.Type), x, h.SeqNo, h.AckNo, 38 | len(h.Data)) 39 | case CloseReq: 40 | fmt.Fprintf(&w, "T:%s X:%d SeqNo:%d AckNo:%d", 41 | typeString(h.Type), x, h.SeqNo, h.AckNo) 42 | case Close: 43 | fmt.Fprintf(&w, "T:%s X:%d SeqNo:%d AckNo:%d", 44 | typeString(h.Type), x, h.SeqNo, h.AckNo) 45 | case Reset: 46 | fmt.Fprintf(&w, "T:%s X:%d SeqNo:%d AckNo:%d ··· RC: %s #RD:%d", 47 | typeString(h.Type), x, h.SeqNo, h.AckNo, 48 | resetCodeString(h.ResetCode), len(h.ResetData)) 49 | case Sync: 50 | fmt.Fprintf(&w, "T:%s X:%d SeqNo:%d AckNo:%d", 51 | typeString(h.Type), x, h.SeqNo, h.AckNo) 52 | case SyncAck: 53 | fmt.Fprintf(&w, "T:%s X:%d SeqNo:%d AckNo:%d", 54 | typeString(h.Type), x, h.SeqNo, h.AckNo) 55 | default: 56 | panic("unknown packet type") 57 | } 58 | return string(w.Bytes()) 59 | } 60 | 61 | func typeString(typ byte) string { 62 | switch typ { 63 | case Request: 64 | return "Request" 65 | case Response: 66 | return "Response" 67 | case Data: 68 | return "Data" 69 | case Ack: 70 | return "Ack" 71 | case DataAck: 72 | return "DataAck" 73 | case CloseReq: 74 | return "CloseReq" 75 | case Close: 76 | return "Close" 77 | case Reset: 78 | return "Reset" 79 | case Sync: 80 | return "Sync" 81 | case SyncAck: 82 | return "SyncAck" 83 | } 84 | panic("un") 85 | } 86 | 87 | func resetCodeString(resetCode byte) string { 88 | switch resetCode { 89 | case ResetUnspecified: 90 | return "Unspecified" 91 | case ResetClosed: 92 | return "Closed" 93 | case ResetAborted: 94 | return "Aborted" 95 | case ResetNoConnection: 96 | return "No Connection" 97 | case ResetPacketError: 98 | return "Packet Error" 99 | case ResetOptionError: 100 | return "Option Error" 101 | case ResetMandatoryError: 102 | return "Mandatory Error" 103 | case ResetConnectionRefused: 104 | return "Connection Refused" 105 | case ResetBadServiceCode: 106 | return "Bad Service Code" 107 | case ResetTooBusy: 108 | return "Too Busy" 109 | case ResetBadInitCookie: 110 | return "Bad Init Cookie" 111 | case ResetAgressionPenalty: 112 | return "Agression Penalty" 113 | } 114 | return strconv.Itoa(int(resetCode)) 115 | } 116 | -------------------------------------------------------------------------------- /dccp/conn.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | // Conn 8 | type Conn struct { 9 | env *Env 10 | amb *Amb 11 | 12 | hc HeaderConn 13 | scc SenderCongestionControl 14 | rcc ReceiverCongestionControl 15 | 16 | Mutex // Protects access to socket, ccidOpen and err 17 | socket 18 | ccidOpen bool // True if the sender and receiver CCID's have been opened 19 | err error // Reason for connection tear down 20 | 21 | readAppLk Mutex 22 | readApp chan []byte // readLoop() sends application data to Read() 23 | writeDataLk Mutex 24 | writeData chan []byte // Write() sends application data to writeLoop() 25 | writeNonDataLk Mutex 26 | writeNonData chan *writeHeader // inject() sends wire-format non-Data packets (higher priority) to writeLoop() 27 | 28 | writeTime monotoneTime 29 | } 30 | 31 | // Joiner returns a Joiner instance that can wait until all goroutines 32 | // associated with the connection have completed. 33 | func (c *Conn) Joiner() Joiner { 34 | return c.env.Joiner() 35 | } 36 | 37 | // Amb returns the Amb instance associated with this connection 38 | func (c *Conn) Amb() *Amb { 39 | return c.amb 40 | } 41 | 42 | func newConn(env *Env, amb *Amb, hc HeaderConn, scc SenderCongestionControl, rcc ReceiverCongestionControl) *Conn { 43 | c := &Conn{ 44 | env: env, 45 | amb: amb, 46 | hc: hc, 47 | scc: scc, 48 | rcc: rcc, 49 | ccidOpen: false, 50 | readApp: make(chan []byte, 5), 51 | writeData: make(chan []byte), 52 | writeNonData: make(chan *writeHeader, 5), 53 | } 54 | c.writeTime.Init(env) 55 | 56 | c.Lock() 57 | // Currently, CCID is not negotiated, rather both sides use the same 58 | c.socket.SetCCIDA(scc.GetID()) 59 | c.socket.SetCCIDB(rcc.GetID()) 60 | 61 | // REMARK: SWAF/SWBF are currently not implemented. 62 | // Instead, we use wide enough fixed-size windows 63 | c.socket.SetSWAF(SEQWIN_FIXED) 64 | c.socket.SetSWBF(SEQWIN_FIXED) 65 | 66 | c.syncWithLink() 67 | c.syncWithCongestionControl() 68 | c.Unlock() 69 | 70 | return c 71 | } 72 | 73 | func NewConnServer(env *Env, amb *Amb, hc HeaderConn, 74 | scc SenderCongestionControl, rcc ReceiverCongestionControl) *Conn { 75 | 76 | c := newConn(env, amb, hc, scc, rcc) 77 | 78 | c.Lock() 79 | c.gotoLISTEN() 80 | c.Unlock() 81 | 82 | c.env.Go(func() { c.writeLoop(c.writeNonData, c.writeData) }, "ConnServer·writLoop") 83 | c.env.Go(func() { c.readLoop() }, "ConnServer·readLoop") 84 | c.env.Go(func() { c.idleLoop() }, "ConnServer·idleLoop") 85 | return c 86 | } 87 | 88 | func NewConnClient(env *Env, amb *Amb, hc HeaderConn, 89 | scc SenderCongestionControl, rcc ReceiverCongestionControl, serviceCode uint32) *Conn { 90 | 91 | c := newConn(env, amb, hc, scc, rcc) 92 | 93 | c.Lock() 94 | c.gotoREQUEST(serviceCode) 95 | c.Unlock() 96 | 97 | c.env.Go(func() { c.writeLoop(c.writeNonData, c.writeData) }, "ConnClient·writeLoop") 98 | c.env.Go(func() { c.readLoop() }, "ConnClient·readLoop") 99 | c.env.Go(func() { c.idleLoop() }, "ConnClient·idleLoop") 100 | return c 101 | } 102 | -------------------------------------------------------------------------------- /dccp/user.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | // This is an approximate upper bound on the size of options that are 12 | // allowed on a Data or DataAck packet. See isOptionValidForType. 13 | const maxDataOptionSize = 24 14 | 15 | // GetMTU() returns the maximum size of an application-level data block that can be passed 16 | // to Write This is an informative number. Packets are sent anyway, but they may be 17 | // dropped by the link layer or a router. 18 | func (c *Conn) GetMTU() int { 19 | c.Lock() 20 | defer c.Unlock() 21 | c.syncWithLink() 22 | return int(c.socket.GetMPS()) - maxDataOptionSize - getFixedHeaderSize(DataAck, true) 23 | } 24 | 25 | // Write blocks until the slice b is sent. 26 | func (c *Conn) Write(data []byte) error { 27 | 28 | //? 29 | 30 | c.writeDataLk.Lock() 31 | defer c.writeDataLk.Unlock() 32 | if c.writeData == nil { 33 | return ErrBad 34 | } 35 | c.writeData <- data 36 | return nil 37 | } 38 | 39 | // Read blocks until the next packet of application data is received. Successfuly read data 40 | // is returned in a slice. The error returned by Read behaves according to io.Reader. If the 41 | // connection was never established or was aborted, Read returns ErrIO. If the connection 42 | // was closed normally, Read returns io.EOF. In the event of a non-nil error, successive 43 | // calls to Read return the same error. 44 | func (c *Conn) Read() (b []byte, err error) { 45 | c.readAppLk.Lock() 46 | readApp := c.readApp 47 | c.readAppLk.Unlock() 48 | if readApp == nil { 49 | if c.Error() == nil { 50 | panic("torn connection missing error") 51 | } 52 | return nil, c.Error() 53 | } 54 | b, ok := <-readApp 55 | if !ok { 56 | if c.Error() == nil { 57 | panic("torn connection missing error") 58 | } 59 | // The connection has been closed 60 | return nil, c.Error() 61 | } 62 | return b, nil 63 | } 64 | 65 | func (c *Conn) Error() error { 66 | c.Lock() 67 | defer c.Unlock() 68 | return c.err 69 | } 70 | 71 | // Close implements SegmentConn.Close. 72 | // It closes the connection, Section 8.3. 73 | func (c *Conn) Close() error { 74 | c.Lock() 75 | defer c.Unlock() 76 | state := c.socket.GetState() 77 | switch state { 78 | case LISTEN: 79 | c.reset(ResetClosed, ErrEOF) 80 | return nil 81 | case REQUEST: 82 | c.reset(ResetClosed, ErrEOF) 83 | return nil 84 | case RESPOND: 85 | c.reset(ResetClosed, ErrEOF) 86 | case PARTOPEN, OPEN: 87 | c.inject(c.generateClose()) 88 | c.gotoCLOSING() 89 | return nil 90 | case CLOSEREQ, CLOSING, TIMEWAIT, CLOSED: 91 | if c.err == nil { 92 | panic(fmt.Sprintf("%s without error", StateString(state))) 93 | } 94 | return c.err 95 | } 96 | panic("unknown state") 97 | } 98 | 99 | func (c *Conn) Abort() { 100 | c.abortWith(ResetAborted) 101 | } 102 | 103 | // LocalLabel implements SegmentConn.LocalLabel 104 | func (c *Conn) LocalLabel() Bytes { return c.hc.LocalLabel() } 105 | 106 | // RemoteLabel implements SegmentConn.RemoteLabel 107 | func (c *Conn) RemoteLabel() Bytes { return c.hc.RemoteLabel() } 108 | 109 | // SetReadExpire implements SegmentConn.SetReadExpire 110 | func (c *Conn) SetReadExpire(nsec int64) error { 111 | panic("not implemented") 112 | } 113 | -------------------------------------------------------------------------------- /dccp/ccid3/strober.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package ccid3 6 | 7 | import ( 8 | "fmt" 9 | "github.com/petar/GoDCCP/dccp" 10 | ) 11 | 12 | // senderStrober is an object that produces regular strobe intervals at a specified rate. 13 | // A senderStrober cannot be used before an initial call to SetInterval or SetRate. 14 | type senderStrober struct { 15 | env *dccp.Env 16 | amb *dccp.Amb 17 | dccp.Mutex 18 | interval int64 // Maximum average time interval between packets, in nanoseconds 19 | last int64 20 | } 21 | 22 | // BytesPerSecondToPacketsPer64Sec converts a rate in byter per second to 23 | // packets of size ss per 64 seconds 24 | func BytesPerSecondToPacketsPer64Sec(bps uint32, ss uint32) int64 { 25 | return (64 * int64(bps)) / int64(ss) 26 | } 27 | 28 | // Init resets the senderStrober instance for new use 29 | func (s *senderStrober) Init(env *dccp.Env, amb *dccp.Amb, bps uint32, ss uint32) { 30 | s.env = env 31 | s.amb = amb.Refine("strober") 32 | s.SetRate(bps, ss) 33 | } 34 | 35 | // SetWait sets the strobing rate by setting the time interval between two strobes in nanoseconds 36 | func (s *senderStrober) SetInterval(interval int64) { 37 | s.Lock() 38 | defer s.Unlock() 39 | s.interval = interval 40 | s.last = 0 41 | } 42 | 43 | // SetRate sets the strobing rate. The argument bps is the desired 44 | // maximum bytes per second, while ss equals the maximum packet size. 45 | // Internally, SetRate converts the two arguments into a maximum 46 | // number of packets per 64 seconds, assuming all packets are of size ss. 47 | // Rates below 1 strobe per 64 sec are not allowed by RFC 4342 48 | func (s *senderStrober) SetRate(bps uint32, ss uint32) { 49 | s.Lock() 50 | defer s.Unlock() 51 | s.interval = 64e9 / BytesPerSecondToPacketsPer64Sec(bps, ss) 52 | if s.interval == 0 { 53 | panic("strobe rate infinity") 54 | } 55 | // This is high frequency. Consider calling it only when rate changes. 56 | // s.amb.E(dccp.EventInfo, fmt.Sprintf("Set strobe rate %d pps", 1e9 / s.interval)) 57 | } 58 | 59 | func (s *senderStrober) SetRatePPS(pps uint32) { 60 | s.Lock() 61 | defer s.Unlock() 62 | if pps == 0 { 63 | panic("strobe rate zero pps") 64 | } 65 | s.interval = 1e9 / int64(pps) 66 | // This is high frequency. Consider calling it only when rate changes. 67 | // s.amb.E(dccp.EventInfo, fmt.Sprintf("Set strobe rate %d pps", 1e9 / s.interval)) 68 | } 69 | 70 | // Strobe ensures that the frequency with which (multiple calls) to Strobe return does not 71 | // exceed the allowed rate. In particular, note that senderStrober makes sure that after data 72 | // limited periods, when the application is not calling it for a while, there is no burst of 73 | // high frequency returns. Strobe MUST not be called concurrently. For efficiency, it does 74 | // not use a lock to prevent concurrent invocation. DCCP currently calls Strobe in a loop, 75 | // so concurrent invocations are not a concern. 76 | // 77 | // XXX: This routine should be optimized 78 | func (s *senderStrober) Strobe() { 79 | s.Lock() 80 | now := s.env.Now() 81 | delta := s.interval - (now - s.last) 82 | _interval := s.interval 83 | s.Unlock() 84 | defer s.amb.E(dccp.EventInfo, fmt.Sprintf("Strobe at %d pps", 1e9 / _interval), nil) 85 | if delta > 0 { 86 | s.env.Sleep(delta) 87 | } 88 | s.Lock() 89 | s.last = s.env.Now() 90 | s.Unlock() 91 | } 92 | -------------------------------------------------------------------------------- /dccp/sandbox/dccp_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package sandbox 6 | 7 | import ( 8 | "testing" 9 | "github.com/petar/GoDCCP/dccp" 10 | ) 11 | 12 | // TestNop checks that no panics occur in the first 5 seconds of connection establishment 13 | func TestNop(t *testing.T) { 14 | // dccp.InstallCtrlCPanic() 15 | // dccp.InstallTimeout(10e9) 16 | env, _ := NewEnv("nop") 17 | NewClientServerPipe(env) 18 | env.Sleep(5e9) 19 | } 20 | 21 | // TestOpenClose verifies that connect and close handshakes function correctly 22 | func TestOpenClose(t *testing.T) { 23 | env, _ := NewEnv("openclose") 24 | clientConn, serverConn, _, _ := NewClientServerPipe(env) 25 | 26 | cchan := make(chan int, 1) 27 | env.Go(func() { 28 | env.Sleep(2e9) 29 | _, err := clientConn.Read() 30 | if err != dccp.ErrEOF { 31 | t.Errorf("client read error (%s), expected EBADF", err) 32 | } 33 | cchan <- 1 34 | close(cchan) 35 | }, "test client") 36 | 37 | schan := make(chan int, 1) 38 | env.Go(func() { 39 | env.Sleep(1e9) 40 | if err := serverConn.Close(); err != nil { 41 | t.Errorf("server close error (%s)", err) 42 | } 43 | schan <- 1 44 | close(schan) 45 | }, "test server") 46 | 47 | <-cchan 48 | <-schan 49 | 50 | // Abort casuses both connection to wrap up the connection quickly 51 | clientConn.Abort() 52 | serverConn.Abort() 53 | // However, even aborting leaves various connection goroutines lingering for a short while. 54 | // The next line ensures that we wait until all goroutines are done. 55 | env.NewGoJoin("end-of-test", clientConn.Joiner(), serverConn.Joiner()).Join() 56 | 57 | dccp.NewAmb("line", env).E(dccp.EventMatch, "Server and client done.") 58 | if err := env.Close(); err != nil { 59 | t.Errorf("Error closing runtime (%s)", err) 60 | } 61 | } 62 | 63 | // Idle keeps the connection between a client and server idle for a few seconds and makes sure that 64 | // no unusual behavior occurs. 65 | func TestIdle(t *testing.T) { 66 | 67 | env, _ := NewEnv("idle") 68 | clientConn, serverConn, _, _ := NewClientServerPipe(env) 69 | payload := []byte{1, 2, 3} 70 | 71 | cchan := make(chan int, 1) 72 | env.Go(func() { 73 | if err := clientConn.Write(payload); err != nil { 74 | t.Errorf("client write (%s)", err) 75 | } 76 | env.Sleep(10e9) // Stay idle for 10 sec 77 | if err := clientConn.Close(); err != nil && err != dccp.ErrEOF { 78 | t.Errorf("client close (%s)", err) 79 | } 80 | cchan <- 1 81 | close(cchan) 82 | }, "test client") 83 | 84 | schan := make(chan int, 1) 85 | env.Go(func() { 86 | if err := serverConn.Write(payload); err != nil { 87 | t.Errorf("server write (%s)", err) 88 | } 89 | env.Sleep(10e9) // Stay idle for 10 sec 90 | if err := serverConn.Close(); err != nil && err != dccp.ErrEOF { 91 | // XXX why not EOF 92 | t.Logf("server close (%s)", err) 93 | } 94 | schan <- 1 95 | close(schan) 96 | }, "test server") 97 | 98 | <-cchan 99 | <-schan 100 | clientConn.Abort() 101 | serverConn.Abort() 102 | env.NewGoJoin("end-of-test", clientConn.Joiner(), serverConn.Joiner()).Join() 103 | 104 | dccp.NewAmb("line", env).E(dccp.EventMatch, "Server and client done.") 105 | if err := env.Close(); err != nil { 106 | t.Errorf("Error closing runtime (%s)", err) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /dccp/wire.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | // All multi-byte numerical quantities in DCCP, such as port numbers, Sequence Numbers, and 8 | // arguments to options, are transmitted in network byte order (most significant byte 9 | // first). 10 | 11 | // Wire format to integers 12 | 13 | func DecodeUint8(w []byte) uint8 { 14 | if len(w) != 1 { 15 | panic("size") 16 | } 17 | return uint8(w[0]) 18 | } 19 | 20 | func DecodeUint16(w []byte) uint16 { 21 | if len(w) != 2 { 22 | panic("size") 23 | } 24 | var u uint16 25 | u |= uint16(w[1]) 26 | u |= uint16(w[0]) << 8 27 | return u 28 | } 29 | 30 | func DecodeUint24(w []byte) uint32 { 31 | if len(w) != 3 { 32 | panic("size") 33 | } 34 | var u uint32 35 | u |= uint32(w[2]) 36 | u |= uint32(w[1]) << (8 * 1) 37 | u |= uint32(w[0]) << (8 * 2) 38 | return u 39 | } 40 | 41 | func DecodeUint32(w []byte) uint32 { 42 | if len(w) != 4 { 43 | panic("size") 44 | } 45 | var u uint32 46 | u |= uint32(w[3]) 47 | u |= uint32(w[2]) << (8 * 1) 48 | u |= uint32(w[1]) << (8 * 2) 49 | u |= uint32(w[0]) << (8 * 3) 50 | return u 51 | } 52 | 53 | func DecodeUint48(w []byte) uint64 { 54 | if len(w) != 6 { 55 | panic("size") 56 | } 57 | var u uint64 58 | u |= uint64(w[5]) 59 | u |= uint64(w[4]) << (8 * 1) 60 | u |= uint64(w[3]) << (8 * 2) 61 | u |= uint64(w[2]) << (8 * 3) 62 | u |= uint64(w[1]) << (8 * 4) 63 | u |= uint64(w[0]) << (8 * 5) 64 | return u 65 | } 66 | 67 | // Integers to wire format 68 | 69 | func EncodeUint8(u uint8, w []byte) { 70 | if len(w) != 1 { 71 | panic("size") 72 | } 73 | w[0] = u 74 | } 75 | 76 | func EncodeUint16(u uint16, w []byte) { 77 | if len(w) != 2 { 78 | panic("size") 79 | } 80 | w[1] = uint8(u & 0xff) 81 | w[0] = uint8((u >> 8) & 0xff) 82 | } 83 | 84 | func EncodeUint24(u uint32, w []byte) { 85 | if len(w) != 3 { 86 | panic("size") 87 | } 88 | w[2] = uint8(u & 0xff) 89 | w[1] = uint8((u >> (8 * 1)) & 0xff) 90 | w[0] = uint8((u >> (8 * 2)) & 0xff) 91 | if (u >> (8 * 3)) != 0 { 92 | panic("overflow") 93 | } 94 | } 95 | 96 | func EncodeUint32(u uint32, w []byte) { 97 | if len(w) != 4 { 98 | panic("size") 99 | } 100 | w[3] = uint8(u & 0xff) 101 | w[2] = uint8((u >> (8 * 1)) & 0xff) 102 | w[1] = uint8((u >> (8 * 2)) & 0xff) 103 | w[0] = uint8((u >> (8 * 3)) & 0xff) 104 | } 105 | 106 | func EncodeUint48(u uint64, w []byte) { 107 | if len(w) != 6 { 108 | panic("size") 109 | } 110 | w[5] = uint8(u & 0xff) 111 | w[4] = uint8((u >> (8 * 1)) & 0xff) 112 | w[3] = uint8((u >> (8 * 2)) & 0xff) 113 | w[2] = uint8((u >> (8 * 3)) & 0xff) 114 | w[1] = uint8((u >> (8 * 4)) & 0xff) 115 | w[0] = uint8((u >> (8 * 5)) & 0xff) 116 | if (u >> (8 * 6)) != 0 { 117 | panic("overflow") 118 | } 119 | } 120 | 121 | // Assertions 122 | 123 | func FitsIn16Bits(x uint64) bool { return x>>16 == 0 } 124 | 125 | func FitsIn24Bits(x uint64) bool { return x>>24 == 0 } 126 | 127 | func FitsIn32Bits(x uint64) bool { return x>>32 == 0 } 128 | 129 | func FitsIn23Bits(x uint64) bool { return x>>23 == 0 } 130 | 131 | func assertFitsIn16Bits(x uint64) { 132 | if !FitsIn16Bits(x) { 133 | panic("width overflow, 2 bytes") 134 | } 135 | } 136 | 137 | func assertFitsIn32Bits(x uint64) { 138 | if !FitsIn32Bits(x) { 139 | panic("width overflow, 4 bytes") 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /dccp/ccid3/nofeedback.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package ccid3 6 | 7 | import ( 8 | "github.com/petar/GoDCCP/dccp" 9 | ) 10 | 11 | // senderNoFeedbackTimer keeps track of the CCID3 nofeedback timeout at the 12 | // sender. The timeout may change in response to various events. 13 | type senderNoFeedbackTimer struct { 14 | resetTime int64 // Last time we got feedback; ns since UTC 15 | idleSince int64 // Time last packet of any type was sent 16 | lastDataSent int64 // Time last data packet was sent, or zero otherwise; ns since UTC 17 | dataInvFreq int64 // Interval between data packets, or zero if unknown; ns 18 | rtt int64 // Current known round-trip time estimate, or zero if none; ns 19 | } 20 | 21 | const ( 22 | NoFeedbackWeightNew = 1 23 | NoFeedbackWeightOld = 2 24 | NoFeedbackTimeoutWithoutRoundtrip = 2e9 // nofeedback timer expiration before RTT estimate, 2 sec 25 | ) 26 | 27 | // Init resets the nofeedback timer for new use 28 | func (t *senderNoFeedbackTimer) Init() { 29 | t.resetTime = 0 30 | t.idleSince = 0 31 | t.lastDataSent = 0 32 | t.dataInvFreq = 0 33 | t.rtt = 0 34 | } 35 | 36 | func (t *senderNoFeedbackTimer) GetIdleSinceAndReset() (idleSince int64, nofeedbackSet int64) { 37 | return t.idleSince, t.resetTime 38 | } 39 | 40 | // Sender calls OnRead each time a feedback packet is received. 41 | // OnRead restarts the nofeedback timer each time a feedback packet is received. 42 | func (t *senderNoFeedbackTimer) OnRead(rtt int64, rttEstimated bool, fb *dccp.FeedbackHeader) { 43 | if fb.Type != dccp.Ack && fb.Type != dccp.DataAck { 44 | return 45 | } 46 | if rttEstimated { 47 | t.rtt = rtt 48 | } else { 49 | t.rtt = 0 50 | } 51 | t.Reset(fb.Time) 52 | } 53 | 54 | // Sender calls OnWrite each time a packet is sent out to the receiver. 55 | // OnWrite is used to calculate timing between data packet sends. 56 | func (t *senderNoFeedbackTimer) OnWrite(ph *dccp.PreHeader) { 57 | // The very first time resetTime is set to equal the time when the first packet goes out, 58 | // since we are waiting for a feedback since that starting time. Afterwards, resetTime 59 | // can only assume times of incoming feedback packets. 60 | if t.resetTime <= 0 { 61 | t.resetTime = ph.TimeWrite 62 | } 63 | t.idleSince = ph.TimeWrite 64 | 65 | // Update inverse frequency of data packets estimate 66 | if ph.Type != dccp.Data && ph.Type != dccp.DataAck { 67 | return 68 | } 69 | if t.lastDataSent == 0 { 70 | t.lastDataSent = ph.TimeWrite 71 | return 72 | } 73 | d := ph.TimeWrite - t.lastDataSent 74 | if d <= 0 { 75 | return 76 | } 77 | if t.dataInvFreq == 0 { 78 | t.dataInvFreq = d 79 | return 80 | } 81 | t.dataInvFreq = (d*NoFeedbackWeightNew + t.dataInvFreq*NoFeedbackWeightOld) / 82 | (NoFeedbackWeightNew + NoFeedbackWeightOld) 83 | } 84 | 85 | // Sender calls OnIdle every time the idle clock ticks. OnIdle returns true if the 86 | // nofeedback timer has expired. 87 | func (t *senderNoFeedbackTimer) IsExpired(now int64) bool { 88 | if t.resetTime <= 0 { 89 | return false 90 | } 91 | return now - t.resetTime >= t.timeout() 92 | } 93 | 94 | func (t *senderNoFeedbackTimer) Reset(now int64) { 95 | t.resetTime = now 96 | } 97 | 98 | // timeout returns the current duration of the nofeedback timer in ns 99 | func (t *senderNoFeedbackTimer) timeout() int64 { 100 | if t.rtt <= 0 { 101 | return NoFeedbackTimeoutWithoutRoundtrip 102 | } 103 | if t.dataInvFreq <= 0 { 104 | return 4*t.rtt 105 | } 106 | return max64(4*t.rtt, 2*t.dataInvFreq) 107 | } 108 | -------------------------------------------------------------------------------- /dccp/segment.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | import "net" 8 | 9 | // Bytes is a type that has an equivalent representation as a byte slice 10 | // We use it for addresses, since net.Addr does not require such representation 11 | type Bytes interface { 12 | Bytes() []byte 13 | } 14 | 15 | // A SegmentConn is an I/O facility that explicitly reads/writes data in the form 16 | // of indivisible blocks of data. Implementors of this interface MUST only return 17 | // i/o errors defined in the dccp package (ErrEOF, ErrBad, ErrTimeout, etc.) 18 | type SegmentConn interface { 19 | // GetMTU returns th he largest allowable block size (for read and write). The MTU may vary. 20 | GetMTU() int 21 | 22 | // Read returns an ErrTimeout in the event of a timeout. See SetReadExpire. 23 | Read() (block []byte, err error) 24 | 25 | // If the user attempts to write a block that is too big, an ErrTooBig is returned 26 | // and the block is not sent. 27 | Write(block []byte) (err error) 28 | 29 | LocalLabel() Bytes 30 | 31 | RemoteLabel() Bytes 32 | 33 | // SetReadExpire sets the expiration time for any blocked calls to Read 34 | // as a time represented in nanoseconds from now. It's semantics are similar to that 35 | // of net.Conn.SetReadDeadline except that the deadline is specified in time from now, 36 | // rather than absolute time. Also note that Read is expected to return 37 | // an ErrTimeout in the event of timeouts. 38 | SetReadExpire(nsec int64) error 39 | 40 | Close() error 41 | } 42 | 43 | // SegmentDialAccepter represents a type that can accept and dial lossy packet connections 44 | type SegmentDialAccepter interface { 45 | Accept() (c SegmentConn, err error) 46 | Dial(addr net.Addr) (c SegmentConn, err error) 47 | Close() error 48 | } 49 | 50 | // Implementors of this interface MUST only return i/o errors defined in the dccp package (ErrEOF, 51 | // ErrBad, ErrTimeout, etc.) 52 | type HeaderConn interface { 53 | // GetMTU() returns the Maximum Transmission Unit size. This is the maximum 54 | // byte size of the header and app data wire-format footprint. 55 | GetMTU() int 56 | 57 | // Read returns ErrTimeout in the event of timeout. See SetReadExpire. 58 | Read() (h *Header, err error) 59 | 60 | // Write can return ErrTooBig, if the wire-format of h exceeds the MTU 61 | Write(h *Header) (err error) 62 | 63 | LocalLabel() Bytes 64 | 65 | RemoteLabel() Bytes 66 | 67 | // SetReadExpire behaves similarly to SegmentConn.SetReadExpire 68 | SetReadExpire(nsec int64) error 69 | 70 | Close() error 71 | } 72 | 73 | // ————— 74 | // NewHeaderConn creates a HeaderConn on top of a SegmentConn 75 | func NewHeaderConn(bc SegmentConn) HeaderConn { 76 | return &headerConn{bc: bc} 77 | } 78 | 79 | type headerConn struct { 80 | bc SegmentConn 81 | } 82 | 83 | func (hc *headerConn) GetMTU() int { 84 | return hc.bc.GetMTU() 85 | } 86 | 87 | // Since a SegmentConn already has the notion of a flow, both Read 88 | // and Write pass zero labels for the Source and Dest IPs 89 | // to the DCCP header's read and write functions. 90 | 91 | func (hc *headerConn) Read() (h *Header, err error) { 92 | p, err := hc.bc.Read() 93 | if err != nil { 94 | return nil, err 95 | } 96 | return ReadHeader(p, LabelZero.Bytes(), LabelZero.Bytes(), AnyProto, false) 97 | } 98 | 99 | func (hc *headerConn) Write(h *Header) (err error) { 100 | p, err := h.Write(LabelZero.Bytes(), LabelZero.Bytes(), AnyProto, false) 101 | if err != nil { 102 | return err 103 | } 104 | return hc.bc.Write(p) 105 | } 106 | 107 | func (hc *headerConn) LocalLabel() Bytes { 108 | return hc.bc.LocalLabel() 109 | } 110 | 111 | func (hc *headerConn) RemoteLabel() Bytes { 112 | return hc.bc.RemoteLabel() 113 | } 114 | 115 | func (hc *headerConn) SetReadExpire(nsec int64) error { 116 | return hc.bc.SetReadExpire(nsec) 117 | } 118 | 119 | func (hc *headerConn) Close() error { 120 | return hc.bc.Close() 121 | } 122 | -------------------------------------------------------------------------------- /dccp/ccid3/senderloss.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package ccid3 6 | 7 | import ( 8 | "fmt" 9 | "github.com/petar/GoDCCP/dccp" 10 | ) 11 | 12 | // ————— 13 | // senderLossTracker processes loss intervals options received at the sender and maintains relevant loss 14 | // statistics. 15 | type senderLossTracker struct { 16 | amb *dccp.Amb 17 | lastAckNo int64 // SeqNo of the last ack'd segment; equals the AckNo of the last feedback 18 | lastRateInv uint32 // Last known value of loss event rate inverse 19 | lossRateCalculator 20 | } 21 | 22 | // Init resets the senderLossTracker instance for new use 23 | func (t *senderLossTracker) Init(amb *dccp.Amb) { 24 | t.amb = amb.Refine("senderLossTracker") 25 | t.lastAckNo = 0 26 | t.lastRateInv = UnknownLossEventRateInv 27 | t.lossRateCalculator.Init(NINTERVAL) 28 | } 29 | 30 | // calcRateInv computes the loss event rate inverse encoded in the loss intervals 31 | func (t *senderLossTracker) calcRateInv(details []*LossIntervalDetail) uint32 { 32 | return t.lossRateCalculator.CalcLossEventRateInv(details) 33 | } 34 | 35 | // LossFeedback contains summary of loss information updates returned by OnRead 36 | type LossFeedback struct { 37 | RateInv uint32 // Loss event rate inverse 38 | NewLossCount byte // Number of loss events reported in this feedback packet 39 | RateInc bool // Has the loss rate increased since the last feedback packet 40 | } 41 | 42 | // Sender calls OnRead whenever a new feedback packet arrives 43 | func (t *senderLossTracker) OnRead(fb *dccp.FeedbackHeader) (LossFeedback, error) { 44 | 45 | // Read the loss options 46 | if fb.Type != dccp.Ack && fb.Type != dccp.DataAck { 47 | return LossFeedback{}, ErrNoAck 48 | } 49 | var lossIntervals *LossIntervalsOption 50 | t.amb.E(dccp.EventInfo, fmt.Sprintf("Encoded option count = %d", len(fb.Options)), fb) 51 | for i, opt := range fb.Options { 52 | if lossIntervals = DecodeLossIntervalsOption(opt); lossIntervals != nil { 53 | break 54 | } 55 | t.amb.E(dccp.EventInfo, fmt.Sprintf("Decodingd option %d", i), fb) 56 | } 57 | if lossIntervals == nil { 58 | t.amb.E(dccp.EventWarn, "Missing lossIntervals option", fb) 59 | return LossFeedback{}, ErrMissingOption 60 | } 61 | 62 | // Calcuate new loss count 63 | var r LossFeedback 64 | details := recoverIntervalDetails(fb.AckNo, lossIntervals.SkipLength, lossIntervals.LossIntervals) 65 | r.NewLossCount = calcNewLossCount(details, t.lastAckNo) 66 | 67 | // Calculate new rate inverse 68 | rateInv := t.calcRateInv(details) 69 | r.RateInv = rateInv 70 | if rateInv < t.lastRateInv { 71 | r.RateInc = true 72 | } 73 | t.lastRateInv = rateInv 74 | t.amb.E(dccp.EventMatch, fmt.Sprintf("Loss rate inv = %0.4g", 1 / float64(rateInv))) 75 | 76 | // XXX: Must use circular arithmetic here 77 | t.lastAckNo = max64(t.lastAckNo, fb.AckNo) 78 | 79 | return r, nil 80 | } 81 | 82 | // recoverIntervalDetails returns a slice containing the estimated details of the loss intervals 83 | func recoverIntervalDetails(ackno int64, skip byte, lis []*LossInterval) []*LossIntervalDetail { 84 | r := make([]*LossIntervalDetail, len(lis)) 85 | var head int64 = ackno + 1 - int64(skip) 86 | for i, li := range lis { 87 | r[i] = &LossIntervalDetail{} 88 | r[i].LossInterval = *li 89 | head -= int64(li.SeqLen()) 90 | r[i].StartSeqNo = head 91 | // TODO: StartTime, StartRTT, Unfinished are not recovered (but also not used) 92 | } 93 | return r 94 | } 95 | 96 | // calcNewLossCount calculates the number of new loss intervals reported in this feedback packet, 97 | // since the last packet (identified by lastAckNo) 98 | // XXX: Must use circular arithmetic here 99 | func calcNewLossCount(details []*LossIntervalDetail, lastAckNo int64) byte { 100 | // If lastAckNo is zero (no acks have been received), this function works correctly 101 | var r byte 102 | for _, d := range details { 103 | if d.StartSeqNo <= lastAckNo { 104 | break 105 | } 106 | r++ 107 | } 108 | return r 109 | } 110 | -------------------------------------------------------------------------------- /dccp/sandbox/measure.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package sandbox 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "testing" 11 | "github.com/petar/GoDCCP/dccp" 12 | ) 13 | 14 | // Measure is a dccp.TraceWriter which listens to the logs emitted from the 15 | // Roundtrip. It measures the real roundtrip time between the sender and 16 | // receiver, based on read and write logs and prints out this information. 17 | type Measure struct { 18 | t *testing.T 19 | env *dccp.Env 20 | leftClient map[int64]int64 // SeqNo —> Time left client 21 | leftServer map[int64]int64 // SeqNo —> Time left server 22 | 23 | clientToServerTransmit int64 24 | serverToClientTransmit int64 25 | 26 | clientToServerDrop int64 27 | serverToClientDrop int64 28 | 29 | clientToServerTriptime Moment 30 | serverToClientTriptime Moment 31 | } 32 | 33 | func NewMeasure(env *dccp.Env, t *testing.T) *Measure { 34 | x := &Measure{ 35 | env: env, 36 | t: t, 37 | leftClient: make(map[int64]int64), 38 | leftServer: make(map[int64]int64), 39 | } 40 | x.clientToServerTriptime.Init() 41 | x.serverToClientTriptime.Init() 42 | return x 43 | } 44 | 45 | func (x *Measure) Write(r *dccp.Trace) { 46 | now := x.env.Now() 47 | switch r.Event { 48 | case dccp.EventWrite: 49 | switch r.Labels[0] { 50 | case "client": 51 | x.leftClient[r.SeqNo] = now 52 | case "server": 53 | x.leftServer[r.SeqNo] = now 54 | } 55 | case dccp.EventRead: 56 | switch r.Labels[0] { 57 | case "client": 58 | left, ok := x.leftServer[r.SeqNo] 59 | if !ok { 60 | fmt.Printf("client read, no server write, seqno=%06x\n", r.SeqNo) 61 | } else { 62 | x.serverToClientTriptime.Add(float64(now - left)) 63 | delete(x.leftServer, r.SeqNo) 64 | x.serverToClientTransmit++ 65 | } 66 | case "server": 67 | left, ok := x.leftClient[r.SeqNo] 68 | if !ok { 69 | fmt.Printf("server read, no client write, seqno=%06x\n", r.SeqNo) 70 | } else { 71 | x.clientToServerTriptime.Add(float64(now - left)) 72 | delete(x.leftClient, r.SeqNo) 73 | x.clientToServerTransmit++ 74 | } 75 | } 76 | case dccp.EventDrop: 77 | if _, ok := x.leftClient[r.SeqNo]; ok { 78 | x.clientToServerDrop++ 79 | } 80 | delete(x.leftClient, r.SeqNo) 81 | if _, ok := x.leftServer[r.SeqNo]; ok { 82 | x.serverToClientDrop++ 83 | } 84 | delete(x.leftServer, r.SeqNo) 85 | } 86 | } 87 | 88 | func (x *Measure) Sync() error { 89 | return nil 90 | } 91 | 92 | func (x *Measure) Loss() (cs float64, csLoss, csTotal int64, sc float64, scLoss, scTotal int64) { 93 | csTotal = x.clientToServerTransmit + x.clientToServerDrop 94 | csLoss = x.clientToServerDrop 95 | if csTotal <= 0 { 96 | cs = 0 97 | } else { 98 | cs = float64(csLoss) / float64(csTotal) 99 | } 100 | scTotal = x.serverToClientTransmit + x.serverToClientDrop 101 | scLoss = x.serverToClientDrop 102 | if scTotal <= 0 { 103 | sc = 0 104 | } else { 105 | sc = float64(scLoss) / float64(scTotal) 106 | } 107 | return 108 | } 109 | 110 | func (x *Measure) String() string { 111 | var w bytes.Buffer 112 | cs, csLoss, csTotal, sc, scLoss, scTotal := x.Loss() 113 | fmt.Fprintf(&w, " Loss: c—>s %0.1f%% (%d/%d), c<—s %0.1f%% (%d/%d)\n", 114 | 100*cs, csLoss, csTotal, 100*sc, scLoss, scTotal) 115 | fmt.Fprintf(&w, "Trip time: c—>s %0.1f/%0.1f ms, c<—s %0.1f/%0.1f\n", 116 | x.clientToServerTriptime.Average()/1e6, x.clientToServerTriptime.StdDev()/1e6, 117 | x.serverToClientTriptime.Average()/1e6, x.serverToClientTriptime.StdDev()/1e6, 118 | ) 119 | if len(x.leftClient) > 0 { 120 | fmt.Fprintf(&w, "Left client and unclaimed:\n") 121 | } 122 | for s, _ := range x.leftClient { 123 | fmt.Fprintf(&w, " %06x\n", s) 124 | } 125 | if len(x.leftServer) > 0 { 126 | fmt.Fprintf(&w, "Left server and unclaimed:\n") 127 | } 128 | for s, _ := range x.leftServer { 129 | fmt.Fprintf(&w, " %06x\n", s) 130 | } 131 | return string(w.Bytes()) 132 | } 133 | 134 | func (x *Measure) Close() error { 135 | //fmt.Println(x.String()) 136 | return nil 137 | } 138 | -------------------------------------------------------------------------------- /dccp/mux_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | import ( 8 | "net" 9 | "testing" 10 | ) 11 | 12 | type endToEnd struct { 13 | t *testing.T 14 | alink Link 15 | dlink Link 16 | addr net.Addr 17 | nc int 18 | done chan int 19 | } 20 | 21 | func newEndToEnd(t *testing.T, alink, dlink Link, addr net.Addr, nc int) *endToEnd { 22 | return &endToEnd{t, alink, dlink, addr, nc, make(chan int)} 23 | } 24 | 25 | func (ee *endToEnd) acceptLoop(link Link) { 26 | 27 | m := NewMux(link) 28 | 29 | // Accept connections 30 | gg := make(chan int) 31 | for i := 0; i < ee.nc; i++ { 32 | c, err := m.Accept() 33 | if err != nil { 34 | ee.t.Fatalf("accept %s", c, err) 35 | } 36 | go func(c SegmentConn) { 37 | i := int(readUint32(ee.t, c)) 38 | 39 | // Expect to read the number i i-times 40 | for j := 0; j < i; j++ { 41 | i0 := int(readUint32(ee.t, c)) 42 | if i0 != i { 43 | ee.t.Fatalf("expecting %d, got %d\n", i, i0) 44 | } 45 | writeUint32(ee.t, c, uint32(i)) 46 | } 47 | if err = c.Close(); err != nil { 48 | ee.t.Fatalf("close %s", err) 49 | } 50 | gg <- i 51 | ee.done <- i 52 | }(c) 53 | } 54 | // Wait until all flows finish 55 | for i := 0; i < ee.nc; i++ { 56 | <-gg 57 | } 58 | if err := m.Close(); err != nil { 59 | ee.t.Errorf("a-close: %s", err) 60 | } 61 | } 62 | 63 | func (ee *endToEnd) dialLoop(link Link) { 64 | 65 | m := NewMux(link) 66 | 67 | // Dial connections 68 | gg := make(chan int) 69 | for i := 0; i < ee.nc; i++ { 70 | go func(i int) { 71 | c, err := m.Dial(ee.addr) 72 | if err != nil { 73 | ee.t.Fatalf("dial #%d: %s", i, err) 74 | } 75 | writeUint32(ee.t, c, uint32(i)) 76 | 77 | // Write the number i i-times 78 | for j := 0; j < i; j++ { 79 | writeUint32(ee.t, c, uint32(i)) 80 | i0 := int(readUint32(ee.t, c)) 81 | if i0 != i { 82 | ee.t.Fatalf("expecting %d, got %d\n", i, i0) 83 | } 84 | } 85 | if err = c.Close(); err != nil { 86 | ee.t.Fatalf("close: %s", err) 87 | } 88 | gg <- i 89 | }(i) 90 | } 91 | // Wait until all flows finish 92 | for i := 0; i < ee.nc; i++ { 93 | <-gg 94 | } 95 | if err := m.Close(); err != nil { 96 | ee.t.Errorf("d-close: %s", err) 97 | } 98 | } 99 | 100 | func readUint32(t *testing.T, c SegmentConn) uint32 { 101 | p, err := c.Read() 102 | if err != nil { 103 | t.Fatalf("read: %s", err) 104 | } 105 | if len(p) != 4 { 106 | t.Fatalf("read size: %d != 4", len(p)) 107 | } 108 | // fmt.Printf(" %s ···> %v\n", c.(*flow).String(), p[:4]) 109 | return DecodeUint32(p[:4]) 110 | } 111 | 112 | func writeUint32(t *testing.T, c SegmentConn, u uint32) { 113 | p := make([]byte, 4) 114 | EncodeUint32(u, p) 115 | err := c.Write(p) 116 | if err != nil { 117 | t.Fatalf("write: %s", err) 118 | } 119 | // fmt.Printf(" %s <··· %v\n", c.(*flow).String(), p) 120 | } 121 | 122 | func (ee *endToEnd) Run() { 123 | go ee.acceptLoop(ee.alink) 124 | go ee.dialLoop(ee.dlink) 125 | for i := 0; i < ee.nc; i++ { 126 | _, ok := <-ee.done 127 | if !ok { 128 | ee.t.Fatalf("premature close") 129 | } 130 | } 131 | } 132 | 133 | func TestMuxOverChan(t *testing.T) { 134 | alink, dlink := NewChanPipe() 135 | ee := newEndToEnd(t, alink, dlink, nil, 10) 136 | ee.Run() 137 | } 138 | 139 | func _TestMuxOverUDP(t *testing.T) { 140 | // Bind acceptor link 141 | aaddr, err := net.ResolveUDPAddr("udp", "0.0.0.0:44000") 142 | if err != nil { 143 | t.Fatalf("resolve a-addr: %s", err) 144 | } 145 | alink, err := BindUDPLink("udp", aaddr) 146 | if err != nil { 147 | t.Fatalf("bind udp a-link: %s", err) 148 | } 149 | 150 | // Bind dialer link 151 | daddr, err := net.ResolveUDPAddr("udp", "0.0.0.0:44001") 152 | if err != nil { 153 | t.Fatalf("resolve d-addr: %s", err) 154 | } 155 | dlink, err := BindUDPLink("udp", daddr) 156 | if err != nil { 157 | t.Fatalf("bind udp d-link: %s", err) 158 | } 159 | 160 | // Resolve dialer address 161 | addr, err := net.ResolveUDPAddr("udp", "127.0.0.1:44000") 162 | if err != nil { 163 | t.Fatalf("resolve addr: %s", err) 164 | } 165 | 166 | ee := newEndToEnd(t, alink, dlink, addr, 10) 167 | ee.Run() 168 | } 169 | -------------------------------------------------------------------------------- /dccp/pipe.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | func (c *Conn) readHeader() (h *Header, err error) { 8 | h, err = c.hc.Read() 9 | if err != nil { 10 | if err != ErrTimeout { 11 | c.amb.E(EventDrop, "Bad header", h) 12 | } 13 | return nil, err 14 | } 15 | // We don't support non-extended (short) SeqNo's 16 | if !h.X { 17 | return nil, ErrUnsupported 18 | } 19 | return h, nil 20 | } 21 | 22 | // idleLoop polls the congestion control OnIdle method at regular intervals 23 | // of approximately one RTT. 24 | func (c *Conn) idleLoop() { 25 | for { 26 | c.pollCongestionControl() 27 | 28 | c.Lock() 29 | c.syncWithCongestionControl() 30 | rtt := c.socket.GetRTT() 31 | state := c.socket.GetState() 32 | c.Unlock() 33 | 34 | if state == CLOSED { 35 | break 36 | } 37 | // This emit prints very often. Use when really necessary 38 | //c.amb.E(EventIdle, "") 39 | c.env.Sleep(max64(RoundtripMin, min64(rtt, RoundtripDefault))) 40 | } 41 | } 42 | 43 | func (c *Conn) readLoop() { 44 | for { 45 | c.Lock() 46 | state := c.socket.GetState() 47 | rtt := c.socket.GetRTT() 48 | c.Unlock() 49 | if state == CLOSED { 50 | break 51 | } 52 | 53 | // Adjust read timeout 54 | if err := c.hc.SetReadExpire(5 * rtt); err != nil { 55 | c.amb.E(EventError, "SetReadExpire") 56 | c.abortQuietly() 57 | return 58 | } 59 | 60 | // Read next header 61 | h, err := c.readHeader() 62 | if err != nil { 63 | _, ok := err.(ProtoError) 64 | if ok { 65 | // Drop packets that are unsupported. Intended for forward compatibility. 66 | continue 67 | } else if err == ErrTimeout { 68 | // In the even of timeout, poll the congestion controls 69 | c.pollCongestionControl() 70 | continue 71 | } else { 72 | // Die if the underlying link is broken 73 | c.abortQuietly() 74 | return 75 | } 76 | } 77 | c.amb.E(EventRead, "", h) 78 | 79 | c.Lock() 80 | c.syncWithCongestionControl() 81 | if c.step2_ProcessTIMEWAIT(h) != nil { 82 | goto Done 83 | } 84 | if c.step3_ProcessLISTEN(h) != nil { 85 | goto Done 86 | } 87 | if c.step4_PrepSeqNoREQUEST(h) != nil { 88 | goto Done 89 | } 90 | if c.step5_PrepSeqNoForSync(h) != nil { 91 | goto Done 92 | } 93 | if c.step6_CheckSeqNo(h) != nil { 94 | goto Done 95 | } 96 | if c.step7_CheckUnexpectedTypes(h) != nil { 97 | goto Done 98 | } 99 | if c.step8_OptionsAndMarkAckbl(h) != nil { 100 | goto Done 101 | } 102 | if c.step9_ProcessReset(h) != nil { 103 | goto Done 104 | } 105 | if c.step10_ProcessREQUEST2(h) != nil { 106 | goto Done 107 | } 108 | if c.step11_ProcessRESPOND(h) != nil { 109 | goto Done 110 | } 111 | if c.step12_ProcessPARTOPEN(h) != nil { 112 | goto Done 113 | } 114 | if c.step13_ProcessCloseReq(h) != nil { 115 | goto Done 116 | } 117 | if c.step14_ProcessClose(h) != nil { 118 | goto Done 119 | } 120 | if c.step15_ProcessSync(h) != nil { 121 | goto Done 122 | } 123 | if c.step16_ProcessData(h) != nil { 124 | goto Done 125 | } 126 | Done: 127 | c.Unlock() 128 | } 129 | c.amb.E(EventInfo, "Read loop EXIT") 130 | } 131 | 132 | func (c *Conn) pollCongestionControl() { 133 | now := c.env.Now() 134 | if e := c.scc.OnIdle(now); e != nil { 135 | if re, ok := e.(CongestionReset); ok { 136 | c.abortWith(re.ResetCode()) 137 | return 138 | } 139 | if e == CongestionAck { 140 | c.Lock() 141 | c.inject(c.generateAck()) 142 | c.Unlock() 143 | return 144 | } 145 | c.amb.E(EventError, "Sender CC unknown idle error") 146 | } 147 | if e := c.rcc.OnIdle(now); e != nil { 148 | if re, ok := e.(CongestionReset); ok { 149 | c.abortWith(re.ResetCode()) 150 | return 151 | } 152 | if e == CongestionAck { 153 | c.Lock() 154 | c.inject(c.generateAck()) 155 | c.Unlock() 156 | return 157 | } 158 | c.amb.E(EventError, "Receiver CC unknown idle error") 159 | } 160 | } 161 | 162 | func (c *Conn) syncWithCongestionControl() { 163 | c.AssertLocked() 164 | c.socket.SetRTT(c.scc.GetRTT()) 165 | c.socket.SetCCMPS(c.scc.GetCCMPS()) 166 | } 167 | 168 | func (c *Conn) syncWithLink() { 169 | c.AssertLocked() 170 | c.socket.SetPMTU(int32(c.hc.GetMTU())) 171 | } 172 | -------------------------------------------------------------------------------- /cmd/dccp-inspector/ui_js.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "io" 4 | 5 | /* 6 | UI behavior: 7 | o Clicking on a row whose log entry pertains to a packet: 8 | (i) Highlights in red the cell that was originally clicked 9 | (ii) Highlights in yellow all other rows pertaining to the same packet (same SeqNo) 10 | (iii) Highlights in orange all other rows pertaining to packets acknowledging this 11 | packet (AckNo is same as SeqNo of original packet) 12 | (iv) Removes highlighting on rows that were previously highlighted using this 13 | procedure 14 | o Clicking the (left-most) cell of a row toggles a dark frame around the client-half of 15 | the row 16 | o Clicking the (right-most) cell of a row toggles a dark frame around the server-half of the row 17 | o Hovering over any row zooms on the row 18 | */ 19 | 20 | const ( 21 | inspectorJavaScript = 22 | ` 23 | jQuery(document).ready(function(){ 24 | $('td[seqno].nonempty').click(onLeftClick); 25 | $('tr').mouseenter(hilightRow); 26 | $('tr').dblclick(toggleFoldRow); 27 | $('td.time').click(rotateMarkClientRow); 28 | $('td.time-abs').click(rotateMarkServerRow); 29 | $('td:has(div.tooltip)').mouseenter(showTooltip); 30 | $('td:has(div.tooltip)').mouseleave(hideTooltip); 31 | buildGraph(); 32 | }) 33 | function showTooltip() { 34 | var tt = $('div.tooltip', this); 35 | tt.css("display", "block"); 36 | } 37 | function hideTooltip() { 38 | var tt = $('div.tooltip', this); 39 | tt.css("display", "none"); 40 | } 41 | function onLeftClick(e) { 42 | var seqno = $(this).attr("seqno"); 43 | if (_.isUndefined(seqno) || seqno == "") { 44 | return; 45 | } 46 | clearEmphasis(); 47 | _.each($('[seqno='+seqno+'].nonempty'), function(t) { emphasize(t, "yellow-bkg") }); 48 | _.each($('[ackno='+seqno+'].nonempty'), function(t) { emphasize(t, "orange-bkg") }); 49 | emphasize($(this), "red-bkg"); 50 | } 51 | function emphasize(t, bkg) { 52 | t = $(t); 53 | var saved_bkg = t.attr("emph"); 54 | if (!_.isUndefined(saved_bkg)) { 55 | t.removeClass(saved_bkg); 56 | } 57 | t.addClass(bkg); 58 | t.attr("emph", bkg); 59 | } 60 | function clearEmphasis() { 61 | _.each($('[emph]'), function(t) { 62 | t = $(t); 63 | var saved_bkg = t.attr("emph"); 64 | t.removeAttr("emph"); 65 | t.removeClass(saved_bkg); 66 | }); 67 | } 68 | function hilightRow() { 69 | _.each($('[hi]'), deHilightRow); 70 | $(this).attr("hi", 1); 71 | $('td', this).addClass("hi-bkg"); 72 | } 73 | function toggleFoldRow() { 74 | $(this).addClass("folded"); 75 | } 76 | function deHilightRow(t) { 77 | t = $(t); 78 | $('td', t).removeClass("hi-bkg"); 79 | t.removeAttr("hi"); 80 | } 81 | function rotateMarkClientRow() { 82 | var trow = $(this).parents()[0]; 83 | _.each($('td.client, td.time', trow), _rotateMark); 84 | } 85 | function _rotateMark(t) { 86 | t = $(t); 87 | if (t.hasClass("mark-0")) { 88 | t.removeClass("mark-0"); 89 | t.addClass("mark-1"); 90 | } else if (t.hasClass("mark-1")) { 91 | t.removeClass("mark-1"); 92 | t.addClass("mark-2"); 93 | } else if (t.hasClass("mark-2")) { 94 | t.removeClass("mark-2"); 95 | } else { 96 | t.addClass("mark-0"); 97 | } 98 | } 99 | function rotateMarkServerRow() { 100 | var trow = $(this).parents()[0]; 101 | _.each($('td.server, td.time-abs', trow), _rotateMark); 102 | } 103 | ` 104 | ) 105 | 106 | func printGraphJavaScript(w io.Writer, sweeper *SeriesSweeper) error { 107 | const one = 108 | ` 109 | function buildGraph() { 110 | g = new Dygraph( 111 | document.getElementById("graph-box"), 112 | ` 113 | const two = 114 | ` 115 | , { 116 | connectSeparatedPoints: true, 117 | labelsDivWidth: 700, 118 | labelsDivStyles: { 'fontFamily': "Droid Sans Mono", 'fontWeight': "normal" }, 119 | labelsSeparateLines: true, 120 | axes: { 121 | x: { 122 | axisLabelFormatter: function(x) { 123 | return x + 'ms'; 124 | } 125 | } 126 | }, 127 | drawPoints: true, 128 | colors: [ '#cc0000', '#00cc00', '#0000cc',' #00cccc', '#cc00cc', '#cccc00' ], 129 | labels: ` 130 | const three = 131 | ` 132 | } 133 | ); 134 | } 135 | ` 136 | w.Write([]byte(one)) 137 | sweeper.EncodeData(w) 138 | w.Write([]byte(two)) 139 | sweeper.EncodeHeader(w) 140 | _, err := w.Write([]byte(three)) 141 | return err 142 | } 143 | -------------------------------------------------------------------------------- /dccp/gauge/reduce.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package gauge 6 | 7 | import ( 8 | "fmt" 9 | "os" 10 | "sort" 11 | "sync" 12 | "github.com/petar/GoDCCP/dccp" 13 | ) 14 | 15 | // LogReducer is a dccp.TraceWriter which processes the logs to a form 16 | // that is convenient as input to illustration tools 17 | type LogReducer struct { 18 | sync.Mutex 19 | checkIns []*dccp.Trace 20 | places map[string]*Place 21 | trips map[int64]*Trip 22 | } 23 | 24 | type Place struct { 25 | latest *dccp.Trace 26 | CheckIns []*dccp.Trace 27 | } 28 | 29 | // A Trip instance captures all packet check-ins whose SeqNo or AckNo are related 30 | type Trip struct { 31 | SeqNo int64 32 | Forward []*dccp.Trace 33 | Backward []*dccp.Trace 34 | Source string 35 | Sink string 36 | Round bool 37 | } 38 | 39 | // Make a new log reducer 40 | func NewLogReducer() *LogReducer { 41 | t := &LogReducer{} 42 | t.Init() 43 | return t 44 | } 45 | 46 | // Init (re)initializes an existing log reducer structure 47 | func (t *LogReducer) Init() { 48 | t.checkIns = make([]*dccp.Trace, 0, 16) 49 | t.places = make(map[string]*Place) 50 | t.trips = make(map[int64]*Trip) 51 | } 52 | 53 | func (t *LogReducer) Write(r *dccp.Trace) { 54 | t.Lock() 55 | defer t.Unlock() 56 | 57 | // Check-ins update 58 | if r.Labels[0] == "" { 59 | panic("empty root label string in log") 60 | } 61 | t.checkIns = append(t.checkIns, r) 62 | 63 | // Places update 64 | p, ok := t.places[r.Labels[0]] 65 | if !ok { 66 | p = &Place{ 67 | latest: nil, 68 | CheckIns: make([]*dccp.Trace, 0), 69 | } 70 | t.places[r.Labels[0]] = p 71 | } 72 | 73 | if p.latest != nil && r.Time <= p.latest.Time { 74 | fmt.Fprintf(os.Stderr, "lastTime=%d, thisTime=%d\n", p.latest.Time, r.Time) 75 | fmt.Fprintf(os.Stderr, "last=%v\nthis=%v\n", p.latest, r) 76 | panic("backward time in reducer") 77 | } 78 | p.latest = r 79 | p.CheckIns = append(p.CheckIns, r) 80 | 81 | // Trips update 82 | if r.SeqNo != 0 { 83 | t.tripForward(r) 84 | } 85 | if r.AckNo != 0 { 86 | t.tripBackward(r) 87 | } 88 | } 89 | 90 | func (t *LogReducer) tripForward(r *dccp.Trace) { 91 | x, ok := t.trips[r.SeqNo] 92 | if !ok { 93 | x = &Trip{ 94 | SeqNo: r.SeqNo, 95 | Forward: make([]*dccp.Trace, 0), 96 | Backward: make([]*dccp.Trace, 0), 97 | } 98 | t.trips[r.SeqNo] = x 99 | } 100 | 101 | x.Forward = append(x.Forward, r) 102 | sort.Sort(TraceChrono(x.Forward)) 103 | x.Source = x.Forward[0].Labels[0] 104 | 105 | updateTrip(x) 106 | } 107 | 108 | func (t *LogReducer) tripBackward(r *dccp.Trace) { 109 | y, ok := t.trips[r.AckNo] 110 | if !ok { 111 | y = &Trip{ 112 | SeqNo: r.AckNo, 113 | Forward: make([]*dccp.Trace, 0), 114 | Backward: make([]*dccp.Trace, 0), 115 | } 116 | t.trips[r.AckNo] = y 117 | } 118 | 119 | y.Backward = append(y.Backward, r) 120 | sort.Sort(TraceChrono(y.Backward)) 121 | y.Sink = y.Backward[len(y.Backward)-1].Labels[0] 122 | 123 | updateTrip(y) 124 | } 125 | 126 | func updateTrip(t *Trip) { 127 | if t.Source == "" { 128 | return 129 | } 130 | if t.Source == t.Sink { 131 | t.Round = true 132 | } 133 | } 134 | 135 | // CheckIns returns a list of all check-ins 136 | func (t *LogReducer) CheckIns() []*dccp.Trace { 137 | t.Lock() 138 | defer func() { t.checkIns = nil }() // So Write does not try to update after this call accidentally 139 | defer t.Unlock() 140 | 141 | sort.Sort(TraceChrono(t.checkIns)) 142 | return t.checkIns 143 | } 144 | 145 | // Places returns places' histories, keyed by place name 146 | func (t *LogReducer) Places() map[string]*Place { 147 | t.Lock() 148 | defer func() { t.places = nil }() 149 | defer t.Unlock() 150 | 151 | return t.places 152 | } 153 | 154 | // Trips returns trip records, keyed by SeqNo 155 | func (t *LogReducer) Trips() map[int64]*Trip { 156 | t.Lock() 157 | defer func() { t.trips = nil }() 158 | defer t.Unlock() 159 | 160 | return t.trips 161 | } 162 | 163 | func TripMapToSlice(m map[int64]*Trip) []*Trip { 164 | s := make([]*Trip, len(m)) 165 | var i int 166 | for _, t := range m { 167 | s[i] = t 168 | i++ 169 | } 170 | return s 171 | } 172 | 173 | // TraceChrono is a chronological sort driver for []*dccp.Trace 174 | type TraceChrono []*dccp.Trace 175 | 176 | func (t TraceChrono) Len() int { 177 | return len(t) 178 | } 179 | 180 | func (t TraceChrono) Less(i, j int) bool { 181 | return t[i].Time < t[j].Time 182 | } 183 | 184 | func (t TraceChrono) Swap(i, j int) { 185 | t[i], t[j] = t[j], t[i] 186 | } 187 | -------------------------------------------------------------------------------- /dccp/flow.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | import ( 8 | "net" 9 | "time" 10 | ) 11 | 12 | // flow is an implementation of SegmentConn 13 | type flow struct { 14 | addr net.Addr 15 | m *Mux 16 | ch chan muxHeader 17 | mtu int 18 | 19 | Mutex // protects the variables below 20 | local *Label 21 | remote *Label 22 | lastRead time.Time 23 | lastWrite time.Time 24 | readDeadline time.Time 25 | 26 | rlk Mutex // synchronizes calls to Read() 27 | } 28 | 29 | // addr is the Link-level address of the remote. 30 | // local and remote are logical labels that are associated with each endpoint 31 | // of the connection. The remote label is not known until a packet is received 32 | // from the other side. 33 | func newFlow(addr net.Addr, m *Mux, ch chan muxHeader, mtu int, local, remote *Label) *flow { 34 | now := time.Now() 35 | return &flow{ 36 | addr: addr, 37 | local: local, 38 | remote: remote, 39 | lastRead: now, 40 | lastWrite: now, 41 | readDeadline: time.Now().Add(-time.Second), // time in the past 42 | m: m, 43 | ch: ch, 44 | mtu: mtu, 45 | } 46 | } 47 | 48 | // GetMTU returns the largest size of read/write block 49 | func (f *flow) GetMTU() int { return f.mtu } 50 | 51 | // SetReadExpire implements SegmentConn.SetReadExpire 52 | func (f *flow) SetReadExpire(nsec int64) error { 53 | if nsec < 0 { 54 | return ErrInvalid 55 | } 56 | f.Lock() 57 | defer f.Unlock() 58 | f.readDeadline = time.Now().Add(time.Duration(nsec)) 59 | return nil 60 | } 61 | 62 | // LastRead() returns the timestamp of the last successful read operation 63 | func (f *flow) LastReadTime() time.Time { 64 | f.Lock() 65 | defer f.Unlock() 66 | return f.lastRead 67 | } 68 | 69 | // LastWrite() returns the timestamp of the last successful write operation 70 | func (f *flow) LastWriteTime() time.Time { 71 | f.Lock() 72 | defer f.Unlock() 73 | return f.lastWrite 74 | } 75 | 76 | func (f *flow) setRemote(remote *Label) { 77 | f.Lock() 78 | defer f.Unlock() 79 | if f.remote != nil { 80 | panic("setting remote label twice") 81 | } 82 | f.remote = remote 83 | } 84 | 85 | func (f *flow) getRemote() *Label { 86 | f.Lock() 87 | defer f.Unlock() 88 | return f.remote 89 | } 90 | 91 | // RemoteLabel implements SegmentConn.RemoteLabel 92 | func (f *flow) RemoteLabel() Bytes { return f.getRemote() } 93 | 94 | func (f *flow) getLocal() *Label { 95 | f.Lock() 96 | defer f.Unlock() 97 | return f.local 98 | } 99 | 100 | // LocalLabel implements SegmentConn.LocalLabel 101 | func (f *flow) LocalLabel() Bytes { return f.getLocal() } 102 | 103 | func (f *flow) String() string { 104 | return f.getLocal().String() + "--" + f.getRemote().String() 105 | } 106 | 107 | // Write implements SegmentConn.Write 108 | func (f *flow) Write(block []byte) error { 109 | f.Lock() 110 | m := f.m 111 | f.Unlock() 112 | if m == nil { 113 | return ErrBad 114 | } 115 | err := m.write(&muxMsg{f.getLocal(), f.getRemote()}, block, f.addr) 116 | if err != nil { 117 | f.Lock() 118 | f.lastWrite = time.Now() 119 | f.Unlock() 120 | } 121 | return err 122 | } 123 | 124 | // Read implements SegmentConn.Read 125 | func (f *flow) Read() (block []byte, err error) { 126 | f.rlk.Lock() 127 | defer f.rlk.Unlock() 128 | 129 | f.Lock() 130 | ch := f.ch 131 | readDeadline := f.readDeadline 132 | f.Unlock() 133 | readTimeout := readDeadline.Sub(time.Now()) 134 | if ch == nil { 135 | return nil, ErrBad 136 | } 137 | 138 | var timer *time.Timer 139 | var tmoch <-chan time.Time 140 | if readTimeout > 0 { 141 | timer = time.NewTimer(readTimeout) 142 | defer timer.Stop() 143 | tmoch = timer.C 144 | } 145 | 146 | var header muxHeader 147 | var ok bool 148 | select { 149 | case header, ok = <-ch: 150 | if !ok { 151 | return nil, ErrIO 152 | } 153 | case <-tmoch: 154 | return nil, ErrTimeout 155 | } 156 | 157 | f.Lock() 158 | f.lastRead = time.Now() 159 | f.Unlock() 160 | 161 | return header.Cargo, nil 162 | } 163 | 164 | func (f *flow) foreclose() { 165 | f.Lock() 166 | defer f.Unlock() 167 | 168 | if f.ch != nil { 169 | close(f.ch) 170 | f.ch = nil 171 | } 172 | } 173 | 174 | // Close implements SegmentConn.Close 175 | func (f *flow) Close() error { 176 | f.Lock() 177 | if f.ch != nil { 178 | close(f.ch) 179 | f.ch = nil 180 | } 181 | m := f.m 182 | f.m = nil 183 | f.Unlock() 184 | if m == nil { 185 | return ErrBad 186 | } 187 | m.del(f.getLocal(), f.getRemote()) 188 | return nil 189 | } 190 | -------------------------------------------------------------------------------- /dccp/label.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | import ( 8 | "bytes" 9 | "errors" 10 | "hash/crc64" 11 | "math/rand" 12 | "strings" 13 | ) 14 | 15 | // Label{} is an immutable object, representing a general-purpose address 16 | // similar to IPv6 in that it consists of 16 opaque bytes. 17 | type Label struct { 18 | data [LabelLen]byte 19 | h uint64 20 | } 21 | 22 | const LabelLen = 16 23 | 24 | var ( 25 | LabelZero = Label{} 26 | labelCRC64Table = crc64.MakeTable(crc64.ISO) 27 | ) 28 | 29 | const labelFootprint = LabelLen 30 | 31 | func (label *Label) Bytes() []byte { 32 | if label != nil { 33 | return label.data[:] 34 | } 35 | return LabelZero.data[:] 36 | } 37 | 38 | func (label *Label) hash() { 39 | label.h = crc64.Checksum(label.data[:], labelCRC64Table) 40 | } 41 | 42 | func isZero(bb []byte) bool { 43 | for _, b := range bb { 44 | if b != 0 { 45 | return false 46 | } 47 | } 48 | return true 49 | } 50 | 51 | // ChooseLabel() creates a new label by choosing its bytes randomly 52 | func ChooseLabel() *Label { 53 | label := &Label{} 54 | for i := 0; i < LabelLen/2; i++ { 55 | q := rand.Int() 56 | label.data[2*i] = byte(q & 0xff) 57 | q >>= 8 58 | label.data[2*i+1] = byte(q & 0xff) 59 | } 60 | label.hash() 61 | return label 62 | } 63 | 64 | // Hash() returns the hash code of this label 65 | func (label *Label) Hash() uint64 { 66 | return label.h 67 | } 68 | 69 | // Equal() performs a deep check for equality with q@ 70 | func (label *Label) Equal(q *Label) bool { 71 | for i := 0; i < LabelLen; i++ { 72 | if label.data[i] != q.data[i] { 73 | return false 74 | } 75 | } 76 | return true 77 | } 78 | 79 | // ReadLabel() reads and creates a new label from a wire format representation in p@ 80 | func ReadLabel(p []byte) (label *Label, n int, err error) { 81 | if len(p) < LabelLen { 82 | return nil, 0, errors.New("label too short") 83 | } 84 | label = &Label{} 85 | copy(label.data[:], p[:LabelLen]) 86 | label.hash() 87 | if isZero(label.data[:]) { 88 | return nil, LabelLen, nil 89 | } 90 | return label, LabelLen, nil 91 | } 92 | 93 | // Write() writes the wire format representation of the label into p@ 94 | func (label *Label) Write(p []byte) (n int, err error) { 95 | if label == nil { 96 | label = &LabelZero 97 | } 98 | if len(p) < LabelLen { 99 | return 0, errors.New("label can't fit") 100 | } 101 | copy(p, label.data[:]) 102 | return LabelLen, nil 103 | } 104 | 105 | // String() returns a string representation of the label 106 | func (label *Label) String() string { 107 | if label == nil { 108 | label = &LabelZero 109 | } 110 | var w bytes.Buffer 111 | for i, b := range label.data { 112 | if i%2 == 0 && i > 0 { 113 | w.WriteByte('`') 114 | } 115 | w.WriteString(btox(b)) 116 | } 117 | return string(w.Bytes()) 118 | } 119 | 120 | func (label *Label) Address() string { 121 | return label.String() 122 | } 123 | 124 | // ParseLabel() parses and creates a new label from the string representation in s@ 125 | func ParseLabel(s string) (label *Label, n int, err error) { 126 | l := LabelLen*2 + LabelLen/2 - 1 127 | if len(s) < l { 128 | return nil, 0, errors.New("bad string label len") 129 | } 130 | s = strings.ToLower(s) 131 | label = &Label{} 132 | k := 0 133 | for i := 0; i < LabelLen; { 134 | if i%2 == 0 && i > 0 { 135 | if s[i] != '`' { 136 | return nil, 0, errors.New("missing label dot") 137 | } 138 | i++ 139 | continue 140 | } 141 | b, err := xtob(s[i : i+2]) 142 | if err != nil { 143 | return nil, 0, err 144 | } 145 | i += 2 146 | label.data[k] = byte(b) 147 | k++ 148 | } 149 | label.hash() 150 | if isZero(label.data[:]) { 151 | return nil, l, nil 152 | } 153 | return label, l, nil 154 | } 155 | 156 | const xalpha = "0123456789abcdef" 157 | 158 | func btox(b byte) string { 159 | r := make([]byte, 2) 160 | r[1] = xalpha[b&0xf] 161 | b >>= 4 162 | r[0] = xalpha[b&0xf] 163 | return string(r) 164 | } 165 | 166 | func xtohb(a byte) (b byte, err error) { 167 | if a >= '0' && a <= '9' { 168 | return a - '0', nil 169 | } 170 | if a >= 'a' && a <= 'f' { 171 | return 9 + a - 'a', nil 172 | } 173 | return 0, errors.New("xtohb invalid char") 174 | } 175 | 176 | func xtob(s string) (b byte, err error) { 177 | if len(s) != 2 { 178 | return 0, errors.New("xtob len error") 179 | } 180 | b0, err := xtohb(s[1]) 181 | if err != nil { 182 | return 0, err 183 | } 184 | b1, err := xtohb(s[0]) 185 | if err != nil { 186 | return 0, err 187 | } 188 | return (b1 << 4) | b0, nil 189 | } 190 | -------------------------------------------------------------------------------- /doc/8.1-Connection-Establishment: -------------------------------------------------------------------------------- 1 | 2 | 8.1. Connection Establishment 3 | ============================== 4 | 5 | Client State Server State 6 | CLOSED LISTEN 7 | 1. REQUEST --> Request --> 8 | 2. <-- Response <-- RESPOND 9 | 3. PARTOPEN --> Ack, DataAck --> 10 | 4. <-- Data, Ack, DataAck <-- OPEN 11 | 5. OPEN <-> Data, Ack, DataAck <-> OPEN 12 | 13 | 8.1.1. Client Request 14 | ====================== 15 | 16 | 17 | CLIENT: 18 | Enter REQUEST state 19 | Choose Initial Seq No 20 | Send DCCP-Request 21 | 22 | (REQUEST) 23 | Use exponential back-off to resent DCCP-Request 24 | First retransmit at 1 sec, 25 | Backing off to not less than one packet every 64 seconds 26 | Each new DCCP-Request MUST increment the Seq No 27 | and MUST contain the same Service Code and application data as the original DCCP-Request. 28 | 29 | MAY give up on DCCP-Requests after some time (eg. 3 minutes) 30 | When it does, it SHOULD send a DCCP-Reset packet to the server with Reset Code 2, "Aborted" 31 | 32 | A client in REQUEST state has never received an initial sequence number from its peer, so the DCCP-Reset's Acknowledgement Number MUST be set to zero. 33 | 34 | The client leaves the REQUEST state for PARTOPEN when it receives a DCCP-Response from the server. 35 | 36 | 37 | ServiceCodes: 38 | 39 | Each active socket(Conn, 2x HalfConn) MUST have exactly one Service Code. 40 | 41 | If the DCCP-Request's Service Code doesn't equal any of the server's Service Codes for the 42 | given port, the server MUST reject the request by sending a DCCP-Reset packet with Reset Code 8, 43 | "Bad Service Code". 44 | 45 | Server Response: 46 | 47 | The server MAY respond to a DCCP-Request packet with a DCCP-Reset 48 | packet to refuse the connection: 7, "Connection Refused"; 8, "Bad Service Code"; 49 | 9, "Too Busy" 50 | 51 | The server SHOULD limit the rate at which it generates these resets; for example, to not more than 1024 per second. 52 | 53 | The server SHOULD NOT retransmit DCCP-Response packets; the client 54 | will retransmit the DCCP-Request if necessary. 55 | 56 | Every valid DCCP-Request received while the server is in the RESPOND 57 | state MUST elicit a new DCCP-Response. 58 | 59 | Each new DCCP-Response MUST increment the server's Sequence Number by one and MUST include the 60 | same application data, if any, as the original DCCP-Response. 61 | 62 | The server MUST NOT accept more than one piece of DCCP-Request 63 | application data per connection. In particular, the DCCP-Response 64 | sent in reply to a retransmitted DCCP-Request with application data 65 | SHOULD contain a Data Dropped option, in which the retransmitted 66 | DCCP-Request data is reported with Drop Code 0, Protocol Constraints. 67 | The original DCCP-Request SHOULD also be reported in the Data Dropped 68 | option, either in a Normal Block (if the server accepted the data or 69 | there was no data) or in a Drop Code 0 Drop Block (if the server 70 | refused the data the first time as well). 71 | 72 | The server leaves the RESPOND state for OPEN when it receives a valid 73 | DCCP-Ack from the client, completing the three-way handshake. 74 | 75 | It MAY also leave the RESPOND state for CLOSED after a timeout of not less 76 | than 4MSL (8 minutes); when doing so, it SHOULD send a DCCP-Reset 77 | with Reset Code 2, "Aborted", to clean up state at the client. 78 | 79 | Handshake completion: 80 | 81 | When the client receives a DCCP-Response from the server, it moves 82 | from the REQUEST state to PARTOPEN and completes the three-way 83 | handshake by sending a DCCP-Ack packet to the server. The client 84 | remains in PARTOPEN until it can be sure that the server has received 85 | some packet the client sent from PARTOPEN (either the initial DCCP- 86 | Ack or a later packet). Clients in the PARTOPEN state that want to 87 | send data MUST do so using DCCP-DataAck packets, not DCCP-Data 88 | packets. This is because DCCP-Data packets lack Acknowledgement 89 | Numbers, so the server can't tell from a DCCP-Data packet whether the 90 | client saw its DCCP-Response. Furthermore, if the DCCP-Response 91 | included an Init Cookie, that Init Cookie MUST be included on every 92 | packet sent in PARTOPEN. 93 | 94 | The single DCCP-Ack sent when entering the PARTOPEN state might, of 95 | course, be dropped by the network. The client SHOULD ensure that 96 | some packet gets through eventually. The preferred mechanism would 97 | be a roughly 200-millisecond timer, set every time a packet is 98 | transmitted in PARTOPEN. If this timer goes off and the client is 99 | still in PARTOPEN, the client generates another DCCP-Ack and backs 100 | off the timer. If the client remains in PARTOPEN for more than 4MSL 101 | (8 minutes), it SHOULD reset the connection with Reset Code 2, 102 | "Aborted". 103 | 104 | The client leaves the PARTOPEN state for OPEN when it receives a 105 | valid packet other than DCCP-Response, DCCP-Reset, or DCCP-Sync from 106 | the server. -------------------------------------------------------------------------------- /dccp/timeopt.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | // TimestampOption, Section 13.1 8 | // Time values are based on a circular uint32 value at 10 microseconds granularity 9 | type TimestampOption struct { 10 | // Timestamp is given in 10 microsecond circular units 11 | Timestamp uint32 12 | } 13 | 14 | func (opt *TimestampOption) Encode() (*Option, error) { 15 | return &Option{ 16 | Type: OptionTimestamp, 17 | Data: encodeTimestamp(opt.Timestamp, make([]byte, 4)), 18 | Mandatory: false, 19 | }, nil 20 | } 21 | 22 | // d must be a 4-byte slice 23 | func encodeTimestamp(t uint32, d []byte) []byte { 24 | EncodeUint32(t, d) 25 | return d[0:4] 26 | } 27 | 28 | func DecodeTimestampOption(opt *Option) *TimestampOption { 29 | if opt.Type != OptionTimestamp || len(opt.Data) != 4 { 30 | return nil 31 | } 32 | return &TimestampOption{Timestamp: decodeTimestamp(opt.Data[0:4])} 33 | } 34 | 35 | func decodeTimestamp(d []byte) uint32 { 36 | return DecodeUint32(d) 37 | } 38 | 39 | // ElapsedTimeOption, Section 13.2 40 | // This option is permitted in any DCCP packet that contains an Acknowledgement Number; such 41 | // options received on other packet types MUST be ignored. It indicates how much time has 42 | // elapsed since the packet being acknowledged -- the packet with the given Acknowledgement 43 | // Number -- was received. 44 | // 45 | // The option data, Elapsed Time, represents an estimated lower bound on the amount of time 46 | // elapsed since the packet being acknowledged was received, with units of hundredths of 47 | // milliseconds (10 microseconds granularity). 48 | type ElapsedTimeOption struct { 49 | Elapsed uint32 50 | } 51 | 52 | const ( 53 | TenMicroInNano = 1e4 // 10 microseconds in nanoseconds 54 | OneSecInTenMicro = 1e5 // 1 seconds in ten microsecond units 55 | MaxTenMicro = 4294967295 // Maximum allowed time in ten microsecond units 56 | MaxElapsedInTenMicro = MaxTenMicro // Maximum elapsed time in ten microsecond units 57 | ) 58 | 59 | func (opt *ElapsedTimeOption) Encode() (*Option, error) { 60 | return &Option{ 61 | Type: OptionElapsedTime, 62 | Data: encodeElapsed(opt.Elapsed, make([]byte, 4)), 63 | Mandatory: false, 64 | }, nil 65 | } 66 | 67 | // d must be a 4-byte slice 68 | func encodeElapsed(elapsed uint32, d []byte) []byte { 69 | if elapsed >= MaxElapsedInTenMicro { 70 | elapsed = MaxElapsedInTenMicro 71 | } 72 | if elapsed < OneSecInTenMicro / 2 { 73 | assertFitsIn16Bits(uint64(elapsed)) 74 | EncodeUint16(uint16(elapsed), d[0:2]) 75 | return d[0:2] 76 | } else { 77 | EncodeUint32(elapsed, d) 78 | return d[0:4] 79 | } 80 | panic("unreach") 81 | } 82 | 83 | func DecodeElapsedTimeOption(opt *Option) *ElapsedTimeOption { 84 | if opt.Type != OptionElapsedTime || (len(opt.Data) != 2 && len(opt.Data) != 4) { 85 | return nil 86 | } 87 | elapsed, err := decodeElapsed(opt.Data) 88 | if err != nil { 89 | return nil 90 | } 91 | return &ElapsedTimeOption{ 92 | Elapsed: elapsed, 93 | } 94 | } 95 | 96 | func decodeElapsed(d []byte) (uint32, error) { 97 | var t uint32 98 | switch len(d) { 99 | case 2: 100 | t = uint32(DecodeUint16(d)) 101 | case 4: 102 | t = DecodeUint32(d) 103 | default: 104 | return 0, ErrSize 105 | } 106 | return t, nil 107 | } 108 | 109 | // ————— 110 | // TimestampEchoOption, Section 13.3 111 | // Time values are based on a circular uint32 value at 10 microseconds granularity 112 | type TimestampEchoOption struct { 113 | // The timestamp echo option value in 10 microsecond circular units 114 | Timestamp uint32 115 | // The elapsed time in nanoseconds 116 | Elapsed uint32 117 | } 118 | 119 | func (opt *TimestampEchoOption) Encode() (*Option, error) { 120 | d := make([]byte, 8) 121 | encodeTimestamp(opt.Timestamp, d[0:4]) 122 | if opt.Elapsed == 0 { 123 | d = d[0:4] 124 | } else { 125 | l := len(encodeElapsed(opt.Elapsed, d[4:])) 126 | d = d[0 : 4+l] 127 | } 128 | // The size of d can be 4, 6 or 8 129 | return &Option{ 130 | Type: OptionTimestampEcho, 131 | Data: d, 132 | Mandatory: false, 133 | }, nil 134 | } 135 | 136 | func DecodeTimestampEchoOption(opt *Option) *TimestampEchoOption { 137 | if opt.Type != OptionTimestampEcho || 138 | (len(opt.Data) != 4 && len(opt.Data) != 6 && len(opt.Data) != 8) { 139 | 140 | return nil 141 | } 142 | var elapsed uint32 143 | if len(opt.Data) > 4 { 144 | var err error 145 | elapsed, err = decodeElapsed(opt.Data[4:]) 146 | if err != nil { 147 | return nil 148 | } 149 | } 150 | return &TimestampEchoOption{ 151 | Timestamp: decodeTimestamp(opt.Data[0:4]), 152 | Elapsed: elapsed, 153 | } 154 | } 155 | 156 | // TenMicroDiff returns the circular difference between t0 an t1. 157 | // The arguments and the result are in ten microsecond units. 158 | func TenMicroDiff(t0, t1 uint32) uint32 { return minu32(t0-t1, t1-t0) } 159 | 160 | // TenMicroFromNano converts a time length given in nanoseconds into 161 | // units of 10 microseconds, capped by MaxTenMicro. 162 | func TenMicroFromNano(ns int64) uint32 { 163 | if ns < 0 { 164 | panic("negative time") 165 | } 166 | return uint32(min64(ns/TenMicroInNano, MaxTenMicro)) 167 | } 168 | 169 | // NanoFromTenMicro converts a time length given in ten microsecond units into 170 | // nanoseconds, without exceeding the maximum allowed time limit of MaxTenMicro. 171 | func NanoFromTenMicro(tenmicro uint32) int64 { 172 | return min64(int64(tenmicro)*TenMicroInNano, MaxTenMicro*TenMicroInNano) 173 | } 174 | -------------------------------------------------------------------------------- /dccp/amb.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | import ( 8 | "reflect" 9 | "github.com/petar/GoGauge/filter" 10 | ) 11 | 12 | // Amb represents a runtime context, embodied by a runtime, a stack of labels 13 | // and set of options. It is also capable of emitting structured logs, that 14 | // are relative to the overall context, and are consequently used for debuging 15 | // and analysis purposes. It lives in the context of a shared time framework 16 | // and a shared filter framework, which may filter some logs out 17 | type Amb struct { 18 | env *Env 19 | 20 | // Debug flags are associated with Amb and not with Env because we 21 | // might want to pass different debug flags to a server and a client 22 | // connection in an experiment, while we would like them to share the 23 | // same runtime so that they share the same notion of time. 24 | flags *Flags 25 | 26 | labels []string 27 | } 28 | 29 | // A zero-value Amb has the special-case behavior of ignoring all emits 30 | var NoLogging *Amb = &Amb{} 31 | 32 | // NewAmb creates a new Amb object with a single entry in the label stack 33 | func NewAmb(label string, env *Env) *Amb { 34 | return &Amb{ 35 | env: env, 36 | flags: NewFlags(), 37 | labels: []string{label}, 38 | } 39 | } 40 | 41 | // Refine clones this amb and stack the additional label l 42 | func (t *Amb) Refine(l string) *Amb { 43 | return t.Copy().Push(l) 44 | } 45 | 46 | // Copy clones this amb into an identical new one 47 | func (t *Amb) Copy() *Amb { 48 | var c Amb = *t 49 | c.labels = make([]string, len(t.labels)) 50 | copy(c.labels, t.labels) 51 | return &c 52 | } 53 | 54 | // Flags returns the flags associated with this Amb instance 55 | func (t *Amb) Flags() *Flags { 56 | return t.flags 57 | } 58 | 59 | // Labels returns the label stack of this amb 60 | func (t *Amb) Labels() []string { 61 | return t.labels 62 | } 63 | 64 | // Push adds the label l onto this amb's label stack 65 | func (t *Amb) Push(l string) *Amb { 66 | t.labels = append(t.labels, l) 67 | return t 68 | } 69 | 70 | func (t *Amb) Filter() *filter.Filter { 71 | return t.env.Filter() 72 | } 73 | 74 | // GetState retrieves the state of the owning object, using the runtime value store 75 | func (t *Amb) GetState() string { 76 | if t.env == nil || len(t.labels) == 0 { 77 | return "" 78 | } 79 | g := t.env.Filter().GetAttr([]string{t.labels[0]}, "state") 80 | if g == nil { 81 | return "" 82 | } 83 | return g.(string) 84 | } 85 | 86 | // SetState saves the state s into the runtime value store 87 | func (t *Amb) SetState(s int) { 88 | if t.env == nil { 89 | return 90 | } 91 | t.env.Filter().SetAttr([]string{t.labels[0]}, "state", StateString(s)) 92 | } 93 | 94 | // E emits a new log record. The arguments args are scanned in turn. The first argument of 95 | // type *Header, *PreHeader, *FeedbackHeader or *FeedforwardHeader is considered the DCCP 96 | // header that this log pertains to. The first argument of type Args is saved in the log 97 | // record. 98 | func (t *Amb) E(event Event, comment string, args ...interface{}) { 99 | t.EC(1, event, comment, args...) 100 | } 101 | 102 | func (t *Amb) EC(skip int, event Event, comment string, args ...interface{}) { 103 | if t.env == nil { 104 | return 105 | } 106 | sinceZero, _ := t.env.Snap() 107 | 108 | // Extract header information 109 | var hType string = "" 110 | var hSeqNo, hAckNo int64 111 | logargs := make(map[string]interface{}) 112 | for _, a := range args { 113 | switch t := a.(type) { 114 | case *Header: 115 | if t != nil { 116 | hSeqNo, hAckNo = t.SeqNo, t.AckNo 117 | hType = typeString(t.Type) 118 | } 119 | case *writeHeader: 120 | if t != nil { 121 | hSeqNo, hAckNo = t.SeqNo, t.AckNo 122 | hType = typeString(t.Type) 123 | } 124 | case *PreHeader: 125 | if t != nil { 126 | hSeqNo, hAckNo = t.SeqNo, t.AckNo 127 | hType = typeString(t.Type) 128 | } 129 | case *FeedbackHeader: 130 | if t != nil { 131 | hSeqNo, hAckNo = t.SeqNo, t.AckNo 132 | hType = typeString(t.Type) 133 | } 134 | case *FeedforwardHeader: 135 | if t != nil { 136 | hSeqNo = t.SeqNo 137 | hType = typeString(t.Type) 138 | } 139 | // By default, take the argument's type and use it as a key in the arguments structure 140 | default: 141 | if a != nil { 142 | logargs[TypeOf(a)] = a 143 | } 144 | } 145 | } 146 | 147 | sfile, sline := FetchCaller(1+skip) 148 | 149 | if t.env.TraceWriter() != nil { 150 | r := &Trace{ 151 | Time: sinceZero, 152 | Labels: t.labels, 153 | Event: event, 154 | State: t.GetState(), 155 | Comment: comment, 156 | Args: logargs, 157 | Type: hType, 158 | SeqNo: hSeqNo, 159 | AckNo: hAckNo, 160 | SourceFile: sfile, 161 | SourceLine: sline, 162 | Trace: StackTrace(t.labels, skip+2, sfile, sline), 163 | } 164 | t.env.TraceWriter().Write(r) 165 | } 166 | } 167 | 168 | func TypeOf(a interface{}) string { 169 | t := reflect.TypeOf(a) 170 | // Remove the '*' from pointers 171 | if t.Kind() == reflect.Ptr { 172 | t = t.Elem() 173 | } 174 | return t.String() 175 | } 176 | 177 | const nsAlpha = "0123456789" 178 | 179 | func Nstoa(ns int64) string { 180 | if ns < 0 { 181 | panic("negative time") 182 | } 183 | if ns == 0 { 184 | return "0" 185 | } 186 | b := make([]byte, 32) 187 | z := len(b) - 1 188 | i := 0 189 | j := 0 190 | for ns != 0 { 191 | if j % 3 == 0 && j > 0 { 192 | b[z-i] = ',' 193 | i++ 194 | } 195 | b[z-i] = nsAlpha[ns % 10] 196 | j++ 197 | i++ 198 | ns /= 10 199 | } 200 | return string(b[z-i+1:]) 201 | } 202 | -------------------------------------------------------------------------------- /dccp/read.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | //"fmt" 8 | 9 | // verifyIPAndProto() checks that both sourceIP# and destIP# are valid for protoNo# 10 | func verifyIPAndProto(sourceIP, destIP []byte, protoNo byte) error { 11 | if sourceIP == nil || destIP == nil { 12 | return ErrIPFormat 13 | } 14 | if !((len(sourceIP) == 4 && len(destIP) == 4) || (len(sourceIP) == 16 && len(destIP) == 16)) { 15 | return ErrIPFormat 16 | } 17 | return nil 18 | } 19 | 20 | func ReadHeader(buf []byte, 21 | sourceIP, destIP []byte, 22 | protoNo byte, 23 | allowShortSeqNoFeature bool) (header *Header, err error) { 24 | 25 | err = verifyIPAndProto(sourceIP, destIP, protoNo) 26 | if err != nil { 27 | return nil, err 28 | } 29 | 30 | if len(buf) < 12 { 31 | return nil, ErrSize 32 | } 33 | gh := &Header{} 34 | k := 0 35 | 36 | // Read (1a) Generic Header 37 | 38 | gh.SourcePort = DecodeUint16(buf[k : k+2]) 39 | k += 2 40 | 41 | gh.DestPort = DecodeUint16(buf[k : k+2]) 42 | k += 2 43 | 44 | // Compute the Data Offset in bytes 45 | dataOffset := int(DecodeUint8(buf[k:k+1])) << 2 46 | k += 1 47 | 48 | // Read CCVal 49 | gh.CCVal = int8(buf[k] >> 4) 50 | 51 | // Read CsCov 52 | gh.CsCov = buf[k] & 0x0f 53 | k += 1 54 | 55 | k += 2 // Skip over the checksum field. It is implicitly used in checksum verification later 56 | 57 | // Read Res 58 | // gh.Res = buf[k] >> 5 // The 3-bit Res field should be ignored 59 | 60 | // Read Type 61 | gh.Type = (buf[k] >> 1) & 0x0f 62 | if !isTypeUnderstood(gh.Type) { 63 | return nil, ErrUnknownType 64 | } 65 | 66 | // Read X 67 | gh.X = (buf[k] & 0x01) == 1 68 | k += 1 69 | 70 | // Check that X and Type are compatible 71 | if !areTypeAndXCompatible(gh.Type, gh.X, allowShortSeqNoFeature) { 72 | return nil, ErrSemantic 73 | } 74 | 75 | // Check Data Offset bounds 76 | if dataOffset < getFixedHeaderSize(gh.Type, gh.X) || dataOffset > len(buf) { 77 | return nil, ErrNumeric 78 | } 79 | 80 | // Verify checksum 81 | appCov, err := getChecksumAppCoverage(gh.CsCov, len(buf)-dataOffset) 82 | if err != nil { 83 | return nil, err 84 | } 85 | csum := csumSum(buf[0:dataOffset]) 86 | csum = csumAdd(csum, csumPseudoIP(sourceIP, destIP, protoNo, len(buf))) 87 | csum = csumAdd(csum, csumSum(buf[dataOffset:dataOffset+appCov])) 88 | csum = csumDone(csum) 89 | if csum != 0 { 90 | return nil, ErrChecksum 91 | } 92 | 93 | // Read SeqNo 94 | switch gh.X { 95 | case false: 96 | gh.SeqNo = int64(DecodeUint24(buf[k : k+3])) 97 | k += 3 98 | case true: 99 | padding := DecodeUint8(buf[k : k+1]) 100 | k += 1 101 | if padding != 0 { 102 | return nil, ErrNumeric 103 | } 104 | gh.SeqNo = int64(DecodeUint48(buf[k : k+6])) 105 | k += 6 106 | } 107 | 108 | // Read (1b) Acknowledgement Number Subheader 109 | 110 | switch getAckNoSubheaderSize(gh.Type, gh.X) { 111 | case 0: 112 | case 4: 113 | padding := DecodeUint8(buf[k : k+1]) 114 | k += 1 115 | if padding != 0 { 116 | return nil, ErrNumeric 117 | } 118 | gh.AckNo = int64(DecodeUint24(buf[k : k+3])) 119 | k += 3 120 | case 8: 121 | padding := DecodeUint16(buf[k : k+2]) 122 | k += 2 123 | if padding != 0 { 124 | return nil, ErrNumeric 125 | } 126 | gh.AckNo = int64(DecodeUint48(buf[k : k+6])) 127 | k += 6 128 | default: 129 | panic("unreach") 130 | } 131 | 132 | // Read (1c) Code Subheader: Service Code, or Reset Code and Reset Data fields 133 | switch gh.Type { 134 | case Request, Response: 135 | gh.ServiceCode = DecodeUint32(buf[k : k+4]) 136 | k += 4 137 | case Reset: 138 | gh.ResetCode = buf[k] 139 | gh.ResetData = buf[k+1 : k+4] 140 | k += 4 141 | } 142 | 143 | // Read (2) Options and Padding 144 | opts, err := readOptions(buf[k:dataOffset]) 145 | if err != nil { 146 | return nil, err 147 | } 148 | opts, err = sanitizeOptionsAfterReading(gh.Type, opts) 149 | if err != nil { 150 | return nil, err 151 | } 152 | gh.Options = opts 153 | 154 | // Read (3) Application Data 155 | gh.Data = buf[dataOffset:] 156 | 157 | return gh, nil 158 | } 159 | 160 | func readOptions(buf []byte) ([]*Option, error) { 161 | if len(buf)&0x3 != 0 { 162 | return nil, ErrAlign 163 | } 164 | 165 | opts := make([]*Option, len(buf)) 166 | j, k := 0, 0 167 | for k < len(buf) { 168 | o := &Option{} 169 | 170 | // Read option type 171 | t := buf[k] 172 | k += 1 173 | 174 | if isOptionSingleByte(t) { 175 | o.Type = t 176 | o.Data = make([]byte, 0) 177 | 178 | opts[j] = o 179 | j += 1 180 | continue 181 | } 182 | 183 | // Read option length 184 | if k+1 > len(buf) { 185 | break 186 | } 187 | l := int(buf[k]) 188 | k += 1 189 | if l < 2 || k+l-2 > len(buf) { 190 | break 191 | } 192 | 193 | o.Type = t 194 | o.Data = buf[k : k+l-2] 195 | k += l - 2 196 | 197 | opts[j] = o 198 | j += 1 199 | 200 | } 201 | 202 | return opts[0:j], nil 203 | } 204 | 205 | func sanitizeOptionsAfterReading(Type byte, opts []*Option) ([]*Option, error) { 206 | r := make([]*Option, len(opts)) 207 | j := 0 208 | 209 | nextIsMandatory := false 210 | for i := 0; i < len(opts); i++ { 211 | if !isOptionValidForType(opts[i].Type, Type) { 212 | if nextIsMandatory { 213 | return nil, ErrOption 214 | } 215 | nextIsMandatory = false 216 | continue 217 | } 218 | switch opts[i].Type { 219 | case OptionMandatory: 220 | if nextIsMandatory { 221 | return nil, ErrOption 222 | } 223 | nextIsMandatory = true 224 | case OptionPadding: 225 | nextIsMandatory = false 226 | continue 227 | default: 228 | r[j] = opts[i] 229 | if nextIsMandatory { 230 | r[j].Mandatory = true 231 | nextIsMandatory = false 232 | } 233 | j++ 234 | continue 235 | } 236 | } 237 | if nextIsMandatory { 238 | return nil, ErrOption 239 | } 240 | 241 | return r[0:j], nil 242 | } 243 | -------------------------------------------------------------------------------- /dccp/header.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package dccp 6 | 7 | // We use a subset of DCCP, defined as follows: 8 | // X=1 9 | // allowShortSeqNoFeature=0 10 | 11 | // Any DCCP header has a subset of the following subheaders, in this order: 12 | // (1a) Generic header 13 | // (1b) Acknowledgement Number Subheader 14 | // (1c) Code Subheader: Service Code, or Reset Code and Reset Data fields 15 | // (2) Options and Padding 16 | // (3) Application Data 17 | 18 | type Header struct { 19 | SourcePort uint16 // Sender port 20 | DestPort uint16 // Receiver port 21 | CCVal int8 // Used by the HC-Sender's CCID to transmit 4-bit values 22 | CsCov byte // Specifies the parts of packet covered by the checksum 23 | Type byte // Packet type: Data, Ack, Sync, etc. 24 | X bool // Extended seq numbers: generally always true (for us) 25 | SeqNo int64 // 48-bit if X=1 26 | AckNo int64 // 48-bit if X=1 27 | ServiceCode uint32 // ServiceCode: Applicaton level service (in Req,Resp pkts) 28 | ResetCode byte // ResetCode: Reason for reset (in Reset pkts) 29 | ResetData []byte // ResetData: Additional reset info (in Reset pkts) 30 | Options []*Option // Used for feature negotiation, padding, mandatory flags 31 | Data []byte // Application data (in Req, Resp, Data, DataAck pkts) 32 | // Ignored (in Ack, Close, CloseReq, Sync, SyncAck pkts) 33 | // Error text (in Reset pkts) 34 | } 35 | 36 | const ( 37 | SEQNOMAX = (2 << 48) - 1 38 | ) 39 | 40 | // Packet types. Stored in the Type field of the generic header. 41 | // Receivers MUST ignore any packets with reserved type. That is, 42 | // packets with reserved type MUST NOT be processed, and they MUST 43 | // NOT be acknowledged as received. 44 | const ( 45 | Request = 0 46 | Response = 1 47 | Data = 2 48 | Ack = 3 49 | DataAck = 4 50 | CloseReq = 5 51 | Close = 6 52 | Reset = 7 53 | Sync = 8 54 | SyncAck = 9 55 | ) 56 | 57 | // Constants for protoNo 58 | const ( 59 | AnyProto = iota 60 | ) 61 | 62 | func isTypeUnderstood(Type byte) bool { 63 | return Type >= Request && Type <= SyncAck 64 | } 65 | 66 | func isTypeReserved(Type byte) bool { 67 | return Type >= 10 && Type <= 15 68 | } 69 | 70 | func isDataPacket(Type byte) bool { 71 | switch Type { 72 | case Request, Response, Data, DataAck: 73 | return true 74 | case Ack, Close, CloseReq, Reset, Sync, SyncAck: 75 | return false 76 | } 77 | panic("unreach") 78 | } 79 | 80 | // Reset codes 81 | const ( 82 | ResetUnspecified = 0 83 | ResetClosed = 1 84 | ResetAborted = 2 85 | ResetNoConnection = 3 86 | ResetPacketError = 4 87 | ResetOptionError = 5 88 | ResetMandatoryError = 6 89 | ResetConnectionRefused = 7 90 | ResetBadServiceCode = 8 91 | ResetTooBusy = 9 92 | ResetBadInitCookie = 10 93 | ResetAgressionPenalty = 11 94 | // Reserved 12 to 127 95 | // CCID-specific 128 to 255 96 | ) 97 | 98 | func isResetCodeReserved(code int) bool { 99 | return code >= 12 && code <= 127 100 | } 101 | 102 | func isResetCodeCCIDSpecific(code int) bool { 103 | return code >= 128 && code <= 255 104 | } 105 | 106 | func areTypeAndXCompatible(Type byte, X bool, AllowShortSeqNoFeature bool) bool { 107 | switch Type { 108 | case Request, Response: 109 | return X 110 | case Data, Ack, DataAck: 111 | if AllowShortSeqNoFeature { 112 | return true 113 | } 114 | return X 115 | case CloseReq, Close: 116 | return X 117 | case Reset: 118 | return X 119 | case Sync, SyncAck: 120 | return X 121 | } 122 | panic("unreach") 123 | } 124 | 125 | // See RFC 4340, Page 21 126 | func getAckNoSubheaderSize(Type byte, X bool) int { 127 | if Type == Request || Type == Data { 128 | return 0 129 | } 130 | if X { 131 | return 8 132 | } 133 | return 4 134 | } 135 | 136 | func getCodeSubheaderSize(Type byte) int { 137 | switch Type { 138 | case Request, Response: 139 | return 4 140 | case Data, Ack, DataAck: 141 | return 0 142 | case CloseReq, Close: 143 | return 0 144 | case Reset: 145 | return 4 146 | case Sync, SyncAck: 147 | return 0 148 | } 149 | panic("unreach") 150 | } 151 | 152 | // getFixedHeaderSize() returns the size of the fixed portion of 153 | // the generic header in bytes, based on its @Type and @X. This 154 | // includes (1a), (1b) and (1c). 155 | func getFixedHeaderSize(Type byte, X bool) int { 156 | var r int 157 | switch X { 158 | case false: 159 | r = 12 160 | case true: 161 | r = 16 162 | } 163 | r += getAckNoSubheaderSize(Type, X) 164 | r += getCodeSubheaderSize(Type) 165 | return r 166 | } 167 | 168 | func mayHaveAppData(Type byte) bool { 169 | switch Type { 170 | case Request, Response: 171 | return true 172 | case Data: 173 | return true 174 | case Ack: 175 | return true // may have App Data (essentially for padding) but must be ignored 176 | case DataAck: 177 | return true 178 | case CloseReq, Close: 179 | return true // may have App Data (essentially for padding) but must be ignored 180 | case Reset: 181 | return true // used for UTF-8 encoded error text 182 | case Sync, SyncAck: 183 | return true // may have App Data (essentially for padding) but must be ignored 184 | } 185 | panic("unreach") 186 | } 187 | 188 | // Checksum 189 | 190 | const ( 191 | CsCovAllData = iota 192 | CsCovNoData 193 | CsCov4 194 | CsCov8 195 | CsCov12 196 | // etc. 197 | ) 198 | 199 | // getChecksumAppCoverage() computes how many bytes of the 200 | // app data is covered by the checksum, not counting neccessary padding 201 | func getChecksumAppCoverage(CsCov byte, dataLen int) (int, error) { 202 | if CsCov > 15 { 203 | return 0, ErrCsCov 204 | } 205 | if CsCov == 0 { 206 | return dataLen, nil 207 | } 208 | cov := int(CsCov-1) << 2 209 | if cov > dataLen { 210 | return 0, ErrCsCov 211 | } 212 | return cov, nil 213 | } 214 | -------------------------------------------------------------------------------- /dccp/sandbox/roundtrip_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 GoDCCP Authors. All rights reserved. 2 | // Use of this source code is governed by a 3 | // license that can be found in the LICENSE file. 4 | 5 | package sandbox 6 | 7 | import ( 8 | "fmt" 9 | "math" 10 | "testing" 11 | "os" 12 | "github.com/petar/GoDCCP/dccp" 13 | "github.com/petar/GoDCCP/dccp/ccid3" 14 | ) 15 | 16 | const ( 17 | roundtripDuration = 10e9 // Duration of the experiment = 10 sec 18 | roundtripInterval = 100e6 // How often we perform heartbeat writes to avoid idle periods = 100 ms 19 | roundtripRate = 1e9 / roundtripInterval // Fixed send rate for both endpoints in packets per second = 10 pps 20 | roundtripLatency = 50e6 // Latency of 50ms 21 | roundtripComputationalLatency = 1e6 // With no latency injection, computational effects in the test inject about 1 ms latency 22 | ) 23 | 24 | // TestRoundtripEstimation checks that round-trip times are estimated accurately. 25 | func TestRoundtripEstimation(t *testing.T) { 26 | dccp.InstallCtrlCPanic() 27 | 28 | env, plex := NewEnv("rtt") 29 | reducer := NewMeasure(env, t) 30 | plex.Add(reducer) 31 | plex.Add(newRoundtripCheckpoint(env, t)) 32 | plex.HighlightSamples(ccid3.RoundtripElapsedSample, ccid3.RoundtripReportSample) 33 | 34 | clientConn, serverConn, clientToServer, _ := NewClientServerPipe(env) 35 | 36 | // Roundtrip estimates might be imprecise during long idle periods, 37 | // as a product of the CCID3 design, since during such period precise 38 | // estimates are not necessary. Therefore, to focus on roundtrip time 39 | // estimation without saturating the link, we generate sufficiently 40 | // regular transmissions. 41 | 42 | payload := []byte{1, 2, 3} 43 | buf := make([]byte, len(payload)) 44 | 45 | // In order to isolate roundtrip measurement testing from the complexities 46 | // of the send rate calculation mechanism, we fix the send rate of both 47 | // endpoints using the debug flag FixRate. 48 | clientConn.Amb().Flags().SetUint32("FixRate", roundtripRate) 49 | serverConn.Amb().Flags().SetUint32("FixRate", roundtripRate) 50 | 51 | // Increase the client—>server latency from 0 to latency at half time 52 | env.Go(func() { 53 | env.Sleep(roundtripDuration / 2) 54 | clientToServer.SetWriteLatency(roundtripLatency) 55 | }, "test controller") 56 | 57 | cchan := make(chan int, 1) 58 | env.Go(func() { 59 | t0 := env.Now() 60 | for env.Now() - t0 < roundtripDuration { 61 | err := clientConn.Write(buf) 62 | if err != nil { 63 | break 64 | } 65 | } 66 | // Close is necessary because otherwise, if no read timeout is in place, the 67 | // server sides hangs forever on Read 68 | clientConn.Close() 69 | close(cchan) 70 | }, "test client") 71 | 72 | schan := make(chan int, 1) 73 | env.Go(func() { 74 | for { 75 | _, err := serverConn.Read() 76 | if err != nil { 77 | break 78 | } 79 | } 80 | close(schan) 81 | }, "test server") 82 | 83 | _, _ = <-cchan 84 | _, _ = <-schan 85 | 86 | // Shutdown the connections properly 87 | clientConn.Abort() 88 | serverConn.Abort() 89 | env.NewGoJoin("end-of-test", clientConn.Joiner(), serverConn.Joiner()).Join() 90 | dccp.NewAmb("line", env).E(dccp.EventMatch, "Server and client done.") 91 | if err := env.Close(); err != nil { 92 | t.Errorf("error closing runtime (%s)", err) 93 | } 94 | } 95 | 96 | // roundtripCheckpoint verifies that roundtrip estimates are within expected at 97 | // different point in time in the Roundtrip test. 98 | type roundtripCheckpoint struct { 99 | env *dccp.Env 100 | t *testing.T 101 | 102 | checkTimes []int64 // Times when test conditions are checked 103 | expected []float64 // Approximate expected value of variables at respective times 104 | tolerance []float64 // Multiplicative error tolerance with respect to expected 105 | 106 | clientElapsed []float64 // Readings for test variables from client of roundtrip estimate after "elapsed" event 107 | clientReport []float64 108 | serverElapsed []float64 109 | serverReport []float64 110 | 111 | } 112 | 113 | func newRoundtripCheckpoint(env *dccp.Env, t *testing.T) *roundtripCheckpoint { 114 | return &roundtripCheckpoint{ 115 | env: env, 116 | t: t, 117 | checkTimes: []int64{roundtripDuration / 2, roundtripDuration}, 118 | expected: []float64{NanoToMilli(roundtripComputationalLatency), NanoToMilli(roundtripLatency)}, 119 | tolerance: []float64{1.0, 0.15}, 120 | clientElapsed: make([]float64, 2), 121 | clientReport: make([]float64, 2), 122 | serverElapsed: make([]float64, 2), 123 | serverReport: make([]float64, 2), 124 | } 125 | } 126 | 127 | func (x *roundtripCheckpoint) Write(r *dccp.Trace) { 128 | reading, ok := r.Sample() 129 | if !ok { 130 | return 131 | } 132 | 133 | var slot []float64 134 | switch { 135 | case r.ArgOfType(ccid3.RoundtripElapsedCheckpoint) != nil: 136 | endpoint := r.Labels[0] 137 | switch endpoint { 138 | case "client": 139 | slot = x.clientElapsed 140 | case "server": 141 | slot = x.serverElapsed 142 | } 143 | case r.ArgOfType(ccid3.RoundtripReportCheckpoint) != nil: 144 | endpoint := r.Labels[0] 145 | switch endpoint { 146 | case "client": 147 | slot = x.clientReport 148 | case "server": 149 | slot = x.serverReport 150 | } 151 | } 152 | if slot == nil { 153 | return 154 | } 155 | for i, checkTime := range x.checkTimes { 156 | if r.Time < checkTime { 157 | slot[i] = reading.Value 158 | } 159 | } 160 | } 161 | 162 | func (x *roundtripCheckpoint) Sync() error { 163 | return nil 164 | } 165 | 166 | func (x *roundtripCheckpoint) Close() error { 167 | checkDeviation(x.t, "client-elapsed", x.clientElapsed, x.expected, x.tolerance) 168 | checkDeviation(x.t, "client-report", x.clientReport, x.expected, x.tolerance) 169 | checkDeviation(x.t, "server-elapsed", x.serverElapsed, x.expected, x.tolerance) 170 | checkDeviation(x.t, "server-report", x.serverReport, x.expected, x.tolerance) 171 | return nil 172 | } 173 | 174 | func checkDeviation(t *testing.T, name string, actual []float64, expected []float64, tolerance []float64) { 175 | for i, _ := range actual { 176 | dev := math.Abs(actual[i]-expected[i]) / expected[i] 177 | if dev > tolerance[i] { 178 | fmt.Fprintf(os.Stderr, "%s=%v, expected=%v, tolerance=%v\n", name, actual, expected, tolerance) 179 | t.Errorf("%s deviates by %0.2f%% in %d-th term", name, dev * 100, i) 180 | } 181 | } 182 | } 183 | --------------------------------------------------------------------------------