Skip to content

Commit

Permalink
refactor subnet manager config
Browse files Browse the repository at this point in the history
- add getter function for flannel network
- add Networks and IPv6Networks
- move specific checks to CheckNetworkConfig
  • Loading branch information
thomasferrandiz committed Sep 27, 2022
1 parent 4391968 commit f174fe9
Show file tree
Hide file tree
Showing 9 changed files with 119 additions and 80 deletions.
2 changes: 1 addition & 1 deletion backend/ipip/ipip.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func (be *IPIPBackend) RegisterNetwork(ctx context.Context, wg *sync.WaitGroup,
return nil, fmt.Errorf("failed to acquire lease: %v", err)
}

link, err := be.configureIPIPDevice(n.SubnetLease, config.Network)
link, err := be.configureIPIPDevice(n.SubnetLease, subnet.GetFlannelNetwork(config))

if err != nil {
return nil, err
Expand Down
2 changes: 1 addition & 1 deletion backend/udp/udp_amd64.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func (be *UdpBackend) RegisterNetwork(ctx context.Context, wg *sync.WaitGroup, c
// and not that of the individual host (e.g. /24)
tunNet := ip.IP4Net{
IP: l.Subnet.IP,
PrefixLen: config.Network.PrefixLen,
PrefixLen: subnet.GetFlannelNetwork(config).PrefixLen,
}

return newNetwork(be.sm, be.extIface, cfg.Port, tunNet, l)
Expand Down
4 changes: 2 additions & 2 deletions backend/vxlan/vxlan.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,12 +191,12 @@ func (be *VXLANBackend) RegisterNetwork(ctx context.Context, wg *sync.WaitGroup,
// This IP is just used as a source address for host to workload traffic (so
// the return path for the traffic has an address on the flannel network to use as the destination)
if config.EnableIPv4 {
if err := dev.Configure(ip.IP4Net{IP: lease.Subnet.IP, PrefixLen: 32}, config.Network); err != nil {
if err := dev.Configure(ip.IP4Net{IP: lease.Subnet.IP, PrefixLen: 32}, subnet.GetFlannelNetwork(config)); err != nil {
return nil, fmt.Errorf("failed to configure interface %s: %w", dev.link.Attrs().Name, err)
}
}
if config.EnableIPv6 {
if err := v6Dev.ConfigureIPv6(ip.IP6Net{IP: lease.IPv6Subnet.IP, PrefixLen: 128}, config.IPv6Network); err != nil {
if err := v6Dev.ConfigureIPv6(ip.IP6Net{IP: lease.IPv6Subnet.IP, PrefixLen: 128}, subnet.GetFlannelIPv6Network(config)); err != nil {
return nil, fmt.Errorf("failed to configure interface %s: %w", v6Dev.link.Attrs().Name, err)
}
}
Expand Down
6 changes: 3 additions & 3 deletions backend/wireguard/wireguard.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,17 +168,17 @@ func (be *WireguardBackend) RegisterNetwork(ctx context.Context, wg *sync.WaitGr
}

if config.EnableIPv4 {
err = dev.Configure(lease.Subnet.IP, config.Network)
err = dev.Configure(lease.Subnet.IP, subnet.GetFlannelNetwork(config))
if err != nil {
return nil, err
}
}

if config.EnableIPv6 {
if cfg.Mode == Separate {
err = v6Dev.ConfigureV6(lease.IPv6Subnet.IP, config.IPv6Network)
err = v6Dev.ConfigureV6(lease.IPv6Subnet.IP, subnet.GetFlannelIPv6Network(config))
} else {
err = dev.ConfigureV6(lease.IPv6Subnet.IP, config.IPv6Network)
err = dev.ConfigureV6(lease.IPv6Subnet.IP, subnet.GetFlannelIPv6Network(config))
}
if err != nil {
return nil, err
Expand Down
12 changes: 6 additions & 6 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -333,25 +333,25 @@ func main() {
// Set up ipMasq if needed
if opts.ipMasq {
if config.EnableIPv4 {
if err = recycleIPTables(config.Network, bn.Lease()); err != nil {
if err = recycleIPTables(subnet.GetFlannelNetwork(config), bn.Lease()); err != nil {
log.Errorf("Failed to recycle IPTables rules, %v", err)
cancel()
wg.Wait()
os.Exit(1)
}
log.Infof("Setting up masking rules")
go network.SetupAndEnsureIP4Tables(network.MasqRules(config.Network, bn.Lease()), opts.iptablesResyncSeconds)
go network.SetupAndEnsureIP4Tables(network.MasqRules(subnet.GetFlannelNetwork(config), bn.Lease()), opts.iptablesResyncSeconds)

}
if config.EnableIPv6 {
if err = recycleIP6Tables(config.IPv6Network, bn.Lease()); err != nil {
if err = recycleIP6Tables(subnet.GetFlannelIPv6Network(config), bn.Lease()); err != nil {
log.Errorf("Failed to recycle IP6Tables rules, %v", err)
cancel()
wg.Wait()
os.Exit(1)
}
log.Infof("Setting up masking ip6 rules")
go network.SetupAndEnsureIP6Tables(network.MasqIP6Rules(config.IPv6Network, bn.Lease()), opts.iptablesResyncSeconds)
go network.SetupAndEnsureIP6Tables(network.MasqIP6Rules(subnet.GetFlannelIPv6Network(config), bn.Lease()), opts.iptablesResyncSeconds)
}
}

Expand All @@ -361,11 +361,11 @@ func main() {
if opts.iptablesForwardRules {
if config.EnableIPv4 {
log.Infof("Changing default FORWARD chain policy to ACCEPT")
go network.SetupAndEnsureIP4Tables(network.ForwardRules(config.Network.String()), opts.iptablesResyncSeconds)
go network.SetupAndEnsureIP4Tables(network.ForwardRules(subnet.GetFlannelNetwork(config).String()), opts.iptablesResyncSeconds)
}
if config.EnableIPv6 {
log.Infof("IPv6: Changing default FORWARD chain policy to ACCEPT")
go network.SetupAndEnsureIP6Tables(network.ForwardRules(config.IPv6Network.String()), opts.iptablesResyncSeconds)
go network.SetupAndEnsureIP6Tables(network.ForwardRules(subnet.GetFlannelIPv6Network(config).String()), opts.iptablesResyncSeconds)
}
}

Expand Down
141 changes: 82 additions & 59 deletions subnet/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ type Config struct {
EnableIPv6 bool
Network ip.IP4Net
IPv6Network ip.IP6Net
Networks []ip.IP4Net
IPv6Networks []ip.IP6Net
SubnetMin ip.IP4
SubnetMax ip.IP4
IPv6SubnetMin *ip.IP6
Expand Down Expand Up @@ -61,132 +63,153 @@ func ParseConfig(s string) (*Config, error) {
return nil, err
}

if cfg.EnableIPv4 {
if cfg.Network.Empty() {
return nil, errors.New("Please define a correct Network parameter in the flannel config")
bt, err := parseBackendType(cfg.Backend)
if err != nil {
return nil, err
}
cfg.BackendType = bt

return cfg, nil
}

func CheckNetworkConfig(config *Config) error {
if config.EnableIPv4 {
if config.Network.Empty() {
return errors.New("please define a correct Network parameter in the flannel config")
}
if cfg.SubnetLen > 0 {
if config.SubnetLen > 0 {
// SubnetLen needs to allow for a tunnel and bridge device on each host.
if cfg.SubnetLen > 30 {
return nil, errors.New("SubnetLen must be less than /31")
if config.SubnetLen > 30 {
return errors.New("SubnetLen must be less than /31")
}

// SubnetLen needs to fit _more_ than twice into the Network.
// the first subnet isn't used, so splitting into two one only provide one usable host.
if cfg.SubnetLen < cfg.Network.PrefixLen+2 {
return nil, errors.New("Network must be able to accommodate at least four subnets")
if config.SubnetLen < config.Network.PrefixLen+2 {
return errors.New("network must be able to accommodate at least four subnets")
}
} else {
// If the network is smaller than a /28 then the network isn't big enough for flannel so return an error.
// Default to giving each host at least a /24 (as long as the network is big enough to support at least four hosts)
// Otherwise, if the network is too small to give each host a /24 just split the network into four.
if cfg.Network.PrefixLen > 28 {
if config.Network.PrefixLen > 28 {
// Each subnet needs at least four addresses (/30) and the network needs to accommodate at least four
// since the first subnet isn't used, so splitting into two would only provide one usable host.
// So the min useful PrefixLen is /28
return nil, errors.New("Network is too small. Minimum useful network prefix is /28")
} else if cfg.Network.PrefixLen <= 22 {
return errors.New("network is too small. Minimum useful network prefix is /28")
} else if config.Network.PrefixLen <= 22 {
// Network is big enough to give each host a /24
cfg.SubnetLen = 24
config.SubnetLen = 24
} else {
// Use +2 to provide four hosts per subnet.
cfg.SubnetLen = cfg.Network.PrefixLen + 2
config.SubnetLen = config.Network.PrefixLen + 2
}
}

subnetSize := ip.IP4(1 << (32 - cfg.SubnetLen))
subnetSize := ip.IP4(1 << (32 - config.SubnetLen))

if cfg.SubnetMin == ip.IP4(0) {
if config.SubnetMin == ip.IP4(0) {
// skip over the first subnet otherwise it causes problems. e.g.
// if Network is 10.100.0.0/16, having an interface with 10.100.0.0
// conflicts with the network address.
cfg.SubnetMin = cfg.Network.IP + subnetSize
} else if !cfg.Network.Contains(cfg.SubnetMin) {
return nil, errors.New("SubnetMin is not in the range of the Network")
config.SubnetMin = config.Network.IP + subnetSize
} else if !config.Network.Contains(config.SubnetMin) {
return errors.New("SubnetMin is not in the range of the Network")
}

if cfg.SubnetMax == ip.IP4(0) {
cfg.SubnetMax = cfg.Network.Next().IP - subnetSize
} else if !cfg.Network.Contains(cfg.SubnetMax) {
return nil, errors.New("SubnetMax is not in the range of the Network")
if config.SubnetMax == ip.IP4(0) {
config.SubnetMax = config.Network.Next().IP - subnetSize
} else if !config.Network.Contains(config.SubnetMax) {
return errors.New("SubnetMax is not in the range of the Network")
}

// The SubnetMin and SubnetMax need to be aligned to a SubnetLen boundary
mask := ip.IP4(0xFFFFFFFF << (32 - cfg.SubnetLen))
if cfg.SubnetMin != cfg.SubnetMin&mask {
return nil, fmt.Errorf("SubnetMin is not on a SubnetLen boundary: %v", cfg.SubnetMin)
mask := ip.IP4(0xFFFFFFFF << (32 - config.SubnetLen))
if config.SubnetMin != config.SubnetMin&mask {
return fmt.Errorf("SubnetMin is not on a SubnetLen boundary: %v", config.SubnetMin)
}

if cfg.SubnetMax != cfg.SubnetMax&mask {
return nil, fmt.Errorf("SubnetMax is not on a SubnetLen boundary: %v", cfg.SubnetMax)
if config.SubnetMax != config.SubnetMax&mask {
return fmt.Errorf("SubnetMax is not on a SubnetLen boundary: %v", config.SubnetMax)
}
}
if cfg.EnableIPv6 {
if cfg.IPv6Network.Empty() {
return nil, errors.New("Please define a correct IPv6Network parameter in the flannel config")
if config.EnableIPv6 {
if config.IPv6Network.Empty() {
return errors.New("please define a correct IPv6Network parameter in the flannel config")
}
if cfg.IPv6SubnetLen > 0 {
if config.IPv6SubnetLen > 0 {
// SubnetLen needs to allow for a tunnel and bridge device on each host.
if cfg.IPv6SubnetLen > 126 {
return nil, errors.New("SubnetLen must be less than /127")
if config.IPv6SubnetLen > 126 {
return errors.New("SubnetLen must be less than /127")
}

// SubnetLen needs to fit _more_ than twice into the Network.
// the first subnet isn't used, so splitting into two one only provide one usable host.
if cfg.IPv6SubnetLen < cfg.IPv6Network.PrefixLen+2 {
return nil, errors.New("Network must be able to accommodate at least four subnets")
if config.IPv6SubnetLen < config.IPv6Network.PrefixLen+2 {
return errors.New("network must be able to accommodate at least four subnets")
}
} else {
// If the network is smaller than a /124 then the network isn't big enough for flannel so return an error.
// Default to giving each host at least a /64 (as long as the network is big enough to support at least four hosts)
// Otherwise, if the network is too small to give each host a /64 just split the network into four.
if cfg.IPv6Network.PrefixLen > 124 {
if config.IPv6Network.PrefixLen > 124 {
// Each subnet needs at least four addresses (/126) and the network needs to accommodate at least four
// since the first subnet isn't used, so splitting into two would only provide one usable host.
// So the min useful PrefixLen is /124
return nil, errors.New("IPv6Network is too small. Minimum useful network prefix is /124")
} else if cfg.IPv6Network.PrefixLen <= 62 {
return errors.New("IPv6Network is too small. Minimum useful network prefix is /124")
} else if config.IPv6Network.PrefixLen <= 62 {
// Network is big enough to give each host a /64
cfg.IPv6SubnetLen = 64
config.IPv6SubnetLen = 64
} else {
// Use +2 to provide four hosts per subnet.
cfg.IPv6SubnetLen = cfg.IPv6Network.PrefixLen + 2
config.IPv6SubnetLen = config.IPv6Network.PrefixLen + 2
}
}

ipv6SubnetSize := big.NewInt(0).Lsh(big.NewInt(1), 128-cfg.IPv6SubnetLen)
ipv6SubnetSize := big.NewInt(0).Lsh(big.NewInt(1), 128-config.IPv6SubnetLen)

if ip.IsEmpty(cfg.IPv6SubnetMin) {
if ip.IsEmpty(config.IPv6SubnetMin) {
// skip over the first subnet otherwise it causes problems. e.g.
// if Network is fc00::/48, having an interface with fc00::
// conflicts with the broadcast address.
cfg.IPv6SubnetMin = ip.GetIPv6SubnetMin(cfg.IPv6Network.IP, ipv6SubnetSize)
} else if !cfg.IPv6Network.Contains(cfg.IPv6SubnetMin) {
return nil, errors.New("IPv6SubnetMin is not in the range of the IPv6Network")
config.IPv6SubnetMin = ip.GetIPv6SubnetMin(config.IPv6Network.IP, ipv6SubnetSize)
} else if !config.IPv6Network.Contains(config.IPv6SubnetMin) {
return errors.New("IPv6SubnetMin is not in the range of the IPv6Network")
}

if ip.IsEmpty(cfg.IPv6SubnetMax) {
cfg.IPv6SubnetMax = ip.GetIPv6SubnetMax(cfg.IPv6Network.Next().IP, ipv6SubnetSize)
} else if !cfg.IPv6Network.Contains(cfg.IPv6SubnetMax) {
return nil, errors.New("IPv6SubnetMax is not in the range of the IPv6Network")
if ip.IsEmpty(config.IPv6SubnetMax) {
config.IPv6SubnetMax = ip.GetIPv6SubnetMax(config.IPv6Network.Next().IP, ipv6SubnetSize)
} else if !config.IPv6Network.Contains(config.IPv6SubnetMax) {
return errors.New("IPv6SubnetMax is not in the range of the IPv6Network")
}

// The SubnetMin and SubnetMax need to be aligned to a SubnetLen boundary
mask := ip.Mask(int(cfg.IPv6SubnetLen))
if !ip.CheckIPv6Subnet(cfg.IPv6SubnetMin, mask) {
return nil, fmt.Errorf("IPv6SubnetMin is not on a SubnetLen boundary: %v", cfg.IPv6SubnetMin)
mask := ip.Mask(int(config.IPv6SubnetLen))
if !ip.CheckIPv6Subnet(config.IPv6SubnetMin, mask) {
return fmt.Errorf("IPv6SubnetMin is not on a SubnetLen boundary: %v", config.IPv6SubnetMin)
}

if !ip.CheckIPv6Subnet(cfg.IPv6SubnetMax, mask) {
return nil, fmt.Errorf("IPv6SubnetMax is not on a SubnetLen boundary: %v", cfg.IPv6SubnetMax)
if !ip.CheckIPv6Subnet(config.IPv6SubnetMax, mask) {
return fmt.Errorf("IPv6SubnetMax is not on a SubnetLen boundary: %v", config.IPv6SubnetMax)
}
}
return nil
}

bt, err := parseBackendType(cfg.Backend)
if err != nil {
return nil, err
func GetFlannelNetwork(config *Config) ip.IP4Net {
if len(config.Networks) > 0 {
//TODO_TF select network properly
return config.Networks[0]
} else {
return config.Network
}
cfg.BackendType = bt
}

return cfg, nil
func GetFlannelIPv6Network(config *Config) ip.IP6Net {
if len(config.IPv6Networks) > 0 {
//TODO_TF select network properly
return config.IPv6Networks[0]
} else {
return config.IPv6Network
}
}
8 changes: 8 additions & 0 deletions subnet/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ func TestConfigDefaults(t *testing.T) {
if err != nil {
t.Fatalf("ParseConfig failed: %s", err)
}
err = CheckNetworkConfig(cfg)
if err != nil {
t.Fatalf("CheckNetworkConfig failed: %s", err)
}

expectedNet := "10.3.0.0/16"
if cfg.Network.String() != expectedNet {
Expand All @@ -51,6 +55,10 @@ func TestIPv6ConfigDefaults(t *testing.T) {
if err != nil {
t.Fatalf("ParseConfig failed: %s", err)
}
err = CheckNetworkConfig(cfg)
if err != nil {
t.Fatalf("CheckNetworkConfig failed: %s", err)
}

expectedNet := "fc00::/48"
if cfg.IPv6Network.String() != expectedNet {
Expand Down
10 changes: 9 additions & 1 deletion subnet/etcd/local_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,15 @@ func (m *LocalManager) GetNetworkConfig(ctx context.Context) (*subnet.Config, er
return nil, err
}

return subnet.ParseConfig(cfg)
config, err := subnet.ParseConfig(cfg)
if err != nil {
return nil, err
}
err = subnet.CheckNetworkConfig(config)
if err != nil {
return nil, err
}
return config, nil
}

func (m *LocalManager) AcquireLease(ctx context.Context, attrs *subnet.LeaseAttrs) (*subnet.Lease, error) {
Expand Down
14 changes: 7 additions & 7 deletions subnet/kube/kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ func (ksm *kubeSubnetManager) GetNetworkConfig(ctx context.Context) (*subnet.Con
}

// AcquireLease adds the flannel specific node annotations (defined in the struct LeaseAttrs) and returns a lease
// with importany information for the backend, such as the subnet. This function is called once by the backend when
// with important information for the backend, such as the subnet. This function is called once by the backend when
// registering
func (ksm *kubeSubnetManager) AcquireLease(ctx context.Context, attrs *subnet.LeaseAttrs) (*subnet.Lease, error) {
cachedNode, err := ksm.nodeStore.Get(ksm.nodeName)
Expand Down Expand Up @@ -360,19 +360,19 @@ func (ksm *kubeSubnetManager) AcquireLease(ctx context.Context, attrs *subnet.Le
Expiration: time.Now().Add(24 * time.Hour),
}
if cidr != nil && ksm.enableIPv4 {
if !containsCIDR(ksm.subnetConf.Network.ToIPNet(), cidr) {
return nil, fmt.Errorf("subnet %q specified in the flannel net config doesn't contain %q PodCIDR of the %q node.", ksm.subnetConf.Network, cidr, ksm.nodeName)
if !containsCIDR(subnet.GetFlannelNetwork(ksm.subnetConf).ToIPNet(), cidr) {
return nil, fmt.Errorf("subnet %q specified in the flannel net config doesn't contain %q PodCIDR of the %q node", ksm.subnetConf.Network, cidr, ksm.nodeName)
}

lease.Subnet = ip.FromIPNet(cidr)
}
if ipv6Cidr != nil {
if ksm.subnetConf.IPv6Network.IP == nil {
return nil, fmt.Errorf("subnet %q specified in the PodCIDR, but doesn't exist in the flannel net config of the %q node.", ipv6Cidr, ksm.nodeName)
if subnet.GetFlannelIPv6Network(ksm.subnetConf).IP == nil {
return nil, fmt.Errorf("subnet %q specified in the PodCIDR, but doesn't exist in the flannel net config of the %q node", ipv6Cidr, ksm.nodeName)
}

if !containsCIDR(ksm.subnetConf.IPv6Network.ToIPNet(), ipv6Cidr) {
return nil, fmt.Errorf("subnet %q specified in the flannel net config doesn't contain %q IPv6 PodCIDR of the %q node.", ksm.subnetConf.IPv6Network, ipv6Cidr, ksm.nodeName)
if !containsCIDR(subnet.GetFlannelIPv6Network(ksm.subnetConf).ToIPNet(), ipv6Cidr) {
return nil, fmt.Errorf("subnet %q specified in the flannel net config doesn't contain %q IPv6 PodCIDR of the %q node", subnet.GetFlannelIPv6Network(ksm.subnetConf), ipv6Cidr, ksm.nodeName)
}

lease.IPv6Subnet = ip.FromIP6Net(ipv6Cidr)
Expand Down

0 comments on commit f174fe9

Please sign in to comment.