udhcp源码详解(五) 之DHCP包--options字段
中間有很長一段時間沒有更新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: }轉載于:https://www.cnblogs.com/woainilsr/p/3181051.html
總結
以上是生活随笔為你收集整理的udhcp源码详解(五) 之DHCP包--options字段的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一台机器上运行多个ActiveMq
- 下一篇: 一个需要原创精神的年代