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