-
Notifications
You must be signed in to change notification settings - Fork 12
/
controller.go
176 lines (149 loc) · 4.4 KB
/
controller.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
package iperf
import (
"context"
"fmt"
"log"
"net"
"sync"
"time"
api "github.com/BGrewell/go-iperf/api/go"
"google.golang.org/grpc"
)
func NewController(port int) (controller *Controller, err error) {
c := &Controller{
Port: port,
clients: make(map[string]*Client),
servers: make(map[string]*Server),
clientLock: sync.Mutex{},
serverLock: sync.Mutex{},
}
err = c.startListener()
return c, err
}
// Controller is a helper in the go-iperf package that is designed to run on both the client and the server side. On the
// server side it listens for new gRPC connections, when a connection is made by a client the client can tell it to
// start a new iperf server instance. It will start a instance on an unused port and return the port number to the
// client. This allows the entire iperf setup and session to be performed from the client side.
type Controller struct {
api.UnimplementedCommandServer
Port int
cmdClient api.CommandClient
clientLock sync.Mutex
serverLock sync.Mutex
clients map[string]*Client
servers map[string]*Server
}
// StartServer is the handler for the gRPC function StartServer()
func (c *Controller) GrpcRequestServer(context.Context, *api.StartServerRequest) (*api.StartServerResponse, error) {
srv, err := c.NewServer()
srv.SetOneOff(true)
if err != nil {
return nil, err
}
err = srv.Start()
if err != nil {
return nil, err
}
c.serverLock.Lock()
c.servers[srv.Id] = srv
c.serverLock.Unlock()
reply := &api.StartServerResponse{
Id: srv.Id,
ListenPort: int32(srv.Port()),
}
return reply, nil
}
// StartListener starts a command listener which is used to accept gRPC connections from another go-iperf controller
func (c *Controller) startListener() (err error) {
listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", "0.0.0.0", c.Port))
if err != nil {
return err
}
gs := grpc.NewServer()
api.RegisterCommandServer(gs, c)
go func() {
err := gs.Serve(listener)
if err != nil {
log.Fatal(err)
}
}()
time.Sleep(250 * time.Millisecond)
return nil
}
// NewServer gets a new instance of an iperf server on a free port
func (c *Controller) NewServer() (server *Server, err error) {
freePort, err := GetUnusedTcpPort()
s := NewServer()
s.SetPort(freePort)
c.serverLock.Lock()
c.servers[s.Id] = s
c.serverLock.Unlock()
return s, nil
}
// StopServer shuts down an iperf server and frees any actively used resources
func (c *Controller) StopServer(id string) (err error) {
c.serverLock.Lock()
delete(c.servers, id)
c.serverLock.Unlock()
return nil
}
// NewClient gets a new instance of an iperf client and also starts up a matched iperf server instance on the specified
// serverAddr. If it fails to connect to the gRPC interface of the controller on the remote side it will return an error
func (c *Controller) NewClient(serverAddr string) (client *Client, err error) {
grpc, err := GetConnectedClient(serverAddr, c.Port)
if err != nil {
return nil, err
}
ctx, cancel := context.WithTimeout(context.Background(), 2 * time.Second)
defer cancel()
reply, err := grpc.GrpcRequestServer(ctx, &api.StartServerRequest{})
srvPort := int(reply.ListenPort)
fmt.Printf("[!] server is listening on port %d\n", srvPort)
cli := NewClient(serverAddr)
cli.SetPort(srvPort)
c.clientLock.Lock()
c.clients[cli.Id] = cli
c.clientLock.Unlock()
return cli, nil
}
// StopClient will clean up the server side connection and shut down any actively used resources
func (c *Controller) StopClient(id string) (err error) {
c.clientLock.Lock()
delete(c.clients, id)
c.clientLock.Unlock()
return nil
}
func GetConnectedClient(addr string, port int) (client api.CommandClient, err error) {
conn, err := grpc.Dial(fmt.Sprintf("%s:%d", addr, port), grpc.WithInsecure(), grpc.WithBlock(), grpc.WithTimeout(time.Second*2))
if err != nil {
return nil, err
}
client = api.NewCommandClient(conn)
return client, nil
}
func GetUnusedTcpPort() (int, error) {
addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
if err != nil {
return 0, err
}
l, err := net.ListenTCP("tcp", addr)
if err != nil {
return 0, err
}
defer l.Close()
return l.Addr().(*net.TCPAddr).Port, nil
}
//func GetUnusedUdpPort() (int, error) {
// addr, err := net.ResolveUDPAddr("udp", "localhost:0")
// if err != nil {
// return 0, err
// }
//
// l, err := net.ListenUDP("udp", addr)
// if err != nil {
// return 0, err
// }
//
// defer l.Close()
// return l.LocalAddr().(*net.UDPAddr).Port, nil
//}