diff options
Diffstat (limited to 'rtems/rtems_dhcp.c')
-rw-r--r-- | rtems/rtems_dhcp.c | 1287 |
1 files changed, 1287 insertions, 0 deletions
diff --git a/rtems/rtems_dhcp.c b/rtems/rtems_dhcp.c new file mode 100644 index 0000000..6a153a4 --- /dev/null +++ b/rtems/rtems_dhcp.c @@ -0,0 +1,1287 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/* + * DCHP client for RTEMS + * Andrew Bythell, <abythell@nortelnetworks.com> + * based on and uses subroutines from c/src/libnetworking/nfs/bootp_subr.c + */ + +/* + * DHCP task added. + * Brendan Gannon, <bgannon@cybertec.com.au> + */ + +/* + * Added interface to terminate DHCP task, and removed panics. + * Arnout Vandecappelle <arnout@mind.be>, Essensium/Mind + * Maarten Van Es <maarten@mind.be>, Essensium/Mind + */ + +/* + * Copyright (c) 1995 Gordon Ross, Adam Glass + * Copyright (c) 1992 Regents of the University of California. + * All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratory and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + */ + +#include <machine/rtems-bsd-kernel-space.h> + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/error.h> +#include <rtems/rtems_bsdnet.h> +#include <rtems/bsdnet/servers.h> + +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#include <sys/sockio.h> +#include <sys/param.h> /* for MAXHOSTNAMELEN */ +#include <sys/systm.h> +#include <sys/socketvar.h> /* for socreat() soclose() */ +#include <sys/socket.h> + +#include <net/if.h> +#include <net/if_var.h> +#include <netinet/in.h> /* for NBO-HBO conversions */ +#include <net/if_types.h> /* for IFT_ETHER */ +#include <net/if_dl.h> /* for LLADDR */ + +#include <sys/stat.h> +#include <sys/types.h> +#include <fcntl.h> +#include <rtems/mkrootfs.h> + +#include "rtems/dhcp.h" +#include "rtems/bootp.h" + +#ifndef EALEN +#define EALEN 6 +#endif + +/* + *DHCP flags + */ +#define DHCP_BROADCAST 0x8000 +#define DHCP_UNICAST 0x0000 + +/* + * DHCP Op Codes + */ +#define DHCP_BOOTREQUEST 1 +#define DHCP_BOOTREPLY 2 + +/* + * DHCP Messages + */ +#define DHCP_DISCOVER 1 +#define DHCP_OFFER 2 +#define DHCP_REQUEST 3 +#define DHCP_DECLINE 4 +#define DHCP_ACK 5 +#define DHCP_NACK 6 +#define DHCP_RELEASE 7 + +/* + * DHCP Options + */ +#define DHCP_OPTION_PAD 0 +#define DHCP_SUBNET 1 +#define DHCP_GATEWAY 3 +#define DHCP_DNS 6 +#define DHCP_HOST 12 +#define DHCP_DOMAIN_NAME 15 +#define DHCP_NETMASK 28 +#define DHCP_REQUESTED_IP 50 +#define DHCP_LEASE 51 +#define DHCP_MESSAGE 53 +#define DHCP_SERVER 54 +#define DHCP_PARAMETERS 55 +#define DHCP_OPTION_END 255 + +/* + * Definitions from RFC + */ +struct dhcp_packet +{ + u_int8_t op; + u_int8_t htype; + u_int8_t hlen; + u_int8_t hops; + u_int32_t xid; + u_int16_t secs; + u_int16_t flags; + struct in_addr ciaddr; + struct in_addr yiaddr; + struct in_addr siaddr; + struct in_addr giaddr; + unsigned char chaddr[16]; + char sname[64]; + char file[128]; + unsigned char vend[312]; +}; + +/* + * Variables + */ +static int dhcp_option_overload = 0; +static char dhcp_gotgw = 0; +static char dhcp_gotnetmask = 0; +static char dhcp_gotserver = 0; +static char dhcp_gotlogserver = 0; +static struct sockaddr_in dhcp_netmask; +static struct sockaddr_in dhcp_gw; +static char *dhcp_hostname; +static int dhcp_message_type = 0; +static unsigned long dhcp_lease_time; +static unsigned long dhcp_elapsed_time = 0; +static const char dhcp_magic_cookie[4] = { 99, 130, 83, 99 }; +static const char dhcp_request_parameters[5] = { DHCP_SUBNET, + DHCP_GATEWAY, + DHCP_DNS, + DHCP_HOST, + DHCP_DOMAIN_NAME }; +#define NUM_NAMESERVERS \ + (sizeof rtems_bsdnet_config.name_server / sizeof rtems_bsdnet_config.name_server[0]) +static struct in_addr rtems_dhcpd_nameserver[NUM_NAMESERVERS]; +static int rtems_dhcpd_nameserver_count = 0; + +/* + * Clean any DNS entries add by a DHCP request. + */ +static void +clean_dns_entries (void) +{ + int e; + for (e = 0; e < rtems_dhcpd_nameserver_count; ++e) + { + int n; + for (n = 0; n < rtems_bsdnet_nameserver_count; ++ n) + { + if (memcmp (&rtems_dhcpd_nameserver[e], &rtems_bsdnet_nameserver[n], 4) == 0) + { + if (n < (NUM_NAMESERVERS - 1)) + memmove (&rtems_bsdnet_nameserver[n], + &rtems_bsdnet_nameserver[n + 1], + (NUM_NAMESERVERS - n - 1) * 4); + --rtems_bsdnet_nameserver_count; + } + } + } + rtems_dhcpd_nameserver_count = 0; +} + +/* + * Format an IP address in dotted decimal. + */ +static void +format_ip (unsigned long ip, char* buffer) +{ + sprintf (buffer, + "%lu.%lu.%lu.%lu", + (ip >> 24), + (ip >> 16) & 0xff, + (ip >> 8) & 0xff, + (ip & 0xff)); + + return; +} + +/* + * Print the IP setup + */ +static void +printsetup (const char *iface, + struct in_addr ip_addr, + struct in_addr mask_addr, + struct in_addr srv_addr, + struct in_addr gw_addr) +{ + unsigned long ip; + char ip_str[15]; + + printf ("dhcpc: %s: ", iface); + + ip = ntohl (ip_addr.s_addr); + format_ip (ip, ip_str); + printf ("inet: %-15s ", ip_str); + + ip = ntohl (mask_addr.s_addr); + format_ip (ip, ip_str); + printf ("mask: %-15s\n", ip_str); + + ip = ntohl (srv_addr.s_addr); + format_ip (ip, ip_str); + printf (" srv: %-15s ", ip_str); + + ip = ntohl (gw_addr.s_addr); + format_ip (ip, ip_str); + printf (" gw: %-15s\n", ip_str); + + return; +} + +/* + * Process options from the DHCP packet. + * Based on BOOTP routine. + */ +static void +process_options (unsigned char *optbuf, int optbufSize) +{ + int j = 0; + int len; + int code, ncode; + char *p; + + dhcp_message_type = 0; + + ncode = optbuf[0]; + while (j < optbufSize) + { + code = optbuf[j] = ncode; + if (code == 255) + return; + if (code == 0) + { + j++; + continue; + } + len = optbuf[j + 1]; + j += 2; + + if ((len + j) >= optbufSize) + { + printf ("Truncated field for code %d", code); + return; + } + + ncode = optbuf[j + len]; + optbuf[j + len] = '\0'; + p = (char*) &optbuf[j]; + j += len; + + /* + * Process the option + */ + switch (code) + { + case 1: + /* Subnet mask */ + if (len != 4) { + printf ("dhcpc: subnet mask len is %d\n", len); + continue; + } + memcpy (&dhcp_netmask.sin_addr, p, 4); + dhcp_gotnetmask = 1; + break; + + case 2: + /* Time offset */ + if (len != 4) { + printf ("dhcpc: time offset len is %d\n", len); + continue; + } + memcpy (&rtems_bsdnet_timeoffset, p, 4); + rtems_bsdnet_timeoffset = ntohl (rtems_bsdnet_timeoffset); + break; + + case 3: + /* Routers */ + if (len % 4) { + printf ("dhcpc: Router Len is %d\n", len); + continue; + } + if (len > 0) + { + memcpy (&dhcp_gw.sin_addr, p, 4); + dhcp_gotgw = 1; + } + break; + + case 42: + /* NTP servers */ + if (len % 4) { + printf ("dhcpc: time server Len is %d\n", len); + continue; + } + { + int tlen = 0; + while ((tlen < len) && + (rtems_bsdnet_ntpserver_count < + sizeof rtems_bsdnet_config.ntp_server / + sizeof rtems_bsdnet_config.ntp_server[0])) + { + memcpy (&rtems_bsdnet_ntpserver[rtems_bsdnet_ntpserver_count], + p + tlen, 4); + rtems_bsdnet_ntpserver_count++; + tlen += 4; + } + } + break; + + case 6: + /* Domain Name servers */ + if (len % 4) { + printf ("dhcpc: DNS Len is %d\n", len); + continue; + } + { + int dlen = 0; + while ((dlen < len) && + (rtems_dhcpd_nameserver_count < NUM_NAMESERVERS) && + (rtems_bsdnet_nameserver_count < NUM_NAMESERVERS)) + { + memcpy (&rtems_dhcpd_nameserver + [rtems_dhcpd_nameserver_count], p + dlen, 4); + rtems_dhcpd_nameserver_count++; + memcpy (&rtems_bsdnet_nameserver + [rtems_bsdnet_nameserver_count], p + dlen, 4); + rtems_bsdnet_nameserver_count++; + dlen += 4; + } + } + break; + + case 12: + /* Host name */ + if (len >= MAXHOSTNAMELEN) { + printf ("dhcpc: hostname >= %d bytes\n", MAXHOSTNAMELEN); + len = MAXHOSTNAMELEN-1; + } + if (sethostname (p, len) < 0) { + printf ("dhcpc: can't set host name"); + } + if (dhcp_hostname != NULL) + { + char *tmp = realloc (dhcp_hostname, len); + if (tmp != NULL) { + dhcp_hostname = tmp; + strncpy (dhcp_hostname, p, len); + } else { /* realloc failed */ + printf ("dhcpc: realloc failed (%s:%d)", __FILE__, __LINE__); + free (dhcp_hostname, 0); + dhcp_hostname = NULL; + } + } else { /* dhcp_hostname == NULL */ + dhcp_hostname = strndup (p, len); + } + break; + + case 7: + /* Log servers */ + if (len % 4) { + printf ("dhcpc: Log server Len is %d\n", len); + continue; + } + if (len > 0) + { + memcpy (&rtems_bsdnet_log_host_address, p, 4); + dhcp_gotlogserver = 1; + } + break; + + case 15: + /* Domain name */ + if (p[0]) + { + rtems_bsdnet_domain_name = strdup (p); + } + break; + + case 16: /* Swap server IP address. unused */ + break; + + case 50: + /* DHCP Requested IP Address */ + if (len != 4) + printf ("dhcpc: DHCP option requested IP len is %d", len); + /* + * although nothing happens here, this case keeps the client + * from complaining about unknown options. The Requested IP + * is necessary to return to the server for a DHCP REQUEST + */ + break; + + case 51: + /* DHCP Lease Length */ + if (len != 4) { + printf ("dhcpc: DHCP option lease-length len is %d", len); + continue; + } + memcpy (&dhcp_lease_time, &p[0], 4); + dhcp_lease_time = ntohl (dhcp_lease_time); + break; + + case 52: + /* DHCP option override */ + if (len != 1) { + printf ("dhcpc: DHCP option overload len is %d", len); + continue; + } + dhcp_option_overload = p[0]; + break; + + case 53: + /* DHCP message */ + if (len != 1) { + printf ("dhcpc: DHCP message len is %d", len); + continue; + } + dhcp_message_type = p[0]; + break; + + case 128: /* Site-specific option for DHCP servers that + * a) don't supply tag 54 + * and + * b) don't supply the server address in siaddr + * For example, on Solaris 2.6 in.dhcpd, include in the dhcptab: + * Bootsrv s Site,128,IP,1,1 + * and use that symbol in the macro that defines the client: + * Bootsrv=<tftp-server-ip-address> + */ + case 54: + /* DHCP server */ + if (len != 4) { + printf ("dhcpc: DHCP server len is %d", len); + continue; + } + memcpy (&rtems_bsdnet_bootp_server_address, p, 4); + dhcp_gotserver = 1; + break; + + case 66: + /* DHCP server name option */ + if (p[0]) + rtems_bsdnet_bootp_server_name = strdup (p); + break; + + case 67: + /* DHCP bootfile option */ + if (p[0]) + rtems_bsdnet_bootp_boot_file_name = strdup (p); + break; + + default: + break; + } + } +} + +/* + * Generate the packet for a DHCP DISCOVER. + */ +static int +dhcp_discover_req (struct dhcp_packet* call, + struct sockaddr_dl *sdl, + unsigned long *xid) +{ + int len = 0; + + memset (call, 0, sizeof (struct dhcp_packet)); + + /* + * Send a DHCP DISCOVER Message + */ + call->op = DHCP_BOOTREQUEST; + call->htype = 1; /* 10mb ethernet */ + call->hlen = sdl->sdl_alen; /* Hardware address length */ + call->hops = 0; + (*xid)++; + call->xid = htonl (*xid); + call->flags = htons (DHCP_BROADCAST); + + memcpy (&call->chaddr, LLADDR (sdl), sdl->sdl_alen); + + /* + * Magic cookie. + */ + memcpy (&call->vend[len], dhcp_magic_cookie, sizeof (dhcp_magic_cookie)); + len += sizeof (dhcp_magic_cookie); + + /* + * DHCP Message type. + */ + call->vend[len++] = DHCP_MESSAGE; + call->vend[len++] = 1; + call->vend[len++] = DHCP_DISCOVER; + + /* + * If a host name is set add it to the request. + */ + if (rtems_bsdnet_config.hostname && \ + (strlen (rtems_bsdnet_config.hostname) > 1) && + (strlen (rtems_bsdnet_config.hostname) < 32)) { + call->vend[len++] = DHCP_HOST; + call->vend[len++] = strlen (rtems_bsdnet_config.hostname); + memcpy (&call->vend[len], + rtems_bsdnet_config.hostname, + strlen (rtems_bsdnet_config.hostname)); + len += strlen (rtems_bsdnet_config.hostname); + } + + /* + * DHCP Parameter request list + */ + call->vend[len++] = DHCP_PARAMETERS; + call->vend[len++] = sizeof (dhcp_request_parameters); + memcpy (&call->vend[len], &dhcp_request_parameters, sizeof (dhcp_request_parameters)); + len += sizeof (dhcp_request_parameters); + + /* + * Lease time. + */ + call->vend[len++] = DHCP_LEASE; + call->vend[len++] = 4; + memset (&call->vend[len], 0xFF, 4); /* request infinite lease time */ + len += 4; + + /* + * End. + */ + call->vend[len++] = DHCP_OPTION_END; + call->secs = 0; + + return len; +} + +/* + * Generate the packet for a DHCP REQUEST. + */ +static int +dhcp_request_req (struct dhcp_packet* call, + struct dhcp_packet* reply, + struct sockaddr_dl *sdl, + int broadcast) +{ + int len = 0; + unsigned long temp; + char *hostname; + + memset (call, 0, sizeof (struct dhcp_packet)); + + /* + * Send a DHCP REQUEST Message + */ + call->op = DHCP_BOOTREQUEST; + call->htype = 1; /* 10mb ethernet */ + call->hlen = sdl->sdl_alen; /* Hardware address length */ + call->hops = 0; + call->xid = reply->xid; + if (broadcast) + call->flags = htons (DHCP_BROADCAST); + else + { + call->flags = htons (DHCP_UNICAST); + call->ciaddr = reply->yiaddr; + } + memcpy (&call->chaddr, LLADDR (sdl), sdl->sdl_alen); + + /* + * Magic cookie. + */ + memcpy (&call->vend[len], dhcp_magic_cookie, sizeof (dhcp_magic_cookie)); + len += sizeof (dhcp_magic_cookie); + + /* + * DHCP Message type. + */ + call->vend[len++] = DHCP_MESSAGE; + call->vend[len++] = 1; + call->vend[len++] = DHCP_REQUEST; + + /* + * DHCP server + */ + if (broadcast) + { + call->vend[len++] = DHCP_SERVER; + call->vend[len++] = sizeof (rtems_bsdnet_bootp_server_address); + memcpy (&call->vend[len], &rtems_bsdnet_bootp_server_address, + sizeof (rtems_bsdnet_bootp_server_address)); + len += sizeof (rtems_bsdnet_bootp_server_address); + } + + /* + * Requested IP + */ + call->vend[len++] = DHCP_REQUESTED_IP; + call->vend[len++] = sizeof (reply->yiaddr); + memcpy (&call->vend[len], &reply->yiaddr, sizeof (reply->yiaddr)); + len += sizeof (reply->yiaddr); + + /* + * DHCP Parameter request list + */ + call->vend[len++] = DHCP_PARAMETERS; + call->vend[len++] = sizeof (dhcp_request_parameters); + memcpy (&call->vend[len], &dhcp_request_parameters, sizeof (dhcp_request_parameters)); + len += sizeof (dhcp_request_parameters); + + /* + * Lease time. + * For the REQUEST, return the lease time the server offered. + */ + call->vend[len++] = DHCP_LEASE; + call->vend[len++] = 4; + temp = htonl (dhcp_lease_time); + memcpy (&call->vend[len], &temp, sizeof (unsigned long)); + len += 4; + + /* + * Host name. + */ + hostname = malloc (MAXHOSTNAMELEN, 0, M_NOWAIT); + if (hostname != NULL) + { + if (gethostname (hostname, MAXHOSTNAMELEN) == 0) + { + call->vend[len++] = DHCP_HOST; + call->vend[len++] = strlen (hostname); + strcpy ((char*) &call->vend[len], hostname); + len += strlen (hostname); + } + free (hostname, 0); + } + + /* + * End. + */ + call->vend[len++] = DHCP_OPTION_END; + call->secs = 0; + + return len; +} + +/* + * Variables for the DHCP task. + */ +static struct dhcp_packet dhcp_req; +static rtems_id dhcp_task_id; + +/* + * The DHCP task counts until half the lease time has expired. + * When this period is up, it sends a DHCP REQUEST packet to the + * server again to renew the lease. + * If the lease is renewed, the task starts counting again. + * If the lease is not renewed, the task retries until it is. + * + * The task will not rebind if the lease is not renewed. + */ +static void +dhcp_task (rtems_task_argument _sdl) +{ + unsigned long count; + struct dhcp_packet call; + struct sockaddr_dl *sdl; + rtems_event_set event_out; + unsigned int timeout = 0; + int error; + struct proc *procp = NULL; + rtems_status_code ev_st; + + sdl = (struct sockaddr_dl *) _sdl; + + count = dhcp_elapsed_time; + + while (true) + { + /* + * Sleep until the next poll + */ + timeout = RTEMS_MILLISECONDS_TO_TICKS (1000); + ev_st = rtems_event_receive (RTEMS_EVENT_0, + RTEMS_WAIT | RTEMS_EVENT_ANY, + timeout, &event_out); + + /* + * Check if not a poll timeout. So when ANY event received, exit task. + * Actually, only event RTEMS_EVENT_0 sent from rtem_dhcp_failsafe.c + * if "failsafe" dhcp enabled when interface down. Otherwise, no + * event should occur, just timeout. + */ + if(ev_st != RTEMS_TIMEOUT) + break; + + count++; + + if (count >= (dhcp_lease_time / 2)) + { + rtems_bsdnet_semaphore_obtain (); + + dhcp_request_req (&call, &dhcp_req, sdl, true); + + /* + * Send the Request. + */ + error = bootpc_call ((struct bootp_packet *)&call, + (struct bootp_packet *)&dhcp_req, procp, NULL, 0); + if (error) { + rtems_bsdnet_semaphore_release (); + printf ("DHCP call failed -- error %d", error); + continue; + } + + /* + * Check for DHCP ACK/NACK + */ + if (memcmp (&dhcp_req.vend[0], + dhcp_magic_cookie, + sizeof (dhcp_magic_cookie)) != 0) + { + rtems_bsdnet_semaphore_release (); + printf ("DHCP server did not send Magic Cookie.\n"); + continue; + } + + /* + * We have an ack. Clear the DNS entries that have been assigned by a previous + * DHCP request. + */ + clean_dns_entries (); + + /* + * Process this requests options. + */ + process_options (&dhcp_req.vend[4], sizeof (dhcp_req.vend) - 4); + + if (dhcp_message_type != DHCP_ACK) + { + rtems_bsdnet_semaphore_release (); + printf ("DHCP server did not accept the DHCP request"); + continue; + } + + rtems_bsdnet_semaphore_release (); + + count = 0; + } + } + + + dhcp_task_id = 0; + printf ("dhcpc: exiting lease renewal task.\n"); + rtems_task_exit(); + +} + +/* + * Start the DHCP task. + */ +static rtems_status_code +dhcp_start_task (struct sockaddr_dl *sdl, + struct dhcp_packet *reply, + int priority) +{ + rtems_status_code sc; + + memcpy (&dhcp_req, reply, sizeof (struct dhcp_packet)); + + sc = rtems_task_create (rtems_build_name ('d','h','c','p'), + priority, + 2048, + RTEMS_PREEMPT | + RTEMS_NO_TIMESLICE | + RTEMS_NO_ASR | + RTEMS_INTERRUPT_LEVEL (0), + RTEMS_LOCAL, + &dhcp_task_id); + + if (sc != RTEMS_SUCCESSFUL) + return sc; + + sc = rtems_task_start (dhcp_task_id, + dhcp_task, + (rtems_task_argument) sdl); + + if (sc != RTEMS_SUCCESSFUL) + return sc; + + return RTEMS_SUCCESSFUL; +} + +/* + * Check if the chosen interface already has an IP. + */ +static int +dhcp_interface_has_ip (struct ifreq *ireq, struct socket *so, struct proc *procp) +{ + struct sockaddr_in* sin; + int error; + + /* + * Check if the interface is already up. + */ + error = ifioctl(so, SIOCGIFFLAGS, (caddr_t)ireq, procp); + if (error) + return 0; + + if ((ireq->ifr_flags & IFF_UP) == 0) + return 0; + + sin = (struct sockaddr_in *)&ireq->ifr_addr; + bzero ((caddr_t)sin, sizeof (struct sockaddr_in)); + sin->sin_len = sizeof (struct sockaddr_in); + sin->sin_family = AF_INET; + error = ifioctl (so, SIOCGIFADDR, (caddr_t)ireq, procp); + if (error) + return 0; + + if (sin->sin_addr.s_addr != 0) + return 1; + + return 0; +} + +/* + * DCHP Client Routine + * - The first DHCP offer is always accepted + * - No DHCP DECLINE message is sent if ARPing fails + * + * return value: + * 0: ok + * < 0: failed to startup or configure interface + */ +static int +dhcp_init (int update_files) +{ + struct dhcp_packet call; + struct dhcp_packet reply; + static unsigned long xid = ~0xFF; + struct ifreq ireq; + struct ifnet *ifp; + struct socket *so; + int error; + struct sockaddr_in myaddr; + struct ifaddr *ifa; + struct sockaddr_dl *sdl = NULL; + struct proc *procp = NULL; + char expected_dhcp_payload[7]; + + clean_dns_entries(); + + /* + * If we are to update the files create the root + * file structure. + */ + if (update_files) + if (rtems_create_root_fs () < 0) { + printf("Error creating the root filesystem.\nFile not created.\n"); + update_files = 0; + } + + /* + * Find a network interface. + */ + for (ifp = ifnet; ifp != 0; ifp = ifp->if_next) + if ((ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) == 0) + break; + if (ifp == NULL){ + printf ("dhcpc_init: no suitable interface\n"); + return -1; + } + + memset (&ireq, 0, sizeof (ireq)); + sprintf (ireq.ifr_name, "%s%d", ifp->if_name, ifp->if_unit); + + if ((error = socreate (AF_INET, &so, SOCK_DGRAM, 0, procp)) != 0) { + printf ("dhcpc_init: socreate, error: %s\n", strerror(error)); + return -1; + } + + if (!dhcp_interface_has_ip (&ireq, so, procp)) + bootpc_fakeup_interface (&ireq, so, procp); + + /* + * Get HW address + */ + for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) + if (ifa->ifa_addr->sa_family == AF_LINK && + (sdl = ((struct sockaddr_dl *) ifa->ifa_addr)) && + sdl->sdl_type == IFT_ETHER) + break; + + if (!sdl){ + printf ("dhcpc_init: Unable to find HW address\n"); + soclose (so); + return -1; + } + if (sdl->sdl_alen != EALEN) { + printf ("dhcpc_init: HW address len is %d, expected value is %d\n", + sdl->sdl_alen, EALEN); + soclose (so); + return -1; + } + + + /* + * Build the DHCP Discover + */ + dhcp_discover_req (&call, sdl, &xid); + + /* + * Expect a DHCP offer as response to DHCP discover + */ + memcpy(expected_dhcp_payload, dhcp_magic_cookie, sizeof(dhcp_magic_cookie)); + expected_dhcp_payload[sizeof(dhcp_magic_cookie) ]=0x35; /* DHCP */ + expected_dhcp_payload[sizeof(dhcp_magic_cookie)+1]=0x01; /* Length : 1 */ + expected_dhcp_payload[sizeof(dhcp_magic_cookie)+2]=0x02; /* DHCP_OFFER */ + + /* + * Send the Discover. + */ + error = bootpc_call ((struct bootp_packet *)&call, + (struct bootp_packet *)&reply, procp, + expected_dhcp_payload, sizeof(expected_dhcp_payload)); + if (error) { + printf ("BOOTP call failed -- %s\n", strerror(error)); + soclose (so); + return -1; + } + + /* + * Check for DHCP OFFER + */ + if (memcmp (&reply.vend[0], dhcp_magic_cookie, sizeof (dhcp_magic_cookie)) != 0) { + printf ("DHCP server did not send Magic Cookie.\n"); + soclose (so); + return -1; + } + + process_options (&reply.vend[4], sizeof (reply.vend) - 4); + + if (dhcp_message_type != DHCP_OFFER) { + printf ("DHCP server did not send a DHCP Offer.\n"); + soclose (so); + return -1; + } + + /* + * Expect a DHCP_ACK as response to the DHCP REQUEST + * No need to reinitialize the whole expected_dhcp_payload variable, + * header and first two bytes of the payload are filled from DHCP offer + */ + expected_dhcp_payload[sizeof(dhcp_magic_cookie)+2]=0x05; /* DHCP_ACK */ + + /* + * Send a DHCP REQUEST + */ + dhcp_request_req (&call, &reply, sdl, true); + + error = bootpc_call ((struct bootp_packet *)&call, + (struct bootp_packet *)&reply, procp, + expected_dhcp_payload, sizeof(expected_dhcp_payload)); + if (error) { + printf ("BOOTP call failed -- %s\n", strerror(error)); + soclose (so); + return -1; + } + + /* + * Check for DHCP ACK/NACK + */ + if (memcmp (&reply.vend[0], dhcp_magic_cookie, sizeof (dhcp_magic_cookie)) != 0) { + printf ("DHCP server did not send Magic Cookie.\n"); + soclose (so); + return -1; + } + + process_options (&reply.vend[4], sizeof (reply.vend) - 4); + + if (dhcp_message_type != DHCP_ACK) { + printf ("DHCP server did not accept the DHCP request\n"); + soclose (so); + return -1; + } + + /* + * Initialize network address structures + */ + memset (&myaddr, 0, sizeof (myaddr)); + memset (&dhcp_netmask, 0, sizeof (dhcp_netmask)); + memset (&dhcp_gw, 0, sizeof (dhcp_gw)); + myaddr.sin_len = sizeof (myaddr); + myaddr.sin_family = AF_INET; + dhcp_netmask.sin_len = sizeof (dhcp_netmask); + dhcp_netmask.sin_family = AF_INET; + dhcp_gw.sin_len = sizeof (dhcp_gw); + dhcp_gw.sin_family = AF_INET; + + /* + * Set our address + */ + myaddr.sin_addr = reply.yiaddr; + + /* + * Process BOOTP/DHCP options + */ + if (memcmp (&reply.vend[0], dhcp_magic_cookie, sizeof (dhcp_magic_cookie)) == 0) + process_options (&reply.vend[4], sizeof (reply.vend) - 4); + + if (dhcp_option_overload & 1) + process_options ((unsigned char*) reply.file, sizeof reply.file); + else + if (reply.file[0]) + rtems_bsdnet_bootp_boot_file_name = strdup (reply.file); + + if (dhcp_option_overload & 2) + process_options ((unsigned char*) reply.sname, sizeof reply.sname); + else + if (reply.sname[0]) + rtems_bsdnet_bootp_server_name = strdup (reply.sname); + + /* + * Use defaults if values were not supplied by BOOTP/DHCP options + */ + if (!dhcp_gotnetmask) + { + if (IN_CLASSA (ntohl (myaddr.sin_addr.s_addr))) + dhcp_netmask.sin_addr.s_addr = htonl (IN_CLASSA_NET); + else if (IN_CLASSB (ntohl (myaddr.sin_addr.s_addr))) + dhcp_netmask.sin_addr.s_addr = htonl (IN_CLASSB_NET); + else + dhcp_netmask.sin_addr.s_addr = htonl (IN_CLASSC_NET); + } + + if (!dhcp_gotserver) + rtems_bsdnet_bootp_server_address = reply.siaddr; + + if (!dhcp_gotgw) + dhcp_gw.sin_addr = reply.giaddr; + + if (!dhcp_gotlogserver) + rtems_bsdnet_log_host_address = rtems_bsdnet_bootp_server_address; + + printsetup (ifp->if_name, myaddr.sin_addr, dhcp_netmask.sin_addr, + rtems_bsdnet_bootp_server_address, dhcp_gw.sin_addr); + + /* + * Update the files if we are asked too. + */ + if (update_files) { + char *dn = rtems_bsdnet_domain_name; + char *hn = dhcp_hostname; + if (!dn) + dn = "mydomain"; + if (!hn) + { + hn = "me"; + sethostname (hn, strlen (hn)); + } + rtems_rootfs_append_host_rec(myaddr.sin_addr.s_addr, hn, dn); + + /* + * Should the given domainname be used here ? + */ + if (dhcp_gotserver) { + if (rtems_bsdnet_bootp_server_name) + hn = rtems_bsdnet_bootp_server_name; + else + hn = "bootps"; + rtems_rootfs_append_host_rec(rtems_bsdnet_bootp_server_address.s_addr, + hn, dn); + } + + if (dhcp_gotlogserver) { + rtems_rootfs_append_host_rec(rtems_bsdnet_log_host_address.s_addr, + "logs", dn); + } + + /* + * Setup the DNS configuration file /etc/resolv.conf. + */ + if (rtems_bsdnet_nameserver_count) { + int i; + char buf[64]; + const char *bufl[1]; + + bufl[0] = buf; + +#define MKFILE_MODE (S_IRUSR | S_IWUSR | S_IWGRP | S_IRGRP | S_IROTH) + + if (rtems_bsdnet_domain_name && + (strlen(rtems_bsdnet_domain_name) < (sizeof(buf) - 1))) { + strcpy(buf, "search "); + strcat(buf, rtems_bsdnet_domain_name); + strcat(buf, "\n"); + rtems_rootfs_file_append ("/etc/resolv.conf", MKFILE_MODE, 1, bufl); + } + + for (i = 0; i < rtems_bsdnet_nameserver_count; i++) { + char addrbuf[INET_ADDRSTRLEN]; + strcpy(buf, "nameserver "); + strcat(buf, inet_ntoa_r(rtems_bsdnet_ntpserver[i], addrbuf)); + strcat(buf, "\n"); + if (rtems_rootfs_file_append ("/etc/resolv.conf", MKFILE_MODE, 1, bufl)) + break; + } + } + } + + /* + * Configure the interface with the new settings + */ + error = bootpc_adjust_interface (&ireq, so, + &myaddr, &dhcp_netmask, &dhcp_gw, procp); + + /* + * Start the DHCP task if the lease isn't infinite. + */ + if (dhcp_lease_time != 0xffffffff) + dhcp_start_task (sdl, &reply, 150); + + soclose (so); + + return 0; +} + +/* + * + * RTEMS Entry point to DHCP client + * + */ +void rtems_bsdnet_do_dhcp (void) +{ + bool update = true; + rtems_bsdnet_semaphore_obtain (); + while( dhcp_init (update) < 0 ) { + update = false; + rtems_bsdnet_semaphore_release(); + rtems_task_wake_after(RTEMS_MILLISECONDS_TO_TICKS(1000)); + rtems_bsdnet_semaphore_obtain (); + } + rtems_bsdnet_semaphore_release (); +} + +int rtems_bsdnet_do_dhcp_timeout( void ) +{ + int return_value; + + rtems_bsdnet_semaphore_obtain (); + return_value = dhcp_init (false); + rtems_bsdnet_semaphore_release (); + + return return_value; +} + +void rtems_bsdnet_dhcp_down (void) +{ + if(dhcp_task_id != 0) { + rtems_event_send (dhcp_task_id, RTEMS_EVENT_0); + } +} + +void +rtems_bsdnet_do_dhcp_refresh_only (unsigned long xid, + unsigned long lease_time, + unsigned long elapsed_time, + unsigned long ip_address, + unsigned long srv_address, + const char* hostname) +{ + struct dhcp_packet reply; + struct ifnet *ifp = NULL; + struct ifaddr *ifa = NULL; + struct sockaddr_dl *sdl = NULL; + struct sockaddr_in *sin = NULL; + int match = 0; + struct ifnet *mtif = NULL; + + /* + * If an infinite lease has been granted, no task is needed. + */ + if (lease_time == 0xffffffff) + return; + + /* + * Find a network interface. + */ + for (ifp = ifnet; (ifp != NULL) && !match; ifp = ifp->if_next) + if ((ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) == 0) + for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) + if (ifa->ifa_addr->sa_family == AF_INET) + { + sin = (struct sockaddr_in *) ifa->ifa_addr; + if (sin->sin_addr.s_addr == htonl (ip_address)) + { + mtif = ifp; + match = 1; + break; + } + } + + if (!match) { + printf ("dhcpc: no matching interface\n"); + return; + } + + for (ifa = mtif->if_addrlist; ifa != NULL; ifa = ifa->ifa_next) + if (ifa->ifa_addr->sa_family == AF_LINK && + (sdl = ((struct sockaddr_dl *) ifa->ifa_addr)) && + sdl->sdl_type == IFT_ETHER) + break; + + if (!match) { + printf ("dhcpc: no matching interface address\n"); + return; + } + + /* + * Set up given values in a simulated DHCP reply. + */ + memset (&reply, 0x00, sizeof (reply)); + reply.xid = htonl (xid); + reply.yiaddr.s_addr = htonl (ip_address); + reply.siaddr.s_addr = htonl (srv_address); + if (reply.siaddr.s_addr != rtems_bsdnet_bootp_server_address.s_addr) + { + memcpy (&rtems_bsdnet_bootp_server_address, &reply.siaddr, + sizeof (reply.siaddr)); + } + + dhcp_lease_time = lease_time; + dhcp_elapsed_time = elapsed_time; + + if (hostname) + { + sethostname ((char *) hostname, strlen (hostname)); + dhcp_hostname = bootp_strdup_realloc (dhcp_hostname, hostname); + } + + dhcp_start_task (sdl, &reply, 150); +} |