plugin.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  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 prefix implements a plugin offering prefixes to clients requesting them
  5. // This plugin attributes prefixes to clients requesting them with IA_PREFIX requests.
  6. //
  7. // Arguments for the plugin configuration are as follows, in this order:
  8. // - prefix: The base prefix from which assigned prefixes are carved
  9. // - max: maximum size of the prefix delegated to clients. When a client requests a larger prefix
  10. // than this, this is the size of the offered prefix
  11. package prefix
  12. // FIXME: various settings will be hardcoded (default size, minimum size, lease times) pending a
  13. // better configuration system
  14. import (
  15. "errors"
  16. "fmt"
  17. "net"
  18. "strconv"
  19. "github.com/insomniacslk/dhcp/dhcpv6"
  20. dhcpIana "github.com/insomniacslk/dhcp/iana"
  21. "github.com/coredhcp/coredhcp/handler"
  22. "github.com/coredhcp/coredhcp/logger"
  23. "github.com/coredhcp/coredhcp/plugins"
  24. "github.com/coredhcp/coredhcp/plugins/allocators"
  25. "github.com/coredhcp/coredhcp/plugins/allocators/bitmap"
  26. )
  27. var log = logger.GetLogger("plugins/prefix")
  28. // Plugin registers the prefix. Prefix delegation only exists for DHCPv6
  29. var Plugin = plugins.Plugin{
  30. Name: "prefix",
  31. Setup6: setupPrefix,
  32. }
  33. func setupPrefix(args ...string) (handler.Handler6, error) {
  34. // - prefix: 2001:db8::/48 64
  35. if len(args) < 2 {
  36. return nil, errors.New("Need both a subnet and an allocation max size")
  37. }
  38. _, prefix, err := net.ParseCIDR(args[0])
  39. if err != nil {
  40. return nil, fmt.Errorf("Invalid pool subnet: %v", err)
  41. }
  42. allocSize, err := strconv.Atoi(args[1])
  43. if err != nil || allocSize > 128 || allocSize < 0 {
  44. return nil, fmt.Errorf("Invalid prefix length: %v", err)
  45. }
  46. // TODO: select allocators based on heuristics or user configuration
  47. alloc, err := bitmap.NewBitmapAllocator(*prefix, allocSize)
  48. if err != nil {
  49. return nil, fmt.Errorf("Could not initialize prefix allocator: %v", err)
  50. }
  51. return (&Handler{
  52. leases: make(map[uint64]dhcpv6.Duid),
  53. allocator: alloc,
  54. }).Handle, nil
  55. }
  56. // Handler holds state of allocations for the plugin
  57. type Handler struct {
  58. leases map[uint64]dhcpv6.Duid
  59. allocator allocators.Allocator
  60. }
  61. func (h *Handler) allocate(p net.IPNet) (net.IPNet, error) {
  62. // TODO: handle the leases array
  63. // TODO: handle renewal
  64. // TODO: handle expiration / fast re-commit (when requesting an expired prefix, don't go through the allocator)
  65. return h.allocator.Allocate(p)
  66. }
  67. // Handle processes DHCPv6 packets for the prefix plugin for a given allocator/leaseset
  68. func (h *Handler) Handle(req, resp dhcpv6.DHCPv6) (dhcpv6.DHCPv6, bool) {
  69. msg, err := req.GetInnerMessage()
  70. if err != nil {
  71. log.Error(err)
  72. return nil, true
  73. }
  74. // Each request IA_PD requires an IA_PD response
  75. for _, iapd := range msg.Options.IAPD() {
  76. if err != nil {
  77. log.Errorf("Malformed IAPD received: %v", err)
  78. resp.AddOption(&dhcpv6.OptStatusCode{StatusCode: dhcpIana.StatusMalformedQuery})
  79. return resp, true
  80. }
  81. iapdResp := &dhcpv6.OptIAPD{
  82. IaId: iapd.IaId,
  83. T1: 3600,
  84. T2: 3600,
  85. }
  86. hints := iapd.Options.Prefixes()
  87. if len(hints) == 0 {
  88. // If there are no IAPrefix hints, this is still a valid IA_PD request (just
  89. // unspecified) and we must attempt to allocate a prefix; so we include an
  90. // empty hint which is equivalent to no hint
  91. hints = []*dhcpv6.OptIAPrefix{&dhcpv6.OptIAPrefix{}}
  92. }
  93. for _, iaprefix := range hints {
  94. // FIXME: This allocates both in ADVERTISE and REPLY.
  95. // Need to offer an available prefix without reserving it in ADVERTISE, or
  96. // with an immediately-expired reservation; then confirm it in REPLY
  97. if iaprefix.Prefix == nil {
  98. iaprefix.Prefix = &net.IPNet{}
  99. }
  100. allocated, err := h.allocate(*iaprefix.Prefix)
  101. if err != nil {
  102. log.Debugf("Nothing allocated for hinted prefix %s", iaprefix)
  103. continue
  104. }
  105. r := &dhcpv6.OptIAPrefix{
  106. PreferredLifetime: 3600,
  107. ValidLifetime: 3600,
  108. Prefix: &allocated,
  109. }
  110. iapdResp.Options.Add(r)
  111. }
  112. if len(iapdResp.Options.Options) == 0 {
  113. log.Debugf("No valid prefix to return for IAID %x", iapd.IaId)
  114. iapdResp.Options.Add(&dhcpv6.OptStatusCode{
  115. StatusCode: dhcpIana.StatusNoPrefixAvail,
  116. })
  117. }
  118. resp.AddOption(iapdResp)
  119. }
  120. return resp, false
  121. }