udhcp源碼詳解(五) 之DHCP包--options字段

   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

  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字段取出選項信息,在對選項過載的處理是存在錯誤的,

 

 

[cpp] view plain copy

  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標誌字段:

 

[cpp] view plain copy

  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向後移動:

 

[cpp] view plain copy

  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字段:

 

[cpp] view plain copy

  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. }  

save_snippets.pnguploading.4e448015.gif轉存失敗重新上傳取消save_snippets.pnguploading.4e448015.gif轉存失敗重新上傳取消

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章