中间有很长一段时间没有更新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字段取出选项信息
1: /*
2: * 参数struct dhcpMessage *packet DHCP报文
3: * int code需要获得什么选项信息(选项信息的标识)
4: *
5: * 返回指向选项信息的指针(去除了 OPT_CODE,OPT_LEN)
6: * 未找到返回NULL
7: */
8: uint8_t *get_option(struct dhcpMessage *packet, int code)
9: {
10: int i, length;
11: uint8_t *optionptr;
12: int over = 0, done = 0, curr = OPTION_FIELD;
13:
14: optionptr = packet->options;
15: i = 0;
16: length = 308; /* 整个options字段的长度308 */
17:
18: /* 在options字段里查找code选项标识信息*/
19: while (!done) {
20: if (i >= length) { /* 查找完所有字段都未找到code标识的信息,返回NULL */
21: LOG(LOG_WARNING, "bogus packet, option fields too long.");
22: return NULL;
23: }
24:
25: //CLV方式存储数据
26: //这里与struct option_set的data存储相似
27: //OPT_CODE字节上存储code标识
28: //OPT_LEN 字节上存储信息长度
29: //OPT_LEN后就是存储信息
30:
31: if (optionptr[i + OPT_CODE] == code) { //Found
32: if (i + 1 + optionptr[i + OPT_LEN] >= length) { //检查选项信息长度
33: LOG(LOG_WARNING, "bogus packet, option fields too long.");
34: return NULL;
35: }
36: return optionptr + i + 2; //Found,返回选项信息的首地址
37: }
38:
39: switch (optionptr[i + OPT_CODE]) {
40: case DHCP_PADDING: //DHCP_PADING(填充)字节,直接 i++;
41: i++;
42: break;
43: case DHCP_OPTION_OVER: //选项过载DHCP_OPTION_OVER
44: if (i + 1 + optionptr[i + OPT_LEN] >= length) {
45: LOG(LOG_WARNING, "bogus packet, option fields too long.");
46: return NULL;
47: }
48:
49: /*
50: optionptr[i + OPT_CODE] == DHCP_OPTION_OVER选项过载;
51: optionptr[i + 3]存放了采用哪个字段来存储过载的选项
52: 可能存储过载选项的字段:
53: uint8_t sname[64]; //server host name (ASCIZ)
54: uint8_t file[128]; // boot file name (ASCIZ)
55:
56: over = optionptr[i + 3];记录下使用那个字段存储过载选项
57: */
58:
59:
60: /*
61: *
62: The code for this option is 52, and its length is 1. Legal values
63: for this option are:
64:
65: Value Meaning
66: ----- --------
67: 1 the 'file' field is used to hold options
68: 2 the 'sname' field is used to hold options
69: 3 both fields are used to hold options
70:
71: Code Len Value
72: +-----+-----+-----+
73: | 52 | 1 |1/2/3|
74: +-----+-----+-----+
75: */
76:
77: over = optionptr[i + OPT_DATA];
78: i += optionptr[i + OPT_LEN] + 2;
79:
80: // over = optionptr[i + 3]; /* Error */
81: // i += optionptr[OPT_LEN] + 2; /* Error */
82: break;
83: case DHCP_END: //选项字段结束标志 DHCP_END 0xff
84:
85: /*
86: * 当选项过载的时候(curr == OPTION_FILE允许选项过载)
87: * 首先用file字段,不够的话再用sname字段
88: * 使用file字段的时候:
89: * over的右起的第0位必须为1
90: * 使用sname字段:
91: * over的右起的第一位必须为1
92: */
93: if (curr == OPTION_FIELD && over & FILE_FIELD) {
94: optionptr = packet->file;
95: i = 0;
96: length = 128;
97: curr = FILE_FIELD;
98: } else if (curr == FILE_FIELD && over & SNAME_FIELD) {
99: optionptr = packet->sname;
100: i = 0;
101: length = 64;
102: curr = SNAME_FIELD;
103:
104: //没有或不允许选项过载或over(options[i + 3])标志不允许,结束查找返回NULL
105: } else done = 1;
106: break;
107:
108:
109: /*
110: * 不是填充信息:DHCP_PADDING
111: * 选项过载:DHCP_OPTION_OVER
112: * 选项结束:DHCP_END
113: *
114: * 表明是属于选项信息,所以可以直接改变偏移量:
115: * i += option[OPT_LEN + i] + 2;
116: */
117: default:
118: i += optionptr[OPT_LEN + i] + 2;
119: }
120: }
121: return NULL;
122: }
在源码busybox 1.2的udhcp源码中,对于从options字段取出选项信息,在对选项过载的处理是存在错误的,
1: case DHCP_OPTION_OVER: //选项过载DHCP_OPTION_OVER
2: if (i + 1 + optionptr[i + OPT_LEN] >= length) {
3: LOG(LOG_WARNING, "bogus packet, option fields too long.");
4: return NULL;
5: }
6: /*
7: * Code Len Value
8: * +-----+-----+-----+
9: * | 52 | 1 |1/2/3|
10: * +-----+-----+-----+
11: */
12: over = optionptr[i + OPT_DATA];
13: i += optionptr[i + OPT_LEN] + 2;
14:
15: // over = optionptr[i + 3]; /* 未改动源码 */
16: // i += optionptr[OPT_LEN] + 2; /* 未改动源码 */
17: break;
2、向options字段写入选项信息
a). 写入是添加在options字段中最后的选项后面,即DHCP_END标志之前
查找DHCP_END标志字段:
1: /* return the position of the 'end' option (no bounds checking) */
2: int end_option(uint8_t *optionptr)
3: {
4: int i = 0;
5:
6: /* 在选项字段里找到DHCP_END的偏移 */
7: /* 在选项字段里面里三类信息:
8: 1.DHCP_PADDING 填充字节
9: 2.DHCP_END 选项结束字节
10: 3.选项信息<CLV> code + length + value
11: */
12: while (optionptr[i] != DHCP_END) {
13: if (optionptr[i] == DHCP_PADDING) i++; //填充字节DHCP_PADDING
14: else i += optionptr[i + OPT_LEN] + 2; //选项信息
15: }
16: return i;
17: }
b). 选项信息已经在一个字符串里以CLV方式组织好,直接copy到DHCP_END标志位置,DHCP_END向后移动:
1: /* add an option string to the options (an option string contains an option code,
2: * length, then data) */
3: int add_option_string(uint8_t *optionptr, uint8_t *string)
4: {
5: int end = end_option(optionptr);//找到DHCP_END在选项字段里偏移
6:
7: /* end position + string length + option code/length + end option */
8: //检查需要添加的选项信息后的长度是否大于选项字段的最大长度
9: if (end + string[OPT_LEN] + 2 + 1 >= 308) {
10: LOG(LOG_ERR,"Option 0x%02x did not fit into packet!",string[OPT_CODE]);
11: return 0;
12: }
13: DEBUG(LOG_INFO, "adding option 0x%02x", string[OPT_CODE]);
14: memcpy(optionptr + end, string, string[OPT_LEN] + 2);
15: optionptr[end + string[OPT_LEN] + 2] = DHCP_END;//在<CLV>的最后添加上DHCP_END
16: return string[OPT_LEN] + 2; //返回<CLV>长度
17: }
c). 把选项信息按CLV的方式组织好存放到一个字符串里,最后调用add_option_string把在字符串内组织好的选项信息添加进options字段:
1: /* add a one to four byte option to a packet */
2: /* add_simple_option函数只能想选项字段添加OPT_LEN = 4 bytes的选项 */
3: /* optionptr: 报文选项字段的首地址
4: code: 选项code
5: data: 选项value
6:
7: 返回值是 <CLV>的长度
8: 返回0 表示添加失败
9: */
10: int add_simple_option(uint8_t *optionptr, uint8_t code, uint32_t data)
11: {
12: struct dhcp_option *dh;
13:
14: /* 检查需要添加的选项是否符合标准的选项 */
15: /* 在dhcp_options数组里查找 */
16: for (dh=dhcp_options; dh->code; dh++) {
17: if (dh->code == code) { //Found
18: uint8_t option[6], len;
19:
20: option[OPT_CODE] = code; //添加code
21: len = option_lengths[dh->flags & TYPE_MASK];//计算length
22: option[OPT_LEN] = len; //添加length
23:
24: /*
25: * 假设data长度是一个字节,但在大端字节序的机器里
26: * 但存放在uint32_t里的放在最高地址的地方,
27: * 所以data << 8 * (4 - len) 把她移到低位
28: */
29: if (BB_BIG_ENDIAN) data <<= 8 * (4 - len);
30:
31: /* This memcpy is for broken processors which can't
32: * handle a simple unaligned 32-bit assignment */
33: memcpy(&option[OPT_DATA], &data, 4);
34: return add_option_string(optionptr, option);
35: }
36: }
37:
38: DEBUG(LOG_ERR, "Could not add option 0x%02x", code);
39: return 0;
40: }