blob: c76a0fda5f5c979bcfd3611ec039770011ea7386 [file] [log] [blame]
Filip Tehlar229f5fc2022-08-09 14:44:47 +00001package main
2
3import (
4 "errors"
5 "fmt"
6 "os/exec"
Maros Ondrejickaffa3f602023-01-26 10:07:29 +01007 "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"
Filip Tehlar229f5fc2022-08-09 14:44:47 +000012)
13
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010014type (
Maros Ondrejicka40cba402023-02-23 13:19:15 +010015 Cmd = exec.Cmd
Maros Ondrejicka7550dd22023-02-07 20:40:27 +010016 MacAddress = ethernet_types.MacAddress
17 AddressWithPrefix = ip_types.AddressWithPrefix
18 IP4AddressWithPrefix = ip_types.IP4AddressWithPrefix
19 InterfaceIndex = interface_types.InterfaceIndex
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +010020
21 NetConfig interface {
Maros Ondrejickae7625d02023-02-28 16:55:01 +010022 configure() error
23 unconfigure()
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +010024 Name() string
25 Type() string
26 }
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010027
28 NetConfigBase struct {
29 name string
30 category string // what else to call this when `type` is reserved?
31 }
32
Maros Ondrejicka40cba402023-02-23 13:19:15 +010033 NetInterface struct {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010034 NetConfigBase
Filip Tehlar3a910ab2023-06-08 17:39:39 +020035 ip4AddrAllocator *Ip4AddressAllocator
36 ip4Address string
Maros Ondrejicka300f70d2023-02-21 10:53:20 +010037 index InterfaceIndex
38 hwAddress MacAddress
39 networkNamespace string
40 networkNumber int
Maros Ondrejicka40cba402023-02-23 13:19:15 +010041 peer *NetInterface
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010042 }
43
44 NetworkNamespace struct {
45 NetConfigBase
46 }
47
48 NetworkBridge struct {
49 NetConfigBase
50 networkNamespace string
51 interfaces []string
52 }
53)
Filip Tehlar229f5fc2022-08-09 14:44:47 +000054
55const (
Maros Ondrejickaffa3f602023-01-26 10:07:29 +010056 NetNs string = "netns"
57 Veth string = "veth"
58 Tap string = "tap"
59 Bridge string = "bridge"
Filip Tehlar229f5fc2022-08-09 14:44:47 +000060)
61
Maros Ondrejicka40cba402023-02-23 13:19:15 +010062type InterfaceAdder func(n *NetInterface) *Cmd
63
64var (
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
Filip Tehlar3a910ab2023-06-08 17:39:39 +020075func newNetworkInterface(cfg NetDevConfig, a *Ip4AddressAllocator) (*NetInterface, error) {
Maros Ondrejicka40cba402023-02-23 13:19:15 +010076 var newInterface *NetInterface = &NetInterface{}
77 var err error
Filip Tehlar3a910ab2023-06-08 17:39:39 +020078 newInterface.ip4AddrAllocator = a
Maros Ondrejicka40cba402023-02-23 13:19:15 +010079 newInterface.name = cfg["name"].(string)
Filip Tehlar608d0062023-04-28 10:29:47 +020080 newInterface.networkNumber = DEFAULT_NETWORK_NUM
Maros Ondrejicka40cba402023-02-23 13:19:15 +010081
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 }
Filip Tehlar3a910ab2023-06-08 17:39:39 +0200101 newInterface.ip4Address, err = newInterface.ip4AddrAllocator.NewIp4InterfaceAddress(
Maros Ondrejicka40cba402023-02-23 13:19:15 +0100102 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
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100115 if newInterface.peer, err = newNetworkInterface(peer, a); err != nil {
Maros Ondrejicka40cba402023-02-23 13:19:15 +0100116 return &NetInterface{}, err
117 }
118
119 return newInterface, nil
120}
121
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100122func (n *NetInterface) configureUpState() error {
123 err := setDevUp(n.Name(), "")
Maros Ondrejicka40cba402023-02-23 13:19:15 +0100124 if err != nil {
125 return fmt.Errorf("set link up failed: %v", err)
126 }
127 return nil
128}
129
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100130func (n *NetInterface) configureNetworkNamespace() error {
Maros Ondrejicka40cba402023-02-23 13:19:15 +0100131 if n.networkNamespace != "" {
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100132 err := linkSetNetns(n.name, n.networkNamespace)
Maros Ondrejicka40cba402023-02-23 13:19:15 +0100133 if err != nil {
134 return err
135 }
136 }
137 return nil
138}
139
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100140func (n *NetInterface) configureAddress() error {
Maros Ondrejicka40cba402023-02-23 13:19:15 +0100141 if n.ip4Address != "" {
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100142 if err := addAddress(
Maros Ondrejicka40cba402023-02-23 13:19:15 +0100143 n.Name(),
144 n.ip4Address,
145 n.networkNamespace,
146 ); err != nil {
147 return err
148 }
149
150 }
151 return nil
152}
153
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100154func (n *NetInterface) configure() error {
Maros Ondrejicka40cba402023-02-23 13:19:15 +0100155 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
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100161 if err := n.configureUpState(); err != nil {
Maros Ondrejicka40cba402023-02-23 13:19:15 +0100162 return err
163 }
164
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100165 if err := n.configureNetworkNamespace(); err != nil {
Maros Ondrejicka40cba402023-02-23 13:19:15 +0100166 return err
167 }
168
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100169 if err := n.configureAddress(); err != nil {
Maros Ondrejicka40cba402023-02-23 13:19:15 +0100170 return err
171 }
172
173 if n.peer != nil && n.peer.name != "" {
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100174 if err := n.peer.configureUpState(); err != nil {
Maros Ondrejicka40cba402023-02-23 13:19:15 +0100175 return err
176 }
177
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100178 if err := n.peer.configureNetworkNamespace(); err != nil {
Maros Ondrejicka40cba402023-02-23 13:19:15 +0100179 return err
180 }
181
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100182 if err := n.peer.configureAddress(); err != nil {
Maros Ondrejicka40cba402023-02-23 13:19:15 +0100183 return err
184 }
185 }
186
187 return nil
188}
189
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100190func (n *NetInterface) unconfigure() {
191 delLink(n.name)
Maros Ondrejicka40cba402023-02-23 13:19:15 +0100192}
193
194func (n *NetInterface) Name() string {
195 return n.name
196}
197
198func (n *NetInterface) Type() string {
199 return n.category
200}
201
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100202func (n *NetInterface) addressWithPrefix() AddressWithPrefix {
Maros Ondrejicka40cba402023-02-23 13:19:15 +0100203 address, _ := ip_types.ParseAddressWithPrefix(n.ip4Address)
204 return address
205}
206
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100207func (n *NetInterface) ip4AddressWithPrefix() IP4AddressWithPrefix {
Maros Ondrejicka40cba402023-02-23 13:19:15 +0100208 ip4Prefix, _ := ip_types.ParseIP4Prefix(n.ip4Address)
209 ip4AddressWithPrefix := ip_types.IP4AddressWithPrefix(ip4Prefix)
210 return ip4AddressWithPrefix
211}
212
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100213func (n *NetInterface) ip4AddressString() string {
Maros Ondrejicka40cba402023-02-23 13:19:15 +0100214 return strings.Split(n.ip4Address, "/")[0]
215}
216
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100217func (b *NetConfigBase) Name() string {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100218 return b.name
Filip Tehlar229f5fc2022-08-09 14:44:47 +0000219}
220
Maros Ondrejicka2908f8c2023-02-02 08:58:04 +0100221func (b *NetConfigBase) Type() string {
Maros Ondrejickaffa3f602023-01-26 10:07:29 +0100222 return b.category
223}
224
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100225func newNetNamespace(cfg NetDevConfig) (NetworkNamespace, error) {
Maros Ondrejicka7550dd22023-02-07 20:40:27 +0100226 var networkNamespace NetworkNamespace
227 networkNamespace.name = cfg["name"].(string)
Maros Ondrejicka40cba402023-02-23 13:19:15 +0100228 networkNamespace.category = NetNs
Maros Ondrejicka7550dd22023-02-07 20:40:27 +0100229 return networkNamespace, nil
230}
231
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100232func (ns *NetworkNamespace) configure() error {
Maros Ondrejicka7550dd22023-02-07 20:40:27 +0100233 return addDelNetns(ns.name, true)
234}
235
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100236func (ns *NetworkNamespace) unconfigure() {
Maros Ondrejicka7550dd22023-02-07 20:40:27 +0100237 addDelNetns(ns.name, false)
238}
239
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100240func newBridge(cfg NetDevConfig) (NetworkBridge, error) {
Maros Ondrejicka7550dd22023-02-07 20:40:27 +0100241 var bridge NetworkBridge
242 bridge.name = cfg["name"].(string)
Maros Ondrejicka40cba402023-02-23 13:19:15 +0100243 bridge.category = Bridge
Maros Ondrejicka7550dd22023-02-07 20:40:27 +0100244 for _, v := range cfg["interfaces"].([]interface{}) {
245 bridge.interfaces = append(bridge.interfaces, v.(string))
246 }
Maros Ondrejicka300f70d2023-02-21 10:53:20 +0100247
248 bridge.networkNamespace = ""
249 if netns, ok := cfg["netns"]; ok {
250 bridge.networkNamespace = netns.(string)
251 }
Maros Ondrejicka7550dd22023-02-07 20:40:27 +0100252 return bridge, nil
253}
254
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100255func (b *NetworkBridge) configure() error {
256 return addBridge(b.name, b.interfaces, b.networkNamespace)
Maros Ondrejicka7550dd22023-02-07 20:40:27 +0100257}
258
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100259func (b *NetworkBridge) unconfigure() {
260 delBridge(b.name, b.networkNamespace)
Maros Ondrejicka7550dd22023-02-07 20:40:27 +0100261}
262
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100263func delBridge(brName, ns string) error {
264 err := setDevDown(brName, ns)
Maros Ondrejickab5c73172023-03-01 09:43:24 +0100265 if err != nil {
Filip Tehlar229f5fc2022-08-09 14:44:47 +0000266 return err
267 }
268
269 err = addDelBridge(brName, ns, false)
270 if err != nil {
271 return err
272 }
273
274 return nil
275}
276
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100277func setDevUp(dev, ns string) error {
Filip Tehlar229f5fc2022-08-09 14:44:47 +0000278 return setDevUpDown(dev, ns, true)
279}
280
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100281func setDevDown(dev, ns string) error {
Filip Tehlar229f5fc2022-08-09 14:44:47 +0000282 return setDevUpDown(dev, ns, false)
283}
284
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100285func delLink(ifName string) {
Filip Tehlar229f5fc2022-08-09 14:44:47 +0000286 cmd := exec.Command("ip", "link", "del", ifName)
287 cmd.Run()
288}
289
290func 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 {
adrianvillin7c675472024-02-12 02:44:53 -0500301 return fmt.Errorf("error bringing %s device %s! (cmd: '%s')", dev, op, cmd)
Filip Tehlar229f5fc2022-08-09 14:44:47 +0000302 }
303 return nil
304}
305
Filip Tehlar229f5fc2022-08-09 14:44:47 +0000306func 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 {
adrianvillin7c675472024-02-12 02:44:53 -0500316 return fmt.Errorf("add/del netns failed (cmd: '%s')", cmd)
Filip Tehlar229f5fc2022-08-09 14:44:47 +0000317 }
318 return nil
319}
320
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100321func linkSetNetns(ifName, ns string) error {
Filip Tehlar229f5fc2022-08-09 14:44:47 +0000322 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
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100330func newCommand(s []string, ns string) *exec.Cmd {
Filip Tehlar229f5fc2022-08-09 14:44:47 +0000331 return appendNetns(s, ns)
332}
333
334func 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
347func 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 {
adrianvillin7c675472024-02-12 02:44:53 -0500358 s := fmt.Sprintf("%s %s failed! err: '%s'", op, brName, err)
Filip Tehlar229f5fc2022-08-09 14:44:47 +0000359 return errors.New(s)
360 }
361 return nil
362}
363
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100364func addBridge(brName string, ifs []string, ns string) error {
Filip Tehlar229f5fc2022-08-09 14:44:47 +0000365 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 {
adrianvillin7c675472024-02-12 02:44:53 -0500375 return fmt.Errorf("error adding %s to bridge %s: %s", v, brName, err)
Filip Tehlar229f5fc2022-08-09 14:44:47 +0000376 }
377 }
Maros Ondrejickae7625d02023-02-28 16:55:01 +0100378 err = setDevUp(brName, ns)
Filip Tehlar229f5fc2022-08-09 14:44:47 +0000379 if err != nil {
380 return err
381 }
382 return nil
383}