handle.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. // Copyright 2018-present the CoreDHCP Authors. All rights reserved
  2. // This source code is licensed under the MIT license found in the
  3. // LICENSE file in the root directory of this source tree.
  4. package server
  5. import (
  6. "fmt"
  7. "net"
  8. "sync"
  9. "golang.org/x/net/ipv4"
  10. "golang.org/x/net/ipv6"
  11. "github.com/insomniacslk/dhcp/dhcpv4"
  12. "github.com/insomniacslk/dhcp/dhcpv6"
  13. )
  14. // BUG(Natolumin): Servers not bound to a specific interface may send responses
  15. // on the wrong interface as they will use the default route.
  16. // See https://github.com/coredhcp/coredhcp/issues/52
  17. // HandleMsg6 runs for every received DHCPv6 packet. It will run every
  18. // registered handler in sequence, and reply with the resulting response.
  19. // It will not reply if the resulting response is `nil`.
  20. func (l *listener6) HandleMsg6(buf []byte, oob *ipv6.ControlMessage, peer *net.UDPAddr) {
  21. d, err := dhcpv6.FromBytes(buf)
  22. bufpool.Put(&buf)
  23. if err != nil {
  24. log.Printf("Error parsing DHCPv6 request: %v", err)
  25. return
  26. }
  27. // decapsulate the relay message
  28. msg, err := d.GetInnerMessage()
  29. if err != nil {
  30. log.Warningf("DHCPv6: cannot get inner message: %v", err)
  31. return
  32. }
  33. // Create a suitable basic response packet
  34. var resp dhcpv6.DHCPv6
  35. switch msg.Type() {
  36. case dhcpv6.MessageTypeSolicit:
  37. if msg.GetOneOption(dhcpv6.OptionRapidCommit) != nil {
  38. resp, err = dhcpv6.NewReplyFromMessage(msg)
  39. } else {
  40. resp, err = dhcpv6.NewAdvertiseFromSolicit(msg)
  41. }
  42. case dhcpv6.MessageTypeRequest, dhcpv6.MessageTypeConfirm, dhcpv6.MessageTypeRenew,
  43. dhcpv6.MessageTypeRebind, dhcpv6.MessageTypeRelease, dhcpv6.MessageTypeInformationRequest:
  44. resp, err = dhcpv6.NewReplyFromMessage(msg)
  45. default:
  46. err = fmt.Errorf("MainHandler6: message type %d not supported", msg.Type())
  47. }
  48. if err != nil {
  49. log.Printf("MainHandler6: NewReplyFromDHCPv6Message failed: %v", err)
  50. return
  51. }
  52. var stop bool
  53. for _, handler := range l.handlers {
  54. resp, stop = handler(d, resp)
  55. if stop {
  56. break
  57. }
  58. }
  59. if resp == nil {
  60. log.Print("MainHandler6: dropping request because response is nil")
  61. return
  62. }
  63. // if the request was relayed, re-encapsulate the response
  64. if d.IsRelay() {
  65. if rmsg, ok := resp.(*dhcpv6.Message); !ok {
  66. log.Warningf("DHCPv6: response is a relayed message, not reencapsulating")
  67. } else {
  68. tmp, err := dhcpv6.NewRelayReplFromRelayForw(d.(*dhcpv6.RelayMessage), rmsg)
  69. if err != nil {
  70. log.Warningf("DHCPv6: cannot create relay-repl from relay-forw: %v", err)
  71. return
  72. }
  73. resp = tmp
  74. }
  75. }
  76. if _, err := l.WriteTo(resp.ToBytes(), nil, peer); err != nil {
  77. log.Printf("MainHandler6: conn.Write to %v failed: %v", peer, err)
  78. }
  79. }
  80. func (l *listener4) HandleMsg4(buf []byte, oob *ipv4.ControlMessage, _peer net.Addr) {
  81. var (
  82. resp, tmp *dhcpv4.DHCPv4
  83. err error
  84. stop bool
  85. )
  86. req, err := dhcpv4.FromBytes(buf)
  87. bufpool.Put(&buf)
  88. if err != nil {
  89. log.Printf("Error parsing DHCPv6 request: %v", err)
  90. return
  91. }
  92. if req.OpCode != dhcpv4.OpcodeBootRequest {
  93. log.Printf("MainHandler4: unsupported opcode %d. Only BootRequest (%d) is supported", req.OpCode, dhcpv4.OpcodeBootRequest)
  94. return
  95. }
  96. tmp, err = dhcpv4.NewReplyFromRequest(req)
  97. if err != nil {
  98. log.Printf("MainHandler4: failed to build reply: %v", err)
  99. return
  100. }
  101. switch mt := req.MessageType(); mt {
  102. case dhcpv4.MessageTypeDiscover:
  103. tmp.UpdateOption(dhcpv4.OptMessageType(dhcpv4.MessageTypeOffer))
  104. case dhcpv4.MessageTypeRequest:
  105. tmp.UpdateOption(dhcpv4.OptMessageType(dhcpv4.MessageTypeAck))
  106. default:
  107. log.Printf("plugins/server: Unhandled message type: %v", mt)
  108. return
  109. }
  110. resp = tmp
  111. for _, handler := range l.handlers {
  112. resp, stop = handler(req, resp)
  113. if stop {
  114. break
  115. }
  116. }
  117. if resp != nil {
  118. var peer net.Addr
  119. if !req.GatewayIPAddr.IsUnspecified() {
  120. // TODO: make RFC8357 compliant
  121. peer = &net.UDPAddr{IP: req.GatewayIPAddr, Port: dhcpv4.ServerPort}
  122. } else if resp.MessageType() == dhcpv4.MessageTypeNak {
  123. peer = &net.UDPAddr{IP: net.IPv4bcast, Port: dhcpv4.ClientPort}
  124. } else if !req.ClientIPAddr.IsUnspecified() {
  125. peer = &net.UDPAddr{IP: req.ClientIPAddr, Port: dhcpv4.ClientPort}
  126. } else if req.IsBroadcast() {
  127. peer = &net.UDPAddr{IP: net.IPv4bcast, Port: dhcpv4.ClientPort}
  128. } else {
  129. // FIXME: we're supposed to unicast to a specific *L2* address, and an L3
  130. // address that's not yet assigned.
  131. // I don't know how to do that with this API...
  132. //peer = &net.UDPAddr{IP: resp.YourIPAddr, Port: dhcpv4.ClientPort}
  133. log.Warn("Cannot handle non-broadcast-capable unspecified peers in an RFC-compliant way. " +
  134. "Response will be broadcast")
  135. peer = &net.UDPAddr{IP: net.IPv4bcast, Port: dhcpv4.ClientPort}
  136. }
  137. if _, err := l.WriteTo(resp.ToBytes(), nil, peer); err != nil {
  138. log.Printf("MainHandler4: conn.Write to %v failed: %v", peer, err)
  139. }
  140. } else {
  141. log.Print("MainHandler4: dropping request because response is nil")
  142. }
  143. }
  144. // XXX: performance-wise, Pool may or may not be good (see https://github.com/golang/go/issues/23199)
  145. // Interface is good for what we want. Maybe "just" trust the GC and we'll be fine ?
  146. var bufpool = sync.Pool{New: func() interface{} { r := make([]byte, MaxDatagram); return &r }}
  147. // MaxDatagram is the maximum length of message that can be received.
  148. const MaxDatagram = 1 << 16
  149. // XXX: investigate using RecvMsgs to batch messages and reduce syscalls
  150. // Serve6 handles datagrams received on conn and passes them to the pluginchain
  151. func (l *listener6) Serve() error {
  152. log.Printf("Listen %s", l.LocalAddr())
  153. for {
  154. b := *bufpool.Get().(*[]byte)
  155. b = b[:MaxDatagram] //Reslice to max capacity in case the buffer in pool was resliced smaller
  156. n, oob, peer, err := l.ReadFrom(b)
  157. if err != nil {
  158. log.Printf("Error reading from connection: %v", err)
  159. return err
  160. }
  161. go l.HandleMsg6(b[:n], oob, peer.(*net.UDPAddr))
  162. }
  163. }
  164. // Serve6 handles datagrams received on conn and passes them to the pluginchain
  165. func (l *listener4) Serve() error {
  166. log.Printf("Listen %s", l.LocalAddr())
  167. for {
  168. b := *bufpool.Get().(*[]byte)
  169. b = b[:MaxDatagram] //Reslice to max capacity in case the buffer in pool was resliced smaller
  170. n, oob, peer, err := l.ReadFrom(b)
  171. if err != nil {
  172. log.Printf("Error reading from connection: %v", err)
  173. return err
  174. }
  175. go l.HandleMsg4(b[:n], oob, peer.(*net.UDPAddr))
  176. }
  177. }