glider/service/dhcpd/dhcpd.go

150 lines
3.5 KiB
Go
Raw Normal View History

2020-09-28 00:49:58 +08:00
package dhcpd
import (
"context"
2020-10-01 19:29:53 +08:00
"errors"
2020-09-28 00:49:58 +08:00
"net"
"time"
"github.com/insomniacslk/dhcp/dhcpv4"
"github.com/insomniacslk/dhcp/dhcpv4/nclient4"
"github.com/insomniacslk/dhcp/dhcpv4/server4"
"github.com/nadoo/glider/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{}
// Run runs the service.
2020-09-28 00:49:58 +08:00
func (*dpcpd) Run(args ...string) {
if len(args) < 3 {
log.F("[dhcpd] not enough parameters, exiting")
return
}
2020-10-01 19:29:53 +08:00
iface := args[0]
ip, mask, err := intfaceIP4(iface)
if err != nil {
log.F("[dhcpd] get ip of interface '%s' error: %s", iface, err)
return
}
2020-09-28 00:49:58 +08:00
2020-10-01 19:29:53 +08:00
if findExistServer(iface) {
2020-09-28 00:49:58 +08:00
log.F("[dhcpd] found existing dhcp server on interface %s, service exiting", iface)
return
}
2020-10-01 19:29:53 +08:00
pool, err := NewPool(leaseTime, net.ParseIP(args[1]), net.ParseIP(args[2]))
2020-09-28 00:49:58 +08:00
if err != nil {
log.F("[dhcpd] error in pool init: %s", err)
return
}
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
case dhcpv4.MessageTypeRequest:
replyType = dhcpv4.MessageTypeAck
default:
log.F("[dpcpd] can't handle type %v", mt)
return
}
replyIp, err := pool.AssignIP(m.ClientHWAddr)
if err != nil {
log.F("[dpcpd] can not assign IP error %s", err)
return
}
reply, err := dhcpv4.NewReplyFromRequest(m,
dhcpv4.WithMessageType(replyType),
dhcpv4.WithServerIP(serverIP),
dhcpv4.WithNetmask(mask),
dhcpv4.WithYourIP(replyIp),
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)),
)
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
}
log.F("[dpcpd] lease %v to client %v", replyIp, reply.ClientHWAddr)
}
}
2020-10-01 19:29:53 +08:00
func findExistServer(iface string) (exists bool) {
2020-09-28 00:49:58 +08:00
client, err := nclient4.New(iface)
if err != nil {
log.F("[dhcpd] failed in dhcp client creation: %s", err)
return
}
defer client.Close()
ctx, _ := context.WithTimeout(context.Background(), 3*time.Second)
_, err = client.Request(ctx)
if err != nil {
return
}
return true
}
2020-10-01 19:29:53 +08:00
func intfaceIP4(iface string) (net.IP, net.IPMask, error) {
2020-09-28 00:49:58 +08:00
intf, err := net.InterfaceByName(iface)
if err != nil {
2020-10-01 19:29:53 +08:00
return nil, nil, err
2020-09-28 00:49:58 +08:00
}
addrs, err := intf.Addrs()
if err != nil {
2020-10-01 19:29:53 +08:00
return nil, nil, 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() {
return nil, nil, errors.New("can't use loopback interface")
}
2020-09-29 00:38:35 +08:00
if ip4 := ipnet.IP.To4(); ip4 != nil {
2020-10-01 19:29:53 +08:00
return ip4, ipnet.Mask, nil
2020-09-29 00:38:35 +08:00
}
2020-09-28 00:49:58 +08:00
}
}
2020-10-01 19:29:53 +08:00
return nil, nil, errors.New("no ip/mask defined on this interface")
2020-09-28 00:49:58 +08:00
}