中间有很长一段时间没有更新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. 1: /*
  1. 2: * 参数struct dhcpMessage *packet DHCP报文
  1. 3: * int code需要获得什么选项信息(选项信息的标识)
  1. 4: *
  1. 5: * 返回指向选项信息的指针(去除了 OPT_CODE,OPT_LEN
  1. 6: * 未找到返回NULL
  1. 7: */
  1. 8: uint8_t *get_option(struct dhcpMessage *packet, int code)
  1. 9: {
  1. 10: int i, length;
  1. 11: uint8_t *optionptr;
  1. 12: int over = 0, done = 0, curr = OPTION_FIELD;
  1. 13:  
  1. 14: optionptr = packet->options;
  1. 15: i = 0;
  1. 16: length = 308; /* 整个options字段的长度308 */
  1. 17:  
  1. 18: /* 在options字段里查找code选项标识信息*/
  1. 19: while (!done) {
  1. 20: if (i >= length) { /* 查找完所有字段都未找到code标识的信息,返回NULL */
  1. 21: LOG(LOG_WARNING, "bogus packet, option fields too long.");
  1. 22: return NULL;
  1. 23: }
  1. 24:  
  1. 25: //CLV方式存储数据
  1. 26: //这里与struct option_set的data存储相似
  1. 27: //OPT_CODE字节上存储code标识
  1. 28: //OPT_LEN 字节上存储信息长度
  1. 29: //OPT_LEN后就是存储信息
  1. 30:  
  1. 31: if (optionptr[i + OPT_CODE] == code) { //Found
  1. 32: if (i + 1 + optionptr[i + OPT_LEN] >= length) { //检查选项信息长度
  1. 33: LOG(LOG_WARNING, "bogus packet, option fields too long.");
  1. 34: return NULL;
  1. 35: }
  1. 36: return optionptr + i + 2; //Found,返回选项信息的首地址
  1. 37: }
  1. 38:
  1. 39: switch (optionptr[i + OPT_CODE]) {
  1. 40: case DHCP_PADDING: //DHCP_PADING(填充)字节,直接 i++;
  1. 41: i++;
  1. 42: break;
  1. 43: case DHCP_OPTION_OVER: //选项过载DHCP_OPTION_OVER
  1. 44: if (i + 1 + optionptr[i + OPT_LEN] >= length) {
  1. 45: LOG(LOG_WARNING, "bogus packet, option fields too long.");
  1. 46: return NULL;
  1. 47: }
  1. 48:  
  1. 49: /*
  1. 50: optionptr[i + OPT_CODE] == DHCP_OPTION_OVER选项过载;
  1. 51: optionptr[i + 3]存放了采用哪个字段来存储过载的选项
  1. 52: 可能存储过载选项的字段:
  1. 53: uint8_t sname[64]; //server host name (ASCIZ)
  1. 54: uint8_t file[128]; // boot file name (ASCIZ)
  1. 55:
  1. 56: over = optionptr[i + 3];记录下使用那个字段存储过载选项
  1. 57: */
  1. 58:  
  1. 59:  
  1. 60: /*
  1. 61: *
  1. 62: The code for this option is 52, and its length is 1. Legal values
  1. 63: for this option are:
  1. 64:
  1. 65: Value Meaning
  1. 66: ----- --------
  1. 67: 1 the 'file' field is used to hold options
  1. 68: 2 the 'sname' field is used to hold options
  1. 69: 3 both fields are used to hold options
  1. 70:
  1. 71: Code Len Value
  1. 72: +-----+-----+-----+
  1. 73: | 52 | 1 |1/2/3|
  1. 74: +-----+-----+-----+
  1. 75: */
  1. 76:  
  1. 77: over = optionptr[i + OPT_DATA];
  1. 78: i += optionptr[i + OPT_LEN] + 2;
  1. 79:
  1. 80: // over = optionptr[i + 3]; /* Error */
  1. 81: // i += optionptr[OPT_LEN] + 2; /* Error */
  1. 82: break;
  1. 83: case DHCP_END: //选项字段结束标志 DHCP_END 0xff
  1. 84:  
  1. 85: /*
  1. 86: * 当选项过载的时候(curr == OPTION_FILE允许选项过载)
  1. 87: * 首先用file字段,不够的话再用sname字段
  1. 88: * 使用file字段的时候:
  1. 89: * over的右起的第0位必须为1
  1. 90: * 使用sname字段:
  1. 91: * over的右起的第一位必须为1
  1. 92: */
  1. 93: if (curr == OPTION_FIELD && over & FILE_FIELD) {
  1. 94: optionptr = packet->file;
  1. 95: i = 0;
  1. 96: length = 128;
  1. 97: curr = FILE_FIELD;
  1. 98: } else if (curr == FILE_FIELD && over & SNAME_FIELD) {
  1. 99: optionptr = packet->sname;
  1. 100: i = 0;
  1. 101: length = 64;
  1. 102: curr = SNAME_FIELD;
  1. 103:  
  1. 104: //没有或不允许选项过载或over(options[i + 3])标志不允许,结束查找返回NULL
  1. 105: } else done = 1;
  1. 106: break;
  1. 107:  
  1. 108:  
  1. 109: /*
  1. 110: * 不是填充信息:DHCP_PADDING
  1. 111: * 选项过载:DHCP_OPTION_OVER
  1. 112: * 选项结束:DHCP_END
  1. 113: *
  1. 114: * 表明是属于选项信息,所以可以直接改变偏移量:
  1. 115: * i += option[OPT_LEN + i] + 2;
  1. 116: */
  1. 117: default:
  1. 118: i += optionptr[OPT_LEN + i] + 2;
  1. 119: }
  1. 120: }
  1. 121: return NULL;
  1. 122: }

