#include /* * DCHP client for RTEMS * Andrew Bythell, * based on and uses subroutines from c/src/libnetworking/nfs/bootp_subr.c */ /* * DHCP task added. * Brendan Gannon, */ /* * Added interface to terminate DHCP task, and removed panics. * Arnout Vandecappelle , Essensium/Mind * Maarten Van Es , 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 #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include /* for MAXHOSTNAMELEN */ #include #include /* for socreat() soclose() */ #include #include #include #include /* for NBO-HBO conversions */ #include /* for IFT_ETHER */ #include /* for LLADDR */ #include #include #include #include #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= */ 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); }