github.com/AbhinandanKurakure/podman/v3@v3.4.10/libpod/network/cni/config.go (about)

     1  // +build linux
     2  
     3  package cni
     4  
     5  import (
     6  	"net"
     7  	"os"
     8  
     9  	"github.com/containers/podman/v3/libpod/define"
    10  	"github.com/containers/podman/v3/libpod/network/types"
    11  	"github.com/containers/podman/v3/libpod/network/util"
    12  	pkgutil "github.com/containers/podman/v3/pkg/util"
    13  	"github.com/pkg/errors"
    14  	"github.com/sirupsen/logrus"
    15  	"github.com/vishvananda/netlink"
    16  )
    17  
    18  // NetworkCreate will take a partial filled Network and fill the
    19  // missing fields. It creates the Network and returns the full Network.
    20  func (n *cniNetwork) NetworkCreate(net types.Network) (types.Network, error) {
    21  	n.lock.Lock()
    22  	defer n.lock.Unlock()
    23  	err := n.loadNetworks()
    24  	if err != nil {
    25  		return types.Network{}, err
    26  	}
    27  	network, err := n.networkCreate(net, true)
    28  	if err != nil {
    29  		return types.Network{}, err
    30  	}
    31  	// add the new network to the map
    32  	n.networks[network.libpodNet.Name] = network
    33  	return *network.libpodNet, nil
    34  }
    35  
    36  func (n *cniNetwork) networkCreate(net types.Network, writeToDisk bool) (*network, error) {
    37  	// if no driver is set use the default one
    38  	if net.Driver == "" {
    39  		net.Driver = types.DefaultNetworkDriver
    40  	}
    41  
    42  	// FIXME: Should we use a different type for network create without the ID field?
    43  	// the caller is not allowed to set a specific ID
    44  	if net.ID != "" {
    45  		return nil, errors.Wrap(define.ErrInvalidArg, "ID can not be set for network create")
    46  	}
    47  
    48  	if net.Labels == nil {
    49  		net.Labels = map[string]string{}
    50  	}
    51  	if net.Options == nil {
    52  		net.Options = map[string]string{}
    53  	}
    54  	if net.IPAMOptions == nil {
    55  		net.IPAMOptions = map[string]string{}
    56  	}
    57  
    58  	var name string
    59  	var err error
    60  	// validate the name when given
    61  	if net.Name != "" {
    62  		if !define.NameRegex.MatchString(net.Name) {
    63  			return nil, errors.Wrapf(define.RegexError, "network name %s invalid", net.Name)
    64  		}
    65  		if _, ok := n.networks[net.Name]; ok {
    66  			return nil, errors.Wrapf(define.ErrNetworkExists, "network name %s already used", net.Name)
    67  		}
    68  	} else {
    69  		name, err = n.getFreeDeviceName()
    70  		if err != nil {
    71  			return nil, err
    72  		}
    73  		net.Name = name
    74  	}
    75  
    76  	switch net.Driver {
    77  	case types.BridgeNetworkDriver:
    78  		// if the name was created with getFreeDeviceName set the interface to it as well
    79  		if name != "" && net.NetworkInterface == "" {
    80  			net.NetworkInterface = name
    81  		}
    82  		err = n.createBridge(&net)
    83  		if err != nil {
    84  			return nil, err
    85  		}
    86  	case types.MacVLANNetworkDriver:
    87  		err = createMacVLAN(&net)
    88  		if err != nil {
    89  			return nil, err
    90  		}
    91  	default:
    92  		return nil, errors.Wrapf(define.ErrInvalidArg, "unsupported driver %s", net.Driver)
    93  	}
    94  
    95  	for i := range net.Subnets {
    96  		err := validateSubnet(&net.Subnets[i], !net.Internal)
    97  		if err != nil {
    98  			return nil, err
    99  		}
   100  		if util.IsIPv6(net.Subnets[i].Subnet.IP) {
   101  			net.IPv6Enabled = true
   102  		}
   103  	}
   104  
   105  	// generate the network ID
   106  	net.ID = getNetworkIDFromName(net.Name)
   107  
   108  	// FIXME: Should this be a hard error?
   109  	if net.DNSEnabled && net.Internal && hasDNSNamePlugin(n.cniPluginDirs) {
   110  		logrus.Warnf("dnsname and internal networks are incompatible. dnsname plugin not configured for network %s", net.Name)
   111  		net.DNSEnabled = false
   112  	}
   113  
   114  	cniConf, path, err := n.createCNIConfigListFromNetwork(&net, writeToDisk)
   115  	if err != nil {
   116  		return nil, err
   117  	}
   118  	return &network{cniNet: cniConf, libpodNet: &net, filename: path}, nil
   119  }
   120  
   121  // NetworkRemove will remove the Network with the given name or ID.
   122  // It does not ensure that the network is unused.
   123  func (n *cniNetwork) NetworkRemove(nameOrID string) error {
   124  	n.lock.Lock()
   125  	defer n.lock.Unlock()
   126  	err := n.loadNetworks()
   127  	if err != nil {
   128  		return err
   129  	}
   130  
   131  	network, err := n.getNetwork(nameOrID)
   132  	if err != nil {
   133  		return err
   134  	}
   135  
   136  	// Removing the default network is not allowed.
   137  	if network.libpodNet.Name == n.defaultNetwork {
   138  		return errors.Errorf("default network %s cannot be removed", n.defaultNetwork)
   139  	}
   140  
   141  	// Remove the bridge network interface on the host.
   142  	if network.libpodNet.Driver == types.BridgeNetworkDriver {
   143  		link, err := netlink.LinkByName(network.libpodNet.NetworkInterface)
   144  		if err == nil {
   145  			err = netlink.LinkDel(link)
   146  			// only log the error, it is not fatal
   147  			if err != nil {
   148  				logrus.Infof("failed to remove network interface %s: %v", network.libpodNet.NetworkInterface, err)
   149  			}
   150  		}
   151  	}
   152  
   153  	file := network.filename
   154  	delete(n.networks, network.libpodNet.Name)
   155  
   156  	return os.Remove(file)
   157  }
   158  
   159  // NetworkList will return all known Networks. Optionally you can
   160  // supply a list of filter functions. Only if a network matches all
   161  // functions it is returned.
   162  func (n *cniNetwork) NetworkList(filters ...types.FilterFunc) ([]types.Network, error) {
   163  	n.lock.Lock()
   164  	defer n.lock.Unlock()
   165  	err := n.loadNetworks()
   166  	if err != nil {
   167  		return nil, err
   168  	}
   169  
   170  	networks := make([]types.Network, 0, len(n.networks))
   171  outer:
   172  	for _, net := range n.networks {
   173  		for _, filter := range filters {
   174  			// All filters have to match, if one does not match we can skip to the next network.
   175  			if !filter(*net.libpodNet) {
   176  				continue outer
   177  			}
   178  		}
   179  		networks = append(networks, *net.libpodNet)
   180  	}
   181  	return networks, nil
   182  }
   183  
   184  // NetworkInspect will return the Network with the given name or ID.
   185  func (n *cniNetwork) NetworkInspect(nameOrID string) (types.Network, error) {
   186  	n.lock.Lock()
   187  	defer n.lock.Unlock()
   188  	err := n.loadNetworks()
   189  	if err != nil {
   190  		return types.Network{}, err
   191  	}
   192  
   193  	network, err := n.getNetwork(nameOrID)
   194  	if err != nil {
   195  		return types.Network{}, err
   196  	}
   197  	return *network.libpodNet, nil
   198  }
   199  
   200  func createMacVLAN(network *types.Network) error {
   201  	if network.Internal {
   202  		return errors.New("internal is not supported with macvlan")
   203  	}
   204  	if network.NetworkInterface != "" {
   205  		interfaceNames, err := util.GetLiveNetworkNames()
   206  		if err != nil {
   207  			return err
   208  		}
   209  		if !pkgutil.StringInSlice(network.NetworkInterface, interfaceNames) {
   210  			return errors.Errorf("parent interface %s does not exists", network.NetworkInterface)
   211  		}
   212  	}
   213  	if len(network.Subnets) == 0 {
   214  		network.IPAMOptions["driver"] = types.DHCPIPAMDriver
   215  	} else {
   216  		network.IPAMOptions["driver"] = types.HostLocalIPAMDriver
   217  	}
   218  	return nil
   219  }
   220  
   221  func (n *cniNetwork) createBridge(network *types.Network) error {
   222  	if network.NetworkInterface != "" {
   223  		bridges := n.getBridgeInterfaceNames()
   224  		if pkgutil.StringInSlice(network.NetworkInterface, bridges) {
   225  			return errors.Errorf("bridge name %s already in use", network.NetworkInterface)
   226  		}
   227  		if !define.NameRegex.MatchString(network.NetworkInterface) {
   228  			return errors.Wrapf(define.RegexError, "bridge name %s invalid", network.NetworkInterface)
   229  		}
   230  	} else {
   231  		var err error
   232  		network.NetworkInterface, err = n.getFreeDeviceName()
   233  		if err != nil {
   234  			return err
   235  		}
   236  	}
   237  
   238  	if len(network.Subnets) == 0 {
   239  		freeSubnet, err := n.getFreeIPv4NetworkSubnet()
   240  		if err != nil {
   241  			return err
   242  		}
   243  		network.Subnets = append(network.Subnets, *freeSubnet)
   244  	}
   245  	// ipv6 enabled means dual stack, check if we already have
   246  	// a ipv4 or ipv6 subnet and add one if not.
   247  	if network.IPv6Enabled {
   248  		ipv4 := false
   249  		ipv6 := false
   250  		for _, subnet := range network.Subnets {
   251  			if util.IsIPv6(subnet.Subnet.IP) {
   252  				ipv6 = true
   253  			}
   254  			if util.IsIPv4(subnet.Subnet.IP) {
   255  				ipv4 = true
   256  			}
   257  		}
   258  		if !ipv4 {
   259  			freeSubnet, err := n.getFreeIPv4NetworkSubnet()
   260  			if err != nil {
   261  				return err
   262  			}
   263  			network.Subnets = append(network.Subnets, *freeSubnet)
   264  		}
   265  		if !ipv6 {
   266  			freeSubnet, err := n.getFreeIPv6NetworkSubnet()
   267  			if err != nil {
   268  				return err
   269  			}
   270  			network.Subnets = append(network.Subnets, *freeSubnet)
   271  		}
   272  	}
   273  	network.IPAMOptions["driver"] = types.HostLocalIPAMDriver
   274  	return nil
   275  }
   276  
   277  // validateSubnet will validate a given Subnet. It checks if the
   278  // given gateway and lease range are part of this subnet. If the
   279  // gateway is empty and addGateway is true it will get the first
   280  // available ip in the subnet assigned.
   281  func validateSubnet(s *types.Subnet, addGateway bool) error {
   282  	if s == nil {
   283  		return errors.New("subnet is nil")
   284  	}
   285  	// Reparse to ensure subnet is valid.
   286  	// Do not use types.ParseCIDR() because we want the ip to be
   287  	// the network address and not a random ip in the subnet.
   288  	_, net, err := net.ParseCIDR(s.Subnet.String())
   289  	if err != nil {
   290  		return errors.Wrap(err, "subnet invalid")
   291  	}
   292  	s.Subnet = types.IPNet{IPNet: *net}
   293  	if s.Gateway != nil {
   294  		if !s.Subnet.Contains(s.Gateway) {
   295  			return errors.Errorf("gateway %s not in subnet %s", s.Gateway, &s.Subnet)
   296  		}
   297  	} else if addGateway {
   298  		ip, err := util.FirstIPInSubnet(net)
   299  		if err != nil {
   300  			return err
   301  		}
   302  		s.Gateway = ip
   303  	}
   304  	if s.LeaseRange != nil {
   305  		if s.LeaseRange.StartIP != nil && !s.Subnet.Contains(s.LeaseRange.StartIP) {
   306  			return errors.Errorf("lease range start ip %s not in subnet %s", s.LeaseRange.StartIP, &s.Subnet)
   307  		}
   308  		if s.LeaseRange.EndIP != nil && !s.Subnet.Contains(s.LeaseRange.EndIP) {
   309  			return errors.Errorf("lease range end ip %s not in subnet %s", s.LeaseRange.EndIP, &s.Subnet)
   310  		}
   311  	}
   312  	return nil
   313  }