github.com/Cloud-Foundations/Dominator@v0.3.4/hypervisor/dhcpd/impl.go (about)

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