github.com/AbhinandanKurakure/podman/v3@v3.4.10/libpod/network/cni/network.go (about) 1 // +build linux 2 3 package cni 4 5 import ( 6 "context" 7 "crypto/sha256" 8 "encoding/hex" 9 "fmt" 10 "net" 11 "os" 12 "strings" 13 14 "github.com/containernetworking/cni/libcni" 15 "github.com/containers/podman/v3/libpod/define" 16 "github.com/containers/podman/v3/libpod/network/types" 17 "github.com/containers/podman/v3/libpod/network/util" 18 pkgutil "github.com/containers/podman/v3/pkg/util" 19 "github.com/containers/storage/pkg/lockfile" 20 "github.com/pkg/errors" 21 "github.com/sirupsen/logrus" 22 ) 23 24 type cniNetwork struct { 25 // cniConfigDir is directory where the cni config files are stored. 26 cniConfigDir string 27 // cniPluginDirs is a list of directories where cni should look for the plugins. 28 cniPluginDirs []string 29 30 cniConf *libcni.CNIConfig 31 32 // defaultNetwork is the name for the default network. 33 defaultNetwork string 34 // defaultSubnet is the default subnet for the default network. 35 defaultSubnet types.IPNet 36 37 // isMachine describes whenever podman runs in a podman machine environment. 38 isMachine bool 39 40 // lock is a internal lock for critical operations 41 lock lockfile.Locker 42 43 // networks is a map with loaded networks, the key is the network name 44 networks map[string]*network 45 } 46 47 type network struct { 48 // filename is the full path to the cni config file on disk 49 filename string 50 libpodNet *types.Network 51 cniNet *libcni.NetworkConfigList 52 } 53 54 type InitConfig struct { 55 // CNIConfigDir is directory where the cni config files are stored. 56 CNIConfigDir string 57 // CNIPluginDirs is a list of directories where cni should look for the plugins. 58 CNIPluginDirs []string 59 60 // DefaultNetwork is the name for the default network. 61 DefaultNetwork string 62 // DefaultSubnet is the default subnet for the default network. 63 DefaultSubnet string 64 65 // IsMachine describes whenever podman runs in a podman machine environment. 66 IsMachine bool 67 68 // LockFile is the path to lock file. 69 LockFile string 70 } 71 72 // NewCNINetworkInterface creates the ContainerNetwork interface for the CNI backend. 73 // Note: The networks are not loaded from disk until a method is called. 74 func NewCNINetworkInterface(conf InitConfig) (types.ContainerNetwork, error) { 75 // TODO: consider using a shared memory lock 76 lock, err := lockfile.GetLockfile(conf.LockFile) 77 if err != nil { 78 return nil, err 79 } 80 81 defaultNetworkName := conf.DefaultNetwork 82 if defaultNetworkName == "" { 83 defaultNetworkName = types.DefaultNetworkName 84 } 85 86 defaultSubnet := conf.DefaultSubnet 87 if defaultSubnet == "" { 88 defaultSubnet = types.DefaultSubnet 89 } 90 defaultNet, err := types.ParseCIDR(defaultSubnet) 91 if err != nil { 92 return nil, errors.Wrap(err, "failed to parse default subnet") 93 } 94 95 cni := libcni.NewCNIConfig(conf.CNIPluginDirs, &cniExec{}) 96 n := &cniNetwork{ 97 cniConfigDir: conf.CNIConfigDir, 98 cniPluginDirs: conf.CNIPluginDirs, 99 cniConf: cni, 100 defaultNetwork: defaultNetworkName, 101 defaultSubnet: defaultNet, 102 isMachine: conf.IsMachine, 103 lock: lock, 104 } 105 106 return n, nil 107 } 108 109 func (n *cniNetwork) loadNetworks() error { 110 // skip loading networks if they are already loaded 111 if n.networks != nil { 112 return nil 113 } 114 // FIXME: do we have to support other file types as well, e.g. .conf? 115 files, err := libcni.ConfFiles(n.cniConfigDir, []string{".conflist"}) 116 if err != nil { 117 return err 118 } 119 networks := make(map[string]*network, len(files)) 120 for _, file := range files { 121 conf, err := libcni.ConfListFromFile(file) 122 if err != nil { 123 // do not log ENOENT errors 124 if !os.IsNotExist(err) { 125 logrus.Warnf("Error loading CNI config file %s: %v", file, err) 126 } 127 continue 128 } 129 130 if !define.NameRegex.MatchString(conf.Name) { 131 logrus.Warnf("CNI config list %s has invalid name, skipping: %v", file, define.RegexError) 132 continue 133 } 134 135 if _, err := n.cniConf.ValidateNetworkList(context.Background(), conf); err != nil { 136 logrus.Warnf("Error validating CNI config file %s: %v", file, err) 137 continue 138 } 139 140 if val, ok := networks[conf.Name]; ok { 141 logrus.Warnf("CNI config list %s has the same network name as %s, skipping", file, val.filename) 142 continue 143 } 144 145 net, err := createNetworkFromCNIConfigList(conf, file) 146 if err != nil { 147 logrus.Errorf("CNI config list %s could not be converted to a libpod config, skipping: %v", file, err) 148 continue 149 } 150 logrus.Tracef("Successfully loaded network %s: %v", net.Name, net) 151 networkInfo := network{ 152 filename: file, 153 cniNet: conf, 154 libpodNet: net, 155 } 156 networks[net.Name] = &networkInfo 157 } 158 159 // create the default network in memory if it did not exists on disk 160 if networks[n.defaultNetwork] == nil { 161 networkInfo, err := n.createDefaultNetwork() 162 if err != nil { 163 return errors.Wrapf(err, "failed to create default network %s", n.defaultNetwork) 164 } 165 networks[n.defaultNetwork] = networkInfo 166 } 167 168 logrus.Debugf("Successfully loaded %d networks", len(networks)) 169 n.networks = networks 170 return nil 171 } 172 173 func (n *cniNetwork) createDefaultNetwork() (*network, error) { 174 net := types.Network{ 175 Name: n.defaultNetwork, 176 NetworkInterface: "cni-podman0", 177 Driver: types.BridgeNetworkDriver, 178 Subnets: []types.Subnet{ 179 {Subnet: n.defaultSubnet}, 180 }, 181 } 182 return n.networkCreate(net, false) 183 } 184 185 // getNetwork will lookup a network by name or ID. It returns an 186 // error when no network was found or when more than one network 187 // with the given (partial) ID exists. 188 // getNetwork will read from the networks map, therefore the caller 189 // must ensure that n.lock is locked before using it. 190 func (n *cniNetwork) getNetwork(nameOrID string) (*network, error) { 191 // fast path check the map key, this will only work for names 192 if val, ok := n.networks[nameOrID]; ok { 193 return val, nil 194 } 195 // If there was no match we might got a full or partial ID. 196 var net *network 197 for _, val := range n.networks { 198 // This should not happen because we already looked up the map by name but check anyway. 199 if val.libpodNet.Name == nameOrID { 200 return val, nil 201 } 202 203 if strings.HasPrefix(val.libpodNet.ID, nameOrID) { 204 if net != nil { 205 return nil, errors.Errorf("more than one result for network ID %s", nameOrID) 206 } 207 net = val 208 } 209 } 210 if net != nil { 211 return net, nil 212 } 213 return nil, errors.Wrapf(define.ErrNoSuchNetwork, "unable to find network with name or ID %s", nameOrID) 214 } 215 216 // getNetworkIDFromName creates a network ID from the name. It is just the 217 // sha256 hash so it is not safe but it should be safe enough for our use case. 218 func getNetworkIDFromName(name string) string { 219 hash := sha256.Sum256([]byte(name)) 220 return hex.EncodeToString(hash[:]) 221 } 222 223 // getFreeIPv6NetworkSubnet returns a unused ipv4 subnet 224 func (n *cniNetwork) getFreeIPv4NetworkSubnet() (*types.Subnet, error) { 225 networks, err := n.getUsedSubnets() 226 if err != nil { 227 return nil, err 228 } 229 230 // the default podman network is 10.88.0.0/16 231 // start locking for free /24 networks 232 network := &net.IPNet{ 233 IP: net.IP{10, 89, 0, 0}, 234 Mask: net.IPMask{255, 255, 255, 0}, 235 } 236 237 // TODO: make sure to not use public subnets 238 for { 239 if intersectsConfig := util.NetworkIntersectsWithNetworks(network, networks); !intersectsConfig { 240 logrus.Debugf("found free ipv4 network subnet %s", network.String()) 241 return &types.Subnet{ 242 Subnet: types.IPNet{IPNet: *network}, 243 }, nil 244 } 245 network, err = util.NextSubnet(network) 246 if err != nil { 247 return nil, err 248 } 249 } 250 } 251 252 // getFreeIPv6NetworkSubnet returns a unused ipv6 subnet 253 func (n *cniNetwork) getFreeIPv6NetworkSubnet() (*types.Subnet, error) { 254 networks, err := n.getUsedSubnets() 255 if err != nil { 256 return nil, err 257 } 258 259 // FIXME: Is 10000 fine as limit? We should prevent an endless loop. 260 for i := 0; i < 10000; i++ { 261 // RFC4193: Choose the ipv6 subnet random and NOT sequentially. 262 network, err := util.GetRandomIPv6Subnet() 263 if err != nil { 264 return nil, err 265 } 266 if intersectsConfig := util.NetworkIntersectsWithNetworks(&network, networks); !intersectsConfig { 267 logrus.Debugf("found free ipv6 network subnet %s", network.String()) 268 return &types.Subnet{ 269 Subnet: types.IPNet{IPNet: network}, 270 }, nil 271 } 272 } 273 return nil, errors.New("failed to get random ipv6 subnet") 274 } 275 276 // getUsedSubnets returns a list of all used subnets by network 277 // configs and interfaces on the host. 278 func (n *cniNetwork) getUsedSubnets() ([]*net.IPNet, error) { 279 // first, load all used subnets from network configs 280 subnets := make([]*net.IPNet, 0, len(n.networks)) 281 for _, val := range n.networks { 282 for _, subnet := range val.libpodNet.Subnets { 283 // nolint:exportloopref 284 subnets = append(subnets, &subnet.Subnet.IPNet) 285 } 286 } 287 // second, load networks from the current system 288 liveSubnets, err := util.GetLiveNetworkSubnets() 289 if err != nil { 290 return nil, err 291 } 292 return append(subnets, liveSubnets...), nil 293 } 294 295 // getFreeDeviceName returns a free device name which can 296 // be used for new configs as name and bridge interface name 297 func (n *cniNetwork) getFreeDeviceName() (string, error) { 298 bridgeNames := n.getBridgeInterfaceNames() 299 netNames := n.getUsedNetworkNames() 300 liveInterfaces, err := util.GetLiveNetworkNames() 301 if err != nil { 302 return "", nil 303 } 304 names := make([]string, 0, len(bridgeNames)+len(netNames)+len(liveInterfaces)) 305 names = append(names, bridgeNames...) 306 names = append(names, netNames...) 307 names = append(names, liveInterfaces...) 308 // FIXME: Is a limit fine? 309 // Start by 1, 0 is reserved for the default network 310 for i := 1; i < 1000000; i++ { 311 deviceName := fmt.Sprintf("%s%d", cniDeviceName, i) 312 if !pkgutil.StringInSlice(deviceName, names) { 313 logrus.Debugf("found free device name %s", deviceName) 314 return deviceName, nil 315 } 316 } 317 return "", errors.New("could not find free device name, to many iterations") 318 } 319 320 // getUsedNetworkNames returns all network names already used 321 // by network configs 322 func (n *cniNetwork) getUsedNetworkNames() []string { 323 names := make([]string, 0, len(n.networks)) 324 for _, val := range n.networks { 325 names = append(names, val.libpodNet.Name) 326 } 327 return names 328 } 329 330 // getUsedNetworkNames returns all bridge device names already used 331 // by network configs 332 func (n *cniNetwork) getBridgeInterfaceNames() []string { 333 names := make([]string, 0, len(n.networks)) 334 for _, val := range n.networks { 335 if val.libpodNet.Driver == types.BridgeNetworkDriver { 336 names = append(names, val.libpodNet.NetworkInterface) 337 } 338 } 339 return names 340 }