/* Adapted by Simon Berg from net/dhcpc.c */ /* * Copyright (c) 2005, Swedish Institute of Computer Science * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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. * * This file is part of the Contiki operating system. * * @(#)$Id: dhcps.c,v 1.1 2009/07/11 14:37:11 ksb Exp $ */ #include #include #include #include "contiki.h" #include "contiki-net.h" #include "dhcps.h" struct dhcp_msg { uint8_t op, htype, hlen, hops; uint8_t xid[4]; uint16_t secs, flags; uint8_t ciaddr[4]; uint8_t yiaddr[4]; uint8_t siaddr[4]; uint8_t giaddr[4]; uint8_t chaddr[16]; #ifndef UIP_CONF_DHCP_LIGHT uint8_t sname[64]; uint8_t file[128]; #endif uint8_t options[312]; } CC_BYTE_ALIGNED; #define BOOTP_BROADCAST 0x8000 #define DHCP_REQUEST 1 #define DHCP_REPLY 2 #define DHCP_HTYPE_ETHERNET 1 #define DHCP_HLEN_ETHERNET 6 #define DHCP_MSG_LEN 236 #define DHCPS_SERVER_PORT 67 #define DHCPS_CLIENT_PORT 68 #define DHCPDISCOVER 1 #define DHCPOFFER 2 #define DHCPREQUEST 3 #define DHCPDECLINE 4 #define DHCPACK 5 #define DHCPNAK 6 #define DHCPRELEASE 7 #define DHCPINFORM 8 #define DHCP_OPTION_SUBNET_MASK 1 #define DHCP_OPTION_ROUTER 3 #define DHCP_OPTION_DNS_SERVER 6 #define DHCP_OPTION_REQ_IPADDR 50 #define DHCP_OPTION_LEASE_TIME 51 #define DHCP_OPTION_MSG_TYPE 53 #define DHCP_OPTION_SERVER_ID 54 #define DHCP_OPTION_REQ_LIST 55 #define DHCP_OPTION_END 255 #define LEASE_FLAGS_ALLOCATED 0x01 /* Lease with an allocated address*/ #define LEASE_FLAGS_VALID 0x02 /* Contains a valid but possibly outdated lease */ static const struct dhcps_config *config; static uint8_t * find_option(uint8_t option) { struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata; uint8_t *optptr = &m->options[4]; uint8_t *end = (uint8_t*)uip_appdata + uip_datalen(); while(optptr < end && *optptr != DHCP_OPTION_END) { if(*optptr == option) { return optptr; } optptr += optptr[1] + 2; } return NULL; } static const uint8_t magic_cookie[4] = {99, 130, 83, 99}; static int check_cookie(void) { struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata; return memcmp(m->options, magic_cookie, 4) == 0; } /* Finds any valid lease for a given MAC address */ static struct dhcps_client_lease * lookup_lease_mac(const uint8_t *chaddr, uint8_t hlen) { struct dhcps_client_lease *lease = config->leases; struct dhcps_client_lease *end = config->leases + config->num_leases; while(lease != end) { if (lease->flags & LEASE_FLAGS_VALID && memcmp(lease->chaddr, chaddr, hlen) == 0) { return lease; } lease++; } return NULL; } static struct dhcps_client_lease * lookup_lease_ip(const uip_ipaddr_t *ip) { struct dhcps_client_lease *lease = config->leases; struct dhcps_client_lease *end = config->leases + config->num_leases; while(lease != end) { if (uip_ipaddr_cmp(&lease->ipaddr, ip)) { return lease; } lease++; } return NULL; } static struct dhcps_client_lease * find_free_lease(void) { struct dhcps_client_lease *found = NULL; struct dhcps_client_lease *lease = config->leases; struct dhcps_client_lease *end = config->leases + config->num_leases; while(lease != end) { if (!(lease->flags & LEASE_FLAGS_VALID)) return lease; if (!(lease->flags & LEASE_FLAGS_ALLOCATED)) found = lease; lease++; } return found; } struct dhcps_client_lease * init_lease(struct dhcps_client_lease *lease, const uint8_t *chaddr, uint8_t hlen) { if (lease) { memcpy(lease->chaddr, chaddr, hlen); lease->flags = LEASE_FLAGS_VALID; } return lease; } static struct dhcps_client_lease * choose_address() { struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata; struct dhcps_client_lease *lease; lease = lookup_lease_mac(m->chaddr, m->hlen); if (lease) { return lease; } { uint8_t *opt; opt = find_option(DHCP_OPTION_REQ_IPADDR); if (opt && (lease = lookup_lease_ip((uip_ipaddr_t*)&opt[2])) && !(lease->flags & LEASE_FLAGS_ALLOCATED)) { return init_lease(lease, m->chaddr,m->hlen); } } lease = find_free_lease(); if (lease) { return init_lease(lease, m->chaddr,m->hlen); } return NULL; } static struct dhcps_client_lease * allocate_address() { struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata; struct dhcps_client_lease *lease; lease = lookup_lease_mac(m->chaddr, m->hlen); if (!lease) { uint8_t *opt; opt = find_option(DHCP_OPTION_REQ_IPADDR); if (!(opt && (lease = lookup_lease_ip((uip_ipaddr_t*)&opt[2])) && !(lease->flags & LEASE_FLAGS_ALLOCATED))) { return NULL; } } lease->lease_end = clock_seconds()+config->default_lease_time; lease->flags |= LEASE_FLAGS_ALLOCATED; return lease; } static struct dhcps_client_lease * release_address() { struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata; struct dhcps_client_lease *lease; lease = lookup_lease_mac(m->chaddr, m->hlen); if (!lease) { return NULL; } lease->flags &= ~LEASE_FLAGS_ALLOCATED; return lease; } /*---------------------------------------------------------------------------*/ static uint8_t * add_msg_type(uint8_t *optptr, uint8_t type) { *optptr++ = DHCP_OPTION_MSG_TYPE; *optptr++ = 1; *optptr++ = type; return optptr; } /*---------------------------------------------------------------------------*/ static uint8_t * add_server_id(uint8_t *optptr) { *optptr++ = DHCP_OPTION_SERVER_ID; *optptr++ = 4; memcpy(optptr, &uip_hostaddr, 4); return optptr + 4; } /*---------------------------------------------------------------------------*/ static uint8_t * add_lease_time(uint8_t *optptr) { uint32_t lt; *optptr++ = DHCP_OPTION_LEASE_TIME; *optptr++ = 4; lt = HTONL(config->default_lease_time); memcpy(optptr, <, 4); return optptr + 4; } /*---------------------------------------------------------------------------*/ static uint8_t * add_end(uint8_t *optptr) { *optptr++ = DHCP_OPTION_END; return optptr; } static uint8_t * add_config(uint8_t *optptr) { if (config->flags & DHCP_CONF_NETMASK) { *optptr++ = DHCP_OPTION_SUBNET_MASK; *optptr++ = 4; memcpy(optptr, &config->netmask, 4); optptr += 4; } if (config->flags & DHCP_CONF_DNSADDR) { *optptr++ = DHCP_OPTION_DNS_SERVER; *optptr++ = 4; memcpy(optptr, &config->dnsaddr, 4); optptr += 4; } if (config->flags & DHCP_CONF_DEFAULT_ROUTER) { *optptr++ = DHCP_OPTION_ROUTER; *optptr++ = 4; memcpy(optptr, &config->default_router, 4); optptr += 4; } return optptr; } static void create_msg(CC_REGISTER_ARG struct dhcp_msg *m) { m->op = DHCP_REPLY; /* m->htype = DHCP_HTYPE_ETHERNET; */ /* m->hlen = DHCP_HLEN_ETHERNET; */ /* memcpy(m->chaddr, &uip_ethaddr,DHCP_HLEN_ETHERNET); */ m->hops = 0; m->secs = 0; memcpy(m->siaddr, &uip_hostaddr, 4); m->sname[0] = '\0'; m->file[0] = '\0'; memcpy(m->options, magic_cookie, sizeof(magic_cookie)); } static uip_ipaddr_t any_addr; static uip_ipaddr_t bcast_addr; /*---------------------------------------------------------------------------*/ static void send_offer(struct uip_udp_conn *conn, struct dhcps_client_lease *lease) { u8_t *end; struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata; create_msg(m); memcpy(&m->yiaddr, &lease->ipaddr,4); end = add_msg_type(&m->options[4], DHCPOFFER); end = add_server_id(end); end = add_lease_time(end); end = add_config(end); end = add_end(end); uip_ipaddr_copy(&conn->ripaddr, &bcast_addr); uip_send(uip_appdata, (int)(end - (u8_t *)uip_appdata)); } static void send_ack(struct uip_udp_conn *conn, struct dhcps_client_lease *lease) { u8_t *end; uip_ipaddr_t ciaddr; struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata; create_msg(m); memcpy(&m->yiaddr, &lease->ipaddr,4); end = add_msg_type(&m->options[4], DHCPACK); end = add_server_id(end); end = add_lease_time(end); end = add_config(end); end = add_end(end); memcpy(&ciaddr, &lease->ipaddr,4); uip_ipaddr_copy(&conn->ripaddr, &bcast_addr); uip_send(uip_appdata, (int)(end - (u8_t *)uip_appdata)); printf("ACK\n"); } static void send_nack(struct uip_udp_conn *conn) { u8_t *end; struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata; create_msg(m); memset(&m->yiaddr, 0, 4); end = add_msg_type(&m->options[4], DHCPNAK); end = add_server_id(end); end = add_end(end); uip_ipaddr_copy(&conn->ripaddr, &bcast_addr); uip_send(uip_appdata, (int)(end - (u8_t *)uip_appdata)); printf("NACK\n"); } /*---------------------------------------------------------------------------*/ PROCESS(dhcp_server_process, "DHCP server"); /*---------------------------------------------------------------------------*/ PROCESS_THREAD(dhcp_server_process, ev , data) { static struct uip_udp_conn *conn; static struct uip_udp_conn *send_conn; static struct dhcps_client_lease *lease; PROCESS_BEGIN(); printf("DHCP server starting\n"); uip_ipaddr(&any_addr, 0,0,0,0); uip_ipaddr(&bcast_addr, 255,255,255,255); conn = udp_new(&any_addr, HTONS(DHCPS_CLIENT_PORT), NULL); if (!conn) goto exit; send_conn = udp_new(&bcast_addr, HTONS(DHCPS_CLIENT_PORT), NULL); if (!send_conn) goto exit; uip_udp_bind(conn, HTONS(DHCPS_SERVER_PORT)); uip_udp_bind(send_conn, HTONS(DHCPS_SERVER_PORT)); while(1) { PROCESS_WAIT_EVENT(); if(ev == tcpip_event) { if (uip_newdata()) { struct dhcp_msg *m = (struct dhcp_msg *)uip_appdata; struct uip_udpip_hdr *header = (struct uip_udpip_hdr *)&uip_buf[UIP_LLH_LEN]; if (m->op == DHCP_REQUEST && check_cookie() && m->hlen <= MAX_HLEN) { uint8_t *opt = find_option(DHCP_OPTION_MSG_TYPE); if (opt) { uint8_t mtype = opt[2]; if (opt[2] == DHCPDISCOVER) { printf("Discover\n"); lease = choose_address(); if (lease) { lease->lease_end = clock_seconds()+config->default_lease_time; tcpip_poll_udp(send_conn); PROCESS_WAIT_EVENT_UNTIL(uip_poll()); send_offer(conn,lease); } } else { uint8_t *opt = find_option(DHCP_OPTION_SERVER_ID); if (!opt || uip_ipaddr_cmp((uip_ipaddr_t*)&opt[2], &uip_hostaddr)) { if (mtype == DHCPREQUEST) { printf("Request\n"); lease = allocate_address(); tcpip_poll_udp(send_conn); PROCESS_WAIT_EVENT_UNTIL(uip_poll()); if (!lease) { send_nack(send_conn); } else { send_ack(send_conn,lease); } } else if (mtype == DHCPRELEASE) { printf("Release\n"); release_address(); } else if (mtype == DHCPDECLINE) { printf("Decline\n"); } else if (mtype == DHCPINFORM) { printf("Inform\n"); } } } } } } } else if (uip_poll()) { } } exit: printf("DHCP server exiting\n"); PROCESS_END(); } void dhcps_init(const struct dhcps_config *conf) { config = conf; process_start(&dhcp_server_process,NULL); }