diff options
Diffstat (limited to 'cpukit/libnetworking/rtems/rtems_dhcp_failsafe.c')
-rw-r--r-- | cpukit/libnetworking/rtems/rtems_dhcp_failsafe.c | 372 |
1 files changed, 372 insertions, 0 deletions
diff --git a/cpukit/libnetworking/rtems/rtems_dhcp_failsafe.c b/cpukit/libnetworking/rtems/rtems_dhcp_failsafe.c new file mode 100644 index 0000000000..609a19eb0a --- /dev/null +++ b/cpukit/libnetworking/rtems/rtems_dhcp_failsafe.c @@ -0,0 +1,372 @@ +/* + $Id$ + + Description: Wrapper around DHCP client to restart it when the interface + moves to another network. + + Authors: Arnout Vandecappelle <arnout@mind.be>, Essensium/Mind + Maarten Van Es <maarten@mind.be>, Essensium/Mind + (C) Septentrio 2008 + + The license and distribution terms for this file may be + found in the file LICENSE in this distribution or at + http://www.rtems.com/license/LICENSE. + + + To use this functionality, call rtems_bsdnet_do_dhcp_failsafe() or set it + as the bootp member of the rtems_bsdnet_config structure. + + The rtems_bsdnet_do_dhcp_failsafe() function provides the following + functionalities: + + * It starts DHCP on the first non-loopback, non-point-to-point interface. + Before DHCP is started, any existing IP address on that interface is + removed, as well as the default route. + + * If DHCP fails to acquire an address for whatever reason, the interface + is reconfigured with the static address provided in its + rtems_bsdnet_ifconfig structure, and the default route from + rtems_bsdnet_config is restored. + It is possible to change the address in the rtems_bsdnet_ifconfig structure + while the system is running. + + * Optionally, after the interface is configured (either with DHCP or + statically), a task is started to monitor it. When the interface remains + disconnected (i.e. its IFF_RUNNING flag is off) for NETWORK_FAIL_TIMEOUT + seconds, the dhcp lease renewal is stopped. As soon as the interface is + connected again, DHCP is started again as above. + If NETWORK_FAIL_TIMEOUT is set to 0, the monitor task is not started. + + * Optionally, when the interface is disconnected, it is also brought down + for NETWORK_DOWN_TIME seconds. Since this possibly makes the IFF_RUNNING + flag unuseable, the interface is brought up again before the flag is + polled. + If NETWORK_DOWN_TIME is set to 0, the interface is not brought down. + + * Optionally, before DHCP is started, we wait for BROADCAST_DELAY seconds. + This is necessary to allow some routers to perform spanning tree discovery. + + Note that DHCP doesn't work well with multiple interfaces: only one of them + is configured using DHCP, and we can't guarantee it's the same one that is + monitored by this function. + +*/ + +#include <rtems.h> +#include <rtems/error.h> +#include <rtems/rtems_bsdnet.h> +#include <rtems/rtems_bsdnet_internal.h> + +#include <string.h> +#include <stdio.h> +#include <errno.h> + +#include <rtems/dhcp.h> + +struct proc; /* Unused parameter of some functions. */ +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <net/route.h> +#include <netinet/in.h> /* for sockaddr_in */ +#include <net/if.h> /* for if.h */ +#include <net/if_var.h> /* for in_var.h */ +#include <netinet/in_var.h> /* for in_aliasreq */ +#include <sys/sockio.h> /* for ioctl definitions */ +#include <arpa/inet.h> /* for inet_addr, inet_ntop */ + + +#define NETWORK_FAIL_TIMEOUT 5 /* the number of seconds before the interface is considered disconnected */ +#define NETWORK_DOWN_TIME 30 /* number of seconds the interface remains down */ +#define BROADCAST_DELAY 0 /* Delay (seconds) before broadcasts are sent */ +#define DHCP_MONITOR_PRIORITY 250 /* Low priority */ + +/* + * returns 0 when successful, negative value for failure + */ +static int remove_address(const char *if_name) +{ + struct sockaddr_in address; + struct in_aliasreq ifra; + int retval = 0; + + memset (&address, '\0', sizeof (address)); + address.sin_len = sizeof (address); + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + + /* Remove old default route to 0.0.0.0 */ + if (rtems_bsdnet_rtrequest (RTM_DELETE, (struct sockaddr *)&address, NULL, + (struct sockaddr *)&address, + (RTF_UP | RTF_STATIC), NULL) < 0 ) { + printf ("Failed to delete default route: %s (%d)\n", strerror (errno), errno); + retval = -1; + } + + /* Remove old ip-address */ + if (rtems_bsdnet_ifconfig(if_name, SIOCGIFADDR, &address) < 0) { + printf ("Failed to get if address: %s (%d)\n", strerror (errno), errno); + return -1; + } + + strncpy (ifra.ifra_name, if_name, IFNAMSIZ); + memcpy (&ifra.ifra_addr, &address, sizeof(address)); + if (rtems_bsdnet_ifconfig(if_name, SIOCDIFADDR, &ifra)) { + printf ("Can't delete if address: %s (%d)\n", strerror (errno), errno); + return -1; + } + + return retval; +} + + +#if NETWORK_DOWN_TIME +static int +dhcp_if_down (const char* ifname) +{ + int flags; + if (rtems_bsdnet_ifconfig (ifname, SIOCGIFFLAGS, &flags) < 0) { + printf ("Can't get flags for %s: %s\n", ifname, strerror (errno)); + return -1; + } + if (flags & IFF_UP) { + flags &= ~IFF_UP; + if (rtems_bsdnet_ifconfig (ifname, SIOCSIFFLAGS, &flags) < 0) { + printf ("Can't bring %s down: %s\n", ifname, strerror (errno)); + return -1; + } + } + + return 0; +} +#endif + +static int +dhcp_if_up (const char* ifname) +{ + int flags; + if (rtems_bsdnet_ifconfig (ifname, SIOCGIFFLAGS, &flags) < 0) { + printf ("Can't get flags for %s: %s\n", ifname, strerror (errno)); + return -1; + } + if (!(flags & IFF_UP)) { + flags |= IFF_UP; + if (rtems_bsdnet_ifconfig (ifname, SIOCSIFFLAGS, &flags) < 0) { + printf ("Can't bring %s up: %s\n", ifname, strerror (errno)); + return -1; + } + } + + return 0; +} + + +static int +set_static_address (struct rtems_bsdnet_ifconfig *ifp) +{ + short flags; + struct sockaddr_in address; + struct sockaddr_in netmask; + struct sockaddr_in broadcast; + struct sockaddr_in gateway; + + if (ifp->ip_address != NULL) { + printf("Setting static address for interface %s.\n", ifp->name); + + /* + * Bring interface up + */ + if (dhcp_if_up (ifp->name) < 0) + return -1; + + /* + * Set interface netmask + */ + memset (&netmask, '\0', sizeof netmask); + netmask.sin_len = sizeof netmask; + netmask.sin_family = AF_INET; + netmask.sin_addr.s_addr = inet_addr (ifp->ip_netmask); + if (rtems_bsdnet_ifconfig (ifp->name, SIOCSIFNETMASK, &netmask) < 0) { + printf ("Can't set %s netmask: %s\n", ifp->name, strerror (errno)); + return -1; + } + + /* + * Set interface address + */ + memset (&address, '\0', sizeof address); + address.sin_len = sizeof address; + address.sin_family = AF_INET; + address.sin_addr.s_addr = inet_addr (ifp->ip_address); + if (rtems_bsdnet_ifconfig (ifp->name, SIOCSIFADDR, &address) < 0) { + printf ("Can't set %s address: %s\n", ifp->name, strerror (errno)); + return -1; + } + + /* + * Set interface broadcast address if the interface has the + * broadcast flag set. + */ + if (rtems_bsdnet_ifconfig (ifp->name, SIOCGIFFLAGS, &flags) < 0) { + printf ("Can't read %s flags: %s\n", ifp->name, strerror (errno)); + return -1; + } + if (flags & IFF_BROADCAST) { + memset (&broadcast, '\0', sizeof broadcast); + broadcast.sin_len = sizeof broadcast; + broadcast.sin_family = AF_INET; + broadcast.sin_addr.s_addr = address.sin_addr.s_addr | ~netmask.sin_addr.s_addr; + + if (rtems_bsdnet_ifconfig (ifp->name, SIOCSIFBRDADDR, &broadcast) < 0) { + struct in_addr in_addr; + char buf[20]; + + in_addr.s_addr = broadcast.sin_addr.s_addr; + if (!inet_ntop (AF_INET, &in_addr, buf, sizeof (buf))) + strcpy (buf, "?.?.?.?"); + printf ("Can't set %s broadcast address %s: %s\n", ifp->name, buf, strerror (errno)); + } + } + } + + /* + * Set default route + */ + if (rtems_bsdnet_config.gateway) { + address.sin_addr.s_addr = INADDR_ANY; + netmask.sin_addr.s_addr = INADDR_ANY; + memset (&gateway, '\0', sizeof gateway); + gateway.sin_len = sizeof gateway; + gateway.sin_family = AF_INET; + gateway.sin_addr.s_addr = inet_addr (rtems_bsdnet_config.gateway); + + if (rtems_bsdnet_rtrequest (RTM_ADD, + (struct sockaddr *) &address, + (struct sockaddr *) &gateway, + (struct sockaddr *) &netmask, + (RTF_UP | RTF_GATEWAY | RTF_STATIC), + NULL + ) < 0) { + printf ("Can't set default route: %s\n", strerror (errno)); + return -1; + } + } + + return 0; +} + +static void +do_dhcp_init (struct rtems_bsdnet_ifconfig *ifp) +{ +#if BROADCAST_DELAY + /* Wait before sending broadcast. */ + rtems_task_wake_after(TOD_MILLISECONDS_TO_TICKS(BROADCAST_DELAY * 1000)); +#endif + + printf ("starting dhcp client...\n"); + + remove_address(ifp->name); + if (rtems_bsdnet_do_dhcp_timeout () != 0) { + remove_address(ifp->name); + set_static_address (ifp); /* use static ip-address if dhcp failed */ + } + +} + +/* + * Main dhcp monitor thread + */ +static void dhcp_monitor_task (rtems_task_argument ifp_arg) +{ + struct rtems_bsdnet_ifconfig *ifp = (struct rtems_bsdnet_ifconfig *)ifp_arg; + char *ifname = ifp->name; + unsigned int downcount = 0; + int ifflags; + int must_renew = FALSE; + + while (TRUE) { + if (rtems_bsdnet_ifconfig(ifname, SIOCGIFFLAGS, &ifflags) < 0) { + printf ("Failed to get if flags: %s (%d)\n", strerror (errno), errno); + goto error_out; + } + + if ((ifflags & IFF_RUNNING) != 0) { + if(must_renew) { + must_renew = FALSE; + do_dhcp_init(ifp); + } + downcount = 0; + } else { + if (downcount < NETWORK_FAIL_TIMEOUT) { + downcount++; + + if (downcount == NETWORK_FAIL_TIMEOUT) { + printf ("lost network connection...\n"); + rtems_bsdnet_dhcp_down (); + must_renew = TRUE; +#if NETWORK_DOWN_TIME + dhcp_if_down(ifname); + rtems_task_wake_after(TOD_MILLISECONDS_TO_TICKS(NETWORK_DOWN_TIME * 1000)); + dhcp_if_up(ifname); + downcount = 0; +#endif + } + } + } + + rtems_task_wake_after(TOD_MILLISECONDS_TO_TICKS(1000)); + } + +error_out: + printf("Stopping dhcp monitoring application.\n"); + rtems_task_delete(RTEMS_SELF); +} + +/* +* initialize dhcp monitoring application +* start dhcp monitoring thread +*/ +void rtems_bsdnet_do_dhcp_failsafe (void) +{ + rtems_status_code sc; + rtems_id id; + struct rtems_bsdnet_ifconfig *ifp; + int ifflags; + + /* Find a suitable interface */ + for (ifp = rtems_bsdnet_config.ifconfig; ifp; ifp = ifp->next) { + if (rtems_bsdnet_ifconfig (ifp->name, SIOCGIFFLAGS, &ifflags) < 0) + continue; + if ((ifflags & (IFF_LOOPBACK | IFF_POINTOPOINT)) == 0) + break; + } + if (ifp == NULL){ + printf ("dhcpc_failsafe: no suitable interface\n"); + return; + } + printf("starting dhcp on interface %s\n", ifp->name); + do_dhcp_init(ifp); + +#if NETWORK_FAIL_TIMEOUT + sc = rtems_task_create (rtems_build_name ('d','h','c','m'), + DHCP_MONITOR_PRIORITY, + 2048, + RTEMS_PREEMPT | + RTEMS_NO_TIMESLICE | + RTEMS_NO_ASR | + RTEMS_INTERRUPT_LEVEL (0), + RTEMS_LOCAL, + &id); + + if (sc != RTEMS_SUCCESSFUL) { + printf ("Failed to create dhcp monitor task, code %d\n", sc); + return; + } + + sc = rtems_task_start (id, dhcp_monitor_task, (rtems_task_argument) ifp); + + if (sc != RTEMS_SUCCESSFUL) { + printf ("Failed to start dhcp monitor task, code %d\n", sc); + } +#endif +} + |