func (h *DHCPHandler) ServeDHCP(p dhcp4.Packet, msgType dhcp4.MessageType, options dhcp4.Options) (d dhcp4.Packet) { dns, err := h.datasource.DNSAddresses() if err != nil { logging.Log(debugTag, "Failed to read dns addresses") return nil } dhcpOptions := dhcp4.Options{ dhcp4.OptionSubnetMask: h.settings.SubnetMask.To4(), dhcp4.OptionDomainNameServer: dns, } if h.settings.RouterAddr != nil { dhcpOptions[dhcp4.OptionRouter] = h.settings.RouterAddr.To4() } macAddress := strings.Join(strings.Split(p.CHAddr().String(), ":"), "") switch msgType { case dhcp4.Discover: ip, err := h.datasource.Assign(p.CHAddr().String()) if err != nil { logging.Debug("DHCP", "err in lease pool - %s", err.Error()) return nil // pool is full } replyOptions := dhcpOptions.SelectOrderOrAll(options[dhcp4.OptionParameterRequestList]) packet := dhcp4.ReplyPacket(p, dhcp4.Offer, h.settings.ServerIP, ip, randLeaseDuration(), replyOptions) // this is a pxe request guidVal, isPxe := options[97] if isPxe { logging.Log("DHCP", "dhcp discover with PXE - CHADDR %s - IP %s - our ip %s", p.CHAddr().String(), ip.String(), h.settings.ServerIP.String()) guid := guidVal[1:] packet.AddOption(60, []byte("PXEClient")) packet.AddOption(97, guid) packet.AddOption(43, h.fillPXE()) } else { logging.Log("DHCP", "dhcp discover - CHADDR %s - IP %s", p.CHAddr().String(), ip.String()) } return packet case dhcp4.Request: if server, ok := options[dhcp4.OptionServerIdentifier]; ok && !net.IP(server).Equal(h.settings.ServerIP) { return nil // this message is not ours } requestedIP := net.IP(options[dhcp4.OptionRequestedIPAddress]) if requestedIP == nil { requestedIP = net.IP(p.CIAddr()) } if len(requestedIP) != 4 || requestedIP.Equal(net.IPv4zero) { logging.Debug("DHCP", "dhcp request - CHADDR %s - bad request", p.CHAddr().String()) return nil } _, err := h.datasource.Request(p.CHAddr().String(), requestedIP) if err != nil { logging.Debug("DHCP", "dhcp request - CHADDR %s - Requested IP %s - NO MATCH", p.CHAddr().String(), requestedIP.String()) return dhcp4.ReplyPacket(p, dhcp4.NAK, h.settings.ServerIP, nil, 0, nil) } replyOptions := dhcpOptions.SelectOrderOrAll(options[dhcp4.OptionParameterRequestList]) packet := dhcp4.ReplyPacket(p, dhcp4.ACK, h.settings.ServerIP, requestedIP, randLeaseDuration(), replyOptions) // this is a pxe request guidVal, isPxe := options[97] if isPxe { logging.Log("DHCP", "dhcp request with PXE - CHADDR %s - Requested IP %s - our ip %s - ACCEPTED", p.CHAddr().String(), requestedIP.String(), h.settings.ServerIP.String()) guid := guidVal[1:] packet.AddOption(60, []byte("PXEClient")) packet.AddOption(97, guid) packet.AddOption(43, h.fillPXE()) } else { logging.Log("DHCP", "dhcp request - CHADDR %s - Requested IP %s - ACCEPTED", p.CHAddr().String(), requestedIP.String()) } packet.AddOption(12, []byte("node"+macAddress+"."+h.datasource.ClusterName())) // host name option return packet case dhcp4.Release, dhcp4.Decline: return nil } return nil }
//ServeDHCP : function for every dhcp request func (h *DHCPHandler) ServeDHCP(p dhcp.Packet, msgType dhcp.MessageType, options dhcp.Options) (d dhcp.Packet) { // options for booting device skinnyOptions := dhcp.Options{ dhcp.OptionSubnetMask: []byte(h.config.Subnet.To4()), dhcp.OptionBootFileName: []byte("http://" + h.ip.String() + "/choose"), dhcp.OptionRouter: []byte(h.config.Gateway.To4()), dhcp.OptionDomainNameServer: []byte(h.config.DNSServer.To4()), dhcp.OptionDomainName: []byte(h.config.Domain), } // get an existing lease or make a new one TheLease, err := h.leases.GetLease(p.CHAddr()) logger.Info("%s has an ip of %s ", TheLease.MAC, TheLease.IP) if err != nil { logger.Critical("lease get fail , %s", err) return nil } // handle the DHCP transactions switch msgType { case dhcp.Discover: logger.Debug("Discover %s", p.CHAddr()) return dhcp.ReplyPacket(p, dhcp.Offer, h.config.BaseIP.To4(), TheLease.GetIP(), h.leaseDuration, h.options.SelectOrderOrAll(options[dhcp.OptionParameterRequestList])) case dhcp.Request: logger.Debug("Request %s", p.CHAddr()) userClass := string(options[77]) switch userClass { // initial hardware boot case "iPXE": logger.Notice("iPXE from %s on %v", TheLease.MAC, TheLease.Name) rp := dhcp.ReplyPacket(p, dhcp.ACK, h.config.BaseIP.To4(), net.IP(options[dhcp.OptionRequestedIPAddress]), h.leaseDuration, h.options.SelectOrderOrAll(options[dhcp.OptionParameterRequestList])) rp.SetSIAddr(h.ip) return rp // scondary iPXE boot from tftp server case "skinny": logger.Notice("Booting Machine %s into %s", TheLease.Name, TheLease.Class) if TheLease.Active == true { skinnyOptions[dhcp.OptionHostName] = []byte(TheLease.Name) skinnyOptions[dhcp.OptionBootFileName] = []byte("http://" + h.ip.String() + "/boot/" + TheLease.Distro + "/${net0/mac}") } rp := dhcp.ReplyPacket(p, dhcp.ACK, h.config.BaseIP.To4(), net.IP(options[dhcp.OptionRequestedIPAddress]), h.leaseDuration, skinnyOptions.SelectOrderOrAll(options[dhcp.OptionParameterRequestList])) return rp default: logger.Info("normal dhcp request") if TheLease.Active == true { skinnyOptions[dhcp.OptionHostName] = []byte(TheLease.Name) } rp := dhcp.ReplyPacket(p, dhcp.ACK, h.config.BaseIP.To4(), net.IP(options[dhcp.OptionRequestedIPAddress]), h.leaseDuration, h.options.SelectOrderOrAll(options[dhcp.OptionParameterRequestList])) return rp } case dhcp.Release: logger.Debug("Release") break case dhcp.Decline: logger.Debug("Decline") break } return nil }
// ServeDHCP replies a dhcp request func (h *Handler) ServeDHCP(p dhcp4.Packet, msgType dhcp4.MessageType, options dhcp4.Options) (d dhcp4.Packet) { switch msgType { case dhcp4.Discover, dhcp4.Request: if server, ok := options[dhcp4.OptionServerIdentifier]; ok && !net.IP(server).Equal(h.serverIP) { if msgType == dhcp4.Discover { log.WithField("where", "dhcp.ServeDHCP").Debugf( "identifying dhcp server in Discover?! (%v)", p) } return nil // this message is not ours } machineInterface := h.datasource.MachineInterface(p.CHAddr()) machine, err := machineInterface.Machine(true, nil) if err != nil { log.WithField("where", "dhcp.ServeDHCP").WithError(err).Warn( "failed to get machine") return nil } netConfStr, err := machineInterface.GetVariable(datasource.SpecialKeyNetworkConfiguration) if err != nil { log.WithField("where", "dhcp.ServeDHCP").WithError(err).Warn( "failed to get network configuration") return nil } netConf, err := datasource.UnmarshalNetworkConfiguration(netConfStr) if err != nil { log.WithField("where", "dhcp.ServeDHCP").WithError(err).Warn( "failed to unmarshal network-configuration=%q", netConfStr) return nil } instanceInfos, err := h.datasource.Instances() if err != nil { log.WithField("where", "dhcp.ServeDHCP").WithError(err).Warn( "failed to get instances") return nil } hostname := strings.Join(strings.Split(p.CHAddr().String(), ":"), "") hostname += "." + h.datasource.ClusterName() dhcpOptions := dhcp4.Options{ dhcp4.OptionSubnetMask: netConf.Netmask.To4(), dhcp4.OptionDomainNameServer: dnsAddressesForDHCP(&instanceInfos), dhcp4.OptionHostName: []byte(hostname), } if netConf.Router != nil { dhcpOptions[dhcp4.OptionRouter] = netConf.Router.To4() } if len(netConf.ClasslessRouteOption) != 0 { var res []byte for _, part := range netConf.ClasslessRouteOption { res = append(res, part.ToBytes()...) } dhcpOptions[dhcp4.OptionClasslessRouteFormat] = res } responseMsgType := dhcp4.Offer if msgType == dhcp4.Request { responseMsgType = dhcp4.ACK requestedIP := net.IP(options[dhcp4.OptionRequestedIPAddress]) if requestedIP == nil { requestedIP = net.IP(p.CIAddr()) } if len(requestedIP) != 4 || requestedIP.Equal(net.IPv4zero) { log.WithFields(log.Fields{ "where": "dhcp.ServeDHCP", "object": p.CHAddr().String(), "subject": msgType, }).Debugf("bad request") return nil } if !requestedIP.Equal(machine.IP) { log.WithFields(log.Fields{ "where": "dhcp.ServeDHCP", "object": p.CHAddr().String(), "subject": msgType, }).Debugf("requestedIP(%s) != assignedIp(%s)", requestedIP.String(), machine.IP.String()) return nil } machineInterface.CheckIn() } guidVal, isPxe := options[97] log.WithFields(log.Fields{ "where": "dhcp.ServeDHCP", "action": "debug", "object": p.CHAddr().String(), "subject": msgType, }).Infof("assignedIp=%s isPxe=%v", machine.IP.String(), isPxe) replyOptions := dhcpOptions.SelectOrderOrAll(options[dhcp4.OptionParameterRequestList]) if isPxe { // this is a pxe request guid := guidVal[1:] replyOptions = append(replyOptions, dhcp4.Option{ Code: dhcp4.OptionVendorClassIdentifier, Value: []byte("PXEClient"), }, dhcp4.Option{ Code: 97, // UUID/GUID-based Client Identifier Value: guid, }, dhcp4.Option{ Code: dhcp4.OptionVendorSpecificInformation, Value: h.fillPXE(), }, ) hash, err := h.datasource.GetClusterVariable(datasource.ActiveWorkspaceHashKey) if err == nil { machineInterface.SetVariable("booted-workspace-hash", hash) } } packet := dhcp4.ReplyPacket(p, responseMsgType, h.serverIP, machine.IP, randLeaseDuration(), replyOptions) return packet case dhcp4.Release, dhcp4.Decline: return nil } return nil }