首先得講明udhcp用戶端設定IP的機制。
在udhcp中,用戶端通過和主機的多次報文互動協商而獲得可用的IP地址。在獲得IP地址之後,用戶端使用execle函數調用shell指令碼完成用戶端ip,netmak,dns,gateway等等參數的設定。而shell指令碼的調用在script.h和script.c中實現。
以下為script.c源碼中的主要實現函數:
/* get a rough idea of how long an option will be (rounding up...) */static int max_option_length[] = {[OPTION_IP] =sizeof("255.255.255.255 "),[OPTION_IP_PAIR] =sizeof("255.255.255.255 ") * 2,[OPTION_STRING] =1,[OPTION_BOOLEAN] =sizeof("yes "),[OPTION_U8] =sizeof("255 "),[OPTION_U16] =sizeof("65535 "),[OPTION_S16] =sizeof("-32768 "),[OPTION_U32] =sizeof("4294967295 "),[OPTION_S32] =sizeof("-2147483684 "),};
本函數為option欄位佔用的最大位元組數。如OPTION_BOOLEAN變數裡,true 對應yes。False對應no,那麼這種類型的變數中對應的最大位元組佔用就為sizeof(yes)。
static int upper_length(int length, struct dhcp_option *option){return max_option_length[option->flags & TYPE_MASK] * (length / option_lengths[option->flags & TYPE_MASK]);}
轉化為shell指令碼參數格式所佔位元組大小的上限。
/* Fill dest with the text of option 'option'. */static void fill_options(char *dest, unsigned char *option, struct dhcp_option *type_p){int type, optlen;u_int16_t val_u16;int16_t val_s16;u_int32_t val_u32;int32_t val_s32;int len = option[OPT_LEN - 2];//報文中該option的長度dest += sprintf(dest, "%s=", type_p->name);type = type_p->flags & TYPE_MASK;optlen = option_lengths[type];for(;;) {switch (type) {case OPTION_IP_PAIR:dest += sprintip(dest, "", option);*(dest++) = '/';option += 4;optlen = 4;case OPTION_IP:/* Works regardless of host byte order. */dest += sprintip(dest, "", option); break;case OPTION_BOOLEAN:dest += sprintf(dest, *option ? "yes " : "no ");break;case OPTION_U8:dest += sprintf(dest, "%u ", *option);break;case OPTION_U16:memcpy(&val_u16, option, 2);dest += sprintf(dest, "%u ", ntohs(val_u16));break;case OPTION_S16:memcpy(&val_s16, option, 2);dest += sprintf(dest, "%d ", ntohs(val_s16));break;case OPTION_U32:memcpy(&val_u32, option, 4);dest += sprintf(dest, "%lu ", (unsigned long) ntohl(val_u32));break;case OPTION_S32:memcpy(&val_s32, option, 4);dest += sprintf(dest, "%ld ", (long) ntohl(val_s32));break;case OPTION_STRING:memcpy(dest, option, len);dest[len] = '\0';return; /* Short circuit this case */}option += optlen;len -= optlen;if (len <= 0) break;}}
//讀取options,並輸出至dest所指記憶體。例如 router輸出格式為router=192.168.2.3
/* put all the paramaters into an environment */static char **fill_envp(struct dhcpMessage *packet){int num_options = 0;int i, j;char **envp;unsigned char *temp;char over = 0;if (packet == NULL)num_options = 0;else {for (i = 0; options[i].code; i++)if (get_option(packet, options[i].code))num_options++;if (packet->siaddr) num_options++;if ((temp = get_option(packet, DHCP_OPTION_OVER)))over = *temp;if (!(over & FILE_FIELD) && packet->file[0]) num_options++;if (!(over & SNAME_FIELD) && packet->sname[0]) num_options++;}envp = xmalloc((num_options + 5) * sizeof(char *));envp[0] = xmalloc(sizeof("interface=") + strlen(client_config.interface));sprintf(envp[0], "interface=%s", client_config.interface);envp[1] = find_env("PATH", "PATH=/bin:/usr/bin:/sbin:/usr/sbin");envp[2] = find_env("HOME", "HOME=/");//讀取本地環境變數if (packet == NULL) {envp[3] = NULL;return envp;}envp[3] = xmalloc(sizeof("ip=255.255.255.255"));//按最大的點分十進位格式申請空間sprintip(envp[3], "ip=", (unsigned char *) &packet->yiaddr);for (i = 0, j = 4; options[i].code; i++) {//讀取if ((temp = get_option(packet, options[i].code))) {envp[j] = xmalloc(upper_length(temp[OPT_LEN - 2], &options[i]) + strlen(options[i].name) + 2);fill_options(envp[j], temp, &options[i]);j++;}}if (packet->siaddr) {envp[j] = xmalloc(sizeof("siaddr=255.255.255.255"));sprintip(envp[j++], "siaddr=", (unsigned char *) &packet->siaddr);}if (!(over & FILE_FIELD) && packet->file[0]) {/* watch out for invalid packets */packet->file[sizeof(packet->file) - 1] = '\0';envp[j] = xmalloc(sizeof("boot_file=") + strlen(packet->file));sprintf(envp[j++], "boot_file=%s", packet->file);}if (!(over & SNAME_FIELD) && packet->sname[0]) {/* watch out for invalid packets */packet->sname[sizeof(packet->sname) - 1] = '\0';envp[j] = xmalloc(sizeof("sname=") + strlen(packet->sname));sprintf(envp[j++], "sname=%s", packet->sname);}envp[j] = NULL;return envp;}
按指定的格式填充環境變數
/* Call a script with a par file and env vars */void run_script(struct dhcpMessage *packet, const char *name){int pid;char **envp;if (client_config.script == NULL)return;/* call script */pid = fork();if (pid) {waitpid(pid, NULL, 0);return;} else if (pid == 0) {envp = fill_envp(packet);//從資料報文和主機中擷取相關資訊/* close fd's? *//* exec script */DEBUG(LOG_INFO, "execle'ing %s", client_config.script);execle(client_config.script, client_config.script, name, NULL, envp);LOG(LOG_ERR, "script %s failed: %s", client_config.script, strerror(errno));exit(1);}}
execle(client_config.script,client_config.script,name, NULL, envp);
函數在執行時envp所存環境變數能夠被shell script所直接使用。這種用法不大常見,具體execle函數用法,請google之。
以下為運行至execle函數時,envp的一個樣本:
interface=eth0
PATH= …(過長不寫明)…
HOME=/root
ip=192.168.0.24
subnet=255.255.255.0
router=192.168.10.2
dns= 129.219.13.81
domain=local
wins=192.168.10.10
leases=8640000
dhcptype=5
serverid=192.168.253.23
由上面可以看出這些參數和udhcpd服務端設定檔所指定的參數一致。
值得注意的是options.c檔案的全域dhcp_option變數並沒有將所有的option包括進去,從這點上講如果你是要擴充某個option欄位,則必需在options按指定的格式中加入相關欄位,同時如果該欄位不屬於option_lengths中指定類型中任何一種,那麼你也必需在其指定該類型欄位所佔的位元組數。當然script.c檔案中的max_option_length欄位也要添加相應資訊。
sample.script指令碼內容如下:
#!/bin/sh
# Currently, we only dispatch according tocommand. However, a more
# elaborate system might dispatch bycommand and interface or do some
# common initialization first, especiallyif more dhcp event notifications
# are added.
exec /usr/share/udhcpc/sample.$1
該指令碼就一句話,就是執行/usr/share/udhcpc/sample.$1指令碼。其中的$1為調用執行sample.script說指定的參數。
例如:
execle(client_config.script, client_config.script,
name, NULL, envp);
中,client_config.script為/usr/…/sample.script,name為”bound”,那麼在sample.script被執行時exec /usr/share/udhcpc/sample.$1語句中的$1就可以被替換為”bound”,亦即執行的是
exec /usr/share/udhcpc/sample.bound。
這裡$1可以為deconfig nak renew bound四個值。
在sample.bound指令碼中,出項了broadcast,subnet,ip,router等等變數這些變數若在envp中被指定了,那麼該變數就存在一個有意義的值。
更多資訊請參見:
1、 dhcp基本原理介紹:
H3C的dhcp技術白皮書
http://www.h3c.com.cn/Products___Technology/Technology/IPv4_IPv6_series/Other_technology/Technology_book/200802/333443_30003_0.htm
dhcp的rfc2131文檔
http://www.ietf.org/rfc/rfc2131.txt
2、 dhcp報文中的options欄位資訊(可選變長選項欄位)介紹:
http://www.networksorcery.com/enp/protocol/bootp/options.htm
rfc2132
http://www.ietf.org/rfc/rfc2132.txt
3、 execle指定環境變數並調用執行shell指令碼介紹:
APUE 8.10
http://blog.csdn.net/baidu20008/article/details/9764765
本人享有部落格文章的著作權,轉載請標明出處http://blog.csdn.net/baidu20008