hs-test: refactor netconfig
[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                 addresser        *Addresser
36                 ip4Address       string // this will have form 10.10.10.1/24
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 *Addresser) (*NetInterface, error) {
76         var newInterface *NetInterface = &NetInterface{}
77         var err error
78         newInterface.addresser = a
79         newInterface.name = cfg["name"].(string)
80         newInterface.networkNumber = defaultNetworkNumber
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.addresser.NewIp4Address(
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) SetAddress(address string) {
203         n.ip4Address = address
204 }
205
206 func (n *NetInterface) SetIndex(index InterfaceIndex) {
207         n.index = index
208 }
209
210 func (n *NetInterface) Index() InterfaceIndex {
211         return n.index
212 }
213
214 func (n *NetInterface) AddressWithPrefix() AddressWithPrefix {
215         address, _ := ip_types.ParseAddressWithPrefix(n.ip4Address)
216         return address
217 }
218
219 func (n *NetInterface) IP4AddressWithPrefix() IP4AddressWithPrefix {
220         ip4Prefix, _ := ip_types.ParseIP4Prefix(n.ip4Address)
221         ip4AddressWithPrefix := ip_types.IP4AddressWithPrefix(ip4Prefix)
222         return ip4AddressWithPrefix
223 }
224
225 func (n *NetInterface) IP4AddressString() string {
226         return strings.Split(n.ip4Address, "/")[0]
227 }
228
229 func (n *NetInterface) HwAddress() MacAddress {
230         return n.hwAddress
231 }
232
233 func (n *NetInterface) Peer() *NetInterface {
234         return n.peer
235 }
236
237 func (b *NetConfigBase) Name() string {
238         return b.name
239 }
240
241 func (b *NetConfigBase) Type() string {
242         return b.category
243 }
244
245 func NewNetNamespace(cfg NetDevConfig) (NetworkNamespace, error) {
246         var networkNamespace NetworkNamespace
247         networkNamespace.name = cfg["name"].(string)
248         networkNamespace.category = NetNs
249         return networkNamespace, nil
250 }
251
252 func (ns *NetworkNamespace) Configure() error {
253         return addDelNetns(ns.name, true)
254 }
255
256 func (ns *NetworkNamespace) Unconfigure() {
257         addDelNetns(ns.name, false)
258 }
259
260 func NewBridge(cfg NetDevConfig) (NetworkBridge, error) {
261         var bridge NetworkBridge
262         bridge.name = cfg["name"].(string)
263         bridge.category = Bridge
264         for _, v := range cfg["interfaces"].([]interface{}) {
265                 bridge.interfaces = append(bridge.interfaces, v.(string))
266         }
267
268         bridge.networkNamespace = ""
269         if netns, ok := cfg["netns"]; ok {
270                 bridge.networkNamespace = netns.(string)
271         }
272         return bridge, nil
273 }
274
275 func (b *NetworkBridge) Configure() error {
276         return AddBridge(b.name, b.interfaces, b.networkNamespace)
277 }
278
279 func (b *NetworkBridge) Unconfigure() {
280         DelBridge(b.name, b.networkNamespace)
281 }
282
283 func DelBridge(brName, ns string) error {
284         err := SetDevDown(brName, ns)
285         if err != err {
286                 return err
287         }
288
289         err = addDelBridge(brName, ns, false)
290         if err != nil {
291                 return err
292         }
293
294         return nil
295 }
296
297 func SetDevUp(dev, ns string) error {
298         return setDevUpDown(dev, ns, true)
299 }
300
301 func SetDevDown(dev, ns string) error {
302         return setDevUpDown(dev, ns, false)
303 }
304
305 func DelLink(ifName string) {
306         cmd := exec.Command("ip", "link", "del", ifName)
307         cmd.Run()
308 }
309
310 func setDevUpDown(dev, ns string, isUp bool) error {
311         var op string
312         if isUp {
313                 op = "up"
314         } else {
315                 op = "down"
316         }
317         c := []string{"ip", "link", "set", "dev", dev, op}
318         cmd := appendNetns(c, ns)
319         err := cmd.Run()
320         if err != nil {
321                 s := fmt.Sprintf("error bringing %s device %s!", dev, op)
322                 return errors.New(s)
323         }
324         return nil
325 }
326
327 func addDelNetns(name string, isAdd bool) error {
328         var op string
329         if isAdd {
330                 op = "add"
331         } else {
332                 op = "del"
333         }
334         cmd := exec.Command("ip", "netns", op, name)
335         _, err := cmd.CombinedOutput()
336         if err != nil {
337                 return errors.New("add/del netns failed")
338         }
339         return nil
340 }
341
342 func AddNetns(nsName string) error {
343         return addDelNetns(nsName, true)
344 }
345
346 func DelNetns(nsName string) error {
347         return addDelNetns(nsName, false)
348 }
349
350 func LinkSetNetns(ifName, ns string) error {
351         cmd := exec.Command("ip", "link", "set", "dev", ifName, "up", "netns", ns)
352         err := cmd.Run()
353         if err != nil {
354                 return fmt.Errorf("error setting device '%s' to netns '%s: %v", ifName, ns, err)
355         }
356         return nil
357 }
358
359 func NewCommand(s []string, ns string) *exec.Cmd {
360         return appendNetns(s, ns)
361 }
362
363 func appendNetns(s []string, ns string) *exec.Cmd {
364         var cmd *exec.Cmd
365         if ns == "" {
366                 // use default namespace
367                 cmd = exec.Command(s[0], s[1:]...)
368         } else {
369                 var args = []string{"netns", "exec", ns}
370                 args = append(args, s[:]...)
371                 cmd = exec.Command("ip", args...)
372         }
373         return cmd
374 }
375
376 func addDelBridge(brName, ns string, isAdd bool) error {
377         var op string
378         if isAdd {
379                 op = "addbr"
380         } else {
381                 op = "delbr"
382         }
383         var c = []string{"brctl", op, brName}
384         cmd := appendNetns(c, ns)
385         err := cmd.Run()
386         if err != nil {
387                 s := fmt.Sprintf("%s %s failed!", op, brName)
388                 return errors.New(s)
389         }
390         return nil
391 }
392
393 func AddBridge(brName string, ifs []string, ns string) error {
394         err := addDelBridge(brName, ns, true)
395         if err != nil {
396                 return err
397         }
398
399         for _, v := range ifs {
400                 c := []string{"brctl", "addif", brName, v}
401                 cmd := appendNetns(c, ns)
402                 err = cmd.Run()
403                 if err != nil {
404                         s := fmt.Sprintf("error adding %s to bridge %s: %v", v, brName, err)
405                         return errors.New(s)
406                 }
407         }
408         err = SetDevUp(brName, ns)
409         if err != nil {
410                 return err
411         }
412         return nil
413 }