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:🅰️2]"

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

Signed-off-by: Anatole Denis <anatole@unverle.fr>
This commit is contained in:
Anatole Denis 2019-09-18 17:29:35 +02:00 committed by insomniac
parent 3806733ad7
commit ca2b5ac2d6
2 changed files with 57 additions and 38 deletions

View File

@ -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 {

View File

@ -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
}