http://download.csdn.net/detail/new_abc/4520361这里有一个介绍dhcp的文档,比较详细
看一下dhcp的过程:
Android DHCP客户端的代码在system\core\libnetutils目录下
- int do_dhcp(char *iname)
- {
- if (ifc_set_addr(iname, 0)) {
- printerr("failed to set ip addr for %s to 0.0.0.0: %s\n", iname, strerror(errno));
- return -1;
- }
- if (ifc_up(iname)) {
- printerr("failed to bring up interface %s: %s\n", iname, strerror(errno));
- return -1;
- }
- sleep(2);
- return dhcp_init_ifc(iname);
- }
这里的iname是网口名,如“eth0",ifc_set_addr设置一个全局socket的地址为传下来的网口名字,ifc_up设置网口标志为启动状态。然后主要的工作在dhcp_init_ifc:
- int dhcp_init_ifc(const char *ifname)
- {
- dhcp_msg discover_msg;
- dhcp_msg request_msg;
- dhcp_msg reply;
- dhcp_msg *msg;
- dhcp_info info;
- int s, r, size;
- int valid_reply;
- uint32_t xid,last,now;
- unsigned char hwaddr[6];
- struct pollfd pfd;
- unsigned int state;
- unsigned int timeout;
- int if_index;
- xid = (uint32_t) get_msecs();
- last = (uint32_t) get_msecs();
- if (ifc_get_hwaddr(ifname, hwaddr)) { //获取mac地址
- return fatal("cannot obtain interface address");
- }
- if (ifc_get_ifindex(ifname, &if_index)) {
- return fatal("cannot obtain interface index");
- }
- s = open_raw_socket(ifname, hwaddr, if_index);
- timeout = TIMEOUT_INITIAL;
- state = STATE_SELECTING;
- info.type = 0;
- goto transmit;
- for (;;) {
- pfd.fd = s;
- pfd.events = POLLIN;
- pfd.revents = 0;
- now = (uint32_t) get_msecs();
- if (now > (last + TIMEOUT_INITIAL))
- goto transmit;
- // LOGD("lijj timeout = %d", timeout);
- r = poll(&pfd, 1, timeout);
- if (r == 0) {
- #if VERBOSE
- printerr("TIMEOUT\n");
- #endif
- if (timeout >= TIMEOUT_MAX) {
- printerr("timed out\n");
- if ( info.type == DHCPOFFER ) {
- printerr("no acknowledgement from DHCP server\nconfiguring %s with offered parameters\n", ifname);
- return ifc_configure(ifname, &info);
- }
- errno = ETIME;
- close(s);
- return -1;
- }
- timeout = timeout * 2;
- transmit:
- size = 0;
- msg = NULL;
- switch(state) {
- case STATE_SELECTING:
- msg = &discover_msg;
- size = init_dhcp_discover_msg(msg, hwaddr, xid);//构建dhcp DISCOVER消息
- break;
- case STATE_REQUESTING:
- msg = &request_msg;
- size = init_dhcp_request_msg(msg, hwaddr, xid, info.ipaddr, info.serveraddr);//构建请求消息
- break;
- default:
- r = 0;
- }
- if (size != 0) {
- r = send_message(s, if_index, msg, size);//发送消息
- if (r < 0) {
- printerr("error sending dhcp msg: %s\n", strerror(errno));
- }
- last = (uint32_t) get_msecs();
- }
- continue;
- }
- if (r < 0) {
- if ((errno == EAGAIN) || (errno == EINTR)) {
- continue;
- }
- return fatal("poll failed");
- }
- errno = 0;
- r = receive_packet(s, &reply);
- if (r < 0) {
- if (errno != 0) {
- LOGD("receive_packet failed (%d): %s", r, strerror(errno));
- if (errno == ENETDOWN || errno == ENXIO) {
- return -1;
- }
- }
- continue;
- }
- #if VERBOSE > 1
- dump_dhcp_msg(&reply, r);
- #endif
- decode_dhcp_msg(&reply, r, &info);//解析返回的dhcp消息
- if (state == STATE_SELECTING) {
- valid_reply = is_valid_reply(&discover_msg, &reply, r);
- } else {
- valid_reply = is_valid_reply(&request_msg, &reply, r);
- }
- if (!valid_reply) {
- printerr("invalid reply\n");
- continue;
- }
- if (verbose) dump_dhcp_info(&info);
- switch(state) {
- case STATE_SELECTING:
- if (info.type == DHCPOFFER) {
- state = STATE_REQUESTING;
- timeout = TIMEOUT_INITIAL;
- xid++;
- goto transmit;
- }
- break;
- case STATE_REQUESTING:
- if (info.type == DHCPACK) {
- printerr("configuring %s\n", ifname);
- close(s);
- return ifc_configure(ifname, &info);
- } else if (info.type == DHCPNAK) {//不能向客户提供这个网络地址或参数
- printerr("configuration request denied\n");
- close(s);
- return -1;
- } else {
- printerr("ignoring %s message in state %d\n",
- dhcp_type_to_name(info.type), state);
- }
- break;
- }
- }
- close(s);
- return 0;
- }
这里大概过程是:
1、进行一些初始化后转到goto transmit;
2、此时state为STATE_SELECTING,所以初始化discover消息,接着调用send_message发送discover消息,然后回到for循环,阻塞在下一次poll,直到超时,如果中间一直未收到回应,则不停的发discover消息,收到回应消息则r>0;
3、调用receive_packet接收消息
4、decode_dhcp_msg解析消息,相应的信息保存在dhcp_info结构中
5、判断回应的消息是否为DHCPOFFER,是则将状态改为STATE_REQUESTING,跳转到transmit
6、发送REQUESTING消息,然后阻塞,等待回应
7、接收消息,decode_dhcp_msg解析消息,相应的信息保存在dhcp_info结构中
8、判断回应的消息是否为DHCPNAK,是则关闭套接字,调用ifc_configure设置相应地址信息,如果消息为DHCPNAK,则说明服务器不能向客户提供这个网络地址或参数,返回 error
基本的消息处理流程就是这样,再来看一下构建discover消息
- int init_dhcp_discover_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid)
- {
- uint8_t *x;
- x = init_dhcp_msg(msg, DHCPDISCOVER, hwaddr, xid);
- *x++ = OPT_PARAMETER_LIST;//由n个option组成
- *x++ = 4;
- *x++ = OPT_SUBNET_MASK;
- *x++ = OPT_GATEWAY;
- *x++ = OPT_DNS;
- *x++ = OPT_BROADCAST_ADDR;
- *x++ = OPT_END;
- return DHCP_MSG_FIXED_SIZE + (x - msg->options);
- }
- static void *init_dhcp_msg(dhcp_msg *msg, int type, void *hwaddr, uint32_t xid)
- {
- uint8_t *x;
- memset(msg, 0, sizeof(dhcp_msg));
- msg->op = OP_BOOTREQUEST;
- msg->htype = HTYPE_ETHER;
- msg->hlen = 6;
- msg->hops = 0;
- msg->flags = 0; // htons(FLAGS_BROADCAST);
- msg->xid = xid;
- memcpy(msg->chaddr, hwaddr, 6);
- x = msg->options;
- *x++ = OPT_COOKIE1;//起标识作用,标识这是一个dhcp消息
- *x++ = OPT_COOKIE2;
- *x++ = OPT_COOKIE3;
- *x++ = OPT_COOKIE4;
- *x++ = OPT_MESSAGE_TYPE; //DHCP协议交互的控制报文类型
- *x++ = 1; //DHCP Discover
- *x++ = type;
- return x;
- }
这里比较简单,构建request消息也是一样的
我们可以在discover或者request消息中添加自己的option,让dhcp服务器收到后进行相应的解析
如在discover消息中:
- int init_dhcp_discover_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid)
- {
- uint8_t *x;
- int length;
- x = init_dhcp_msg(msg, DHCPDISCOVER, hwaddr, xid);
- *x++ = OPT_PARAMETER_LIST;//由n个option组成
- *x++ = 4;
- *x++ = OPT_SUBNET_MASK;
- *x++ = OPT_GATEWAY;
- *x++ = OPT_DNS;
- *x++ = OPT_BROADCAST_ADDR;
- #ifdef TEST_DHCP
- /*****************************
- 标识 长度 数据
- *****************************/
- length = strlen("THIS is a Test");
- *x++ = 188;
- *x++ = length;
- memcpy(x,"THIS is a Test",length);
- x += length;
- #endif
- *x++ = OPT_END;
- return DHCP_MSG_FIXED_SIZE + (x - msg->options);
- }
我们加了一个Tag为188的选项,注意这里option的格式。然后重新编译一下dhcp,抓个包,我们可以看到
没有加时
可以看到多了一个option.
No comments:
Post a Comment