diff options
Diffstat (limited to 'src/net/internal/socktest/switch.go')
-rw-r--r-- | src/net/internal/socktest/switch.go | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/src/net/internal/socktest/switch.go b/src/net/internal/socktest/switch.go new file mode 100644 index 0000000..3c37b6f --- /dev/null +++ b/src/net/internal/socktest/switch.go @@ -0,0 +1,169 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package socktest provides utilities for socket testing. +package socktest + +import ( + "fmt" + "sync" +) + +// A Switch represents a callpath point switch for socket system +// calls. +type Switch struct { + once sync.Once + + fmu sync.RWMutex + fltab map[FilterType]Filter + + smu sync.RWMutex + sotab Sockets + stats stats +} + +func (sw *Switch) init() { + sw.fltab = make(map[FilterType]Filter) + sw.sotab = make(Sockets) + sw.stats = make(stats) +} + +// Stats returns a list of per-cookie socket statistics. +func (sw *Switch) Stats() []Stat { + var st []Stat + sw.smu.RLock() + for _, s := range sw.stats { + ns := *s + st = append(st, ns) + } + sw.smu.RUnlock() + return st +} + +// Sockets returns mappings of socket descriptor to socket status. +func (sw *Switch) Sockets() Sockets { + sw.smu.RLock() + tab := make(Sockets, len(sw.sotab)) + for i, s := range sw.sotab { + tab[i] = s + } + sw.smu.RUnlock() + return tab +} + +// A Cookie represents a 3-tuple of a socket; address family, socket +// type and protocol number. +type Cookie uint64 + +// Family returns an address family. +func (c Cookie) Family() int { return int(c >> 48) } + +// Type returns a socket type. +func (c Cookie) Type() int { return int(c << 16 >> 32) } + +// Protocol returns a protocol number. +func (c Cookie) Protocol() int { return int(c & 0xff) } + +func cookie(family, sotype, proto int) Cookie { + return Cookie(family)<<48 | Cookie(sotype)&0xffffffff<<16 | Cookie(proto)&0xff +} + +// A Status represents the status of a socket. +type Status struct { + Cookie Cookie + Err error // error status of socket system call + SocketErr error // error status of socket by SO_ERROR +} + +func (so Status) String() string { + return fmt.Sprintf("(%s, %s, %s): syscallerr=%v socketerr=%v", familyString(so.Cookie.Family()), typeString(so.Cookie.Type()), protocolString(so.Cookie.Protocol()), so.Err, so.SocketErr) +} + +// A Stat represents a per-cookie socket statistics. +type Stat struct { + Family int // address family + Type int // socket type + Protocol int // protocol number + + Opened uint64 // number of sockets opened + Connected uint64 // number of sockets connected + Listened uint64 // number of sockets listened + Accepted uint64 // number of sockets accepted + Closed uint64 // number of sockets closed + + OpenFailed uint64 // number of sockets open failed + ConnectFailed uint64 // number of sockets connect failed + ListenFailed uint64 // number of sockets listen failed + AcceptFailed uint64 // number of sockets accept failed + CloseFailed uint64 // number of sockets close failed +} + +func (st Stat) String() string { + return fmt.Sprintf("(%s, %s, %s): opened=%d connected=%d listened=%d accepted=%d closed=%d openfailed=%d connectfailed=%d listenfailed=%d acceptfailed=%d closefailed=%d", familyString(st.Family), typeString(st.Type), protocolString(st.Protocol), st.Opened, st.Connected, st.Listened, st.Accepted, st.Closed, st.OpenFailed, st.ConnectFailed, st.ListenFailed, st.AcceptFailed, st.CloseFailed) +} + +type stats map[Cookie]*Stat + +func (st stats) getLocked(c Cookie) *Stat { + s, ok := st[c] + if !ok { + s = &Stat{Family: c.Family(), Type: c.Type(), Protocol: c.Protocol()} + st[c] = s + } + return s +} + +// A FilterType represents a filter type. +type FilterType int + +const ( + FilterSocket FilterType = iota // for Socket + FilterConnect // for Connect or ConnectEx + FilterListen // for Listen + FilterAccept // for Accept, Accept4 or AcceptEx + FilterGetsockoptInt // for GetsockoptInt + FilterClose // for Close or Closesocket +) + +// A Filter represents a socket system call filter. +// +// It will only be executed before a system call for a socket that has +// an entry in internal table. +// If the filter returns a non-nil error, the execution of system call +// will be canceled and the system call function returns the non-nil +// error. +// It can return a non-nil AfterFilter for filtering after the +// execution of the system call. +type Filter func(*Status) (AfterFilter, error) + +func (f Filter) apply(st *Status) (AfterFilter, error) { + if f == nil { + return nil, nil + } + return f(st) +} + +// An AfterFilter represents a socket system call filter after an +// execution of a system call. +// +// It will only be executed after a system call for a socket that has +// an entry in internal table. +// If the filter returns a non-nil error, the system call function +// returns the non-nil error. +type AfterFilter func(*Status) error + +func (f AfterFilter) apply(st *Status) error { + if f == nil { + return nil + } + return f(st) +} + +// Set deploys the socket system call filter f for the filter type t. +func (sw *Switch) Set(t FilterType, f Filter) { + sw.once.Do(sw.init) + sw.fmu.Lock() + sw.fltab[t] = f + sw.fmu.Unlock() +} |