zoukankan      html  css  js  c++  java
  • 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:  }
  • 相关阅读:
    Intent.ACTION_TIME_TICK 广播
    Android ContentObserver
    android:duplicateParentState属性解释
    Android CursorAdapter
    android AndroidManifest.xml 多个android.intent.action.MAIN (
    PreferenceActivity详解
    WORD和WPS中英文混合的内容间距离很大怎么办?
    Android 屏幕适配
    OC第四课
    PL/SQL联系oracle成功可以sql解决的办法是检查表的名称无法显示
  • 原文地址:https://www.cnblogs.com/woainilsr/p/3181051.html
Copyright © 2011-2022 走看看