mirror of https://github.com/sipwise/dhtest.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1006 lines
24 KiB
1006 lines
24 KiB
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h> /* To set non blocking on socket */
|
|
#include <sys/socket.h> /* Generic socket calls */
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <sys/ioctl.h>
|
|
#include <time.h>
|
|
#include <sys/types.h>
|
|
#include <signal.h>
|
|
#include <linux/if_packet.h>
|
|
#include <linux/if_ether.h>
|
|
#include <linux/if_arp.h>
|
|
#include <stdarg.h>
|
|
#include <errno.h>
|
|
#include "headers.h"
|
|
|
|
|
|
static unsigned char dhopt_buff[500];
|
|
/* Pointers for all layer data structures */
|
|
struct vlan_eth_hdr *vlan_hg;
|
|
struct iphdr *iph_g = { 0 };
|
|
struct udphdr *uh_g = { 0 };
|
|
static struct arp_hdr *arp_hg;
|
|
static struct icmp_hdr *icmp_hg;
|
|
static unsigned char *dhopt_pointer_g;
|
|
|
|
/* DHCP packet, option buffer and size of option buffer */
|
|
static unsigned char dhcp_packet_send[1518];
|
|
static unsigned char dhcp_packet_recv[1518];
|
|
static u_int16_t dhcp_hdr_size = sizeof(struct dhcpv4_hdr);
|
|
static u_int32_t dhopt_size;
|
|
static struct sockaddr_ll ll = { 0 }; /* Socket address structure */
|
|
static int sock_packet;
|
|
static u_char arp_icmp_packet[1514] = { 0 };
|
|
static u_char arp_icmp_reply[1514] = { 0 };
|
|
static u_int16_t icmp_len = 0;
|
|
static int have_set_promisc;
|
|
static unsigned int lease_time;
|
|
u_char dmac[ETHER_ADDR_LEN];
|
|
|
|
|
|
|
|
|
|
static void read_options(int len);
|
|
static int check_packet(int pkt_type, int len);
|
|
static int map_all_layer_ptr(int pkt_type);
|
|
|
|
|
|
|
|
/*
|
|
* Opens PF_PACKET socket and return error if socket
|
|
* opens fails
|
|
*/
|
|
|
|
int open_socket()
|
|
{
|
|
sock_packet = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
|
|
if(sock_packet < 0)
|
|
return SOCKET_ERR;
|
|
/* Set link layer parameters */
|
|
ll.sll_family = AF_PACKET;
|
|
ll.sll_protocol = htons(ETH_P_ALL);
|
|
ll.sll_ifindex = iface;
|
|
ll.sll_hatype = ARPHRD_ETHER;
|
|
ll.sll_pkttype = PACKET_OTHERHOST;
|
|
ll.sll_halen = 6;
|
|
|
|
bind(sock_packet, (struct sockaddr *)&ll, sizeof(struct sockaddr_ll));
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Sets the promiscous mode on the interface
|
|
*/
|
|
static int set_clear_promisc(int op)
|
|
{
|
|
struct ifreq ifr;
|
|
|
|
if (!op && !have_set_promisc)
|
|
return 0;
|
|
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
strcpy(ifr.ifr_name, iface_name);
|
|
|
|
if (ioctl(sock_packet, SIOCGIFFLAGS, &ifr))
|
|
goto error;
|
|
|
|
if (op) {
|
|
if ((ifr.ifr_flags & IFF_PROMISC)) {
|
|
have_set_promisc = 0;
|
|
return 0;
|
|
}
|
|
have_set_promisc = 1;
|
|
}
|
|
|
|
if (op)
|
|
ifr.ifr_flags |= IFF_PROMISC;
|
|
else
|
|
ifr.ifr_flags &= ~IFF_PROMISC;
|
|
|
|
if (ioctl(sock_packet, SIOCSIFFLAGS, &ifr))
|
|
goto error;
|
|
|
|
return 0;
|
|
|
|
error:
|
|
critical("Error on setting promisc: %m");
|
|
}
|
|
|
|
int set_promisc()
|
|
{
|
|
return set_clear_promisc(1);
|
|
}
|
|
|
|
int clear_promisc()
|
|
{
|
|
return set_clear_promisc(0);
|
|
}
|
|
|
|
/*
|
|
* Get address from the interface
|
|
*/
|
|
u_int32_t get_interface_address()
|
|
{
|
|
int status;
|
|
struct ifreq ifr;
|
|
|
|
strcpy(ifr.ifr_name, iface_name);
|
|
ifr.ifr_addr.sa_family = AF_INET;
|
|
status = ioctl(sock_packet, SIOCGIFADDR, &ifr);
|
|
|
|
if(status < 0)
|
|
critical("Error getting interface address: %m");
|
|
|
|
return ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr;
|
|
}
|
|
|
|
/*
|
|
* Sends DHCP packet on the socket. Packet type
|
|
* is passed as argument. Extended to send ARP and ICMP packets
|
|
*/
|
|
int send_packet(int pkt_type)
|
|
{
|
|
int ret;
|
|
switch (pkt_type) {
|
|
case DHCP_MSGDISCOVER:
|
|
case DHCP_MSGREQUEST:
|
|
case DHCP_MSGRELEASE:
|
|
ret = sendto(sock_packet,\
|
|
dhcp_packet_send,\
|
|
(l2_hdr_size + l3_hdr_size + l4_hdr_size + dhcp_hdr_size + dhopt_size),\
|
|
0,\
|
|
(struct sockaddr *) &ll,\
|
|
sizeof(ll));
|
|
break;
|
|
|
|
case ARP_SEND:
|
|
ret = sendto(sock_packet,\
|
|
arp_icmp_reply,\
|
|
60,\
|
|
0,\
|
|
(struct sockaddr *) &ll,\
|
|
sizeof(ll));
|
|
break;
|
|
|
|
case ICMP_SEND:
|
|
ret = sendto(sock_packet,\
|
|
arp_icmp_reply,\
|
|
(l2_hdr_size + l3_hdr_size + ICMP_H + icmp_len),\
|
|
0,\
|
|
(struct sockaddr *) &ll,\
|
|
sizeof(ll));
|
|
break;
|
|
|
|
default:
|
|
abort();
|
|
}
|
|
|
|
if(ret < 0)
|
|
critical("Packet send failure: %m");
|
|
|
|
if(pkt_type == DHCP_MSGDISCOVER) {
|
|
if (!nagios_flag && !quiet) {
|
|
printf("DHCP discover sent\t - ");
|
|
printf("Client MAC : " ETH_F_FMT "\n", ETH_F_ARG(dhmac));
|
|
}
|
|
} else if (pkt_type == DHCP_MSGREQUEST) {
|
|
if (!nagios_flag && !quiet) {
|
|
printf("DHCP request sent\t - ");
|
|
printf("Client MAC : " ETH_F_FMT "\n", ETH_F_ARG(dhmac));
|
|
}
|
|
} else if (pkt_type == DHCP_MSGRELEASE) {
|
|
if (!nagios_flag && !quiet) {
|
|
printf("DHCP release sent\t - ");
|
|
printf("Client MAC : " ETH_F_FMT "\n", ETH_F_ARG(dhmac));
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Receives DHCP packet. Packet type is passed as argument
|
|
* Extended to recv ARP and ICMP packets
|
|
*/
|
|
int recv_packet(int pkt_type)
|
|
|
|
{
|
|
int ret, retval, chk_pkt_state;
|
|
socklen_t sock_len;
|
|
fd_set read_fd;
|
|
struct timeval tval, *tvp;
|
|
int timeout_rv, recv_len;
|
|
unsigned char *recv_buf;
|
|
|
|
tval.tv_sec = 5;
|
|
tval.tv_usec = 0;
|
|
tvp = &tval;
|
|
|
|
switch (pkt_type) {
|
|
case DHCP_MSGOFFER:
|
|
timeout_rv = DHCP_DISC_RESEND;
|
|
recv_buf = dhcp_packet_recv;
|
|
recv_len = sizeof(dhcp_packet_recv);
|
|
break;
|
|
case DHCP_MSGACK:
|
|
timeout_rv = DHCP_REQ_RESEND;
|
|
recv_buf = dhcp_packet_recv;
|
|
recv_len = sizeof(dhcp_packet_recv);
|
|
break;
|
|
case ARP_ICMP_RCV:
|
|
tvp = &tval_listen;
|
|
timeout_rv = LISTEN_TIMEOUT;
|
|
recv_buf = arp_icmp_packet;
|
|
recv_len = sizeof(arp_icmp_packet);
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
|
|
while(tvp->tv_sec != 0) {
|
|
FD_ZERO(&read_fd);
|
|
FD_SET(sock_packet, &read_fd);
|
|
retval = select(sock_packet + 1, &read_fd, NULL, NULL, tvp);
|
|
if (retval == 0)
|
|
return timeout_rv;
|
|
if (retval < 0)
|
|
abort();
|
|
|
|
sock_len = sizeof(ll);
|
|
ret = recvfrom(sock_packet,
|
|
recv_buf,
|
|
recv_len,
|
|
0,
|
|
(struct sockaddr *)&ll,
|
|
&sock_len);
|
|
|
|
if (ret < 0)
|
|
return timeout_rv;
|
|
|
|
chk_pkt_state = check_packet(pkt_type, ret);
|
|
|
|
switch (chk_pkt_state) {
|
|
case DHCP_OFFR_RCVD:
|
|
case DHCP_ACK_RCVD:
|
|
case DHCP_NAK_RCVD:
|
|
case ARP_RCVD:
|
|
case ICMP_RCVD:
|
|
return chk_pkt_state;
|
|
}
|
|
}
|
|
return timeout_rv;
|
|
}
|
|
|
|
/* Debug function - Prints the buffer on HEX format */
|
|
int print_buff(u_int8_t *buff, int size)
|
|
{
|
|
int tmp;
|
|
printf("\n---------Buffer data-------\n");
|
|
for(tmp = 0; tmp < size; tmp++) {
|
|
printf("%02X ", buff[tmp]);
|
|
if((tmp % 16) == 0 && tmp != 0) {
|
|
printf("\n");
|
|
}
|
|
}
|
|
printf("\n");
|
|
return 0;
|
|
}
|
|
|
|
/* Reset the DHCP option buffer to zero and dhopt_size to zero */
|
|
int reset_dhopt_size()
|
|
{
|
|
bzero(dhopt_buff, sizeof(dhopt_buff));
|
|
dhopt_size = 0;
|
|
return 0;
|
|
}
|
|
|
|
void init_rand() {
|
|
srand(time(NULL) ^ (getpid() << 16));
|
|
}
|
|
|
|
/*
|
|
* Sets a random DHCP xid
|
|
*/
|
|
int set_rand_dhcp_xid()
|
|
{
|
|
if(dhcp_xid == 0)
|
|
dhcp_xid = (rand() % 0xfffffff0) + 1;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* IP checksum function - Calculates the IP checksum
|
|
*/
|
|
u_int16_t ipchksum(u_int16_t *buff, int words)
|
|
{
|
|
unsigned int sum;
|
|
int i;
|
|
|
|
sum = 0;
|
|
for(i = 0;i < words; i++){
|
|
sum = sum + *(buff + i);
|
|
}
|
|
sum = (sum >> 16) + sum;
|
|
return (u_int16_t)~sum;
|
|
}
|
|
|
|
/*
|
|
* ICMP checksum function - Calculates the ICMP checksum
|
|
*/
|
|
u_int16_t icmpchksum(u_int16_t *buff, int words)
|
|
{
|
|
unsigned int sum;
|
|
unsigned int last_word = 0;
|
|
int i;
|
|
|
|
/* Checksum enhancement for odd packets */
|
|
if((icmp_len % 2) == 1) {
|
|
last_word = *((u_int8_t *)buff + icmp_len + ICMP_H - 1);
|
|
last_word = (htons(last_word) << 8);
|
|
sum = 0;
|
|
for(i = 0;i < words; i++){
|
|
sum = sum + *(buff + i);
|
|
}
|
|
sum = sum + last_word;
|
|
sum = (sum >> 16) + sum;
|
|
return (u_int16_t)~sum;
|
|
} else {
|
|
sum = 0;
|
|
for(i = 0;i < words; i++){
|
|
sum = sum + *(buff + i);
|
|
}
|
|
sum = (sum >> 16) + sum;
|
|
return (u_int16_t)~sum;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* TCP/UDP checksum function
|
|
*/
|
|
u_int16_t l4_sum(u_int16_t *buff, int words, u_int16_t *srcaddr, u_int16_t *dstaddr, u_int16_t proto, u_int16_t len)
|
|
{
|
|
unsigned int sum, last_word = 0;
|
|
int i;
|
|
|
|
/* Checksum enhancement - Support for odd byte packets */
|
|
if((htons(len) % 2) == 1) {
|
|
last_word = *((u_int8_t *)buff + ntohs(len) - 1);
|
|
last_word = (htons(last_word) << 8);
|
|
sum = 0;
|
|
for(i = 0;i < words; i++){
|
|
sum = sum + *(buff + i);
|
|
}
|
|
sum = sum + last_word;
|
|
sum = sum + *(srcaddr) + *(srcaddr + 1) + *(dstaddr) + *(dstaddr + 1) + proto + len;
|
|
sum = (sum >> 16) + sum;
|
|
return ~sum;
|
|
} else {
|
|
/* Original checksum function */
|
|
sum = 0;
|
|
for(i = 0;i < words; i++){
|
|
sum = sum + *(buff + i);
|
|
}
|
|
|
|
sum = sum + *(srcaddr) + *(srcaddr + 1) + *(dstaddr) + *(dstaddr + 1) + proto + len;
|
|
sum = (sum >> 16) + sum;
|
|
return ~sum;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Builds DHCP option53 on dhopt_buff
|
|
*/
|
|
int build_option53(int msg_type)
|
|
{
|
|
dhopt_buff[dhopt_size++] = DHCP_MESSAGETYPE;
|
|
dhopt_buff[dhopt_size++] = 1;
|
|
dhopt_buff[dhopt_size++] = (unsigned char) msg_type;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Builds DHCP option50 on dhopt_buff
|
|
*/
|
|
int build_option50()
|
|
{
|
|
dhopt_buff[dhopt_size++] = DHCP_REQUESTEDIP;
|
|
dhopt_buff[dhopt_size++] = 4;
|
|
memcpy(dhopt_buff + dhopt_size, &option50_ip, 4);
|
|
dhopt_size += 4;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Builds DHCP option51 on dhopt_buff - DHCP lease time requested
|
|
*/
|
|
int build_option51()
|
|
{
|
|
u_int32_t msg = htonl(option51_lease_time);
|
|
|
|
dhopt_buff[dhopt_size++] = DHCP_LEASETIME;
|
|
dhopt_buff[dhopt_size++] = 4;
|
|
memcpy(dhopt_buff + dhopt_size, &msg, 4);
|
|
dhopt_size += 4;
|
|
return 0;
|
|
}
|
|
/*
|
|
* Builds DHCP option54 on dhopt_buff
|
|
*/
|
|
int build_option54()
|
|
{
|
|
dhopt_buff[dhopt_size++] = DHCP_SERVIDENT;
|
|
dhopt_buff[dhopt_size++] = 4;
|
|
memcpy(dhopt_buff + dhopt_size, &server_id, 4);
|
|
dhopt_size += 4;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Builds DHCP option55 on dhopt_buff
|
|
*/
|
|
int build_option55()
|
|
{
|
|
dhopt_buff[dhopt_size++] = DHCP_PARAMREQUEST;
|
|
dhopt_buff[dhopt_size++] = 4;
|
|
dhopt_buff[dhopt_size++] = DHCP_SUBNETMASK;
|
|
dhopt_buff[dhopt_size++] = DHCP_ROUTER;
|
|
dhopt_buff[dhopt_size++] = DHCP_DOMAINNAME;
|
|
dhopt_buff[dhopt_size++] = DHCP_DNS;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Builds DHCP option60 on dhopt_buff
|
|
*/
|
|
int build_option60_vci()
|
|
{
|
|
dhopt_buff[dhopt_size++] = DHCP_CLASSID;
|
|
dhopt_buff[dhopt_size++] = (unsigned char) strlen(vci_buff);
|
|
memcpy(dhopt_buff + dhopt_size, vci_buff, strlen(vci_buff));
|
|
dhopt_size += strlen(vci_buff);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Builds DHCP option 12, hostname, on dhopt_buff
|
|
*/
|
|
int build_option12_hostname()
|
|
{
|
|
dhopt_buff[dhopt_size++] = DHCP_HOSTNAME;
|
|
dhopt_buff[dhopt_size++] = (unsigned char) strlen(hostname_buff);
|
|
memcpy(dhopt_buff + dhopt_size, hostname_buff, strlen(hostname_buff));
|
|
dhopt_size += strlen(hostname_buff);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Builds DHCP option 81, fqdn, on dhopt_buff
|
|
*/
|
|
int build_option81_fqdn()
|
|
{
|
|
unsigned char flags = 0;
|
|
|
|
if (fqdn_n)
|
|
flags |= FQDN_N_FLAG;
|
|
if (fqdn_s)
|
|
flags |= FQDN_S_FLAG;
|
|
|
|
dhopt_buff[dhopt_size++] = DHCP_FQDN;
|
|
dhopt_buff[dhopt_size++] = (unsigned char) strlen(fqdn_buff) + 3;
|
|
dhopt_buff[dhopt_size++] = flags;
|
|
dhopt_buff[dhopt_size++] = 0;
|
|
dhopt_buff[dhopt_size++] = 0;
|
|
memcpy(dhopt_buff + dhopt_size, fqdn_buff, strlen(fqdn_buff));
|
|
dhopt_size += strlen(fqdn_buff);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Builds DHCP end of option on dhopt_buff
|
|
*/
|
|
int build_optioneof()
|
|
{
|
|
dhopt_buff[dhopt_size++] = 0xff;
|
|
return 0;
|
|
}
|
|
|
|
static void vlanize(struct vlan_eth_hdr *vhdr) {
|
|
if (!vlan)
|
|
return;
|
|
vhdr->vlan_len = vhdr->vlan_tpi;
|
|
vhdr->vlan_tpi = htons(ETHERTYPE_VLAN);
|
|
vhdr->vlan_priority_c_vid = htons(vlan);
|
|
}
|
|
|
|
/*
|
|
* Build DHCP packet. Packet type is passed as argument
|
|
*/
|
|
int build_dhpacket(int pkt_type)
|
|
{
|
|
u_int32_t dhcp_packet_size = dhcp_hdr_size + dhopt_size;
|
|
if(!dhcp_release_flag) {
|
|
u_char dmac_tmp[ETHER_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
|
memcpy(dmac, dmac_tmp, ETHER_ADDR_LEN);
|
|
}
|
|
|
|
struct vlan_eth_hdr *vhdr = (struct vlan_eth_hdr *)dhcp_packet_send;
|
|
memcpy(vhdr->vlan_dhost, dmac, ETHER_ADDR_LEN);
|
|
memcpy(vhdr->vlan_shost, dhmac, ETHER_ADDR_LEN);
|
|
vhdr->vlan_tpi = htons(ETHERTYPE_IP);
|
|
vlanize(vhdr);
|
|
|
|
//print_buff(dhcp_packet_disc, sizeof(struct ethernet_hdr));
|
|
|
|
if (padding_flag && dhcp_packet_size < MINIMUM_PACKET_SIZE) {
|
|
memset(dhopt_buff + dhopt_size, 0, MINIMUM_PACKET_SIZE - dhcp_packet_size);
|
|
dhopt_size += MINIMUM_PACKET_SIZE - dhcp_packet_size;
|
|
}
|
|
|
|
struct iphdr *iph = (struct iphdr *)(dhcp_packet_send + l2_hdr_size);
|
|
iph->version = 4;
|
|
iph->ihl = 5;
|
|
iph->tos = l3_tos;
|
|
iph->tot_len = htons(l3_hdr_size + l4_hdr_size + dhcp_hdr_size + dhopt_size);
|
|
iph->id = 0;
|
|
iph->frag_off = 0;
|
|
iph->ttl = 64;
|
|
iph->protocol = 17;
|
|
iph->check = 0; // Filled later;
|
|
|
|
switch (pkt_type) {
|
|
case DHCP_MSGDISCOVER:
|
|
case DHCP_MSGREQUEST:
|
|
if (unicast_flag)
|
|
iph->saddr = unicast_ip_address;
|
|
else
|
|
iph->saddr = inet_addr("0.0.0.0");
|
|
iph->daddr = inet_addr(server_addr);
|
|
break;
|
|
|
|
case DHCP_MSGRELEASE:
|
|
iph->saddr = option50_ip; //inet_addr("0.0.0.0");
|
|
iph->daddr = server_id; //inet_addr("255.255.255.255");
|
|
break;
|
|
}
|
|
iph->check = ipchksum((u_int16_t *)(dhcp_packet_send + l2_hdr_size), iph->ihl << 1);
|
|
|
|
struct udphdr *uh = (struct udphdr *) (dhcp_packet_send + l2_hdr_size + l3_hdr_size);
|
|
uh->source = htons(port + 1);
|
|
uh->dest = htons(port);
|
|
u_int16_t l4_proto = 17;
|
|
u_int16_t l4_len = (l4_hdr_size + dhcp_hdr_size + dhopt_size);
|
|
uh->len = htons(l4_len);
|
|
uh->check = 0; /* UDP checksum will be done after dhcp header*/
|
|
|
|
struct dhcpv4_hdr *dhpointer = (struct dhcpv4_hdr *)(dhcp_packet_send + l2_hdr_size + l3_hdr_size + l4_hdr_size);
|
|
dhpointer->dhcp_opcode = DHCP_REQUEST;
|
|
dhpointer->dhcp_htype = ARPHRD_ETHER;
|
|
dhpointer->dhcp_hlen = ETHER_ADDR_LEN;
|
|
dhpointer->dhcp_hopcount = 0;
|
|
dhpointer->dhcp_xid = htonl(dhcp_xid);
|
|
dhpointer->dhcp_secs = 0;
|
|
dhpointer->dhcp_flags = bcast_flag;
|
|
dhpointer->dhcp_yip = 0;
|
|
if (unicast_flag)
|
|
dhpointer->dhcp_cip = unicast_ip_address;
|
|
else
|
|
dhpointer->dhcp_cip = 0;
|
|
|
|
switch (pkt_type) {
|
|
case DHCP_MSGDISCOVER:
|
|
dhpointer->dhcp_sip = 0;
|
|
break;
|
|
|
|
case DHCP_MSGRELEASE:
|
|
dhpointer->dhcp_cip = option50_ip;
|
|
dhpointer->dhcp_sip = 0;
|
|
break;
|
|
|
|
case DHCP_MSGREQUEST:
|
|
dhpointer->dhcp_sip = server_id;
|
|
break;
|
|
}
|
|
dhpointer->dhcp_gip = inet_addr(giaddr);
|
|
memcpy(dhpointer->dhcp_chaddr, dhmac, ETHER_ADDR_LEN);
|
|
/*dhpointer->dhcp_sname
|
|
dhpointer->dhcp_file*/
|
|
dhpointer->dhcp_magic = htonl(DHCP_MAGIC);
|
|
|
|
/* DHCP option buffer is copied here to DHCP packet */
|
|
u_char *dhopt_pointer = (u_char *)(dhcp_packet_send + l2_hdr_size + l3_hdr_size + l4_hdr_size + dhcp_hdr_size);
|
|
memcpy(dhopt_pointer, dhopt_buff, dhopt_size);
|
|
|
|
/* UDP checksum is done here */
|
|
uh->check = l4_sum((u_int16_t *) (dhcp_packet_send + l2_hdr_size + l3_hdr_size), ((dhcp_hdr_size + dhopt_size + l4_hdr_size) / 2), (u_int16_t *)&iph->saddr, (u_int16_t *)&iph->daddr, htons(l4_proto), htons(l4_len));
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* build packet - Builds ARP reply and ICMP reply packets
|
|
*/
|
|
int build_packet(int pkt_type)
|
|
{
|
|
bzero(arp_icmp_reply, sizeof(arp_icmp_reply));
|
|
if(pkt_type == ARP_SEND) {
|
|
struct vlan_eth_hdr *vhdr = (struct vlan_eth_hdr *)arp_icmp_reply;
|
|
memcpy(vhdr->vlan_dhost, vlan_hg->vlan_shost, ETHER_ADDR_LEN);
|
|
memcpy(vhdr->vlan_shost, dhmac, ETHER_ADDR_LEN);
|
|
vhdr->vlan_tpi = htons(ETHERTYPE_ARP);
|
|
vlanize(vhdr);
|
|
|
|
struct arp_hdr *arph = (struct arp_hdr *)(arp_icmp_reply + l2_hdr_size);
|
|
arph->ar_hrd = htons(ARPHRD_ETHER);
|
|
arph->ar_pro = htons(ETHERTYPE_IP);
|
|
arph->ar_hln = ETHER_ADDR_LEN;
|
|
arph->ar_pln = IP_ADDR_LEN;
|
|
arph->ar_op = htons(ARPOP_REPLY);
|
|
u_int32_t ip_addr_tmp;
|
|
ip_addr_tmp = htonl(ip_address);
|
|
memcpy(arph->sender_mac, dhmac, ETHER_ADDR_LEN);
|
|
memcpy(arph->sender_ip, (u_char *)&ip_addr_tmp, IP_ADDR_LEN);
|
|
memcpy(arph->target_mac, arp_hg->sender_mac, ETHER_ADDR_LEN);
|
|
memcpy(arph->target_ip, arp_hg->sender_ip, IP_ADDR_LEN);
|
|
} else if(ICMP_SEND) {
|
|
struct vlan_eth_hdr *vhdr = (struct vlan_eth_hdr *)arp_icmp_reply;
|
|
memcpy(vhdr->vlan_dhost, vlan_hg->vlan_shost, ETHER_ADDR_LEN);
|
|
memcpy(vhdr->vlan_shost, dhmac, ETHER_ADDR_LEN);
|
|
vhdr->vlan_tpi = htons(ETHERTYPE_IP);
|
|
vlanize(vhdr);
|
|
|
|
//print_buff(dhcp_packet_request, sizeof(struct ethernet_hdr));
|
|
|
|
struct iphdr *iph = (struct iphdr *)(arp_icmp_reply + l2_hdr_size);
|
|
iph->version = 4;
|
|
iph->ihl = 5;
|
|
iph->tos = l3_tos;
|
|
iph->tot_len = 0; /* Filled later */
|
|
iph->id = 0; /* (iph_g->id + 5000); */
|
|
iph->frag_off = 0;
|
|
iph->ttl = 128;
|
|
iph->protocol = 1;
|
|
iph->check = 0; // Filled later;
|
|
iph->saddr = htonl(ip_address);
|
|
iph->daddr = iph_g->saddr;
|
|
/* iph->daddr = inet_addr("255.255.255.255"); */
|
|
|
|
struct icmp_hdr *ich = (struct icmp_hdr *)(arp_icmp_reply + l2_hdr_size + l3_hdr_size);
|
|
ich->icmp_type = ICMP_ECHOREPLY;
|
|
ich->icmp_code = 0;
|
|
ich->icmp_sum = 0;
|
|
ich->id = icmp_hg->id;
|
|
ich->seq = icmp_hg->seq;
|
|
icmp_len = (ntohs(iph_g->tot_len) - (iph_g->ihl << 2) - ICMP_H);
|
|
memcpy((((u_char *)&ich->seq) + 1), (((u_char *)&icmp_hg->seq) +1), (icmp_len + 1));
|
|
iph->tot_len = htons((l3_hdr_size + ICMP_H + icmp_len));
|
|
iph->check = ipchksum((u_int16_t *)(arp_icmp_reply + l2_hdr_size), iph->ihl << 1);
|
|
ich->icmp_sum = icmpchksum((u_int16_t *)(arp_icmp_reply + l2_hdr_size + l3_hdr_size), ((icmp_len + ICMP_H) / 2));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Checks whether received packet is DHCP offer/ACK/NACK/ARP/ICMP
|
|
* and retunrs the received packet type
|
|
*/
|
|
static int check_packet(int pkt_type, int len)
|
|
{
|
|
int ret;
|
|
|
|
if (len < 40)
|
|
return UNKNOWN_PACKET;
|
|
|
|
if(pkt_type == DHCP_MSGOFFER || pkt_type == DHCP_MSGACK) {
|
|
if (map_all_layer_ptr(pkt_type))
|
|
return UNKNOWN_PACKET;
|
|
|
|
if (vlan) {
|
|
if (ntohs(vlan_hg->vlan_priority_c_vid) != vlan || ntohs(vlan_hg->vlan_tpi) != ETHERTYPE_VLAN)
|
|
return UNKNOWN_PACKET;
|
|
}
|
|
|
|
if (dhopt_pointer_g > dhcp_packet_recv + len)
|
|
return UNKNOWN_PACKET;
|
|
|
|
if (iph_g->protocol != 17 || uh_g->source != htons(port) || uh_g->dest != htons(port + 1))
|
|
return UNKNOWN_PACKET;
|
|
if (htonl(dhcph_g->dhcp_xid) != dhcp_xid)
|
|
return UNKNOWN_PACKET;
|
|
|
|
if(*(dhopt_pointer_g + 2) == DHCP_MSGOFFER)
|
|
ret = DHCP_OFFR_RCVD;
|
|
else if(*(dhopt_pointer_g + 2) == DHCP_MSGACK)
|
|
ret = DHCP_ACK_RCVD;
|
|
else if(*(dhopt_pointer_g + 2) == DHCP_MSGNACK)
|
|
ret = DHCP_NAK_RCVD;
|
|
else
|
|
return UNKNOWN_PACKET;
|
|
|
|
read_options(len);
|
|
|
|
return ret;
|
|
|
|
} else if(pkt_type == ARP_ICMP_RCV) {
|
|
if (map_all_layer_ptr(ARP_MAP))
|
|
return UNKNOWN_PACKET;
|
|
|
|
if(!vlan && vlan_hg->vlan_tpi != htons(ETHERTYPE_ARP))
|
|
goto not_arp;
|
|
if (vlan && (ntohs(vlan_hg->vlan_priority_c_vid) != vlan
|
|
|| vlan_hg->vlan_len != htons(ETHERTYPE_ARP)))
|
|
goto not_arp;
|
|
|
|
if ((unsigned char *) arp_hg + sizeof(*arp_hg) > arp_icmp_packet + len)
|
|
return UNKNOWN_PACKET;
|
|
|
|
if((ntohs(arp_hg->ar_op)) == ARPOP_REQUEST && htonl(ip_address) == arp_hg->target_ip32) {
|
|
if (!quiet)
|
|
printf("Arp request received\n");
|
|
return ARP_RCVD;
|
|
}
|
|
return UNKNOWN_PACKET;
|
|
|
|
not_arp:
|
|
if (map_all_layer_ptr(ICMP_MAP))
|
|
return UNKNOWN_PACKET;
|
|
|
|
if(!vlan && vlan_hg->vlan_tpi != htons(ETHERTYPE_IP))
|
|
goto not_icmp;
|
|
if (vlan && (ntohs(vlan_hg->vlan_priority_c_vid) != vlan
|
|
|| vlan_hg->vlan_len != htons(ETHERTYPE_IP)))
|
|
goto not_icmp;
|
|
|
|
if ((unsigned char *) icmp_hg + sizeof(*icmp_hg) > arp_icmp_packet + len)
|
|
return -1;
|
|
|
|
if (iph_g->protocol != 1)
|
|
goto not_icmp;
|
|
|
|
if(ip_address == ntohl(iph_g->daddr) && icmp_hg->icmp_type == ICMP_ECHO)
|
|
return ICMP_RCVD;
|
|
|
|
not_icmp:
|
|
return UNKNOWN_PACKET;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Sets the server ip and offerered ip on serv_id, option50_ip
|
|
* from the DHCP offer packet
|
|
*/
|
|
int set_serv_id_opt50()
|
|
{
|
|
option50_ip = dhcph_g->dhcp_yip;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void read_options(int len) {
|
|
unsigned char *p = dhopt_pointer_g,
|
|
*end = dhcp_packet_recv + len;
|
|
|
|
while(*(p) != DHCP_END && p < end) {
|
|
if ((p + *(p + 1) + 2) > end)
|
|
break;
|
|
|
|
switch(*(p)) {
|
|
case DHCP_SERVIDENT:
|
|
server_id = *(u_int32_t *)(p + 2);
|
|
break;
|
|
|
|
case DHCP_LEASETIME:
|
|
lease_time = ntohl(*(u_int32_t *)(p + 2));
|
|
break;
|
|
}
|
|
|
|
p = p + *(p + 1) + 2;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Prints the DHCP offer/ack info
|
|
*/
|
|
int print_dhinfo(int pkt_type)
|
|
{
|
|
u_int16_t tmp;
|
|
unsigned char *p;
|
|
|
|
if(pkt_type == DHCP_MSGOFFER) {
|
|
printf("\nDHCP offer details\n");
|
|
printf("----------------------------------------------------------\n");
|
|
printf("DHCP offered IP from server - %s\n", get_ip_str(dhcph_g->dhcp_yip));
|
|
printf("Next server IP(Probably TFTP server) - %s\n", get_ip_str(dhcph_g->dhcp_sip));
|
|
printf("Boot File Name (Probably used in PXE) - %s\n", dhcph_g->dhcp_file);
|
|
if(dhcph_g->dhcp_gip) {
|
|
printf("DHCP Relay agent IP - %s\n", get_ip_str(dhcph_g->dhcp_gip));
|
|
}
|
|
} else if( pkt_type == DHCP_MSGACK) {
|
|
printf("\nDHCP ack details\n");
|
|
printf("----------------------------------------------------------\n");
|
|
printf("DHCP offered IP from server - %s\n", get_ip_str(dhcph_g->dhcp_yip));
|
|
printf("Next server IP(Probably TFTP server) - %s\n", get_ip_str(dhcph_g->dhcp_sip));
|
|
printf("Boot File Name (Probably used in PXE) - %s\n", dhcph_g->dhcp_file);
|
|
if(dhcph_g->dhcp_gip) {
|
|
printf("DHCP Relay agent IP - %s\n", get_ip_str(dhcph_g->dhcp_gip));
|
|
}
|
|
}
|
|
|
|
p = dhopt_pointer_g;
|
|
while(*(p) != DHCP_END) {
|
|
|
|
switch(*(p)) {
|
|
case DHCP_SERVIDENT:
|
|
printf("DHCP server - %s\n", get_ip_str(*(u_int32_t *)(p + 2)));
|
|
break;
|
|
|
|
case DHCP_LEASETIME:
|
|
printf("Lease time - %d Days %d Hours %d Minutes\n", \
|
|
(ntohl(*(u_int32_t *)(p + 2))) / (3600 * 24), \
|
|
((ntohl(*(u_int32_t *)(p + 2))) % (3600 * 24)) / 3600, \
|
|
(((ntohl(*(u_int32_t *)(p + 2))) % (3600 * 24)) % 3600) / 60);
|
|
break;
|
|
|
|
case DHCP_SUBNETMASK:
|
|
printf("Subnet mask - %s\n", get_ip_str(*(u_int32_t *)(p + 2)));
|
|
break;
|
|
|
|
case DHCP_ROUTER:
|
|
for(tmp = 0; tmp < (*(p + 1) / 4); tmp++) {
|
|
printf("Router/gateway - %s\n", get_ip_str(*(u_int32_t *)(p + 2 + (tmp * 4))));
|
|
}
|
|
break;
|
|
|
|
case DHCP_DNS:
|
|
for(tmp = 0; tmp < ((*(p + 1)) / 4); tmp++) {
|
|
printf("DNS server - %s\n", get_ip_str(*(u_int32_t *)(p + 2 + (tmp * 4))));
|
|
}
|
|
break;
|
|
|
|
case DHCP_FQDN:
|
|
{
|
|
/* Minus 3 beacause 3 bytes are used to flags, rcode1 and rcode2 */
|
|
u_int32_t size = (u_int32_t)*(p + 1) - 3;
|
|
/* Plus 2 to add string terminator */
|
|
u_char fqdn_client_name[size + 1];
|
|
|
|
/* Plus 5 to reach the beginning of the string */
|
|
memcpy(fqdn_client_name, p + 5, size);
|
|
fqdn_client_name[size] = '\0';
|
|
|
|
printf("FQDN Client name - %s\n", fqdn_client_name);
|
|
}
|
|
}
|
|
|
|
p = p + *(p + 1) + 2;
|
|
}
|
|
|
|
printf("----------------------------------------------------------\n\n");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Function maps all pointers on OFFER/ACK/ARP/ICMP packet
|
|
*/
|
|
static int map_all_layer_ptr(int pkt_type)
|
|
{
|
|
switch (pkt_type) {
|
|
case DHCP_MSGOFFER:
|
|
case DHCP_MSGACK:
|
|
vlan_hg = (struct vlan_eth_hdr *)dhcp_packet_recv;
|
|
iph_g = (struct iphdr *)(dhcp_packet_recv + l2_hdr_size);
|
|
uh_g = (struct udphdr *)(dhcp_packet_recv + l2_hdr_size + l3_hdr_size);
|
|
dhcph_g = (struct dhcpv4_hdr *)(dhcp_packet_recv + l2_hdr_size + l3_hdr_size + l4_hdr_size);
|
|
dhopt_pointer_g = (u_int8_t *)(dhcp_packet_recv + l2_hdr_size + l3_hdr_size + l4_hdr_size + sizeof(struct dhcpv4_hdr));
|
|
break;
|
|
case ARP_MAP:
|
|
vlan_hg = (struct vlan_eth_hdr *)arp_icmp_packet;
|
|
arp_hg = (struct arp_hdr *)(arp_icmp_packet + l2_hdr_size);
|
|
break;
|
|
case ICMP_MAP:
|
|
vlan_hg = (struct vlan_eth_hdr *)arp_icmp_packet;
|
|
iph_g = (struct iphdr *)(arp_icmp_packet + l2_hdr_size);
|
|
icmp_hg = (struct icmp_hdr *)(arp_icmp_packet + l2_hdr_size + l3_hdr_size);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Logs DHCP info to the log file
|
|
* This file is used later for DHCP release
|
|
*/
|
|
int log_dhinfo()
|
|
{
|
|
int dh_file;
|
|
struct dhcp_status ds;
|
|
|
|
dh_file = open(dhmac_fname, O_WRONLY | O_CREAT | O_TRUNC, 0666);
|
|
if (dh_file < 0)
|
|
critical("Error opening file: %m");
|
|
|
|
memset(&ds, 0, sizeof(ds));
|
|
|
|
ds.magic = STATUS_MAGIC;
|
|
memcpy(&ds.client_mac, dhmac, sizeof(ds.client_mac));
|
|
if (dhcph_g && vlan_hg) {
|
|
ds.have_ip = 1;
|
|
ds.client_ip = dhcph_g->dhcp_yip;
|
|
ds.server_ip = server_id;
|
|
memcpy(&ds.server_mac, vlan_hg->vlan_shost, sizeof(ds.server_mac));
|
|
time(&ds.acquired_at);
|
|
ds.lease_time = lease_time;
|
|
}
|
|
|
|
if (ip_listen_flag)
|
|
ds.listen_pid = getpid();
|
|
|
|
if (write(dh_file, &ds, sizeof(ds)) != sizeof(ds))
|
|
critical("Error writing to file: %m");
|
|
|
|
close(dh_file);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Takes the DHCP info from log file and removes it(unlinks it)
|
|
* Used for DHCP release
|
|
*/
|
|
int get_dhinfo()
|
|
{
|
|
int dh_file;
|
|
struct dhcp_status ds;
|
|
|
|
dh_file = open(dhmac_fname, O_RDONLY);
|
|
if(dh_file < 0)
|
|
return errno;
|
|
|
|
if (read(dh_file, &ds, sizeof(ds)) != sizeof(ds))
|
|
return errno;
|
|
|
|
memcpy(dhmac, ds.client_mac, sizeof(dhmac));
|
|
if (ds.have_ip) {
|
|
option50_ip = ds.client_ip;
|
|
server_id = ds.server_ip;
|
|
memcpy(dmac, ds.server_mac, sizeof(dmac));
|
|
if (ds.listen_pid)
|
|
kill(ds.listen_pid, SIGKILL); /* XXX what about promisc mode? */
|
|
option51_lease_time = ds.lease_time;
|
|
lease_expires_at = ds.acquired_at + ds.lease_time;
|
|
}
|
|
|
|
close(dh_file);
|
|
return 0;
|
|
}
|
|
|
|
char *get_ip_str(u_int32_t ip)
|
|
{
|
|
struct in_addr src;
|
|
src.s_addr = ip;
|
|
inet_ntop(AF_INET, ((struct sockaddr_in *) &src),
|
|
ip_str, sizeof(ip_str));
|
|
return ip_str;
|
|
}
|
|
|
|
void critical (const char *fmt, ...) {
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
if (nagios_flag) {
|
|
printf("CRITICAL: ");
|
|
vprintf(fmt, ap);
|
|
}
|
|
else {
|
|
vfprintf(stderr, fmt, ap);
|
|
fprintf(stderr, "\n");
|
|
}
|
|
exit(2);
|
|
}
|