diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0d70cc9 --- /dev/null +++ b/Makefile @@ -0,0 +1,22 @@ +Cflag= +LDflag= + +all: dhclient + +dhclient: dhclient.o dhcp.o net.o + gcc $(LDflag) dhclient.o dhcp.o net.o -o dhclient + +dhclient.o: dhclient.c + gcc $(Cflag) -c dhclient.c + +dhcp.o: dhcp.c dhcp.h + gcc $(Cflag) -c dhcp.c + +net.o: net.c net.h + gcc $(Cflag) -c net.c + +clean: + rm -f *.o + +cleanall: clean + rm -f dhclient diff --git a/dhclient.c b/dhclient.c new file mode 100644 index 0000000..327d052 --- /dev/null +++ b/dhclient.c @@ -0,0 +1,157 @@ +#include "dhcp.h" +#include "net.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define BUFF_SIZE 1024 + +//http://aschauf.landshut.org/fh/linux/udp_vs_raw/ch01s03.html + +void main(int argc, char** argv) +{ + if(geteuid() != 0) + { + fprintf(stderr, "You need root permissions\n"); + exit(1); + } + + //TODO : Add a debug mode + char* interface; + if(argc > 1) + interface = argv[1]; + else + { + fprintf(stderr, "Please specify network interface to use\n"); + exit(1); + } + +// printf("Interface = %s\n", interface); + + int sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)); + + //Set receive timeout + struct timeval tv; + tv.tv_usec = 0; + tv.tv_sec = 10; //10 seconds in case of latency on the network + setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + + //retrieve ethernet NIC index and HW address + struct hw_eth_iface iface = find_iface(sock, interface); // TODO : Check interface existency, and print a list of possible NIC + + struct sockaddr_ll target; + memset(&target, 0, sizeof(target)); + target.sll_family = PF_PACKET; + target.sll_protocol = htons(ETH_P_IP); + target.sll_ifindex = iface.index; + target.sll_hatype = ARPHRD_ETHER; + target.sll_pkttype = PACKET_HOST; + target.sll_halen = ETH_ALEN; + + memset(target.sll_addr, 0xff, 6); + + char buffer[BUFF_SIZE]; + +// struct dhcp_pkt *dhcp = (struct dhcp_pkt*)(buffer + sizeof(struct udpheader) + sizeof(struct ipheader)); + struct dhcp_pkt dhcp; + int dhcp_len = build_dhcp_discover(&dhcp, iface.hw_addr, iface.addr_len); + + int len = build_ip4_udp_pkt(buffer, BUFF_SIZE, (unsigned char*)&dhcp, dhcp_len, "0.0.0.0", "255.255.255.255", 68, 67, IPPROTO_UDP); + + //Send the packet over the network + if(sendto(sock, buffer, len, 0, (struct sockaddr *)&target, sizeof(target)) < 0) + { + perror("Error while writing to socket"); + exit(1); + } + + //Now, wait for the server response, and read it + +receive: + memset(buffer, 0, BUFF_SIZE); + + //Read a packet + int read_len = recvfrom(sock, buffer, BUFF_SIZE, 0, NULL, NULL); + if(read_len <= 0) + { + perror("Cannot read"); + exit(1); + } + + struct ipheader *rip = (struct ipheader*) buffer; + struct udpheader *rudp = (struct udpheader*)(buffer + sizeof(struct ipheader)); + struct dhcp_pkt *rdhcp = (struct dhcp_pkt*)(buffer + sizeof(struct udpheader) + sizeof(struct ipheader)); + + //Check packet validity + // if dest port isn't our or packet is not a dhcp one, drop the packet + if(rip->iph_protocol != IPPROTO_UDP || rudp->udph_destport != htons(68) || !is_dhcp(rdhcp) || rdhcp->op != OP_BOOT_REPLY) + goto receive; + +// printf("Data Recieved, length = %d\n", read_len); + + //Find the IP attributed to us by the server + struct in_addr raddr; + raddr.s_addr = rdhcp->yi_addr; + + printf("IPADDR=%s\n", inet_ntoa(raddr)); + + //Now check DHCP options, and process them + struct dhcp_opt *opt = NULL; + int offs = 0; + opt = get_dhcp_option(rdhcp, &offs); + while(opt != NULL) + { +// printf("OPT FOUND offset = %d\n", offs); + switch(opt->id) + { + case OPTION_ROUTER: // If the option is the gateway address + if(opt->len == 4) + { + raddr.s_addr = char_to_ip(opt->values); + printf("GATEWAY=%s\n", inet_ntoa(raddr)); + } + break; + + case OPTION_SUBNET_MASK: // If the option is the netwmask + if(opt->len == 4) + { + raddr.s_addr = char_to_ip(opt->values); + printf("NETMASK=%s\n", inet_ntoa(raddr)); + } + break; + + case OPTION_DNS: // If option is the DNS addresses + if(opt->len % 4 == 0) + { + int i = 0; + printf("NAMESERVER="); + int max = opt->len / 4; + for(i = 0; i < max; i++) + { + raddr.s_addr = char_to_ip(opt->values + 4*i); + printf("%s", inet_ntoa(raddr)); + if(i < max - 1) + printf(","); + } + printf("\n"); + } + break; + + default: + break; + } + opt = get_dhcp_option(rdhcp, &offs); + } + + close(sock); + exit(0); +} diff --git a/dhcp.c b/dhcp.c new file mode 100644 index 0000000..1cad3d2 --- /dev/null +++ b/dhcp.c @@ -0,0 +1,72 @@ +#include "dhcp.h" + +#include +#include + +int build_dhcp_discover(struct dhcp_pkt* pkt, unsigned char* src_mac, int mac_len) +{ + memset(pkt, 0, sizeof(struct dhcp_pkt)); + pkt->op = OP_BOOT_REQUEST; + pkt->htype = HW_TYPE_ETHERNET; + pkt->hlen = HW_LENGTH_ETHERNET; + pkt->hops = 0x00; + pkt->xid = 0x3903f326; //TODO: Random transaction ID + pkt->secs = 0x0000; + pkt->flags = 0x0000; + pkt->ci_addr = 0x00000000; + pkt->yi_addr = 0x00000000; + pkt->si_addr = 0x00000000; + pkt->gi_addr = 0x00000000; + + // memcpy(pkt->cm_addr, src_mac, mac_len); // LINK problem + int i; + for(i = 0; i < mac_len; i++) + pkt->cm_addr[i] = src_mac[i]; + + pkt->magic = DHCP_MAGIC; + + //Add DHCP options + pkt->opt[0] = OPTION_DHCP_MESSAGE_TYPE; + pkt->opt[1] = 0x01; + pkt->opt[2] = VALUE_MESSAGE_DISCOVER; + + pkt->opt[3] = OPTION_PARAMETER_REQUEST_LIST; + pkt->opt[4] = 0x03; + pkt->opt[5] = OPTION_ROUTER; // Ask for gateway address + pkt->opt[6] = OPTION_SUBNET_MASK; // Ask for the netmask + pkt->opt[7] = OPTION_DNS; // ASK for DNS address + + pkt->opt[8] = DHCP_END; + + + //TODO : Use the same procedure to write options, than the one used to read options +// pkt->opt[0].id = 53; +// pkt->opt[0].len = 0x01; +// pkt->opt[0].values[0] = 0x01; +// +// pkt->opt[1].id = DHCP_END; + + return sizeof(struct dhcp_pkt); +} + +int is_dhcp(struct dhcp_pkt* pkt) +{ + // It's a DHCP packet if dhcp magic number is good + //TODO: check the packet length ? + return pkt->magic == DHCP_MAGIC; +} + +struct dhcp_opt* get_dhcp_option(struct dhcp_pkt *pkt, int *offset) +{ + if(pkt->opt[*offset] == 0x00 || pkt->opt[*offset] == DHCP_END) + return NULL; + // If the opt != end or != empty, cast the memory zone into a option struct, and return it + struct dhcp_opt* opt = (struct dhcp_opt*)&(pkt->opt[*offset]); + *offset += sizeof(struct dhcp_opt) + opt->len; + return opt; +} + +unsigned int char_to_ip(unsigned char* ip) +{ + return htonl(ip[0] << 24 | ip[1] << 16 | ip[2] << 8 | ip[3]); +} diff --git a/dhcp.h b/dhcp.h new file mode 100644 index 0000000..405d6a1 --- /dev/null +++ b/dhcp.h @@ -0,0 +1,96 @@ +#ifndef _DHCP_H +#define _DHCP_H + +#define DHCP_MAGIC htonl(0x63825363) + +#define DHCP_MIN_PACK_SIZE 240 + +#define OP_BOOT_REQUEST 0x01 +#define OP_BOOT_REPLY 0x02 + +#define HW_TYPE_ETHERNET 0x01 +#define HW_LENGTH_ETHERNET 0x06 + +//DHCP options + +#define DHCP_END 0xff + +#define OPTION_DHCP_MESSAGE_TYPE 53 +#define VALUE_MESSAGE_DISCOVER 0x01 +#define VALUE_MESSAGE_OFFER 0x02 +#define VALUE_MESSAGE_REQUEST 0x01 +#define VALUE_MESSAGE_ACK 0x05 +#define VALUE_MESSAGE_NAK 0x06 +#define VALUE_MESSAGE_INFORM 0x08 + +#define OPTION_SERVER_IP 54 +#define OPTION_LEASE_TIME 51 +#define OPTION_REQUESTED_IP 50 + +#define OPTION_PARAMETER_REQUEST_LIST 55 + +#define OPTION_SUBNET_MASK 1 +#define OPTION_ROUTER 3 +#define OPTION_BROADCAST_ADDR 28 +#define OPTION_DNS 6 +#define OPTION_DOMAIN_NAME 15 +#define OPTION_HOST_NAME 12 + +//These should not really be usefull for what we do +#define OPTION_TIME_OFFSET 2 +#define OPTION_STATIC_ROUTE 121 +#define OPTION_NIS_DOMAIN 40 +#define OPTION_NIS_SERVERS 41 +#define OPTION_NTP_SERVERS 42 +#define OPTION_MTU 26 +#define OPTION_DOMAIN_SEARCH 119 + +//DHCP options +struct dhcp_opt { + unsigned char id; // Option ID + unsigned char len; // Option value length + unsigned char values[]; // Option value(s) +}; + +//DHCP packet structure +struct dhcp_pkt +{ + unsigned char op; // Message type + unsigned char htype; // HW type + unsigned char hlen; // HW addr length + unsigned char hops; // Hops + + unsigned int xid; // Transaction ID + + unsigned short secs; // seconds elapsed + unsigned short flags; // Bootp flags + + unsigned int ci_addr; // Client address + unsigned int yi_addr; // Your address + unsigned int si_addr; // Next Server IP address + unsigned int gi_addr; // Relay agent IP address + unsigned char cm_addr[6]; // Client MAC address + unsigned char ch_addr[10]; // Client hardware address padding + + unsigned char unused[192]; + + unsigned int magic; // DHCP magic number + + unsigned char opt[128]; // Options padding +// struct dhcp_opt opt [64]; +}; + +//Build a discover DHCP packet, return packet size +int build_dhcp_discover(struct dhcp_pkt* pkt, unsigned char* src_mac, int mac_len); + +//Check if the packet is a DHCP one +int is_dhcp(struct dhcp_pkt* pkt); + +//Find an option in the DHCP packet. offset is the position of the option in the packet. +// After the call, offset is updated to the offset of the next option (if any) +struct dhcp_opt* get_dhcp_option(struct dhcp_pkt *pkt, int *offset); + +//Read an IP in a 4 bytes data array +unsigned int char_to_ip(unsigned char* ip); + +#endif diff --git a/net.c b/net.c new file mode 100644 index 0000000..3fa4cc9 --- /dev/null +++ b/net.c @@ -0,0 +1,89 @@ +#include "net.h" + +#include +#include +#include + +#include + +unsigned short csum_ip(unsigned short *buf, int nwords) +{ + //Checksum the buffer + unsigned long sum; + for(sum=0; nwords>0; nwords--) + sum += *buf++; + sum = (sum >> 16) + (sum &0xffff); + sum += (sum >> 16); + return (unsigned short)(~sum); +} + +struct hw_eth_iface find_iface(int sock_fd, char* iface_name) +{ + struct hw_eth_iface iface; + struct ifreq ifr; + strncpy(ifr.ifr_name, iface_name, IFNAMSIZ); + + //Get the iface index + ioctl(sock_fd, SIOCGIFINDEX, &ifr); + iface.index = ifr.ifr_ifindex; + + //Get the iface HW address + ioctl(sock_fd, SIOCGIFHWADDR, &ifr); + + //Copy the address in our structure +// memcpy(iface.hw_addr, ifr.ifr_hwaddr.sa_data, 6); // LINK problem + int i; + for(i = 0; i < 6; i++) + iface.hw_addr[i] = ifr.ifr_hwaddr.sa_data[i]; + iface.addr_len = 6; + + return iface; +} + +int build_upd_hdr(void* ptr, unsigned short data_len, unsigned short src_port, unsigned short dst_port) +{ + struct udpheader *udp = (struct udpheader*)ptr; + int len = sizeof(struct udpheader); + udp->udph_srcport = htons(68); + udp->udph_destport = htons(67); + udp->udph_len = htons(len + data_len); + //TODO : UDP checksum + return len; +} + +int build_ip4_hdr(void *ptr, unsigned short data_len, char* src_addr, char* dst_addr, unsigned char proto) +{ + struct ipheader *ip = (struct ipheader*)ptr; + int len = sizeof(struct ipheader); + ip->iph_ihl = 5; + ip->iph_ver = 4; + ip->iph_tos = 0; + ip->iph_offset = 0; + ip->iph_len = htons(len + data_len); + ip->iph_ident = htons(54321); //TODO: random number + ip->iph_ttl = 64; // hops + ip->iph_protocol = proto; + ip->iph_sourceip = inet_addr(src_addr); + ip->iph_destip = inet_addr(dst_addr); + ip->iph_chksum = csum_ip((unsigned short *)ip, len/2); + + return len; +} + +int build_ip4_udp_pkt(unsigned char* buffer, int buff_len, unsigned char* data, unsigned short data_len, char* src_addr, char* dst_addr, unsigned short src_port, unsigned short dst_port, unsigned char proto) +{ + memset(buffer, 0, buff_len); + + struct ipheader *ip = (struct ipheader*) buffer; + struct udpheader *udp = (struct udpheader*)(buffer + sizeof(struct ipheader)); + + int udp_len = build_upd_hdr(udp, data_len, src_port, dst_port); + int ip_len = build_ip4_hdr(ip, data_len + udp_len, src_addr, dst_addr, proto); +// memcpy(buffer + sizeof(struct udpheader) + sizeof(struct ipheader), data, data_len); // LINK problem + int i; + int offset = sizeof(struct udpheader) + sizeof(struct ipheader); + for(i = 0; i < data_len; i++) + buffer[offset + i] = data[i]; + + return data_len + udp_len + ip_len; +} diff --git a/net.h b/net.h new file mode 100644 index 0000000..cc27175 --- /dev/null +++ b/net.h @@ -0,0 +1,52 @@ +#ifndef _NET_H +#define _NET_H + +//IP header +struct ipheader +{ + unsigned int iph_ihl:4; //IP header length in 32 bits words + unsigned int iph_ver:4; //IP version + unsigned char iph_tos; // Type of service + unsigned short iph_len; // Total packet length + unsigned short iph_ident; // Identification + unsigned short iph_offset; //Fragment offset + unsigned char iph_ttl; // Time to live + unsigned char iph_protocol; // Protocol + unsigned short iph_chksum; // Header checksum + unsigned int iph_sourceip; // Sender IP address + unsigned int iph_destip; // Destination IP address +}; + +//UDP header +struct udpheader +{ + unsigned short udph_srcport; // Scource port + unsigned short udph_destport; // Destination port + unsigned short udph_len; // Length (UDP + data) + unsigned short udph_chksum; // UDP checksum +}; + +//Structure describing an ethernet interface +struct hw_eth_iface +{ + int index; // Index identifying the HW interface in the system + int addr_len : 6; // HW address lentgh + char hw_addr[6]; // HW address +}; + +//Checksum calculation function +unsigned short csum_ip(unsigned short *buf, int nwords); + +//Find a HW network interface based on its name +struct hw_eth_iface find_iface(int sock_fd, char* iface_name); + +//Build an UDP header +int build_upd_hdr(void* ptr, unsigned short len, unsigned short src_port, unsigned short dst_port); + +//Build an ipV4 header +int build_ip4_hdr(void *ptr, unsigned short data_len, char* src_addr, char* dst_addr, unsigned char proto); + +// Build an UDP over IPV4 packet +int build_ip4_udp_pkt(unsigned char* buffer, int buff_len, unsigned char* data, unsigned short data_len, char* src_addr, char* dst_addr, unsigned short src_port, unsigned short dst_port, unsigned char proto); + +#endif