udhcp源碼詳解(五) 之DHCP包--options字段
Author : hui <[email protected]>
From : <http://blog.csdn.net/hui_love2046>
Created : 2010-10-25
中間有很長一段時間沒有更新udhcp源碼詳解的博客,主要是源碼裏的函數太多,不知道要不要一個一個講下去,要知道講DHCP的實現理論的話一篇博文也就可以大致的講完,但實現的源碼卻要關心很多的問題,比如說,理論上說從IP地址池取到一個空閒的IP,就這麼一句,在源碼的體現也是一大段。算啦,講多少算多少吧,進入主題!
struct dhcpMessage報文裏uint8_t options[308]字段,在整個DHCP過程中是報文的一個很重要的字段,博文的系列(二)有講解該字段的數據組織方式,CLV(Code + Len + Value),現在來講解下怎麼把選項信息添加進該字段,以及怎麼從該字段取到相應的選項信息。
options字段存儲三類數據:
a). DHCP_PADDING 填充字節, 沒有任何意義,填充 0x00
b). DHCP_END potions字段結束的標誌 0xFF
c). 選項信息<CLV> 對於DHCP過程真正有價值的信息,承載了選項數據(V)
對於選options字段的操作主要就是read/write value:
1、根據選項信息的CODE從option字段取出選項信息.
[cpp] view plain copy
- /*
- * 參數struct dhcpMessage *packet DHCP報文
- * int code需要獲得什麼選項信息(選項信息的標識)
- *
- * 返回指向選項信息的指針(去除了 OPT_CODE,OPT_LEN)
- * 未找到返回NULL
- */
- uint8_t *get_option(struct dhcpMessage *packet, int code)
- {
- int i, length;
- uint8_t *optionptr;
- int over = 0, done = 0, curr = OPTION_FIELD;
- optionptr = packet->options;
- i = 0;
- length = 308; /* 整個options字段的長度308 */
- /* 在options字段裏查找code選項標識信息*/
- while (!done) {
- if (i >= length) { /* 查找完所有字段都未找到code標識的信息,返回NULL */
- LOG(LOG_WARNING, "bogus packet, option fields too long.");
- return NULL;
- }
- //CLV方式存儲數據
- //這裏與struct option_set的data存儲相似
- //OPT_CODE字節上存儲code標識
- //OPT_LEN 字節上存儲信息長度
- //OPT_LEN後就是存儲信息
- if (optionptr[i + OPT_CODE] == code) { //Found
- if (i + 1 + optionptr[i + OPT_LEN] >= length) { //檢查選項信息長度
- LOG(LOG_WARNING, "bogus packet, option fields too long.");
- return NULL;
- }
- return optionptr + i + 2; //Found,返回選項信息的首地址
- }
- switch (optionptr[i + OPT_CODE]) {
- case DHCP_PADDING: //DHCP_PADING(填充)字節,直接 i++;
- i++;
- break;
- case DHCP_OPTION_OVER: //選項過載DHCP_OPTION_OVER
- if (i + 1 + optionptr[i + OPT_LEN] >= length) {
- LOG(LOG_WARNING, "bogus packet, option fields too long.");
- return NULL;
- }
- /*
- optionptr[i + OPT_CODE] == DHCP_OPTION_OVER選項過載;
- optionptr[i + 3]存放了採用哪個字段來存儲過載的選項
- 可能存儲過載選項的字段:
- uint8_t sname[64]; //server host name (ASCIZ)
- uint8_t file[128]; // boot file name (ASCIZ)
- over = optionptr[i + 3];記錄下使用那個字段存儲過載選項
- */
- /*
- *
- The code for this option is 52, and its length is 1. Legal values
- for this option are:
- Value Meaning
- ----- --------
- 1 the 'file' field is used to hold options
- 2 the 'sname' field is used to hold options
- 3 both fields are used to hold options
- Code Len Value
- +-----+-----+-----+
- | 52 | 1 |1/2/3|
- +-----+-----+-----+
- */
- over = optionptr[i + OPT_DATA];
- i += optionptr[i + OPT_LEN] + 2;
- // over = optionptr[i + 3]; /* Error */
- // i += optionptr[OPT_LEN] + 2; /* Error */
- break;
- case DHCP_END: //選項字段結束標誌 DHCP_END 0xff
- /*
- * 當選項過載的時候(curr == OPTION_FILE允許選項過載)
- * 首先用file字段,不夠的話再用sname字段
- * 使用file字段的時候:
- * over的右起的第0位必須爲1
- * 使用sname字段:
- * over的右起的第一位必須爲1
- */
- if (curr == OPTION_FIELD && over & FILE_FIELD) {
- optionptr = packet->file;
- i = 0;
- length = 128;
- curr = FILE_FIELD;
- } else if (curr == FILE_FIELD && over & SNAME_FIELD) {
- optionptr = packet->sname;
- i = 0;
- length = 64;
- curr = SNAME_FIELD;
- //沒有或不允許選項過載或over(options[i + 3])標誌不允許,結束查找返回NULL
- } else done = 1;
- break;
- /*
- * 不是填充信息:DHCP_PADDING
- * 選項過載:DHCP_OPTION_OVER
- * 選項結束:DHCP_END
- *
- * 表明是屬於選項信息,所以可以直接改變偏移量:
- * i += option[OPT_LEN + i] + 2;
- */
- default:
- i += optionptr[OPT_LEN + i] + 2;
- }
- }
- return NULL;
- }
在源碼busybox 1.2的udhcp源碼中,對於從options字段取出選項信息,在對選項過載的處理是存在錯誤的,
[cpp] view plain copy
- case DHCP_OPTION_OVER: //選項過載DHCP_OPTION_OVER
- if (i + 1 + optionptr[i + OPT_LEN] >= length) {
- LOG(LOG_WARNING, "bogus packet, option fields too long.");
- return NULL;
- }
- /*
- * Code Len Value
- * +-----+-----+-----+
- * | 52 | 1 |1/2/3|
- * +-----+-----+-----+
- */
- over = optionptr[i + OPT_DATA];
- i += optionptr[i + OPT_LEN] + 2;
- // over = optionptr[i + 3]; /* 未改動源碼 */
- // i += optionptr[OPT_LEN] + 2; /* 未改動源碼 */
- break;
2、向options字段寫入選項信息
a). 寫入是添加在options字段中最後的選項後面,即DHCP_END標誌之前
查找DHCP_END標誌字段:
[cpp] view plain copy
- /* return the position of the 'end' option (no bounds checking) */
- int end_option(uint8_t *optionptr)
- {
- int i = 0;
- /* 在選項字段裏找到DHCP_END的偏移 */
- /* 在選項字段裏面裏三類信息:
- 1.DHCP_PADDING 填充字節
- 2.DHCP_END 選項結束字節
- 3.選項信息<CLV> code + length + value
- */
- while (optionptr[i] != DHCP_END) {
- if (optionptr[i] == DHCP_PADDING) i++; //填充字節DHCP_PADDING
- else i += optionptr[i + OPT_LEN] + 2; //選項信息
- }
- return i;
- }
b). 選項信息已經在一個字符串裏以CLV方式組織好,直接copy到DHCP_END標誌位置,DHCP_END向後移動:
[cpp] view plain copy
- /* add an option string to the options (an option string contains an option code,
- * length, then data) */
- int add_option_string(uint8_t *optionptr, uint8_t *string)
- {
- int end = end_option(optionptr);//找到DHCP_END在選項字段裏偏移
- /* end position + string length + option code/length + end option */
- //檢查需要添加的選項信息後的長度是否大於選項字段的最大長度
- if (end + string[OPT_LEN] + 2 + 1 >= 308) {
- LOG(LOG_ERR,"Option 0x%02x did not fit into packet!",string[OPT_CODE]);
- return 0;
- }
- DEBUG(LOG_INFO, "adding option 0x%02x", string[OPT_CODE]);
- memcpy(optionptr + end, string, string[OPT_LEN] + 2);
- optionptr[end + string[OPT_LEN] + 2] = DHCP_END;//在<CLV>的最後添加上DHCP_END
- return string[OPT_LEN] + 2; //返回<CLV>長度
- }
c). 把選項信息按CLV的方式組織好存放到一個字符串裏,最後調用add_option_string把在字符串內組織好的選項信息添加進options字段:
[cpp] view plain copy
- /* add a one to four byte option to a packet */
- /* add_simple_option函數只能想選項字段添加OPT_LEN = 4 bytes的選項 */
- /* optionptr: 報文選項字段的首地址
- code: 選項code
- data: 選項value
- 返回值是 <CLV>的長度
- 返回0 表示添加失敗
- */
- int add_simple_option(uint8_t *optionptr, uint8_t code, uint32_t data)
- {
- struct dhcp_option *dh;
- /* 檢查需要添加的選項是否符合標準的選項 */
- /* 在dhcp_options數組裏查找 */
- for (dh=dhcp_options; dh->code; dh++) {
- if (dh->code == code) { //Found
- uint8_t option[6], len;
- option[OPT_CODE] = code; //添加code
- len = option_lengths[dh->flags & TYPE_MASK];//計算length
- option[OPT_LEN] = len; //添加length
- /*
- * 假設data長度是一個字節,但在大端字節序的機器裏
- * 但存放在uint32_t裏的放在最高地址的地方,
- * 所以data << 8 * (4 - len) 把她移到低位
- */
- if (BB_BIG_ENDIAN) data <<= 8 * (4 - len);
- /* This memcpy is for broken processors which can't
- * handle a simple unaligned 32-bit assignment */
- memcpy(&option[OPT_DATA], &data, 4);
- return add_option_string(optionptr, option);
- }
- }
- DEBUG(LOG_ERR, "Could not add option 0x%02x", code);
- return 0;
- }