diff options
Diffstat (limited to 'roms/SLOF/clients/net-snk/app/netlib/dhcp.c')
| -rw-r--r-- | roms/SLOF/clients/net-snk/app/netlib/dhcp.c | 998 | 
1 files changed, 998 insertions, 0 deletions
| diff --git a/roms/SLOF/clients/net-snk/app/netlib/dhcp.c b/roms/SLOF/clients/net-snk/app/netlib/dhcp.c new file mode 100644 index 00000000..5f26f3af --- /dev/null +++ b/roms/SLOF/clients/net-snk/app/netlib/dhcp.c @@ -0,0 +1,998 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + *     IBM Corporation - initial implementation + *****************************************************************************/ + + +/*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ALGORITHMS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + +/** \file dhcp.c <pre> + * **************** State-transition diagram for DHCP client  ************* + * + *   +---------+                  Note: DHCP-server msg / DHCP-client msg + *   |  INIT   | + *   +---------+ + *        | + *        |  - / Discover + *        V + *   +---------+ + *   | SELECT  |                     Timeout + *   +---------+                        | + *        |                             | + *        |  Offer / Request            | + *        |                             | + *        V                             V + *   +---------+     NACK / -      *********** + *   | REQUEST | ----------------> *  FAULT  * + *   +---------+                   *********** + *        | + *        |          ACK / -       *********** + *        +----------------------> * SUCCESS * + *                                 *********** + * + * ************************************************************************ + * </pre> */ + + +/*>>>>>>>>>>>>>>>>>>>>> DEFINITIONS & DECLARATIONS <<<<<<<<<<<<<<<<<<<<<<*/ + +#include <dhcp.h> +#include <ethernet.h> +#include <ipv4.h> +#include <udp.h> +#include <dns.h> + +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <sys/socket.h> +#include <ctype.h> +#include <stdlib.h> + +/* DHCP Message Types */ +#define DHCPDISCOVER    1 +#define DHCPOFFER       2 +#define DHCPREQUEST     3 +#define DHCPDECLINE     4 +#define DHCPACK	        5 +#define DHCPNACK        6 +#define DHCPRELEASE     7 +#define DHCPINFORM      8 + +/* DHCP Option Codes */ +#define DHCP_MASK              1 +#define DHCP_ROUTER            3 +#define DHCP_DNS               6 +#define DHCP_REQUESTED_IP     50 +#define DHCP_OVERLOAD         52 +#define DHCP_MSG_TYPE         53 +#define DHCP_SERVER_ID        54 +#define DHCP_REQUEST_LIST     55 +#define DHCP_TFTP_SERVER      66 +#define DHCP_BOOTFILE         67 +#define DHCP_CLIENT_ARCH      93 +#define DHCP_ENDOPT         0xFF +#define DHCP_PADOPT         0x00 + +/* "file/sname" overload option values */ +#define DHCP_OVERLOAD_FILE     1 +#define DHCP_OVERLOAD_SNAME    2 +#define DHCP_OVERLOAD_BOTH     3 + +/* DHCP states codes */ +#define DHCP_STATE_SELECT      1 +#define DHCP_STATE_REQUEST     2 +#define DHCP_STATE_SUCCESS     3 +#define DHCP_STATE_FAULT       4 + +/* DHCP Client Architecture */ +#ifndef DHCPARCH +#define USE_DHCPARCH 0 +#define DHCPARCH 0 +#else +#define USE_DHCPARCH 1 +#endif + +static uint8_t dhcp_magic[] = {0x63, 0x82, 0x53, 0x63}; +/**< DHCP_magic is a cookie, that identifies DHCP options (see RFC 2132) */ + +/** \struct dhcp_options_t + *  This structure is used to fill options in DHCP-msg during transmitting + *  or to retrieve options from DHCP-msg during receiving. + *  <p> + *  If flag[i] == TRUE then field for i-th option retains valid value and + *  information from this field may retrived (in case of receiving) or will + *  be transmitted (in case of transmitting). + *   + */ +typedef struct { +	uint8_t    flag[256];         /**< Show if corresponding opt. is valid */ +	uint8_t    request_list[256]; /**< o.55 If i-th member is TRUE, then i-th   +	                                  option will be requested from server */ +	uint32_t   server_ID;         /**< o.54 Identifies DHCP-server         */ +	uint32_t   requested_IP;      /**< o.50 Must be filled in DHCP-Request */ +	uint32_t   dns_IP;            /**< o. 6 DNS IP                         */ +	uint32_t   router_IP;         /**< o. 3 Router IP                      */ +	uint32_t   subnet_mask;       /**< o. 1 Subnet mask                    */ +	uint8_t    msg_type;          /**< o.53 DHCP-message type              */ +	uint8_t    overload;          /**< o.52 Overload sname/file fields     */ +	int8_t     tftp_server[256];  /**< o.66 TFTP server name               */ +	int8_t     bootfile[256];     /**< o.67 Boot file name                 */ +	uint16_t   client_arch;       /**< o.93 Client architecture type       */ +} dhcp_options_t; + +/** Stores state of DHCP-client (refer to State-transition diagram) */ +static uint8_t dhcp_state; + + +/*>>>>>>>>>>>>>>>>>>>>>>>>>>>> PROTOTYPES <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + +static int32_t +dhcp_attempt(int fd); + +static int32_t +dhcp_encode_options(uint8_t * opt_field, dhcp_options_t * opt_struct); + +static int32_t +dhcp_decode_options(uint8_t opt_field[], uint32_t opt_len, +                    dhcp_options_t * opt_struct); + +static int8_t +dhcp_merge_options(uint8_t dst_options[], uint32_t * dst_len, +                   uint8_t src_options[], uint32_t src_len); + +static int8_t +dhcp_find_option(uint8_t options[], uint32_t len, +                 uint8_t op_code, uint32_t * op_offset); + +static void +dhcp_append_option(uint8_t dst_options[], uint32_t * dst_len, +                   uint8_t * new_option); + +static void +dhcp_combine_option(uint8_t dst_options[], uint32_t * dst_len, +                    uint32_t dst_offset, uint8_t * new_option); + +static void +dhcp_send_discover(int fd); + +static void +dhcp_send_request(int fd); + +static uint8_t +strtoip(int8_t * str, uint32_t * ip); + + +/*>>>>>>>>>>>>>>>>>>>>>>>>>>>> LOCAL VARIABLES <<<<<<<<<<<<<<<<<<<<<<<<<<*/ + +static uint8_t  ether_packet[ETH_MTU_SIZE]; +static uint32_t dhcp_own_ip        = 0; +static uint32_t dhcp_server_ip     = 0; +static uint32_t dhcp_siaddr_ip     = 0; +static int8_t   dhcp_filename[256]; +static int8_t   dhcp_tftp_name[256]; + +static char   * response_buffer; + +/*>>>>>>>>>>>>>>>>>>>>>>>>>>>> IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + +int32_t +dhcpv4(char *ret_buffer, filename_ip_t * fn_ip) { + +	uint32_t dhcp_tftp_ip     = 0; +	int fd = fn_ip->fd; + +	strcpy((char *) dhcp_filename, ""); +	strcpy((char *) dhcp_tftp_name, ""); + +	response_buffer = ret_buffer; + +	if (dhcp_attempt(fd) == 0) +		return -1; + +	if (fn_ip->own_ip) { +		dhcp_own_ip = fn_ip->own_ip; +	} +	if (fn_ip->server_ip) { +		dhcp_siaddr_ip = fn_ip->server_ip; +	} +	if(fn_ip->filename[0] != 0) { +		strcpy((char *) dhcp_filename, (char *) fn_ip->filename); +	} + +	// TFTP SERVER +	if (!strlen((char *) dhcp_tftp_name)) { +		if (!dhcp_siaddr_ip) { +			// ERROR: TFTP name is not presented +			return -3; +		} + +		// take TFTP-ip from siaddr field +		dhcp_tftp_ip = dhcp_siaddr_ip; +	} +	else { +		// TFTP server defined by its name +		if (!strtoip(dhcp_tftp_name, &(dhcp_tftp_ip))) { +			if (!dns_get_ip(fd, dhcp_tftp_name, (uint8_t *)&(dhcp_tftp_ip), 4)) { +				// DNS error - can't obtain TFTP-server name   +				// Use TFTP-ip from siaddr field, if presented +				if (dhcp_siaddr_ip) { +					dhcp_tftp_ip = dhcp_siaddr_ip; +				} +				else { +					// ERROR: Can't obtain TFTP server IP +					return -4; +				} +			} +		} +	} + +	// Store configuration info into filename_ip strucutre +	fn_ip -> own_ip = dhcp_own_ip; +	fn_ip -> server_ip = dhcp_tftp_ip; +	strcpy((char *) fn_ip -> filename, (char *) dhcp_filename); + +	return 0; +} + +/** + * DHCP: Tries o obtain DHCP parameters, refer to state-transition diagram + */ +static int32_t +dhcp_attempt(int fd) { +	int sec; + +	// Send DISCOVER message and switch DHCP-client to SELECT state +	dhcp_send_discover(fd); + +	dhcp_state = DHCP_STATE_SELECT; + +	// setting up a timer with a timeout of two seconds +	for (sec = 0; sec < 2; sec++) { +		set_timer(TICKS_SEC); +		do { +			receive_ether(fd); + +			// Wait until client will switch to Final state or Timeout occurs +			switch (dhcp_state) { +			case DHCP_STATE_SUCCESS : +				return 1; +			case DHCP_STATE_FAULT : +				return 0; +			} +		} while (get_timer() > 0); +	} + +	// timeout  +	return 0; +} + +/** + * DHCP: Supplements DHCP-message with options stored in structure. + *       For more information about option coding see dhcp_options_t. + * + * @param  opt_field     Points to the "vend" field of DHCP-message   + *                       (destination) + * @param  opt_struct    this structure stores info about the options which + *                       will be added to DHCP-message (source) + * @return               TRUE - options packed; + *                       FALSE - error condition occurs. + * @see                  dhcp_options_t + */ +static int32_t +dhcp_encode_options(uint8_t * opt_field, dhcp_options_t * opt_struct) { +	uint8_t * options = opt_field; +	uint16_t i, sum; // used to define is any options set + +	// magic +	memcpy(options, dhcp_magic, 4); +	options += 4; + +	// fill message type +	switch (opt_struct -> msg_type) { +	case DHCPDISCOVER : +	case DHCPREQUEST : +	case DHCPDECLINE : +	case DHCPINFORM : +	case DHCPRELEASE : +		options[0] = DHCP_MSG_TYPE; +		options[1] = 1; +		options[2] = opt_struct -> msg_type; +		options += 3; +		break; +	default : +		return 0; // Unsupported DHCP-message +	} + +	if (opt_struct -> overload) { +		options[0] = DHCP_OVERLOAD; +		options[1] = 0x01; +		options[2] = opt_struct -> overload; +		options +=3; +	} + +	if (opt_struct -> flag[DHCP_REQUESTED_IP]) { +		options[0] = DHCP_REQUESTED_IP; +		options[1] = 0x04; +		* (uint32_t *) (options + 2) = htonl (opt_struct -> requested_IP); +		options +=6; +	} + +	if (opt_struct -> flag[DHCP_SERVER_ID]) { +		options[0] = DHCP_SERVER_ID; +		options[1] = 0x04; +		* (uint32_t *) (options + 2) = htonl (opt_struct -> server_ID); +		options +=6; +	} + +	sum = 0; +	for (i = 0; i < 256; i++) +		sum += opt_struct -> request_list[i]; + +	if (sum) { +		options[0] = DHCP_REQUEST_LIST; +		options[1] = sum; +		options += 2; +		for (i = 0; i < 256; i++) { +			if (opt_struct -> request_list[i]) { +				options[0] = i; options++; +			} +		} +	} + +	if (opt_struct -> flag[DHCP_TFTP_SERVER]) { +		options[0] = DHCP_TFTP_SERVER; +		options[1] = strlen((char *) opt_struct -> tftp_server) + 1; +		memcpy(options + 2, opt_struct -> tftp_server, options[1]); +		options += options[1] + 2; +	} + +	if (opt_struct -> flag[DHCP_BOOTFILE]) { +		options[0] = DHCP_BOOTFILE; +		options[1] = strlen((char *) opt_struct -> bootfile) + 1; +		memcpy(options + 2, opt_struct -> bootfile, options[1]); +		options += options[1] + 2; +	} + +	if (opt_struct -> flag[DHCP_CLIENT_ARCH]) { +		options[0] = DHCP_CLIENT_ARCH; +		options[1] = 2; +		options[2] = (DHCPARCH >> 8); +		options[3] = DHCPARCH & 0xff; +		options += 4; +	} + +	// end options +	options[0] = 0xFF; +	options++; + +	return 1; +} + +/** + * DHCP: Extracts encoded options from DHCP-message into the structure. + *       For more information about option coding see dhcp_options_t. + * + * @param  opt_field     Points to the "options" field of DHCP-message   + *                       (source). + * @param  opt_len       Length of "options" field. + * @param  opt_struct    this structure stores info about the options which + *                       was extracted from DHCP-message (destination). + * @return               TRUE - options extracted; + *                       FALSE - error condition occurs. + * @see                  dhcp_options_t + */ +static int32_t +dhcp_decode_options(uint8_t opt_field[], uint32_t opt_len, +                    dhcp_options_t * opt_struct) { +	int32_t offset = 0; + +	memset(opt_struct, 0, sizeof(dhcp_options_t)); + +	// magic +	if (memcmp(opt_field, dhcp_magic, 4)) { +		return 0; +	} + +	offset += 4; +	while (offset < opt_len) { +		opt_struct -> flag[opt_field[offset]] = 1; +		switch(opt_field[offset]) { +		case DHCP_OVERLOAD : +			opt_struct -> overload = opt_field[offset + 2]; +			offset += 2 + opt_field[offset + 1];  +			break; + +		case DHCP_REQUESTED_IP : +			opt_struct -> requested_IP = htonl(* (uint32_t *) (opt_field + offset + 2)); +			offset += 2 + opt_field[offset + 1];  +			break; + +		case DHCP_MASK : +			opt_struct -> flag[DHCP_MASK] = 1; +			opt_struct -> subnet_mask = htonl(* (uint32_t *) (opt_field + offset + 2)); +			offset += 2 + opt_field[offset + 1];  +			break; + +		case DHCP_DNS : +			opt_struct -> flag[DHCP_DNS] = 1; +			opt_struct -> dns_IP = htonl(* (uint32_t *) (opt_field + offset + 2)); +			offset += 2 + opt_field[offset + 1];  +			break; + +		case DHCP_ROUTER : +			opt_struct -> flag[DHCP_ROUTER] = 1; +			opt_struct -> router_IP = htonl(* (uint32_t *) (opt_field + offset + 2)); +			offset += 2 + opt_field[offset + 1];  +			break; + +		case DHCP_MSG_TYPE : +			if ((opt_field[offset + 2] > 0) && (opt_field[offset + 2] < 9)) +				opt_struct -> msg_type = opt_field[offset + 2]; +			else +				return 0; +			offset += 2 + opt_field[offset + 1]; +			break; + +		case DHCP_SERVER_ID : +			opt_struct -> server_ID = htonl(* (uint32_t *) (opt_field + offset + 2)); +			offset += 2 + opt_field[offset + 1]; +			break; + +		case DHCP_TFTP_SERVER	: +			memcpy(opt_struct -> tftp_server, opt_field + offset + 2, opt_field[offset + 1]); +			(opt_struct -> tftp_server)[opt_field[offset + 1]] = 0; +			offset += 2 + opt_field[offset + 1]; +			break; + +		case DHCP_BOOTFILE : +			memcpy(opt_struct ->  bootfile, opt_field + offset + 2, opt_field[offset + 1]); +			(opt_struct -> bootfile)[opt_field[offset + 1]] = 0; +			offset += 2 + opt_field[offset + 1]; +			break; + +		case DHCP_CLIENT_ARCH : +			opt_struct -> client_arch = ((opt_field[offset + 2] << 8) & 0xFF00) | (opt_field[offset + 3] & 0xFF); +			offset += 4; +			break; + +		case DHCP_PADOPT : +			offset++; +			break; + +		case DHCP_ENDOPT :  // End of options +			return 1; + +		default : +			offset += 2 + opt_field[offset + 1]; // Unsupported opt. - do nothing +		} +	} +	if (offset == opt_len) +		return 1; // options finished without 0xFF + +	return 0; +} + +/** + * DHCP: Appends information from source "options" into dest "options". + *       This function is used to support "file/sname" overloading. + * + * @param  dst_options   destanation "options" field + * @param  dst_len       size of dst_options (modified by this function) + * @param  src_options   source "options" field + * @param  src_len       size of src_options + * @return               TRUE - options merged; + *                       FALSE - error condition occurs. + */ +static int8_t dhcp_merge_options(uint8_t dst_options[], uint32_t * dst_len, +                                 uint8_t src_options[], uint32_t src_len) { +	int32_t dst_offset, src_offset = 0; + +	// remove ENDOPT if presented +	if (dhcp_find_option(dst_options, * dst_len, DHCP_ENDOPT, (uint32_t *) &dst_offset)) +		* dst_len = dst_offset; + +	while (src_offset < src_len) { +		switch(src_options[src_offset]) { +		case DHCP_PADOPT: +			src_offset++; +			break; +		case DHCP_ENDOPT: +			return 1; +		default: +			if (dhcp_find_option(dst_options, * dst_len, +			                     src_options[src_offset], +			                     (uint32_t *) &dst_offset)) { +				dhcp_combine_option(dst_options, dst_len, +				                    dst_offset, +				                    (uint8_t *) src_options + +				                    src_offset); +			} +			else { +				dhcp_append_option(dst_options, dst_len, src_options + src_offset); +			} +			src_offset += 2 + src_options[src_offset + 1]; +		} +	} + +	if (src_offset == src_len)  +		return 1; +	return 0; +} + +/** + * DHCP: Finds given occurrence of the option with the given code (op_code) + *       in "options" field of DHCP-message. + * + * @param  options       "options" field of DHCP-message + * @param  len           length of the "options" field + * @param  op_code       code of the option to find + * @param  op_offset     SUCCESS - offset to an option occurrence; + *                       FAULT - offset is set to zero. + * @return               TRUE - option was find; + *                       FALSE - option wasn't find. + */ +static int8_t dhcp_find_option(uint8_t options[], uint32_t len, +                               uint8_t op_code, uint32_t * op_offset) { +	uint32_t srch_offset = 0; +	* op_offset = 0; + +	while (srch_offset < len) { +		if (options[srch_offset] == op_code) { +			* op_offset = srch_offset; +			return 1; +		} +		if (options[srch_offset] == DHCP_ENDOPT) +			return 0; + +		if (options[srch_offset] == DHCP_PADOPT) +			srch_offset++; +		else +			srch_offset += 2 + options[srch_offset + 1]; +	} +	return 0; +} + +/** + * DHCP: Appends new option from one list (src) into the tail + *       of another option list (dst) + * + * @param  dst_options   "options" field of DHCP-message + * @param  dst_len       length of the "options" field (modified) + * @param  new_option    points to an option in another list (src) + */ +static void +dhcp_append_option(uint8_t dst_options[], uint32_t * dst_len, +                   uint8_t * new_option) { +	memcpy(dst_options + ( * dst_len), new_option, 2 + (* (new_option + 1))); +	* dst_len += 2 + *(new_option + 1); +} + +/** + * DHCP: This function is used when options with the same code are + *       presented in both merged lists. In this case information + *       about the option from one list (src) is combined (complemented) + *       with information about the option in another list (dst). + * + * @param  dst_options  "options" field of DHCP-message + * @param  dst_len       length of the "options" field (modified) + * @param  dst_offset    offset of the option from beginning of the list + * @param  new_option    points to an option in another list (src) + */ +static void +dhcp_combine_option(uint8_t dst_options[], uint32_t * dst_len, +                    uint32_t dst_offset, uint8_t * new_option) { + +	uint8_t tmp_buffer[1024]; // use to provide safe memcpy +	uint32_t tail_len; + +	// move all subsequent options (allocate size for additional info) +	tail_len = (* dst_len) - dst_offset - 2 - dst_options[dst_offset + 1]; + +	memcpy(tmp_buffer, dst_options + (* dst_len) - tail_len, tail_len); +	memcpy(dst_options + (* dst_len) - tail_len + (* (new_option + 1)), +	       tmp_buffer, tail_len); + +	// add new_content to option +	memcpy(dst_options + (* dst_len) - tail_len, new_option + 2, +	       * (new_option + 1)); +	dst_options[dst_offset + 1] += * (new_option + 1); + +	// correct dst_len +	* dst_len += * (new_option + 1); +} + +/** + * DHCP: Sends DHCP-Discover message. Looks for DHCP servers. + */ +static void +dhcp_send_discover(int fd) { +	uint32_t packetsize = sizeof(struct iphdr) + +	                      sizeof(struct udphdr) + sizeof(struct btphdr); +	struct btphdr *btph; +	dhcp_options_t opt; + +	memset(ether_packet, 0, packetsize); + +	btph = (struct btphdr *) (ðer_packet[ +	       sizeof(struct iphdr) + sizeof(struct udphdr)]); + +	btph -> op = 1; +	btph -> htype = 1; +	btph -> hlen = 6; +	memcpy(btph -> chaddr, get_mac_address(), 6); + +	memset(&opt, 0, sizeof(dhcp_options_t)); + +	opt.msg_type = DHCPDISCOVER; + +	opt.request_list[DHCP_MASK] = 1; +	opt.request_list[DHCP_DNS] = 1; +	opt.request_list[DHCP_ROUTER] = 1; +	opt.request_list[DHCP_TFTP_SERVER] = 1; +	opt.request_list[DHCP_BOOTFILE] = 1; +	opt.request_list[DHCP_CLIENT_ARCH] = USE_DHCPARCH; + +	dhcp_encode_options(btph -> vend, &opt); + +	fill_udphdr(ðer_packet[sizeof(struct iphdr)], +	            sizeof(struct btphdr) + sizeof(struct udphdr), +	            UDPPORT_BOOTPC, UDPPORT_BOOTPS); +	fill_iphdr(ether_packet, sizeof(struct btphdr) + +	           sizeof(struct udphdr) + sizeof(struct iphdr), +	           IPTYPE_UDP, dhcp_own_ip, 0xFFFFFFFF); + +	send_ipv4(fd, ether_packet, packetsize); +} + +/** + * DHCP: Sends DHCP-Request message. Asks for acknowledgment to occupy IP. + */ +static void +dhcp_send_request(int fd) { +	uint32_t packetsize = sizeof(struct iphdr) + +	                      sizeof(struct udphdr) + sizeof(struct btphdr); +	struct btphdr *btph; +	dhcp_options_t opt; + +	memset(ether_packet, 0, packetsize); + +	btph = (struct btphdr *) (ðer_packet[ +	       sizeof(struct iphdr) + sizeof(struct udphdr)]); + +	btph -> op = 1; +	btph -> htype = 1; +	btph -> hlen = 6; +	memcpy(btph -> chaddr, get_mac_address(), 6); + +	memset(&opt, 0, sizeof(dhcp_options_t)); + +	opt.msg_type = DHCPREQUEST; +	memcpy(&(opt.requested_IP), &dhcp_own_ip, 4); +	opt.flag[DHCP_REQUESTED_IP] = 1; +	memcpy(&(opt.server_ID), &dhcp_server_ip, 4); +	opt.flag[DHCP_SERVER_ID] = 1; + +	opt.request_list[DHCP_MASK] = 1; +	opt.request_list[DHCP_DNS] = 1; +	opt.request_list[DHCP_ROUTER] = 1; +	opt.request_list[DHCP_TFTP_SERVER] = 1; +	opt.request_list[DHCP_BOOTFILE] = 1; +	opt.request_list[DHCP_CLIENT_ARCH] = USE_DHCPARCH; +	opt.flag[DHCP_CLIENT_ARCH] = USE_DHCPARCH; + +	dhcp_encode_options(btph -> vend, &opt); + +	fill_udphdr(ðer_packet[sizeof(struct iphdr)], +	            sizeof(struct btphdr) + sizeof(struct udphdr), +	            UDPPORT_BOOTPC, UDPPORT_BOOTPS); +	fill_iphdr(ether_packet, sizeof(struct btphdr) + +	           sizeof(struct udphdr) + sizeof(struct iphdr), +	           IPTYPE_UDP, 0, 0xFFFFFFFF); + +	send_ipv4(fd, ether_packet, packetsize); +} + + +/** + * DHCP: Sends DHCP-Release message. Releases occupied IP. + */ +void dhcp_send_release(int fd) { +	uint32_t packetsize = sizeof(struct iphdr) + +	                      sizeof(struct udphdr) + sizeof(struct btphdr); +	struct btphdr *btph; +	dhcp_options_t opt; + +	btph = (struct btphdr *) (ðer_packet[ +	       sizeof(struct iphdr) + sizeof(struct udphdr)]); + +	memset(ether_packet, 0, packetsize); + +	btph -> op = 1; +	btph -> htype = 1; +	btph -> hlen = 6; +	strcpy((char *) btph -> file, ""); +	memcpy(btph -> chaddr, get_mac_address(), 6); +	btph -> ciaddr = htonl(dhcp_own_ip); + +	memset(&opt, 0, sizeof(dhcp_options_t)); + +	opt.msg_type = DHCPRELEASE; +	opt.server_ID = dhcp_server_ip; +	opt.flag[DHCP_SERVER_ID] = 1; + +	dhcp_encode_options(btph -> vend, &opt); + +	fill_udphdr(ðer_packet[sizeof(struct iphdr)],  +	            sizeof(struct btphdr) + sizeof(struct udphdr), +	            UDPPORT_BOOTPC, UDPPORT_BOOTPS); +	fill_iphdr(ether_packet, sizeof(struct btphdr) + +	           sizeof(struct udphdr) + sizeof(struct iphdr), IPTYPE_UDP, +	           dhcp_own_ip, dhcp_server_ip); + +	send_ipv4(fd, ether_packet, packetsize); +} + +/** + * DHCP: Handles DHCP-messages according to Receive-handle diagram. + *       Changes the state of DHCP-client. + * + * @param  fd         socket descriptor + * @param  packet     BootP/DHCP-packet to be handled + * @param  packetsize length of the packet + * @return            ZERO - packet handled successfully; + *                    NON ZERO - packet was not handled (e.g. bad format) + * @see               receive_ether + * @see               btphdr + */ + +int8_t +handle_dhcp(int fd, uint8_t * packet, int32_t packetsize) { +	struct btphdr * btph; +	struct iphdr * iph; +	dhcp_options_t opt; + +	memset(&opt, 0, sizeof(dhcp_options_t));   +	btph = (struct btphdr *) packet; +	iph = (struct iphdr *) packet - sizeof(struct udphdr) - +	      sizeof(struct iphdr); +	if (btph -> op != 2) +		return -1; // it is not Boot Reply + +	if (memcmp(btph -> vend, dhcp_magic, 4)) { +		// It is BootP - RFC 951 +		dhcp_own_ip    = htonl(btph -> yiaddr); +		dhcp_siaddr_ip = htonl(btph -> siaddr); +		dhcp_server_ip = htonl(iph -> ip_src); + +		if (strlen((char *) btph -> sname) && !dhcp_siaddr_ip) { +			strncpy((char *) dhcp_tftp_name, (char *) btph -> sname, +			        sizeof(btph -> sname)); +			dhcp_tftp_name[sizeof(btph -> sname)] = 0; +		} + +		if (strlen((char *) btph -> file)) { +			strncpy((char *) dhcp_filename, (char *) btph -> file, sizeof(btph -> file)); +			dhcp_filename[sizeof(btph -> file)] = 0; +		} + +		dhcp_state = DHCP_STATE_SUCCESS; +		return 0; +	} + + +	// decode options   +	if (!dhcp_decode_options(btph -> vend, packetsize - +	                         sizeof(struct btphdr) + sizeof(btph -> vend), +	                         &opt)) { +		return -1;  // can't decode options +	} + +	if (opt.overload) { +		int16_t decode_res = 0; +		uint8_t options[1024]; // buffer for merged options +		uint32_t opt_len; + +		// move 1-st part of options from vend field into buffer +		opt_len = packetsize - sizeof(struct btphdr) + +		          sizeof(btph -> vend) - 4; +		memcpy(options, btph -> vend, opt_len + 4); + +		// add other parts +		switch (opt.overload) { +		case DHCP_OVERLOAD_FILE: +			decode_res = dhcp_merge_options(options + 4, &opt_len, +			                                btph -> file, +			                                sizeof(btph -> file)); +			break; +		case DHCP_OVERLOAD_SNAME: +			decode_res = dhcp_merge_options(options + 4, &opt_len, +			                                btph -> sname, +			                                sizeof(btph -> sname)); +			break; +		case DHCP_OVERLOAD_BOTH: +			decode_res = dhcp_merge_options(options + 4, &opt_len, +			                                btph -> file, +			                                sizeof(btph -> file)); +			if (!decode_res) +				break; +			decode_res = dhcp_merge_options(options + 4, &opt_len, +			                                btph -> sname, +			                                sizeof(btph -> sname)); +			break; +		} + +		if (!decode_res) +			return -1; // bad options in sname/file fields + +		// decode merged options +		if (!dhcp_decode_options(options, opt_len + 4, &opt)) { +			return -1; // can't decode options +		} +	} + +	if (!opt.msg_type) { +		// It is BootP with Extensions - RFC 1497 +		// retrieve conf. settings from BootP - reply +		dhcp_own_ip = htonl(btph -> yiaddr); +		dhcp_siaddr_ip = htonl(btph -> siaddr); +		if (strlen((char *) btph -> sname) && !dhcp_siaddr_ip) { +			strncpy((char *) dhcp_tftp_name, (char *) btph -> sname, sizeof(btph -> sname)); +			dhcp_tftp_name[sizeof(btph -> sname)] = 0; +		} + +		if (strlen((char *) btph -> file)) { +			strncpy((char *) dhcp_filename, (char *) btph -> file, sizeof(btph -> file)); +			dhcp_filename[sizeof(btph -> file)] = 0; +		} + +		// retrieve DHCP-server IP from IP-header +		dhcp_server_ip = iph -> htonl(ip_src); + +		dhcp_state = DHCP_STATE_SUCCESS; +	} +	else { +		// It is DHCP - RFC 2131 & RFC 2132 +		// opt contains parameters from server +		switch (dhcp_state) { +		case DHCP_STATE_SELECT : +			if (opt.msg_type == DHCPOFFER) { +				dhcp_own_ip = htonl(btph -> yiaddr); +				dhcp_server_ip = opt.server_ID; +				dhcp_send_request(fd); +				dhcp_state = DHCP_STATE_REQUEST; +			} +			return 0; +		case DHCP_STATE_REQUEST : +			switch (opt.msg_type) { +			case DHCPNACK : +				dhcp_own_ip = 0; +				dhcp_server_ip = 0; +				dhcp_state = DHCP_STATE_FAULT; +				break; +			case DHCPACK : +				dhcp_own_ip = htonl(btph -> yiaddr); +				dhcp_server_ip = opt.server_ID; +				dhcp_siaddr_ip = htonl(btph -> siaddr); +				if (opt.flag[DHCP_TFTP_SERVER]) { +					strcpy((char *) dhcp_tftp_name, (char *) opt.tftp_server); +				} +				else { +					strcpy((char *) dhcp_tftp_name, ""); +					if ((opt.overload != DHCP_OVERLOAD_SNAME && +					     opt.overload != DHCP_OVERLOAD_BOTH) && +					     !dhcp_siaddr_ip) { +						strncpy((char *) dhcp_tftp_name, +						        (char *) btph->sname, +						        sizeof(btph -> sname)); +						dhcp_tftp_name[sizeof(btph->sname)] = 0; +					} +				} + +				if (opt.flag[DHCP_BOOTFILE]) { +					strcpy((char *) dhcp_filename, (char *) opt.bootfile); +				} +				else { +					strcpy((char *) dhcp_filename, ""); +					if (opt.overload != DHCP_OVERLOAD_FILE && +						opt.overload != DHCP_OVERLOAD_BOTH &&  +						strlen((char *) btph -> file)) { +						strncpy((char *) dhcp_filename, +						        (char *) btph->file, +						        sizeof(btph->file)); +						dhcp_filename[sizeof(btph -> file)] = 0; +					} +				} + +				dhcp_state = DHCP_STATE_SUCCESS; +				break; +			default: +				break; // Unused DHCP-message - do nothing +			} +			break; +		default : +			return -1; // Illegal DHCP-client state +		} +	} + +	if (dhcp_state == DHCP_STATE_SUCCESS) { + +		// initialize network entity with real own_ip +		// to be able to answer for foreign requests +		set_ipv4_address(dhcp_own_ip); + +		if(response_buffer) { +			if(packetsize <= 1720) +				memcpy(response_buffer, packet, packetsize); +			else +				memcpy(response_buffer, packet, 1720); +		} + +		/* Subnet mask */ +		if (opt.flag[DHCP_MASK]) { +			/* Router */ +			if (opt.flag[DHCP_ROUTER]) { +				set_ipv4_router(opt.router_IP); +				set_ipv4_netmask(opt.subnet_mask); +			} +		} + +		/* DNS-server */ +		if (opt.flag[DHCP_DNS]) { +			dns_init(opt.dns_IP, 0, 4); +		} +	} + +	return 0; +} + +/** + * DHCP: Converts "255.255.255.255" -> 32-bit long IP + * + * @param  str        string to be converted + * @param  ip         in case of SUCCESS - 32-bit long IP +                      in case of FAULT - zero + * @return            TRUE - IP converted successfully; + *                    FALSE - error condition occurs (e.g. bad format) + */ +static uint8_t +strtoip(int8_t * str, uint32_t * ip) { +	int8_t ** ptr = &str; +	int16_t i = 0, res, len; +	char octet[256]; + +	* ip = 0; + +	while (**ptr != 0) { +		if (i > 3 || !isdigit(**ptr)) +			return 0; +		if (strstr((char *) * ptr, ".") != NULL) { +			len = (int16_t) ((int8_t *) strstr((char *) * ptr, ".") -  +			      (int8_t *) (* ptr)); +			strncpy(octet, (char *) * ptr, len); octet[len] = 0; +			* ptr += len; +		} +		else { +			strcpy(octet, (char *) * ptr); +			* ptr += strlen(octet); +		} +		res = strtol(octet, NULL, 10); +		if ((res > 255) || (res < 0)) +			return 0; +		* ip = ((* ip) << 8) + res; +		i++; +		if (** ptr == '.') +			(*ptr)++; +	} + +	if (i != 4) +		return 0; +	return 1; +} | 
