dhcpd: added dhcpd-failover service, only keep linux support

This commit is contained in:
nadoo 2022-02-23 22:20:06 +08:00
parent 1f196c9cf5
commit c261e5989c
6 changed files with 69 additions and 33 deletions

View File

@ -38,7 +38,7 @@ we can set up local listeners as proxy servers, and forward requests to internet
- Periodical availability checking for forwarders - Periodical availability checking for forwarders
- Send requests from specific local ip/interface - Send requests from specific local ip/interface
- Services: - 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 ## Protocols
@ -84,6 +84,8 @@ we can set up local listeners as proxy servers, and forward requests to internet
## Usage ## Usage
#### Run
```bash ```bash
glider -config CONFIG_PATH glider -config CONFIG_PATH
``` ```
@ -94,7 +96,7 @@ glider -verbose -listen :8443 -forward SCHEME://HOST:PORT
#### Help #### Help
<details> <details>
<summary>`glider -help` (click to see details)</summary> <summary>`glider -help` (click)</summary>
```bash ```bash
Usage: glider [-listen URL]... [-forward URL]... [OPTION]... Usage: glider [-listen URL]... [-forward URL]... [OPTION]...
@ -207,6 +209,7 @@ Forwarder Options: FORWARD_URL#OPTIONS
Services: Services:
dhcpd: service=dhcpd,INTERFACE,START_IP,END_IP,LEASE_MINUTES[,MAC=IP,MAC=IP...] 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 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 #### Schemes
<details> <details>
<summary>`glider -scheme all` (click to see details)</summary> <summary>`glider -scheme all` (click)</summary>
```bash ```bash
KCP scheme: KCP scheme:
@ -340,7 +343,7 @@ TLS and Websocket with a specified proxy protocol:
#### Examples #### Examples
<details> <details>
<summary>`glider -example` (click to see details)</summary> <summary>`glider -example` (click)</summary>
```bash ```bash
Examples: Examples:
@ -391,9 +394,11 @@ Examples:
- dhcpd: - dhcpd:
- service=dhcpd,INTERFACE,START_IP,END_IP,LEASE_MINUTES[,MAC=IP,MAC=IP...] - 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,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,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 ## Linux Service

View File

@ -204,6 +204,7 @@ Forwarder Options: FORWARD_URL#OPTIONS
Services: Services:
dhcpd: service=dhcpd,INTERFACE,START_IP,END_IP,LEASE_MINUTES[,MAC=IP,MAC=IP...] 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 e.g. service=dhcpd,eth1,192.168.1.100,192.168.1.199,720
-- --

View File

