The hostapd backend never populated IPs: NewDHCPCorrelator was defined
but never instantiated, and even when it was, the parser only handled
ISC dhcpd's text format. On a BusyBox-based router using udhcpd, every
device showed up with an empty IP.
Two fixes:
- Add a udhcpd binary lease parser. The format is documented in
busybox/networking/udhcp/dhcpd.{h,c}: an 8-byte big-endian unix-time
header followed by 36-byte dyn_lease records (expires, IP, MAC,
20-byte hostname, 2-byte pad). ParseLeases auto-detects the format
by inspecting the header so the same code path handles both udhcpd
and ISC text leases.
- Wire the DHCPCorrelator into Backend.Initialize and have it merge
two sources: ARP first (universal IP fallback for any station that
has been talked to) and DHCP leases on top (authoritative, carries
the hostname). ARP fills the gap when leases are missing or the
station uses a static IP; DHCP wins on conflict.
Default DHCPLeasesPath updated to /var/lib/udhcpd/udhcpd.leases — the
common BusyBox path. Configurable as before.
108 lines
2.8 KiB
Go
108 lines
2.8 KiB
Go
package config
|
|
|
|
import (
|
|
"flag"
|
|
"log"
|
|
"os"
|
|
"path"
|
|
"strings"
|
|
)
|
|
|
|
type Config struct {
|
|
Bind string
|
|
WifiInterface string
|
|
HotspotInterface string
|
|
EthernetInterface string
|
|
WifiBackend string
|
|
StationBackend string // "arp", "dhcp", or "hostapd"
|
|
DHCPLeasesPath string
|
|
ARPTablePath string
|
|
SyslogEnabled bool
|
|
SyslogPath string
|
|
SyslogFilter []string
|
|
SyslogSource string
|
|
}
|
|
|
|
// ConsolidateConfig fills an Options struct by reading configuration from
|
|
// config files, environment, then command line.
|
|
//
|
|
// Should be called only one time.
|
|
func ConsolidateConfig() (opts *Config, err error) {
|
|
// Define defaults options
|
|
opts = &Config{
|
|
Bind: "127.0.0.1:8080",
|
|
WifiInterface: "wlan0",
|
|
HotspotInterface: "wlan1",
|
|
EthernetInterface: "eth0",
|
|
DHCPLeasesPath: "/var/lib/udhcpd/udhcpd.leases",
|
|
ARPTablePath: "/proc/net/arp",
|
|
SyslogEnabled: false,
|
|
SyslogPath: "/var/log/messages",
|
|
SyslogFilter: []string{"daemon.info wpa_supplicant:", "daemon.info iwd:", "daemon.info hostapd:"},
|
|
SyslogSource: "iwd",
|
|
}
|
|
|
|
declareFlags(opts)
|
|
|
|
// Establish a list of possible configuration file locations
|
|
configLocations := []string{
|
|
"repeater.conf",
|
|
}
|
|
|
|
if home, err := os.UserConfigDir(); err == nil {
|
|
configLocations = append(configLocations, path.Join(home, "repeater", "repeater.conf"))
|
|
}
|
|
|
|
configLocations = append(configLocations, path.Join("etc", "repeater.conf"))
|
|
|
|
// If config file exists, read configuration from it
|
|
for _, filename := range configLocations {
|
|
if _, e := os.Stat(filename); !os.IsNotExist(e) {
|
|
log.Printf("Loading configuration from %s\n", filename)
|
|
err = parseFile(opts, filename)
|
|
if err != nil {
|
|
return
|
|
}
|
|
break
|
|
}
|
|
}
|
|
|
|
// Then, overwrite that by what is present in the environment
|
|
err = parseEnvironmentVariables(opts)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// Finaly, command line takes precedence
|
|
err = parseCLI(opts)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// Validate configuration
|
|
if opts.WifiBackend != "iwd" && opts.WifiBackend != "wpasupplicant" {
|
|
log.Fatalf("wifi-backend must be set to 'iwd' or 'wpasupplicant' (got: '%s')", opts.WifiBackend)
|
|
}
|
|
|
|
if opts.StationBackend != "arp" && opts.StationBackend != "dhcp" && opts.StationBackend != "hostapd" {
|
|
log.Fatalf("station-backend must be set to 'arp', 'dhcp', or 'hostapd' (got: '%s')", opts.StationBackend)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// parseLine treats a config line and place the read value in the variable
|
|
// declared to the corresponding flag.
|
|
func parseLine(o *Config, line string) (err error) {
|
|
fields := strings.SplitN(line, "=", 2)
|
|
orig_key := strings.TrimSpace(fields[0])
|
|
value := strings.TrimSpace(fields[1])
|
|
|
|
key := strings.TrimPrefix(orig_key, "REPEATER_")
|
|
key = strings.Replace(key, "_", "-", -1)
|
|
key = strings.ToLower(key)
|
|
|
|
err = flag.Set(key, value)
|
|
|
|
return
|
|
}
|