参考文档:http://www.ietf.org/rfc/rfc2132.txt
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.
包:http://download.csdn.net/detail/new_abc/4522426