在啓動dhcpd進程的日誌記錄開始部分,我們都會看到類似如下的打印:
create for interface for eth5 dhcp_interface_setup_hook eth5, for addr 10.40.124.205 create for interface for base0 dhcp_interface_setup_hook base0, for addr 169.254.1.1 add to interface for base0 dhcp_interface_setup_hook base0, for addr 169.254.100.100 add to interface for VIF2 dhcp_interface_setup_hook VIF2, for addr 192.168.1.10 Listening on LPF/VIF2/b8:c8:55:ac:03:81/network-VIF2 Sending on LPF/VIF2/b8:c8:55:ac:03:81/network-VIF2 Sending on Socket/fallback/fallback-net
如果你再使用ifconfig查看一下系統的網口信息就會發現,eth5/base0/VIF2都是有ip地址,其他的網口是沒有地址的(當然lo除外),並且它們與上面的前8行對應。
我將dhcpd監聽網口的步驟理解爲兩大步:發現網口,監聽網口。在common/discover.c中我們能找到函數void discover_interfaces(int state)。所有被發現的網口都會存放到全局變量interfaces鏈表中,然後調用判斷網口信息是否合法,與地址池是否匹配等等,最後完成網口接收和發送報文的初始化。
一、發現網口
在這一步主要使用了三個函數(begin_iface_scan/next_iface/end_iface_scan)組成。但對於不同的系統它們的實現也有所不同。在這裏支持三個類型的系統Solaris、Linux、BSD,他們都是類UNIX系統,之間有很多相似之處,也有很多不同。爲什麼要做這樣的區分呢?
Solaris:ioctl()支持SIOCGLIFCONF參數擴展來獲取網口配置信息。
Linux:通過/proc/net/dev文件來讀取本機所有網口名,同樣也通過ioctl()來獲取ip/flag等信息。
BSD:提供getifaddrs() 函數來獲取所有網口。
在進行上述三個函數來發現所有網口時,同時過濾掉不可用的網口(loopback或者沒有ip的網口),並調用dhcp_interface_setup_hook找出每個可用的網口所在的網段(subnet)。
二、監聽網口
這一步只需要關注兩個函數(if_register_receive/if_register_send)。這兩個函數只是針對dhcpv4來說,dhcpv6則需要使用if_register6。
與第一步類似,對於不同的系統也有所區分。主要有以下幾種:
1. LPF
全稱Linux Packet Filter,主要適用一般linux系統。接收/發送報文使用raw socket,並在接收報文時通過setsockopt()過濾指定端口的報文。dhcpv4使用67端口,v6則使用547。
2. Socket
主要適用BSD系統,使用udp socket。
3. UPF
全稱 Ultrix Packet Filter。然後打一個upf設備,通過ioctl()來關聯對應的網口進行收發報文。
4. BPF
與UPF類似,只是打開的設備文件不同。
5. NIT
全稱Network Interface Tap。與UPF也類似,區別在於打開的設備文件。
6. DLPI
全稱Data Link Provider Interface。打開一個dlpi設備,同樣將這個打開的設備文件與網口綁定。
在根據上面各種不同情況,初始化了接收/發送報文的文件描述符,然後調用omapi_register_io_object()來註冊回調函數,接收報文,並調用處理報文函數。
over