2020-09-28 00:49:58 +08:00
|
|
|
package dhcpd
|
|
|
|
|
|
|
|
import (
|
2020-10-01 19:29:53 +08:00
|
|
|
"errors"
|
2020-09-28 00:49:58 +08:00
|
|
|
"net"
|
2022-01-28 23:35:29 +08:00
|
|
|
"net/netip"
|
2021-06-10 20:09:21 +08:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
2020-09-28 00:49:58 +08:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/insomniacslk/dhcp/dhcpv4"
|
|
|
|
"github.com/insomniacslk/dhcp/dhcpv4/server4"
|
|
|
|
|
2022-01-08 15:05:55 +08:00
|
|
|
"github.com/nadoo/glider/pkg/log"
|
2020-09-28 00:49:58 +08:00
|
|
|
"github.com/nadoo/glider/service"
|
|
|
|
)
|
|
|
|
|
|
|
|
var leaseTime = time.Hour * 12
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
service.Register("dhcpd", &dpcpd{})
|
|
|
|
}
|
|
|
|
|
|
|
|
type dpcpd struct{}
|
|
|
|
|
2020-09-29 18:59:57 +08:00
|
|
|
// Run runs the service.
|
2020-09-28 00:49:58 +08:00
|
|
|
func (*dpcpd) Run(args ...string) {
|
2021-06-10 20:09:21 +08:00
|
|
|
if len(args) < 4 {
|
2020-09-28 00:49:58 +08:00
|
|
|
log.F("[dhcpd] not enough parameters, exiting")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-01-28 23:35:29 +08:00
|
|
|
iface, start, end, leaseMin := args[0], args[1], args[2], args[3]
|
2021-06-10 20:09:21 +08:00
|
|
|
if i, err := strconv.Atoi(leaseMin); err != nil {
|
|
|
|
leaseTime = time.Duration(i) * time.Minute
|
|
|
|
}
|
|
|
|
|
2021-07-17 22:36:42 +08:00
|
|
|
ip, mask, _, err := ifaceAddr(iface)
|
2020-10-01 19:29:53 +08:00
|
|
|
if err != nil {
|
|
|
|
log.F("[dhcpd] get ip of interface '%s' error: %s", iface, err)
|
|
|
|
return
|
|
|
|
}
|
2020-09-28 00:49:58 +08:00
|
|
|
|
2021-06-10 20:09:21 +08:00
|
|
|
if existsServer(iface) {
|
2020-09-28 00:49:58 +08:00
|
|
|
log.F("[dhcpd] found existing dhcp server on interface %s, service exiting", iface)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-01-28 23:35:29 +08:00
|
|
|
startIP, err := netip.ParseAddr(start)
|
|
|
|
if err != nil {
|
|
|
|
log.F("[dhcpd] startIP %s is not valid: %s", start, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
endIP, err := netip.ParseAddr(end)
|
|
|
|
if err != nil {
|
|
|
|
log.F("[dhcpd] endIP %s is not valid: %s", end, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
pool, err := NewPool(leaseTime, startIP, endIP)
|
2020-09-28 00:49:58 +08:00
|
|
|
if err != nil {
|
|
|
|
log.F("[dhcpd] error in pool init: %s", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-06-10 20:09:21 +08:00
|
|
|
// static ips
|
|
|
|
for _, host := range args[4:] {
|
2022-02-20 01:07:51 +08:00
|
|
|
if mac, ip, ok := strings.Cut(host, "="); ok {
|
|
|
|
if mac, err := net.ParseMAC(mac); err == nil {
|
|
|
|
if ip, err := netip.ParseAddr(ip); err == nil {
|
2022-01-28 23:35:29 +08:00
|
|
|
pool.LeaseStaticIP(mac, ip)
|
|
|
|
}
|
2021-06-10 20:09:21 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-29 00:38:35 +08:00
|
|
|
laddr := net.UDPAddr{IP: net.IPv4(0, 0, 0, 0), Port: 67}
|
|
|
|
server, err := server4.NewServer(iface, &laddr, handleDHCP(ip, mask, pool))
|
2020-09-28 00:49:58 +08:00
|
|
|
if err != nil {
|
2020-09-29 00:38:35 +08:00
|
|
|
log.F("[dhcpd] error in server creation: %s", err)
|
2020-09-28 00:49:58 +08:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-10-01 19:29:53 +08:00
|
|
|
log.F("[dhcpd] Listening on interface %s(%s/%d.%d.%d.%d)",
|
2020-09-28 00:49:58 +08:00
|
|
|
iface, ip, mask[0], mask[1], mask[2], mask[3])
|
|
|
|
|
|
|
|
server.Serve()
|
|
|
|
}
|
|
|
|
|
|
|
|
func 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 {
|
|
|
|
case dhcpv4.MessageTypeDiscover:
|
|
|
|
replyType = dhcpv4.MessageTypeOffer
|
2021-06-07 19:14:58 +08:00
|
|
|
case dhcpv4.MessageTypeRequest, dhcpv4.MessageTypeInform:
|
2020-09-28 00:49:58 +08:00
|
|
|
replyType = dhcpv4.MessageTypeAck
|
2021-06-07 19:14:58 +08:00
|
|
|
case dhcpv4.MessageTypeRelease:
|
|
|
|
pool.ReleaseIP(m.ClientHWAddr)
|
|
|
|
log.F("[dpcpd] %v released ip %v", m.ClientHWAddr, m.ClientIPAddr)
|
|
|
|
return
|
|
|
|
case dhcpv4.MessageTypeDecline:
|
|
|
|
pool.ReleaseIP(m.ClientHWAddr)
|
|
|
|
log.F("[dpcpd] received decline message from %v", m.ClientHWAddr)
|
|
|
|
return
|
2020-09-28 00:49:58 +08:00
|
|
|
default:
|
|
|
|
log.F("[dpcpd] can't handle type %v", mt)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-01-01 00:44:31 +08:00
|
|
|
replyIP, err := pool.LeaseIP(m.ClientHWAddr)
|
2020-09-28 00:49:58 +08:00
|
|
|
if err != nil {
|
2020-10-03 20:51:27 +08:00
|
|
|
log.F("[dpcpd] can not assign IP, error %s", err)
|
2020-09-28 00:49:58 +08:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
reply, err := dhcpv4.NewReplyFromRequest(m,
|
|
|
|
dhcpv4.WithMessageType(replyType),
|
|
|
|
dhcpv4.WithServerIP(serverIP),
|
|
|
|
dhcpv4.WithNetmask(mask),
|
2022-01-28 23:35:29 +08:00
|
|
|
dhcpv4.WithYourIP(replyIP.AsSlice()),
|
2020-10-01 19:29:53 +08:00
|
|
|
dhcpv4.WithRouter(serverIP),
|
|
|
|
dhcpv4.WithDNS(serverIP),
|
2020-09-28 00:49:58 +08:00
|
|
|
// RFC 2131, Section 4.3.1. Server Identifier: MUST
|
|
|
|
dhcpv4.WithOption(dhcpv4.OptServerIdentifier(serverIP)),
|
|
|
|
// RFC 2131, Section 4.3.1. IP lease time: MUST
|
|
|
|
dhcpv4.WithOption(dhcpv4.OptIPAddressLeaseTime(leaseTime)),
|
|
|
|
)
|
2020-10-03 20:51:27 +08:00
|
|
|
if err != nil {
|
|
|
|
log.F("[dpcpd] can not create reply message, error %s", err)
|
|
|
|
return
|
|
|
|
}
|
2020-09-28 00:49:58 +08:00
|
|
|
|
|
|
|
if val := m.Options.Get(dhcpv4.OptionClientIdentifier); len(val) > 0 {
|
|
|
|
reply.UpdateOption(dhcpv4.OptGeneric(dhcpv4.OptionClientIdentifier, val))
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := conn.WriteTo(reply.ToBytes(), peer); err != nil {
|
2020-09-29 00:38:35 +08:00
|
|
|
log.F("[dpcpd] could not write to client %s(%s): %s", peer, reply.ClientHWAddr, err)
|
2020-09-28 00:49:58 +08:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-01-01 00:44:31 +08:00
|
|
|
log.F("[dpcpd] lease %v to client %v", replyIP, reply.ClientHWAddr)
|
2020-09-28 00:49:58 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-17 22:36:42 +08:00
|
|
|
func ifaceAddr(iface string) (net.IP, net.IPMask, net.HardwareAddr, error) {
|
2020-09-28 00:49:58 +08:00
|
|
|
intf, err := net.InterfaceByName(iface)
|
|
|
|
if err != nil {
|
2021-07-17 22:36:42 +08:00
|
|
|
return nil, nil, nil, err
|
2020-09-28 00:49:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
addrs, err := intf.Addrs()
|
|
|
|
if err != nil {
|
2021-07-17 22:36:42 +08:00
|
|
|
return nil, nil, intf.HardwareAddr, err
|
2020-09-28 00:49:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, addr := range addrs {
|
2020-10-01 19:29:53 +08:00
|
|
|
if ipnet, ok := addr.(*net.IPNet); ok {
|
|
|
|
if ipnet.IP.IsLoopback() {
|
2021-07-17 22:36:42 +08:00
|
|
|
return nil, nil, intf.HardwareAddr, errors.New("can't use loopback interface")
|
2020-10-01 19:29:53 +08:00
|
|
|
}
|
2020-09-29 00:38:35 +08:00
|
|
|
if ip4 := ipnet.IP.To4(); ip4 != nil {
|
2021-07-17 22:36:42 +08:00
|
|
|
return ip4, ipnet.Mask, intf.HardwareAddr, nil
|
2020-09-29 00:38:35 +08:00
|
|
|
}
|
2020-09-28 00:49:58 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-17 22:36:42 +08:00
|
|
|
return nil, nil, intf.HardwareAddr, errors.New("no ip/mask defined on this interface")
|
2020-09-28 00:49:58 +08:00
|
|
|
}
|