OpenWrt也是Linux,題目其實也可以叫做“Linux獲取網關IP”。一般想得知網關IP,都是因為本地接口設置了DHCP,網關IP,大多數也即是DHCP Server的IP(DHCP Relay除外)。
二、一般方法匯總
1、猜
沒錯,是猜,因為網關IP一般為XXX.XXX.XXX.1,所以ifconfig出來的接口IP,就可以推測出網關的IP。
2、查路由表
當連接建立之后,本地一般會生成默認的路由,因此查路由表,就可以直接得知網關IP。
此類方法有很多,比如一些命令,以及以這些命令為基礎制作的腳本
1)route -n
2)ip route show
3)netstat -r
還有一些其他命令,如traceroute等。
3、寫代碼獲取內核消息
是方法2的進階,除了利用命令寫腳本,也可以通過與內核通信,獲取一些信息
通過NLMSG從內核獲取路由信息,本質還是在查路由表,
4、查看resolv.conf
針對dhcp,一般如果使用了dnsmasq,會記錄一些信息如網關、dns等,這些信息一般會在resolv.conf/resolv.conf.auto等文件中,而這個文件大部會在/var/resolv.conf、/etc/resolv.conf等目錄,具體可以查看dhcp或dnsmasq的配置文件。
三、解析DHCP消息
這里想著重提一下從dhcp消息中,獲取dhcp server ip(即網關IP)。DHCP的詳細原理這里就不在冗述了,直接看圖
DHCP原理圖
本端作為dhcp client,從收到dhcp offer開始,就已經知道dhcp server的IP,只要解析這個消息即可。
dhcp offer報文格式中的Siaddr字段內容,即dhcp server IP.
Siaddr: IP address of next server to use in bootstrap.
這個才是最根本的源頭。
那么問題來了,dhcp程序有很多,比如常用的有dhclient或busybox中的udhcpc等,如何從進程中獲得dhcp Packet的內容呢?
一種方式是打補丁,這些代碼都是開源的,如果開發的系統允許自行修改,可以干脆修改dhcp源碼增加一些輸出。
另外發現dhcp程序的參數支持script運行,比如udhcpc的-s參數
-s,--script PROG Run PROG at DHCP events (default /usr/share/udhcpc/default.script)
以OpenWrt為例
root@OpenWrt:/# ps |grep dhcp
1384 root 1516 S udhcpc -S -p /var/run/udhcpc-ath01.pid -s /lib/netifd/dhcp.script -f -t 0 -i ath01 -C
3212 root 1500 S grep dhcp
root@OpenWrt:/#
/lib/netifd/dhcp.script的內容:
root@OpenWrt:/# cat /lib/netifd/dhcp.script
#!/bin/sh
[ -z "$1" ] && echo "Error: should be run by udhcpc" && exit 1
. /lib/functions.sh
. /lib/netifd/netifd-proto.sh
set_classless_routes() {
local max=128
local type
while [ -n "$1" -a -n "$2" -a $max -gt 0 ]; do
proto_add_ipv4_route "${1%%/*}" "${1##*/}" "$2"
max=$(($max-1))
shift 2
done
}
setup_interface () {
proto_init_update "$IFNAME" 1
proto_add_ipv4_address "$ip" "${subnet:-255.255.255.0}"
# TODO: apply $broadcast
for i in $router; do
echo "i=$i" > /dev/console
proto_add_ipv4_route 0.0.0.0 0 "$i"
done
# CIDR STATIC ROUTES (rfc3442)
[ -n "$staticroutes" ] && set_classless_routes $staticroutes
[ -n "$msstaticroutes" ] && set_classless_routes $msstaticroutes
for dns in $dns; do
proto_add_dns_server "$dns"
done
for domain in $domain; do
proto_add_dns_search "$domain"
done
proto_send_update "$INTERFACE"
# TODO
# [ -n "$ntpsrv" ] && change_state network "$ifc" lease_ntpsrv "$ntpsrv"
# [ -n "$timesvr" ] && change_state network "$ifc" lease_timesrv "$timesvr"
# [ -n "$hostname" ] &&change_state network "$ifc" lease_hostname "$hostname"
# [ -n "$timezone" ] && change_state network "$ifc" lease_timezone "$timezone"
}
deconfig_interface() {
proto_init_update "*" 0
proto_send_update "$INTERFACE"
}
case "$1" in
deconfig)
deconfig_interface
;;
renew|bound)
setup_interface
;;
esac
echo "4=$4" > /dev/console
# user rules
[ -f /etc/udhcpc.user ] && . /etc/udhcpc.user
exit 0
root@OpenWrt:/#
經測試$router的值就是Siaddr的值。