misc: add test framework for host stack
[vpp.git] / extras / hs-test / netconfig.go
1 package main
2
3 import (
4         "errors"
5         "fmt"
6         "os/exec"
7 )
8
9 type NetType string
10
11 const (
12         NetNs NetType = "netns"
13         Veth          = "veth"
14         Tap           = "tap"
15 )
16
17 type NetConfig struct {
18         Configure   func() error
19         Unconfigure func()
20 }
21
22 type NetTopology []NetConfig
23
24 func (t *NetTopology) Configure() error {
25         for _, c := range *t {
26                 err := c.Configure()
27                 if err != nil {
28                         return err
29                 }
30         }
31         return nil
32 }
33
34 func (t *NetTopology) Unconfigure() {
35         for _, c := range *t {
36                 c.Unconfigure()
37         }
38 }
39
40 func newConfigFn(cfg NetDevConfig) func() error {
41         t := cfg["type"]
42         if t == "netns" {
43                 return func() error { return AddNetns(cfg["name"].(string)) }
44         } else if t == "veth" {
45                 return func() error {
46                         var peerNs string
47                         peer := cfg["peer"].(NetDevConfig)
48                         peerName := peer["name"].(string)
49                         err := AddVethPair(cfg["name"].(string), peerName)
50                         if err != nil {
51                                 return err
52                         }
53
54                         if peer["netns"] != nil {
55                                 peerNs = peer["netns"].(string)
56                                 if peerNs != "" {
57                                         err := LinkSetNetns(peerName, peerNs)
58                                         if err != nil {
59                                                 return err
60                                         }
61                                 }
62                         }
63                         if peer["ip4"] != nil {
64                                 err = AddAddress(peerName, peer["ip4"].(string), peerNs)
65                                 if err != nil {
66                                         return fmt.Errorf("failed to add configure address for %s: %v", peerName, err)
67                                 }
68                         }
69                         return nil
70                 }
71         } else if t == "bridge" {
72                 return func() error { return configureBridge(cfg) }
73         } else if t == "tap" {
74                 return func() error { return configureTap(cfg) }
75         }
76         return nil
77 }
78
79 func newUnconfigFn(cfg NetDevConfig) func() {
80         t := cfg["type"]
81         name := cfg["name"].(string)
82
83         if t == "tap" {
84                 return func() { DelLink(name) }
85         } else if t == "netns" {
86                 return func() { DelNetns(name) }
87         } else if t == "veth" {
88                 return func() { DelLink(name) }
89         } else if t == "bridge" {
90                 return func() { DelBridge(name, cfg["netns"].(string)) }
91         }
92         return nil
93 }
94
95 func NewNetConfig(cfg NetDevConfig) NetConfig {
96         var nc NetConfig
97
98         nc.Configure = newConfigFn(cfg)
99         nc.Unconfigure = newUnconfigFn(cfg)
100
101         return nc
102 }
103
104 func DelBridge(brName, ns string) error {
105         err := SetDevDown(brName, ns)
106         if err != err {
107                 return err
108         }
109
110         err = addDelBridge(brName, ns, false)
111         if err != nil {
112                 return err
113         }
114
115         return nil
116 }
117
118 func configureBridge(dev NetDevConfig) error {
119         var ifs []string
120         for _, v := range dev["interfaces"].([]interface{}) {
121                 ifs = append(ifs, v.(string))
122         }
123         return AddBridge(dev["name"].(string), ifs, dev["netns"].(string))
124 }
125
126 func configureTap(dev NetDevConfig) error {
127         return AddTap(dev["name"].(string), dev["ip4"].(string))
128 }
129
130 func SetDevUp(dev, ns string) error {
131         return setDevUpDown(dev, ns, true)
132 }
133
134 func SetDevDown(dev, ns string) error {
135         return setDevUpDown(dev, ns, false)
136 }
137
138 func AddTap(ifName, ifAddress string) error {
139         cmd := exec.Command("ip", "tuntap", "add", ifName, "mode", "tap")
140         o, err := cmd.CombinedOutput()
141         if err != nil {
142                 s := fmt.Sprintf("error creating tap %s: %v: %s", ifName, err, string(o))
143                 return errors.New(s)
144         }
145
146         cmd = exec.Command("ip", "addr", "add", ifAddress, "dev", ifName)
147         err = cmd.Run()
148         if err != nil {
149                 DelLink(ifName)
150                 s := fmt.Sprintf("error setting addr for tap %s: %v", ifName, err)
151                 return errors.New(s)
152         }
153
154         err = SetDevUp(ifName, "")
155         if err != nil {
156                 DelLink(ifName)
157                 return err
158         }
159         return nil
160 }
161
162 func DelLink(ifName string) {
163         cmd := exec.Command("ip", "link", "del", ifName)
164         cmd.Run()
165 }
166
167 func setDevUpDown(dev, ns string, isUp bool) error {
168         var op string
169         if isUp {
170                 op = "up"
171         } else {
172                 op = "down"
173         }
174         c := []string{"ip", "link", "set", "dev", dev, op}
175         cmd := appendNetns(c, ns)
176         err := cmd.Run()
177         if err != nil {
178                 s := fmt.Sprintf("error bringing %s device %s!", dev, op)
179                 return errors.New(s)
180         }
181         return nil
182 }
183
184 func AddVethPair(ifName, peerName string) error {
185         cmd := exec.Command("ip", "link", "add", ifName, "type", "veth", "peer", "name", peerName)
186         err := cmd.Run()
187         if err != nil {
188                 return fmt.Errorf("creating veth pair failed: %v", err)
189         }
190         err = SetDevUp(ifName, "")
191         if err != nil {
192                 return fmt.Errorf("set link up failed: %v", err)
193         }
194         return nil
195 }
196
197 func addDelNetns(name string, isAdd bool) error {
198         var op string
199         if isAdd {
200                 op = "add"
201         } else {
202                 op = "del"
203         }
204         cmd := exec.Command("ip", "netns", op, name)
205         _, err := cmd.CombinedOutput()
206         if err != nil {
207                 return errors.New("add/del netns failed")
208         }
209         return nil
210 }
211
212 func AddNetns(nsName string) error {
213         return addDelNetns(nsName, true)
214 }
215
216 func DelNetns(nsName string) error {
217         return addDelNetns(nsName, false)
218 }
219
220 func LinkSetNetns(ifName, ns string) error {
221         cmd := exec.Command("ip", "link", "set", "dev", ifName, "up", "netns", ns)
222         err := cmd.Run()
223         if err != nil {
224                 return fmt.Errorf("error setting device '%s' to netns '%s: %v", ifName, ns, err)
225         }
226         return nil
227 }
228
229 func NewCommand(s []string, ns string) *exec.Cmd {
230         return appendNetns(s, ns)
231 }
232
233 func appendNetns(s []string, ns string) *exec.Cmd {
234         var cmd *exec.Cmd
235         if ns == "" {
236                 // use default namespace
237                 cmd = exec.Command(s[0], s[1:]...)
238         } else {
239                 var args = []string{"netns", "exec", ns}
240                 args = append(args, s[:]...)
241                 cmd = exec.Command("ip", args...)
242         }
243         return cmd
244 }
245
246 func addDelBridge(brName, ns string, isAdd bool) error {
247         var op string
248         if isAdd {
249                 op = "addbr"
250         } else {
251                 op = "delbr"
252         }
253         var c = []string{"brctl", op, brName}
254         cmd := appendNetns(c, ns)
255         err := cmd.Run()
256         if err != nil {
257                 s := fmt.Sprintf("%s %s failed!", op, brName)
258                 return errors.New(s)
259         }
260         return nil
261 }
262
263 func AddBridge(brName string, ifs []string, ns string) error {
264         err := addDelBridge(brName, ns, true)
265         if err != nil {
266                 return err
267         }
268
269         for _, v := range ifs {
270                 c := []string{"brctl", "addif", brName, v}
271                 cmd := appendNetns(c, ns)
272                 err = cmd.Run()
273                 if err != nil {
274                         s := fmt.Sprintf("error adding %s to bridge %s: %v", v, brName, err)
275                         return errors.New(s)
276                 }
277         }
278         err = SetDevUp(brName, ns)
279         if err != nil {
280                 return err
281         }
282         return nil
283 }