Skip to content

Commit

Permalink
Merge pull request #248 from gabrielmougard/fix/ipv6-disabled
Browse files Browse the repository at this point in the history
fix: don't panic when IPv6 is not supported
  • Loading branch information
masnax committed Jul 2, 2024
2 parents ce8ceb6 + 8f91864 commit db1e041
Show file tree
Hide file tree
Showing 9 changed files with 219 additions and 63 deletions.
56 changes: 45 additions & 11 deletions cmd/microcloud/ask.go
Original file line number Diff line number Diff line change
Expand Up @@ -625,32 +625,62 @@ func (c *CmdControl) askRemotePool(systems map[string]InitSystem, autoSetup bool
return nil
}

func (c *CmdControl) askProceedIfNoOverlayNetwork() error {
proceedWithNoOverlayNetworking, err := c.asker.AskBool("FAN networking is not usable. Do you want to proceed with setting up an inoperable cluster? (yes/no) [default=no]: ", "no")
if err != nil {
return err
}

if proceedWithNoOverlayNetworking {
return nil
}

return fmt.Errorf("cluster bootstrapping aborted due to lack of usable networking")
}

func (c *CmdControl) askNetwork(sh *service.Handler, systems map[string]InitSystem, microCloudInternalSubnet *net.IPNet, autoSetup bool) error {
_, bootstrap := systems[sh.Name]
lxd := sh.Services[types.LXD].(*service.LXDService)
for peer, system := range systems {
if bootstrap {
system.TargetNetworks = []api.NetworksPost{lxd.DefaultPendingFanNetwork()}
if peer == sh.Name {
network, err := lxd.DefaultFanNetwork()
if err != nil {
return err
}

system.Networks = []api.NetworksPost{network}
// Check if FAN networking is usable.
fanUsable, _, err := lxd.FanNetworkUsable()
if err != nil {
return err
}

if fanUsable {
for peer, system := range systems {
if bootstrap {
system.TargetNetworks = []api.NetworksPost{lxd.DefaultPendingFanNetwork()}
if peer == sh.Name {
network, err := lxd.DefaultFanNetwork()
if err != nil {
return err
}

system.Networks = []api.NetworksPost{network}
}
}
}

systems[peer] = system
systems[peer] = system
}
}

// Automatic setup gets a basic fan setup.
if autoSetup {
if !fanUsable {
return c.askProceedIfNoOverlayNetwork()
}

return nil
}

// Environments without OVN get a basic fan setup.
if sh.Services[types.MicroOVN] == nil {
if !fanUsable {
return c.askProceedIfNoOverlayNetwork()
}

return nil
}

Expand Down Expand Up @@ -769,6 +799,10 @@ func (c *CmdControl) askNetwork(sh *service.Handler, systems map[string]InitSyst

if !canOVN {
fmt.Println("No dedicated uplink interfaces detected, skipping distributed networking")
if !fanUsable {
return c.askProceedIfNoOverlayNetwork()
}

return nil
}

Expand Down
13 changes: 6 additions & 7 deletions cmd/microcloud/main_init.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,14 +200,13 @@ func lookupPeers(s *service.Handler, autoSetup bool, iface *net.Interface, subne
}()
}

var timeAfter <-chan time.Time
timeoutDuration := time.Minute
if autoSetup {
timeAfter = time.After(5 * time.Second)
timeoutDuration = 5 * time.Second
}

if len(expectedSystems) > 0 {
timeAfter = time.After(1 * time.Minute)
}
ctx, cancel := context.WithTimeout(context.Background(), timeoutDuration)
defer cancel()

expectedSystemsMap := make(map[string]bool, len(expectedSystems))
for _, system := range expectedSystems {
Expand All @@ -219,7 +218,7 @@ func lookupPeers(s *service.Handler, autoSetup bool, iface *net.Interface, subne
done := false
for !done {
select {
case <-timeAfter:
case <-ctx.Done():
done = true
case err := <-selectionCh:
if err != nil {
Expand All @@ -235,7 +234,7 @@ func lookupPeers(s *service.Handler, autoSetup bool, iface *net.Interface, subne
break
}

peers, err := mdns.LookupPeers(context.Background(), iface, mdns.Version, s.Name)
peers, err := mdns.LookupPeers(ctx, iface, mdns.Version, s.Name)
if err != nil {
return err
}
Expand Down
8 changes: 7 additions & 1 deletion cmd/microcloud/main_init_preseed.go
Original file line number Diff line number Diff line change
Expand Up @@ -498,8 +498,14 @@ func (p *Preseed) Parse(s *service.Handler, bootstrap bool) (map[string]InitSyst
systems[peer] = system
}

// Check if FAN networking is usable.
fanUsable, _, err := lxd.FanNetworkUsable()
if err != nil {
return nil, err
}

// Setup FAN network if OVN not available.
if len(ifaceByPeer) == 0 {
if len(ifaceByPeer) == 0 && fanUsable {
for peer, system := range systems {
if bootstrap {
system.TargetNetworks = append(system.TargetNetworks, lxd.DefaultPendingFanNetwork())
Expand Down
21 changes: 20 additions & 1 deletion mdns/lookup.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,26 @@ func Lookup(ctx context.Context, iface *net.Interface, service string, size int)
params.Interface = iface
params.Entries = entriesCh
params.Timeout = 100 * time.Millisecond
err := mdns.Query(params)
ipv4Supported, ipv6Supported, err := checkIPStatus(iface.Name)
if err != nil {
return nil, fmt.Errorf("Failed to check IP status: %w", err)
}

if !ipv4Supported {
logger.Info("IPv4 is not supported on this system network interface: disabling IPv4 mDNS", logger.Ctx{"iface": iface.Name})
params.DisableIPv4 = true
}

if !ipv6Supported {
logger.Info("IPv6 is not supported on this system network interface: disabling IPv6 mDNS", logger.Ctx{"iface": iface.Name})
params.DisableIPv6 = true
}

if params.DisableIPv4 && params.DisableIPv6 {
return nil, fmt.Errorf("No supported IP versions on the network interface %q", iface.Name)
}

err = mdns.Query(params)
if err != nil {
return nil, fmt.Errorf("Failed lookup: %w", err)
}
Expand Down
36 changes: 36 additions & 0 deletions mdns/mdns.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,39 @@ func dnsTXTSlice(list []byte) []string {

return parts
}

// checkIPStatus checks if the interface is up, has multicast, and supports IPv4/IPv6.
func checkIPStatus(iface string) (ipv4OK bool, ipv6OK bool, err error) {
netInterface, err := net.InterfaceByName(iface)
if err != nil {
return false, false, err
}

if netInterface.Flags&net.FlagUp != net.FlagUp {
return false, false, nil
}

if netInterface.Flags&net.FlagMulticast != net.FlagMulticast {
return false, false, nil
}

addrs, err := netInterface.Addrs()
if err != nil {
return false, false, err
}

for _, addr := range addrs {
ipNet, ok := addr.(*net.IPNet)
if !ok {
continue
}

if ipNet.IP.To4() != nil {
ipv4OK = true
} else if ipNet.IP.To16() != nil {
ipv6OK = true
}
}

return ipv4OK, ipv6OK, nil
}
29 changes: 4 additions & 25 deletions service/lxd.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
package service

import (
"bufio"
"context"
"fmt"
"net"
"net/url"
"os"
"strings"

Check failure on line 8 in service/lxd.go

View workflow job for this annotation

GitHub Actions / Code

"strings" imported and not used

Check failure on line 8 in service/lxd.go

View workflow job for this annotation

GitHub Actions / System (1.22.x, add)

"strings" imported and not used

Check failure on line 8 in service/lxd.go

View workflow job for this annotation

GitHub Actions / System (1.22.x, instances)

"strings" imported and not used

Check failure on line 8 in service/lxd.go

View workflow job for this annotation

GitHub Actions / System (1.22.x, basic)

"strings" imported and not used

Check failure on line 8 in service/lxd.go

View workflow job for this annotation

GitHub Actions / System (1.22.x, interactive)

"strings" imported and not used

Check failure on line 8 in service/lxd.go

View workflow job for this annotation

GitHub Actions / System (1.22.x, mismatch)

"strings" imported and not used

Check failure on line 8 in service/lxd.go

View workflow job for this annotation

GitHub Actions / System (1.22.x, preseed)

"strings" imported and not used
"time"

Expand Down Expand Up @@ -637,33 +635,14 @@ func (s *LXDService) waitReady(ctx context.Context, c lxd.InstanceServer, timeou
}

// defaultGatewaySubnetV4 returns subnet of default gateway interface.
func defaultGatewaySubnetV4() (*net.IPNet, string, error) {
file, err := os.Open("/proc/net/route")
func (s LXDService) defaultGatewaySubnetV4() (*net.IPNet, string, error) {
available, ifaceName, err := s.FanNetworkUsable()
if err != nil {
return nil, "", err
}

defer func() { _ = file.Close() }()

ifaceName := ""

scanner := bufio.NewReader(file)
for {
line, _, err := scanner.ReadLine()
if err != nil {
break
}

fields := strings.Fields(string(line))

if fields[1] == "00000000" && fields[7] == "00000000" {
ifaceName = fields[0]
break
}
}

if ifaceName == "" {
return nil, "", fmt.Errorf("No default gateway for IPv4")
if !available {
return nil, "", fmt.Errorf("No default IPv4 gateway available")
}

iface, err := net.InterfaceByName(ifaceName)
Expand Down
37 changes: 36 additions & 1 deletion service/lxd_config.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package service

import (
"bufio"
"fmt"
"net"
"os"
"strconv"
"strings"

"github.com/canonical/lxd/shared/api"
)
Expand Down Expand Up @@ -32,10 +35,42 @@ func (s LXDService) DefaultPendingFanNetwork() api.NetworksPost {
return api.NetworksPost{Name: "lxdfan0", Type: "bridge"}
}

// FanNetworkUsable checks if the current host is capable of using a Fan network.
// It actually checks if there is a default IPv4 gateway available.
func (s LXDService) FanNetworkUsable() (available bool, ifaceName string, err error) {
file, err := os.Open("/proc/net/route")
if err != nil {
return false, "", err
}

defer func() { _ = file.Close() }()

scanner := bufio.NewReader(file)
for {
line, _, err := scanner.ReadLine()
if err != nil {
break
}

fields := strings.Fields(string(line))

if fields[1] == "00000000" && fields[7] == "00000000" {
ifaceName = fields[0]
break
}
}

if ifaceName == "" {
return false, "", nil // There is no default gateway for IPv4
}

return true, ifaceName, nil
}

// DefaultFanNetwork returns the default Ubuntu Fan network configuration when
// creating the finalized network.
func (s LXDService) DefaultFanNetwork() (api.NetworksPost, error) {
underlay, _, err := defaultGatewaySubnetV4()
underlay, _, err := s.defaultGatewaySubnetV4()
if err != nil {
return api.NetworksPost{}, fmt.Errorf("Could not determine Fan overlay subnet: %w", err)
}
Expand Down
42 changes: 25 additions & 17 deletions test/includes/microcloud.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ unset_interactive_vars() {
unset LOOKUP_IFACE LIMIT_SUBNET SKIP_SERVICE EXPECT_PEERS REUSE_EXISTING REUSE_EXISTING_COUNT \
SETUP_ZFS ZFS_FILTER ZFS_WIPE \
SETUP_CEPH CEPH_WARNING CEPH_FILTER CEPH_WIPE SETUP_CEPHFS CEPH_CLUSTER_NETWORK IGNORE_CEPH_NETWORKING \
SETUP_OVN OVN_WARNING OVN_FILTER IPV4_SUBNET IPV4_START IPV4_END DNS_ADDRESSES IPV6_SUBNET
PROCEED_WITH_NO_OVERLAY_NETWORKING SETUP_OVN OVN_WARNING OVN_FILTER IPV4_SUBNET IPV4_START IPV4_END DNS_ADDRESSES IPV6_SUBNET
}

# microcloud_interactive: outputs text that can be passed to `TEST_CONSOLE=1 microcloud init`
Expand All @@ -19,24 +19,25 @@ microcloud_interactive() {
EXPECT_PEERS=${EXPECT_PEERS:-} # wait for this number of systems to be available to join the cluster.
REUSE_EXISTING=${REUSE_EXISTING:-} # (yes/no) incorporate an existing clustered service.
REUSE_EXISTING_COUNT=${REUSE_EXISTING_COUNT:-0} # (number) number of existing clusters to incorporate.
SETUP_ZFS=${SETUP_ZFS:-} # (yes/no) input for initiating ZFS storage pool setup.
ZFS_FILTER=${ZFS_FILTER:-} # filter string for ZFS disks.
ZFS_WIPE=${ZFS_WIPE:-} # (yes/no) to wipe all disks.
SETUP_CEPH=${SETUP_CEPH:-} # (yes/no) input for initiating CEPH storage pool setup.
SETUP_CEPHFS=${SETUP_CEPHFS:-} # (yes/no) input for initialising CephFS storage pool setup.
CEPH_WARNING=${CEPH_WARNING:-} # (yes/no) input for warning about eligible disk detection.
CEPH_FILTER=${CEPH_FILTER:-} # filter string for CEPH disks.
CEPH_WIPE=${CEPH_WIPE:-} # (yes/no) to wipe all disks.
SETUP_ZFS=${SETUP_ZFS:-} # (yes/no) input for initiating ZFS storage pool setup.
ZFS_FILTER=${ZFS_FILTER:-} # filter string for ZFS disks.
ZFS_WIPE=${ZFS_WIPE:-} # (yes/no) to wipe all disks.
SETUP_CEPH=${SETUP_CEPH:-} # (yes/no) input for initiating CEPH storage pool setup.
SETUP_CEPHFS=${SETUP_CEPHFS:-} # (yes/no) input for initialising CephFS storage pool setup.
CEPH_WARNING=${CEPH_WARNING:-} # (yes/no) input for warning about eligible disk detection.
CEPH_FILTER=${CEPH_FILTER:-} # filter string for CEPH disks.
CEPH_WIPE=${CEPH_WIPE:-} # (yes/no) to wipe all disks.
CEPH_CLUSTER_NETWORK=${CEPH_CLUSTER_NETWORK:-} # (default: MicroCloud internal subnet or Ceph public network if specified previously) input for setting up a cluster network.
IGNORE_CEPH_NETWORKING=${IGNORE_CEPH_NETWORKING:-} # (yes/no) input for ignoring Ceph network setup. Set it to `yes` during `microcloud add` .
SETUP_OVN=${SETUP_OVN:-} # (yes/no) input for initiating OVN network setup.
OVN_WARNING=${OVN_WARNING:-} # (yes/no) input for warning about eligible interface detection.
OVN_FILTER=${OVN_FILTER:-} # filter string for OVN interfaces.
IPV4_SUBNET=${IPV4_SUBNET:-} # OVN ipv4 gateway subnet.
IPV4_START=${IPV4_START:-} # OVN ipv4 range start.
IPV4_END=${IPV4_END:-} # OVN ipv4 range end.
DNS_ADDRESSES=${DNS_ADDRESSES:-} # OVN custom DNS addresses.
IPV6_SUBNET=${IPV6_SUBNET:-} # OVN ipv6 range.
PROCEED_WITH_NO_OVERLAY_NETWORKING=${PROCEED_WITH_NO_OVERLAY_NETWORKING:-} # (yes/no) input for proceeding without overlay networking.
SETUP_OVN=${SETUP_OVN:-} # (yes/no) input for initiating OVN network setup.
OVN_WARNING=${OVN_WARNING:-} # (yes/no) input for warning about eligible interface detection.
OVN_FILTER=${OVN_FILTER:-} # filter string for OVN interfaces.
IPV4_SUBNET=${IPV4_SUBNET:-} # OVN ipv4 gateway subnet.
IPV4_START=${IPV4_START:-} # OVN ipv4 range start.
IPV4_END=${IPV4_END:-} # OVN ipv4 range end.
DNS_ADDRESSES=${DNS_ADDRESSES:-} # OVN custom DNS addresses.
IPV6_SUBNET=${IPV6_SUBNET:-} # OVN ipv6 range.

setup="
${LOOKUP_IFACE} # filter the lookup interface
Expand Down Expand Up @@ -88,6 +89,13 @@ $(true) # workaround for set -e
"
fi

if [ -n "${PROCEED_WITH_NO_OVERLAY_NETWORKING}" ]; then
setup="${setup}
${PROCEED_WITH_NO_OVERLAY_NETWORKING} # agree to proceed without overlay networking (neither FAN nor OVN networking) (yes/no)
$(true) # workaround for set -e
"
fi

if [ -z "${IGNORE_CEPH_NETWORKING}" ]; then
if [ -n "${CEPH_CLUSTER_NETWORK}" ]; then
setup="${setup}
Expand Down
Loading

0 comments on commit db1e041

Please sign in to comment.