plugin.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  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 serverid
  5. import (
  6. "errors"
  7. "net"
  8. "strings"
  9. "github.com/coredhcp/coredhcp/handler"
  10. "github.com/coredhcp/coredhcp/logger"
  11. "github.com/coredhcp/coredhcp/plugins"
  12. "github.com/insomniacslk/dhcp/dhcpv4"
  13. "github.com/insomniacslk/dhcp/dhcpv6"
  14. "github.com/insomniacslk/dhcp/iana"
  15. )
  16. var log = logger.GetLogger("plugins/server_id")
  17. // Plugin wraps plugin registration information
  18. var Plugin = plugins.Plugin{
  19. Name: "server_id",
  20. Setup6: setup6,
  21. Setup4: setup4,
  22. }
  23. // v6ServerID is the DUID of the v6 server
  24. var (
  25. v6ServerID *dhcpv6.Duid
  26. v4ServerID net.IP
  27. )
  28. // Handler6 handles DHCPv6 packets for the server_id plugin.
  29. func Handler6(req, resp dhcpv6.DHCPv6) (dhcpv6.DHCPv6, bool) {
  30. if v6ServerID == nil {
  31. log.Fatal("BUG: Plugin is running uninitialized!")
  32. return nil, true
  33. }
  34. msg, err := req.GetInnerMessage()
  35. if err != nil {
  36. // BUG: this should already have failed in the main handler. Abort
  37. log.Error(err)
  38. return nil, true
  39. }
  40. if opt := msg.GetOneOption(dhcpv6.OptionServerID); opt != nil {
  41. // RFC8415 §16.{2,5,7}
  42. // These message types MUST be discarded if they contain *any* ServerID option
  43. if msg.Type() == dhcpv6.MessageTypeSolicit ||
  44. msg.Type() == dhcpv6.MessageTypeConfirm ||
  45. msg.Type() == dhcpv6.MessageTypeRebind {
  46. return nil, true
  47. }
  48. // Approximately all others MUST be discarded if the ServerID doesn't match
  49. sid := opt.(*dhcpv6.OptServerId)
  50. if !sid.Sid.Equal(*v6ServerID) {
  51. log.Infof("requested server ID does not match this server's ID. Got %v, want %v", sid.Sid, *v6ServerID)
  52. return nil, true
  53. }
  54. } else {
  55. // RFC8415 §16.{6,8,10,11}
  56. // These message types MUST be discarded if they *don't* contain a ServerID option
  57. if msg.Type() == dhcpv6.MessageTypeRequest ||
  58. msg.Type() == dhcpv6.MessageTypeRenew ||
  59. msg.Type() == dhcpv6.MessageTypeDecline ||
  60. msg.Type() == dhcpv6.MessageTypeRelease {
  61. return nil, true
  62. }
  63. }
  64. dhcpv6.WithServerID(*v6ServerID)(resp)
  65. return resp, false
  66. }
  67. // Handler4 handles DHCPv4 packets for the server_id plugin.
  68. func Handler4(req, resp *dhcpv4.DHCPv4) (*dhcpv4.DHCPv4, bool) {
  69. if v4ServerID == nil {
  70. log.Fatal("BUG: Plugin is running uninitialized!")
  71. return nil, true
  72. }
  73. if req.OpCode != dhcpv4.OpcodeBootRequest {
  74. log.Warningf("not a BootRequest, ignoring")
  75. return resp, false
  76. }
  77. if req.ServerIPAddr != nil &&
  78. !req.ServerIPAddr.Equal(net.IPv4zero) &&
  79. !req.ServerIPAddr.Equal(v4ServerID) {
  80. // This request is not for us, drop it.
  81. log.Infof("requested server ID does not match this server's ID. Got %v, want %v", req.ServerIPAddr, v4ServerID)
  82. return nil, true
  83. }
  84. resp.ServerIPAddr = make(net.IP, net.IPv4len)
  85. copy(resp.ServerIPAddr[:], v4ServerID)
  86. resp.UpdateOption(dhcpv4.OptServerIdentifier(v4ServerID))
  87. return resp, false
  88. }
  89. func setup4(args ...string) (handler.Handler4, error) {
  90. log.Printf("loading `server_id` plugin for DHCPv4 with args: %v", args)
  91. if len(args) < 1 {
  92. return nil, errors.New("need an argument")
  93. }
  94. serverID := net.ParseIP(args[0])
  95. if serverID == nil {
  96. return nil, errors.New("invalid or empty IP address")
  97. }
  98. if serverID.To4() == nil {
  99. return nil, errors.New("not a valid IPv4 address")
  100. }
  101. v4ServerID = serverID
  102. return Handler4, nil
  103. }
  104. func setup6(args ...string) (handler.Handler6, error) {
  105. log.Printf("loading `server_id` plugin for DHCPv6 with args: %v", args)
  106. if len(args) < 2 {
  107. return nil, errors.New("need a DUID type and value")
  108. }
  109. duidType := args[0]
  110. if duidType == "" {
  111. return nil, errors.New("got empty DUID type")
  112. }
  113. duidValue := args[1]
  114. if duidValue == "" {
  115. return nil, errors.New("got empty DUID value")
  116. }
  117. duidType = strings.ToLower(duidType)
  118. hwaddr, err := net.ParseMAC(duidValue)
  119. if err != nil {
  120. return nil, err
  121. }
  122. switch duidType {
  123. case "ll", "duid-ll", "duid_ll":
  124. v6ServerID = &dhcpv6.Duid{
  125. Type: dhcpv6.DUID_LL,
  126. // sorry, only ethernet for now
  127. HwType: iana.HWTypeEthernet,
  128. LinkLayerAddr: hwaddr,
  129. }
  130. case "llt", "duid-llt", "duid_llt":
  131. v6ServerID = &dhcpv6.Duid{
  132. Type: dhcpv6.DUID_LLT,
  133. // sorry, zero-time for now
  134. Time: 0,
  135. // sorry, only ethernet for now
  136. HwType: iana.HWTypeEthernet,
  137. LinkLayerAddr: hwaddr,
  138. }
  139. case "en", "uuid":
  140. return nil, errors.New("EN/UUID DUID type not supported yet")
  141. default:
  142. return nil, errors.New("Opaque DUID type not supported yet")
  143. }
  144. log.Printf("using %s %s", duidType, duidValue)
  145. return Handler6, nil
  146. }