hs-test: fixed timed out tests passing in the CI
[vpp.git] / extras / hs-test / netconfig.go
1 package main
2
3 import (
4         "errors"
5         "fmt"
6         "os/exec"
7         "strings"
8
9         "go.fd.io/govpp/binapi/ethernet_types"
10         "go.fd.io/govpp/binapi/interface_types"
11         "go.fd.io/govpp/binapi/ip_types"
12 )
13
14 type (
15         Cmd                  = exec.Cmd
16         MacAddress           = ethernet_types.MacAddress
17         AddressWithPrefix    = ip_types.AddressWithPrefix
18         IP4AddressWithPrefix = ip_types.IP4AddressWithPrefix
19         InterfaceIndex       = interface_types.InterfaceIndex
20
21         NetConfig interface {
22                 configure() error
23                 unconfigure()
24                 Name() string
25                 Type() string
26         }
27
28         NetConfigBase struct {
29                 name     string
30                 category string // what else to call this when `type` is reserved?
31         }
32
33         NetInterface struct {
34                 NetConfigBase
35                 ip4AddrAllocator *Ip4AddressAllocator
36                 ip4Address       string
37                 index            InterfaceIndex
38                 hwAddress        MacAddress
39                 networkNamespace string
40                 networkNumber    int
41                 peer             *NetInterface
42         }
43
44         NetworkNamespace struct {
45                 NetConfigBase
46         }
47
48         NetworkBridge struct {
49                 NetConfigBase
50                 networkNamespace string
51                 interfaces       []string
52         }
53 )
54
55 const (
56         NetNs  string = "netns"
57         Veth   string = "veth"
58         Tap    string = "tap"
59         Bridge string = "bridge"
60 )
61
62 type InterfaceAdder func(n *NetInterface) *Cmd
63
64 var (
65         ipCommandMap = map[string]InterfaceAdder{
66                 Veth: func(n *NetInterface) *Cmd {
67                         return exec.Command("ip", "link", "add", n.name, "type", "veth", "peer", "name", n.peer.name)
68                 },
69                 Tap: func(n *NetInterface) *Cmd {
70                         return exec.Command("ip", "tuntap", "add", n.name, "mode", "tap")
71                 },
72         }
73 )
74
75 func newNetworkInterface(cfg NetDevConfig, a *Ip4AddressAllocator) (*NetInterface, error) {
76         var newInterface *NetInterface = &NetInterface{}
77         var err error
78         newInterface.ip4AddrAllocator = a
79         newInterface.name = cfg["name"].(string)
80         newInterface.networkNumber = DEFAULT_NETWORK_NUM
81
82         if interfaceType, ok := cfg["type"]; ok {
83                 newInterface.category = interfaceType.(string)
84         }
85
86         if presetHwAddress, ok := cfg["preset-hw-address"]; ok {
87                 newInterface.hwAddress, err = ethernet_types.ParseMacAddress(presetHwAddress.(string))
88                 if err != nil {
89                         return &NetInterface{}, err
90                 }
91         }
92
93         if netns, ok := cfg["netns"]; ok {
94                 newInterface.networkNamespace = netns.(string)
95         }
96
97         if ip, ok := cfg["ip4"]; ok {
98                 if n, ok := ip.(NetDevConfig)["network"]; ok {
99                         newInterface.networkNumber = n.(int)
100                 }
101                 newInterface.ip4Address, err = newInterface.ip4AddrAllocator.NewIp4InterfaceAddress(
102                         newInterface.networkNumber,
103                 )
104                 if err != nil {
105                         return &NetInterface{}, err
106                 }
107         }
108
109         if _, ok := cfg["peer"]; !ok {
110                 return newInterface, nil
111         }
112
113         peer := cfg["peer"].(NetDevConfig)
114
115         if newInterface.peer, err = newNetworkInterface(peer, a); err != nil {
116                 return &NetInterface{}, err
117         }
118
119         return newInterface, nil
120 }
121
122 func (n *NetInterface) configureUpState() error {
123         err := setDevUp(n.Name(), "")
124         if err != nil {
125                 return fmt.Errorf("set link up failed: %v", err)
126         }
127         return nil
128 }
129
130 func (n *NetInterface) configureNetworkNamespace() error {
131         if n.networkNamespace != "" {
132                 err := linkSetNetns(n.name, n.networkNamespace)
133                 if err != nil {
134                         return err
135                 }
136         }
137         return nil
138 }
139
140 func (n *NetInterface) configureAddress() error {
141         if n.ip4Address != "" {
142                 if err := addAddress(
143                         n.Name(),
144                         n.ip4Address,
145                         n.networkNamespace,
146                 ); err != nil {
147                         return err
148                 }
149
150         }
151         return nil
152 }
153
154 func (n *NetInterface) configure() error {
155         cmd := ipCommandMap[n.Type()](n)
156         _, err := cmd.CombinedOutput()
157         if err != nil {
158                 return fmt.Errorf("creating interface '%v' failed: %v", n.Name(), err)
159         }
160
161         if err := n.configureUpState(); err != nil {
162                 return err
163         }
164
165         if err := n.configureNetworkNamespace(); err != nil {
166                 return err
167         }
168
169         if err := n.configureAddress(); err != nil {
170                 return err
171         }
172
173         if n.peer != nil && n.peer.name != "" {
174                 if err := n.peer.configureUpState(); err != nil {
175                         return err
176                 }
177
178                 if err := n.peer.configureNetworkNamespace(); err != nil {
179                         return err
180                 }
181
182                 if err := n.peer.configureAddress(); err != nil {
183                         return err
184                 }
185         }
186
187         return nil
188 }
189
190 func (n *NetInterface) unconfigure() {
191         delLink(n.name)
192 }
193
194 func (n *NetInterface) Name() string {
195         return n.name
196 }
197
198 func (n *NetInterface) Type() string {
199         return n.category
200 }
201
202 func (n *NetInterface) addressWithPrefix() AddressWithPrefix {
203         address, _ := ip_types.ParseAddressWithPrefix(n.ip4Address)
204         return address
205 }
206
207 func (n *NetInterface) ip4AddressWithPrefix() IP4AddressWithPrefix {
208         ip4Prefix, _ := ip_types.ParseIP4Prefix(n.ip4Address)
209         ip4AddressWithPrefix := ip_types.IP4AddressWithPrefix(ip4Prefix)
210         return ip4AddressWithPrefix
211 }
212
213 func (n *NetInterface) ip4AddressString() string {
214         return strings.Split(n.ip4Address, "/")[0]
215 }
216
217 func (b *NetConfigBase) Name() string {
218         return b.name
219 }
220
221 func (b *NetConfigBase) Type() string {
222         return b.category
223 }
224
225 func newNetNamespace(cfg NetDevConfig) (NetworkNamespace, error) {
226         var networkNamespace NetworkNamespace
227         networkNamespace.name = cfg["name"].(string)
228         networkNamespace.category = NetNs
229         return networkNamespace, nil
230 }
231
232 func (ns *NetworkNamespace) configure() error {
233         return addDelNetns(ns.name, true)
234 }
235
236 func (ns *NetworkNamespace) unconfigure() {
237         addDelNetns(ns.name, false)
238 }
239
240 func newBridge(cfg NetDevConfig) (NetworkBridge, error) {
241         var bridge NetworkBridge
242         bridge.name = cfg["name"].(string)
243         bridge.category = Bridge
244         for _, v := range cfg["interfaces"].([]interface{}) {
245                 bridge.interfaces = append(bridge.interfaces, v.(string))
246         }
247
248         bridge.networkNamespace = ""
249         if netns, ok := cfg["netns"]; ok {
250                 bridge.networkNamespace = netns.(string)
251         }
252         return bridge, nil
253 }
254
255 func (b *NetworkBridge) configure() error {
256         return addBridge(b.name, b.interfaces, b.networkNamespace)
257 }
258
259 func (b *NetworkBridge) unconfigure() {
260         delBridge(b.name, b.networkNamespace)
261 }
262
263 func delBridge(brName, ns string) error {
264         err := setDevDown(brName, ns)
265         if err != nil {
266                 return err
267         }
268
269         err = addDelBridge(brName, ns, false)
270         if err != nil {
271                 return err
272         }
273
274         return nil
275 }
276
277 func setDevUp(dev, ns string) error {
278         return setDevUpDown(dev, ns, true)
279 }
280
281 func setDevDown(dev, ns string) error {
282         return setDevUpDown(dev, ns, false)
283 }
284
285 func delLink(ifName string) {
286         cmd := exec.Command("ip", "link", "del", ifName)
287         cmd.Run()
288 }
289
290 func setDevUpDown(dev, ns string, isUp bool) error {
291         var op string
292         if isUp {
293                 op = "up"
294         } else {
295                 op = "down"
296         }
297         c := []string{"ip", "link", "set", "dev", dev, op}
298         cmd := appendNetns(c, ns)
299         err := cmd.Run()
300         if err != nil {
301                 return fmt.Errorf("error bringing %s device %s! (cmd: '%s')", dev, op, cmd)
302         }
303         return nil
304 }
305
306 func addDelNetns(name string, isAdd bool) error {
307         var op string
308         if isAdd {
309                 op = "add"
310         } else {
311                 op = "del"
312         }
313         cmd := exec.Command("ip", "netns", op, name)
314         _, err := cmd.CombinedOutput()
315         if err != nil {
316                 return fmt.Errorf("add/del netns failed (cmd: '%s')", cmd)
317         }
318         return nil
319 }
320
321 func linkSetNetns(ifName, ns string) error {
322         cmd := exec.Command("ip", "link", "set", "dev", ifName, "up", "netns", ns)
323         err := cmd.Run()
324         if err != nil {
325                 return fmt.Errorf("error setting device '%s' to netns '%s: %v", ifName, ns, err)
326         }
327         return nil
328 }
329
330 func newCommand(s []string, ns string) *exec.Cmd {
331         return appendNetns(s, ns)
332 }
333
334 func appendNetns(s []string, ns string) *exec.Cmd {
335         var cmd *exec.Cmd
336         if ns == "" {
337                 // use default namespace
338                 cmd = exec.Command(s[0], s[1:]...)
339         } else {
340                 var args = []string{"netns", "exec", ns}
341                 args = append(args, s[:]...)
342                 cmd = exec.Command("ip", args...)
343         }
344         return cmd
345 }
346
347 func addDelBridge(brName, ns string, isAdd bool) error {
348         var op string
349         if isAdd {
350                 op = "addbr"
351         } else {
352                 op = "delbr"
353         }
354         var c = []string{"brctl", op, brName}
355         cmd := appendNetns(c, ns)
356         err := cmd.Run()
357         if err != nil {
358                 s := fmt.Sprintf("%s %s failed! err: '%s'", op, brName, err)
359                 return errors.New(s)
360         }
361         return nil
362 }
363
364 func addBridge(brName string, ifs []string, ns string) error {
365         err := addDelBridge(brName, ns, true)
366         if err != nil {
367                 return err
368         }
369
370         for _, v := range ifs {
371                 c := []string{"brctl", "addif", brName, v}
372                 cmd := appendNetns(c, ns)
373                 err = cmd.Run()
374                 if err != nil {
375                         return fmt.Errorf("error adding %s to bridge %s: %s", v, brName, err)
376                 }
377         }
378         err = setDevUp(brName, ns)
379         if err != nil {
380                 return err
381         }
382         return nil
383 }