本文轉載自:http://blog.csdn.net/myvest/article/details/51483647
版權聲明:本文為博主原創文章,未經博主允許不得轉載。
1、問題背景
最近遇到一個問題,在一個項目中,無論靜態或是DHCP,都無法成功修改DNS。
最后發現,是因為/etc/dhcpcd/dhcpcd-hooks/20-dns.conf 這個腳本中,將DNS寫死了,如下代碼片,每次設置完以后,還去在設置一次。
setprop dhcp.${intf}.dns${dnsvalue} 203.82.48.3 setprop dhcp.${intf}.dns${standydnsvalue} 203.82.48.4
# Set net.<iface>.dnsN properties that contain the # DNS server addresses given by the DHCP server. if [[ $interface == p2p* ]] then intf=p2p else intf=$interface fi set_dns_props() { case "${new_domain_name_servers}" in "") return 0;; esac count=1 for i in 1 2 3 4; do setprop dhcp.${intf}.dns${i} "" done count=1 for dnsaddr in ${new_domain_name_servers}; do setprop dhcp.${intf}.dns${count} ${dnsaddr} count=$(($count + 1)) done dnsvalue=1 standydnsvalue=2 setprop dhcp.${intf}.dns${dnsvalue} 203.82.48.3 setprop dhcp.${intf}.dns${standydnsvalue} 203.82.48.4 separator=" " if [ -z "$new_domain_name" ]; then separator="" else if [ -z "$new_domain_search" ]; then separator="" fi fi setprop dhcp.${interface}.domain "${new_domain_name}$separator${new_domain_search}" } unset_dns_props() { for i in 1 2 3 4; do setprop dhcp.${intf}.dns${i} "" done setprop dhcp.${interface}.domain "" } case "${reason}" in BOUND|INFORM|REBIND|REBOOT|RENEW|TIMEOUT) set_dns_props;; EXPIRE|FAIL|IPV4LL|RELEASE|STOP) unset_dns_props;; esac
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
那么,這個腳本是有什么用處呢?為什么這個腳本可以寫死DNS。
這是因為android中,DHCP分為兩個部分,一個是DHCP Client端,一個是server端。
2、Dhcp客戶端
client端就是我們從上層APP到framework后發出的DHCP請求。這個流程不難,一般是最后會調用runDhcp函數,其JNI在
frameworks\base\core\jni\android_net_NetUtils.cpp
中的android_net_utils_runDhcpCommon。然后會在調用dhcp_do_request函數。到這里,framework的部分就完了,接下來會調用到system/core/libnetutils中去。
在dhcp_do_request函數中,當[dhcp.eth0.result]屬性變為[ok]時,會調用fill_ip_info函數,這個函數的作用,就是去讀取IP信息而已。
static void fill_ip_info(const char *interface, in_addr_t *ipaddr, in_addr_t *gateway, in_addr_t *mask, in_addr_t *dns1, in_addr_t *dns2, in_addr_t *server, uint32_t *lease) { property_get(“dhcp.eth0. ipaddress”, prop_value,NULL); property_get(“dhcp.eth0. gateway”, prop_value,NULL); property_get(“dhcp.eth0. mask”, prop_value,NULL); property_get(“dhcp.eth0. dns1”, prop_value,NULL); property_get(“dhcp.eth0. dns2”, prop_value,NULL); property_get(“dhcp.eth0. server”, prop_value,NULL); property_get(“dhcp.eth0. leasetime”, prop_value,NULL); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
那是誰去真正做DHCP的工作呢,是dhcpd這個守護進程。這個進程是根據init.svc.dhcpcd來控制的。這個進程會去設置這些屬性,這些屬性最后都是根據腳本設置的,也就是我們上面提到的腳本20-dns.conf和95-configured。這兩個腳本又是通過etc/dhcpcd/dhcpcd-run-hooks調起來的。
大概流程如下面:
3、Dhcp server端
dhcpcd 調用過程:
代碼在external/dhcpcd下,
=>main
# define SYSCONFDIR "/system/etc/dhcpcd" #define PACKAGE "dhcpcd" # define CONFIG SYSCONFDIR "/" PACKAGE ".conf" # define LIBEXECDIR "/system/etc/dhcpcd" # define SCRIPT LIBEXECDIR "/" PACKAGE "-run-hooks" =>strlcpy(options->script, SCRIPT, sizeof(options->script));//默認的options->script="/system/etc/dhcpcd /dhcpcd-run-hooks" =>f = fopen(cf ? cf : CONFIG, "r");//如果沒有指定.conf文件,那么使用默認.conf文件 =>parse_config_line//解析"/system/etc/dhcpcd/dhcpcd.conf"默認配置文件 =>parse_option =>如果在"/system/etc/dhcpcd/dhcpcd.conf"有"script"這個節 =>那么執行strlcpy(options->script, oarg, sizeof(options->script));直接拷貝 /* {"script", required_argument, NULL, 'c'}, {"option", required_argument, NULL, 'o'}, "/system/etc/dhcpcd/dhcpcd.conf"中的部分內容如下: ... option domain_name_servers, domain_name, domain_search, host_name ... */ =>dhcp_run =>handle_dhcp_packet =>handle_dhcp =>bind_dhcp reason = "TIMEOUT";reason = "BOUND";reason = "REBIND";reason = "RENEW"; system/extra/dhcpcd-4.0.0-beta9/configure.c => configure(iface, reason, state->new, state->old, &state->lease, options, 1); //如果dhcp超時或者dhcp成功,都會調用exec_script來執行腳本, //執行setprop dhcp.${interface}.result "failed"或者 //執行setprop dhcp.${interface}.result "ok" =>exec_script(options, iface->name, reason, NULL, old); =>然后configure_env通過環境變量將reason傳遞到腳本中 int exec_script(const struct options *options, const char *iface, const char *reason, const struct dhcp_message *dhcpn, const struct dhcp_message *dhcpo) =>pid = fork(); =>if(pid == 0)execve(options->script, argv, env);//子進程執行腳本,默認"/system/etc/dhcpcd/dhcpcd-run-hooks" //dhcpcd-run-hooks腳本會根據level值,決定是否執行system/etc/dhcpcd/dhcpcd-hook/*目錄下的相應文件 //我們的系統在該system/etc/dhcpcd/dhcpcd-hook/*目錄下有如下2個文件 //95-configured //20-dns.conf =>父進程返回while (waitpid(pid, &status, 0) == -1)等待子進程腳本執行完成
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
4、DHCP協議
有一點,在過濾網絡包的時候,應該過濾的是bootp,而不是dhcp哦,dhcp報文的種類有以下這些:
(1)DHCPDISCOVER(0×01),此為Client開始DHCP過程的第一個報文;
(2)DHCPOFFER(0×02),此為Server對DHCPDISCOVER報文的響應;
(3)DHCPREQUEST(0×03),此報文是Slient開始DHCP過程中對server的DHCPOFFER報文的回應,或者是client續延IP地址租期時發出的報文;
(4)DHCPDECLINE(0×04),當Client發現Server分配給它的IP地址無法使用,如IP地址沖突時,將發出此報文,通知Server禁止使用IP地址;
(5)DHCPACK(0×05),Server對Client的DHCPREQUEST報文的確認響應報文,Client收到此報文后,才真正獲得了IP地址和相關的配置信息;
(6)DHCPNAK(0×06),Server對Client的DHCPREQUEST報文的拒絕響應報文,Client收到此報文后,一般會重新開始新的DHCP過程;
(7)DHCPRELEASE(0×07),Client主動釋放server分配給它的IP地址的報文,當Server收到此報文后,就可以回收這個IP地址,能夠分配給其他的Client;
(8)DHCPINFORM(0×08),Client已經獲得了IP地址,發送此報文,只是為了從DHCPSERVER處獲取其他的一些網絡配置信息,如routeip,DNSIp等,這種報文的應用非常少見。
