From af3be9a067e6603134eefa4b4c640e8520d459ea Mon Sep 17 00:00:00 2001 From: logicog Date: Thu, 25 Dec 2025 21:24:01 +0100 Subject: [PATCH] Initial DHCP implementation --- dhcp.c | 391 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ dhcp.h | 48 +++++++ 2 files changed, 439 insertions(+) create mode 100644 dhcp.c create mode 100644 dhcp.h diff --git a/dhcp.c b/dhcp.c new file mode 100644 index 0000000..ed49b5e --- /dev/null +++ b/dhcp.c @@ -0,0 +1,391 @@ +/* + * This is a DHCP client implementation for the RTL837x-based switches + */ + +// #define REGDBG +// #define DEBUG + +#include +#include "rtl837x_sfr.h" +#include "rtl837x_common.h" +#include "dhcp.h" +#include "uip.h" +#include "uip/uip.h" + +extern __code struct uip_eth_addr uip_ethaddr; +__xdata struct dhcp_state dhcp_state; +__xdata uip_ipaddr_t server; + +#define DHCP_HW_TYPE_ETH 1 + +#define DHCP_SUBNET_MASK 1 +#define DHCP_SUBNET_MASK_LEN 4 +#define DHCP_ROUTER 3 +#define DHCP_ROUTER_LEN 4 +#define DHCP_DNS 6 +#define DHCP_DNS_LEN 4 +#define DHCP_BROADCAST 28 +#define DHCP_BROADCAST_LEN 4 +#define DHCP_SERVER_ID 54 +#define DHCP_SERVER_ID_LEN 4 +#define DHCP_MESSAGE_TYPE 53 +#define DHCP_MESSAGE_TYPE_LEN 1 +#define DHCP_MESSAGE_DISCOVER 1 +#define DHCP_MESSAGE_OFFER 2 +#define DHCP_MESSAGE_REQUEST 3 +#define DHCP_MESSAGE_ACK 5 +#define DHCP_LEASE 51 +#define DHCP_LEASE_LEN 4 +#define DHCP_RENEWAL 58 +#define DHCP_RENEWAL_LEN 4 +#define DHCP_REBIND 59 +#define DHCP_REBIND_LEN 4 +#define DHCP_CLIENT_ID 61 +#define DHCP_CLIENT_ID_LEN 7 +#define DHCP_REQUEST_IP 50 +#define DHCP_REQUEST_IP_LEN 4 +#define DHCP_PARAMS 55 +#define DHCP_PARAM_SUBNET 1 +#define DHCP_PARAM_ROUTER 3 +#define DHCP_PARAM_DNS 6 +#define DHCP_END 255 + +#pragma codeseg BANK2 +#pragma constseg BANK2 + +struct dhcp_pkt { + uint8_t type; + uint8_t hw; + uint8_t hw_len; + uint8_t hops; + uint32_t tid; + uint16_t delay; + uint16_t flags; + uint8_t client_ip[4]; + uint8_t your_ip[4]; + uint8_t next_server_ip[4]; + uint8_t relay_ip[4]; + uint8_t client_addr[6]; + uint8_t client_pad[10]; + uint8_t server_name[64]; + uint8_t file[128]; + uint8_t cookie[4]; +}; + +/* +#define DHCP_P ((__xdata struct dhcp_pkt *)&uip_buf[RTL_TAG_SIZE + VLAN_TAG_SIZE]) +#define DHCP_OPT ((__xdata uint8_t *)&uip_buf[RTL_TAG_SIZE + VLAN_TAG_SIZE + sizeof (struct dhcp_pkt)]) +*/ + +#define DHCP_P ((__xdata struct dhcp_pkt *)uip_appdata) +#define DHCP_OPT ((__xdata uint8_t *)(uip_appdata) + sizeof (struct dhcp_pkt)) + +__xdata uint32_t long_value; + + +void dhcp_print_ip(uint8_t *a) +{ + itoa(a[0]); write_char('.'); + itoa(a[1]); write_char('.'); + itoa(a[2]); write_char('.'); + itoa(a[3]); +} + + +void dhcp_prepare_request(void) +{ + DHCP_P->type = 1; + DHCP_P->hw = DHCP_HW_TYPE_ETH; + DHCP_P->hw_len = 6; + DHCP_P->hops = 0; + + DHCP_P->tid = HTONS(dhcp_state.transaction_id); + DHCP_P->delay = HTONS(0); + DHCP_P->flags = 0; + // Clear fields client_ip to bootp_file + memset(DHCP_P->client_ip, 0, 224); + memcpyc(DHCP_P->client_addr, uip_ethaddr.addr, 6); + DHCP_P->cookie[0] = 0x63; + DHCP_P->cookie[1] = 0x82; + DHCP_P->cookie[2] = 0x53; + DHCP_P->cookie[3] = 0x63; +} + + +void dhcp_addopt_client_id(void) +{ + DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_CLIENT_ID; + DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_CLIENT_ID_LEN; + DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_HW_TYPE_ETH; + memcpyc(&DHCP_OPT[dhcp_state.opt_ptr], uip_ethaddr.addr, 6); + dhcp_state.opt_ptr += 6; +} + + +void dhcp_addopt_request_ip(void) +{ + DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_REQUEST_IP; + DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_REQUEST_IP_LEN; + DHCP_OPT[dhcp_state.opt_ptr++] = dhcp_state.current_ip[0]; + DHCP_OPT[dhcp_state.opt_ptr++] = dhcp_state.current_ip[1]; + DHCP_OPT[dhcp_state.opt_ptr++] = dhcp_state.current_ip[2]; + DHCP_OPT[dhcp_state.opt_ptr++] = dhcp_state.current_ip[3]; + memcpyc(&DHCP_OPT[dhcp_state.opt_ptr], uip_ethaddr.addr, 4); +} + + +void dhcp_addopt_server_id(void) +{ + DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_SERVER_ID; + DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_SERVER_ID_LEN; + DHCP_OPT[dhcp_state.opt_ptr++] = dhcp_state.server[0]; + DHCP_OPT[dhcp_state.opt_ptr++] = dhcp_state.server[1]; + DHCP_OPT[dhcp_state.opt_ptr++] = dhcp_state.server[2]; + DHCP_OPT[dhcp_state.opt_ptr++] = dhcp_state.server[3]; + memcpyc(&DHCP_OPT[dhcp_state.opt_ptr], uip_ethaddr.addr, 4); +} + + +void dhcp_send_discover(void) +{ + print_string("dhcp_send_discover called\n"); + dhcp_prepare_request(); + + dhcp_state.opt_ptr = 0; + DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_MESSAGE_TYPE; + DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_MESSAGE_TYPE_LEN; + DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_MESSAGE_DISCOVER; + + dhcp_addopt_client_id(); + dhcp_addopt_request_ip(); + + DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_PARAMS; + DHCP_OPT[dhcp_state.opt_ptr++] = 3; + DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_PARAM_SUBNET; + DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_PARAM_ROUTER; + DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_PARAM_DNS; + + DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_END; + // Padding to 300 bytes + DHCP_OPT[dhcp_state.opt_ptr++] = 0; + DHCP_OPT[dhcp_state.opt_ptr++] = 0; + DHCP_OPT[dhcp_state.opt_ptr++] = 0; + DHCP_OPT[dhcp_state.opt_ptr++] = 0; + DHCP_OPT[dhcp_state.opt_ptr++] = 0; + DHCP_OPT[dhcp_state.opt_ptr++] = 0; + + uip_udp_send(sizeof(struct dhcp_pkt) + dhcp_state.opt_ptr); + dhcp_state.state = DHCP_DISCOVER_SENT; + dhcp_state.ticks = SYS_TICK_HZ; + dhcp_state.dhcp_timer = 30; // Timeout for discover +} + + +void dhcp_send_request(void) +{ + print_string("dhcp_send_request called\n"); + dhcp_prepare_request(); + + dhcp_state.opt_ptr = 0; + DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_MESSAGE_TYPE; + DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_MESSAGE_TYPE_LEN; + DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_MESSAGE_REQUEST; + + dhcp_addopt_client_id(); + dhcp_addopt_request_ip(); + dhcp_addopt_server_id(); + + DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_PARAMS; + DHCP_OPT[dhcp_state.opt_ptr++] = 3; + DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_PARAM_SUBNET; + DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_PARAM_ROUTER; + DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_PARAM_DNS; + + DHCP_OPT[dhcp_state.opt_ptr++] = DHCP_END; + // Padding to 300 bytes + DHCP_OPT[dhcp_state.opt_ptr++] = 0; + DHCP_OPT[dhcp_state.opt_ptr++] = 0; + DHCP_OPT[dhcp_state.opt_ptr++] = 0; + DHCP_OPT[dhcp_state.opt_ptr++] = 0; + DHCP_OPT[dhcp_state.opt_ptr++] = 0; + DHCP_OPT[dhcp_state.opt_ptr++] = 0; + + uip_udp_send(sizeof(struct dhcp_pkt) + dhcp_state.opt_ptr); + dhcp_state.state = DHCP_REQUEST_SENT; + dhcp_state.ticks = SYS_TICK_HZ; + dhcp_state.dhcp_timer = 30; // Timeout for request +} + + +void ip_opt(uint8_t * __xdata ip) +{ + dhcp_state.opt_ptr++; + uint8_t len = DHCP_OPT[dhcp_state.opt_ptr++]; + *ip++ = DHCP_OPT[dhcp_state.opt_ptr++]; + *ip++ = DHCP_OPT[dhcp_state.opt_ptr++]; + *ip++ = DHCP_OPT[dhcp_state.opt_ptr++]; + *ip++ = DHCP_OPT[dhcp_state.opt_ptr++]; + // There may be more than one IP option, such as 2 DNS servers advertised + dhcp_state.opt_ptr += len - 4; +} + + +void long_opt(void) +{ + dhcp_state.opt_ptr++; + dhcp_state.opt_ptr++; + long_value = DHCP_OPT[dhcp_state.opt_ptr++]; + long_value <<= 8; + long_value |= DHCP_OPT[dhcp_state.opt_ptr++]; + long_value <<= 8; + long_value |= DHCP_OPT[dhcp_state.opt_ptr++]; + long_value <<= 8; + long_value |= DHCP_OPT[dhcp_state.opt_ptr++]; +} + + +void parse_opts(void) +{ + while (DHCP_OPT[dhcp_state.opt_ptr] && DHCP_OPT[dhcp_state.opt_ptr] != DHCP_END) { + switch(DHCP_OPT[dhcp_state.opt_ptr]) { + case DHCP_SUBNET_MASK: + ip_opt(&dhcp_state.subnet[0]); + break; + case DHCP_ROUTER: + ip_opt(&dhcp_state.router[0]); + break; + case DHCP_DNS: + ip_opt(&dhcp_state.dns[0]); + break; + case DHCP_SERVER_ID: + ip_opt(&dhcp_state.server[0]); + break; + case DHCP_BROADCAST: + ip_opt(&dhcp_state.broadcast[0]); + break; + case DHCP_LEASE: + long_opt(); + dhcp_state.lease = long_value; + break; + case DHCP_REBIND: + long_opt(); + dhcp_state.rebind = long_value; + break; + case DHCP_RENEWAL: + long_opt(); + dhcp_state.renewal = long_value; + break; + case DHCP_END: + break; + default: + print_string("Unknown DHCP option: "); print_byte(DHCP_OPT[dhcp_state.opt_ptr]); write_char('\n'); + dhcp_state.opt_ptr++; + dhcp_state.opt_ptr += DHCP_OPT[dhcp_state.opt_ptr]; + dhcp_state.opt_ptr++; + } + } +} + + +void parse_dhcp(void) +{ + if (!DHCP_P->tid == HTONS(dhcp_state.transaction_id)) + return; + if (DHCP_P->cookie[0] != 0x63 || DHCP_P->cookie[1] != 0x82 || DHCP_P->cookie[2] != 0x53 || DHCP_P->cookie[3] != 0x63) + return; + + dhcp_state.opt_ptr = 0; + if (DHCP_OPT[dhcp_state.opt_ptr++] != DHCP_MESSAGE_TYPE || DHCP_OPT[dhcp_state.opt_ptr++] != DHCP_MESSAGE_TYPE_LEN) + return; + if (DHCP_OPT[dhcp_state.opt_ptr] == DHCP_MESSAGE_OFFER) { + dhcp_state.opt_ptr++; + dhcp_state.current_ip[0] = DHCP_P->your_ip[0]; + dhcp_state.current_ip[1] = DHCP_P->your_ip[1]; + dhcp_state.current_ip[2] = DHCP_P->your_ip[2]; + dhcp_state.current_ip[3] = DHCP_P->your_ip[3]; + parse_opts(); + print_string("DHCP offer received for IP "); dhcp_print_ip(dhcp_state.current_ip); + write_char('\n'); + dhcp_send_request(); + } else if (DHCP_OPT[dhcp_state.opt_ptr++] == DHCP_MESSAGE_ACK) { + parse_opts(); + print_string("DHCP ACK, our IP is "); dhcp_print_ip(dhcp_state.current_ip); + write_char('\n'); + print_string("DHCP netmask "); dhcp_print_ip(dhcp_state.subnet); + write_char('\n'); + print_string("DHCP gateway "); dhcp_print_ip(dhcp_state.router); + write_char('\n'); + print_string("DHCP lease-time "); + print_long(dhcp_state.lease); + write_char('\n'); + uip_ipaddr(&uip_hostaddr, dhcp_state.current_ip[0], dhcp_state.current_ip[1], dhcp_state.current_ip[2], dhcp_state.current_ip[3]); + uip_ipaddr(&uip_draddr, dhcp_state.router[0], dhcp_state.router[1], dhcp_state.router[2], dhcp_state.router[3]); + uip_ipaddr(&uip_netmask, dhcp_state.subnet[0], dhcp_state.subnet[1], dhcp_state.subnet[2], dhcp_state.subnet[3]); + dhcp_state.state = DHCP_LEASING; + dhcp_state.ticks = SYS_TICK_HZ; + dhcp_state.dhcp_timer = dhcp_state.renewal > 0xffff ? 0xffff : dhcp_state.renewal; + } +} + + +void dhcp_start(void) __banked +{ + uip_ipaddr(server, 255,255,255,255); + dhcp_state.conn = uip_udp_new(&server, HTONS(DHCPC_SERVER_PORT)); + dhcp_state.current_ip[0] = dhcp_state.current_ip[1] = dhcp_state.current_ip[2] = dhcp_state.current_ip[3] = 0; + if(dhcp_state.conn) { + uip_udp_bind(dhcp_state.conn, HTONS(DHCPC_CLIENT_PORT)); + } else { + print_string("dhcp_start failed to set up socket\n"); + return; + } + get_random_32(); + dhcp_state.transaction_id = SFR_DATA_U32; + dhcp_state.state = DHCP_START; + print_string("dhcp_start done\n"); +} + + +void dhcp_stop(void) __banked +{ + print_string("dhcp_stop called\n"); + uip_udp_remove(dhcp_state.conn); + dhcp_state.state = DHCP_OFF; +} + + +void dhcp_callback(void) __banked +{ + if (!dhcp_state.state) + return; + if (uip_closed()) { + print_string("Closed\n"); + return; + } else if (uip_newdata()) { + parse_dhcp(); + } else { + if (dhcp_state.state == DHCP_START) { + dhcp_send_discover(); + } else if (!--dhcp_state.ticks) { +// print_string("Timer: "); print_short(dhcp_state.ticks); write_char(' '); print_short(dhcp_state.dhcp_timer); + dhcp_state.dhcp_timer--; + dhcp_state.ticks = SYS_TICK_HZ; + } + if (!dhcp_state.dhcp_timer) { + switch (dhcp_state.state) { + case DHCP_DISCOVER_SENT: + dhcp_send_discover(); + break; + case DHCP_LEASING: + case DHCP_REQUEST_SENT: + dhcp_send_request(); + break; + default: + print_string("UNKNOWN STATE\n"); + } + } + } + + // By default we do not send anything out + uip_len = 0; +} diff --git a/dhcp.h b/dhcp.h new file mode 100644 index 0000000..fdcc35d --- /dev/null +++ b/dhcp.h @@ -0,0 +1,48 @@ +#ifndef _DHCP_H_ +#define _DHCP_H_ + +#include "uipopt.h" +#include + +#define DHCPC_SERVER_PORT 67 +#define DHCPC_CLIENT_PORT 68 + +#define DHCP_OFF 0 +#define DHCP_START 1 +#define DHCP_DISCOVER_SENT 2 +#define DHCP_REQUEST_SENT 3 +#define DHCP_LEASING 4 + +void dhcp_start(void) __banked; +void dhcp_stop(void) __banked; +// void dhcp_periodic(void) __banked; +void dhcp_callback(void) __banked; + + +struct dhcp_state { + uint8_t state; + uint32_t transaction_id; + uint16_t dhcp_timer; + uint8_t ticks; + uint16_t opt_ptr; + uint8_t current_ip[4]; + uint8_t server[4]; + uint8_t router[4]; + uint8_t subnet[4]; + uint8_t dns[4]; + uint8_t broadcast[4]; + uint32_t lease; + uint32_t rebind; + uint32_t renewal; + + struct uip_udp_conn *conn; +}; + +typedef struct dhcp_state uip_udp_appstate_t; + +/* Finally we define the application function to be called by uIP. */ +#ifndef UIP_UDP_APPCALL +#define UIP_UDP_APPCALL dhcp_callback +#endif /* UIP_APPCALL */ + +#endif