-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
prevent unpredictable results with network create|remove
due to a lack of "locking" on cni operations, we could get ourselves in trouble when doing rapid creation or removal of networks. added a simple file lock to deal with the collision and because it is not considered a performent path, use of the file lock should be ok. if proven otherwise in the future, some generic shared memory lock should be implemented for libpod and also used here. moved pkog/network to libpod/network because libpod is now being pulled into the package and it has therefore lost its generic nature. this will make it easier to absorb into libpod as we try to make the network closer to core operations. Fixes: #7807 Signed-off-by: baude <[email protected]>
- Loading branch information
Showing
17 changed files
with
249 additions
and
185 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
package network | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"io/ioutil" | ||
"os" | ||
"path/filepath" | ||
|
||
"github.com/containernetworking/cni/pkg/version" | ||
"github.com/containers/podman/v2/libpod" | ||
"github.com/containers/podman/v2/pkg/domain/entities" | ||
"github.com/containers/podman/v2/pkg/util" | ||
"github.com/pkg/errors" | ||
) | ||
|
||
func Create(name string, options entities.NetworkCreateOptions, r *libpod.Runtime) (*entities.NetworkCreateReport, error) { | ||
var fileName string | ||
if err := isSupportedDriver(options.Driver); err != nil { | ||
return nil, err | ||
} | ||
config, err := r.GetConfig() | ||
if err != nil { | ||
return nil, err | ||
} | ||
// Acquire a lock for CNI | ||
l, err := acquireCNILock(filepath.Join(config.Engine.TmpDir, LockFileName)) | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer l.releaseCNILock() | ||
if len(options.MacVLAN) > 0 { | ||
fileName, err = createMacVLAN(r, name, options) | ||
} else { | ||
fileName, err = createBridge(r, name, options) | ||
} | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &entities.NetworkCreateReport{Filename: fileName}, nil | ||
} | ||
|
||
// createBridge creates a CNI network | ||
func createBridge(r *libpod.Runtime, name string, options entities.NetworkCreateOptions) (string, error) { | ||
isGateway := true | ||
ipMasq := true | ||
subnet := &options.Subnet | ||
ipRange := options.Range | ||
runtimeConfig, err := r.GetConfig() | ||
if err != nil { | ||
return "", err | ||
} | ||
// if range is provided, make sure it is "in" network | ||
if subnet.IP != nil { | ||
// if network is provided, does it conflict with existing CNI or live networks | ||
err = ValidateUserNetworkIsAvailable(runtimeConfig, subnet) | ||
} else { | ||
// if no network is provided, figure out network | ||
subnet, err = GetFreeNetwork(runtimeConfig) | ||
} | ||
if err != nil { | ||
return "", err | ||
} | ||
gateway := options.Gateway | ||
if gateway == nil { | ||
// if no gateway is provided, provide it as first ip of network | ||
gateway = CalcGatewayIP(subnet) | ||
} | ||
// if network is provided and if gateway is provided, make sure it is "in" network | ||
if options.Subnet.IP != nil && options.Gateway != nil { | ||
if !subnet.Contains(gateway) { | ||
return "", errors.Errorf("gateway %s is not in valid for subnet %s", gateway.String(), subnet.String()) | ||
} | ||
} | ||
if options.Internal { | ||
isGateway = false | ||
ipMasq = false | ||
} | ||
|
||
// if a range is given, we need to ensure it is "in" the network range. | ||
if options.Range.IP != nil { | ||
if options.Subnet.IP == nil { | ||
return "", errors.New("you must define a subnet range to define an ip-range") | ||
} | ||
firstIP, err := FirstIPInSubnet(&options.Range) | ||
if err != nil { | ||
return "", err | ||
} | ||
lastIP, err := LastIPInSubnet(&options.Range) | ||
if err != nil { | ||
return "", err | ||
} | ||
if !subnet.Contains(firstIP) || !subnet.Contains(lastIP) { | ||
return "", errors.Errorf("the ip range %s does not fall within the subnet range %s", options.Range.String(), subnet.String()) | ||
} | ||
} | ||
bridgeDeviceName, err := GetFreeDeviceName(runtimeConfig) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
if len(name) > 0 { | ||
netNames, err := GetNetworkNamesFromFileSystem(runtimeConfig) | ||
if err != nil { | ||
return "", err | ||
} | ||
if util.StringInSlice(name, netNames) { | ||
return "", errors.Errorf("the network name %s is already used", name) | ||
} | ||
} else { | ||
// If no name is given, we give the name of the bridge device | ||
name = bridgeDeviceName | ||
} | ||
|
||
ncList := NewNcList(name, version.Current()) | ||
var plugins []CNIPlugins | ||
var routes []IPAMRoute | ||
|
||
defaultRoute, err := NewIPAMDefaultRoute(IsIPv6(subnet.IP)) | ||
if err != nil { | ||
return "", err | ||
} | ||
routes = append(routes, defaultRoute) | ||
ipamConfig, err := NewIPAMHostLocalConf(subnet, routes, ipRange, gateway) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
// TODO need to iron out the role of isDefaultGW and IPMasq | ||
bridge := NewHostLocalBridge(bridgeDeviceName, isGateway, false, ipMasq, ipamConfig) | ||
plugins = append(plugins, bridge) | ||
plugins = append(plugins, NewPortMapPlugin()) | ||
plugins = append(plugins, NewFirewallPlugin()) | ||
// if we find the dnsname plugin, we add configuration for it | ||
if HasDNSNamePlugin(runtimeConfig.Network.CNIPluginDirs) && !options.DisableDNS { | ||
// Note: in the future we might like to allow for dynamic domain names | ||
plugins = append(plugins, NewDNSNamePlugin(DefaultPodmanDomainName)) | ||
} | ||
ncList["plugins"] = plugins | ||
b, err := json.MarshalIndent(ncList, "", " ") | ||
if err != nil { | ||
return "", err | ||
} | ||
if err := os.MkdirAll(GetCNIConfDir(runtimeConfig), 0755); err != nil { | ||
return "", err | ||
} | ||
cniPathName := filepath.Join(GetCNIConfDir(runtimeConfig), fmt.Sprintf("%s.conflist", name)) | ||
err = ioutil.WriteFile(cniPathName, b, 0644) | ||
return cniPathName, err | ||
} | ||
|
||
func createMacVLAN(r *libpod.Runtime, name string, options entities.NetworkCreateOptions) (string, error) { | ||
var ( | ||
plugins []CNIPlugins | ||
) | ||
liveNetNames, err := GetLiveNetworkNames() | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
config, err := r.GetConfig() | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
// Make sure the host-device exists | ||
if !util.StringInSlice(options.MacVLAN, liveNetNames) { | ||
return "", errors.Errorf("failed to find network interface %q", options.MacVLAN) | ||
} | ||
if len(name) > 0 { | ||
netNames, err := GetNetworkNamesFromFileSystem(config) | ||
if err != nil { | ||
return "", err | ||
} | ||
if util.StringInSlice(name, netNames) { | ||
return "", errors.Errorf("the network name %s is already used", name) | ||
} | ||
} else { | ||
name, err = GetFreeDeviceName(config) | ||
if err != nil { | ||
return "", err | ||
} | ||
} | ||
ncList := NewNcList(name, version.Current()) | ||
macvlan := NewMacVLANPlugin(options.MacVLAN) | ||
plugins = append(plugins, macvlan) | ||
ncList["plugins"] = plugins | ||
b, err := json.MarshalIndent(ncList, "", " ") | ||
if err != nil { | ||
return "", err | ||
} | ||
cniPathName := filepath.Join(GetCNIConfDir(config), fmt.Sprintf("%s.conflist", name)) | ||
err = ioutil.WriteFile(cniPathName, b, 0644) | ||
return cniPathName, err | ||
} |
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package network | ||
|
||
import ( | ||
"github.com/containers/storage" | ||
) | ||
|
||
// acquireCNILock gets a lock that should be used in create and | ||
// delete cases to avoid unwanted collisions in network names. | ||
// TODO this uses a file lock and should be converted to shared memory | ||
// when we have a more general shared memory lock in libpod | ||
func acquireCNILock(lockPath string) (*CNILock, error) { | ||
l, err := storage.GetLockfile(lockPath) | ||
if err != nil { | ||
return nil, err | ||
} | ||
l.Lock() | ||
cnilock := CNILock{ | ||
Locker: l, | ||
} | ||
return &cnilock, nil | ||
} | ||
|
||
// ReleaseCNILock unlocks the previously held lock | ||
func (l *CNILock) releaseCNILock() { | ||
l.Unlock() | ||
} |
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.