123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- package reservation
- import (
- "context"
- "dhcp/data"
- "errors"
- "github.com/go-logr/logr"
- "github.com/insomniacslk/dhcp/dhcpv4"
- "github.com/rs/zerolog/log"
- "golang.org/x/net/ipv4"
- "net"
- )
- // setDefaults will update the Handler struct to have default values so as
- // to avoid panic for nil pointers and such.
- func (h *Handler) setDefaults() {
- if h.Backend == nil {
- h.Backend = noop{}
- }
- if h.Log.GetSink() == nil {
- h.Log = logr.Discard()
- }
- }
- // Handle responds to dhcpServer messages with dhcpServer server options.
- func (h *Handler) Handle(ctx context.Context, conn *ipv4.PacketConn, p data.Packet) {
- h.setDefaults()
- if p.Pkt == nil {
- h.Log.Error(errors.New("incoming packet is nil"), "not able to respond when the incoming packet is nil")
- return
- }
- upeer, ok := p.Peer.(*net.UDPAddr)
- if !ok {
- h.Log.Error(errors.New("peer is not a UDP connection"), "not able to respond when the peer is not a UDP connection")
- return
- }
- if upeer == nil {
- h.Log.Error(errors.New("peer is nil"), "not able to respond when the peer is nil")
- return
- }
- if conn == nil {
- h.Log.Error(errors.New("connection is nil"), "not able to respond when the connection is nil")
- return
- }
- var ifName string
- if p.Md != nil {
- ifName = p.Md.IfName
- }
- log := h.Log.WithValues("mac", p.Pkt.ClientHWAddr.String(), "xid", p.Pkt.TransactionID.String(), "interface", ifName)
- var reply *dhcpv4.DHCPv4
- switch mt := p.Pkt.MessageType(); mt {
- case dhcpv4.MessageTypeDiscover:
- d, err := h.readBackend(ctx, p.Pkt.ClientHWAddr, ifName)
- if err != nil {
- if hardwareNotFound(err) {
- return
- }
- log.Info("error reading from macAndIPXE", "error", err)
- return
- }
- log.Info("received dhcpServer packet", "type", p.Pkt.MessageType().String())
- reply = h.updateMsg(ctx, &p, d, dhcpv4.MessageTypeOffer)
- log = log.WithValues("type", dhcpv4.MessageTypeOffer.String())
- case dhcpv4.MessageTypeRequest:
- d, err := h.readBackend(ctx, p.Pkt.ClientHWAddr, ifName)
- if err != nil {
- if hardwareNotFound(err) {
- return
- }
- log.Info("error reading from macAndIPXE", "error", err)
- return
- }
- log.Info("received dhcpServer packet", "type", p.Pkt.MessageType().String())
- reply = h.updateMsg(ctx, &p, d, dhcpv4.MessageTypeAck)
- log = log.WithValues("type", dhcpv4.MessageTypeAck.String())
- case dhcpv4.MessageTypeRelease:
- // 固定ip
- // 不需要客户端去主动释放
- log.Info("received dhcpServer release packet, no response required, all IPs are host reservations", "type", p.Pkt.MessageType().String())
- return
- default:
- log.Info("received unknown message type", "type", p.Pkt.MessageType().String())
- return
- }
- if ns := reply.ServerIPAddr; ns != nil {
- log = log.WithValues("nextServer", ns.String())
- }
- dst := replyDestination(p.Peer, p.Pkt.GatewayIPAddr)
- log = log.WithValues("ipAddress", reply.YourIPAddr.String(), "destination", dst.String())
- cm := &ipv4.ControlMessage{}
- if p.Md != nil {
- cm.IfIndex = p.Md.IfIndex
- }
- if _, err := conn.WriteTo(reply.ToBytes(), cm, dst); err != nil {
- log.Error(err, "failed to send dhcpServer")
- return
- }
- log.Info("sent dhcpServer response")
- }
- // replyDestination determines the destination address for the dhcpServer reply.
- // If the giaddr is set, then the reply should be sent to the giaddr.
- // Otherwise, the reply should be sent to the direct peer.
- //
- // From page 22 of https://www.ietf.org/rfc/rfc2131.txt:
- // "If the 'giaddr' field in a dhcpServer message from a client is non-zero,
- // the server sends any return messages to the 'dhcpServer server' port on
- // the BOOTP relay agent whose address appears in 'giaddr'.".
- func replyDestination(directPeer net.Addr, giaddr net.IP) net.Addr {
- if !giaddr.IsUnspecified() && giaddr != nil {
- return &net.UDPAddr{IP: giaddr, Port: dhcpv4.ServerPort}
- }
- return directPeer
- }
- // readBackend encapsulates the macAndIPXE read and opentelemetry handling.
- func (h *Handler) readBackend(ctx context.Context, mac net.HardwareAddr, ifName string) (*data.DHCP, error) {
- h.setDefaults()
- d, err := h.Backend.GetByMac(ctx, mac, ifName)
- if err != nil {
- return nil, err
- }
- return d, nil
- }
- // updateMsg handles updating dhcpServer packets with the data from the macAndIPXE.
- func (h *Handler) updateMsg(ctx context.Context, p *data.Packet, d *data.DHCP, msgType dhcpv4.MessageType) *dhcpv4.DHCPv4 {
- h.setDefaults()
- mods := []dhcpv4.Modifier{
- dhcpv4.WithMessageType(msgType),
- dhcpv4.WithGeneric(dhcpv4.OptionServerIdentifier, h.IPAddr.AsSlice()),
- dhcpv4.WithServerIP(d.ServerIP),
- dhcpv4.WithHwAddr(p.Pkt.ClientHWAddr),
- }
- mods = append(mods, h.setDHCPOpts(ctx, p.Pkt, d)...)
- // We ignore the error here because:
- // 1. it's only non-nil if the generation of a transaction id (XID) fails.
- // 2. We always use the clients transaction id (XID) in responses. See dhcpv4.WithReply().
- reply, _ := dhcpv4.NewReplyFromRequest(p.Pkt, mods...)
- log.Printf("%+v", reply)
- return reply
- }
- // hardwareNotFound returns true if the error is from a hardware record not being found.
- func hardwareNotFound(err error) bool {
- type hardwareNotFound interface {
- NotFound() bool
- }
- te, ok := err.(hardwareNotFound)
- return ok && te.NotFound()
- }
|