hs-test: refactor test cases from ns suite
[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         MacAddress        = ethernet_types.MacAddress
16         AddressWithPrefix = ip_types.AddressWithPrefix
17         InterfaceIndex    = interface_types.InterfaceIndex
18
19         LegacyNetConfig struct {
20                 Configure   func() error
21                 Unconfigure func()
22         }
23
24         NetTopology []LegacyNetConfig
25
26         NetConfig interface {
27                 Configure() error
28                 Unconfigure()
29                 Name() string
30                 Type() string
31         }
32
33         NetConfigBase struct {
34                 name     string
35                 category string // what else to call this when `type` is reserved?
36         }
37
38         NetInterface interface {
39                 NetConfig
40                 SetAddress(string)
41                 Ip4AddressWithPrefix() AddressWithPrefix
42                 Ip4AddressString() string
43                 SetIndex(InterfaceIndex)
44                 Index() InterfaceIndex
45                 HwAddress() MacAddress
46         }
47
48         NetInterfaceBase struct {
49                 NetConfigBase
50                 addresser  *Addresser
51                 ip4address string // this will have form 10.10.10.1/24
52                 index      InterfaceIndex
53                 hwAddress  MacAddress
54         }
55
56         NetworkInterfaceVeth struct {
57                 NetInterfaceBase
58                 peerNetworkNamespace string
59                 peerName             string
60                 peerIp4Address       string
61         }
62
63         NetworkInterfaceTap struct {
64                 NetInterfaceBase
65         }
66
67         NetworkNamespace struct {
68                 NetConfigBase
69         }
70
71         NetworkBridge struct {
72                 NetConfigBase
73                 networkNamespace string
74                 interfaces       []string
75         }
76 )
77
78 const (
79         NetNs  string = "netns"
80         Veth   string = "veth"
81         Tap    string = "tap"
82         Bridge string = "bridge"
83 )
84
85 func (b *NetConfigBase) Name() string {
86         return b.name
87 }
88
89 func (b *NetConfigBase) Type() string {
90         return b.category
91 }
92
93 func (b *NetInterfaceBase) SetAddress(address string) {
94         b.ip4address = address
95 }
96
97 func (b *NetInterfaceBase) SetIndex(index InterfaceIndex) {
98         b.index = index
99 }
100
101 func (b *NetInterfaceBase) Index() InterfaceIndex {
102         return b.index
103 }
104
105 func (b *NetInterfaceBase) Ip4AddressWithPrefix() AddressWithPrefix {
106         address, _ := ip_types.ParseAddressWithPrefix(b.ip4address)
107         return address
108 }
109
110 func (b *NetInterfaceBase) Ip4AddressString() string {
111         return strings.Split(b.ip4address, "/")[0]
112 }
113
114 func (b *NetInterfaceBase) HwAddress() MacAddress {
115         return b.hwAddress
116 }
117
118 func (iface *NetworkInterfaceVeth) Configure() error {
119         err := AddVethPair(iface.name, iface.peerName)
120         if err != nil {
121                 return err
122         }
123
124         if iface.peerNetworkNamespace != "" {
125                 err := LinkSetNetns(iface.peerName, iface.peerNetworkNamespace)
126                 if err != nil {
127                         return err
128                 }
129         }
130
131         if iface.peerIp4Address != "" {
132                 err = AddAddress(iface.peerName, iface.peerIp4Address, iface.peerNetworkNamespace)
133                 if err != nil {
134                         return fmt.Errorf("failed to add configure address for %s: %v", iface.peerName, err)
135                 }
136         }
137         return nil
138 }
139
140 func (iface *NetworkInterfaceVeth) Unconfigure() {
141         DelLink(iface.name)
142 }
143
144 func (iface *NetworkInterfaceVeth) PeerIp4AddressString() string {
145         return strings.Split(iface.peerIp4Address, "/")[0]
146 }
147
148 func (iface *NetworkInterfaceTap) Configure() error {
149         err := AddTap(iface.name, iface.Ip4AddressString())
150         if err != nil {
151                 return err
152         }
153         return nil
154 }
155
156 func (iface *NetworkInterfaceTap) Unconfigure() {
157         DelLink(iface.name)
158 }
159
160 func (ns *NetworkNamespace) Configure() error {
161         return addDelNetns(ns.name, true)
162 }
163
164 func (ns *NetworkNamespace) Unconfigure() {
165         addDelNetns(ns.name, false)
166 }
167
168 func (b *NetworkBridge) Configure() error {
169         return AddBridge(b.name, b.interfaces, b.networkNamespace)
170 }
171
172 func (b *NetworkBridge) Unconfigure() {
173         DelBridge(b.name, b.networkNamespace)
174 }
175
176 func (t *NetTopology) Configure() error {
177         for _, c := range *t {
178                 err := c.Configure()
179                 if err != nil {
180                         return err
181                 }
182         }
183         return nil
184 }
185
186 func (t *NetTopology) Unconfigure() {
187         for _, c := range *t {
188                 c.Unconfigure()
189         }
190 }
191
192 func newConfigFn(cfg NetDevConfig) func() error {
193         t := cfg["type"]
194         if t == "netns" {
195                 return func() error { return AddNetns(cfg["name"].(string)) }
196         } else if t == "veth" {
197                 return func() error {
198                         var peerNs string
199                         peer := cfg["peer"].(NetDevConfig)
200                         peerName := peer["name"].(string)
201                         err := AddVethPair(cfg["name"].(string), peerName)
202                         if err != nil {
203                                 return err
204                         }
205
206                         if peer["netns"] != nil {
207                                 peerNs = peer["netns"].(string)
208                                 if peerNs != "" {
209                                         err := LinkSetNetns(peerName, peerNs)
210                                         if err != nil {
211                                                 return err
212                                         }
213                                 }
214                         }
215                         if peer["ip4"] != nil {
216                                 err = AddAddress(peerName, peer["ip4"].(string), peerNs)
217                                 if err != nil {
218                                         return fmt.Errorf("failed to add configure address for %s: %v", peerName, err)
219                                 }
220                         }
221                         return nil
222                 }
223         } else if t == "bridge" {
224                 return func() error { return configureBridge(cfg) }
225         }
226         return nil
227 }
228
229 func newUnconfigFn(cfg NetDevConfig) func() {
230         t := cfg["type"]
231         name := cfg["name"].(string)
232
233         if t == "netns" {
234                 return func() { DelNetns(name) }
235         } else if t == "veth" {
236                 return func() { DelLink(name) }
237         } else if t == "bridge" {
238                 return func() { DelBridge(name, cfg["netns"].(string)) }
239         }
240         return nil
241 }
242
243 func NewNetConfig(cfg NetDevConfig) LegacyNetConfig {
244         var nc LegacyNetConfig
245
246         nc.Configure = newConfigFn(cfg)
247         nc.Unconfigure = newUnconfigFn(cfg)
248
249         return nc
250 }
251
252 func NewNetNamespace(cfg NetDevConfig) (NetworkNamespace, error) {
253         var networkNamespace NetworkNamespace
254         networkNamespace.name = cfg["name"].(string)
255         networkNamespace.category = "netns"
256         return networkNamespace, nil
257 }
258
259 func NewBridge(cfg NetDevConfig) (NetworkBridge, error) {
260         var bridge NetworkBridge
261         bridge.name = cfg["name"].(string)
262         bridge.category = "bridge"
263         for _, v := range cfg["interfaces"].([]interface{}) {
264                 bridge.interfaces = append(bridge.interfaces, v.(string))
265         }
266         bridge.networkNamespace = cfg["netns"].(string)
267         return bridge, nil
268 }
269
270 func NewVeth(cfg NetDevConfig, a *Addresser) (NetworkInterfaceVeth, error) {
271         var veth NetworkInterfaceVeth
272         var err error
273         veth.addresser = a
274         veth.name = cfg["name"].(string)
275         veth.category = "veth"
276
277         if cfg["preset-hw-address"] != nil {
278                 veth.hwAddress, err = ethernet_types.ParseMacAddress(cfg["preset-hw-address"].(string))
279                 if err != nil {
280                         return NetworkInterfaceVeth{}, err
281                 }
282         }
283
284         peer := cfg["peer"].(NetDevConfig)
285
286         veth.peerName = peer["name"].(string)
287
288         if peer["netns"] != nil {
289                 veth.peerNetworkNamespace = peer["netns"].(string)
290         }
291
292         if peer["ip4"] != nil && peer["ip4"].(bool) == true {
293                 veth.peerIp4Address, err = veth.addresser.
294                         NewIp4AddressWithNamespace(veth.peerNetworkNamespace)
295                 if err != nil {
296                         return NetworkInterfaceVeth{}, err
297                 }
298         }
299
300         return veth, nil
301 }
302
303 func NewTap(cfg NetDevConfig, a *Addresser) (NetworkInterfaceTap, error) {
304         var tap NetworkInterfaceTap
305         tap.addresser = a
306         tap.name = cfg["name"].(string)
307         tap.category = "tap"
308         ip4Address, err := tap.addresser.NewIp4Address()
309         if err != nil {
310                 return NetworkInterfaceTap{}, err
311         }
312         tap.SetAddress(ip4Address)
313         return tap, nil
314 }
315
316 func DelBridge(brName, ns string) error {
317         err := SetDevDown(brName, ns)
318         if err != err {
319                 return err
320         }
321
322         err = addDelBridge(brName, ns, false)
323         if err != nil {
324                 return err
325         }
326
327         return nil
328 }
329
330 func configureBridge(dev NetDevConfig) error {
331         var ifs []string
332         for _, v := range dev["interfaces"].([]interface{}) {
333                 ifs = append(ifs, v.(string))
334         }
335         return AddBridge(dev["name"].(string), ifs, dev["netns"].(string))
336 }
337
338 func configureTap(dev NetDevConfig) error {
339         return AddTap(dev["name"].(string), dev["ip4"].(string))
340 }
341
342 func SetDevUp(dev, ns string) error {
343         return setDevUpDown(dev, ns, true)
344 }
345
346 func SetDevDown(dev, ns string) error {
347         return setDevUpDown(dev, ns, false)
348 }
349
350 func AddTap(ifName, ifAddress string) error {
351         cmd := exec.Command("ip", "tuntap", "add", ifName, "mode", "tap")
352         o, err := cmd.CombinedOutput()
353         if err != nil {
354                 s := fmt.Sprintf("error creating tap %s: %v: %s", ifName, err, string(o))
355                 return errors.New(s)
356         }
357
358         cmd = exec.Command("ip", "addr", "add", ifAddress, "dev", ifName)
359         err = cmd.Run()
360         if err != nil {
361                 DelLink(ifName)
362                 s := fmt.Sprintf("error setting addr for tap %s: %v", ifName, err)
363                 return errors.New(s)
364         }
365
366         err = SetDevUp(ifName, "")
367         if err != nil {
368                 DelLink(ifName)
369                 return err
370         }
371         return nil
372 }
373
374 func DelLink(ifName string) {
375         cmd := exec.Command("ip", "link", "del", ifName)
376         cmd.Run()
377 }
378
379 func setDevUpDown(dev, ns string, isUp bool) error {
380         var op string
381         if isUp {
382                 op = "up"
383         } else {
384                 op = "down"
385         }
386         c := []string{"ip", "link", "set", "dev", dev, op}
387         cmd := appendNetns(c, ns)
388         err := cmd.Run()
389         if err != nil {
390                 s := fmt.Sprintf("error bringing %s device %s!", dev, op)
391                 return errors.New(s)
392         }
393         return nil
394 }
395
396 func AddVethPair(ifName, peerName string) error {
397         cmd := exec.Command("ip", "link", "add", ifName, "type", "veth", "peer", "name", peerName)
398         err := cmd.Run()
399         if err != nil {
400                 return fmt.Errorf("creating veth pair failed: %v", err)
401         }
402         err = SetDevUp(ifName, "")
403         if err != nil {
404                 return fmt.Errorf("set link up failed: %v", err)
405         }
406         return nil
407 }
408
409 func addDelNetns(name string, isAdd bool) error {
410         var op string
411         if isAdd {
412                 op = "add"
413         } else {
414                 op = "del"
415         }
416         cmd := exec.Command("ip", "netns", op, name)
417         _, err := cmd.CombinedOutput()
418         if err != nil {
419                 return errors.New("add/del netns failed")
420         }
421         return nil
422 }
423
424 func AddNetns(nsName string) error {
425         return addDelNetns(nsName, true)
426 }
427
428 func DelNetns(nsName string) error {
429         return addDelNetns(nsName, false)
430 }
431
432 func LinkSetNetns(ifName, ns string) error {
433         cmd := exec.Command("ip", "link", "set", "dev", ifName, "up", "netns", ns)
434         err := cmd.Run()
435         if err != nil {
436                 return fmt.Errorf("error setting device '%s' to netns '%s: %v", ifName, ns, err)
437         }
438         return nil
439 }
440
441 func NewCommand(s []string, ns string) *exec.Cmd {
442         return appendNetns(s, ns)
443 }
444
445 func appendNetns(s []string, ns string) *exec.Cmd {
446         var cmd *exec.Cmd
447         if ns == "" {
448                 // use default namespace
449                 cmd = exec.Command(s[0], s[1:]...)
450         } else {
451                 var args = []string{"netns", "exec", ns}
452                 args = append(args, s[:]...)
453                 cmd = exec.Command("ip", args...)
454         }
455         return cmd
456 }
457
458 func addDelBridge(brName, ns string, isAdd bool) error {
459         var op string
460         if isAdd {
461                 op = "addbr"
462         } else {
463                 op = "delbr"
464         }
465         var c = []string{"brctl", op, brName}
466         cmd := appendNetns(c, ns)
467         err := cmd.Run()
468         if err != nil {
469                 s := fmt.Sprintf("%s %s failed!", op, brName)
470                 return errors.New(s)
471         }
472         return nil
473 }
474
475 func AddBridge(brName string, ifs []string, ns string) error {
476         err := addDelBridge(brName, ns, true)
477         if err != nil {
478                 return err
479         }
480
481         for _, v := range ifs {
482                 c := []string{"brctl", "addif", brName, v}
483                 cmd := appendNetns(c, ns)
484                 err = cmd.Run()
485                 if err != nil {
486                         s := fmt.Sprintf("error adding %s to bridge %s: %v", v, brName, err)
487                         return errors.New(s)
488                 }
489         }
490         err = SetDevUp(brName, ns)
491         if err != nil {
492                 return err
493         }
494         return nil
495 }