在源码busybox 1.2的udhcp源码中,对于从options字段取出选项信息,在对选项过载的处理是存在错误的,

  1. 1: case DHCP_OPTION_OVER: //选项过载DHCP_OPTION_OVER
  1. 2: if (i + 1 + optionptr[i + OPT_LEN] >= length) {
  1. 3: LOG(LOG_WARNING, "bogus packet, option fields too long.");
  1. 4: return NULL;
  1. 5: }
  1. 6: /*
  1. 7: * Code Len Value
  1. 8: * +-----+-----+-----+
  1. 9: * | 52 | 1 |1/2/3|
  1. 10: * +-----+-----+-----+
  1. 11: */
  1. 12: over = optionptr[i + OPT_DATA];
  1. 13: i += optionptr[i + OPT_LEN] + 2;
  1. 14:
  1. 15: // over = optionptr[i + 3]; /* 未改动源码 */
  1. 16: // i += optionptr[OPT_LEN] + 2; /* 未改动源码 */
  1. 17: break;

2、向options字段写入选项信息

a).  写入是添加在options字段中最后的选项后面,即DHCP_END标志之前

查找DHCP_END标志字段:

  1. 1: /* return the position of the 'end' option (no bounds checking) */
  1. 2: int end_option(uint8_t *optionptr)
  1. 3: {
  1. 4: int i = 0;
  1. 5:  
  1. 6: /* 在选项字段里找到DHCP_END的偏移 */
  1. 7: /* 在选项字段里面里三类信息:
  1. 8: 1.DHCP_PADDING 填充字节
  1. 9: 2.DHCP_END 选项结束字节
  1. 10: 3.选项信息<CLV> code + length + value
  1. 11: */
  1. 12: while (optionptr[i] != DHCP_END) {
  1. 13: if (optionptr[i] == DHCP_PADDING) i++; //填充字节DHCP_PADDING
  1. 14: else i += optionptr[i + OPT_LEN] + 2; //选项信息
  1. 15: }
  1. 16: return i;
  1. 17: }

b).   选项信息已经在一个字符串里以CLV方式组织好,直接copy到DHCP_END标志位置,DHCP_END向后移动:

  1. 1: /* add an option string to the options (an option string contains an option code,
  1. 2: * length, then data) */
  1. 3: int add_option_string(uint8_t *optionptr, uint8_t *string)
  1. 4: {
  1. 5: int end = end_option(optionptr);//找到DHCP_END在选项字段里偏移
  1. 6:  
  1. 7: /* end position + string length + option code/length + end option */
  1. 8: //检查需要添加的选项信息后的长度是否大于选项字段的最大长度
  1. 9: if (end + string[OPT_LEN] + 2 + 1 >= 308) {
  1. 10: LOG(LOG_ERR,"Option 0x%02x did not fit into packet!",string[OPT_CODE]);
  1. 11: return 0;
  1. 12: }
  1. 13: DEBUG(LOG_INFO, "adding option 0x%02x", string[OPT_CODE]);
  1. 14: memcpy(optionptr + end, string, string[OPT_LEN] + 2);
  1. 15: optionptr[end + string[OPT_LEN] + 2] = DHCP_END;//在<CLV>的最后添加上DHCP_END
  1. 16: return string[OPT_LEN] + 2; //返回<CLV>长度
  1. 17: }

