Saturday, June 01, 2013

android dhcp

http://blog.csdn.net/new_abc/article/details/7898543

http://download.csdn.net/detail/new_abc/4520361这里有一个介绍dhcp的文档,比较详细
看一下dhcp的过程:
Android DHCP客户端的代码在system\core\libnetutils目录下
  1. int do_dhcp(char *iname)  
  2. {  
  3.     if (ifc_set_addr(iname, 0)) {  
  4.         printerr("failed to set ip addr for %s to 0.0.0.0: %s\n", iname, strerror(errno));  
  5.         return -1;  
  6.     }  
  7.   
  8.     if (ifc_up(iname)) {  
  9.         printerr("failed to bring up interface %s: %s\n", iname, strerror(errno));  
  10.         return -1;  
  11.     }  
  12.     sleep(2);  
  13.     return dhcp_init_ifc(iname);  
  14. }  

这里的iname是网口名,如“eth0",ifc_set_addr设置一个全局socket的地址为传下来的网口名字,ifc_up设置网口标志为启动状态。然后主要的工作在dhcp_init_ifc:
  1. int dhcp_init_ifc(const char *ifname)  
  2. {  
  3.     dhcp_msg discover_msg;  
  4.     dhcp_msg request_msg;  
  5.     dhcp_msg reply;  
  6.     dhcp_msg *msg;  
  7.     dhcp_info info;  
  8.     int s, r, size;  
  9.     int valid_reply;  
  10.     uint32_t xid,last,now;  
  11.     unsigned char hwaddr[6];  
  12.     struct pollfd pfd;  
  13.     unsigned int state;  
  14.     unsigned int timeout;  
  15.     int if_index;  
  16.       
  17.     xid = (uint32_t) get_msecs();  
  18.     last = (uint32_t) get_msecs();  
  19.       
  20.     if (ifc_get_hwaddr(ifname, hwaddr)) { //获取mac地址  
  21.         return fatal("cannot obtain interface address");  
  22.     }  
  23.       
  24.     if (ifc_get_ifindex(ifname, &if_index)) {  
  25.         return fatal("cannot obtain interface index");  
  26.     }  
  27.       
  28.     s = open_raw_socket(ifname, hwaddr, if_index);  
  29.   
  30.     timeout = TIMEOUT_INITIAL;  
  31.     state = STATE_SELECTING;  
  32.     info.type = 0;  
  33.     goto transmit;  
  34.   
  35.     for (;;) {  
  36.         pfd.fd = s;  
  37.         pfd.events = POLLIN;  
  38.         pfd.revents = 0;  
  39.     now = (uint32_t) get_msecs();  
  40.     if (now > (last + TIMEOUT_INITIAL))  
  41.         goto transmit;  
  42.     //  LOGD("lijj timeout = %d", timeout);  
  43.         r = poll(&pfd, 1, timeout);  
  44.         if (r == 0) {  
  45. #if VERBOSE  
  46.             printerr("TIMEOUT\n");  
  47. #endif  
  48.             if (timeout >= TIMEOUT_MAX) {  
  49.                 printerr("timed out\n");  
  50.                 if ( info.type == DHCPOFFER ) {  
  51.                     printerr("no acknowledgement from DHCP server\nconfiguring %s with offered parameters\n", ifname);  
  52.                     return ifc_configure(ifname, &info);  
  53.                 }  
  54.                 errno = ETIME;  
  55.                 close(s);  
  56.                 return -1;  
  57.             }  
  58.             timeout = timeout * 2;  
  59.   
  60.         transmit:  
  61.             size = 0;  
  62.             msg = NULL;  
  63.             switch(state) {  
  64.             case STATE_SELECTING:  
  65.                 msg = &discover_msg;  
  66.                 size = init_dhcp_discover_msg(msg, hwaddr, xid);//构建dhcp DISCOVER消息  
  67.                 break;  
  68.             case STATE_REQUESTING:  
  69.                 msg = &request_msg;  
  70.                 size = init_dhcp_request_msg(msg, hwaddr, xid, info.ipaddr, info.serveraddr);//构建请求消息  
  71.                 break;  
  72.             default:  
  73.                 r = 0;  
  74.             }  
  75.             if (size != 0) {  
  76.                 r = send_message(s, if_index, msg, size);//发送消息  
  77.                 if (r < 0) {  
  78.                     printerr("error sending dhcp msg: %s\n", strerror(errno));  
  79.                 }  
  80.             last = (uint32_t) get_msecs();  
  81.             }  
  82.             continue;  
  83.         }  
  84.   
  85.         if (r < 0) {  
  86.             if ((errno == EAGAIN) || (errno == EINTR)) {  
  87.                 continue;  
  88.             }  
  89.             return fatal("poll failed");  
  90.         }  
  91.   
  92.         errno = 0;  
  93.         r = receive_packet(s, &reply);  
  94.         if (r < 0) {  
  95.             if (errno != 0) {  
  96.                 LOGD("receive_packet failed (%d): %s", r, strerror(errno));  
  97.                 if (errno == ENETDOWN || errno == ENXIO) {  
  98.                     return -1;  
  99.                 }  
  100.             }  
  101.             continue;  
  102.         }  
  103.   
  104. #if VERBOSE > 1  
  105.         dump_dhcp_msg(&reply, r);  
  106. #endif  
  107.         decode_dhcp_msg(&reply, r, &info);//解析返回的dhcp消息  
  108.   
  109.         if (state == STATE_SELECTING) {  
  110.             valid_reply = is_valid_reply(&discover_msg, &reply, r);  
  111.         } else {  
  112.             valid_reply = is_valid_reply(&request_msg, &reply, r);  
  113.         }  
  114.         if (!valid_reply) {  
  115.             printerr("invalid reply\n");  
  116.             continue;  
  117.         }  
  118.   
  119.         if (verbose) dump_dhcp_info(&info);  
  120.   
  121.         switch(state) {  
  122.         case STATE_SELECTING:  
  123.             if (info.type == DHCPOFFER) {  
  124.                 state = STATE_REQUESTING;  
  125.                 timeout = TIMEOUT_INITIAL;  
  126.                 xid++;  
  127.                 goto transmit;  
  128.             }  
  129.             break;  
  130.         case STATE_REQUESTING:  
  131.             if (info.type == DHCPACK) {  
  132.                 printerr("configuring %s\n", ifname);  
  133.                 close(s);  
  134.                 return ifc_configure(ifname, &info);  
  135.             } else if (info.type == DHCPNAK) {//不能向客户提供这个网络地址或参数  
  136.                 printerr("configuration request denied\n");  
  137.                 close(s);  
  138.                 return -1;  
  139.             } else {  
  140.                 printerr("ignoring %s message in state %d\n",  
  141.                          dhcp_type_to_name(info.type), state);  
  142.             }  
  143.             break;  
  144.         }  
  145.     }  
  146.     close(s);  
  147.     return 0;  
  148. }  

