BlinkenArea - GitList
Repositories
Blog
Wiki
mips_sys
Code
Commits
Branches
Tags
Search
Tree:
c2b0401
Branches
Tags
master
mips_sys
fw
dhcp.c
added file headers
Stefan Schuermans
commited
c2b0401
at 2012-04-08 11:54:40
dhcp.c
Blame
History
Raw
/* MIPS I system * Copyright 2011-2012 Stefan Schuermans <stefan@schuermans.info> * Copyleft GNU public license V2 or later * http://www.gnu.org/copyleft/gpl.html */ #include "config.h" #include "checksum.h" #include "dhcp.h" #include "ethernet.h" #include "ip.h" #include "macros.h" #include "memcpy.h" #include "nethelp.h" #include "random.h" #include "udp.h" /// configuration //@{ /// avarage timeout after which to retry DHCP action static const unsigned char dhcp_retry_secs_max = 10; /// maximum number of retries before aborting DHCP action static const unsigned char dhcp_retries_max = 5; /// when to ask to extend lease static const unsigned long dhcp_lease_rest_min = 300; /// to renw lease when only this fraction of lease is left static const unsigned char dhcp_lease_renew_fraction = 8; //@} /// DHCP tick counter to get full seconds static unsigned char dhcp_ticks = 0; /// time of current DHCP action //@{ static unsigned char dhcp_in_progress = 0; ///< if DHCP action is in progress static unsigned char dhcp_retries = 0; ///< number of retries left static unsigned char dhcp_retry_secs = 0; ///< rest of time for current retry //@} /// current x_id //@{ static unsigned char dhcp_have_x_id = 0; ///< if an x_id exists at the moment static unsigned long dhcp_x_id = 0; ///< the current x_id data static unsigned short dhcp_x_id_secs = 0; ///< time of x_id //@} /// DHCP server and lease time //@{ static unsigned char dhcp_active = 0; ///< if own IP is leased from DHCP static unsigned char dhcp_server[4]; /**< IP address of DHCP server own IP is leased from */ static unsigned long dhcp_lease_time = 0; ///< total lease time static unsigned long dhcp_lease_rest = 0; ///< rest of lease time //@} /** * @brief send a DHCP packet * @param[in] ptr pointer to data of packet * @param[in] sz size of packet * * ptr must point to a dhcp_packet * with ip_hdr.dest, dhcp_hdr.x_id, dhcp_hdr.secs, dhcp_hdr.flags * and dhcp_hdr.yi_addr already initialized */ static void dhcp_send(void *ptr, unsigned int sz) { struct dhcp_packet *p_dhcp_pack; // packet too short if (sz < sizeof(struct dhcp_packet)) return; p_dhcp_pack = ptr; // we are DHCP client // - source port must be 68 // - destination port must be 67 p_dhcp_pack->udp_hdr.src_port = htons(68); p_dhcp_pack->udp_hdr.dest_port = htons(67); // set up DHCP header fields as client p_dhcp_pack->dhcp_hdr.op = 1; p_dhcp_pack->dhcp_hdr.h_type = 1; p_dhcp_pack->dhcp_hdr.h_len = 6; p_dhcp_pack->dhcp_hdr.h_ops = 0; memset(p_dhcp_pack->dhcp_hdr.ci_addr, 0, 4); memset(p_dhcp_pack->dhcp_hdr.si_addr, 0, 4); memset(p_dhcp_pack->dhcp_hdr.gi_addr, 0, 4); mac_cpy(p_dhcp_pack->dhcp_hdr.ch_addr, config_mac.mac); memset(p_dhcp_pack->dhcp_hdr.ch_addr + 6, 0, 10); memset(p_dhcp_pack->dhcp_hdr.s_name, 0, 64); memset(p_dhcp_pack->dhcp_hdr.file, 0, 128); p_dhcp_pack->dhcp_hdr.m_cookie = htonl(0x63825363); // send DHCP packet udp_send(ptr, sz); } /// send a DHCP discover packet static void dhcp_discover(void) { struct { struct dhcp_packet dhcp_pack; unsigned char opt_type[3]; unsigned char opt_end[2]; } __attribute__((packed)) dhcp_discover; ip_cpy(dhcp_discover.dhcp_pack.ip_hdr.dest, "\xFF\xFF\xFF\xFF"); // broadcast dhcp_discover.dhcp_pack.dhcp_hdr.x_id = htonl(dhcp_x_id); dhcp_discover.dhcp_pack.dhcp_hdr.secs = htons(dhcp_x_id_secs); // x_id time dhcp_discover.dhcp_pack.dhcp_hdr.flags = htons(0x8000); // broadcast memset(dhcp_discover.dhcp_pack.dhcp_hdr.yi_addr, 0, 4); dhcp_discover.opt_type[0] = 0x35; // DHCP discover dhcp_discover.opt_type[1] = 0x01; dhcp_discover.opt_type[2] = 0x01; dhcp_discover.opt_end[0] = 0xFF; // end of options dhcp_discover.opt_end[1] = 0x00; dhcp_send(&dhcp_discover, sizeof(dhcp_discover)); } /// send a DHCP request packet static void dhcp_request(void) { struct { struct dhcp_packet dhcp_pack; unsigned char opt_type[3]; unsigned char opt_req[6]; unsigned char opt_server[6]; unsigned char opt_end[2]; } __attribute__((packed)) dhcp_request; ip_cpy(dhcp_request.dhcp_pack.ip_hdr.dest, dhcp_server); // to DHCP server dhcp_request.dhcp_pack.dhcp_hdr.x_id = htonl(dhcp_x_id); dhcp_request.dhcp_pack.dhcp_hdr.secs = htons(dhcp_x_id_secs); // x_id time dhcp_request.dhcp_pack.dhcp_hdr.flags = htons(0x0000); ip_cpy(dhcp_request.dhcp_pack.dhcp_hdr.yi_addr, config_ip.ip); dhcp_request.opt_type[0] = 0x35; // DHCP request dhcp_request.opt_type[1] = 0x01; dhcp_request.opt_type[2] = 0x03; dhcp_request.opt_req[0] = 0x32; // requested IP dhcp_request.opt_req[1] = 0x04; ip_cpy(&dhcp_request.opt_req[2], config_ip.ip); dhcp_request.opt_server[0] = 0x36; // DHCP server dhcp_request.opt_server[1] = 0x04; ip_cpy(&dhcp_request.opt_server[2], dhcp_server); dhcp_request.opt_end[0] = 0xFF; // end of options dhcp_request.opt_end[1] = 0x00; dhcp_send(&dhcp_request, sizeof(dhcp_request)); } /// abort DHCP action static void dhcp_abort(void) { // reset DHCP action status dhcp_in_progress = 0; dhcp_retries = 0; dhcp_retry_secs = 0; // forget x_id dhcp_have_x_id = 0; dhcp_x_id = 0; dhcp_x_id_secs = 0; // give up IP address if it was leased if (dhcp_active) { ip_cpy(config_ip.ip, "\0\0\0\0"); ip_cpy(config_ip.mask, "\0\0\0\0"); ip_cpy(config_ip.gw, "\0\0\0\0"); dhcp_active = 0; dhcp_lease_time = 0; dhcp_lease_rest = 0; } } /// retry DHCP action static void dhcp_retry(void) { // DHCP lease not active ---> re-send DHCP discover if (! dhcp_active) { // get time for next try random_get_data(&dhcp_retry_secs, sizeof(dhcp_retry_secs)); dhcp_retry_secs = dhcp_retry_secs % dhcp_retry_secs_max + dhcp_retry_secs_max / 2; // re-send DHCP discover dhcp_discover(); } // DHCP lease active and lease time almost over ---> re-send DHCP request if (dhcp_active && (dhcp_lease_rest < dhcp_lease_rest_min || dhcp_lease_rest < dhcp_lease_time / dhcp_lease_renew_fraction)) { // get time for next try random_get_data(&dhcp_retry_secs, sizeof(dhcp_retry_secs)); dhcp_retry_secs = dhcp_retry_secs % dhcp_retry_secs_max + dhcp_retry_secs_max / 2; // re-send DHCP request dhcp_request(); } } /// start new DHCP action static void dhcp_start(void) { // DHCP lease not active ---> DHCP discover if (! dhcp_active) { // DHCP operation starts dhcp_in_progress = 1; dhcp_retries = dhcp_retries_max; random_get_data(&dhcp_retry_secs, sizeof(dhcp_retry_secs)); dhcp_retry_secs = dhcp_retry_secs % dhcp_retry_secs_max + dhcp_retry_secs_max / 2; // get new x_id random_get_data(&dhcp_x_id, sizeof(dhcp_x_id)); dhcp_x_id_secs++; dhcp_have_x_id = 1; // send DHCP discover dhcp_discover(); } // DHCP lease active and lease time almost over ---> DHCP request if (dhcp_active && (dhcp_lease_rest < dhcp_lease_rest_min || dhcp_lease_rest < dhcp_lease_time / dhcp_lease_renew_fraction)) { // DHCP operation starts dhcp_in_progress = 1; dhcp_retries = dhcp_retries_max; random_get_data(&dhcp_retry_secs, sizeof(dhcp_retry_secs)); dhcp_retry_secs = dhcp_retry_secs % dhcp_retry_secs_max + dhcp_retry_secs_max / 2; // get new x_id if none available if (! dhcp_have_x_id) { random_get_data(&dhcp_x_id, sizeof(dhcp_x_id)); dhcp_x_id_secs++; dhcp_have_x_id = 1; } // send DHCP request dhcp_request(); } } /// tick procedure - every 1000ms static void dhcp_tick1000(void) { // DHCP operation in progress if (dhcp_in_progress) { // count down time of current try of current action dhcp_retry_secs--; if (dhcp_retry_secs <= 0) { // count down retries dhcp_retries--; // no more retries left ---> abort, retry otherwise if (dhcp_retries <= 0) dhcp_abort(); else dhcp_retry(); } } // increase time of x_id dhcp_x_id_secs++; // dcrease remaining lease time dhcp_lease_rest--; // lease timed out ---> abort DHCP (will also invalidate IP) if (dhcp_lease_rest <= 0) dhcp_abort(); } /// tick procedure - call every 200ms void dhcp_tick200(void) { // get 1 second interval dhcp_ticks++; if (dhcp_ticks >= 5) { dhcp_ticks = 0; dhcp_tick1000(); } // no DHCP operation in progress if (! dhcp_in_progress) { // DHCP lease active or no IP address ---> DHCP allowed (i.e. no static IP) if (dhcp_active || ip_eq(config_ip.ip, "\0\0\0\0")) { // start new DHCP action dhcp_start(); } } } /** * @brief process a received DHCP offer * @param[in] addr assigned IP address * @param[in] mask assigned subnet mask * @param[in] gateway assigned gateway IP address * @param[in] time lease time in seconds * @param[in] DHCP server IP address */ static void dhcp_offer(unsigned char addr[4], unsigned char mask[4], unsigned char gateway[4], unsigned long time, unsigned char server[4]) { // use this IP address, mask and gateway ip_cpy(config_ip.ip, addr); ip_cpy(config_ip.mask, mask); ip_cpy(config_ip.gw, gateway); // store DHCP server and lease time dhcp_active = 1; ip_cpy(dhcp_server, server); dhcp_lease_time = time; dhcp_lease_rest = dhcp_lease_rest_min; // schedule DHCP request // DHCP action finished dhcp_in_progress = 0; dhcp_retries = 0; dhcp_retry_secs = 0; } /** * @brief process a received DHCP ack * @param[in] addr assigned IP address * @param[in] mask assigned subnet mask * @param[in] gateway assigned gateway IP address * @param[in] time lease time in seconds * @param[in] DHCP server IP address */ static void dhcp_ack(unsigned char addr[4], unsigned char mask[4], unsigned char gateway[4], unsigned long time, unsigned char server[4]) { // check if IP address, mask and gateway match if (! ip_eq(config_ip.ip, addr) || ! ip_eq(config_ip.mask, mask) || ! ip_eq(config_ip.gw, gateway)) { // mismatch ---> something is wrong, abort dhcp_abort(); return; } // store DHCP server and lease time dhcp_active = 1; ip_cpy(dhcp_server, server); dhcp_lease_time = time; dhcp_lease_rest = time; // DHCP action finished dhcp_in_progress = 0; dhcp_retries = 0; dhcp_retry_secs = 0; // forget x_id dhcp_have_x_id = 0; dhcp_x_id = 0; dhcp_x_id_secs = 0; } /** * @brief process a received DHCP packet * @param[in] ptr pointer to data of packet * @param[in] sz size of packet */ void dhcp_recv(void *ptr, unsigned int sz) { struct dhcp_packet *p_dhcp_pack; unsigned char *opt_ptr, tag, len; unsigned short opt_len; unsigned char type; unsigned char have_mask, mask[4], have_gateway, gateway[4]; unsigned char have_time, have_server, server[4]; unsigned long time; // packet too short if (sz < sizeof(struct dhcp_packet)) return; p_dhcp_pack = ptr; // we are DHCP client // - source port must be 67 // - destination port must be 68 if (p_dhcp_pack->udp_hdr.src_port != htons(67) || p_dhcp_pack->udp_hdr.dest_port != htons(68)) return; // check DHCP fields if (p_dhcp_pack->dhcp_hdr.op != 2 || p_dhcp_pack->dhcp_hdr.h_type != 1 || p_dhcp_pack->dhcp_hdr.h_len != 6 || p_dhcp_pack->dhcp_hdr.h_ops != 0 || p_dhcp_pack->dhcp_hdr.m_cookie != htonl(0x63825363)) return; // check x_id if (! dhcp_have_x_id || ntohl(p_dhcp_pack->dhcp_hdr.x_id) != dhcp_x_id) return; // get options opt_ptr = ptr + sizeof(struct dhcp_packet); opt_len = sz - sizeof(struct dhcp_packet); // parse options type = 0; have_mask = 0; have_gateway = 0; time = 0; have_time = 0; have_server = 0; while (opt_len > 2) { // get tag and length tag = *opt_ptr++; len = *opt_ptr++; if (opt_len < 2 + len) break; // get option switch (tag) { // type case 0x35: if (len >= 1) type = opt_ptr[0]; break; // subnet mask case 0x01: if (len >= 4) memcpy(mask, opt_ptr, 4); have_mask = 1; break; // gateway case 0x03: if (len >= 4) { memcpy(gateway, opt_ptr, 4); have_gateway = 1; } break; // lease time case 0x33: if (len >= 4) { time = ntohl(*(unsigned long *)opt_ptr); have_time = 1; } break; // DHCP server case 0x36: if (len >= 4) { memcpy(server, opt_ptr, 4); have_server = 1; } break; } // skip option opt_ptr += len; opt_len -= len; } // fill in server from source address if not present if (! have_server) { ip_cpy(server, p_dhcp_pack->ip_hdr.src); have_server = 1; } // process DHCP response switch (type) { // DHCP offer case 2: if (have_mask && have_gateway && have_time && have_server) dhcp_offer(p_dhcp_pack->dhcp_hdr.yi_addr, mask, gateway, time, server); break; // DHCP ack case 5: if (have_mask && have_gateway && have_time && have_server) dhcp_ack(p_dhcp_pack->dhcp_hdr.yi_addr, mask, gateway, time, server); break; } }