Compare commits
	
		
			4 Commits
		
	
	
		
			d11855bfd0
			...
			ac7185bfb5
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | ac7185bfb5 | ||
|  | cb8b6c03b6 | ||
|  | b2dc964861 | ||
|  | 0bef42e761 | 
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -21,3 +21,6 @@ go-dhcp | ||||
|  | ||||
| # Go workspace file | ||||
| go.work | ||||
|  | ||||
| # direnv | ||||
| .envrc | ||||
|   | ||||
							
								
								
									
										70
									
								
								cmd/conn_unix.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								cmd/conn_unix.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | ||||
| // Copied with modifications from https://github.com/insomniacslk/dhcp/blob/master/dhcpv4/server4/conn_unix.go (under BSD-3-Clause license) | ||||
| //go:build !windows | ||||
|  | ||||
| package cmd | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"net" | ||||
| 	"os" | ||||
|  | ||||
| 	"github.com/adrianokf/go-dhcp/pkg/types" | ||||
| 	"golang.org/x/sys/unix" | ||||
| ) | ||||
|  | ||||
| // NewIPv4UDPConn returns a UDP connection bound to both the interface and port | ||||
| // given based on a IPv4 DGRAM socket. The UDP connection allows broadcasting. | ||||
| // | ||||
| // The interface must already be configured. | ||||
| func NewIPv4UDPConn(iface string, addr *net.UDPAddr) (*net.UDPConn, error) { | ||||
| 	fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, unix.IPPROTO_UDP) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("cannot get a UDP socket: %v", err) | ||||
| 	} | ||||
| 	f := os.NewFile(uintptr(fd), "") | ||||
| 	// net.FilePacketConn dups the FD, so we have to close this in any case. | ||||
| 	defer f.Close() | ||||
|  | ||||
| 	// Allow broadcasting. | ||||
| 	if err := unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_BROADCAST, 1); err != nil { | ||||
| 		return nil, fmt.Errorf("cannot set broadcasting on socket: %v", err) | ||||
| 	} | ||||
| 	// Allow reusing the addr to aid debugging. | ||||
| 	if err := unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_REUSEADDR, 1); err != nil { | ||||
| 		return nil, fmt.Errorf("cannot set reuseaddr on socket: %v", err) | ||||
| 	} | ||||
| 	// Allow reusing the port to aid debugging and testing. | ||||
| 	if err := unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_REUSEPORT, 1); err != nil { | ||||
| 		return nil, fmt.Errorf("cannot set reuseport on socket: %v", err) | ||||
| 	} | ||||
| 	if len(iface) != 0 { | ||||
| 		// Bind directly to the interface. | ||||
| 		if err := unix.BindToDevice(fd, iface); err != nil { | ||||
| 			return nil, fmt.Errorf("cannot bind to interface %s: %v", iface, err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if addr == nil { | ||||
| 		addr = &net.UDPAddr{Port: types.ServerPort} | ||||
| 	} | ||||
| 	// Bind to the port. | ||||
| 	saddr := unix.SockaddrInet4{Port: addr.Port} | ||||
| 	if addr.IP != nil && addr.IP.To4() == nil { | ||||
| 		return nil, fmt.Errorf("wrong address family (expected v4) for %s", addr.IP) | ||||
| 	} | ||||
| 	copy(saddr.Addr[:], addr.IP.To4()) | ||||
| 	if err := unix.Bind(fd, &saddr); err != nil { | ||||
| 		return nil, fmt.Errorf("cannot bind to port %d: %v", addr.Port, err) | ||||
| 	} | ||||
|  | ||||
| 	conn, err := net.FilePacketConn(f) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	udpconn, ok := conn.(*net.UDPConn) | ||||
| 	if !ok { | ||||
| 		return nil, errors.New("BUG: incorrect socket type, expected UDP") | ||||
| 	} | ||||
| 	return udpconn, nil | ||||
| } | ||||
							
								
								
									
										101
									
								
								cmd/root.go
									
									
									
									
									
								
							
							
						
						
									
										101
									
								
								cmd/root.go
									
									
									
									
									
								
							| @@ -11,20 +11,22 @@ import ( | ||||
| 	"github.com/adrianokf/go-dhcp/pkg/leases" | ||||
| 	"github.com/adrianokf/go-dhcp/pkg/messages" | ||||
| 	"github.com/adrianokf/go-dhcp/pkg/types" | ||||
| 	"github.com/adrianokf/go-dhcp/pkg/util" | ||||
| 	"github.com/spf13/cobra" | ||||
| 	"go.uber.org/zap" | ||||
| ) | ||||
|  | ||||
| var listenInterface string | ||||
|  | ||||
| var manager = leases.NewLeaseManager() | ||||
| type Handler struct { | ||||
| 	conn         *net.UDPConn | ||||
| 	leaseManager leases.LeaseManager | ||||
| } | ||||
|  | ||||
| var magic = [4]byte{0x63, 0x82, 0x53, 0x63} | ||||
|  | ||||
| func parseOptions(data []byte) messages.Options { | ||||
| 	s := zap.S() | ||||
| 	i := 0 | ||||
|  | ||||
| 	options := make(messages.Options) | ||||
|  | ||||
| out: | ||||
| @@ -32,11 +34,11 @@ out: | ||||
| 		code := messages.OptionCode(data[i]) | ||||
| 		switch code { | ||||
| 		case messages.OptionEnd: | ||||
| 			s.Debug("Found END option at offset ", i) | ||||
| 			zap.S().Debug("Found END option at offset ", i) | ||||
| 			break out | ||||
|  | ||||
| 		case messages.OptionPad: | ||||
| 			s.Debug("Found padding option at offset ", i) | ||||
| 			zap.S().Debug("Found padding option at offset ", i) | ||||
| 			i += 1 | ||||
| 			continue | ||||
| 		} | ||||
| @@ -44,12 +46,12 @@ out: | ||||
| 		size := int(data[i+1]) | ||||
| 		payload := data[i+2 : i+2+size] | ||||
|  | ||||
| 		s.Debugf("code=%d, size=%d, payload=%x", code, size, payload) | ||||
| 		zap.S().Debugf("code=%d, size=%d, payload=%x", code, size, payload) | ||||
| 		options[code] = messages.Option{Code: code, Data: data[i+1 : i+2+size]} | ||||
| 		i += size + 2 | ||||
| 	} | ||||
|  | ||||
| 	s.Debugf("Parsed options: ", options) | ||||
| 	zap.S().Debugf("Parsed options: ", options) | ||||
| 	return options | ||||
| } | ||||
|  | ||||
| @@ -108,7 +110,18 @@ func prepareAck(request messages.DhcpMessage, lease leases.Lease) messages.DhcpM | ||||
| // sendMessage transmits a DHCP message with options via a UDP connection | ||||
| // The end option (code 255) is automatically appended and does not need to | ||||
| // be passed explicitly. | ||||
| func sendMessage(conn *net.UDPConn, message messages.DhcpMessage, options []messages.Option) error { | ||||
| func (h Handler) sendMessage(remote *net.UDPAddr, message messages.DhcpMessage, options []messages.Option) error { | ||||
| 	// Send packets for 0.0.0.0 to broadcast address (255.255.255.255) instead | ||||
| 	var destination *net.UDPAddr | ||||
| 	if remote.IP.IsUnspecified() { | ||||
| 		destination = &net.UDPAddr{ | ||||
| 			IP:   net.ParseIP("255.255.255.255"), | ||||
| 			Port: types.ClientPort, | ||||
| 		} | ||||
| 	} else { | ||||
| 		destination = remote | ||||
| 	} | ||||
|  | ||||
| 	buf := make([]byte, 0) | ||||
| 	w := bytes.NewBuffer(buf) | ||||
| 	err := binary.Write(w, binary.BigEndian, message) | ||||
| @@ -127,16 +140,16 @@ func sendMessage(conn *net.UDPConn, message messages.DhcpMessage, options []mess | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Automatically add END option, so the caller doesn't | ||||
| 	// need to specificy it for every invocation. | ||||
| 	// Automatically add END option, so the caller doesn't need to specify it for every invocation. | ||||
| 	err = w.WriteByte(byte(messages.OptionEnd)) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	msg := w.Bytes() | ||||
| 	zap.S().Debug("Msg", msg) | ||||
| 	_, err = conn.Write(msg) | ||||
| 	zap.S().Debugf("Local addr: %s, remote addr: %s ", h.conn.LocalAddr(), destination) | ||||
| 	zap.S().Debug("Msg data: ", msg) | ||||
| 	_, err = h.conn.WriteToUDP(msg, destination) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| @@ -144,21 +157,14 @@ func sendMessage(conn *net.UDPConn, message messages.DhcpMessage, options []mess | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func handleOffer(dhcp messages.DhcpMessage, remote *net.UDPAddr) error { | ||||
| func (h Handler) sendOffer(dhcp messages.DhcpMessage, remote *net.UDPAddr) error { | ||||
| 	s := zap.S() | ||||
|  | ||||
| 	lease, err := manager.Request(dhcp.Xid, dhcp.Chaddr) | ||||
| 	lease, err := h.leaseManager.Request(dhcp.Xid, dhcp.Chaddr) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	offer := prepareOffer(dhcp, *lease) | ||||
| 	localAddr, _ := net.ResolveUDPAddr("udp", "172.17.0.1:68") | ||||
| 	clientAddr, _ := net.ResolveUDPAddr("udp", "255.255.255.255:68") | ||||
| 	conn, err := net.DialUDP("udp", localAddr, clientAddr) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	defer conn.Close() | ||||
|  | ||||
| 	s.Info("Sending DHCPOFFER...") | ||||
| 	options := []messages.Option{ | ||||
| @@ -167,14 +173,14 @@ func handleOffer(dhcp messages.DhcpMessage, remote *net.UDPAddr) error { | ||||
| 			Data: []byte{1, byte(messages.MessageTypeOffer)}, | ||||
| 		}, | ||||
| 	} | ||||
| 	sendMessage(conn, offer, options) | ||||
| 	h.sendMessage(remote, offer, options) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func handleAck(dhcp messages.DhcpMessage, remote *net.UDPAddr) error { | ||||
| func (h Handler) sendAck(dhcp messages.DhcpMessage, remote *net.UDPAddr) error { | ||||
| 	s := zap.S() | ||||
|  | ||||
| 	lease, err := manager.Lookup(dhcp.Xid) | ||||
| 	lease, err := h.leaseManager.Lookup(dhcp.Xid) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| @@ -188,31 +194,23 @@ func handleAck(dhcp messages.DhcpMessage, remote *net.UDPAddr) error { | ||||
| 		}, | ||||
| 		{ | ||||
| 			Code: messages.OptionIPAddressLeaseTime, | ||||
| 			Data: append([]byte{4}, u32tob(3600)...), | ||||
| 			Data: append([]byte{4}, util.U32ToByte(3600)...), | ||||
| 		}, | ||||
| 	} | ||||
| 	s.Debug("Options: ", options) | ||||
|  | ||||
| 	localAddr, _ := net.ResolveUDPAddr("udp", "172.17.0.1:68") | ||||
| 	clientAddr, _ := net.ResolveUDPAddr("udp", "255.255.255.255:68") | ||||
| 	conn, err := net.DialUDP("udp", localAddr, clientAddr) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	defer conn.Close() | ||||
|  | ||||
| 	s.Info("Sending DHCPACK") | ||||
| 	sendMessage(conn, ack, options) | ||||
| 	h.sendMessage(remote, ack, options) | ||||
|  | ||||
| 	lease, err = manager.Request(dhcp.Xid, dhcp.Chaddr) | ||||
| 	lease, err = h.leaseManager.Request(dhcp.Xid, dhcp.Chaddr) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	s.Debug("Found lease", lease) | ||||
| 	s.Debug("Found lease: ", lease) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func handleMsg(data []byte, remote *net.UDPAddr) { | ||||
| func (h Handler) handleMsg(data []byte, remote *net.UDPAddr) { | ||||
| 	s := zap.S() | ||||
|  | ||||
| 	s.Debugf("Connection from client %v", remote.IP) | ||||
| @@ -222,7 +220,7 @@ func handleMsg(data []byte, remote *net.UDPAddr) { | ||||
| 	binary.Read(reader, binary.BigEndian, &dhcp) | ||||
| 	dhcp.Debug(s) | ||||
|  | ||||
| 	if dhcp.Magic != [4]byte{0x63, 0x82, 0x53, 0x63} { | ||||
| 	if dhcp.Magic != magic { | ||||
| 		panic("Invalid DHCP magic field") | ||||
| 	} | ||||
|  | ||||
| @@ -236,10 +234,10 @@ func handleMsg(data []byte, remote *net.UDPAddr) { | ||||
|  | ||||
| 	switch messages.MessageType(dhcpMsgType.Data[1]) { | ||||
| 	case messages.MessageTypeDiscover: | ||||
| 		go handleOffer(dhcp, remote) | ||||
| 		go h.sendOffer(dhcp, remote) | ||||
|  | ||||
| 	case messages.MessageTypeRequest: | ||||
| 		go handleAck(dhcp, remote) | ||||
| 		go h.sendAck(dhcp, remote) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -249,7 +247,7 @@ func runServer(interfaceName string) { | ||||
| 		zap.L().Debug("Listening on all interfaces") | ||||
| 		addr, _ = net.ResolveUDPAddr("udp4", ":67") | ||||
| 	} else { | ||||
| 		zap.S().Debugf("Listening on interface %s", interfaceName) | ||||
| 		zap.S().Debug("Listening on interface ", interfaceName) | ||||
| 		iface, err := net.InterfaceByName(interfaceName) | ||||
| 		if err != nil { | ||||
| 			panic(err) | ||||
| @@ -275,17 +273,28 @@ func runServer(interfaceName string) { | ||||
| 			} | ||||
| 		} | ||||
| 		if ip == nil { | ||||
| 			zap.S().Panicf("No IPv4 address associated with interface %s", interfaceName) | ||||
| 			zap.S().Panic("No IPv4 address associated with interface ", interfaceName) | ||||
| 		} | ||||
| 		fmt.Printf("%+v\n", ip) | ||||
| 		addr, _ = net.ResolveUDPAddr("udp4", ip.String()+":67") | ||||
| 		addr, _ = net.ResolveUDPAddr("udp4", ":67") | ||||
| 	} | ||||
|  | ||||
| 	var conn *net.UDPConn | ||||
| 	var err error | ||||
| 	if interfaceName != "all" { | ||||
| 		conn, err = NewIPv4UDPConn(interfaceName, addr) | ||||
| 	} else { | ||||
| 		conn, err = net.ListenUDP("udp4", addr) | ||||
| 	} | ||||
| 	conn, err := net.ListenUDP("udp4", addr) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	defer conn.Close() | ||||
|  | ||||
| 	handler := Handler{ | ||||
| 		conn:         conn, | ||||
| 		leaseManager: *leases.NewLeaseManager(), | ||||
| 	} | ||||
|  | ||||
| 	zap.S().Infof("Listening for incoming connections on %s", addr.String()) | ||||
|  | ||||
| 	for { | ||||
| @@ -301,7 +310,7 @@ func runServer(interfaceName string) { | ||||
| 			zap.S().Warn("Not a valid remote IP address: ", remote) | ||||
| 			continue | ||||
| 		} | ||||
| 		go handleMsg(buf[0:rlen], remoteAddr) | ||||
| 		go handler.handleMsg(buf[0:rlen], remoteAddr) | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										2
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
									
									
									
									
								
							| @@ -11,6 +11,6 @@ require ( | ||||
|  | ||||
| require ( | ||||
| 	github.com/spf13/cobra v1.8.0 | ||||
| 	go.uber.org/atomic v1.11.0 // indirect | ||||
| 	go.uber.org/multierr v1.11.0 // indirect | ||||
| 	golang.org/x/sys v0.16.0 | ||||
| ) | ||||
|   | ||||
							
								
								
									
										32
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								go.sum
									
									
									
									
									
								
							| @@ -1,6 +1,5 @@ | ||||
| github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= | ||||
| github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= | ||||
| github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= | ||||
| github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= | ||||
| github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
| github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||||
| github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
| @@ -9,8 +8,7 @@ github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLf | ||||
| github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= | ||||
| github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | ||||
| github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= | ||||
| github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= | ||||
| github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||||
| github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= | ||||
| github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||||
| github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||||
| github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= | ||||
| @@ -19,24 +17,19 @@ github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyh | ||||
| github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= | ||||
| github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= | ||||
| github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||||
| github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | ||||
| github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= | ||||
| github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= | ||||
| github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= | ||||
| github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||||
| github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||||
| github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= | ||||
| github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= | ||||
| github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= | ||||
| github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= | ||||
| go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= | ||||
| go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= | ||||
| go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= | ||||
| go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= | ||||
| go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= | ||||
| go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= | ||||
| go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= | ||||
| go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= | ||||
| go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= | ||||
| go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= | ||||
| go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= | ||||
| go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= | ||||
| go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= | ||||
| go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= | ||||
| go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= | ||||
| go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= | ||||
| go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= | ||||
| golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||||
| @@ -54,6 +47,8 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w | ||||
| golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= | ||||
| golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||||
| golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | ||||
| golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
| golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||||
| @@ -66,9 +61,6 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T | ||||
| golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= | ||||
| gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||
| gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||
| gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= | ||||
| gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||
| gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||||
| gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||
|   | ||||
							
								
								
									
										27
									
								
								hack/make-interfaces.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										27
									
								
								hack/make-interfaces.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| #!/bin/bash -ex | ||||
|  | ||||
| # Create a venv-based virtual network for testing, consisting of: | ||||
| # | ||||
| # - venv0: Interface for the DHCP server (static address: 10.0.0.254/24) | ||||
| # - venv1: Interface for DHCP client | ||||
| # - br-dhcp: A bridge for communication between these interfaces | ||||
|  | ||||
| BR=br-dhcp | ||||
| SERVERADDR="10.0.0.254/24" | ||||
|  | ||||
| brctl delbr $BR | ||||
| brctl addbr $BR | ||||
|  | ||||
| for i in {0..1}; do | ||||
|     IFNAME="veth${i}" | ||||
|     PEERNAME="${IFNAME}s" | ||||
|  | ||||
|     ip link add "$IFNAME" type veth peer name "$PEERNAME" | ||||
|     ip link set "$IFNAME" up | ||||
|     ip link set "$PEERNAME" up | ||||
|  | ||||
|     brctl addif $BR "$PEERNAME" | ||||
| done | ||||
|  | ||||
| ip addr add dev veth0 "$SERVERADDR" | ||||
| ip link set "$BR" up | ||||
| @@ -1,8 +1,11 @@ | ||||
| package leases | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/adrianokf/go-dhcp/pkg/types" | ||||
| @@ -16,12 +19,14 @@ const ( | ||||
| 	Requested | ||||
| ) | ||||
|  | ||||
| const DumpFile = "/var/lib/go-dhcp/leases.json" | ||||
|  | ||||
| type Lease struct { | ||||
| 	TransactionId types.TxId | ||||
| 	TTL           time.Time | ||||
| 	State         LeaseState | ||||
| 	ClientAddr    types.HwAddr | ||||
| 	Address       types.Ipv4Addr | ||||
| 	TransactionId types.TxId     `json:"TransactionID"` | ||||
| 	TTL           time.Time      `json:"TTL"` | ||||
| 	State         LeaseState     `json:"State"` | ||||
| 	ClientAddr    types.HwAddr   `json:"ClientAddr"` | ||||
| 	Address       types.Ipv4Addr `json:"Address"` | ||||
| } | ||||
|  | ||||
| type ILeaseManager interface { | ||||
| @@ -35,9 +40,51 @@ type LeaseManager struct { | ||||
| 	count  byte | ||||
| } | ||||
|  | ||||
| func (m LeaseManager) DumpLeases() { | ||||
| 	err := os.MkdirAll(filepath.Dir(DumpFile), 0755) | ||||
| 	if err != nil { | ||||
| 		zap.S().Panic(err) | ||||
| 	} | ||||
|  | ||||
| 	f, err := os.Create(DumpFile) | ||||
| 	if err != nil { | ||||
| 		zap.S().Panic(err) | ||||
| 	} | ||||
| 	defer f.Close() | ||||
|  | ||||
| 	leases := make([]Lease, len(m.leases)) | ||||
| 	for _, l := range m.leases { | ||||
| 		leases = append(leases, l) | ||||
| 	} | ||||
|  | ||||
| 	enc := json.NewEncoder(f) | ||||
| 	enc.Encode(leases) | ||||
| 	f.Sync() | ||||
| } | ||||
|  | ||||
| func LoadLeases() (map[types.TxId]Lease, error) { | ||||
| 	var leaseMap = make(map[types.TxId]Lease) | ||||
|  | ||||
| 	data, err := os.ReadFile(DumpFile) | ||||
| 	if err != nil { | ||||
| 		return leaseMap, err | ||||
| 	} | ||||
| 	var leases []Lease | ||||
| 	json.Unmarshal(data, &leases) | ||||
|  | ||||
| 	for _, l := range leases { | ||||
| 		leaseMap[l.TransactionId] = l | ||||
| 	} | ||||
| 	return leaseMap, nil | ||||
| } | ||||
|  | ||||
| func NewLeaseManager() *LeaseManager { | ||||
| 	leases, err := LoadLeases() | ||||
| 	if err != nil { | ||||
| 		zap.S().Warnf("Could not restore saved leases from %s: %s", DumpFile, err) | ||||
| 	} | ||||
| 	m := &LeaseManager{ | ||||
| 		leases: make(map[types.TxId]Lease), | ||||
| 		leases: leases, | ||||
| 	} | ||||
| 	return m | ||||
| } | ||||
| @@ -62,6 +109,9 @@ func (m *LeaseManager) Request(xid types.TxId, clientAddr types.HwAddr) (*Lease, | ||||
| 	m.count += 1 | ||||
|  | ||||
| 	zap.S().Debug("lease=", lease) | ||||
|  | ||||
| 	m.DumpLeases() | ||||
|  | ||||
| 	return &lease, nil | ||||
| } | ||||
|  | ||||
| @@ -71,6 +121,9 @@ func (m LeaseManager) Release(l Lease) error { | ||||
| 		return fmt.Errorf("invalid lease %v", l) | ||||
| 	} | ||||
| 	delete(m.leases, l.TransactionId) | ||||
|  | ||||
| 	m.DumpLeases() | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										46
									
								
								pkg/types/json.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								pkg/types/json.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| package types | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"net" | ||||
| 	"strconv" | ||||
|  | ||||
| 	util "github.com/adrianokf/go-dhcp/pkg/util" | ||||
| ) | ||||
|  | ||||
| func (txid *TxId) UnmarshalText(text []byte) (err error) { | ||||
| 	result, err := strconv.ParseUint(string(text), 16, 32) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	copy(txid[:], util.U32ToByte(uint32(result))) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (id TxId) MarshalText() ([]byte, error) { | ||||
| 	return []byte(fmt.Sprintf("%x", id)), nil | ||||
| } | ||||
|  | ||||
| func (addr *Ipv4Addr) UnmarshalText(text []byte) (err error) { | ||||
| 	ip := net.ParseIP(string(text)) | ||||
| 	fmt.Println(ip) | ||||
| 	if ip == nil { | ||||
| 		return fmt.Errorf("could not parse IP address: '%s'", text) | ||||
| 	} | ||||
| 	*addr = Ipv4AddrFromNetIP(ip) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (addr Ipv4Addr) MarshalText() ([]byte, error) { | ||||
| 	return addr.ToNetIPv4().MarshalText() | ||||
| } | ||||
|  | ||||
| func (addr HwAddr) MarshalText() ([]byte, error) { | ||||
| 	return []byte(addr.String()), nil | ||||
| } | ||||
|  | ||||
| func (addr *HwAddr) UnmarshalText(text []byte) (err error) { | ||||
| 	*addr, err = HwAddrFromString(string(text)) | ||||
| 	return err | ||||
| } | ||||
| @@ -1,5 +1,37 @@ | ||||
| package types | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"net" | ||||
| ) | ||||
|  | ||||
| type TxId [4]byte | ||||
| type Ipv4Addr [4]byte | ||||
| type HwAddr [16]byte | ||||
|  | ||||
| const ( | ||||
| 	ServerPort = 67 | ||||
| 	ClientPort = 68 | ||||
| ) | ||||
|  | ||||
| func Ipv4AddrFromNetIP(ip net.IP) Ipv4Addr { | ||||
| 	ipv4 := ip.To4() | ||||
| 	return Ipv4Addr{ipv4[0], ipv4[1], ipv4[2], ipv4[3]} | ||||
| } | ||||
|  | ||||
| func (addr Ipv4Addr) ToNetIPv4() net.IP { | ||||
| 	return net.IPv4(addr[0], addr[1], addr[2], addr[3]) | ||||
| } | ||||
|  | ||||
| func (addr Ipv4Addr) String() string { | ||||
| 	return addr.ToNetIPv4().String() | ||||
| } | ||||
|  | ||||
| func (addr HwAddr) String() string { | ||||
| 	return fmt.Sprintf("%02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]) | ||||
| } | ||||
|  | ||||
| func HwAddrFromString(s string) (addr HwAddr, err error) { | ||||
| 	_, err = fmt.Sscanf(s, "%02x:%02x:%02x:%02x:%02x:%02x", &addr[0], &addr[1], &addr[2], &addr[3], &addr[4], &addr[5]) | ||||
| 	return addr, err | ||||
| } | ||||
|   | ||||
| @@ -1,10 +1,10 @@ | ||||
| package cmd | ||||
| package util | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/binary" | ||||
| ) | ||||
| 
 | ||||
| func u32tob(u uint32) []byte { | ||||
| func U32ToByte(u uint32) []byte { | ||||
| 	buf := make([]byte, 4) | ||||
| 	binary.BigEndian.PutUint32(buf, u) | ||||
| 	return buf | ||||
							
								
								
									
										13
									
								
								skaffold.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								skaffold.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| apiVersion: skaffold/v4beta8 | ||||
| kind: Config | ||||
| metadata: | ||||
|   name: go-dhcp | ||||
| build: | ||||
|   local: | ||||
|     push: false | ||||
|   artifacts: | ||||
|     - image: go-dhcp | ||||
|       ko: {} | ||||
| deploy: | ||||
|   docker: | ||||
|     images: [go-dhcp] | ||||
		Reference in New Issue
	
	Block a user