这里大概过程是:
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消息
  1. int init_dhcp_discover_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid)  
  2. {  
  3.     uint8_t *x;  
  4.   
  5.     x = init_dhcp_msg(msg, DHCPDISCOVER, hwaddr, xid);  
  6.   
  7.     *x++ = OPT_PARAMETER_LIST;//由n个option组成  
  8.     *x++ = 4;  
  9.     *x++ = OPT_SUBNET_MASK;  
  10.     *x++ = OPT_GATEWAY;  
  11.     *x++ = OPT_DNS;  
  12.     *x++ = OPT_BROADCAST_ADDR;  
  13.   
  14.     *x++ = OPT_END;  
  15.   
  16.     return DHCP_MSG_FIXED_SIZE + (x - msg->options);  
  17. }  

  1. static void *init_dhcp_msg(dhcp_msg *msg, int type, void *hwaddr, uint32_t xid)  
  2. {  
  3.     uint8_t *x;  
  4.   
  5.     memset(msg, 0, sizeof(dhcp_msg));  
  6.   
  7.     msg->op = OP_BOOTREQUEST;  
  8.     msg->htype = HTYPE_ETHER;  
  9.     msg->hlen = 6;  
  10.     msg->hops = 0;  
  11.   
  12.     msg->flags = 0; // htons(FLAGS_BROADCAST);  
  13.   
  14.     msg->xid = xid;  
  15.   
  16.     memcpy(msg->chaddr, hwaddr, 6);  
  17.   
  18.     x = msg->options;  
  19.   
  20.     *x++ = OPT_COOKIE1;//起标识作用,标识这是一个dhcp消息  
  21.     *x++ = OPT_COOKIE2;  
  22.     *x++ = OPT_COOKIE3;  
  23.     *x++ = OPT_COOKIE4;  
  24.   
  25.     *x++ = OPT_MESSAGE_TYPE; //DHCP协议交互的控制报文类型  
  26.     *x++ = 1;                   //DHCP Discover  
  27.     *x++ = type;  
  28.   
  29.     return x;  
  30. }  

这里比较简单,构建request消息也是一样的
我们可以在discover或者request消息中添加自己的option,让dhcp服务器收到后进行相应的解析
如在discover消息中:
  1. int init_dhcp_discover_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid)  
  2. {  
  3.     uint8_t *x;  
  4.     int length;  
  5.     x = init_dhcp_msg(msg, DHCPDISCOVER, hwaddr, xid);  
  6.   
  7.     *x++ = OPT_PARAMETER_LIST;//由n个option组成  
  8.     *x++ = 4;  
  9.     *x++ = OPT_SUBNET_MASK;  
  10.     *x++ = OPT_GATEWAY;  
  11.     *x++ = OPT_DNS;  
  12.     *x++ = OPT_BROADCAST_ADDR;  
  13. #ifdef TEST_DHCP  
  14.     /***************************** 
  15.         标识  长度  数据 
  16.     *****************************/  
  17.       
  18.     length = strlen("THIS is a Test");  
  19.     *x++ = 188;  
  20.     *x++ = length;  
  21.     memcpy(x,"THIS is a Test",length);  
  22.     x += length;  
  23. #endif  
  24.     *x++ = OPT_END;  
  25.   
  26.     return DHCP_MSG_FIXED_SIZE + (x - msg->options);  
  27. }  

我们加了一个Tag为188的选项,注意这里option的格式。然后重新编译一下dhcp,抓个包,我们可以看到
没有加时
可以看到多了一个option.


No comments: