Skip to content

Commit

Permalink
Merge pull request #967 from Luap99/ipam-none
Browse files Browse the repository at this point in the history
libnetwork: allow new none ipam driver
  • Loading branch information
openshift-merge-robot authored Mar 29, 2022
2 parents 288b988 + 5d02f0e commit 5816258
Show file tree
Hide file tree
Showing 12 changed files with 583 additions and 148 deletions.
234 changes: 128 additions & 106 deletions libnetwork/cni/cni_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,76 +128,76 @@ func findPluginByName(plugins []*libcni.NetworkConfig, name string) bool {
// convertIPAMConfToNetwork converts A cni IPAMConfig to libpod network subnets.
// It returns an array of subnets and an extra bool if dhcp is configured.
func convertIPAMConfToNetwork(network *types.Network, ipam *ipamConfig, confPath string) error {
if ipam.PluginType == types.DHCPIPAMDriver {
switch ipam.PluginType {
case "":
network.IPAMOptions[types.Driver] = types.NoneIPAMDriver
case types.DHCPIPAMDriver:
network.IPAMOptions[types.Driver] = types.DHCPIPAMDriver
return nil
}

if ipam.PluginType != types.HostLocalIPAMDriver {
// This is not an error. While we only support certain ipam drivers, we
// cannot make it fail for unsupported ones. CNI is still able to use them,
// just our translation logic cannot convert this into a Network.
// For the same reason this is not warning, it would just be annoying for
// everyone using a unknown ipam driver.
logrus.Infof("unsupported ipam plugin %q in %s", ipam.PluginType, confPath)
return nil
}

network.IPAMOptions[types.Driver] = types.HostLocalIPAMDriver
for _, r := range ipam.Ranges {
for _, ipam := range r {
s := types.Subnet{}

// Do not use types.ParseCIDR() because we want the ip to be
// the network address and not a random ip in the sub.
_, sub, err := net.ParseCIDR(ipam.Subnet)
if err != nil {
return err
}
s.Subnet = types.IPNet{IPNet: *sub}

// gateway
var gateway net.IP
if ipam.Gateway != "" {
gateway = net.ParseIP(ipam.Gateway)
if gateway == nil {
return errors.Errorf("failed to parse gateway ip %s", ipam.Gateway)
}
// convert to 4 byte if ipv4
util.NormalizeIP(&gateway)
} else if !network.Internal {
// only add a gateway address if the network is not internal
gateway, err = util.FirstIPInSubnet(sub)
case types.HostLocalIPAMDriver:
network.IPAMOptions[types.Driver] = types.HostLocalIPAMDriver
for _, r := range ipam.Ranges {
for _, ipam := range r {
s := types.Subnet{}

// Do not use types.ParseCIDR() because we want the ip to be
// the network address and not a random ip in the sub.
_, sub, err := net.ParseCIDR(ipam.Subnet)
if err != nil {
return errors.Errorf("failed to get first ip in subnet %s", sub.String())
return err
}
}
s.Gateway = gateway

var rangeStart net.IP
var rangeEnd net.IP
if ipam.RangeStart != "" {
rangeStart = net.ParseIP(ipam.RangeStart)
if rangeStart == nil {
return errors.Errorf("failed to parse range start ip %s", ipam.RangeStart)
s.Subnet = types.IPNet{IPNet: *sub}

// gateway
var gateway net.IP
if ipam.Gateway != "" {
gateway = net.ParseIP(ipam.Gateway)
if gateway == nil {
return errors.Errorf("failed to parse gateway ip %s", ipam.Gateway)
}
// convert to 4 byte if ipv4
util.NormalizeIP(&gateway)
} else if !network.Internal {
// only add a gateway address if the network is not internal
gateway, err = util.FirstIPInSubnet(sub)
if err != nil {
return errors.Errorf("failed to get first ip in subnet %s", sub.String())
}
}
}
if ipam.RangeEnd != "" {
rangeEnd = net.ParseIP(ipam.RangeEnd)
if rangeEnd == nil {
return errors.Errorf("failed to parse range end ip %s", ipam.RangeEnd)
s.Gateway = gateway

var rangeStart net.IP
var rangeEnd net.IP
if ipam.RangeStart != "" {
rangeStart = net.ParseIP(ipam.RangeStart)
if rangeStart == nil {
return errors.Errorf("failed to parse range start ip %s", ipam.RangeStart)
}
}
if ipam.RangeEnd != "" {
rangeEnd = net.ParseIP(ipam.RangeEnd)
if rangeEnd == nil {
return errors.Errorf("failed to parse range end ip %s", ipam.RangeEnd)
}
}
if rangeStart != nil || rangeEnd != nil {
s.LeaseRange = &types.LeaseRange{}
s.LeaseRange.StartIP = rangeStart
s.LeaseRange.EndIP = rangeEnd
}
if util.IsIPv6(s.Subnet.IP) {
network.IPv6Enabled = true
}
network.Subnets = append(network.Subnets, s)
}
if rangeStart != nil || rangeEnd != nil {
s.LeaseRange = &types.LeaseRange{}
s.LeaseRange.StartIP = rangeStart
s.LeaseRange.EndIP = rangeEnd
}
if util.IsIPv6(s.Subnet.IP) {
network.IPv6Enabled = true
}
network.Subnets = append(network.Subnets, s)
}
default:
// This is not an error. While we only support certain ipam drivers, we
// cannot make it fail for unsupported ones. CNI is still able to use them,
// just our translation logic cannot convert this into a Network.
// For the same reason this is not warning, it would just be annoying for
// everyone using a unknown ipam driver.
logrus.Infof("unsupported ipam plugin %q in %s", ipam.PluginType, confPath)
network.IPAMOptions[types.Driver] = ipam.PluginType
}
return nil
}
Expand Down Expand Up @@ -225,10 +225,13 @@ func (n *cniNetwork) createCNIConfigListFromNetwork(network *types.Network, writ
var (
routes []ipamRoute
ipamRanges [][]ipamLocalHostRangeConf
ipamConf ipamConfig
ipamConf *ipamConfig
err error
)
if len(network.Subnets) > 0 {

ipamDriver := network.IPAMOptions[types.Driver]
switch ipamDriver {
case types.HostLocalIPAMDriver:
defIpv4Route := false
defIpv6Route := false
for _, subnet := range network.Subnets {
Expand Down Expand Up @@ -257,46 +260,20 @@ func (n *cniNetwork) createCNIConfigListFromNetwork(network *types.Network, writ
routes = append(routes, route)
}
}
ipamConf = newIPAMHostLocalConf(routes, ipamRanges)
} else {
ipamConf = ipamConfig{PluginType: "dhcp"}
}
conf := newIPAMHostLocalConf(routes, ipamRanges)
ipamConf = &conf
case types.DHCPIPAMDriver:
ipamConf = &ipamConfig{PluginType: "dhcp"}

vlan := 0
mtu := 0
vlanPluginMode := ""
for k, v := range network.Options {
switch k {
case "mtu":
mtu, err = internalutil.ParseMTU(v)
if err != nil {
return nil, "", err
}

case "vlan":
vlan, err = internalutil.ParseVlan(v)
if err != nil {
return nil, "", err
}

case "mode":
switch network.Driver {
case types.MacVLANNetworkDriver:
if !pkgutil.StringInSlice(v, types.ValidMacVLANModes) {
return nil, "", errors.Errorf("unknown macvlan mode %q", v)
}
case types.IPVLANNetworkDriver:
if !pkgutil.StringInSlice(v, types.ValidIPVLANModes) {
return nil, "", errors.Errorf("unknown ipvlan mode %q", v)
}
default:
return nil, "", errors.Errorf("cannot set option \"mode\" with driver %q", network.Driver)
}
vlanPluginMode = v
case types.NoneIPAMDriver:
// do nothing
default:
return nil, "", errors.Errorf("unsupported ipam driver %q", ipamDriver)
}

default:
return nil, "", errors.Errorf("unsupported network option %s", k)
}
opts, err := parseOptions(network.Options, network.Driver)
if err != nil {
return nil, "", err
}

isGateway := true
Expand All @@ -314,7 +291,7 @@ func (n *cniNetwork) createCNIConfigListFromNetwork(network *types.Network, writ

switch network.Driver {
case types.BridgeNetworkDriver:
bridge := newHostLocalBridge(network.NetworkInterface, isGateway, ipMasq, mtu, vlan, &ipamConf)
bridge := newHostLocalBridge(network.NetworkInterface, isGateway, ipMasq, opts.mtu, opts.vlan, ipamConf)
plugins = append(plugins, bridge, newPortMapPlugin(), newFirewallPlugin(), newTuningPlugin())
// if we find the dnsname plugin we add configuration for it
if hasDNSNamePlugin(n.cniPluginDirs) && network.DNSEnabled {
Expand All @@ -323,10 +300,10 @@ func (n *cniNetwork) createCNIConfigListFromNetwork(network *types.Network, writ
}

case types.MacVLANNetworkDriver:
plugins = append(plugins, newVLANPlugin(types.MacVLANNetworkDriver, network.NetworkInterface, vlanPluginMode, mtu, &ipamConf))
plugins = append(plugins, newVLANPlugin(types.MacVLANNetworkDriver, network.NetworkInterface, opts.vlanPluginMode, opts.mtu, ipamConf))

case types.IPVLANNetworkDriver:
plugins = append(plugins, newVLANPlugin(types.IPVLANNetworkDriver, network.NetworkInterface, vlanPluginMode, mtu, &ipamConf))
plugins = append(plugins, newVLANPlugin(types.IPVLANNetworkDriver, network.NetworkInterface, opts.vlanPluginMode, opts.mtu, ipamConf))

default:
return nil, "", errors.Errorf("driver %q is not supported by cni", network.Driver)
Expand Down Expand Up @@ -402,3 +379,48 @@ func removeMachinePlugin(conf *libcni.NetworkConfigList) *libcni.NetworkConfigLi
conf.Plugins = plugins
return conf
}

type options struct {
vlan int
mtu int
vlanPluginMode string
}

func parseOptions(networkOptions map[string]string, networkDriver string) (*options, error) {
opt := &options{}
var err error
for k, v := range networkOptions {
switch k {
case "mtu":
opt.mtu, err = internalutil.ParseMTU(v)
if err != nil {
return nil, err
}

case "vlan":
opt.vlan, err = internalutil.ParseVlan(v)
if err != nil {
return nil, err
}

case "mode":
switch networkDriver {
case types.MacVLANNetworkDriver:
if !pkgutil.StringInSlice(v, types.ValidMacVLANModes) {
return nil, errors.Errorf("unknown macvlan mode %q", v)
}
case types.IPVLANNetworkDriver:
if !pkgutil.StringInSlice(v, types.ValidIPVLANModes) {
return nil, errors.Errorf("unknown ipvlan mode %q", v)
}
default:
return nil, errors.Errorf("cannot set option \"mode\" with driver %q", networkDriver)
}
opt.vlanPluginMode = v

default:
return nil, errors.Errorf("unsupported network option %s", k)
}
}
return opt, nil
}
14 changes: 9 additions & 5 deletions libnetwork/cni/cni_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,11 +145,13 @@ func newHostLocalBridge(name string, isGateWay, ipMasq bool, mtu, vlan int, ipam
MTU: mtu,
HairpinMode: true,
Vlan: vlan,
IPAM: *ipamConf,
}
// if we use host-local set the ips cap to ensure we can set static ips via runtime config
if ipamConf.PluginType == types.HostLocalIPAMDriver {
bridge.Capabilities = caps
if ipamConf != nil {
bridge.IPAM = *ipamConf
// if we use host-local set the ips cap to ensure we can set static ips via runtime config
if ipamConf.PluginType == types.HostLocalIPAMDriver {
bridge.Capabilities = caps
}
}
return &bridge
}
Expand Down Expand Up @@ -259,7 +261,9 @@ func hasDNSNamePlugin(paths []string) bool {
func newVLANPlugin(pluginType, device, mode string, mtu int, ipam *ipamConfig) VLANConfig {
m := VLANConfig{
PluginType: pluginType,
IPAM: *ipam,
}
if ipam != nil {
m.IPAM = *ipam
}
if mtu > 0 {
m.MTU = mtu
Expand Down
42 changes: 36 additions & 6 deletions libnetwork/cni/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ func (n *cniNetwork) networkCreate(newNetwork *types.Network, defaultNet bool) (
return nil, err
}

err = validateIPAMDriver(newNetwork)
if err != nil {
return nil, err
}

// Only get the used networks for validation if we do not create the default network.
// The default network should not be validated against used subnets, we have to ensure
// that this network can always be created even when a subnet is already used on the host.
Expand Down Expand Up @@ -197,13 +202,38 @@ func createIPMACVLAN(network *types.Network) error {
return errors.Errorf("parent interface %s does not exist", network.NetworkInterface)
}
}
if len(network.Subnets) == 0 {
network.IPAMOptions[types.Driver] = types.DHCPIPAMDriver
if network.Internal {
return errors.New("internal is not supported with macvlan and dhcp ipam driver")

switch network.IPAMOptions[types.Driver] {
// set default
case "":
if len(network.Subnets) == 0 {
// if no subnets and no driver choose dhcp
network.IPAMOptions[types.Driver] = types.DHCPIPAMDriver
} else {
network.IPAMOptions[types.Driver] = types.HostLocalIPAMDriver
}
case types.HostLocalIPAMDriver:
if len(network.Subnets) == 0 {
return errors.New("host-local ipam driver set but no subnets are given")
}
}

if network.IPAMOptions[types.Driver] == types.DHCPIPAMDriver && network.Internal {
return errors.New("internal is not supported with macvlan and dhcp ipam driver")
}
return nil
}

func validateIPAMDriver(n *types.Network) error {
ipamDriver := n.IPAMOptions[types.Driver]
switch ipamDriver {
case "", types.HostLocalIPAMDriver:
case types.DHCPIPAMDriver, types.NoneIPAMDriver:
if len(n.Subnets) > 0 {
return errors.Errorf("%s ipam driver is set but subnets are given", ipamDriver)
}
} else {
network.IPAMOptions[types.Driver] = types.HostLocalIPAMDriver
default:
return errors.Errorf("unsupported ipam driver %q", ipamDriver)
}
return nil
}
Loading

0 comments on commit 5816258

Please sign in to comment.