c).  把选项信息按CLV的方式组织好存放到一个字符串里,最后调用add_option_string把在字符串内组织好的选项信息添加进options字段:

  1. 1: /* add a one to four byte option to a packet */
  1. 2: /* add_simple_option函数只能想选项字段添加OPT_LEN = 4 bytes的选项 */
  1. 3: /* optionptr: 报文选项字段的首地址
  1. 4: code: 选项code
  1. 5: data: 选项value
  1. 6:
  1. 7: 返回值是 <CLV>的长度
  1. 8: 返回0 表示添加失败
  1. 9: */
  1. 10: int add_simple_option(uint8_t *optionptr, uint8_t code, uint32_t data)
  1. 11: {
  1. 12: struct dhcp_option *dh;
  1. 13:  
  1. 14: /* 检查需要添加的选项是否符合标准的选项 */
  1. 15: /* 在dhcp_options数组里查找 */
  1. 16: for (dh=dhcp_options; dh->code; dh++) {
  1. 17: if (dh->code == code) { //Found
  1. 18: uint8_t option[6], len;
  1. 19:  
  1. 20: option[OPT_CODE] = code; //添加code
  1. 21: len = option_lengths[dh->flags & TYPE_MASK];//计算length
  1. 22: option[OPT_LEN] = len; //添加length
  1. 23:  
  1. 24: /*
  1. 25: * 假设data长度是一个字节,但在大端字节序的机器里
  1. 26: * 但存放在uint32_t里的放在最高地址的地方,
  1. 27: * 所以data << 8 * (4 - len) 把她移到低位
  1. 28: */
  1. 29: if (BB_BIG_ENDIAN) data <<= 8 * (4 - len);
  1. 30:  
  1. 31: /* This memcpy is for broken processors which can't
  1. 32: * handle a simple unaligned 32-bit assignment */
  1. 33: memcpy(&option[OPT_DATA], &data, 4);
  1. 34: return add_option_string(optionptr, option);
  1. 35: }
  1. 36: }
  1. 37:  
  1. 38: DEBUG(LOG_ERR, "Could not add option 0x%02x", code);
  1. 39: return 0;
  1. 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. 【2018 CCPC网络赛】1003 - 费马小定理

    题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=6440 这题主要是理解题意: 题意:定义一个加法和乘法,使得 (m+n)p = mp+np; 其中给定 ...

  2. 三、C++ const分析

    1.C语言中的const: const修饰的变量是只读的,本质还是变量 const修饰的局部变量在栈上分配空间 const修饰的全局变量在只读存储区分配空间 const只在编译期有用,在运行期无效 c ...

  3. C++ 给自己的扫盲笔记

    1.运算符new分配内存的格式: 指针变量名 = new 类型: 如分配一个20字节的name变量    :name = new char[20]; 2.strlen(s);函数: 返回字符串s的长度 ...

  4. 关于nagios系统下使用shell脚本自定义监控插件的编写以及没有实时监控图的问题

    关于nagios系统下shell自定义监控插件的编写.脚本规范以及没有实时监控图的问题的解决办法 在自已编写监控插件之前我们首先需要对nagios监控原理有一定的了解 Nagios的功能是监控服务和主 ...

  5. 如何用纯 CSS 创作一种侧立图书的特效

    效果预览 在线演示 按下右侧的"点击预览"按钮在当前页面预览,点击链接全屏预览. https://codepen.io/zhang-ou/pen/deVgRM 可交互视频教程 此视 ...

  6. JQuery给元素动态增删类或特性

    背景:通过JQuery动态给Html元素增加.删除类或属性,使Html元素在不同的时刻呈现不同的样式,给用户更好的体验感觉. 如存在以下p片段和button按钮,代码如下: <p id=&quo ...

  7. [数据结构]C#基于数组实现泛型顺序表

    前方预警,只完成了顺序表的插入/删除/查找. 错误代码示例: /// <summary> /// 查找顺序表第i个位置的元素 /// 在显示情况中,我们更常用下标 /// </sum ...

  8. 大数据学习——flume安装部署

    1.Flume的安装非常简单,只需要解压即可,当然,前提是已有hadoop环境 上传安装包到数据源所在节点上 然后解压  tar -zxvf apache-flume-1.6.0-bin.tar.gz ...

  9. mybatis自动映射和手动映射

    一对一查询 第一种方法: <!-- 查询所有订单信息 --> <select id="findOrdersList" resultType="cn.it ...

  10. Codeforces Round #321 (Div. 2)-A. Kefa and First Steps,暴力水过~~

    A. Kefa and First Steps time limit per test 2 seconds memory limit per test 256 megabytes input stan ...