github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/hypervisor/manager/subnets.go (about)

     1  package manager
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"net"
     7  	"os"
     8  	"path"
     9  	"sort"
    10  
    11  	"github.com/Cloud-Foundations/Dominator/lib/json"
    12  	"github.com/Cloud-Foundations/Dominator/lib/net/util"
    13  	"github.com/Cloud-Foundations/Dominator/lib/srpc"
    14  	proto "github.com/Cloud-Foundations/Dominator/proto/hypervisor"
    15  )
    16  
    17  var (
    18  	addHypervisorSubnet = flag.Bool("addHypervisorSubnet", false,
    19  		"If true, automatically add the subnet for the default gateway (deprecated)")
    20  )
    21  
    22  func checkSubnetAccess(subnet proto.Subnet,
    23  	authInfo *srpc.AuthInformation) bool {
    24  	if authInfo.HaveMethodAccess {
    25  		return true
    26  	}
    27  	if len(subnet.AllowedUsers) < 1 && len(subnet.AllowedGroups) < 1 {
    28  		return true
    29  	}
    30  	for _, allowedUser := range subnet.AllowedUsers {
    31  		if authInfo.Username == allowedUser {
    32  			return true
    33  		}
    34  	}
    35  	for _, allowedGroup := range subnet.AllowedGroups {
    36  		if _, ok := authInfo.GroupList[allowedGroup]; ok {
    37  			return true
    38  		}
    39  	}
    40  	return false
    41  }
    42  
    43  func getHypervisorSubnet() (proto.Subnet, error) {
    44  	defaultRoute, err := util.GetDefaultRoute()
    45  	if err != nil {
    46  		return proto.Subnet{}, err
    47  	}
    48  	resolverConfig, err := util.GetResolverConfiguration()
    49  	if err != nil {
    50  		return proto.Subnet{}, err
    51  	}
    52  	myIP, err := util.GetMyIP()
    53  	if err != nil {
    54  		return proto.Subnet{}, err
    55  	}
    56  	nameservers := make([]net.IP, 0, len(resolverConfig.Nameservers))
    57  	for _, nameserver := range resolverConfig.Nameservers {
    58  		if nameserver[0] == 127 {
    59  			nameservers = append(nameservers, myIP)
    60  		} else {
    61  			nameservers = append(nameservers, nameserver)
    62  		}
    63  	}
    64  	return proto.Subnet{
    65  		Id:                "hypervisor",
    66  		IpGateway:         defaultRoute.Address,
    67  		IpMask:            net.IP(defaultRoute.Mask),
    68  		DomainNameServers: nameservers,
    69  	}, nil
    70  }
    71  
    72  func (m *Manager) getBridgeForSubnet(subnet proto.Subnet) (
    73  	string, string, error) {
    74  	mapName := fmt.Sprintf("br@%s", subnet.Id)
    75  	if _, ok := m.BridgeMap[mapName]; ok {
    76  		return mapName, "", nil
    77  	} else if bridge, ok := m.VlanIdToBridge[subnet.VlanId]; ok {
    78  		return bridge, "", nil
    79  	} else if bridge, ok = m.VlanIdToBridge[0]; ok {
    80  		return bridge, fmt.Sprintf(",vlan=%d", subnet.VlanId), nil
    81  	} else {
    82  		return "", "", fmt.Errorf("no usable bridge")
    83  	}
    84  }
    85  
    86  // This must be called with the lock held.
    87  func (m *Manager) getMatchingSubnet(ipAddr net.IP) string {
    88  	if len(ipAddr) > 0 {
    89  		for id, subnet := range m.subnets {
    90  			subnetMask := net.IPMask(subnet.IpMask)
    91  			subnetAddr := subnet.IpGateway.Mask(subnetMask)
    92  			if ipAddr.Mask(subnetMask).Equal(subnetAddr) {
    93  				return id
    94  			}
    95  		}
    96  	}
    97  	return ""
    98  }
    99  
   100  // This must be called with the lock held.
   101  func (m *Manager) getSubnetAndAuth(subnetId string,
   102  	authInfo *srpc.AuthInformation) (proto.Subnet, error) {
   103  	if len(m.subnets) < 1 {
   104  		return proto.Subnet{}, fmt.Errorf("no subnets exist")
   105  	}
   106  	if subnetId != "" {
   107  		if subnet, ok := m.subnets[subnetId]; !ok {
   108  			return proto.Subnet{}, fmt.Errorf("subnet: %s does not exist",
   109  				subnetId)
   110  		} else if !checkSubnetAccess(subnet, authInfo) {
   111  			return proto.Subnet{}, fmt.Errorf("no access to subnet: %s",
   112  				subnetId)
   113  		} else {
   114  			return subnet, nil
   115  		}
   116  	}
   117  	// Unspecified subnet: try to find one.
   118  	if len(m.subnets) == 1 {
   119  		if subnet, ok := m.subnets["hypervisor"]; !ok {
   120  			return proto.Subnet{}, fmt.Errorf(
   121  				"hypervisor subnet does not exist")
   122  		} else {
   123  			return subnet, nil
   124  		}
   125  	}
   126  	subnetsPermitted := make([]string, 0, 1)
   127  	for _, subnet := range m.subnets {
   128  		if subnet.Id == "hypervisor" {
   129  			continue
   130  		}
   131  		if !checkSubnetAccess(subnet, authInfo) {
   132  			continue
   133  		}
   134  		if len(subnetsPermitted) > 0 {
   135  			return proto.Subnet{}, fmt.Errorf(
   136  				"multiple available subnets: pick one")
   137  		}
   138  		subnetsPermitted = append(subnetsPermitted, subnet.Id)
   139  	}
   140  	if len(subnetsPermitted) < 1 {
   141  		return proto.Subnet{}, fmt.Errorf("no subnets permitted")
   142  	}
   143  	return m.subnets[subnetsPermitted[0]], nil
   144  }
   145  
   146  func (m *Manager) listSubnets(doSort bool) []proto.Subnet {
   147  	m.mutex.Lock()
   148  	defer m.mutex.Unlock()
   149  	subnets := make([]proto.Subnet, 0, len(m.subnets))
   150  	if !doSort {
   151  		for _, subnet := range m.subnets {
   152  			subnets = append(subnets, subnet)
   153  		}
   154  		return subnets
   155  	}
   156  	subnetIDs := make([]string, 0, len(m.subnets))
   157  	for subnetID := range m.subnets {
   158  		subnetIDs = append(subnetIDs, subnetID)
   159  	}
   160  	sort.Strings(subnetIDs)
   161  	for _, subnetID := range subnetIDs {
   162  		subnets = append(subnets, m.subnets[subnetID])
   163  	}
   164  	return subnets
   165  }
   166  
   167  // This returns with the Manager locked, waiting for existing subnets to be
   168  // drained from the channel by the caller before unlocking.
   169  func (m *Manager) makeSubnetChannel() <-chan proto.Subnet {
   170  	ch := make(chan proto.Subnet, 1)
   171  	m.mutex.Lock()
   172  	m.subnetChannels = append(m.subnetChannels, ch)
   173  	go func() {
   174  		defer m.mutex.Unlock()
   175  		for _, subnet := range m.subnets {
   176  			ch <- subnet
   177  		}
   178  	}()
   179  	return ch
   180  }
   181  
   182  func (m *Manager) loadSubnets() error {
   183  	var subnets []proto.Subnet
   184  	err := json.ReadFromFile(path.Join(m.StateDir, "subnets.json"), &subnets)
   185  	if err != nil && !os.IsNotExist(err) {
   186  		return err
   187  	}
   188  	for index := range subnets {
   189  		subnets[index].Shrink()
   190  	}
   191  	m.subnets = make(map[string]proto.Subnet, len(subnets)+1)
   192  	for _, subnet := range subnets {
   193  		m.subnets[subnet.Id] = subnet
   194  	}
   195  	if *addHypervisorSubnet {
   196  		if subnet, err := getHypervisorSubnet(); err != nil {
   197  			return err
   198  		} else {
   199  			m.subnets["hypervisor"] = subnet
   200  		}
   201  	}
   202  	for _, subnet := range m.subnets {
   203  		m.DhcpServer.AddSubnet(subnet)
   204  	}
   205  	return nil
   206  }
   207  
   208  func (m *Manager) updateSubnets(request proto.UpdateSubnetsRequest) error {
   209  	for index, subnet := range request.Add {
   210  		if subnet.Id == "hypervisor" {
   211  			return fmt.Errorf("cannot add hypervisor subnet")
   212  		}
   213  		request.Add[index].Shrink()
   214  	}
   215  	for index, subnet := range request.Change {
   216  		if subnet.Id == "hypervisor" {
   217  			return fmt.Errorf("cannot change hypervisor subnet")
   218  		}
   219  		request.Change[index].Shrink()
   220  	}
   221  	for _, subnetId := range request.Delete {
   222  		if subnetId == "hypervisor" {
   223  			return fmt.Errorf("cannot delete hypervisor subnet")
   224  		}
   225  	}
   226  	if err := m.updateSubnetsLocked(request); err != nil {
   227  		return err
   228  	}
   229  	for _, subnet := range request.Add {
   230  		m.DhcpServer.AddSubnet(subnet)
   231  		for _, ch := range m.subnetChannels {
   232  			ch <- subnet
   233  		}
   234  	}
   235  	for _, subnet := range request.Change {
   236  		m.DhcpServer.RemoveSubnet(subnet.Id)
   237  		m.DhcpServer.AddSubnet(subnet)
   238  		// TOOO(rgooch): Design a clean way to send updates to the channels.
   239  	}
   240  	for _, subnetId := range request.Delete {
   241  		m.DhcpServer.RemoveSubnet(subnetId)
   242  		// TOOO(rgooch): Design a clean way to send deletes to the channels.
   243  	}
   244  	return nil
   245  }
   246  
   247  func (m *Manager) updateSubnetsLocked(
   248  	request proto.UpdateSubnetsRequest) error {
   249  	m.mutex.Lock()
   250  	defer m.mutex.Unlock()
   251  	for _, subnet := range request.Add {
   252  		if _, ok := m.subnets[subnet.Id]; ok {
   253  			return fmt.Errorf("subnet: %s already exists", subnet.Id)
   254  		}
   255  	}
   256  	for _, subnet := range request.Change {
   257  		if _, ok := m.subnets[subnet.Id]; !ok {
   258  			return fmt.Errorf("subnet: %s does not exist", subnet.Id)
   259  		}
   260  	}
   261  	for _, subnetId := range request.Delete {
   262  		if _, ok := m.subnets[subnetId]; !ok {
   263  			return fmt.Errorf("subnet: %s does not exist", subnetId)
   264  		}
   265  	}
   266  	for _, subnet := range request.Add {
   267  		m.subnets[subnet.Id] = subnet
   268  	}
   269  	for _, subnet := range request.Change {
   270  		m.subnets[subnet.Id] = subnet
   271  	}
   272  	for _, subnetId := range request.Delete {
   273  		delete(m.subnets, subnetId)
   274  	}
   275  	subnetsToWrite := make([]proto.Subnet, 0, len(m.subnets)-1)
   276  	for _, subnet := range m.subnets {
   277  		if subnet.Id != "hypervisor" {
   278  			subnetsToWrite = append(subnetsToWrite, subnet)
   279  		}
   280  	}
   281  	// TODO(rgooch): Should precompute the numFreeAddresses map.
   282  	numFreeAddresses, err := m.computeNumFreeAddressesMap(m.addressPool)
   283  	if err != nil {
   284  		return err
   285  	}
   286  	err = json.WriteToFile(path.Join(m.StateDir, "subnets.json"),
   287  		publicFilePerms, "    ", subnetsToWrite)
   288  	if err != nil {
   289  		return err
   290  	}
   291  	m.sendUpdateWithLock(
   292  		proto.Update{
   293  			HaveAddressPool:  true,
   294  			AddressPool:      m.addressPool.Registered,
   295  			NumFreeAddresses: numFreeAddresses,
   296  			HaveSubnets:      true,
   297  			Subnets:          subnetsToWrite,
   298  		})
   299  	return nil
   300  }