@ -3,7 +3,6 @@ package main
import ( import (
// comment out the services you don't need to make the compiled binary smaller. // 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/xxx"
_ "github.com/nadoo/glider/service/dhcpd"
// comment out the protocols you don't need to make the compiled binary smaller. // comment out the protocols you don't need to make the compiled binary smaller.
_ "github.com/nadoo/glider/proxy/http" _ "github.com/nadoo/glider/proxy/http"

View File

@ -3,6 +3,7 @@ package main
import ( import (
// comment out the services you don't need to make the compiled binary smaller. // 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/xxx"
_ "github.com/nadoo/glider/service/dhcpd"
// comment out the protocols you don't need to make the compiled binary smaller. // comment out the protocols you don't need to make the compiled binary smaller.
_ "github.com/nadoo/glider/proxy/redir" _ "github.com/nadoo/glider/proxy/redir"

View File

@ -3,40 +3,30 @@ package dhcpd
import ( import (
"context" "context"
"net" "net"
"runtime"
"time" "time"
"github.com/insomniacslk/dhcp/dhcpv4" "github.com/insomniacslk/dhcp/dhcpv4"
"github.com/nadoo/glider/pkg/log"
"github.com/nadoo/glider/pkg/sockopt" "github.com/nadoo/glider/pkg/sockopt"
) )
func existsServer(intf *net.Interface) (exists bool) { func discovery(intf *net.Interface) (found bool) {
lc := &net.ListenConfig{} lc := &net.ListenConfig{}
lc.Control = sockopt.BindControl(intf) lc.Control = sockopt.BindControl(intf)
addr := "0.0.0.0:68" pc, err := lc.ListenPacket(context.Background(), "udp4", "255.255.255.255:68")
if runtime.GOOS == "linux" || runtime.GOOS == "android" {
addr = "255.255.255.255:68"
}
pc, err := lc.ListenPacket(context.Background(), "udp4", addr)
if err != nil { if err != nil {
log.F("[dhcpd] failed in dhcp client ListenPacket: %s", err)
return return
} }
defer pc.Close() defer pc.Close()
discovery, err := dhcpv4.NewDiscovery(intf.HardwareAddr, dhcpv4.WithBroadcast(true)) discovery, err := dhcpv4.NewDiscovery(intf.HardwareAddr, dhcpv4.WithBroadcast(true))
if err != nil { if err != nil {
log.F("[dhcpd] failed in dhcp client NewDiscovery: %s", err)
return return
} }
_, err = pc.WriteTo(discovery.ToBytes(), &net.UDPAddr{IP: net.IPv4bcast, Port: 67}) _, err = pc.WriteTo(discovery.ToBytes(), &net.UDPAddr{IP: net.IPv4bcast, Port: 67})
if err != nil { if err != nil {
log.F("[dhcpd] failed in dhcp client WriteTo: %s", err)
return return
} }

View File

@ -1,11 +1,13 @@
package dhcpd package dhcpd
import ( import (
"bytes"
"errors" "errors"
"net" "net"
"net/netip" "net/netip"
"strconv" "strconv"
"strings" "strings"
"sync"
"time" "time"
"github.com/insomniacslk/dhcp/dhcpv4" "github.com/insomniacslk/dhcp/dhcpv4"
@ -18,13 +20,19 @@ import (
var leaseTime = time.Hour * 12 var leaseTime = time.Hour * 12
func init() { 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. // Run runs the service.
func (*dpcpd) Run(args ...string) { func (d *dhcpd) Run(args ...string) {
if len(args) < 4 { if len(args) < 4 {
log.F("[dhcpd] not enough parameters, exiting") log.F("[dhcpd] not enough parameters, exiting")
return return
@ -40,10 +48,11 @@ func (*dpcpd) Run(args ...string) {
log.F("[dhcpd] get ip of interface '%s' error: %s", iface, err) log.F("[dhcpd] get ip of interface '%s' error: %s", iface, err)
return return
} }
d.intface = intf
if existsServer(intf) { if d.detect {
log.F("[dhcpd] found existing dhcp server on interface %s, service exiting", iface) d.setFailoverMode(discovery(intf))
return go d.detectServer(time.Second * 60)
} }
startIP, err := netip.ParseAddr(start) 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} 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 { if err != nil {
log.F("[dhcpd] error in server creation: %s", err) log.F("[dhcpd] error in server creation: %s", err)
return return
} }
log.F("[dhcpd] Listening on interface %s(%s/%d.%d.%d.%d)", 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]) iface, ip, mask[0], mask[1], mask[2], mask[3], d.detect)
server.Serve() 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) { return func(conn net.PacketConn, peer net.Addr, m *dhcpv4.DHCPv4) {
var replyType dhcpv4.MessageType var reqType, replyType dhcpv4.MessageType
switch mt := m.MessageType(); mt { switch reqType = m.MessageType(); reqType {
case dhcpv4.MessageTypeDiscover: case dhcpv4.MessageTypeDiscover:
replyType = dhcpv4.MessageTypeOffer replyType = dhcpv4.MessageTypeOffer
case dhcpv4.MessageTypeRequest, dhcpv4.MessageTypeInform: 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) log.F("[dpcpd] received decline message from %v", m.ClientHWAddr)
return return
default: 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 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) { func ifaceAddr(iface string) (*net.Interface, net.IP, net.IPMask, error) {
intf, err := net.InterfaceByName(iface) intf, err := net.InterfaceByName(iface)
if err != nil { if err != nil {