ソースを参照

Allow listening on multiple addresses at once

This extends both the configuration and the server, to allow binding the
server(s) to multiple addresses at once.

The `listen` stanza can now accept either a string (for a single address) or
a list to listen to multiple addresses, ie all of these are valid:
```yaml
server6:
...
server6:
    listen: "[2001:db8::a:2]"

...
server6:
    listen:
        - "[2001:db8::a:2]:530"
	- "%eth0"
```
and the equivalent for server4

Signed-off-by: Anatole Denis <anatole@unverle.fr>
Anatole Denis 6 年 前
コミット
ca2b5ac2d6
2 ファイル変更57 行追加38 行削除
  1. 25 18
      config/config.go
  2. 32 20
      coredhcp.go

+ 25 - 18
config/config.go

@@ -41,8 +41,7 @@ func New() *Config {
 // ServerConfig holds a server configuration that is specific to either the
 // DHCPv6 server or the DHCPv4 server.
 type ServerConfig struct {
-	Listener  *net.UDPAddr
-	Interface string
+	Addresses []*net.UDPAddr
 	Plugins   []*PluginConfig
 }
 
@@ -136,12 +135,11 @@ func splitHostPort(hostport string) (ip string, zone string, port string, err er
 	return
 }
 
-func (c *Config) getListenAddress(ver protocolVersion) (*net.UDPAddr, error) {
+func (c *Config) getListenAddress(addr string, ver protocolVersion) (*net.UDPAddr, error) {
 	if err := protoVersionCheck(ver); err != nil {
 		return nil, err
 	}
 
-	addr := c.v.GetString(fmt.Sprintf("server%d.listen", ver))
 	ipStr, ifname, portStr, err := splitHostPort(addr)
 	if err != nil {
 		return nil, ConfigErrorFromString("dhcpv%d: %v", ver, err)
@@ -154,13 +152,15 @@ func (c *Config) getListenAddress(ver protocolVersion) (*net.UDPAddr, error) {
 			ip = net.IPv4zero
 		case protocolV6:
 			ip = net.IPv6unspecified
+		default:
+			panic("BUG: Unknown protocol version")
 		}
 	}
 	if ip == nil {
 		return nil, ConfigErrorFromString("dhcpv%d: invalid IP address in `listen` directive: %s", ver, ipStr)
 	}
 	if ip4 := ip.To4(); (ver == protocolV6 && ip4 != nil) || (ver == protocolV4 && ip4 == nil) {
-		return nil, ConfigErrorFromString("dhcpv%d: not a valid IPv%d address in `listen` directive", ver, ver)
+		return nil, ConfigErrorFromString("dhcpv%d: not a valid IPv%d address in `listen` directive: '%s'", ver, ver, ipStr)
 	}
 
 	var port int
@@ -170,11 +170,13 @@ func (c *Config) getListenAddress(ver protocolVersion) (*net.UDPAddr, error) {
 			port = dhcpv4.ServerPort
 		case protocolV6:
 			port = dhcpv6.DefaultServerPort
+		default:
+			panic("BUG: Unknown protocol version")
 		}
 	} else {
 		port, err = strconv.Atoi(portStr)
 		if err != nil {
-			return nil, ConfigErrorFromString("dhcpv%d: invalid `listen` port", ver)
+			return nil, ConfigErrorFromString("dhcpv%d: invalid `listen` port '%s'", ver, portStr)
 		}
 	}
 
@@ -205,16 +207,6 @@ func (c *Config) parseConfig(ver protocolVersion) error {
 		// it is valid to have no server configuration defined
 		return nil
 	}
-	listenAddr, err := c.getListenAddress(ver)
-	if err != nil {
-		return err
-	}
-	if listenAddr == nil {
-		// no listener is configured, so `c.Server6` (or `c.Server4` if v4)
-		// will stay nil.
-		log.Warnf("DHCPv%d: server%d present but no listen address defined. The server will not be started", ver, ver)
-		return nil
-	}
 	// read plugin configuration
 	plugins, err := c.getPlugins(ver)
 	if err != nil {
@@ -223,9 +215,24 @@ func (c *Config) parseConfig(ver protocolVersion) error {
 	for _, p := range plugins {
 		log.Printf("DHCPv%d: found plugin `%s` with %d args: %v", ver, p.Name, len(p.Args), p.Args)
 	}
+
+	listen := c.v.Get(fmt.Sprintf("server%d.listen", ver))
+	addrs, err := cast.ToStringSliceE(listen)
+	if err != nil {
+		addrs = []string{cast.ToString(listen)}
+	}
+
+	listeners := []*net.UDPAddr{}
+	for _, a := range addrs {
+		listenAddr, err := c.getListenAddress(a, ver)
+		if err != nil {
+			return err
+		}
+		listeners = append(listeners, listenAddr)
+	}
+
 	sc := ServerConfig{
-		Listener:  listenAddr,
-		Interface: listenAddr.Zone,
+		Addresses: listeners,
 		Plugins:   plugins,
 	}
 	if ver == protocolV6 {

+ 32 - 20
coredhcp.go

@@ -27,8 +27,8 @@ type Server struct {
 	Handlers6 []handler.Handler6
 	Handlers4 []handler.Handler4
 	Config    *config.Config
-	Server6   *server6.Server
-	Server4   *server4.Server
+	Servers6  []*server6.Server
+	Servers4  []*server4.Server
 	errors    chan error
 }
 
@@ -240,25 +240,33 @@ func (s *Server) Start() error {
 
 	// listen
 	if s.Config.Server6 != nil {
-		log.Printf("Starting DHCPv6 listener on %v", s.Config.Server6.Listener)
-		s.Server6, err = server6.NewServer(s.Config.Server6.Interface, s.Config.Server6.Listener, s.MainHandler6)
-		if err != nil {
-			return err
+		log.Println("Starting DHCPv6 server")
+		for _, l := range s.Config.Server6.Addresses {
+			s6, err := server6.NewServer(l.Zone, l, s.MainHandler6)
+			if err != nil {
+				return err
+			}
+			s.Servers6 = append(s.Servers6, s6)
+			log.Infof("Listen %s", l)
+			go func() {
+				s.errors <- s6.Serve()
+			}()
 		}
-		go func() {
-			s.errors <- s.Server6.Serve()
-		}()
 	}
 
 	if s.Config.Server4 != nil {
-		log.Printf("Starting DHCPv4 listener on %v", s.Config.Server4.Listener)
-		s.Server4, err = server4.NewServer(s.Config.Server4.Interface, s.Config.Server4.Listener, s.MainHandler4)
-		if err != nil {
-			return err
+		log.Println("Starting DHCPv4 server")
+		for _, l := range s.Config.Server4.Addresses {
+			s4, err := server4.NewServer(l.Zone, l, s.MainHandler4)
+			if err != nil {
+				return err
+			}
+			s.Servers4 = append(s.Servers4, s4)
+			log.Infof("Listen %s", l)
+			go func() {
+				s.errors <- s4.Serve()
+			}()
 		}
-		go func() {
-			s.errors <- s.Server4.Serve()
-		}()
 	}
 
 	return nil
@@ -268,11 +276,15 @@ func (s *Server) Start() error {
 func (s *Server) Wait() error {
 	log.Print("Waiting")
 	err := <-s.errors
-	if s.Server6 != nil {
-		s.Server6.Close()
+	for _, s6 := range s.Servers6 {
+		if s6 != nil {
+			s6.Close()
+		}
 	}
-	if s.Server4 != nil {
-		s.Server4.Close()
+	for _, s4 := range s.Servers4 {
+		if s4 != nil {
+			s4.Close()
+		}
 	}
 	return err
 }