在一些网络环境中,需要用到dhcp服务探测技术,动态调整本地dhcp服务的。本文提供一个探测的demo实现。
原理:
发送dhcp discover 报文,如果能够接受到 dhcp offer回应,则可以获取到服务信息。
代码如下:
-
#include <errno.h>
-
#include <unistd.h>
-
#include <stdio.h>
-
#include <stdlib.h>
-
#include <stddef.h>
-
#include <time.h>
-
#include <string.h>
-
#include <signal.h>
-
#include <sys/ioctl.h>
-
#include <arpa/inet.h>
-
#include <netpacket/packet.h>
-
#include <netinet/if_ether.h>
-
#include <netinet/ip.h>
-
#include <netinet/udp.h>
-
#include <linux/if.h>
-
-
#define max_wait_time 15
-
#define max_error_length 1500
-
-
/* endianness check */
-
#define is_big_endian (*(uint16_t *)"\0\xff" < 0x100)
-
-
-
/* dhcp protocol. rfc 2131, rfc 2132 */
-
#define dhcp_magic 0x63825363
-
#define dhcp_fixed_len 240 /* with dhcp magic */
-
#define dhcp_udp_overhead 28 /* ip udp headers */
-
#define dhcp_mtu_max 1500
-
#define dhcp_mtu_min 576
-
#define dhcp_options_buf_min (dhcp_mtu_min - dhcp_fixed_len - dhcp_udp_overhead)
-
#define dhcp_options_buf_max (dhcp_mtu_max - dhcp_fixed_len - dhcp_udp_overhead)
-
-
#define bootrequest 1
-
#define bootreply 2
-
-
/* dhcp ports and addresses */
-
#define client_port 68
-
#define server_port 67
-
-
/* dhcp packet */
-
struct dhcp_packet
-
{
-
uint8_t op; /* bootrequest or bootreply */
-
uint8_t htype; /* hardware address type. 1 = 10mb ethernet */
-
uint8_t hlen; /* hardware address length */
-
uint8_t hops; /* used by relay agents only */
-
uint32_t xid; /* unuque id */
-
uint16_t secs; /* seconds since client started looking */
-
uint16_t flags; /* only one flag */
-
uint32_t ciaddr; /* clients ip address (if already in use) */
-
uint32_t yiaddr; /* client ip address */
-
uint32_t siaddr_nip; /* next server used in bootstrap */
-
uint32_t gateway_nip; /* relay agent ip address */
-
uint8_t chaddr[16]; /* mac address of client */
-
uint8_t sname[64]; /* server host name (asciz) */
-
uint8_t file[128]; /* boot file name (asciiz) */
-
uint32_t cookie; /* fixed first four option bytes (99, 130, 83, 99 dec) */
-
uint8_t options[dhcp_options_buf_max];
-
};
-
-
/* ip packet with dhcp */
-
struct ip_udp_dhcp_packet
-
{
-
struct iphdr ip; /* ip header */
-
struct udphdr udp; /* udp header */
-
struct dhcp_packet data; /* udp payload */
-
};
-
-
/* udp packet with dhcp */
-
struct udp_dhcp_packet
-
{
-
struct udphdr udp; /* udp header */
-
struct dhcp_packet data; /* udp payload */
-
};
-
-
/* packets size */
-
enum
-
{
-
ip_udp_dhcp_size = sizeof(struct ip_udp_dhcp_packet),
-
udp_dhcp_size = sizeof(struct udp_dhcp_packet),
-
dhcp_size = sizeof(struct dhcp_packet),
-
};
-
-
/* dhcp options codes */
-
#define dhcp_padding 0x00
-
#define dhcp_subnet 0x01
-
#define dhcp_router 0x03
-
#define dhcp_dns 0x06
-
#define dhcp_broadcast 0x1c
-
#define dhcp_static 0x21
-
#define dhcp_ntp 0x2a
-
#define dhcp_vendor 0x2b
-
#define dhcp_netbios_name_srv 0x2c
-
#define dhcp_lease_time 0x33
-
#define dhcp_overload 0x34
-
#define dhcp_message_type 0x35
-
#define dhcp_server_id 0x36 /* dhcp server ip */
-
#define dhcp_param_req 0x37 /* list of options client wants */
-
#define dhcp_max_size 0x39
-
#define dhcp_renewal_time 0x3a
-
#define dhcp_rebinding_time 0x3b
-
#define dhcp_vendor_class_id 0x3c
-
#define dhcp_client_id 0x3d /* client's mac addr*/
-
#define dhcp_sip 0x78
-
#define dhcp_classless_static 0x79
-
#define dhcp_end 0xff
-
-
/* dhcp_message_type values */
-
#define dhcpdiscover 1 /* client -> server */
-
#define dhcpoffer 2 /* client <- server */
-
-
/* offsets in option byte sequence */
-
#define opt_code 0
-
#define opt_len 1
-
#define opt_data 2
-
-
/* bits in "overload" option */
-
#define option_field 0
-
#define file_field 1
-
#define sname_field 2
-
-
-
static volatile sig_atomic_t got_alarm = 0;
-
-
/**
-
* get mac address of interface.
-
*
-
* @param ifname interface name.
-
* @param addr array for mac address.
-
*
-
* @return -1 on failure, 0 on success.
-
*/
-
int get_if_mac(const char* ifname, uint8_t addr[eth_alen])
-
{
-
/* copy interface name into ifreq */
-
struct ifreq ifr;
-
memset(&ifr, 0, sizeof(struct ifreq));
-
-
size_t if_name_len = strlen(ifname);
-
-
if (if_name_len < sizeof(ifr.ifr_name))
-
{
-
memcpy(ifr.ifr_name, ifname, if_name_len);
-
ifr.ifr_name[if_name_len] = 0;
-
}
-
else
-
{
-
return -1;
-
}
-
-
/* create socket */
-
int fd = socket(af_unix, sock_dgram, 0);
-
-
if (fd < 0)
-
{
-
return -1;
-
}
-
-
/* get hw address */
-
if (ioctl(fd, siocgifhwaddr, &ifr) < 0)
-
{
-
close (fd);
-
return -1;
-
}
-
-
close(fd);
-
memcpy (addr, ifr.ifr_hwaddr.sa_data, eth_alen);
-
return 0;
-
}
-
-
-
/**
-
* get interface index.
-
*
-
* @param ifname interface name.
-
*
-
* @return interface index on success, zero on failure.
-
*/
-
unsigned int get_if_index(const char* ifname)
-
{
-
/* copy interface name into ifreq */
-
struct ifreq ifr;
-
memset(&ifr, 0, sizeof(struct ifreq));
-
-
size_t if_name_len = strlen(ifname);
-
-
if (if_name_len < sizeof(ifr.ifr_name))
-
{
-
memcpy(ifr.ifr_name, ifname, if_name_len);
-
ifr.ifr_name[if_name_len] = 0;
-
}
-
else
-
{
-
return -1;
-
}
-
-
/* create socket */
-
int fd = socket(af_unix, sock_dgram, 0);
-
-
if (fd < 0)
-
{
-
return 0;
-
}
-
-
/* get interface index */
-
if (ioctl(fd, siocgifindex, &ifr) < 0)
-
{
-
close (fd);
-
return 0;
-
}
-
-
close(fd);
-
return ifr.ifr_ifindex;
-
}
-
-
/**
-
* calculate internet checksum.
-
*
-
* @param addr start address.
-
* @param nleft length in bytes.
-
*
-
* @return checksum.
-
*/
-
uint16_t inet_chksum(uint16_t* addr, int nleft)
-
{
-
/*
-
* algorithm is simple, using a 32 bit accumulator,
-
* we add sequential 16 bit words to it, and at the end, fold
-
* back all the carry bits from the top 16 bits into the lower
-
* 16 bits.
-
*/
-
unsigned sum = 0;
-
while (nleft > 1)
-
{
-
sum = *addr;
-
nleft -= 2;
-
}
-
/* mop up an odd byte, if necessary */
-
if (nleft == 1)
-
{
-
if (is_big_endian)
-
sum = *(uint8_t*)addr << 8;
-
else
-
sum = *(uint8_t*)addr;
-
}
-
-
/* add back carry outs from top 16 bits to low 16 bits */
-
sum = (sum >> 16) (sum & 0xffff); /* add hi 16 to low 16 */
-
sum = (sum >> 16); /* add carry */
-
-
return (uint16_t)~sum;
-
}
-
-
/**
-
* calculate random id (unsigned 32-bit value).
-
*
-
* @param void.
-
* @return id
-
*/
-
uint32_t random_id(void)
-
{
-
srand(time(null));
-
return rand();
-
}
-
-
-
/**
-
* fill dhcp packet.
-
*
-
* @param packet dhcp packet.
-
* @param hwaddr mac address of interface.
-
* @return void.
-
*/
-
static void dhcp_init_packet(struct dhcp_packet* packet, uint8_t* hwaddr)
-
{
-
unsigned index = 0;
-
-
/* dhcp header */
-
memset(packet, 0, sizeof(*packet));
-
packet->op = bootrequest;
-
packet->htype = 1; /* ethernet */
-
packet->hlen = eth_alen;
-
packet->cookie = htonl(dhcp_magic);
-
packet->secs = 0;
-
memcpy(packet->chaddr, hwaddr, eth_alen);
-
-
/* dhcp options */
-
/* message type */
-
packet->options[index] = dhcp_message_type;
-
packet->options[index] = 1; //length of option data
-
packet->options[index] = dhcpdiscover;
-
-
/* requested parameters */
-
packet->options[index] = dhcp_param_req;
-
packet->options[index] = 9; // number of options
-
packet->options[index] = dhcp_subnet;
-
packet->options[index] = dhcp_router;
-
packet->options[index] = dhcp_dns;
-
packet->options[index] = dhcp_broadcast;
-
packet->options[index] = dhcp_static;
-
packet->options[index] = dhcp_ntp;
-
packet->options[index] = dhcp_vendor;
-
packet->options[index] = dhcp_sip;
-
packet->options[index] = dhcp_classless_static;
-
-
/* client id */
-
packet->options[index] = dhcp_client_id;
-
packet->options[index] = eth_alen 1;
-
packet->options[index] = 1; //ethernet
-
memcpy(&packet->options[index], hwaddr, eth_alen);
-
index = eth_alen;
-
-
/* end option */
-
packet->options[index] = dhcp_end;
-
}
-
-
/**
-
* calculate position of the end option.
-
*
-
* @param optionptr beginning of dhcp options.
-
* @return position of 'end' option.
-
*/
-
static int dhcp_end_option(uint8_t* optionptr)
-
{
-
int i = 0;
-
while (optionptr[i] != dhcp_end)
-
{
-
if (optionptr[i] != dhcp_padding)
-
i = optionptr[i opt_len] opt_data - 1;
-
i;
-
}
-
return i;
-
}
-
-
/**
-
* get an option with bounds checking
-
*
-
* @param packet dhcp packet.
-
* @param code option code.
-
*
-
* @return null or pointer to beginning of the option data.
-
*/
-
static uint8_t* dhcp_get_option(struct dhcp_packet* packet, int code)
-
{
-
uint8_t* optionptr;
-
int len;
-
int rem;
-
int overload = 0;
-
enum
-
{
-
file_field101 = file_field * 0x101,
-
sname_field101 = sname_field * 0x101,
-
};
-
-
/* option bytes: [code][len][data1][data2]...[datalen] */
-
optionptr = packet->options;
-
rem = sizeof(packet->options);
-
while (1)
-
{
-
if (rem <= 0)
-
{
-
return null;
-
}
-
if (optionptr[opt_code] == dhcp_padding)
-
{
-
rem--;
-
optionptr;
-
continue;
-
}
-
if (optionptr[opt_code] == dhcp_end)
-
{
-
if ((overload & file_field101) == file_field)
-
{
-
/* we can use packet->file */
-
overload |= file_field101;
-
optionptr = packet->file;
-
rem = sizeof(packet->file);
-
continue;
-
}
-
if ((overload & sname_field101) == sname_field)
-
{
-
/* we can use packet->sname */
-
overload |= sname_field101;
-
optionptr = packet->sname;
-
rem = sizeof(packet->sname);
-
continue;
-
}
-
break;
-
}
-
len = opt_data optionptr[opt_len];
-
rem -= len;
-
if (rem < 0)
-
continue;
-
-
if (optionptr[opt_code] == code)
-
return optionptr opt_data;
-
-
if (optionptr[opt_code] == dhcp_overload)
-
overload |= optionptr[opt_data];
-
-
optionptr = len;
-
}
-
-
return null;
-
}
-
-
/**
-
* send dhcp discover
-
*
-
* @param fd socket descriptor.
-
* @param ifindex interface index.
-
* @param hwaddr mac address of interface.
-
* @param id unique id.
-
*
-
* @return number of bytes sent on success, -1 on error.
-
*/
-
static int dhcp_send_discover(int fd, int ifindex, uint8_t* hwaddr, uint32_t id)
-
{
-
struct sockaddr_ll sa;
-
struct ip_udp_dhcp_packet packet;
-
unsigned padding;
-
int result = -1;
-
-
/* bind */
-
memset(&sa, 0, sizeof(sa));
-
sa.sll_family = af_packet;
-
sa.sll_protocol = htons(eth_p_ip);
-
sa.sll_ifindex = ifindex;
-
sa.sll_halen = eth_alen;
-
memset(&sa.sll_addr, 0xff, eth_alen);
-
-
if (bind(fd, (struct sockaddr*)&sa, sizeof(sa)) < 0)
-
{
-
return result;
-
}
-
-
/* craft packet */
-
memset(&packet, 0, sizeof(packet));
-
dhcp_init_packet(&packet.data, hwaddr);
-
packet.data.xid = id;
-
-
/* for badly configured servers (they drop dhcp packets > 576 octets (with ethernet header),
-
* but they may only drop packets > 576 octets without ethernet header (590 with ethernet header).
-
* rfc 1542: minimal bootp header 300 octets.
-
*/
-
padding = dhcp_options_buf_max - 1 - dhcp_end_option(packet.data.options);
-
if (padding > dhcp_size - 300)
-
padding = dhcp_size - 300;
-
-
/* ip and udp headers */
-
packet.ip.protocol = ipproto_udp;
-
packet.ip.saddr = htonl(inaddr_any);
-
packet.ip.daddr = htonl(inaddr_broadcast);
-
packet.udp.source = htons(client_port);
-
packet.udp.dest = htons(server_port);
-
/* size, excluding ip header */
-
packet.udp.len = htons(udp_dhcp_size - padding);
-
/* for udp checksumming, ip.len is set to udp packet len */
-
packet.ip.tot_len = packet.udp.len;
-
-
packet.udp.check = inet_chksum((uint16_t*)&packet,
-
ip_udp_dhcp_size - padding);
-
/* for sending, it is set to ip packet len */
-
packet.ip.tot_len = htons(ip_udp_dhcp_size - padding);
-
packet.ip.version = ipversion;
-
packet.ip.ihl = sizeof(packet.ip) >> 2;
-
packet.ip.ttl = ipdefttl;
-
packet.ip.check = inet_chksum((uint16_t*)&packet.ip, sizeof(packet.ip));
-
-
/* send packet */
-
result = sendto(fd, &packet, ip_udp_dhcp_size - padding, 0,
-
(struct sockaddr*)&sa, sizeof(sa));
-
-
return result;
-
}
-
-
-
/**
-
* receive offer
-
*
-
* @param fd socket descriptor.
-
* @param id unique id.
-
* @param hwaddr mac address of interface.
-
*
-
* @return 0 on success, -1 on read error,
-
* -2 packet is not correct, -3 on eintr (time is out).
-
*/
-
static int dhcp_recv_offer(int fd, uint32_t id, uint8_t* hwaddr)
-
{
-
int bytes;
-
struct ip_udp_dhcp_packet packet;
-
struct dhcp_packet data;
-
-
uint8_t* opt_data;
-
uint16_t check;
-
char ip_str[inet_addrstrlen];
-
uint32_t ip_addr;
-
int i;
-
-
memset(&packet, 0, sizeof(packet));
-
memset(&data, 0, sizeof(data));
-
-
/* read packet */
-
bytes = read(fd, &packet, sizeof(packet));
-
-
if (bytes < 0)
-
{
-
if (errno == eintr)
-
{
-
return -3;
-
}
-
else
-
{
-
return -1;
-
}
-
}
-
/* packet is too short */
-
if (bytes < (int) (sizeof(packet.ip) sizeof(packet.udp)))
-
return -2;
-
/* oversized packet */
-
if (bytes < (int) ntohs(packet.ip.tot_len))
-
return -2;
-
-
/* ignore any extra garbage bytes */
-
bytes = ntohs(packet.ip.tot_len);
-
-
/* unrelated/bogus packet */
-
if (packet.ip.protocol != ipproto_udp
-
|| packet.ip.version != ipversion
-
|| packet.ip.ihl != (sizeof(packet.ip) >> 2)
-
|| packet.udp.dest != htons(client_port)
-
|| ntohs(packet.udp.len) != (uint16_t)(bytes - sizeof(packet.ip)) )
-
return -2;
-
-
/* verify ip checksum */
-
check = packet.ip.check;
-
packet.ip.check = 0;
-
if (check != inet_chksum((uint16_t*)&packet.ip, sizeof(packet.ip)))
-
return -2;
-
-
/* verify udp checksum, ip header has to be modified for this */
-
memset(&packet.ip, 0, offsetof(struct iphdr, protocol));
-
/* ip.xx fields which are not memset: protocol, check, saddr, daddr */
-
packet.ip.tot_len = packet.udp.len;
-
check = packet.udp.check;
-
packet.udp.check = 0;
-
if (check && check != inet_chksum((uint16_t*)&packet, bytes))
-
return -2;
-
-
memcpy(&data, &packet.data, sizeof(data));
-
-
/* check dhcp magic */
-
if (bytes < (int)offsetof(struct dhcp_packet, options)
-
|| data.cookie != htonl(dhcp_magic))
-
return -2;
-
-
/* check xid */
-
if (data.xid != id)
-
return -2;
-
-
/* ignore packets that aren't for us */
-
if (data.hlen != eth_alen || memcmp(&data.chaddr, hwaddr, eth_alen))
-
return -2;
-
-
/* check message type */
-
opt_data = dhcp_get_option(&data, dhcp_message_type);
-
if (!opt_data || *opt_data != dhcpoffer)
-
return -2;
-
-
/* print output */
-
/* get dhcp server id */
-
opt_data = dhcp_get_option(&data, dhcp_server_id);
-
if (opt_data)
-
{
-
ip_addr = *(uint32_t*)opt_data;
-
if (inet_ntop(af_inet, &ip_addr, ip_str, sizeof(ip_str)))
-
printf("-->> server:%s\n", ip_str);
-
}
-
-
/* get netmask */
-
opt_data = dhcp_get_option(&data, dhcp_subnet);
-
if (opt_data)
-
{
-
ip_addr = *(uint32_t*)opt_data;
-
if (inet_ntop(af_inet, &ip_addr, ip_str, sizeof(ip_str)))
-
printf("-->> mask:%s\n", ip_str);
-
}
-
-
/* get router */
-
opt_data = dhcp_get_option(&data, dhcp_router);
-
if (opt_data)
-
{
-
ip_addr = *(uint32_t*)opt_data;
-
if (inet_ntop(af_inet, &ip_addr, ip_str, sizeof(ip_str)))
-
printf("-->> router:%s\n", ip_str);
-
-
}
-
-
/* get dns servers */
-
opt_data = dhcp_get_option(&data, dhcp_dns);
-
if (opt_data)
-
{
-
-
int num = *(opt_data - 1) >> 2; /* get number of servers */
-
for (i = 0; i < num; i)
-
{
-
ip_addr = (*((uint32_t*)opt_data i));
-
if (inet_ntop(af_inet, &ip_addr, ip_str, sizeof(ip_str)))
-
printf("-->> dns%d:%s\n", i 1, ip_str);
-
else
-
break;
-
}
-
}
-
-
/* get broadcast address */
-
opt_data = dhcp_get_option(&data, dhcp_broadcast);
-
if (opt_data)
-
{
-
ip_addr = *(uint32_t*)opt_data;
-
if (inet_ntop(af_inet, &ip_addr, ip_str, sizeof(ip_str)))
-
printf("-->> broadcast:%s\n", ip_str);
-
-
}
-
/* get netbios name servers */
-
opt_data = dhcp_get_option(&data, dhcp_netbios_name_srv);
-
if (opt_data)
-
{
-
int num = *(opt_data - 1) >> 2; /* get number of servers */
-
for (i = 0; i < num; i)
-
{
-
ip_addr = *((uint32_t*)opt_data i);
-
if (inet_ntop(af_inet, &ip_addr, ip_str, sizeof(ip_str)))
-
printf("-->> netbios%d:%s\n", i 1, ip_str);
-
else
-
break;
-
}
-
}
-
return 0;
-
}
-
-
/**
-
* signal handler
-
* @param signal value.
-
*
-
* @return void.
-
*/
-
static void handler(int sig)
-
{
-
if (sig == sigalrm)
-
got_alarm = 1;
-
}
-
-
-
/**
-
* search dhcp server.
-
*
-
* @param ifname network interface name.
-
* @param time wait time.
-
*
-
* @return 1 on success, 0 on time is out, -2 on invalid json object for result,
-
* -1 on other errors.
-
*/
-
int scan_dhcp(const char* ifname, unsigned int time)
-
{
-
int ifindex;
-
uint8_t hwaddr[eth_alen];
-
struct sigaction sa;
-
uid_t euid;
-
uint32_t id;
-
int fd = -1;
-
int ret = -1;
-
-
-
/* check superuser priveleges */
-
if ((euid = geteuid()) != 0)
-
{
-
printf( "you must be root\n");
-
goto err_exit;
-
}
-
-
/* check input data */
-
/* check wait time */
-
if (time == 0)
-
time = 1;
-
-
if (time > max_wait_time)
-
{
-
printf("max wait time is %d\n", max_wait_time);
-
goto err_exit;
-
}
-
-
/* check interface name */
-
if (!ifname || *ifname == '\0')
-
{
-
printf("empty interface name\n");
-
goto err_exit;
-
}
-
-
/* check interface name length */
-
if (strlen(ifname) > ifnamsiz)
-
{
-
printf("interface name is too long\n");
-
goto err_exit;
-
}
-
-
/* get interface index */
-
if ((ifindex = get_if_index(ifname)) == 0)
-
{
-
printf("no interface found\n");
-
goto err_exit;
-
}
-
-
/* get mac address */
-
if ((get_if_mac(ifname, hwaddr)) < 0)
-
{
-
printf( "can't get mac address\n");
-
goto err_exit;
-
}
-
-
/* create socket */
-
fd = socket(pf_packet, sock_dgram, htons(eth_p_ip));
-
-
if (fd < 0)
-
{
-
printf("can't create socket\n");
-
goto err_exit;
-
}
-
-
/* establish handler for notification signal */
-
memset(&sa, 0, sizeof(struct sigaction));
-
sa.sa_flags = 0;
-
sigemptyset(&sa.sa_mask);
-
sa.sa_handler = handler;
-
if ((sigaction(sigalrm, &sa, null)) == -1)
-
{
-
printf("can't set handler for timer\n");
-
goto err_exit2;
-
}
-
-
/* send request */
-
id = random_id();
-
ret = dhcp_send_discover(fd, ifindex, hwaddr, id);
-
if (ret < 0)
-
{
-
printf("can't send request\n");
-
goto err_exit2;
-
}
-
-
/* start timer */
-
alarm(time);
-
-
/* loop, recv packets && search answer for us */
-
while(1)
-
{
-
ret = dhcp_recv_offer(fd, id, hwaddr);
-
-
/* answer received */
-
if (ret == 0)
-
{
-
ret = 1;
-
break;
-
}
-
/* read error */
-
if (ret == -1)
-
{
-
printf("read socket error\n");
-
break;
-
}
-
-
/* check timer */
-
if (got_alarm == 1)
-
{
-
ret = 0;
-
break;
-
}
-
}
-
/* cancel timer */
-
alarm(0);
-
err_exit2:
-
close(fd);
-
err_exit:
-
return ret;
-
}
-
-
-
int main(int argc, char** argv)
-
{
-
if(argc < 2)
-
{
-
printf("must appoint a interface\n");
-
return 127;
-
}
-
return scan_dhcp(argv[1], 3);
-
}
demo测试:
注意,需要root权限运行
阅读(1890) | 评论(0) | 转发(0) |