Пример #1
0
func (d *DHCPService) getRequestState(packet dhcp4.Packet, reqOptions dhcp4.Options) (string, net.IP) {
	state := "NEW"
	requestedIP := net.IP(reqOptions[dhcp4.OptionRequestedIPAddress])
	if len(requestedIP) == 0 || requestedIP.IsUnspecified() { // empty
		state = "RENEWAL"
		requestedIP = packet.CIAddr()
	}
	return state, requestedIP
}
Пример #2
0
func (h *DHCPHandler) ServeDHCP(p dhcp.Packet, msgType dhcp.MessageType, options dhcp.Options) (d dhcp.Packet) {
	switch msgType {

	case dhcp.Discover:
		free, nic := -1, p.CHAddr().String()
		for i, v := range h.leases { // Find previous lease
			if v.nic == nic {
				free = i
				goto reply
			}
		}
		if free = h.freeLease(); free == -1 {
			return
		}
	reply:
		return dhcp.ReplyPacket(p, dhcp.Offer, h.ip, dhcp.IPAdd(h.start, free), h.leaseDuration,
			h.options.SelectOrderOrAll(options[dhcp.OptionParameterRequestList]))

	case dhcp.Request:
		if server, ok := options[dhcp.OptionServerIdentifier]; ok && !net.IP(server).Equal(h.ip) {
			return nil // Message not for this dhcp server
		}
		reqIP := net.IP(options[dhcp.OptionRequestedIPAddress])
		if reqIP == nil {
			reqIP = net.IP(p.CIAddr())
		}

		if len(reqIP) == 4 && !reqIP.Equal(net.IPv4zero) {
			if leaseNum := dhcp.IPRange(h.start, reqIP) - 1; leaseNum >= 0 && leaseNum < h.leaseRange {
				if l, exists := h.leases[leaseNum]; !exists || l.nic == p.CHAddr().String() {
					h.leases[leaseNum] = lease{nic: p.CHAddr().String(), expiry: time.Now().Add(h.leaseDuration)}
					return dhcp.ReplyPacket(p, dhcp.ACK, h.ip, net.IP(options[dhcp.OptionRequestedIPAddress]), h.leaseDuration,
						h.options.SelectOrderOrAll(options[dhcp.OptionParameterRequestList]))
				}
			}
		}
		return dhcp.ReplyPacket(p, dhcp.NAK, h.ip, nil, 0, nil)

	case dhcp.Release, dhcp.Decline:
		nic := p.CHAddr().String()
		for i, v := range h.leases {
			if v.nic == nic {
				delete(h.leases, i)
				break
			}
		}
	}
	return nil
}
Пример #3
0
// ReplyPacket creates a reply packet that a Server would send to a client.
// It uses the req Packet param to copy across common/necessary fields to
// associate the reply with the request.
func informReplyPacket(req dhcp4.Packet, mt dhcp4.MessageType, serverID net.IP, options []dhcp4.Option) dhcp4.Packet {
	p := dhcp4.NewPacket(dhcp4.BootReply)
	p.SetXId(req.XId())
	p.SetHType(req.HType())
	p[2] = req.HLen() // dhcp4 library does not provide a setter
	p.SetFlags(req.Flags())
	p.SetCIAddr(req.CIAddr())
	p.SetCHAddr(req.CHAddr())
	p.AddOption(dhcp4.OptionDHCPMessageType, []byte{byte(mt)})
	p.AddOption(dhcp4.OptionServerIdentifier, []byte(serverID))
	for _, o := range options {
		p.AddOption(o.Code, o.Value)
	}
	p.PadToMinSize()
	return p
}
Пример #4
0
func (h *DHCPHandler) ServeDHCP(p dhcp4.Packet, msgType dhcp4.MessageType, options dhcp4.Options) (d dhcp4.Packet) {
	var macAddress string = strings.Join(strings.Split(p.CHAddr().String(), ":"), "")
	switch msgType {
	case dhcp4.Discover:
		ip, err := h.leasePool.Assign(p.CHAddr().String())
		if err != nil {
			logging.Debug("DHCP", "err in lease pool - %s", err.Error())
			return nil // pool is full
		}
		replyOptions := h.dhcpOptions.SelectOrderOrAll(options[dhcp4.OptionParameterRequestList])
		packet := dhcp4.ReplyPacket(p, dhcp4.Offer, h.settings.ServerIP, ip, h.settings.LeaseDuration, replyOptions)
		// this is a pxe request
		guidVal, is_pxe := options[97]
		if is_pxe {
			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.leasePool.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 := h.dhcpOptions.SelectOrderOrAll(options[dhcp4.OptionParameterRequestList])
		packet := dhcp4.ReplyPacket(p, dhcp4.ACK, h.settings.ServerIP, requestedIP, h.settings.LeaseDuration, replyOptions)
		// this is a pxe request
		guidVal, is_pxe := options[97]
		if is_pxe {
			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)) // host name option
		return packet
	case dhcp4.Release, dhcp4.Decline:
		return nil
	}
	return nil
}
Пример #5
0
// ServeDHCP is called by dhcp4.ListenAndServe when the service is started
func (d *DHCPService) ServeDHCP(packet dhcp4.Packet, msgType dhcp4.MessageType, reqOptions dhcp4.Options) (response dhcp4.Packet) {
	switch msgType {
	case dhcp4.Discover:
		// RFC 2131 4.3.1
		// FIXME: send to StatHat and/or increment a counter
		mac := packet.CHAddr()

		// Check MAC blacklist
		if !d.isMACPermitted(mac) {
			log.Printf("DHCP Discover from %s\n is not permitted", mac.String())
			return nil
		}
		log.Printf("DHCP Discover from %s\n", mac.String())

		// Look up the MAC entry with cascaded attributes
		lease, found, err := d.db.GetMAC(mac, true)
		if err != nil {
			return nil
		}

		// Existing Lease
		if found {
			options := d.getOptionsFromMAC(lease)
			log.Printf("DHCP Discover from %s (we offer %s from current lease)\n", lease.MAC.String(), lease.IP.String())
			// for x, y := range reqOptions {
			// 	log.Printf("\tR[%v] %v %s\n", x, y, y)
			// }
			// for x, y := range options {
			// 	log.Printf("\tO[%v] %v %s\n", x, y, y)
			// }
			return dhcp4.ReplyPacket(packet, dhcp4.Offer, d.ip.To4(), lease.IP.To4(), d.getLeaseDurationForRequest(reqOptions, lease.Duration), options.SelectOrderOrAll(reqOptions[dhcp4.OptionParameterRequestList]))
		}

		// New Lease
		ip := d.getIPFromPool()
		if ip != nil {
			options := d.getOptionsFromMAC(lease)
			log.Printf("DHCP Discover from %s (we offer %s from pool)\n", mac.String(), ip.String())
			// for x, y := range reqOptions {
			// 	log.Printf("\tR[%v] %v %s\n", x, y, y)
			// }
			// for x, y := range options {
			// 	log.Printf("\tO[%v] %v %s\n", x, y, y)
			// }
			return dhcp4.ReplyPacket(packet, dhcp4.Offer, d.ip.To4(), ip.To4(), d.getLeaseDurationForRequest(reqOptions, d.leaseDuration), options.SelectOrderOrAll(reqOptions[dhcp4.OptionParameterRequestList]))
		}

		log.Printf("DHCP Discover from %s (no offer due to no addresses available in pool)\n", mac.String())
		// FIXME: Send to StatHat and/or increment a counter
		// TODO: Send an email?

		return nil

	case dhcp4.Request:
		// RFC 2131 4.3.2
		// FIXME: send to StatHat and/or increment a counter
		mac := packet.CHAddr()

		// Check MAC blacklist
		if !d.isMACPermitted(mac) {
			log.Printf("DHCP Request from %s\n is not permitted", mac.String())
			return nil
		}

		// Check IP presence
		state, requestedIP := d.getRequestState(packet, reqOptions)
		log.Printf("DHCP Request (%s) from %s...\n", state, mac.String())
		if len(requestedIP) == 0 || requestedIP.IsUnspecified() { // no IP provided at all... why? FIXME
			log.Printf("DHCP Request (%s) from %s (empty IP, so we're just ignoring this request)\n", state, mac.String())
			return nil
		}

		// Check IPv4
		if len(requestedIP) != net.IPv4len {
			log.Printf("DHCP Request (%s) from %s wanting %s (IPv6 address requested, so we're just ignoring this request)\n", state, mac.String(), requestedIP.String())
			return nil
		}

		// Check IP subnet
		if !d.subnet.Contains(requestedIP) {
			log.Printf("DHCP Request (%s) from %s wanting %s (we reject due to wrong subnet)\n", state, mac.String(), requestedIP.String())
			return dhcp4.ReplyPacket(packet, dhcp4.NAK, d.ip.To4(), nil, 0, nil)
		}

		// Check Target Server
		targetServerIP := packet.SIAddr()
		if len(targetServerIP) > 0 && !targetServerIP.IsUnspecified() {
			log.Printf("DHCP Request (%s) from %s wanting %s is in response to a DHCP offer from %s\n", state, mac.String(), requestedIP.String(), targetServerIP.String())
			if d.ip.Equal(targetServerIP) {
				return nil
			}
		}

		// Process Request
		log.Printf("DHCP Request (%s) from %s wanting %s...\n", state, mac.String(), requestedIP.String())
		lease, found, err := d.db.GetMAC(mac, true)
		if err != nil {
			return nil
		}

		if found {
			// Existing Lease
			lease.Duration = d.getLeaseDurationForRequest(reqOptions, d.leaseDuration)
			if lease.IP.Equal(requestedIP) {
				err = d.db.RenewLease(lease)
			} else {
				log.Printf("DHCP Request (%s) from %s wanting %s (we reject due to lease mismatch, should be %s)\n", state, lease.MAC.String(), requestedIP.String(), lease.IP.String())
				return dhcp4.ReplyPacket(packet, dhcp4.NAK, d.ip.To4(), nil, 0, nil)
			}
		} else {
			// Check IP subnet is within the guestPool (we don't want users requesting non-pool addresses unless we assigned it to their MAC, administratively)
			if !d.guestPool.Contains(requestedIP) {
				log.Printf("DHCP Request (%s) from %s wanting %s (we reject due to not being within the guestPool)\n", state, mac.String(), requestedIP.String())
				return dhcp4.ReplyPacket(packet, dhcp4.NAK, d.ip.To4(), nil, 0, nil)
			}

			// New lease
			lease = &MACEntry{
				MAC:      mac,
				IP:       requestedIP,
				Duration: d.getLeaseDurationForRequest(reqOptions, d.leaseDuration),
			}
			err = d.db.CreateLease(lease)
		}

		if err == nil {
			d.maintainDNSRecords(lease, packet, reqOptions) // TODO: Move this?
			options := d.getOptionsFromMAC(lease)
			log.Printf("DHCP Request (%s) from %s wanting %s (we agree)\n", state, mac.String(), requestedIP.String())
			return dhcp4.ReplyPacket(packet, dhcp4.ACK, d.ip.To4(), requestedIP.To4(), lease.Duration, options.SelectOrderOrAll(reqOptions[dhcp4.OptionParameterRequestList]))
		}

		log.Printf("DHCP Request (%s) from %s wanting %s (we reject due to address collision)\n", state, mac.String(), requestedIP.String())
		return dhcp4.ReplyPacket(packet, dhcp4.NAK, d.ip.To4(), nil, 0, nil)

	case dhcp4.Decline:
		// RFC 2131 4.3.3
		// FIXME: release from DB?  tick a flag?  increment a counter?  send to StatHat?
		mac := packet.CHAddr()
		log.Printf("DHCP Decline from %s\n", mac.String())

	case dhcp4.Release:
		// RFC 2131 4.3.4
		// FIXME: release from DB?  tick a flag?  increment a counter?  send to StatHat?
		mac := packet.CHAddr()
		log.Printf("DHCP Release from %s\n", mac.String())

	case dhcp4.Inform:
		// RFC 2131 4.3.5
		// https://tools.ietf.org/html/draft-ietf-dhc-dhcpinform-clarify-06
		// FIXME: release from DB?  tick a flag?  increment a counter?  send to StatHat?
		// FIXME: we should reply with valuable info, but not assign an IP to this client, per RFC 2131 for DHCPINFORM
		// NOTE: the client's IP is supposed to only be in the ciaddr field, not the requested IP field, per RFC 2131 4.4.3
		mac := packet.CHAddr()
		ip := packet.CIAddr()
		if len(ip) > 0 && !ip.IsUnspecified() {
			log.Printf("DHCP Inform from %s for %s \n", mac.String(), ip.String())
			if len(ip) == net.IPv4len && d.guestPool.Contains(ip) {
				entry, found, _ := d.db.GetMAC(mac, true)
				if found {
					options := d.getOptionsFromMAC(entry)
					return informReplyPacket(packet, dhcp4.ACK, d.ip.To4(), options.SelectOrderOrAll(reqOptions[dhcp4.OptionParameterRequestList]))
				}
			}
		}
	}

	return nil
}
Пример #6
0
// 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
}
Пример #7
0
func (h *DHCPHandler) ServeDHCP(p dhcp.Packet, msgType dhcp.MessageType, options dhcp.Options) (d dhcp.Packet) {

	// First find the subnet to use. giaddr field to lookup subnet if not all zeros.
	// If all zeros, use the interfaces Addrs to find a subnet, first wins.
	var subnet *Subnet
	subnet = nil

	giaddr := p.GIAddr()
	if !giaddr.Equal(net.IPv4zero) {
		subnet = h.info.FindSubnet(giaddr)
	} else {
		log.Println("Received Broadcast/Local message on ", h.intf.Name)
		addrs, err := h.intf.Addrs()
		if err != nil {
			log.Println("Can't find addresses for ", h.intf.Name, ": ", err)
		}

		for _, a := range addrs {
			aip, _, _ := net.ParseCIDR(a.String())

			// Only operate on v4 addresses
			if aip.To4() == nil {
				continue
			}

			subnet = h.info.FindSubnet(aip)
			if subnet != nil {
				break
			}

		}

		if ignore_anonymus {
			// Search all subnets for a binding. First wins
			log.Println("Looking up bound subnet for ", p.CHAddr().String())
			subnet = h.info.FindBoundIP(p.CHAddr())
		}

		if subnet == nil {
			// We didn't find a subnet for the interface.  Look for the assigned server IP
			subnet = h.info.FindSubnet(h.ip)
		}

	}

	if subnet == nil {
		log.Println("Can not find subnet for packet, ignoring")
		return
	}

	nic := p.CHAddr().String()
	switch msgType {

	case dhcp.Discover:
		lease, binding := subnet.find_or_get_info(h.info, nic, p.CIAddr())
		if lease == nil {
			log.Println("Out of IPs for ", subnet.Name, ", ignoring")
			return nil
		}
		// Ignore unknown MAC address
		if ignore_anonymus && binding == nil {
			log.Println("Ignoring request from unknown MAC address")
			return dhcp.ReplyPacket(p, dhcp.NAK, h.ip, nil, 0, nil)
		}

		options, lease_time := subnet.build_options(lease, binding)

		reply := dhcp.ReplyPacket(p, dhcp.Offer,
			h.ip,
			lease.Ip,
			lease_time,
			subnet.Options.SelectOrderOrAll(options[dhcp.OptionParameterRequestList]))
		log.Println("Discover: Handing out: ", reply.YIAddr(), " to ", reply.CHAddr())
		return reply

	case dhcp.Request:
		server, ok := options[dhcp.OptionServerIdentifier]
		if ok && !net.IP(server).Equal(h.ip) {
			return nil // Message not for this dhcp server
		}
		reqIP := net.IP(options[dhcp.OptionRequestedIPAddress])
		if reqIP == nil {
			reqIP = net.IP(p.CIAddr())
		}

		if len(reqIP) != 4 || reqIP.Equal(net.IPv4zero) {
			return dhcp.ReplyPacket(p, dhcp.NAK, h.ip, nil, 0, nil)
		}

		lease, binding := subnet.find_info(h.info, nic)
		// Ignore unknown MAC address
		if ignore_anonymus && binding == nil {
			log.Println("Ignoring request from unknown MAC address")
			return dhcp.ReplyPacket(p, dhcp.NAK, h.ip, nil, 0, nil)
		}
		if lease == nil || !lease.Ip.Equal(reqIP) {
			return dhcp.ReplyPacket(p, dhcp.NAK, h.ip, nil, 0, nil)
		}

		options, lease_time := subnet.build_options(lease, binding)

		subnet.update_lease_time(h.info, lease, lease_time)

		reply := dhcp.ReplyPacket(p, dhcp.ACK,
			h.ip,
			lease.Ip,
			lease_time,
			subnet.Options.SelectOrderOrAll(options[dhcp.OptionParameterRequestList]))
		if binding != nil && binding.NextServer != nil {
			reply.SetSIAddr(net.ParseIP(*binding.NextServer))
		} else if subnet.NextServer != nil {
			reply.SetSIAddr(*subnet.NextServer)
		}
		log.Println("Request: Handing out: ", reply.YIAddr(), " to ", reply.CHAddr())
		return reply

	case dhcp.Release, dhcp.Decline:
		nic := p.CHAddr().String()
		subnet.free_lease(h.info, nic)
	}
	return nil
}