diff options
Diffstat (limited to 'extras/hs-test/infra/netconfig.go')
-rw-r--r-- | extras/hs-test/infra/netconfig.go | 383 |
1 files changed, 383 insertions, 0 deletions
diff --git a/extras/hs-test/infra/netconfig.go b/extras/hs-test/infra/netconfig.go new file mode 100644 index 00000000000..3f3d3e3e84c --- /dev/null +++ b/extras/hs-test/infra/netconfig.go @@ -0,0 +1,383 @@ +package hst + +import ( + "errors" + "fmt" + "os/exec" + "strings" + + "go.fd.io/govpp/binapi/ethernet_types" + "go.fd.io/govpp/binapi/interface_types" + "go.fd.io/govpp/binapi/ip_types" +) + +type ( + Cmd = exec.Cmd + MacAddress = ethernet_types.MacAddress + AddressWithPrefix = ip_types.AddressWithPrefix + IP4AddressWithPrefix = ip_types.IP4AddressWithPrefix + InterfaceIndex = interface_types.InterfaceIndex + + NetConfig interface { + configure() error + unconfigure() + Name() string + Type() string + } + + NetConfigBase struct { + name string + category string // what else to call this when `type` is reserved? + } + + NetInterface struct { + NetConfigBase + Ip4AddrAllocator *Ip4AddressAllocator + Ip4Address string + Index InterfaceIndex + HwAddress MacAddress + NetworkNamespace string + NetworkNumber int + Peer *NetInterface + } + + NetworkNamespace struct { + NetConfigBase + } + + NetworkBridge struct { + NetConfigBase + NetworkNamespace string + Interfaces []string + } +) + +const ( + NetNs string = "netns" + Veth string = "veth" + Tap string = "tap" + Bridge string = "bridge" +) + +type InterfaceAdder func(n *NetInterface) *Cmd + +var ( + ipCommandMap = map[string]InterfaceAdder{ + Veth: func(n *NetInterface) *Cmd { + return exec.Command("ip", "link", "add", n.name, "type", "veth", "peer", "name", n.Peer.name) + }, + Tap: func(n *NetInterface) *Cmd { + return exec.Command("ip", "tuntap", "add", n.name, "mode", "tap") + }, + } +) + +func newNetworkInterface(cfg NetDevConfig, a *Ip4AddressAllocator) (*NetInterface, error) { + var newInterface *NetInterface = &NetInterface{} + var err error + newInterface.Ip4AddrAllocator = a + newInterface.name = cfg["name"].(string) + newInterface.NetworkNumber = DEFAULT_NETWORK_NUM + + if interfaceType, ok := cfg["type"]; ok { + newInterface.category = interfaceType.(string) + } + + if presetHwAddress, ok := cfg["preset-hw-address"]; ok { + newInterface.HwAddress, err = ethernet_types.ParseMacAddress(presetHwAddress.(string)) + if err != nil { + return &NetInterface{}, err + } + } + + if netns, ok := cfg["netns"]; ok { + newInterface.NetworkNamespace = netns.(string) + } + + if ip, ok := cfg["ip4"]; ok { + if n, ok := ip.(NetDevConfig)["network"]; ok { + newInterface.NetworkNumber = n.(int) + } + newInterface.Ip4Address, err = newInterface.Ip4AddrAllocator.NewIp4InterfaceAddress( + newInterface.NetworkNumber, + ) + if err != nil { + return &NetInterface{}, err + } + } + + if _, ok := cfg["peer"]; !ok { + return newInterface, nil + } + + peer := cfg["peer"].(NetDevConfig) + + if newInterface.Peer, err = newNetworkInterface(peer, a); err != nil { + return &NetInterface{}, err + } + + return newInterface, nil +} + +func (n *NetInterface) configureUpState() error { + err := setDevUp(n.Name(), "") + if err != nil { + return fmt.Errorf("set link up failed: %v", err) + } + return nil +} + +func (n *NetInterface) configureNetworkNamespace() error { + if n.NetworkNamespace != "" { + err := linkSetNetns(n.name, n.NetworkNamespace) + if err != nil { + return err + } + } + return nil +} + +func (n *NetInterface) configureAddress() error { + if n.Ip4Address != "" { + if err := addAddress( + n.Name(), + n.Ip4Address, + n.NetworkNamespace, + ); err != nil { + return err + } + + } + return nil +} + +func (n *NetInterface) configure() error { + cmd := ipCommandMap[n.Type()](n) + _, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("creating interface '%v' failed: %v", n.Name(), err) + } + + if err := n.configureUpState(); err != nil { + return err + } + + if err := n.configureNetworkNamespace(); err != nil { + return err + } + + if err := n.configureAddress(); err != nil { + return err + } + + if n.Peer != nil && n.Peer.name != "" { + if err := n.Peer.configureUpState(); err != nil { + return err + } + + if err := n.Peer.configureNetworkNamespace(); err != nil { + return err + } + + if err := n.Peer.configureAddress(); err != nil { + return err + } + } + + return nil +} + +func (n *NetInterface) unconfigure() { + delLink(n.name) +} + +func (n *NetInterface) Name() string { + return n.name +} + +func (n *NetInterface) Type() string { + return n.category +} + +func (n *NetInterface) AddressWithPrefix() AddressWithPrefix { + address, _ := ip_types.ParseAddressWithPrefix(n.Ip4Address) + return address +} + +func (n *NetInterface) Ip4AddressWithPrefix() IP4AddressWithPrefix { + ip4Prefix, _ := ip_types.ParseIP4Prefix(n.Ip4Address) + Ip4AddressWithPrefix := ip_types.IP4AddressWithPrefix(ip4Prefix) + return Ip4AddressWithPrefix +} + +func (n *NetInterface) Ip4AddressString() string { + return strings.Split(n.Ip4Address, "/")[0] +} + +func (b *NetConfigBase) Name() string { + return b.name +} + +func (b *NetConfigBase) Type() string { + return b.category +} + +func newNetNamespace(cfg NetDevConfig) (NetworkNamespace, error) { + var networkNamespace NetworkNamespace + networkNamespace.name = cfg["name"].(string) + networkNamespace.category = NetNs + return networkNamespace, nil +} + +func (ns *NetworkNamespace) configure() error { + return addDelNetns(ns.name, true) +} + +func (ns *NetworkNamespace) unconfigure() { + addDelNetns(ns.name, false) +} + +func newBridge(cfg NetDevConfig) (NetworkBridge, error) { + var bridge NetworkBridge + bridge.name = cfg["name"].(string) + bridge.category = Bridge + for _, v := range cfg["interfaces"].([]interface{}) { + bridge.Interfaces = append(bridge.Interfaces, v.(string)) + } + + bridge.NetworkNamespace = "" + if netns, ok := cfg["netns"]; ok { + bridge.NetworkNamespace = netns.(string) + } + return bridge, nil +} + +func (b *NetworkBridge) configure() error { + return addBridge(b.name, b.Interfaces, b.NetworkNamespace) +} + +func (b *NetworkBridge) unconfigure() { + delBridge(b.name, b.NetworkNamespace) +} + +func delBridge(brName, ns string) error { + err := setDevDown(brName, ns) + if err != nil { + return err + } + + err = addDelBridge(brName, ns, false) + if err != nil { + return err + } + + return nil +} + +func setDevUp(dev, ns string) error { + return setDevUpDown(dev, ns, true) +} + +func setDevDown(dev, ns string) error { + return setDevUpDown(dev, ns, false) +} + +func delLink(ifName string) { + cmd := exec.Command("ip", "link", "del", ifName) + cmd.Run() +} + +func setDevUpDown(dev, ns string, isUp bool) error { + var op string + if isUp { + op = "up" + } else { + op = "down" + } + c := []string{"ip", "link", "set", "dev", dev, op} + cmd := appendNetns(c, ns) + err := cmd.Run() + if err != nil { + return fmt.Errorf("error bringing %s device %s! (cmd: '%s')", dev, op, cmd) + } + return nil +} + +func addDelNetns(name string, isAdd bool) error { + var op string + if isAdd { + op = "add" + } else { + op = "del" + } + cmd := exec.Command("ip", "netns", op, name) + _, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("add/del netns failed (cmd: '%s')", cmd) + } + return nil +} + +func linkSetNetns(ifName, ns string) error { + cmd := exec.Command("ip", "link", "set", "dev", ifName, "up", "netns", ns) + err := cmd.Run() + if err != nil { + return fmt.Errorf("error setting device '%s' to netns '%s: %v", ifName, ns, err) + } + return nil +} + +func newCommand(s []string, ns string) *exec.Cmd { + return appendNetns(s, ns) +} + +func appendNetns(s []string, ns string) *exec.Cmd { + var cmd *exec.Cmd + if ns == "" { + // use default namespace + cmd = exec.Command(s[0], s[1:]...) + } else { + var args = []string{"netns", "exec", ns} + args = append(args, s[:]...) + cmd = exec.Command("ip", args...) + } + return cmd +} + +func addDelBridge(brName, ns string, isAdd bool) error { + var op string + if isAdd { + op = "addbr" + } else { + op = "delbr" + } + var c = []string{"brctl", op, brName} + cmd := appendNetns(c, ns) + err := cmd.Run() + if err != nil { + s := fmt.Sprintf("%s %s failed! err: '%s'", op, brName, err) + return errors.New(s) + } + return nil +} + +func addBridge(brName string, ifs []string, ns string) error { + err := addDelBridge(brName, ns, true) + if err != nil { + return err + } + + for _, v := range ifs { + c := []string{"brctl", "addif", brName, v} + cmd := appendNetns(c, ns) + err = cmd.Run() + if err != nil { + return fmt.Errorf("error adding %s to bridge %s: %s", v, brName, err) + } + } + err = setDevUp(brName, ns) + if err != nil { + return err + } + return nil +} |