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

     1  package dhcpd
     2  
     3  import (
     4  	"errors"
     5  	"net"
     6  	"os"
     7  	"sort"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/Cloud-Foundations/Dominator/lib/fsutil"
    12  	"github.com/Cloud-Foundations/Dominator/lib/json"
    13  	"github.com/Cloud-Foundations/Dominator/lib/log"
    14  	"github.com/Cloud-Foundations/Dominator/lib/log/prefixlogger"
    15  	libnet "github.com/Cloud-Foundations/Dominator/lib/net"
    16  	"github.com/Cloud-Foundations/Dominator/lib/net/util"
    17  	proto "github.com/Cloud-Foundations/Dominator/proto/hypervisor"
    18  	dhcp "github.com/krolaw/dhcp4"
    19  	"golang.org/x/net/ipv4"
    20  )
    21  
    22  const dynamicLeaseTime = time.Hour * 4
    23  const staticLeaseTime = time.Hour * 48
    24  
    25  type dynamicLeaseType struct {
    26  	ClientHostName string `json:",omitempty"`
    27  	Expires        time.Time
    28  	proto.Address
    29  }
    30  
    31  type serveIfConn struct {
    32  	ifIndices        map[int]string
    33  	conn             *ipv4.PacketConn
    34  	cm               *ipv4.ControlMessage
    35  	requestInterface *string
    36  }
    37  
    38  func listMyIPs() (map[string][]net.IP, []net.IP, error) {
    39  	interfaces, err := net.Interfaces()
    40  	if err != nil {
    41  		return nil, nil, err
    42  	}
    43  	ifMap := make(map[string][]net.IP)
    44  	ipMap := make(map[string]net.IP)
    45  	for _, iface := range interfaces {
    46  		if iface.Flags&net.FlagUp == 0 {
    47  			continue
    48  		}
    49  		if iface.Flags&net.FlagBroadcast == 0 {
    50  			continue
    51  		}
    52  		interfaceAddrs, err := iface.Addrs()
    53  		if err != nil {
    54  			return nil, nil, err
    55  		}
    56  		for _, addr := range interfaceAddrs {
    57  			IP, _, err := net.ParseCIDR(addr.String())
    58  			if err != nil {
    59  				return nil, nil, err
    60  			}
    61  			if IP = IP.To4(); IP == nil {
    62  				continue
    63  			}
    64  			ifMap[iface.Name] = append(ifMap[iface.Name], IP)
    65  			ipMap[IP.String()] = IP
    66  		}
    67  	}
    68  	var IPs []net.IP
    69  	for _, IP := range ipMap {
    70  		IPs = append(IPs, IP)
    71  	}
    72  	return ifMap, IPs, nil
    73  }
    74  
    75  func newServer(interfaceNames []string, dynamicLeasesFile string,
    76  	logger log.DebugLogger) (
    77  	*DhcpServer, error) {
    78  	logger = prefixlogger.New("dhcpd: ", logger)
    79  	cleanupTriggerChannel := make(chan struct{}, 1)
    80  	dhcpServer := &DhcpServer{
    81  		dynamicLeasesFile: dynamicLeasesFile,
    82  		logger:            logger,
    83  		cleanupTrigger:    cleanupTriggerChannel,
    84  		ackChannels:       make(map[string]chan struct{}),
    85  		interfaceSubnets:  make(map[string][]*subnetType),
    86  		ipAddrToMacAddr:   make(map[string]string),
    87  		staticLeases:      make(map[string]leaseType),
    88  		requestChannels:   make(map[string]chan net.IP),
    89  		routeTable:        make(map[string]*util.RouteEntry),
    90  		dynamicLeases:     make(map[string]*leaseType),
    91  	}
    92  	if interfaceIPs, myIPs, err := listMyIPs(); err != nil {
    93  		return nil, err
    94  	} else {
    95  		if len(myIPs) < 1 {
    96  			return nil, errors.New("no IP addresses found")
    97  		}
    98  		dhcpServer.interfaceIPs = interfaceIPs
    99  		dhcpServer.myIPs = myIPs
   100  	}
   101  	routeTable, err := util.GetRouteTable()
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  	for _, routeEntry := range routeTable.RouteEntries {
   106  		if len(routeEntry.GatewayAddr) < 1 ||
   107  			routeEntry.GatewayAddr.Equal(net.IPv4zero) {
   108  			dhcpServer.routeTable[routeEntry.InterfaceName] = routeEntry
   109  		}
   110  	}
   111  	if len(interfaceNames) < 1 {
   112  		logger.Debugln(0, "Starting server on all broadcast interfaces")
   113  		interfaces, _, err := libnet.ListBroadcastInterfaces(
   114  			libnet.InterfaceTypeEtherNet|
   115  				libnet.InterfaceTypeBridge|
   116  				libnet.InterfaceTypeVlan,
   117  			logger)
   118  		if err != nil {
   119  			return nil, err
   120  		} else {
   121  			for _, iface := range interfaces {
   122  				interfaceNames = append(interfaceNames, iface.Name)
   123  			}
   124  		}
   125  	} else {
   126  		logger.Debugln(0, "Starting server on interfaces: "+
   127  			strings.Join(interfaceNames, ","))
   128  	}
   129  	serveConn := &serveIfConn{
   130  		ifIndices:        make(map[int]string, len(interfaceNames)),
   131  		requestInterface: &dhcpServer.requestInterface,
   132  	}
   133  	for _, interfaceName := range interfaceNames {
   134  		if iface, err := net.InterfaceByName(interfaceName); err != nil {
   135  			return nil, err
   136  		} else {
   137  			serveConn.ifIndices[iface.Index] = iface.Name
   138  		}
   139  	}
   140  	if err := dhcpServer.readDynamicLeases(); err != nil {
   141  		return nil, err
   142  	}
   143  	listener, err := net.ListenPacket("udp4", ":67")
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  	pktConn := ipv4.NewPacketConn(listener)
   148  	if err := pktConn.SetControlMessage(ipv4.FlagInterface, true); err != nil {
   149  		listener.Close()
   150  		return nil, err
   151  	}
   152  	serveConn.conn = pktConn
   153  	go func() {
   154  		if err := dhcp.Serve(serveConn, dhcpServer); err != nil {
   155  			logger.Println(err)
   156  		}
   157  	}()
   158  	go dhcpServer.cleanupDynamicLeasesLoop(cleanupTriggerChannel)
   159  	return dhcpServer, nil
   160  }
   161  
   162  func (s *DhcpServer) acknowledgeLease(ipAddr net.IP) {
   163  	ipStr := ipAddr.String()
   164  	s.mutex.Lock()
   165  	ackChan, ok := s.ackChannels[ipStr]
   166  	delete(s.ackChannels, ipStr)
   167  	s.mutex.Unlock()
   168  	if ok {
   169  		ackChan <- struct{}{}
   170  		close(ackChan)
   171  	}
   172  }
   173  
   174  func (s *DhcpServer) addLease(address proto.Address, doNetboot bool,
   175  	hostname string, protoSubnet *proto.Subnet) error {
   176  	address.Shrink()
   177  	if len(address.IpAddress) < 1 {
   178  		return errors.New("no IP address")
   179  	}
   180  	ipAddr := address.IpAddress.String()
   181  	var subnet *subnetType
   182  	s.mutex.Lock()
   183  	defer s.mutex.Unlock()
   184  	if protoSubnet == nil {
   185  		if subnet = s.findMatchingSubnet(address.IpAddress); subnet == nil {
   186  			return errors.New("no subnet found for " + ipAddr)
   187  		}
   188  	} else {
   189  		subnet = s.makeSubnet(protoSubnet)
   190  	}
   191  	if doNetboot {
   192  		if len(s.networkBootImage) < 1 {
   193  			return errors.New("no Network Boot Image name configured")
   194  		}
   195  		if _, ok := s.staticLeases[address.MacAddress]; ok {
   196  			return errors.New("already have lease for: " + address.MacAddress)
   197  		}
   198  	}
   199  	if lease, ok := s.dynamicLeases[address.MacAddress]; ok {
   200  		leaseIpAddr := lease.IpAddress.String()
   201  		s.logger.Printf("discarding {%s %s}: static lease\n",
   202  			leaseIpAddr, address.MacAddress)
   203  		delete(s.ipAddrToMacAddr, leaseIpAddr)
   204  		delete(s.dynamicLeases, address.MacAddress)
   205  		if err := s.writeDynamicLeases(); err != nil {
   206  			s.logger.Println(err)
   207  		}
   208  	}
   209  	if lease, ok := s.staticLeases[address.MacAddress]; ok {
   210  		leaseIpAddr := lease.IpAddress.String()
   211  		s.logger.Printf("replacing {%s %s}\n", leaseIpAddr, address.MacAddress)
   212  		delete(s.ipAddrToMacAddr, leaseIpAddr)
   213  		delete(s.staticLeases, address.MacAddress)
   214  	}
   215  	if macAddr, ok := s.ipAddrToMacAddr[ipAddr]; ok {
   216  		s.logger.Printf("replacing {%s %s}\n", ipAddr, macAddr)
   217  		delete(s.ipAddrToMacAddr, ipAddr)
   218  		delete(s.dynamicLeases, macAddr)
   219  		delete(s.staticLeases, macAddr)
   220  	}
   221  	s.ipAddrToMacAddr[ipAddr] = address.MacAddress
   222  	s.staticLeases[address.MacAddress] = leaseType{
   223  		Address:   address,
   224  		hostname:  hostname,
   225  		doNetboot: doNetboot,
   226  		subnet:    subnet,
   227  	}
   228  	return nil
   229  }
   230  
   231  func (s *DhcpServer) addSubnet(protoSubnet proto.Subnet) {
   232  	subnet := s.makeSubnet(&protoSubnet)
   233  	var ifaceName string
   234  	for name, ips := range s.interfaceIPs {
   235  		for _, ip := range ips {
   236  			if protoSubnet.IpGateway.Equal(ip) {
   237  				ifaceName = name
   238  				s.logger.Printf("attaching subnet GW: %s to interface: %s\n",
   239  					ip, name)
   240  				break
   241  			}
   242  		}
   243  		if ifaceName != "" {
   244  			break
   245  		}
   246  	}
   247  	s.mutex.Lock()
   248  	defer s.mutex.Unlock()
   249  	if ifaceName != "" {
   250  		s.interfaceSubnets[ifaceName] = append(s.interfaceSubnets[ifaceName],
   251  			subnet)
   252  	}
   253  	s.subnets = append(s.subnets, subnet)
   254  }
   255  
   256  func (s *DhcpServer) checkRouteOnInterface(addr net.IP,
   257  	interfaceName string) bool {
   258  	if route, ok := s.routeTable[interfaceName]; !ok {
   259  		return true
   260  	} else if route.Flags&util.RouteFlagUp == 0 {
   261  		return true
   262  	} else if addr.Mask(route.Mask).Equal(route.BaseAddr) {
   263  		return true
   264  	}
   265  	return false
   266  }
   267  
   268  func (s *DhcpServer) cleanupDynamicLeases() time.Duration {
   269  	numExpired := 0
   270  	waitTime := time.Hour
   271  	s.mutex.Lock()
   272  	defer s.mutex.Unlock()
   273  	for macAddr, lease := range s.dynamicLeases {
   274  		expiresIn := time.Until(lease.expires)
   275  		if expiresIn > 0 {
   276  			if expiresIn < waitTime {
   277  				waitTime = expiresIn
   278  			}
   279  		} else {
   280  			delete(s.ipAddrToMacAddr, lease.Address.IpAddress.String())
   281  			delete(s.dynamicLeases, macAddr)
   282  			numExpired++
   283  		}
   284  	}
   285  	if numExpired > 0 {
   286  		s.logger.Debugf(0, "expired %d dynamic leases\n", numExpired)
   287  		if err := s.writeDynamicLeases(); err != nil {
   288  			s.logger.Println(err)
   289  		}
   290  	}
   291  	return waitTime
   292  }
   293  
   294  func (s *DhcpServer) cleanupDynamicLeasesLoop(cleanupTrigger <-chan struct{}) {
   295  	timer := time.NewTimer(time.Second)
   296  	for {
   297  		select {
   298  		case <-cleanupTrigger:
   299  			if !timer.Stop() {
   300  				<-timer.C
   301  			}
   302  			timer.Reset(s.cleanupDynamicLeases())
   303  		case <-timer.C:
   304  			timer.Reset(s.cleanupDynamicLeases())
   305  		}
   306  	}
   307  }
   308  
   309  // This must be called with the lock held.
   310  func (s *DhcpServer) computeLeaseTime(lease *leaseType,
   311  	offer bool) time.Duration {
   312  	if lease.expires.IsZero() {
   313  		return staticLeaseTime
   314  	}
   315  	if offer {
   316  		return time.Minute
   317  	}
   318  	lease.expires = time.Now().Add(dynamicLeaseTime)
   319  	select {
   320  	case s.cleanupTrigger <- struct{}{}:
   321  	default:
   322  	}
   323  	if err := s.writeDynamicLeases(); err != nil {
   324  		s.logger.Println(err)
   325  	}
   326  	return dynamicLeaseTime >> 1
   327  }
   328  
   329  // This must be called with the lock held.
   330  func (s *DhcpServer) findDynamicLease(macAddr, iface string) (
   331  	*leaseType, *subnetType) {
   332  	lease, ok := s.dynamicLeases[macAddr]
   333  	if !ok {
   334  		return nil, nil
   335  	}
   336  	if lease.subnet == nil {
   337  		if subnet := s.findMatchingSubnet(lease.IpAddress); subnet == nil {
   338  			s.logger.Printf("discarding {%s %s}: no subnet\n",
   339  				lease.IpAddress, lease.MacAddress)
   340  			delete(s.ipAddrToMacAddr, lease.IpAddress.String())
   341  			delete(s.dynamicLeases, lease.MacAddress)
   342  			return nil, nil
   343  		} else {
   344  			lease.subnet = subnet
   345  		}
   346  	}
   347  	if !lease.subnet.dynamicOK() {
   348  		s.logger.Printf("discarding {%s %s}: no dynamic leases\n",
   349  			lease.IpAddress, lease.MacAddress)
   350  		delete(s.ipAddrToMacAddr, lease.IpAddress.String())
   351  		delete(s.dynamicLeases, lease.MacAddress)
   352  		return nil, nil
   353  	}
   354  	for _, subnet := range s.interfaceSubnets[iface] {
   355  		if lease.subnet == subnet {
   356  			return lease, subnet
   357  		}
   358  	}
   359  	s.logger.Printf("discarding {%s %s}: no interface\n",
   360  		lease.IpAddress, lease.MacAddress)
   361  	delete(s.ipAddrToMacAddr, lease.IpAddress.String())
   362  	delete(s.dynamicLeases, lease.MacAddress)
   363  	return nil, nil
   364  }
   365  
   366  // This must be called with the lock held.
   367  func (s *DhcpServer) findLease(macAddr, iface string, reqIP net.IP) (
   368  	*leaseType, *subnetType) {
   369  	if lease, subnet := s.findStaticLease(macAddr); lease != nil {
   370  		return lease, subnet
   371  	}
   372  	if lease, subnet := s.findDynamicLease(macAddr, iface); lease != nil {
   373  		return lease, subnet
   374  	}
   375  	for _, subnet := range s.interfaceSubnets[iface] {
   376  		if lease := s.makeDynamicLease(macAddr, subnet, reqIP); lease != nil {
   377  			return lease, subnet
   378  		}
   379  	}
   380  	return nil, nil
   381  }
   382  
   383  // This must be called with the lock held.
   384  func (s *DhcpServer) findMatchingSubnet(ipAddr net.IP) *subnetType {
   385  	for _, subnet := range s.subnets {
   386  		subnetMask := net.IPMask(subnet.IpMask)
   387  		subnetAddr := subnet.IpGateway.Mask(subnetMask)
   388  		if ipAddr.Mask(subnetMask).Equal(subnetAddr) {
   389  			return subnet
   390  		}
   391  	}
   392  	return nil
   393  }
   394  
   395  // This must be called with the lock held.
   396  func (s *DhcpServer) findStaticLease(macAddr string) (*leaseType, *subnetType) {
   397  	if lease, ok := s.staticLeases[macAddr]; ok {
   398  		if lease.subnet != nil {
   399  			return &lease, lease.subnet
   400  		} else {
   401  			return &lease, s.findMatchingSubnet(lease.IpAddress)
   402  		}
   403  	}
   404  	return nil, nil
   405  }
   406  
   407  func (s *DhcpServer) makeAcknowledgmentChannel(ipAddr net.IP) <-chan struct{} {
   408  	ipStr := ipAddr.String()
   409  	newChan := make(chan struct{}, 1)
   410  	s.mutex.Lock()
   411  	oldChan, ok := s.ackChannels[ipStr]
   412  	s.ackChannels[ipStr] = newChan
   413  	s.mutex.Unlock()
   414  	if ok {
   415  		close(oldChan)
   416  	}
   417  	return newChan
   418  }
   419  
   420  // This must be called with the lock held.
   421  func (s *DhcpServer) makeDynamicLease(macAddr string, subnet *subnetType,
   422  	reqIP net.IP) *leaseType {
   423  	if !subnet.dynamicOK() {
   424  		return nil
   425  	}
   426  	stopIP := util.CopyIP(subnet.LastDynamicIP)
   427  	util.IncrementIP(stopIP)
   428  	if len(reqIP) == 4 {
   429  		reqIpString := reqIP.String()
   430  		if _, ok := s.ipAddrToMacAddr[reqIpString]; !ok {
   431  			lowIP := util.CopyIP(subnet.FirstDynamicIP)
   432  			util.DecrementIP(lowIP)
   433  			if util.CompareIPs(lowIP, reqIP) && util.CompareIPs(reqIP, stopIP) {
   434  				lease := leaseType{Address: proto.Address{
   435  					IpAddress:  reqIP,
   436  					MacAddress: macAddr,
   437  				},
   438  					expires: time.Now().Add(time.Second * 10),
   439  					subnet:  subnet,
   440  				}
   441  				s.dynamicLeases[macAddr] = &lease
   442  				s.ipAddrToMacAddr[reqIpString] = macAddr
   443  				select {
   444  				case s.cleanupTrigger <- struct{}{}:
   445  				default:
   446  				}
   447  				return &lease
   448  			}
   449  		}
   450  	}
   451  	if len(subnet.nextDynamicIP) < 4 {
   452  		subnet.nextDynamicIP = util.CopyIP(subnet.FirstDynamicIP)
   453  	}
   454  	initialIp := util.CopyIP(subnet.nextDynamicIP)
   455  	for {
   456  		testIp := util.CopyIP(subnet.nextDynamicIP)
   457  		testIpString := testIp.String()
   458  		util.IncrementIP(subnet.nextDynamicIP)
   459  		if _, ok := s.ipAddrToMacAddr[testIpString]; !ok {
   460  			lease := leaseType{Address: proto.Address{
   461  				IpAddress:  testIp,
   462  				MacAddress: macAddr,
   463  			},
   464  				expires: time.Now().Add(time.Second * 10),
   465  				subnet:  subnet,
   466  			}
   467  			s.dynamicLeases[macAddr] = &lease
   468  			s.ipAddrToMacAddr[testIpString] = macAddr
   469  			select {
   470  			case s.cleanupTrigger <- struct{}{}:
   471  			default:
   472  			}
   473  			return &lease
   474  		}
   475  		if subnet.nextDynamicIP.Equal(stopIP) {
   476  			copy(subnet.nextDynamicIP, subnet.FirstDynamicIP)
   477  		}
   478  		if initialIp.Equal(subnet.nextDynamicIP) {
   479  			break
   480  		}
   481  	}
   482  	return nil
   483  }
   484  
   485  func (s *DhcpServer) makeOptions(subnet *subnetType,
   486  	lease *leaseType) dhcp.Options {
   487  	dnsServers := make([]byte, 0)
   488  	for _, dnsServer := range subnet.DomainNameServers {
   489  		dnsServers = append(dnsServers, dnsServer...)
   490  	}
   491  	leaseOptions := dhcp.Options{
   492  		dhcp.OptionSubnetMask:       subnet.IpMask,
   493  		dhcp.OptionRouter:           subnet.IpGateway,
   494  		dhcp.OptionDomainNameServer: dnsServers,
   495  	}
   496  	if subnet.DomainName != "" {
   497  		leaseOptions[dhcp.OptionDomainName] = []byte(subnet.DomainName)
   498  	}
   499  	if lease.hostname != "" {
   500  		leaseOptions[dhcp.OptionHostName] = []byte(lease.hostname)
   501  	}
   502  	if lease.doNetboot {
   503  		leaseOptions[dhcp.OptionBootFileName] = s.networkBootImage
   504  	}
   505  	return leaseOptions
   506  }
   507  
   508  func (s *DhcpServer) makeRequestChannel(macAddr string) <-chan net.IP {
   509  	s.mutex.Lock()
   510  	defer s.mutex.Unlock()
   511  	if oldChan, ok := s.requestChannels[macAddr]; ok {
   512  		return oldChan
   513  	}
   514  	newChan := make(chan net.IP, 16)
   515  	s.requestChannels[macAddr] = newChan
   516  	return newChan
   517  }
   518  
   519  func (s *DhcpServer) makeSubnet(protoSubnet *proto.Subnet) *subnetType {
   520  	subnet := &subnetType{Subnet: *protoSubnet, myIP: s.myIPs[0]}
   521  	for _, ip := range s.myIPs {
   522  		if ip.Equal(subnet.IpGateway) {
   523  			subnet.amGateway = true
   524  		}
   525  		subnetMask := net.IPMask(subnet.IpMask)
   526  		subnetAddr := subnet.IpGateway.Mask(subnetMask)
   527  		if ip.Mask(subnetMask).Equal(subnetAddr) {
   528  			subnet.myIP = ip
   529  			break
   530  		}
   531  	}
   532  	return subnet
   533  }
   534  
   535  func (s *DhcpServer) notifyRequest(address proto.Address) {
   536  	s.mutex.RLock()
   537  	requestChan, ok := s.requestChannels[address.MacAddress]
   538  	s.mutex.RUnlock()
   539  	if ok {
   540  		select {
   541  		case requestChan <- address.IpAddress:
   542  		default:
   543  		}
   544  	}
   545  }
   546  
   547  // This must be called with the lock held.
   548  func (s *DhcpServer) readDynamicLeases() error {
   549  	var leases []dynamicLeaseType
   550  	if err := json.ReadFromFile(s.dynamicLeasesFile, &leases); err != nil {
   551  		if os.IsNotExist(err) {
   552  			return nil
   553  		}
   554  		return err
   555  	}
   556  	numExpiredLeases := 0
   557  	numValidLeases := 0
   558  	for _, lease := range leases {
   559  		if time.Until(lease.Expires) > 0 {
   560  			s.dynamicLeases[lease.Address.MacAddress] = &leaseType{
   561  				Address: lease.Address,
   562  				expires: lease.Expires,
   563  			}
   564  			s.ipAddrToMacAddr[lease.Address.IpAddress.String()] =
   565  				lease.Address.MacAddress
   566  			numValidLeases++
   567  		} else {
   568  			numExpiredLeases++
   569  		}
   570  	}
   571  	if numExpiredLeases > 0 {
   572  		return s.writeDynamicLeases()
   573  	}
   574  	if numExpiredLeases > 0 || numValidLeases > 0 {
   575  		s.logger.Printf("read dynamic leases: %d valid and %d expired\n",
   576  			numValidLeases, numExpiredLeases)
   577  	}
   578  	return nil
   579  }
   580  
   581  func (s *DhcpServer) removeLease(ipAddr net.IP) {
   582  	if len(ipAddr) < 1 {
   583  		return
   584  	}
   585  	ipStr := ipAddr.String()
   586  	s.mutex.Lock()
   587  	delete(s.staticLeases, s.ipAddrToMacAddr[ipStr])
   588  	delete(s.ipAddrToMacAddr, ipStr)
   589  	ackChan, ok := s.ackChannels[ipStr]
   590  	delete(s.ackChannels, ipStr)
   591  	s.mutex.Unlock()
   592  	if ok {
   593  		close(ackChan)
   594  	}
   595  }
   596  
   597  func (s *DhcpServer) removeSubnet(subnetId string) {
   598  	s.mutex.Lock()
   599  	defer s.mutex.Unlock()
   600  	subnets := make([]*subnetType, 0, len(s.subnets)-1)
   601  	var subnetToDelete *subnetType
   602  	for _, subnet := range s.subnets {
   603  		if subnet.Id == subnetId {
   604  			subnetToDelete = subnet
   605  		} else {
   606  			subnets = append(subnets, subnet)
   607  		}
   608  	}
   609  	s.subnets = subnets
   610  	if subnetToDelete == nil {
   611  		return
   612  	}
   613  	for name, subnets := range s.interfaceSubnets {
   614  		subnets := make([]*subnetType, 0, len(subnets)-1)
   615  		for _, subnet := range subnets {
   616  			if subnet == subnetToDelete {
   617  				s.logger.Printf("detaching subnet GW: %s from interface: %s\n",
   618  					subnet.IpGateway, name)
   619  			} else {
   620  				subnets = append(subnets, subnet)
   621  			}
   622  		}
   623  		s.interfaceSubnets[name] = subnets
   624  	}
   625  }
   626  
   627  func (s *DhcpServer) ServeDHCP(req dhcp.Packet, msgType dhcp.MessageType,
   628  	options dhcp.Options) dhcp.Packet {
   629  	switch msgType {
   630  	case dhcp.Discover:
   631  		macAddr := req.CHAddr().String()
   632  		s.logger.Debugf(1, "Discover from: %s on: %s\n",
   633  			macAddr, s.requestInterface)
   634  		s.mutex.Lock()
   635  		defer s.mutex.Unlock()
   636  		lease, subnet := s.findLease(macAddr, s.requestInterface, nil)
   637  		if lease == nil {
   638  			return nil
   639  		}
   640  		if subnet == nil {
   641  			s.logger.Printf("No subnet found for %s\n", lease.IpAddress)
   642  			return nil
   643  		}
   644  		if !s.checkRouteOnInterface(lease.IpAddress, s.requestInterface) {
   645  			s.logger.Printf(
   646  				"suppressing offer: %s for: %s, wrong interface: %s\n",
   647  				lease.IpAddress, macAddr, s.requestInterface)
   648  			return nil
   649  		}
   650  		s.logger.Debugf(0, "Offer: %s for: %s, server: %s\n",
   651  			lease.IpAddress, macAddr, subnet.myIP)
   652  		leaseOptions := s.makeOptions(subnet, lease)
   653  		packet := dhcp.ReplyPacket(req, dhcp.Offer, subnet.myIP,
   654  			lease.IpAddress, s.computeLeaseTime(lease, true),
   655  			leaseOptions.SelectOrderOrAll(
   656  				options[dhcp.OptionParameterRequestList]))
   657  		packet.SetSIAddr(subnet.myIP)
   658  		return packet
   659  	case dhcp.Request:
   660  		macAddr := req.CHAddr().String()
   661  		reqIP := net.IP(options[dhcp.OptionRequestedIPAddress])
   662  		if reqIP == nil {
   663  			reqIP = net.IP(req.CIAddr())
   664  			s.logger.Debugf(0,
   665  				"Request from: %s on: %s did not request an IP, using: %s\n",
   666  				macAddr, s.requestInterface, reqIP)
   667  		}
   668  		reqIP = util.ShrinkIP(reqIP)
   669  		s.notifyRequest(proto.Address{reqIP, macAddr})
   670  		server, ok := options[dhcp.OptionServerIdentifier]
   671  		if ok {
   672  			serverIP := net.IP(server)
   673  			if !serverIP.IsUnspecified() {
   674  				isMe := false
   675  				for _, ip := range s.myIPs {
   676  					if serverIP.Equal(ip) {
   677  						isMe = true
   678  						break
   679  					}
   680  				}
   681  				if !isMe {
   682  					s.logger.Debugf(0,
   683  						"Request for: %s from: %s to: %s is not me\n",
   684  						reqIP, macAddr, serverIP)
   685  					return nil // Message not for this DHCP server.
   686  				}
   687  			}
   688  		}
   689  		hostname := string(options[dhcp.OptionHostName])
   690  		if hostname != "" {
   691  			s.logger.Debugf(0, "Request for: %s from: %s on: %s HostName=%s\n",
   692  				reqIP, macAddr, s.requestInterface, hostname)
   693  		} else {
   694  			s.logger.Debugf(0, "Request for: %s from: %s on: %s\n",
   695  				reqIP, macAddr, s.requestInterface)
   696  		}
   697  		s.mutex.Lock()
   698  		defer s.mutex.Unlock()
   699  		lease, subnet := s.findLease(macAddr, s.requestInterface, reqIP)
   700  		if lease == nil {
   701  			s.logger.Printf("No lease found for %s\n", macAddr)
   702  			return nil
   703  		}
   704  		if subnet == nil {
   705  			s.logger.Printf("No subnet found for %s\n", lease.IpAddress)
   706  			return nil
   707  		}
   708  		if reqIP.Equal(lease.IpAddress) &&
   709  			s.checkRouteOnInterface(lease.IpAddress, s.requestInterface) {
   710  			leaseOptions := s.makeOptions(subnet, lease)
   711  			go s.acknowledgeLease(lease.IpAddress)
   712  			lease.clientHostname = hostname
   713  			s.logger.Debugf(0, "ACK for: %s to: %s on: %s, server: %s\n",
   714  				reqIP, macAddr, s.requestInterface, subnet.myIP)
   715  			packet := dhcp.ReplyPacket(req, dhcp.ACK, subnet.myIP, reqIP,
   716  				s.computeLeaseTime(lease, false), leaseOptions.SelectOrderOrAll(
   717  					options[dhcp.OptionParameterRequestList]))
   718  			packet.SetSIAddr(subnet.myIP)
   719  			return packet
   720  		} else {
   721  			s.logger.Debugf(0, "NAK for: %s to: %s on: %s\n",
   722  				reqIP, macAddr, s.requestInterface)
   723  			return dhcp.ReplyPacket(req, dhcp.NAK, subnet.myIP, nil, 0, nil)
   724  		}
   725  	default:
   726  		s.logger.Debugf(0, "Unsupported message type: %s on: %s\n",
   727  			msgType, s.requestInterface)
   728  	}
   729  	return nil
   730  }
   731  
   732  // This must be called with the lock held.
   733  func (s *DhcpServer) writeDynamicLeases() error {
   734  	var leases []dynamicLeaseType
   735  	for _, lease := range s.dynamicLeases {
   736  		if time.Until(lease.expires) > 0 {
   737  			leases = append(leases,
   738  				dynamicLeaseType{
   739  					ClientHostName: lease.clientHostname,
   740  					Expires:        lease.expires,
   741  					Address:        lease.Address,
   742  				})
   743  		}
   744  	}
   745  	if len(leases) < 1 {
   746  		os.Remove(s.dynamicLeasesFile)
   747  		return nil
   748  	}
   749  	sort.Slice(leases, func(left, right int) bool {
   750  		return leases[left].Address.IpAddress.String() <
   751  			leases[right].Address.IpAddress.String()
   752  	})
   753  	return json.WriteToFile(s.dynamicLeasesFile, fsutil.PublicFilePerms, "    ",
   754  		leases)
   755  }
   756  
   757  func (s *serveIfConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
   758  	for {
   759  		n, s.cm, addr, err = s.conn.ReadFrom(b)
   760  		if err != nil || s.cm == nil {
   761  			*s.requestInterface = "UNKNOWN"
   762  			break
   763  		}
   764  		if name, ok := s.ifIndices[s.cm.IfIndex]; ok {
   765  			*s.requestInterface = name
   766  			break
   767  		}
   768  	}
   769  	return
   770  }
   771  
   772  func (s *serveIfConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
   773  	s.cm.Src = nil
   774  	return s.conn.WriteTo(b, s.cm, addr)
   775  }
   776  
   777  // This must be called with the lock held.
   778  func (subnet *subnetType) dynamicOK() bool {
   779  	if !subnet.amGateway {
   780  		return false
   781  	}
   782  	if len(subnet.FirstDynamicIP) < 4 || len(subnet.LastDynamicIP) < 4 {
   783  		return false
   784  	}
   785  	return true
   786  }