From ca2b5ac2d647066e472c8dd857b3dbd99f15c233 Mon Sep 17 00:00:00 2001 From: Anatole Denis Date: Wed, 18 Sep 2019 17:29:35 +0200 Subject: [PATCH] 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 --- config/config.go | 43 ++++++++++++++++++++++----------------- coredhcp.go | 52 +++++++++++++++++++++++++++++------------------- 2 files changed, 57 insertions(+), 38 deletions(-) diff --git a/config/config.go b/config/config.go index 8103c5a..a114085 100644 --- a/config/config.go +++ b/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 { diff --git a/coredhcp.go b/coredhcp.go index ff0f114..7ab90b8 100644 --- a/coredhcp.go +++ b/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 }