config.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. package config
  2. import (
  3. "errors"
  4. "log"
  5. "net"
  6. "strconv"
  7. "strings"
  8. "github.com/spf13/cast"
  9. "github.com/spf13/viper"
  10. )
  11. // Config holds the DHCPv6/v4 server configuration
  12. type Config struct {
  13. v *viper.Viper
  14. Server6 *ServerConfig
  15. Server4 *ServerConfig
  16. }
  17. // ServerConfig holds a server configuration that is specific to either the
  18. // DHCPv6 server or the DHCPv4 server.
  19. type ServerConfig struct {
  20. Listener *net.UDPAddr
  21. Plugins []*PluginConfig
  22. }
  23. // PluginConfig holds the configuration of a single plugin
  24. type PluginConfig struct {
  25. Name string
  26. Args []string
  27. }
  28. // Parse returns a Config object after reading a configuration file.
  29. // It returns an error if the file is invalid or not found.
  30. func Parse() (*Config, error) {
  31. log.Print("Loading configuration")
  32. v := viper.New()
  33. v.SetConfigType("yml")
  34. v.SetConfigName("config")
  35. v.AddConfigPath(".")
  36. v.AddConfigPath("$HOME/.coredhcp/")
  37. v.AddConfigPath("/etc/coredhcp/")
  38. if err := v.ReadInConfig(); err != nil {
  39. return nil, err
  40. }
  41. v6, err := parseV6Config(v)
  42. if err != nil {
  43. return nil, err
  44. }
  45. v4, err := parseV4Config(v)
  46. if err != nil {
  47. return nil, err
  48. }
  49. if v6 == nil && v4 == nil {
  50. return nil, ConfigErrorFromString("need at least one valid config for DHCPv6 or DHCPv4")
  51. }
  52. return &Config{
  53. v: v,
  54. Server6: v6,
  55. Server4: v4,
  56. }, nil
  57. }
  58. func parseV6Config(v *viper.Viper) (*ServerConfig, error) {
  59. if exists := v.Get("server6"); exists == nil {
  60. // it is valid to have no DHCPv6 configuration defined, so no
  61. // server and no error are returned
  62. return nil, nil
  63. }
  64. addr := v.GetString("server6.listen")
  65. if addr == "" {
  66. return nil, ConfigErrorFromString("dhcpv6: missing `server6.listen` directive")
  67. }
  68. ipStr, portStr, err := net.SplitHostPort(addr)
  69. if err != nil {
  70. return nil, ConfigErrorFromString("dhcpv6: %v", err)
  71. }
  72. ip := net.ParseIP(ipStr)
  73. if ip.To4() != nil {
  74. return nil, ConfigErrorFromString("dhcpv6: missing or invalid `listen` address")
  75. }
  76. port, err := strconv.Atoi(portStr)
  77. if err != nil {
  78. return nil, ConfigErrorFromString("dhcpv6: invalid `listen` port")
  79. }
  80. listener := net.UDPAddr{
  81. IP: ip,
  82. Port: port,
  83. }
  84. sc := ServerConfig{
  85. Listener: &listener,
  86. Plugins: nil,
  87. }
  88. // load plugins
  89. pluginList := cast.ToSlice(v.Get("server6.plugins"))
  90. if pluginList == nil {
  91. return nil, ConfigErrorFromString("dhcpv6: invalid plugins section, not a list")
  92. }
  93. if len(pluginList) == 0 {
  94. return &sc, nil
  95. }
  96. for name, v := range pluginList {
  97. conf := cast.ToStringMap(v)
  98. if conf == nil {
  99. return nil, ConfigErrorFromString("dhcpv6: plugin `%s` is not a string map", name)
  100. }
  101. // make sure that only one item is specified, since it's a
  102. // map name -> args
  103. if len(conf) != 1 {
  104. return nil, ConfigErrorFromString("dhcpv6: exactly one plugin per item can be specified")
  105. }
  106. var (
  107. name string
  108. args []string
  109. )
  110. // only one item, as enforced above, so read just that
  111. for k, v := range conf {
  112. name = k
  113. args = strings.Fields(cast.ToString(v))
  114. break
  115. }
  116. log.Printf("Found plugin: `%s` with %d args, `%v`", name, len(args), args)
  117. sc.Plugins = append(sc.Plugins, &PluginConfig{Name: name, Args: args})
  118. }
  119. return &sc, nil
  120. }
  121. func parseV4Config(v *viper.Viper) (*ServerConfig, error) {
  122. if exists := v.Get("server4"); exists != nil {
  123. return nil, errors.New("DHCPv4 config parser not implemented yet")
  124. }
  125. return nil, nil
  126. }