瀏覽代碼

Config file now requires interface name

The interface name is necessary to create multicast listeners.

Signed-off-by: Andrea Barberio <insomniac@slackware.it>
Andrea Barberio 6 年之前
父節點
當前提交
c7d962327a
共有 4 個文件被更改,包括 90 次插入14 次删除
  1. 7 6
      cmds/coredhcp/config.yml.example
  2. 40 6
      config/config.go
  3. 15 1
      coredhcp.go
  4. 28 1
      plugins/file/plugin.go

+ 7 - 6
cmds/coredhcp/config.yml.example

@@ -1,12 +1,13 @@
 server6:
-    listen: '[::]:547'
+    listen: '[ff02::1:2]:547'
+    interface: "eth0"
     plugins:
         - server_id: LL 00:de:ad:be:ef:00
         - file: "leases.txt"
         # - dns: 8.8.8.8 8.8.4.4 2001:4860:4860::8888 2001:4860:4860::8844
 
-#server4:
-#    listen: '0.0.0.0:67'
-#    plugins:
-#        - server_id: 192.168.1.12
-##        - file: "leases.txt"
+server4:
+    listen: '0.0.0.0:67'
+    interface: "eth0"
+    plugins:
+        - server_id: 192.168.1.12

+ 40 - 6
config/config.go

@@ -35,8 +35,9 @@ 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
-	Plugins  []*PluginConfig
+	Listener  *net.UDPAddr
+	Interface string
+	Plugins   []*PluginConfig
 }
 
 // PluginConfig holds the configuration of a plugin
@@ -70,6 +71,13 @@ func Load() (*Config, error) {
 	return c, nil
 }
 
+func protoVersionCheck(v protocolVersion) error {
+	if v != protocolV6 && v != protocolV4 {
+		return fmt.Errorf("invalid protocol version: %d", v)
+	}
+	return nil
+}
+
 func parsePlugins(pluginList []interface{}) ([]*PluginConfig, error) {
 	plugins := make([]*PluginConfig, 0)
 	for idx, val := range pluginList {
@@ -97,7 +105,25 @@ func parsePlugins(pluginList []interface{}) ([]*PluginConfig, error) {
 	return plugins, nil
 }
 
+func (c *Config) getInterface(ver protocolVersion) (string, error) {
+	if err := protoVersionCheck(ver); err != nil {
+		return "", err
+	}
+	directive := fmt.Sprintf("server%d.interface", ver)
+	if exists := c.v.Get(directive); exists == nil {
+		return "", ConfigErrorFromString("dhcpv%d: missing `%s` directive", ver, directive)
+	}
+	ifname := c.v.GetString(directive)
+	if ifname == "" {
+		return "", ConfigErrorFromString("dhcpv%d: missing `%s` directive", ver, directive)
+	}
+	return ifname, nil
+}
+
 func (c *Config) getListenAddress(ver protocolVersion) (*net.UDPAddr, error) {
+	if err := protoVersionCheck(ver); err != nil {
+		return nil, err
+	}
 	if exists := c.v.Get(fmt.Sprintf("server%d", ver)); exists == nil {
 		// it is valid to have no server configuration defined, and in this case
 		// no listening address and no error are returned.
@@ -132,6 +158,9 @@ func (c *Config) getListenAddress(ver protocolVersion) (*net.UDPAddr, error) {
 }
 
 func (c *Config) getPlugins(ver protocolVersion) ([]*PluginConfig, error) {
+	if err := protoVersionCheck(ver); err != nil {
+		return nil, err
+	}
 	pluginList := cast.ToSlice(c.v.Get(fmt.Sprintf("server%d.plugins", ver)))
 	if pluginList == nil {
 		return nil, ConfigErrorFromString("dhcpv%d: invalid plugins section, not a list or no plugin specified", ver)
@@ -140,8 +169,12 @@ func (c *Config) getPlugins(ver protocolVersion) ([]*PluginConfig, error) {
 }
 
 func (c *Config) parseConfig(ver protocolVersion) error {
-	if ver != protocolV6 && ver != protocolV4 {
-		return ConfigErrorFromString("unknown protocol version: %d", ver)
+	if err := protoVersionCheck(ver); err != nil {
+		return err
+	}
+	ifname, err := c.getInterface(ver)
+	if err != nil {
+		return err
 	}
 	listenAddr, err := c.getListenAddress(ver)
 	if err != nil {
@@ -161,8 +194,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)
 	}
 	sc := ServerConfig{
-		Listener: listenAddr,
-		Plugins:  plugins,
+		Listener:  listenAddr,
+		Interface: ifname,
+		Plugins:   plugins,
 	}
 	if ver == protocolV6 {
 		c.Server6 = &sc

+ 15 - 1
coredhcp.go

@@ -186,7 +186,21 @@ 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.Listener, s.MainHandler6)
+		listener := s.Config.Server6.Listener
+		ifname := s.Config.Server6.Interface
+		opts := make([]server6.ServerOpt, 0)
+		if listener.IP.IsMulticast() {
+			iface, err := net.InterfaceByName(ifname)
+			if err != nil {
+				return err
+			}
+			conn, err := net.ListenMulticastUDP("udp6", iface, listener)
+			if err != nil {
+				return err
+			}
+			opts = append(opts, server6.WithConn(conn))
+		}
+		s.Server6, err = server6.NewServer(s.Config.Server6.Listener, s.MainHandler6, opts...)
 		if err != nil {
 			return err
 		}

+ 28 - 1
plugins/file/plugin.go

@@ -77,7 +77,34 @@ func Handler6(req, resp dhcpv6.DHCPv6) (dhcpv6.DHCPv6, bool) {
 		return nil, false
 	}
 	log.Printf("Found IP address %s for MAC %s", ipaddr, mac)
-	// TODO add an OptIANA based on the above data
+	resp.AddOption(&dhcpv6.OptIANA{
+		// FIXME this must be unique per client address
+		IaId: [4]byte{0xaa, 0xbb, 0xcc, 0xdd},
+		Options: []dhcpv6.Option{
+			&dhcpv6.OptIAAddress{
+				IPv6Addr:          ipaddr,
+				PreferredLifetime: 3600,
+				ValidLifetime:     3600,
+			},
+		},
+	})
+	resp.AddOption(&dhcpv6.OptDNSRecursiveNameServer{
+		NameServers: []net.IP{
+			// FIXME this must be read from the config file
+			net.ParseIP("2001:4860:4860::8888"),
+			net.ParseIP("2001:4860:4860::4444"),
+		},
+	})
+	if oro := req.GetOption(dhcpv6.OptionORO); len(oro) > 0 {
+		for _, code := range oro[0].(*dhcpv6.OptRequestedOption).RequestedOptions() {
+			if code == dhcpv6.OptionBootfileURL {
+				// bootfile URL is requested
+				resp.AddOption(
+					&dhcpv6.OptBootFileURL{BootFileURL: []byte("http://[2001:db8::0:1]/nbp")},
+				)
+			}
+		}
+	}
 	return resp, true
 }