| /****************************************************************************** |
| * 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 <netapps/args.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); |
| |
| /***************************** 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 char dhcp_filename[256]; |
| static char dhcp_tftp_name[256]; |
| static uint32_t dhcp_xid; |
| |
| static char * response_buffer; |
| |
| /***************************** IMPLEMENTATION ****************************/ |
| |
| void dhcpv4_generate_transaction_id(void) |
| { |
| dhcp_xid = (rand() << 16) ^ rand(); |
| } |
| |
| int32_t dhcpv4(char *ret_buffer, filename_ip_t *fn_ip) |
| { |
| uint32_t dhcp_tftp_ip = 0; |
| int fd = fn_ip->fd; |
| |
| strcpy(dhcp_filename, ""); |
| strcpy(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(dhcp_filename, (char *) fn_ip->filename); |
| } |
| |
| // TFTP SERVER |
| if (!strlen(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, (char *)&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, 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) |
| { |
| uint32_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) |
| { |
| uint32_t dst_offset, src_offset = 0; |
| |
| // remove ENDOPT if presented |
| if (dhcp_find_option(dst_options, * dst_len, DHCP_ENDOPT, &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], |
| &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; |
| btph -> xid = dhcp_xid; |
| 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; |
| btph -> xid = dhcp_xid; |
| 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; |
| btph -> xid = dhcp_xid; |
| 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 a Bootp/DHCP reply */ |
| if (btph->xid != dhcp_xid) |
| return -1; /* The transaction ID does not match */ |
| |
| 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; |
| } |