dhcp query libnet

/* Ultra-fast dhcp discovery
*
* $Id: dhcp_discover.c,v 2004/01/03 20:31:01 mike Exp $
* dhd.c,v LatinSuD 2006
*
* Compile:
* gcc -o dhd dhd.c -lnet -lpcap -lpthread
*
* Example usage:
* dhd
* dhd eth0
*
* Example output:
* #0:
* SERVER-MAC: 00:01:38:12:34:56
* IP: 192.168.1.1
* CLIENT-MAC: 00:0e:35:ab:cd:ef
* IP: 192.168.1.40
*
* >> "0 "0
*
* Dhd injects 10 dhcp requests on interface. In this case,
* it has been received a response, numbered as "0".
* Later, response 0 was received twice more.
*/

#define DHD_VERSION "2.1"

/*
* Changes:
* 2.1 (2007-04-07):
* - Identify equivalent packets with different time fields
* - Add domain-suffix field
* - Minor cleanup & enhanced usage help
*
* 2.0 (2006-??-??):
* - Beautify printf
*
* libnet 1.1
* Build a DHCP discover packet
* To view: /usr/sbin/tcpdump -vvvvven -s 4096 'port 67 or port 68'
*
* Copyright (c) 1998 - 2004 Mike D. Schiffman
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/

#include
#include #include
#include #include
#include
#include #include

/* Ethernet addresses are 6 bytes */
#ifndef ETHER_ADDR_LEN
#define ETHER_ADDR_LEN 6
#endif

/* Ethernet header */
struct sniff_ethernet {
u_char ether_dhost[ETHER_ADDR_LEN]; /* Destination host address */
u_char ether_shost[ETHER_ADDR_LEN]; /* Source host address */
u_short ether_type; /* IP? ARP? RARP? etc */
};

/* IP header */
struct sniff_ip {
u_char ip_vhl; /* version << 4 | header length >> 2 */
u_char ip_tos; /* type of service */
u_short ip_len; /* total length */
u_short ip_id; /* identification */
u_short ip_off; /* fragment offset field */
u_char ip_ttl; /* time to live */
u_char ip_p; /* protocol */
u_short ip_sum; /* checksum */
struct in_addr ip_src, ip_dst; /* source and dest address */
};

typedef struct {
u_short uh_sport; /* source port */
u_short uh_dport; /* destination port */

u_short len;
u_short chk;

unsigned char type;
char a;
char b;
char c;
u_short d;
u_short e;
} sniff_udp;

/* ethernet headers are always exactly 14 bytes */
int SIZE_ETHERNET = 14;
#define IP_HL(ip) (((ip)->ip_vhl) & 0x0f)
#define IP_V(ip) (((ip)->ip_vhl) >> 4)

const struct sniff_ethernet *ethernet; /* The ethernet header */
struct sniff_ip *ip; /* The IP header */
const struct sniff_tcp *tcp; /* The TCP header */
const char *payload; /* Packet payload */

pthread_t sniffer, injector;

void print_mac(unsigned char *packet, int moffset)
{
int i;
if (moffset > 80)
exit(0);
for (i = 0; i < 6; i++) printf("%02x%c", packet[i + moffset], (i < 5) ? ':' : '\0'); } void print_ip(unsigned char *ip, int offset) { int i; for (i = 0; i < 4; i++) { printf("%d", ip[i + offset]); if (i < 3) printf("."); } } #define DNS 0 #define MASK 1 #define CIP 2 #define SIP 3 #define GW 4 int didCR = 1; #define HASH 8 #define PCACHE 256 int h_pack[PCACHE]; int n_pack = 0; unsigned int miChecksum(u_char * packet, int len) { int i; int s1 = 0; int s2 = 0; u_char * ptr=packet; while (ptr - packet < len - 1) { if (ptr - packet + ptr[1] + 2 > len)
break;

// ignore time dependent fields
if (ptr[0]==2 || ptr[0]==51 || ptr[0]==58 || ptr[0]==59) {
ptr += 2 + ptr[1];
continue;
}

