handler.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. package reservation
  2. import (
  3. "context"
  4. "dhcp/data"
  5. "errors"
  6. "github.com/go-logr/logr"
  7. "github.com/insomniacslk/dhcp/dhcpv4"
  8. "github.com/rs/zerolog/log"
  9. "golang.org/x/net/ipv4"
  10. "net"
  11. )
  12. // setDefaults will update the Handler struct to have default values so as
  13. // to avoid panic for nil pointers and such.
  14. func (h *Handler) setDefaults() {
  15. if h.Backend == nil {
  16. h.Backend = noop{}
  17. }
  18. if h.Log.GetSink() == nil {
  19. h.Log = logr.Discard()
  20. }
  21. }
  22. // Handle responds to dhcpServer messages with dhcpServer server options.
  23. func (h *Handler) Handle(ctx context.Context, conn *ipv4.PacketConn, p data.Packet) {
  24. h.setDefaults()
  25. if p.Pkt == nil {
  26. h.Log.Error(errors.New("incoming packet is nil"), "not able to respond when the incoming packet is nil")
  27. return
  28. }
  29. upeer, ok := p.Peer.(*net.UDPAddr)
  30. if !ok {
  31. h.Log.Error(errors.New("peer is not a UDP connection"), "not able to respond when the peer is not a UDP connection")
  32. return
  33. }
  34. if upeer == nil {
  35. h.Log.Error(errors.New("peer is nil"), "not able to respond when the peer is nil")
  36. return
  37. }
  38. if conn == nil {
  39. h.Log.Error(errors.New("connection is nil"), "not able to respond when the connection is nil")
  40. return
  41. }
  42. var ifName string
  43. if p.Md != nil {
  44. ifName = p.Md.IfName
  45. }
  46. log := h.Log.WithValues("mac", p.Pkt.ClientHWAddr.String(), "xid", p.Pkt.TransactionID.String(), "interface", ifName)
  47. var reply *dhcpv4.DHCPv4
  48. switch mt := p.Pkt.MessageType(); mt {
  49. case dhcpv4.MessageTypeDiscover:
  50. d, err := h.readBackend(ctx, p.Pkt.ClientHWAddr, ifName)
  51. if err != nil {
  52. if hardwareNotFound(err) {
  53. return
  54. }
  55. log.Info("error reading from macAndIPXE", "error", err)
  56. return
  57. }
  58. log.Info("received dhcpServer packet", "type", p.Pkt.MessageType().String())
  59. reply = h.updateMsg(ctx, &p, d, dhcpv4.MessageTypeOffer)
  60. log = log.WithValues("type", dhcpv4.MessageTypeOffer.String())
  61. case dhcpv4.MessageTypeRequest:
  62. d, err := h.readBackend(ctx, p.Pkt.ClientHWAddr, ifName)
  63. if err != nil {
  64. if hardwareNotFound(err) {
  65. return
  66. }
  67. log.Info("error reading from macAndIPXE", "error", err)
  68. return
  69. }
  70. log.Info("received dhcpServer packet", "type", p.Pkt.MessageType().String())
  71. reply = h.updateMsg(ctx, &p, d, dhcpv4.MessageTypeAck)
  72. log = log.WithValues("type", dhcpv4.MessageTypeAck.String())
  73. case dhcpv4.MessageTypeRelease:
  74. // 固定ip
  75. // 不需要客户端去主动释放
  76. log.Info("received dhcpServer release packet, no response required, all IPs are host reservations", "type", p.Pkt.MessageType().String())
  77. return
  78. default:
  79. log.Info("received unknown message type", "type", p.Pkt.MessageType().String())
  80. return
  81. }
  82. if ns := reply.ServerIPAddr; ns != nil {
  83. log = log.WithValues("nextServer", ns.String())
  84. }
  85. dst := replyDestination(p.Peer, p.Pkt.GatewayIPAddr)
  86. log = log.WithValues("ipAddress", reply.YourIPAddr.String(), "destination", dst.String())
  87. cm := &ipv4.ControlMessage{}
  88. if p.Md != nil {
  89. cm.IfIndex = p.Md.IfIndex
  90. }
  91. if _, err := conn.WriteTo(reply.ToBytes(), cm, dst); err != nil {
  92. log.Error(err, "failed to send dhcpServer")
  93. return
  94. }
  95. log.Info("sent dhcpServer response")
  96. }
  97. // replyDestination determines the destination address for the dhcpServer reply.
  98. // If the giaddr is set, then the reply should be sent to the giaddr.
  99. // Otherwise, the reply should be sent to the direct peer.
  100. //
  101. // From page 22 of https://www.ietf.org/rfc/rfc2131.txt:
  102. // "If the 'giaddr' field in a dhcpServer message from a client is non-zero,
  103. // the server sends any return messages to the 'dhcpServer server' port on
  104. // the BOOTP relay agent whose address appears in 'giaddr'.".
  105. func replyDestination(directPeer net.Addr, giaddr net.IP) net.Addr {
  106. if !giaddr.IsUnspecified() && giaddr != nil {
  107. return &net.UDPAddr{IP: giaddr, Port: dhcpv4.ServerPort}
  108. }
  109. return directPeer
  110. }
  111. // readBackend encapsulates the macAndIPXE read and opentelemetry handling.
  112. func (h *Handler) readBackend(ctx context.Context, mac net.HardwareAddr, ifName string) (*data.DHCP, error) {
  113. h.setDefaults()
  114. d, err := h.Backend.GetByMac(ctx, mac, ifName)
  115. if err != nil {
  116. return nil, err
  117. }
  118. return d, nil
  119. }
  120. // updateMsg handles updating dhcpServer packets with the data from the macAndIPXE.
  121. func (h *Handler) updateMsg(ctx context.Context, p *data.Packet, d *data.DHCP, msgType dhcpv4.MessageType) *dhcpv4.DHCPv4 {
  122. h.setDefaults()
  123. mods := []dhcpv4.Modifier{
  124. dhcpv4.WithMessageType(msgType),
  125. dhcpv4.WithGeneric(dhcpv4.OptionServerIdentifier, h.IPAddr.AsSlice()),
  126. dhcpv4.WithServerIP(d.ServerIP),
  127. dhcpv4.WithHwAddr(p.Pkt.ClientHWAddr),
  128. }
  129. mods = append(mods, h.setDHCPOpts(ctx, p.Pkt, d)...)
  130. // We ignore the error here because:
  131. // 1. it's only non-nil if the generation of a transaction id (XID) fails.
  132. // 2. We always use the clients transaction id (XID) in responses. See dhcpv4.WithReply().
  133. reply, _ := dhcpv4.NewReplyFromRequest(p.Pkt, mods...)
  134. log.Printf("%+v", reply)
  135. return reply
  136. }
  137. // hardwareNotFound returns true if the error is from a hardware record not being found.
  138. func hardwareNotFound(err error) bool {
  139. type hardwareNotFound interface {
  140. NotFound() bool
  141. }
  142. te, ok := err.(hardwareNotFound)
  143. return ok && te.NotFound()
  144. }