Просмотр исходного кода

config: Expand unspecified ll-mcast to all interfaces

Specifying a link-local multicast address to listen on without an
interface is rejected. Instead, list all available interfaces where it
makes sense ("MULTICAST" flag) and listen on all of them, which is the
least-surprising behaviour.

This will be needed to have sane default values since the RFC specifies
a default link-local multicast address to listen on

Document a surprising behaviour when interfaces appear and disappear
during runtime. Note it is *not* a regression as the previous behaviour
was worse (join the mcast group on only one interface with a default
route)

Signed-off-by: Anatole Denis <anatole@unverle.fr>
Anatole Denis 6 лет назад
Родитель
Сommit
fcc0c8fbb9
1 измененных файлов с 72 добавлено и 12 удалено
  1. 72 12
      config/config.go

+ 72 - 12
config/config.go

@@ -5,6 +5,7 @@
 package config
 
 import (
+	"errors"
 	"fmt"
 	"net"
 	"strconv"
@@ -216,19 +217,9 @@ func (c *Config) parseConfig(ver protocolVersion) error {
 		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)
+	listeners, err := c.parseListen(ver)
 	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)
+		return err
 	}
 
 	sc := ServerConfig{
@@ -242,3 +233,72 @@ func (c *Config) parseConfig(ver protocolVersion) error {
 	}
 	return nil
 }
+
+// BUG(Natolumin): When listening on link-local multicast addresses without
+// binding to a specific interface, new interfaces coming up after the server
+// starts will not be taken into account.
+
+func expandLLMulticast(addr *net.UDPAddr) ([]net.UDPAddr, error) {
+	if !addr.IP.IsLinkLocalMulticast() && !addr.IP.IsInterfaceLocalMulticast() {
+		return nil, errors.New("Address is not multicast")
+	}
+	if addr.Zone != "" {
+		return nil, errors.New("Address is already zoned")
+	}
+	var needFlags = net.FlagMulticast
+	if addr.IP.To4() != nil {
+		// We need to be able to send broadcast responses in ipv4
+		needFlags |= net.FlagBroadcast
+	}
+
+	ifs, err := net.Interfaces()
+	ret := make([]net.UDPAddr, 0, len(ifs))
+	if err != nil {
+		return nil, fmt.Errorf("Could not list network interfaces: %v", err)
+	}
+	for _, iface := range ifs {
+		if (iface.Flags & needFlags) != needFlags {
+			continue
+		}
+		caddr := *addr
+		caddr.Zone = iface.Name
+		ret = append(ret, caddr)
+	}
+	if len(ret) == 0 {
+		return nil, errors.New("No suitable interface found for multicast listener")
+	}
+	return ret, nil
+}
+
+func (c *Config) parseListen(ver protocolVersion) ([]net.UDPAddr, error) {
+	if err := protoVersionCheck(ver); err != nil {
+		return nil, err
+	}
+
+	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 {
+		l, err := c.getListenAddress(a, ver)
+		if err != nil {
+			return nil, err
+		}
+
+		if l.Zone == "" && (l.IP.IsLinkLocalMulticast() || l.IP.IsInterfaceLocalMulticast()) {
+			// link-local multicast specified without interface gets expanded to listen on all interfaces
+			expanded, err := expandLLMulticast(l)
+			if err != nil {
+				return nil, err
+			}
+			listeners = append(listeners, expanded...)
+			continue
+		}
+
+		listeners = append(listeners, *l)
+	}
+	return listeners, nil
+}