diff --git a/README.md b/README.md index 54ffcbf..4ae0d6b 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ we can set up local listeners as proxy servers, and forward requests to internet - Periodical availability checking for forwarders - Send requests from specific local ip/interface - Services: - - dhcpd: a simple dhcp server that can detect existing dhcp server and avoid conflicts + - dhcpd: a simple dhcp server that can run in failover mode ## Protocols @@ -84,6 +84,8 @@ we can set up local listeners as proxy servers, and forward requests to internet ## Usage +#### Run + ```bash glider -config CONFIG_PATH ``` @@ -94,7 +96,7 @@ glider -verbose -listen :8443 -forward SCHEME://HOST:PORT #### Help
-`glider -help` (click to see details) +`glider -help` (click) ```bash Usage: glider [-listen URL]... [-forward URL]... [OPTION]... @@ -207,6 +209,7 @@ Forwarder Options: FORWARD_URL#OPTIONS Services: dhcpd: service=dhcpd,INTERFACE,START_IP,END_IP,LEASE_MINUTES[,MAC=IP,MAC=IP...] + service=dhcpd-failover,INTERFACE,START_IP,END_IP,LEASE_MINUTES[,MAC=IP,MAC=IP...] e.g. service=dhcpd,eth1,192.168.1.100,192.168.1.199,720 -- @@ -225,7 +228,7 @@ glider 0.16.0, https://github.com/nadoo/glider #### Schemes
-`glider -scheme all` (click to see details) +`glider -scheme all` (click) ```bash KCP scheme: @@ -340,7 +343,7 @@ TLS and Websocket with a specified proxy protocol: #### Examples
-`glider -example` (click to see details) +`glider -example` (click) ```bash Examples: @@ -391,9 +394,11 @@ Examples: - dhcpd: - service=dhcpd,INTERFACE,START_IP,END_IP,LEASE_MINUTES[,MAC=IP,MAC=IP...] - - e.g.: - - service=dhcpd,eth1,192.168.1.100,192.168.1.199,720 - - service=dhcpd,eth2,192.168.2.100,192.168.2.199,720,fc:23:34:9e:25:01=192.168.2.101 + - service=dhcpd,eth1,192.168.1.100,192.168.1.199,720 + - service=dhcpd,eth2,192.168.2.100,192.168.2.199,720,fc:23:34:9e:25:01=192.168.2.101 + +- dhcpd-failover: only serves requests when there's no other dhcp server exists + - service=dhcpd-failover,INTERFACE,START_IP,END_IP,LEASE_MINUTES[,MAC=IP,MAC=IP...] ## Linux Service diff --git a/config.go b/config.go index 53e3576..a68b93d 100644 --- a/config.go +++ b/config.go @@ -204,6 +204,7 @@ Forwarder Options: FORWARD_URL#OPTIONS Services: dhcpd: service=dhcpd,INTERFACE,START_IP,END_IP,LEASE_MINUTES[,MAC=IP,MAC=IP...] + service=dhcpd-failover,INTERFACE,START_IP,END_IP,LEASE_MINUTES[,MAC=IP,MAC=IP...] e.g. service=dhcpd,eth1,192.168.1.100,192.168.1.199,720 -- diff --git a/feature.go b/feature.go index 9289089..21b49fc 100644 --- a/feature.go +++ b/feature.go @@ -3,7 +3,6 @@ package main import ( // comment out the services you don't need to make the compiled binary smaller. // _ "github.com/nadoo/glider/service/xxx" - _ "github.com/nadoo/glider/service/dhcpd" // comment out the protocols you don't need to make the compiled binary smaller. _ "github.com/nadoo/glider/proxy/http" diff --git a/feature_linux.go b/feature_linux.go index d3a7804..be2c2c7 100644 --- a/feature_linux.go +++ b/feature_linux.go @@ -3,6 +3,7 @@ package main import ( // comment out the services you don't need to make the compiled binary smaller. // _ "github.com/nadoo/glider/service/xxx" + _ "github.com/nadoo/glider/service/dhcpd" // comment out the protocols you don't need to make the compiled binary smaller. _ "github.com/nadoo/glider/proxy/redir" diff --git a/service/dhcpd/cilent.go b/service/dhcpd/cilent.go index ce8e26a..55316f6 100644 --- a/service/dhcpd/cilent.go +++ b/service/dhcpd/cilent.go @@ -3,40 +3,30 @@ package dhcpd import ( "context" "net" - "runtime" "time" "github.com/insomniacslk/dhcp/dhcpv4" - "github.com/nadoo/glider/pkg/log" "github.com/nadoo/glider/pkg/sockopt" ) -func existsServer(intf *net.Interface) (exists bool) { +func discovery(intf *net.Interface) (found bool) { lc := &net.ListenConfig{} lc.Control = sockopt.BindControl(intf) - addr := "0.0.0.0:68" - if runtime.GOOS == "linux" || runtime.GOOS == "android" { - addr = "255.255.255.255:68" - } - - pc, err := lc.ListenPacket(context.Background(), "udp4", addr) + pc, err := lc.ListenPacket(context.Background(), "udp4", "255.255.255.255:68") if err != nil { - log.F("[dhcpd] failed in dhcp client ListenPacket: %s", err) return } defer pc.Close() discovery, err := dhcpv4.NewDiscovery(intf.HardwareAddr, dhcpv4.WithBroadcast(true)) if err != nil { - log.F("[dhcpd] failed in dhcp client NewDiscovery: %s", err) return } _, err = pc.WriteTo(discovery.ToBytes(), &net.UDPAddr{IP: net.IPv4bcast, Port: 67}) if err != nil { - log.F("[dhcpd] failed in dhcp client WriteTo: %s", err) return } diff --git a/service/dhcpd/dhcpd.go b/service/dhcpd/dhcpd.go index 9d096f1..44b7b98 100644 --- a/service/dhcpd/dhcpd.go +++ b/service/dhcpd/dhcpd.go @@ -1,11 +1,13 @@ package dhcpd import ( + "bytes" "errors" "net" "net/netip" "strconv" "strings" + "sync" "time" "github.com/insomniacslk/dhcp/dhcpv4" @@ -18,13 +20,19 @@ import ( var leaseTime = time.Hour * 12 func init() { - service.Register("dhcpd", &dpcpd{}) + service.Register("dhcpd", &dhcpd{}) + service.Register("dhcpd-failover", &dhcpd{detect: true}) } -type dpcpd struct{} +type dhcpd struct { + mu sync.Mutex + detect bool + failover bool + intface *net.Interface +} // Run runs the service. -func (*dpcpd) Run(args ...string) { +func (d *dhcpd) Run(args ...string) { if len(args) < 4 { log.F("[dhcpd] not enough parameters, exiting") return @@ -40,10 +48,11 @@ func (*dpcpd) Run(args ...string) { log.F("[dhcpd] get ip of interface '%s' error: %s", iface, err) return } + d.intface = intf - if existsServer(intf) { - log.F("[dhcpd] found existing dhcp server on interface %s, service exiting", iface) - return + if d.detect { + d.setFailoverMode(discovery(intf)) + go d.detectServer(time.Second * 60) } startIP, err := netip.ParseAddr(start) @@ -76,23 +85,23 @@ func (*dpcpd) Run(args ...string) { } laddr := net.UDPAddr{IP: net.IPv4(0, 0, 0, 0), Port: 67} - server, err := server4.NewServer(iface, &laddr, handleDHCP(ip, mask, pool)) + server, err := server4.NewServer(iface, &laddr, d.handleDHCP(ip, mask, pool)) if err != nil { log.F("[dhcpd] error in server creation: %s", err) return } - log.F("[dhcpd] Listening on interface %s(%s/%d.%d.%d.%d)", - iface, ip, mask[0], mask[1], mask[2], mask[3]) + log.F("[dhcpd] Listening on interface %s(%s/%d.%d.%d.%d), server detection: %t", + iface, ip, mask[0], mask[1], mask[2], mask[3], d.detect) server.Serve() } -func handleDHCP(serverIP net.IP, mask net.IPMask, pool *Pool) server4.Handler { +func (d *dhcpd) handleDHCP(serverIP net.IP, mask net.IPMask, pool *Pool) server4.Handler { return func(conn net.PacketConn, peer net.Addr, m *dhcpv4.DHCPv4) { - var replyType dhcpv4.MessageType - switch mt := m.MessageType(); mt { + var reqType, replyType dhcpv4.MessageType + switch reqType = m.MessageType(); reqType { case dhcpv4.MessageTypeDiscover: replyType = dhcpv4.MessageTypeOffer case dhcpv4.MessageTypeRequest, dhcpv4.MessageTypeInform: @@ -106,7 +115,11 @@ func handleDHCP(serverIP net.IP, mask net.IPMask, pool *Pool) server4.Handler { log.F("[dpcpd] received decline message from %v", m.ClientHWAddr) return default: - log.F("[dpcpd] can't handle type %v", mt) + log.F("[dpcpd] can't handle type %v", reqType) + return + } + + if d.inFailoverMode() || bytes.Equal(d.intface.HardwareAddr, m.ClientHWAddr) { return } @@ -146,6 +159,33 @@ func handleDHCP(serverIP net.IP, mask net.IPMask, pool *Pool) server4.Handler { } } +func (d *dhcpd) inFailoverMode() bool { + d.mu.Lock() + defer d.mu.Unlock() + return d.failover +} + +func (d *dhcpd) setFailoverMode(v bool) { + d.mu.Lock() + defer d.mu.Unlock() + + if d.failover != v { + if v { + log.F("[dpcpd] existing dhcp server detected, enter failover mode") + } else { + log.F("[dpcpd] no dhcp server detected, exit failover mode") + } + } + d.failover = v +} + +func (d *dhcpd) detectServer(interval time.Duration) { + for { + d.setFailoverMode(discovery(d.intface)) + time.Sleep(interval) + } +} + func ifaceAddr(iface string) (*net.Interface, net.IP, net.IPMask, error) { intf, err := net.InterfaceByName(iface) if err != nil {