Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Writing a DHCPv4 server on Windows. Is it supported? #497

Open
clarkmcc opened this issue Mar 8, 2023 · 7 comments
Open

Writing a DHCPv4 server on Windows. Is it supported? #497

clarkmcc opened this issue Mar 8, 2023 · 7 comments

Comments

@clarkmcc
Copy link

clarkmcc commented Mar 8, 2023

I'm working on writing a DHCP server. I recognize that on Windows this library won't bind to a specific network interface, so I tried this approach. I am able to successfully bind the UDP listener to the specific IP address which is assigned to a specific network interface, but I'm not seeing any logs indicating that packets are received.

conn, err := net.ListenUDP("udp4", &net.UDPAddr{
	IP:   net.ParseIP("10.0.0.253"),
	Port: dhcpv4.ServerPort,
})
require.NoError(t, err)

srv, err := server4.NewServer("Ethernet 2", nil, func(conn net.PacketConn, peer net.Addr, pkt *dhcpv4.DHCPv4) {
	fmt.Println("Received packet:", pkt.Summary())
}, server4.WithConn(conn))
require.NoError(t, err)
require.NoError(t, srv.Serve())

When I run this on macOS instead, it works just fine. Has anyone got this working on Windows before?

srv, err := server4.NewServer("Ethernet 2", &net.UDPAddr{
	IP:   net.ParseIP(":0"),
	Port: dhcpv4.ServerPort,
}, func(conn net.PacketConn, peer net.Addr, pkt *dhcpv4.DHCPv4) {
	fmt.Println("Received packet:", pkt.Summary())
})
require.NoError(t, err)
require.NoError(t, srv.Serve())
@QQ2017
Copy link

QQ2017 commented Apr 24, 2023

You may need to set the broadcast option of the socket.

@clarkmcc
Copy link
Author

clarkmcc commented Apr 25, 2023

@QQ2017 is that possible to do on a Windows socket from Go? Or do you need some syscall wizardry?

@QQ2017
Copy link

QQ2017 commented May 8, 2023

@QQ2017 is that possible to do on a Windows socket from Go? Or do you need some syscall wizardry?

On windows, You can use this lib:
https://github.com/krolaw/dhcp4

@clarkmcc
Copy link
Author

@QQ2017 doesn't seem to work. I'm able to receive DHCP DISCOVER requests using both libs, but on Windows, it seems like the OFFER responses are never making it back to the client. When I run the same code using either lib on macOS, the DISCOVER is immediately followed by a REQUEST from the client indicating that the OFFER was received.

I tried to figure out how to make it fail on macOS, and disabling the interface binding seemed to cause macOS to exhibit the same behavior as Windows.

I tried to enable broadcasting on the socket using the following code, but no change. I'm suspicious now that the issue may be related to the routing table. Meaning that my OFFER response is not actually being sent over the same socket that I received the DISCOVER. Sadly, I'm very outside of my comfort zone here. Any ideas?

func enableBroadcasting(conn net.PacketConn) error {
	c := conn.(*net.UDPConn)
	r, err := c.SyscallConn()
	if err != nil {
		return fmt.Errorf("getting syscall conn: %v", err)
	}
	cErr := r.Control(func(fd uintptr) {
		err = windows.SetsockoptInt(windows.Handle(fd), windows.SOL_SOCKET, windows.SO_BROADCAST, 1)
		if err != nil {
			err = fmt.Errorf("setting SO_BROADCAST: %v", err)
			return
		}
		err = windows.SetsockoptInt(windows.Handle(fd), windows.SOL_SOCKET, windows.SO_REUSEADDR, 1)
		if err != nil {
			err = fmt.Errorf("setting SO_REUSEADDR: %v", err)
			return
		}
	})
	if err != nil {
		return err
	}
	if cErr != nil {
		return fmt.Errorf("control: %v", cErr)
	}
	return nil
}

@QQ2017
Copy link

QQ2017 commented May 11, 2023

Use TCPDUMP to grab the data of port 67 on the server and submit it to me.

tcpdump -i ens1 port 67 -w 67.pcap

@clarkmcc
Copy link
Author

@QQ2017 Any ideas on a good way to do that on Windows? I couldn't seem to find any good tools for that for Windows. I'm running parallels Windows 11 on M1.

I ran the test on macOS without the interface binding which replicates the behavior that Windows has and here's what I've got.

