plugin.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  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 sid := msg.Options.ServerID(); sid != nil {
  41. // RFC8415 §16.{2,5,7}
  42. // These message types MUST be discarded if they contain *any* ServerID option
  43. if msg.MessageType == dhcpv6.MessageTypeSolicit ||
  44. msg.MessageType == dhcpv6.MessageTypeConfirm ||
  45. msg.MessageType == dhcpv6.MessageTypeRebind {
  46. return nil, true
  47. }
  48. // Approximately all others MUST be discarded if the ServerID doesn't match
  49. if !sid.Equal(v6ServerID) {
  50. log.Infof("requested server ID does not match this server's ID. Got %v, want %v", sid, v6ServerID)
  51. return nil, true
  52. }
  53. } else if msg.MessageType == dhcpv6.MessageTypeRequest ||
  54. msg.MessageType == dhcpv6.MessageTypeRenew ||
  55. msg.MessageType == dhcpv6.MessageTypeDecline ||
  56. msg.MessageType == dhcpv6.MessageTypeRelease {
  57. // RFC8415 §16.{6,8,10,11}
  58. // These message types MUST be discarded if they *don't* contain a ServerID option
  59. return nil, true
  60. }
  61. dhcpv6.WithServerID(v6ServerID)(resp)
  62. return resp, false
  63. }
  64. // Handler4 handles DHCPv4 packets for the server_id plugin.
  65. func Handler4(req, resp *dhcpv4.DHCPv4) (*dhcpv4.DHCPv4, bool) {
  66. if v4ServerID == nil {
  67. log.Fatal("BUG: Plugin is running uninitialized!")
  68. return nil, true
  69. }
  70. if req.OpCode != dhcpv4.OpcodeBootRequest {
  71. log.Warningf("not a BootRequest, ignoring")
  72. return resp, false
  73. }
  74. if req.ServerIPAddr != nil &&
  75. !req.ServerIPAddr.Equal(net.IPv4zero) &&
  76. !req.ServerIPAddr.Equal(v4ServerID) {
  77. // This request is not for us, drop it.
  78. log.Infof("requested server ID does not match this server's ID. Got %v, want %v", req.ServerIPAddr, v4ServerID)
  79. return nil, true
  80. }
  81. resp.ServerIPAddr = make(net.IP, net.IPv4len)
  82. copy(resp.ServerIPAddr[:], v4ServerID)
  83. resp.UpdateOption(dhcpv4.OptServerIdentifier(v4ServerID))
  84. return resp, false
  85. }
  86. func setup4(args ...string) (handler.Handler4, error) {
  87. log.Printf("loading `server_id` plugin for DHCPv4 with args: %v", args)
  88. if len(args) < 1 {
  89. return nil, errors.New("need an argument")
  90. }
  91. serverID := net.ParseIP(args[0])
  92. if serverID == nil {
  93. return nil, errors.New("invalid or empty IP address")
  94. }
  95. if serverID.To4() == nil {
  96. return nil, errors.New("not a valid IPv4 address")
  97. }
  98. v4ServerID = serverID.To4()
  99. return Handler4, nil
  100. }
  101. func setup6(args ...string) (handler.Handler6, error) {
  102. log.Printf("loading `server_id` plugin for DHCPv6 with args: %v", args)
  103. if len(args) < 2 {
  104. return nil, errors.New("need a DUID type and value")
  105. }
  106. duidType := args[0]
  107. if duidType == "" {
  108. return nil, errors.New("got empty DUID type")
  109. }
  110. duidValue := args[1]
  111. if duidValue == "" {
  112. return nil, errors.New("got empty DUID value")
  113. }
  114. duidType = strings.ToLower(duidType)
  115. hwaddr, err := net.ParseMAC(duidValue)
  116. if err != nil {
  117. return nil, err
  118. }
  119. switch duidType {
  120. case "ll", "duid-ll", "duid_ll":
  121. v6ServerID = &dhcpv6.DUIDLL{
  122. // sorry, only ethernet for now
  123. HWType: iana.HWTypeEthernet,
  124. LinkLayerAddr: hwaddr,
  125. }
  126. case "llt", "duid-llt", "duid_llt":
  127. v6ServerID = &dhcpv6.DUIDLLT{
  128. // sorry, zero-time for now
  129. Time: 0,
  130. // sorry, only ethernet for now
  131. HWType: iana.HWTypeEthernet,
  132. LinkLayerAddr: hwaddr,
  133. }
  134. case "en", "uuid":
  135. return nil, errors.New("EN/UUID DUID type not supported yet")
  136. default:
  137. return nil, errors.New("Opaque DUID type not supported yet")
  138. }
  139. log.Printf("using %s %s", duidType, duidValue)
  140. return Handler6, nil
  141. }