Một trong những giải pháp hiệu quả để xử lý yêu cầu cấp phát địa chỉ IP trong mạng là sử dụng DHCP relay (còn gọi là DHCP proxy). Dự án dhcp-relay của Mirantis trên GitHub cung cấp một công cụ nhẹ, được viết bằng Go, cho phép chuyển tiếp các gói tin DHCP giữa client và server. Dự án này được phân phối dưới giấy phép Apache-2.0, phù hợp với nhiều môi trường sản xuất.
Cài đặt và biên dịch dự án
Đầu tiên, sao chép mã nguồn từ kho lưu trữ chính thức:
git clone https://github.com/Mirantis/dhcp-relay
cd dhcp-relay
Sử dụng lệnh make all để thực hiện build ban đầu, hoặc tự biên dịch trực tiếp với cấu hình hỗ trợ build tĩnh:
env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOPROXY=proxy.golang.org go build \
--tags "netgo osusergo static_build" \
-ldflags='-s -w -extldflags "-static"' \
-a -o /home/user/dhcp-relay/BUILD/dhcp-relay
Khởi động dịch vụ DHCP proxy với các tham số cần thiết:
sudo ./BUILD/dhcp-relay \
-debug \
-dhcp-server-address 192.168.122.1 \
-debug-server localhost:8082
Lệnh này kích hoạt chế độ gỡ lỗi, chỉ định địa chỉ máy chủ DHCP đích và bật một endpoint HTTP để theo dõi trạng thái.
Kiểm tra hoạt động bằng công cụ bên ngoài
Từ một máy khách khác trong cùng mạng, gửi yêu cầu DHCP Discover bằng Nmap:
sudo nmap --script broadcast-dhcp-discover
Nmap sẽ phát tán gói DHCPDISCOVER và nếu cấu hình mạng cho phép, gói tin sẽ được bắt bởi proxy và chuyển tiếp đến máy chủ DHCP đích.
Quan sát nhật ký hoạt động
Trên máy chạy proxy, bạn có thể theo dõi luồng dữ liệu qua log đầu ra như sau:
2023/10/31 02:46:37 DBG | Received 358 bytes of data from socket
2023/10/31 02:46:37 INF | --> 0x64080343: DHCP-DISCOVER [316], IfIndex=2, Src=192.168.122.54:68(52:54:00:2f:19:ff), Dst=255.255.255.255:67(ff:ff:ff:ff:ff:ff)
2023/10/31 02:46:37 DBG | Option 82 -> Sub-option: Type=1, Len=1, Data=[32], ASCII="2"
2023/10/31 02:46:37 INF | <-- 0x64080343: DHCP-DISCOVER [321], Src=0.0.0.0:67, Dst=192.168.122.1:67
2023/10/31 02:46:40 INF | --> 0x64080343: DHCP-OFFER [297], Src=192.168.122.1:67(52:54:00:7e:c2:89), Dst=192.168.122.171:67(52:54:00:2b:ad:08)
2023/10/31 02:46:40 INF | <-- 0x64080343: DHCP-OFFER [292], IfIndex=2, Src=0.0.0.0:67, Dst=255.255.255.255:68
Các bản ghi cho thấy rõ quá trình nhận gói DISCOVER từ client, chuyển tiếp đến server và phản hồi lại gói OFFER.
Phân tích gói tin bằng tcpdump
Để kiểm tra sâu hơn, bạn có thể bắt toàn bộ lưu lượng liên quan bằng lệnh:
sudo tcpdump -i any -s 0 -U -w ~/proxy-dhcp.cap not port 22
Tệp proxy-dhcp.cap sau đó có thể mở bằng Wireshark để xem chi tiết cấu trúc các gói tin DHCP.
Mở rộng chức năng xử lý tùy chọn DHCP
Dự án có thể được tùy chỉnh để trích xuất thông tin từ các tùy chọn DHCP phổ biến. Ví dụ, thêm đoạn mã sau để đọc các trường Option 53 (Message Type), Option 60 (Class Identifier) và Option 77 (User Class Information):
const (
UserClassInformation layers.DHCPOpt = 77
)
func getUserClassOption(dhcpLayer *layers.DHCPv4) layers.DHCPOption {
opt := dhcp.GetOption(dhcpLayer, UserClassInformation)
if !dhcp.IsOption(opt) || len(opt.Data) == 0 || int(opt.Length) > len(opt.Data) {
return layers.DHCPOption{}
}
return opt
}
Sau đó, tích hợp vào vòng xử lý chính:
opt77 := getUserClassOption(layerDHCPv4)
if dhcp.IsOption(opt77) {
userClassValue := string(opt77.Data[:opt77.Length])
cl.Debugf("Tìm thấy Option 77: %s", userClassValue)
} else {
cl.Debugf("Không tìm thấy Option 77")
}
opt60 := dhcp.GetOption(layerDHCPv4, layers.DHCPOptClassID)
if opt60.Length > 0 {
classID := string(opt60.Data[:opt60.Length])
cl.Debugf("Tìm thấy Option 60: %s", classID)
// Kiểm tra loại client theo class ID
if strings.Split(classID, ":")[0] == "HTTPClient" {
cl.Debugf("Phát hiện client HTTP đặc biệt.")
}
}
Việc bổ sung này giúp lọc và xử lý riêng biệt các thiết bị client dựa trên định danh lớp hoặc nhóm người dùng.