blob: 539819160ea3b90c3b2c1313d0478f01cbbb600b [file] [log] [blame]
Mikio Hara4d54f272015-02-26 17:52:26 +09001// Copyright 2015 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Package socktest provides utilities for socket testing.
6package socktest
7
8import "sync"
9
10func switchInit(sw *Switch) {
11 sw.fltab = make(map[FilterType]Filter)
12 sw.sotab = make(Sockets)
13 sw.stats = make(stats)
14}
15
16// A Switch represents a callpath point switch for socket system
17// calls.
18type Switch struct {
19 once sync.Once
20
21 fmu sync.RWMutex
22 fltab map[FilterType]Filter
23
24 smu sync.RWMutex
25 sotab Sockets
26 stats stats
27}
28
29// Stats returns a list of per-cookie socket statistics.
30func (sw *Switch) Stats() []Stat {
31 var st []Stat
32 sw.smu.RLock()
33 for _, s := range sw.stats {
34 ns := *s
35 st = append(st, ns)
36 }
37 sw.smu.RUnlock()
38 return st
39}
40
41// Sockets returns mappings of socket descriptor to socket status.
42func (sw *Switch) Sockets() Sockets {
43 sw.smu.RLock()
44 tab := make(Sockets, len(sw.sotab))
45 for i, s := range sw.sotab {
46 tab[i] = s
47 }
48 sw.smu.RUnlock()
49 return tab
50}
51
52// A Cookie represents a 3-tuple of a socket; address family, socket
53// type and protocol number.
54type Cookie uint64
55
56// Family returns an address family.
57func (c Cookie) Family() int { return int(c >> 48) }
58
59// Type returns a socket type.
60func (c Cookie) Type() int { return int(c << 16 >> 32) }
61
62// Protocol returns a protocol number.
63func (c Cookie) Protocol() int { return int(c & 0xff) }
64
65func cookie(family, sotype, proto int) Cookie {
66 return Cookie(family)<<48 | Cookie(sotype)&0xffffffff<<16 | Cookie(proto)&0xff
67}
68
69// A Status represents the status of a socket.
70type Status struct {
71 Cookie Cookie
72 Err error // error status of socket system call
73 SocketErr int // error status of socket by SO_ERROR
74}
75
76// A Stat represents a per-cookie socket statistics.
77type Stat struct {
78 Family int // address family
79 Type int // socket type
80 Protocol int // protocol number
81
82 Opened uint64 // number of sockets opened
83 Accepted uint64 // number of sockets accepted
84 Connected uint64 // number of sockets connected
85 Closed uint64 // number of sockets closed
86}
87
88type stats map[Cookie]*Stat
89
90func (st stats) getLocked(c Cookie) *Stat {
91 s, ok := st[c]
92 if !ok {
93 s = &Stat{Family: c.Family(), Type: c.Type(), Protocol: c.Protocol()}
94 st[c] = s
95 }
96 return s
97}
98
99// A FilterType represents a filter type.
100type FilterType int
101
102const (
103 FilterSocket FilterType = iota // for Socket
104 FilterAccept // for Accept or Accept4
105 FilterConnect // for Connect or ConnectEx
106 FilterGetsockoptInt // for GetsockoptInt
107 FilterClose // for Close or Closesocket
108)
109
110// A Filter represents a socket system call filter.
111//
112// It will only be executed before a system call for a socket that has
113// an entry in internal table.
114// If the filter returns a non-nil error, the execution of system call
115// will be canceled and the system call function returns the non-nil
116// error.
117// It can return a non-nil AfterFilter for filtering after the
118// execution of the system call.
119type Filter func(*Status) (AfterFilter, error)
120
121func (f Filter) apply(st *Status) (AfterFilter, error) {
122 if f == nil {
123 return nil, nil
124 }
125 return f(st)
126}
127
128// An AfterFilter represents a socket system call filter after an
129// execution of a system call.
130//
131// It will only be executed after a system call for a socket that has
132// an entry in internal table.
133// If the filter returns a non-nil error, the system call function
134// returns the non-nil error.
135type AfterFilter func(*Status) error
136
137func (f AfterFilter) apply(st *Status) error {
138 if f == nil {
139 return nil
140 }
141 return f(st)
142}
143
144// Set deploys the socket system call filter f for the filter type t.
145func (sw *Switch) Set(t FilterType, f Filter) {
146 sw.once.Do(func() { switchInit(sw) })
147 sw.fmu.Lock()
148 sw.fltab[t] = f
149 sw.fmu.Unlock()
150}