中间有很长一段时间没有更新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:  }

udhcp源码详解(五) 之DHCP包--options字段的更多相关文章

  1. udhcp源码详解(四) 之租赁IP的管理

    Server端对于租赁出去的IP的管理是基于结构体dhcpOfferedAddr的,该结构体的定义是在leases.c文件里:(结构体的成员介绍说明见详解之数据结构) 1: struct dhcpOf ...

  2. OkHttp3源码详解(五) okhttp连接池复用机制

    1.概述 提高网络性能优化,很重要的一点就是降低延迟和提升响应速度. 通常我们在浏览器中发起请求的时候header部分往往是这样的 keep-alive 就是浏览器和服务端之间保持长连接,这个连接是可 ...

  3. udhcp源码详解(三)上 之配置信息的读取

    上节介绍了存储管理配置信息的结构体struct server_config_t,该结构体贯穿整个server端程序的运行. 在dhcpd.c里的用该结构体定义个一个全局的变量: struct serv ...

  4. udhcp源码详解(二)--转

    定义的数据结构对于C程序的重要性,不言而喻.面向对象设计的程序是一个个对象的集合,而面向过程语言设计的程序则是数据结构与算法的集合. 下面来分析的是dhcp server中的定义结构体: 1).在pa ...

  5. udhcp源码详解(一)之文件组织结构(dhcp server) --转

    udhcp目录下有十几个源文件,一个源文件相对应一个模块,完成一系列相关的功能,例如在static_leases.c主要针对static_lease链表增删查找等操作. dhcpd.c——   整个d ...

  6. udhcp源码详解(三) 下 之配置信息的读取

    上节讲解了read_config函数,读取配置信息到server_config的相应成员变量里,但read_config函数只负责把配置信息重文件里读出来,具体怎么把信息填写到指定的地址内,是调用ke ...

  7. spring事务详解(三)源码详解

    系列目录 spring事务详解(一)初探事务 spring事务详解(二)简单样例 spring事务详解(三)源码详解 spring事务详解(四)测试验证 spring事务详解(五)总结提高 一.引子 ...

  8. [转]Linux内核源码详解--iostat

    Linux内核源码详解——命令篇之iostat 转自:http://www.cnblogs.com/york-hust/p/4846497.html 本文主要分析了Linux的iostat命令的源码, ...

  9. Shiro 登录认证源码详解

    Shiro 登录认证源码详解 Apache Shiro 是一个强大且灵活的 Java 开源安全框架,拥有登录认证.授权管理.企业级会话管理和加密等功能,相比 Spring Security 来说要更加 ...

随机推荐

  1. 倍增实现LCA

    Today,we will talk about how to find The Least Common Ancestor. Now ,let us get into the business(正题 ...

  2. mysql5.7zip安装

    一.下载mysql zip文件 二.解压.(我的目录A:\mysql\mysql-5.7.23-winx64) 三.配置环境变量   Path后面追加%A:\mysql\mysql-5.7.23-wi ...

  3. python3.x Day6 IO多路复用

    IO多路复用import asyncio 这个是异步IO模块 这个还不知道怎么用 select poll epoll 都是IO多路复用 windows 仅支持select linux2.6以后 支持e ...

  4. h5 页面 禁止网页缩放

    //禁用双指缩放: document.documentElement.addEventListener('touchstart', function (event) { if (event.touch ...

  5. Django框架基础知识09-请求与响应

    视图函数接受到的request到底是个什么对象呢? HttpRequest对象: 服务器接收到http协议的请求后,会根据报文创建HttpRequest对象视图函数的第一个参数是HttpRequest ...

  6. 初识Pyhon

    如果你的系统安装了这两个版本,请使用Python 3 如果没有安装Python,请安装Python 3 主要介绍在windows 64位操作系统上安装Python 3 1. 安装Python 首先,检 ...

  7. [第一波模拟\day3\T2]{独立集}(bubble.cpp)

    [问题描述] 有一天,一个名叫顺旺基的程序员从石头里诞生了.又有一天,他学会了冒泡排序和独立集.在一个图里,独立集就是一个点集,满足任意两个点之间没有边.于是他就想把这两个东西结合在一起.众所周知,独 ...

  8. Dev Express中Dock panel的使用

    使用DockManager,添加DockPanel. 1,DockManager位于“导航和布局”分类中. 添加一个DockManager控件到窗体中以后,即是在当前窗体类中,添加一个DockMana ...

  9. Leetcode 260.只出现一次的数字III

    只出现一次的数字III 给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次. 找出只出现一次的那两个元素. 示例 : 输入: [1,2,1,3,2,5] 输出: [3,5 ...

  10. php5.5编译安装

    系统环境:centos6.5PHP包:5.5.15https://wiki.swoole.com/wiki/page/177.html下载 PHP 源码包wget http://cn2.php.net ...