/* libdhcp/dhcp.c - A DHCP client * * Copyright (C) 2024 Luke T. Shumaker * SPDX-Licence-Identifier: AGPL-3.0-or-later * * ----------------------------------------------------------------------------- * https://github.com/Wiznet/ioLibrary_Driver/blob/b981401e7f3d07015619adf44c13998e13e777f9/Internet/DHCP/dhcp.c * * Copyright (c) 2013, WIZnet Co., LTD. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 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. * * Neither the name of the nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. * * SPDX-Licence-Identifier: BSD-3-Clause * * ----------------------------------------------------------------------------- * https://github.com/Wiznet/ioLibrary_Driver/blob/b981401e7f3d07015619adf44c13998e13e777f9/license.txt * * Copyright (c) 2014 WIZnet Co.,Ltd. * Copyright (c) WIZnet ioLibrary Project. * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * SPDX-Licence-Identifier: MIT */ #include #include #include /* If you want to display debug & processing message, Define _DHCP_DEBUG_ in dhcp.h */ #ifdef _DHCP_DEBUG_ # include # define debugf(fmt, ...) printf("> " fmt "\n" __VA_OPT__(,) __VA_ARGS__) #else # define debugf(fmt, ...) ((void)0) #endif /* DHCP state machine. */ #define STATE_DHCP_INIT 0 ///< Initialize #define STATE_DHCP_DISCOVER 1 ///< send DISCOVER and wait OFFER #define STATE_DHCP_REQUEST 2 ///< send REQEUST and wait ACK or NACK #define STATE_DHCP_LEASED 3 ///< ReceiveD ACK and IP leased #define STATE_DHCP_REREQUEST 4 ///< send REQUEST for maintaining leased IP #define STATE_DHCP_RELEASE 5 ///< No use #define STATE_DHCP_STOP 6 ///< Stop processing DHCP #define DHCP_FLAGSBROADCAST 0x8000 ///< The broadcast value of flags in @ref RIP_MSG #define DHCP_FLAGSUNICAST 0x0000 ///< The unicast value of flags in @ref RIP_MSG /** * https://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml#message-type-53 */ #define DHCP_MSGTYP_DISCOVER 1 #define DHCP_MSGTYP_OFFER 2 #define DHCP_MSGTYP_REQUEST 3 #define DHCP_MSGTYP_DECLINE 4 #define DHCP_MSGTYP_ACK 5 #define DHCP_MSGTYP_NAK 6 #define DHCP_MSGTYP_RELEASE 7 #define DHCP_MSGTYP_INFORM 8 #define DHCP_HOPS 0 ///< Used in hops of @ref RIP_MSG #define DHCP_SECS 0 ///< Used in secs of @ref RIP_MSG #define INFINITE_LEASETIME 0xffffffff ///< Infinite lease time #ifndef CONFIG_DHCP_OPT_SIZE # define CONFIG_DHCP_OPT_SIZE 312 #endif /** * https://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml */ #define DHCP_OPT_PAD ((uint8_t) 0) /* RFC2132: length: 0; meaning: None */ #define DHCP_OPT_SUBNET_MASK ((uint8_t) 1) /* RFC2132: length: 4; meaning: Subnet Mask Value */ #define DHCP_OPT_TIME_OFFSET ((uint8_t) 2) /* RFC2132: length: 4; meaning: Time Offset in Seconds from UTC (note: deprecated by 100 and 101 */ #define DHCP_OPT_ROUTER ((uint8_t) 3) /* RFC2132: length: N; meaning: N/4 Router addresses */ #define DHCP_OPT_TIME_SERVER ((uint8_t) 4) /* RFC2132: length: N; meaning: N/4 Timeserver addresses */ #define DHCP_OPT_NAME_SERVER ((uint8_t) 5) /* RFC2132: length: N; meaning: N/4 IEN-116 Server addresses */ #define DHCP_OPT_DOMAIN_SERVER ((uint8_t) 6) /* RFC2132: length: N; meaning: N/4 DNS Server addresses */ #define DHCP_OPT_LOG_SERVER ((uint8_t) 7) /* RFC2132: length: N; meaning: N/4 Logging Server addresses */ #define DHCP_OPT_QUOTES_SERVER ((uint8_t) 8) /* RFC2132: length: N; meaning: N/4 Quotes Server addresses */ #define DHCP_OPT_LPR_SERVER ((uint8_t) 9) /* RFC2132: length: N; meaning: N/4 Printer Server addresses */ #define DHCP_OPT_IMPRESS_SERVER ((uint8_t) 10) /* RFC2132: length: N; meaning: N/4 Impress Server addresses */ #define DHCP_OPT_RLP_SERVER ((uint8_t) 11) /* RFC2132: length: N; meaning: N/4 RLP Server addresses */ #define DHCP_OPT_HOSTNAME ((uint8_t) 12) /* RFC2132: length: N; meaning: Hostname string */ #define DHCP_OPT_BOOT_FILE_SIZE ((uint8_t) 13) /* RFC2132: length: 2; meaning: Size of boot file in 512 byte chunks */ #define DHCP_OPT_MERIT_DUMP_FILE ((uint8_t) 14) /* RFC2132: length: N; meaning: Client to dump and name the file to dump it to */ #define DHCP_OPT_DOMAIN_NAME ((uint8_t) 15) /* RFC2132: length: N; meaning: The DNS domain name of the client */ #define DHCP_OPT_SWAP_SERVER ((uint8_t) 16) /* RFC2132: length: N; meaning: Swap Server address */ #define DHCP_OPT_ROOT_PATH ((uint8_t) 17) /* RFC2132: length: N; meaning: Path name for root disk */ #define DHCP_OPT_EXTENSION_FILE ((uint8_t) 18) /* RFC2132: length: N; meaning: Path name for more BOOTP info */ #define DHCP_OPT_FORWARD_ONOFF ((uint8_t) 19) /* RFC2132: length: 1; meaning: Enable/Disable IP Forwarding */ #define DHCP_OPT_SRCRTE_ONOFF ((uint8_t) 20) /* RFC2132: length: 1; meaning: Enable/Disable Source Routing */ #define DHCP_OPT_POLICY_FILTER ((uint8_t) 21) /* RFC2132: length: N; meaning: Routing Policy Filters */ #define DHCP_OPT_MAX_DG_ASSEMBLY ((uint8_t) 22) /* RFC2132: length: 2; meaning: Max Datagram Reassembly Size */ #define DHCP_OPT_DEFAULT_IP_TTL ((uint8_t) 23) /* RFC2132: length: 1; meaning: Default IP Time to Live */ #define DHCP_OPT_MTU_TIMEOUT ((uint8_t) 24) /* RFC2132: length: 4; meaning: Path MTU Aging Timeout */ #define DHCP_OPT_MTU_PLATEAU ((uint8_t) 25) /* RFC2132: length: N; meaning: Path MTU Plateau Table */ #define DHCP_OPT_MTU_INTERFACE ((uint8_t) 26) /* RFC2132: length: 2; meaning: Interface MTU Size */ #define DHCP_OPT_MTU_SUBNET ((uint8_t) 27) /* RFC2132: length: 1; meaning: All Subnets are Local */ #define DHCP_OPT_BROADCAST_ADDRESS ((uint8_t) 28) /* RFC2132: length: 4; meaning: Broadcast Address */ #define DHCP_OPT_MASK_DISCOVERY ((uint8_t) 29) /* RFC2132: length: 1; meaning: Perform Mask Discovery */ #define DHCP_OPT_MASK_SUPPLIER ((uint8_t) 30) /* RFC2132: length: 1; meaning: Provide Mask to Others */ #define DHCP_OPT_ROUTER_DISCOVERY ((uint8_t) 31) /* RFC2132: length: 1; meaning: Perform Router Discovery */ #define DHCP_OPT_ROUTER_REQUEST ((uint8_t) 32) /* RFC2132: length: 4; meaning: Router Solicitation Address */ #define DHCP_OPT_STATIC_ROUTE ((uint8_t) 33) /* RFC2132: length: N; meaning: Static Routing Table */ #define DHCP_OPT_TRAILERS ((uint8_t) 34) /* RFC2132: length: 1; meaning: Trailer Encapsulation */ #define DHCP_OPT_ARP_TIMEOUT ((uint8_t) 35) /* RFC2132: length: 4; meaning: ARP Cache Timeout */ #define DHCP_OPT_ETHERNET ((uint8_t) 36) /* RFC2132: length: 1; meaning: Ethernet Encapsulation */ #define DHCP_OPT_DEFAULT_TCP_TTL ((uint8_t) 37) /* RFC2132: length: 1; meaning: Default TCP Time to Live */ #define DHCP_OPT_KEEPALIVE_TIME ((uint8_t) 38) /* RFC2132: length: 4; meaning: TCP Keepalive Interval */ #define DHCP_OPT_KEEPALIVE_DATA ((uint8_t) 39) /* RFC2132: length: 1; meaning: TCP Keepalive Garbage */ #define DHCP_OPT_NIS_DOMAIN ((uint8_t) 40) /* RFC2132: length: N; meaning: NIS Domain Name */ #define DHCP_OPT_NIS_SERVERS ((uint8_t) 41) /* RFC2132: length: N; meaning: NIS Server Addresses */ #define DHCP_OPT_NTP_SERVERS ((uint8_t) 42) /* RFC2132: length: N; meaning: NTP Server Addresses */ #define DHCP_OPT_VENDOR_SPECIFIC ((uint8_t) 43) /* RFC2132: length: N; meaning: Vendor Specific Information */ #define DHCP_OPT_NETBIOS_NAME_SRV ((uint8_t) 44) /* RFC2132: length: N; meaning: NETBIOS Name Servers */ #define DHCP_OPT_NETBIOS_DIST_SRV ((uint8_t) 45) /* RFC2132: length: N; meaning: NETBIOS Datagram Distribution */ #define DHCP_OPT_NETBIOS_NODE_TYPE ((uint8_t) 46) /* RFC2132: length: 1; meaning: NETBIOS Node Type */ #define DHCP_OPT_NETBIOS_SCOPE ((uint8_t) 47) /* RFC2132: length: N; meaning: NETBIOS Scope */ #define DHCP_OPT_X_WINDOW_FONT ((uint8_t) 48) /* RFC2132: length: N; meaning: X Window Font Server */ #define DHCP_OPT_X_WINDOW_MANAGER ((uint8_t) 49) /* RFC2132: length: N; meaning: X Window Display Manager */ #define DHCP_OPT_ADDRESS_REQUEST ((uint8_t) 50) /* RFC2132: length: 4; meaning: Requested IP Address */ #define DHCP_OPT_ADDRESS_TIME ((uint8_t) 51) /* RFC2132: length: 4; meaning: IP Address Lease Time */ #define DHCP_OPT_OVERLOAD ((uint8_t) 52) /* RFC2132: length: 1; meaning: Overload "sname" or "file */ #define DHCP_OPT_DHCP_MSG_TYPE ((uint8_t) 53) /* RFC2132: length: 1; meaning: DHCP Message Type */ #define DHCP_OPT_DHCP_SERVER_ID ((uint8_t) 54) /* RFC2132: length: 4; meaning: DHCP Server Identification */ #define DHCP_OPT_PARAMETER_LIST ((uint8_t) 55) /* RFC2132: length: N; meaning: Parameter Request List */ #define DHCP_OPT_DHCP_MESSAGE ((uint8_t) 56) /* RFC2132: length: N; meaning: DHCP Error Message */ #define DHCP_OPT_DHCP_MAX_MSG_SIZE ((uint8_t) 57) /* RFC2132: length: 2; meaning: DHCP Maximum Message Size */ #define DHCP_OPT_RENEWAL_TIME ((uint8_t) 58) /* RFC2132: length: 4; meaning: DHCP Renewal (T1) Time */ #define DHCP_OPT_REBINDING_TIME ((uint8_t) 59) /* RFC2132: length: 4; meaning: DHCP Rebinding (T2) Time */ #define DHCP_OPT_CLASS_ID ((uint8_t) 60) /* RFC2132: length: N; meaning: Class Identifier */ #define DHCP_OPT_CLIENT_ID ((uint8_t) 61) /* RFC2132: length: N; meaning: Client Identifier */ #define DHCP_OPT_END ((uint8_t)255) /* RFC2132: length: 0; meaning: None */ /** * DHCP message. See RFC 2131, figure 1 (page 9). */ struct dhcp_msg { uint8_t op; /* DHCP_OP_{x} */ uint8_t htype; /* DHCP_HTYPE_{x} */ uint8_t hlen; /* length of ->chaddr (`sizeof(struct net_eth_addr)`) */ uint8_t hops; /* DHCP_HOPS */ uint32be_t xid; /* `global_xid`; this increase one every DHCP transaction */ uint16be_t secs; /* DHCP_SECS */ uint16be_t flags; /* DHCP_FLAGSBROADCAST or @ref DHCP_FLAGSUNICAST */ struct net_ip4_addr ciaddr; /* Request IP to DHCP sever */ struct net_ip4_addr yiaddr; /* Offered IP from DHCP server */ struct net_ip4_addr siaddr; /* next-server IP address (not used) */ struct net_ip4_addr giaddr; /* relay-agent IP address (not used) */ uint8_t chaddr[16]; /* client hardware (MAC) address */ uint8_t sname[64]; /* server name (not used) */ uint8_t file[128]; /* boot file name (not used) */ uint8_t options[CONFIG_DHCP_OPT_SIZE]; }; static_assert(offsetof(struct dhcp_msg, options) == 236); #define DHCP_MSG_BASE_SIZE offsetof(struct dhcp_msg, options) /* DHCP message OP code; RFC 2131 page 10*/ #define DHCP_OP_BOOTREQUEST 1 #define DHCP_OP_BOOTREPLY 2 /* DHCP hardware type; * https://www.iana.org/assignments/arp-parameters/arp-parameters.xhtml#arp-parameters-2 */ #define DHCP_HTYPE_ETHERNET 1 uint8_t DHCP_SIP[4]; // DHCP Server IP address uint8_t DHCP_REAL_SIP[4]; // For extract my DHCP server in a few DHCP server // Network information from DHCP Server struct net_ip4_addr OLD_allocated_ip = {0}; // Previous IP address struct dhcp_lease global_lease = { .lifetime = INFINITE_LEASETIME, }; int8_t dhcp_state = STATE_DHCP_INIT; // DHCP state int8_t dhcp_retry_count = 0; volatile uint32_t dhcp_tick_1s = 0; // unit 1 second uint32_t dhcp_tick_next = DHCP_WAIT_TIME ; uint32_t global_xid; // Any number struct dhcp_msg* pDHCPMSG; // Buffer pointer for DHCP processing char global_hostname[64]; struct net_eth_addr global_eth_addr; // DHCP Client MAC address. /* IP conflict check by sending ARP-request to leased IP and wait ARP-response. */ int8_t check_DHCP_leasedIP(void); /* check the timeout in DHCP process */ uint8_t check_DHCP_timeout(void); /* Initialize to timeout process. */ void reset_DHCP_timeout(void); /* Parse message as OFFER and ACK and NACK from DHCP server.*/ int8_t parseDHCPCMSG(void); /* make the common DHCP message */ void dhcp_msg_init(struct dhcp_msg *msg, size_t *optlen) { msg->op = DHCP_OP_BOOTREQUEST; msg->htype = DHCP_HTYPE_ETHERNET; msg->hlen = sizeof(global_eth_addr); msg->hops = DHCP_HOPS; msg->xid = uint32be_marshal(global_xid); msg->secs = uint16be_marshal(DHCP_SECS); msg->flags = uint16be_marshal(DHCP_FLAGSBROADCAST); msg->ciaddr = (struct net_ip4_addr){0}; msg->yiaddr = (struct net_ip4_addr){0}; msg->siaddr = (struct net_ip4_addr){0}; msg->siaddr = (struct net_ip4_addr){0}; msg->giaddr = (struct net_ip4_addr){0}; memset(msg->chaddr, 0, sizeof(msg->chaddr)); memcpy(msg->chaddr, global_eth_addr.octets, sizeof(global_eth_addr)); memset(msg->sname, 0, sizeof(msg->sname)); memset(msg->file, 0, sizeof(msg->file)); *optlen = 0; uint32be_encode(&msg->options[*optlen], MAGIC_COOKIE); *optlen += 4; } /* SEND DHCP DISCOVER */ void send_DHCP_DISCOVER(implements_net_udpsock *sock) { size_t k; dhcp_msg_init(pDHCPMSG, &k); DHCP_SIP[0]=0; DHCP_SIP[1]=0; DHCP_SIP[2]=0; DHCP_SIP[3]=0; DHCP_REAL_SIP[0]=0; DHCP_REAL_SIP[1]=0; DHCP_REAL_SIP[2]=0; DHCP_REAL_SIP[3]=0; /* Message type. */ pDHCPMSG->options[k++] = DHCP_OPT_DHCP_MSG_TYPE; pDHCPMSG->options[k++] = 1; pDHCPMSG->options[k++] = DHCP_MSGTYP_DISCOVER; /* Our Client ID. */ pDHCPMSG->options[k++] = DHCP_OPT_CLIENT_ID; pDHCPMSG->options[k++] = 1+sizeof(global_eth_addr); pDHCPMSG->options[k++] = DHCP_HTYPE_ETHERNET; memcpy(&pDHCPMSG->options[k], global_eth_addr.octets, sizeof(global_eth_addr)); k += sizeof(global_eth_addr); /* Our requested hostname. */ pDHCPMSG->options[k++] = DHCP_OPT_HOSTNAME; pDHCPMSG->options[k++] = strlen(global_hostname); strcpy((char *)&pDHCPMSG->options[k], global_hostname); k += strlen(global_hostname); /* Which parameters we want back. */ pDHCPMSG->options[k++] = DHCP_OPT_PARAMETER_LIST; pDHCPMSG->options[k++] = 6; pDHCPMSG->options[k++] = DHCP_OPT_SUBNET_MASK; pDHCPMSG->options[k++] = DHCP_OPT_ROUTER; pDHCPMSG->options[k++] = DHCP_OPT_DOMAIN_SERVER; pDHCPMSG->options[k++] = DHCP_OPT_DOMAIN_NAME; pDHCPMSG->options[k++] = DHCP_OPT_RENEWAL_TIME; pDHCPMSG->options[k++] = DHCP_OPT_REBINDING_TIME; /* End. */ pDHCPMSG->options[k++] = DHCP_OPT_END; debugf("> Send DHCP_DISCOVER"); sendto(sock, pDHCPMSG, DHCP_MSG_BASE_SIZE + k, net_ip4_broadcast_addr, DHCP_SERVER_PORT); } /* SEND DHCP REQUEST */ void send_DHCP_REQUEST(implements_net_udpsock *sock) { int i; struct net_ip4_addr ip; size_t k = 0; dhcp_msg_init(pDHCPMSG, &k); if(dhcp_state == STATE_DHCP_LEASED || dhcp_state == STATE_DHCP_REREQUEST) { pDHCPMSG->flags = uint16be_marshal(DHCP_FLAGSUNICAST); pDHCPMSG->ciaddr = global_lease.addr; ip = DHCP_SIP; } else { ip = net_ip4_broadcast_addr; } /* Message type. */ pDHCPMSG->options[k++] = DHCP_OPT_DHCP_MSG_TYPE; pDHCPMSG->options[k++] = 1; pDHCPMSG->options[k++] = DHCP_REQUEST; /* Our Client ID. */ pDHCPMSG->options[k++] = DHCP_OPT_CLIENT_ID; pDHCPMSG->options[k++] = 1+sizeof(global_eth_addr); pDHCPMSG->options[k++] = DHCP_HTYPE_ETHERNET; memcpy(&pDHCPMSG->options[k], global_eth_addr.octets, sizeof(global_eth_addr)); k += sizeof(global_eth_addr); if(ip.octets[3] == 255) { // if(dchp_state == STATE_DHCP_LEASED || dchp_state == DHCP_REREQUEST_STATE) pDHCPMSG->options[k++] = dhcpRequestedIPaddr; pDHCPMSG->options[k++] = 0x04; pDHCPMSG->options[k++] = global_lease.addr.octets[0]; pDHCPMSG->options[k++] = global_lease.addr.octets[1]; pDHCPMSG->options[k++] = global_lease.addr.octets[2]; pDHCPMSG->options[k++] = global_lease.addr.octets[3]; pDHCPMSG->options[k++] = dhcpServerIdentifier; pDHCPMSG->options[k++] = 0x04; pDHCPMSG->options[k++] = DHCP_SIP[0]; pDHCPMSG->options[k++] = DHCP_SIP[1]; pDHCPMSG->options[k++] = DHCP_SIP[2]; pDHCPMSG->options[k++] = DHCP_SIP[3]; } // host name pDHCPMSG->options[k++] = hostName; pDHCPMSG->options[k++] = 0; // length of hostname for(i = 0 ; HOST_NAME[i] != 0; i++) pDHCPMSG->options[k++] = HOST_NAME[i]; pDHCPMSG->options[k++] = hexdig(global_eth_addr[3] >> 4); pDHCPMSG->options[k++] = hexdig(global_eth_addr[3]); pDHCPMSG->options[k++] = hexdig(global_eth_addr[4] >> 4); pDHCPMSG->options[k++] = hexdig(global_eth_addr[4]); pDHCPMSG->options[k++] = hexdig(global_eth_addr[5] >> 4); pDHCPMSG->options[k++] = hexdig(global_eth_addr[5]); pDHCPMSG->options[k - (i+6+1)] = i+6; // length of hostname pDHCPMSG->options[k++] = dhcpParamRequest; pDHCPMSG->options[k++] = 0x08; pDHCPMSG->options[k++] = DHCP_OPT_SUBNET_MASK; pDHCPMSG->options[k++] = DHCP_OPT_ROUTER; pDHCPMSG->options[k++] = DHCP_OPT_DOMAIN_SERVER; pDHCPMSG->options[k++] = domainName; pDHCPMSG->options[k++] = dhcpT1value; pDHCPMSG->options[k++] = dhcpT2value; pDHCPMSG->options[k++] = performRouterDiscovery; pDHCPMSG->options[k++] = staticRoute; pDHCPMSG->options[k++] = DHCP_OPT_END; debugf("> Send DHCP_REQUEST"); sendto(sock, pDHCPMSG, DHCP_MSG_BASE_SIZE + k, ip, DHCP_SERVER_PORT); } /* SEND DHCP DHCPDECLINE */ void send_DHCP_DECLINE(implements_net_udpsock *sock) { int i; struct net_ip4_addr ip; uint16_t k = 0; makeDHCPMSG(); k = 4; // because MAGIC_COOKIE already made by makeDHCPMSG() *((uint8_t*)(&pDHCPMSG->flags)) = ((DHCP_FLAGSUNICAST & 0xFF00)>> 8); *((uint8_t*)(&pDHCPMSG->flags)+1) = (DHCP_FLAGSUNICAST & 0x00FF); // Option Request Param. pDHCPMSG->options[k++] = DHCP_OPT_DHCP_MSG_TYPE; pDHCPMSG->options[k++] = 0x01; pDHCPMSG->options[k++] = DHCP_DECLINE; /* Our Client ID. */ pDHCPMSG->options[k++] = DHCP_OPT_CLIENT_ID; pDHCPMSG->options[k++] = 1+sizeof(global_eth_addr); pDHCPMSG->options[k++] = DHCP_HTYPE_ETHERNET; memcpy(&pDHCPMSG->options[k], global_eth_addr.octets, sizeof(global_eth_addr)); k += sizeof(global_eth_addr); pDHCPMSG->options[k++] = dhcpRequestedIPaddr; pDHCPMSG->options[k++] = 0x04; pDHCPMSG->options[k++] = global_lease.addr.octets[0]; pDHCPMSG->options[k++] = global_lease.addr.octets[1]; pDHCPMSG->options[k++] = global_lease.addr.octets[2]; pDHCPMSG->options[k++] = global_lease.addr.octets[3]; pDHCPMSG->options[k++] = dhcpServerIdentifier; pDHCPMSG->options[k++] = 0x04; pDHCPMSG->options[k++] = DHCP_SIP[0]; pDHCPMSG->options[k++] = DHCP_SIP[1]; pDHCPMSG->options[k++] = DHCP_SIP[2]; pDHCPMSG->options[k++] = DHCP_SIP[3]; pDHCPMSG->options[k++] = DHCP_OPT_END; for (i = k; i < options_SIZE; i++) pDHCPMSG->options[i] = 0; //send broadcasting packet ip.octets[0] = 0xFF; ip.octets[1] = 0xFF; ip.octets[2] = 0xFF; ip.octets[3] = 0xFF; debugf("> Send DHCP_DECLINE"); sendto(sock, pDHCPMSG, RIP_MSG_SIZE, ip, DHCP_SERVER_PORT); } /* PARSE REPLY pDHCPMSG */ int8_t parseDHCPMSG(implements_net_udpsock *sock) { struct net_ip4_addr srv_addr; uint16_t srv_port; size_t len; uint8_t * p; uint8_t * e; uint8_t type = 0; uint8_t opt_len; len = recvfrom(sock, pDHCPMSG, len, &srv_addr, &srv_port); debugf("DHCP message : %d.%d.%d.%d(%d) %d received.", srv_addr.octets[0], srv_addr.octets[1], srv_addr.octets[2], srv_addr.octets[3], srv_port, len); if (srv_port == DHCP_SERVER_PORT) { // compare mac address if ( (pDHCPMSG->chaddr[0] != global_eth_addr[0]) || (pDHCPMSG->chaddr[1] != global_eth_addr[1]) || (pDHCPMSG->chaddr[2] != global_eth_addr[2]) || (pDHCPMSG->chaddr[3] != global_eth_addr[3]) || (pDHCPMSG->chaddr[4] != global_eth_addr[4]) || (pDHCPMSG->chaddr[5] != global_eth_addr[5]) ) { debugf("No My DHCP Message. This message is ignored."); return 0; } //compare DHCP server ip address if ((DHCP_SIP[0]!=0) || (DHCP_SIP[1]!=0) || (DHCP_SIP[2]!=0) || (DHCP_SIP[3]!=0)) { if ( ((srv_addr.octets[0]!=DHCP_SIP[0])|| (srv_addr.octets[1]!=DHCP_SIP[1])|| (srv_addr.octets[2]!=DHCP_SIP[2])|| (srv_addr.octets[3]!=DHCP_SIP[3])) && ((srv_addr.octets[0]!=DHCP_REAL_SIP[0])|| (srv_addr.octets[1]!=DHCP_REAL_SIP[1])|| (srv_addr.octets[2]!=DHCP_REAL_SIP[2])|| (srv_addr.octets[3]!=DHCP_REAL_SIP[3])) ) { debugf("Another DHCP sever send a response message. This is ignored."); return 0; } } p = (uint8_t *)(&pDHCPMSG->op); p = p + 240; // 240 = sizeof(RIP_MSG) + MAGIC_COOKIE size in RIP_MSG.opt - sizeof(RIP_MSG.opt) e = p + (len - 240); while ( p < e ) { switch ( *p ) { case DHCP_OPT_END : p = e; // for break while(p < e) break; case DHCP_OPT_PAD : p++; break; case DHCP_OPT_DHCP_MSG_TYPE : p++; p++; type = *p++; break; case DHCP_OPT_SUBNET_MASK : p++; p++; global_lease.subnet_mask.octets[0] = *p++; global_lease.subnet_mask.octets[1] = *p++; global_lease.subnet_mask.octets[2] = *p++; global_lease.subnet_mask.octets[3] = *p++; break; case DHCP_OPT_ROUTER : p++; opt_len = *p++; global_lease.gateway.octets[0] = *p++; global_lease.gateway.octets[1] = *p++; global_lease.gateway.octets[2] = *p++; global_lease.gateway.octets[3] = *p++; p = p + (opt_len - 4); break; case DHCP_OPT_DOMAIN_SERVER : p++; opt_len = *p++; global_lease.dns.octets[0] = *p++; global_lease.dns.octets[1] = *p++; global_lease.dns.octets[2] = *p++; global_lease.dns.octets[3] = *p++; p = p + (opt_len - 4); break; case dhcpIPaddrLeaseTime : p++; opt_len = *p++; global_lease.lifetime = *p++; global_lease.lifetime = (global_lease.lifetime << 8) + *p++; global_lease.lifetime = (global_lease.lifetime << 8) + *p++; global_lease.lifetime = (global_lease.lifetime << 8) + *p++; #ifdef _DHCP_DEBUG_ global_lease.lifetime = 10; #endif break; case dhcpServerIdentifier : p++; opt_len = *p++; DHCP_SIP[0] = *p++; DHCP_SIP[1] = *p++; DHCP_SIP[2] = *p++; DHCP_SIP[3] = *p++; DHCP_REAL_SIP[0]=srv_addr.octets[0]; DHCP_REAL_SIP[1]=srv_addr.octets[1]; DHCP_REAL_SIP[2]=srv_addr.octets[2]; DHCP_REAL_SIP[3]=srv_addr.octets[3]; break; default : p++; opt_len = *p++; p += opt_len; break; } // switch } // while } // if return type; } uint8_t DHCP_run(implements_net_udpsock *sock, dhcp_callback_t cb) { uint8_t type; uint8_t ret; if(dhcp_state == STATE_DHCP_STOP) return DHCP_STOPPED; if(getSn_SR(sock) != SOCK_UDP) socket(sock, Sn_MR_UDP, DHCP_CLIENT_PORT, 0x00); ret = DHCP_RUNNING; type = parseDHCPMSG(); switch ( dhcp_state ) { case STATE_DHCP_INIT : global_lease.addr.octets[0] = 0; global_lease.addr.octets[1] = 0; global_lease.addr.octets[2] = 0; global_lease.addr.octets[3] = 0; send_DHCP_DISCOVER(); dhcp_state = STATE_DHCP_DISCOVER; break; case STATE_DHCP_DISCOVER : if (type == DHCP_OFFER){ debugf("> Receive DHCP_OFFER"); global_lease.addr.octets[0] = pDHCPMSG->yiaddr[0]; global_lease.addr.octets[1] = pDHCPMSG->yiaddr[1]; global_lease.addr.octets[2] = pDHCPMSG->yiaddr[2]; global_lease.addr.octets[3] = pDHCPMSG->yiaddr[3]; send_DHCP_REQUEST(); dhcp_state = STATE_DHCP_REQUEST; } else ret = check_DHCP_timeout(); break; case STATE_DHCP_REQUEST : if (type == DHCP_ACK) { debugf("> Receive DHCP_ACK"); if (check_DHCP_leasedIP()) { // Network info assignment from DHCP cb(DHCP_ASSIGN, global_lease); reset_DHCP_timeout(); dhcp_state = STATE_DHCP_LEASED; } else { // IP address conflict occurred reset_DHCP_timeout(); cb(DHCP_CONFLICT, global_lease); dhcp_state = STATE_DHCP_INIT; } } else if (type == DHCP_NAK) { debugf("> Receive DHCP_NACK"); reset_DHCP_timeout(); dhcp_state = STATE_DHCP_DISCOVER; } else ret = check_DHCP_timeout(); break; case STATE_DHCP_LEASED : ret = DHCP_IP_LEASED; if ((global_lease.lifetime != INFINITE_LEASETIME) && ((global_lease.lifetime/2) < dhcp_tick_1s)) { debugf("> Maintains the IP address"); type = 0; OLD_allocated_ip[0] = global_lease.addr.octets[0]; OLD_allocated_ip[1] = global_lease.addr.octets[1]; OLD_allocated_ip[2] = global_lease.addr.octets[2]; OLD_allocated_ip[3] = global_lease.addr.octets[3]; global_xid++; send_DHCP_REQUEST(); reset_DHCP_timeout(); dhcp_state = STATE_DHCP_REREQUEST; } break; case STATE_DHCP_REREQUEST : ret = DHCP_IP_LEASED; if (type == DHCP_ACK) { dhcp_retry_count = 0; if (OLD_allocated_ip[0] != global_lease.addr.octets[0] || OLD_allocated_ip[1] != global_lease.addr.octets[1] || OLD_allocated_ip[2] != global_lease.addr.octets[2] || OLD_allocated_ip[3] != global_lease.addr.octets[3]) { ret = DHCP_IP_CHANGED; cb(DHCP_UPDATE, global_lease); debugf(">IP changed."); } else { debugf(">IP is continued."); } reset_DHCP_timeout(); dhcp_state = STATE_DHCP_LEASED; } else if (type == DHCP_NAK) { debugf("> Receive DHCP_NACK, Failed to maintain ip"); reset_DHCP_timeout(); dhcp_state = STATE_DHCP_DISCOVER; } else ret = check_DHCP_timeout(); break; default : break; } return ret; } void DHCP_stop(void) { close(DHCP_SOCKET); dhcp_state = STATE_DHCP_STOP; } uint8_t check_DHCP_timeout(void) { uint8_t ret = DHCP_RUNNING; if (dhcp_retry_count < MAX_DHCP_RETRY) { if (dhcp_tick_next < dhcp_tick_1s) { switch ( dhcp_state ) { case STATE_DHCP_DISCOVER : //debugf("<> state : STATE_DHCP_DISCOVER"); send_DHCP_DISCOVER(); break; case STATE_DHCP_REQUEST : //debugf("<> state : STATE_DHCP_REQUEST"); send_DHCP_REQUEST(); break; case STATE_DHCP_REREQUEST : //debugf("<> state : STATE_DHCP_REREQUEST"); send_DHCP_REQUEST(); break; default : break; } dhcp_tick_1s = 0; dhcp_tick_next = dhcp_tick_1s + DHCP_WAIT_TIME; dhcp_retry_count++; } } else { // timeout occurred switch(dhcp_state) { case STATE_DHCP_DISCOVER: dhcp_state = STATE_DHCP_INIT; ret = DHCP_FAILED; break; case STATE_DHCP_REQUEST: case STATE_DHCP_REREQUEST: send_DHCP_DISCOVER(); dhcp_state = STATE_DHCP_DISCOVER; break; default : break; } reset_DHCP_timeout(); } return ret; } int8_t check_DHCP_leasedIP(void) { uint8_t tmp; int32_t ret; //WIZchip RCR value changed for ARP Timeout count control tmp = getRCR(); setRCR(0x03); // IP conflict detection : ARP request - ARP reply // Broadcasting ARP Request for check the IP conflict using UDP sendto() function ret = sendto(DHCP_SOCKET, "CHECK_IP_CONFLICT", 17, global_lease.addr, 5000); // RCR value restore setRCR(tmp); if(ret == SOCKERR_TIMEOUT) { // UDP send Timeout occurred : allocated IP address is unique, DHCP Success debugf("\r\n> Check leased IP - OK"); return 1; } else { // Received ARP reply or etc : IP address conflict occur, DHCP Failed send_DHCP_DECLINE(); ret = dhcp_tick_1s; while((dhcp_tick_1s - ret) < 2) ; // wait for 1s over; wait to complete to send DECLINE message; return 0; } } void DHCP_init(uint8_t s, uint8_t * buf) { uint8_t zeroip[4] = {0,0,0,0}; getSHAR(global_eth_addr); if ((global_eth_addr[0] | global_eth_addr[1] | global_eth_addr[2] | global_eth_addr[3] | global_eth_addr[4] | global_eth_addr[5]) == 0x00) { // assigning temporary mac address, you should be set SHAR before call this function. global_eth_addr[0] = 0x00; global_eth_addr[1] = 0x08; global_eth_addr[2] = 0xdc; global_eth_addr[3] = 0x00; global_eth_addr[4] = 0x00; global_eth_addr[5] = 0x00; setSHAR(global_eth_addr); } global_hostname[0] = "W"; global_hostname[1] = "I"; global_hostname[2] = "Z"; global_hostname[3] = "n"; global_hostname[4] = "e"; global_hostname[5] = "t"; global_hostname[6] = "0123456789ABCDEF"[(global_eth_addr.octets[3] >> 4)&0xF]; global_hostname[7] = "0123456789ABCDEF"[(global_eth_addr.octets[3] >> 0)&0xF]; global_hostname[8] = "0123456789ABCDEF"[(global_eth_addr.octets[4] >> 4)&0xF]; global_hostname[9] = "0123456789ABCDEF"[(global_eth_addr.octets[4] >> 0)&0xF]; global_hostname[10] = "0123456789ABCDEF"[(global_eth_addr.octets[5] >> 4)&0xF]; global_hostname[11] = "0123456789ABCDEF"[(global_eth_addr.octets[5] >> 0)&0xF]; global_hostname[12] = '\0'; DHCP_SOCKET = s; // SOCK_DHCP pDHCPMSG = (RIP_MSG*)buf; global_xid = 0x12345678; { global_xid += global_eth_addr[3]; global_xid += global_eth_addr[4]; global_xid += global_eth_addr[5]; global_xid += (global_eth_addr[3] ^ global_eth_addr[4] ^ global_eth_addr[5]); } // WIZchip Netinfo Clear setSIPR(zeroip); setGAR(zeroip); reset_DHCP_timeout(); dhcp_state = STATE_DHCP_INIT; } /* Reset the DHCP timeout count and retry count. */ void reset_DHCP_timeout(void) { dhcp_tick_1s = 0; dhcp_tick_next = DHCP_WAIT_TIME; dhcp_retry_count = 0; } void DHCP_time_handler(void) { dhcp_tick_1s++; }