for (i = 0; i < ptr[1]+2; i++) { s1 += ptr[i] * i; s2 += ptr[i]; } ptr += 2 + ptr[1]; } return s1 + (s2 << 16); } // Caches known packets, so they are printed only once int cache_packet(u_char * packet, int len, int *num) { int i; int hash = miChecksum(packet, len); for (i = 0; i < n_pack; i++) { if (h_pack[i] == hash) { *num = i; return 0; } } if (n_pack < PCACHE) { h_pack[n_pack] = hash; *num = n_pack; n_pack++; } return -1; } // Pcap callback that does all the work void got_packet(u_char * args, const struct pcap_pkthdr *header, const u_char * packet) { sniff_udp *udp; int i; uint size_ip; u_char *ptr; int idx; int dijoIp = 0; ethernet = (struct sniff_ethernet *) (packet); ip = (struct sniff_ip *) (packet + SIZE_ETHERNET); size_ip = (IP_HL(ip)) * 4; udp = (sniff_udp *) (packet + SIZE_ETHERNET + size_ip); if (*(((char *) udp) + 8) != 2) return; ptr = ((unsigned char *) udp) + 0xf8; if (cache_packet(ptr, header->len - (ptr - packet), &idx) == 0) {
// packet already in cache, show only reference number
if (didCR)
printf("\n >>");
printf(" \"%d ", idx);
didCR = 0;
return;
} else {
// start dissecting packet
puts("");
if (!didCR)
puts("");
printf(" #%d:\n", idx);
}

printf("\tSERVER-MAC: \x1b[1m");
print_mac((unsigned char *) packet, 6);
printf("\x1b[0m\n");

// print only known interesting fields
while (ptr - packet < header->len - 1) {
if (ptr - packet + ptr[1] + 2 > header->len)
break;

if (ptr[0] == 6) {
printf("\tDNS: \x1b[1m");
for (i = 0; i < (ptr[1] / 4); i++) { if (i != 0) printf(", "); print_ip(ptr, 2 + 4 * i); } printf("\x1b[0m\n"); } else if (ptr[0] == 1) { printf("\t MASK: \x1b[1m"); print_ip(ptr, 2); printf("\x1b[0m\n"); } else if (ptr[0] == 54) { printf("\t IP: \x1b[1m"); print_ip(ptr, 2); printf("\x1b[0m\n"); printf("\tCLIENT-MAC: \x1b[1m"); print_mac((unsigned char *) udp, 0x24); printf("\x1b[0m\n"); printf("\t IP: \x1b[1m"); print_ip((unsigned char *) udp, 0x18); printf("\x1b[0m\n"); dijoIp = 1; } else if (ptr[0] == 15) { printf("\tDOMAIN-SUFFIX: \x1b[1m"); for (i=0; i=0x20 && ptr[2+i]<0x7F)?ptr[2+i]:' '); printf("\x1b[0m\n"); } else if (ptr[0] == 3) { printf("\tGW: \x1b[1m"); print_ip(ptr, 2); printf("\x1b[0m\n"); } else if (ptr[0] == 53) { if (ptr[2] != 2) break; } ptr += 2 + ptr[1]; } if (!dijoIp) { printf("\tCLIENT-IP: \x1b[1m"); print_ip((unsigned char *) udp, 0x18); printf("\x1b[0m\n"); } didCR = 1; } void usage(char *prog) { fprintf(stderr, "DHD " DHD_VERSION " - A fast DHcp Discover tool.\nUsage: dhd [INTERFACE]\n\tIf no INTERFACE is given, it will write on all available interfaces and sniff on any.\n"); exit(1); } typedef struct { char *intf; libnet_t *l; pcap_t *pcap; struct libnet_ether_addr *ethaddr; } context; struct libnet_stats ls; u_char *options; void init_libnet(context * c) { char errbuf[LIBNET_ERRBUF_SIZE]; int i; c->l = libnet_init(LIBNET_LINK, // injection type
c->intf, // network interface
errbuf); // errbuf
if (!c->l) {
fprintf(stderr, "libnet_init: %s", errbuf);
exit(EXIT_FAILURE);
}

if ((c->ethaddr = libnet_get_hwaddr(c->l)) == NULL) {
fprintf(stderr, "libnet_get_hwaddr: %s\n", libnet_geterror(c->l));
exit(EXIT_FAILURE);
}
for (i = 0; i < 6; i++) { fprintf(stderr, "%02x", c->ethaddr->ether_addr_octet[i]);
if (i != 5) {
fprintf(stderr, ":");
}
}

}