13:56:25.497602 IP 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP, Request from 00:68:eb:4b:4b:bc, length 300
13:56:25.498374 IP 192.168.0.34.67 > 255.255.255.255.68: BOOTP/DHCP, Reply, length 274
13:56:29.763459 IP 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP, Request from 00:68:eb:4b:4b:bc, length 300
13:56:29.764065 IP 192.168.0.34.67 > 255.255.255.255.68: BOOTP/DHCP, Reply, length 274
13:56:38.195574 IP 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP, Request from 00:68:eb:4b:4b:bc, length 300
13:56:38.197123 IP 192.168.0.34.67 > 255.255.255.255.68: BOOTP/DHCP, Reply, length 274
13:56:54.859759 IP 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP, Request from 00:68:eb:4b:4b:bc, length 300
13:56:54.860249 IP 192.168.0.34.67 > 255.255.255.255.68: BOOTP/DHCP, Reply, length 274

If I enable interface binding again

# sudo tcpdump port 67 -n -i en4
14:00:28.010758 IP 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP, Request from 00:68:eb:4b:4b:bc, length 300
14:00:28.014130 IP 169.254.225.108.67 > 255.255.255.255.68: BOOTP/DHCP, Reply, length 274
14:00:28.014791 IP 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP, Request from 00:68:eb:4b:4b:bc, length 300
14:00:28.014997 IP 169.254.225.108.67 > 255.255.255.255.68: BOOTP/DHCP, Reply, length 274

# My application logs
time="2023-05-11T14:00:28-06:00" level=info msg="Got a Discover from 00:68:eb:4b:4b:bc"
time="2023-05-11T14:00:28-06:00" level=info msg="offer 10.0.0.14"
time="2023-05-11T14:00:28-06:00" level=info msg="Got a Request from 00:68:eb:4b:4b:bc"
time="2023-05-11T14:00:28-06:00" level=info msg="ack for 10.0.0.14"

Not sure if any of that is helpful, I can work on getting some kind of tcpdump solution working on Windows in the mean time.

@QQ2017
Copy link

QQ2017 commented May 15, 2023

@QQ2017 Any ideas on a good way to do that on Windows? I couldn't seem to find any good tools for that for Windows. I'm running parallels Windows 11 on M1.

I ran the test on macOS without the interface binding which replicates the behavior that Windows has and here's what I've got.

13:56:25.497602 IP 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP, Request from 00:68:eb:4b:4b:bc, length 300
13:56:25.498374 IP 192.168.0.34.67 > 255.255.255.255.68: BOOTP/DHCP, Reply, length 274
13:56:29.763459 IP 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP, Request from 00:68:eb:4b:4b:bc, length 300
13:56:29.764065 IP 192.168.0.34.67 > 255.255.255.255.68: BOOTP/DHCP, Reply, length 274
13:56:38.195574 IP 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP, Request from 00:68:eb:4b:4b:bc, length 300
13:56:38.197123 IP 192.168.0.34.67 > 255.255.255.255.68: BOOTP/DHCP, Reply, length 274
13:56:54.859759 IP 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP, Request from 00:68:eb:4b:4b:bc, length 300
13:56:54.860249 IP 192.168.0.34.67 > 255.255.255.255.68: BOOTP/DHCP, Reply, length 274

If I enable interface binding again

# sudo tcpdump port 67 -n -i en4
14:00:28.010758 IP 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP, Request from 00:68:eb:4b:4b:bc, length 300
14:00:28.014130 IP 169.254.225.108.67 > 255.255.255.255.68: BOOTP/DHCP, Reply, length 274
14:00:28.014791 IP 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP, Request from 00:68:eb:4b:4b:bc, length 300
14:00:28.014997 IP 169.254.225.108.67 > 255.255.255.255.68: BOOTP/DHCP, Reply, length 274

# My application logs
time="2023-05-11T14:00:28-06:00" level=info msg="Got a Discover from 00:68:eb:4b:4b:bc"
time="2023-05-11T14:00:28-06:00" level=info msg="offer 10.0.0.14"
time="2023-05-11T14:00:28-06:00" level=info msg="Got a Request from 00:68:eb:4b:4b:bc"
time="2023-05-11T14:00:28-06:00" level=info msg="ack for 10.0.0.14"

Not sure if any of that is helpful, I can work on getting some kind of tcpdump solution working on Windows in the mean time.

You can use wireshark on Windows, Post the pcap file to me.
capture filter: port 67

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants