// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved. // syscontainer-tools is licensed under the Mulan PSL v2. // You can use this software according to the terms and conditions of the Mulan PSL v2. // You may obtain a copy of Mulan PSL v2 at: // http://license.coscl.org.cn/MulanPSL2 // THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR // PURPOSE. // See the Mulan PSL v2 for more details. // Description: network interface operation // Author: zhangwei // Create: 2018-01-18 package libnetwork import ( "fmt" "os" "strings" "github.com/sirupsen/logrus" hconfig "isula.org/syscontainer-tools/config" "isula.org/syscontainer-tools/container" "isula.org/syscontainer-tools/libnetwork/drivers" "isula.org/syscontainer-tools/types" ) // AddNic will add a network interface to container, it will update the config for container func AddNic(ctr *container.Container, config *types.InterfaceConf, updateConfigOnly bool) error { if err := ctr.Lock(); err != nil { return err } defer ctr.Unlock() // create config file handler. hConfig, err := hconfig.NewContainerConfig(ctr) if err != nil { return err } defer hConfig.Flush() if err := hConfig.CheckNicNum(); err != nil { return err } if err := hConfig.IsConflictInterface(config); err != nil { return err } if err := hConfig.UpdateNetworkInterface(config, true); err != nil { return err } // don't insert net interface when: // 1. update-config-only flag is set // 2. container isn't running(pid==0) if !updateConfigOnly && ctr.Pid() > 0 && ctr.CheckPidExist() { if err := AddNicToContainer(ctr.NetNsPath(), config); err != nil { // roll back hConfig.UpdateNetworkInterface(config, false) return err } } fmt.Fprintf(os.Stdout, "Add network interface (%s) to container (%s,%s) done\n", config.HostNicName, ctr.Name(), config.CtrNicName) logrus.Infof("Add network interface (%s) to container (%s,%s) done", config.HostNicName, ctr.Name(), config.CtrNicName) return nil } // AddNicToContainer will add a network interface to container only. // It will be called by network-hook func AddNicToContainer(nsPath string, config *types.InterfaceConf) (rErr error) { driver, err := drivers.New(config.Type, drivers.NicOptionCtrNicName(config.CtrNicName), drivers.NicOptionHostNicName(config.HostNicName), drivers.NicOptionNsPath(nsPath), drivers.NicOptionIP(config.IP), drivers.NicOptionMac(config.Mac), drivers.NicOptionMtu(config.Mtu), drivers.NicOptionQlen(config.Qlen), drivers.NicOptionBridge(config.Bridge)) if err != nil { return err } if err := driver.CreateIf(); err != nil { return fmt.Errorf("failed to create interface: %v", err) } // do not need to DeleteIf here, if CreateIf failed, there are no ifs. // JoinAndConfigure is doing cleanup within itself return driver.JoinAndConfigure() } // UpdateNicInContainer will update an existing network interface in container. func UpdateNicInContainer(nsPath string, config *types.InterfaceConf) (rErr error) { driver, err := drivers.New(config.Type, drivers.NicOptionCtrNicName(config.CtrNicName), drivers.NicOptionHostNicName(config.HostNicName), drivers.NicOptionNsPath(nsPath), drivers.NicOptionIP(config.IP), drivers.NicOptionMac(config.Mac), drivers.NicOptionMtu(config.Mtu), drivers.NicOptionQlen(config.Qlen), drivers.NicOptionBridge(config.Bridge)) if err != nil { return err } // Configure is doing cleanup within itself return driver.Configure() } // DelNic will remove a network interface from container and update the config func DelNic(ctr *container.Container, config *types.InterfaceConf) error { if err := ctr.Lock(); err != nil { return err } defer ctr.Unlock() // create config file handler. hConfig, err := hconfig.NewContainerConfig(ctr) if err != nil { return err } defer hConfig.Flush() var newConfig *types.InterfaceConf if newConfig = hConfig.FindInterfaceByName(config); newConfig == nil { return fmt.Errorf("Network interface %s,%s with type %s not exist in container %s", config.HostNicName, config.CtrNicName, config.Type, ctr.Name()) } if err := hConfig.UpdateNetworkInterface(newConfig, false); err != nil { return err } // only work for running container if ctr.Pid() > 0 && ctr.CheckPidExist() { if err := DelNicFromContainer(ctr.NetNsPath(), newConfig); err != nil { if !strings.Contains(err.Error(), "failed to get host link by name") { // roll back hConfig.UpdateNetworkInterface(newConfig, true) return err } logrus.Errorf("Remove network interface error: %s", err) } } fmt.Fprintf(os.Stdout, "Remove network interface (%s) from container (%s,%s) done\n", newConfig.HostNicName, ctr.Name(), newConfig.CtrNicName) logrus.Infof("Remove network interface (%s) from container (%s,%s) done", newConfig.HostNicName, ctr.Name(), newConfig.CtrNicName) return nil } // DelNicFromContainer will remove a network interface from container only func DelNicFromContainer(nsPath string, config *types.InterfaceConf) error { driver, err := drivers.New(config.Type, drivers.NicOptionCtrNicName(config.CtrNicName), drivers.NicOptionHostNicName(config.HostNicName), drivers.NicOptionNsPath(nsPath), drivers.NicOptionIP(config.IP), drivers.NicOptionMac(config.Mac), drivers.NicOptionMtu(config.Mtu), drivers.NicOptionBridge(config.Bridge)) if err != nil { return err } return driver.DeleteIf() } // UpdateNic will reconfigure network interface for a container func UpdateNic(ctr *container.Container, config *types.InterfaceConf, updateConfigOnly bool) error { if err := ctr.Lock(); err != nil { return err } defer ctr.Unlock() hConfig, err := hconfig.NewContainerConfig(ctr) if err != nil { return err } var tmpConfig = new(types.InterfaceConf) tmpConfig.CtrNicName = config.CtrNicName var newConfig *types.InterfaceConf if newConfig = hConfig.FindInterfaceByName(tmpConfig); newConfig == nil { return fmt.Errorf("Network interface %s,%s with type %s not exist in container %s", config.HostNicName, config.CtrNicName, config.Type, ctr.Name()) } if config.IP == "" { tmpConfig.IP = newConfig.IP } else { tmpConfig.IP = config.IP msg := fmt.Sprintf("Update IP address for network interface (%s,%v) done", config.CtrNicName, config.IP) fmt.Fprintln(os.Stdout, msg) logrus.Info(msg) } if config.Mac == "" { tmpConfig.Mac = newConfig.Mac } else { tmpConfig.Mac = config.Mac msg := fmt.Sprintf("Update MAC address for network interface (%s,%v) done", config.CtrNicName, config.Mac) fmt.Fprintln(os.Stdout, msg) logrus.Info(msg) } if config.Bridge == "" { tmpConfig.Bridge = newConfig.Bridge } else { tmpConfig.Bridge = config.Bridge msg := fmt.Sprintf("Update Bridge for network interface (%s,%v) done", config.CtrNicName, config.Bridge) fmt.Fprintln(os.Stdout, msg) logrus.Info(msg) } if config.Mtu == 0 { tmpConfig.Mtu = newConfig.Mtu } else { tmpConfig.Mtu = config.Mtu msg := fmt.Sprintf("Update Mtu for network interface (%s,%v) done", config.CtrNicName, config.Mtu) fmt.Fprintln(os.Stdout, msg) logrus.Info(msg) } // we use qlen < 0 to check if the user has set parameter qlen or not if config.Qlen < 0 { tmpConfig.Qlen = newConfig.Qlen } else { tmpConfig.Qlen = config.Qlen msg := fmt.Sprintf("Update Qlen for network interface (%s,%v)", config.CtrNicName, config.Qlen) fmt.Fprintln(os.Stdout, msg) logrus.Info(msg) } tmpConfig.Type = newConfig.Type tmpConfig.HostNicName = newConfig.HostNicName if hConfig.IsSameInterface(tmpConfig) { logrus.Infof("Network interface in container %s: Identical setting, nothing to change", config.CtrNicName, ctr.Name()) return nil } if err := hConfig.UpdateNetworkInterface(newConfig, false); err != nil { return err } if err := hConfig.IsConflictInterface(tmpConfig); err != nil { if err := hConfig.UpdateNetworkInterface(newConfig, true); err != nil { return err } return err } if !updateConfigOnly && ctr.Pid() > 0 && ctr.CheckPidExist() { if err := UpdateNicInContainer(ctr.NetNsPath(), tmpConfig); err != nil { if err := hConfig.UpdateNetworkInterface(newConfig, true); err != nil { return err } return err } } // update the config file. if err := hConfig.UpdateNetworkInterface(tmpConfig, true); err != nil { return err } hConfig.Flush() logrus.Infof("Network interface %s in container %s update successfully", config.CtrNicName, ctr.Name()) return nil } // ListNic will list all network interfaces in a container func ListNic(ctr *container.Container, filter *types.InterfaceConf) ([]*types.InterfaceConf, error) { if err := ctr.Lock(); err != nil { return nil, err } defer ctr.Unlock() hConfig, err := hconfig.NewContainerConfig(ctr) if err != nil { return nil, err } return hConfig.GetNics(filter), nil }