void init_packet(context * c)
{

u_long options_len, orig_len;
int i;

libnet_ptag_t t;
libnet_ptag_t ip;
libnet_ptag_t udp;
libnet_ptag_t dhcp;

u_char options_req[] =
{ LIBNET_DHCP_SUBNETMASK, LIBNET_DHCP_BROADCASTADDR,
LIBNET_DHCP_TIMEOFFSET, LIBNET_DHCP_ROUTER, LIBNET_DHCP_DOMAINNAME,
LIBNET_DHCP_DNS,
LIBNET_DHCP_HOSTNAME
};
u_char enet_dst[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
u_char *tmp;

// build options packet
i = 0;
options_len = 3; // update total payload size

// we are a discover packet
options = malloc(3);
options[i++] = LIBNET_DHCP_MESSAGETYPE; // type
options[i++] = 1; // len
options[i++] = LIBNET_DHCP_MSGDISCOVER; // data

orig_len = options_len;
options_len += sizeof(options_req) + 2; // update total payload size

// workaround for realloc on old machines
// options = realloc(options, options_len); // resize options buffer
tmp = malloc(options_len);
memcpy(tmp, options, orig_len);
free(options);
options = tmp;

// we are going to request some parameters
options[i++] = LIBNET_DHCP_PARAMREQUEST; // type
options[i++] = sizeof(options_req); // len
memcpy(options + i, options_req, sizeof(options_req)); // data
i += sizeof(options_req);

// end our options packet
// workaround for realloc on old machines
// options = realloc(options, options_len); // resize options buffer
orig_len = options_len;
options_len += 1;
tmp = malloc(options_len);
memcpy(tmp, options, orig_len);
free(options);
options = tmp;
options[i++] = LIBNET_DHCP_END;

// make sure we are at least the minimum length, if not fill
// this could go in libnet, but we will leave it in the app for now
if (options_len + LIBNET_DHCPV4_H < LIBNET_BOOTP_MIN_LEN) { orig_len = options_len; options_len = LIBNET_BOOTP_MIN_LEN - LIBNET_DHCPV4_H; // workaround for realloc on old machines // options = realloc(options, options_len); tmp = malloc(options_len); memcpy(tmp, options, orig_len); free(options); options = tmp; memset(options + i, 0, options_len - i); } // the goodies are here dhcp = libnet_build_dhcpv4(LIBNET_DHCP_REQUEST, // opcode 1, // hardware type 6, // hardware address length 0, // hop count 0xdeadbeee, // transaction id 0, // seconds since bootstrap 0x8000, // flags 0, // client ip 0, // your ip 0, // server ip 0, // gateway ip c->ethaddr->ether_addr_octet, // client hardware addr
//unamac, // client hardware addr
NULL, // server host name
NULL, // boot file
options, // dhcp options stuck in payload since it is dynamic
options_len, // length of options
c->l, // libnet handle
0); // libnet id

// wrap it
udp = libnet_build_udp(68, // source port
67, // destination port
LIBNET_UDP_H + LIBNET_DHCPV4_H + options_len, // packet size
0, // checksum
NULL, // payload
0, // payload size
c->l, // libnet handle
0); // libnet id

// hook me up with some ipv4
ip = libnet_build_ipv4(LIBNET_IPV4_H + LIBNET_UDP_H + LIBNET_DHCPV4_H + options_len, // length
0x10, // TOS
0, // IP ID
0, // IP Frag
16, // TTL
IPPROTO_UDP, // protocol
0, // checksum
inet_addr("0.0.0.0"), // src ip
inet_addr("255.255.255.255"), // destination ip
NULL, // payload
0, // payload size
c->l, // libnet handle
0); // libnet id

// we can just autobuild since we arent doing anything tricky
t = libnet_autobuild_ethernet(enet_dst, // ethernet destination
ETHERTYPE_IP, // protocol type
c->l); // libnet handle
}

int init_pcap(char *intf)
{
char errbuf[PCAP_ERRBUF_SIZE];
pcap_t *pcap;
struct bpf_program fp; /* The compiled filter expression */
char filter_exp[] = "udp and (port 67 or port 68)"; /* The filter expression */
bpf_u_int32 net = 0; /* The IP of our sniffing device */
setbuf(stdout, 0);

//pcap = pcap_open_offline("sample.cap", errbuf);
pcap = pcap_open_live(intf, BUFSIZ, 0, 1000, errbuf);
if (pcap == NULL) {
fprintf(stderr, "Couldn't open device %s: %s\n", "any", errbuf);
return (2);
}

if (pcap_compile(pcap, &fp, filter_exp, 0, net) == -1) {
fprintf(stderr, "Couldn't parse filter %s: %s\n", filter_exp,
pcap_geterr(pcap));
return (2);
}
if (pcap_setfilter(pcap, &fp) == -1) {
fprintf(stderr, "Couldn't install filter %s: %s\n", filter_exp,
pcap_geterr(pcap));
return (2);
}
if (pcap_datalink(pcap) == DLT_LINUX_SLL)
SIZE_ETHERNET = 16;
//if (pcap_datalink (pcap) == DLT_EN10MB);

pcap_loop(pcap, -1, got_packet, (unsigned char *) errbuf);

return (0);
}

void inyecta(context * c)
{
// write to the wire
if (libnet_write(c->l) == -1) {
fprintf(stderr, " dhd: libnet_write: %s\n", strerror(errno));
}
}

void end_libnet(context * c)
{
libnet_destroy(c->l);

// free mem
free(options);

exit(0);
}

int get_datalink(char *intf)
{
int res;
pcap_t *handle;
char buf[8192];
handle = pcap_open_live(intf, 0, 0, 0, buf);
if (!handle) {
//printf("NOP SE PUDO ABRIR [%s] : %s\n", intf, buf);
return 0;
}
res = pcap_datalink(handle);
//printf(" :%s: ", pcap_datalink_val_to_description(res));
pcap_close(handle);
return res;
}

context ifaces[16];
int nIfaces = 0;

void init_iface(char * iface) {
if (get_datalink(iface) == DLT_EN10MB) {
ifaces[nIfaces].intf = strdup(iface);
fprintf(stderr, "Injecting on %s (",
ifaces[nIfaces].intf);
init_libnet(&ifaces[nIfaces]);
fprintf(stderr, ")\n");
init_packet(&ifaces[nIfaces]);
nIfaces++;
}
}

void init_all_ifaces()
{
FILE *f;
char *i1, *i2;
char buf[1024];
f = fopen("/proc/net/dev", "r");
if (!f)
return;

while (fgets(buf, 1023, f) && nIfaces<16) { i1 = buf; while (*i1 == ' ') i1++; i2 = i1; while (*i2 != '\0') { if (*i2 == ':') { *i2 = '\0'; if (strcmp(i1, "lo")) { init_iface(i1); } } i2++; } } } void *esnifa(void *data) { fprintf(stderr, "Sniffing on %s\n", (char *) data); init_pcap((char *) data); return NULL; } int main(int argc, char *argv[]) { int i, j, dormir; if (getuid() != 0) { fprintf(stderr, "Need to be root!\n"); return -1; } setbuf(stdout, 0); dormir = 100; // init if (argc == 1) { pthread_create(&sniffer, NULL, esnifa, "any"); init_all_ifaces(); } else if (argc == 2) { if (!strcmp(argv[1],"-h") || !strcmp(argv[1],"--help")) usage(argv[0]); pthread_create(&sniffer, NULL, esnifa, (void *) argv[1]); init_iface(argv[1]); } else { fprintf(stderr, "Only 1 interface can be specified!\n"); usage(argv[0]); } // inject all packets on all ifaces for (i = 0; i < 10; i++) { usleep(dormir * 1000); for (j = 0; j < nIfaces; j++) { inyecta(&ifaces[j]); } dormir = dormir + 100; } usleep(250000); if (!didCR) puts(""); for (j = 0; j < nIfaces; j++) end_libnet(&ifaces[j]); return 0; }