source: rtems/cpukit/libnetworking/rtems/rtems_dhcp_failsafe.c @ c499856

4.115
Last change on this file since c499856 was c499856, checked in by Chris Johns <chrisj@…>, on Mar 20, 2014 at 9:10:47 PM

Change all references of rtems.com to rtems.org.

  • Property mode set to 100644
File size: 11.3 KB
Line 
1/*
2  Description: Wrapper around DHCP client to restart it when the interface
3               moves to another network.
4
5  Authors: Arnout Vandecappelle <arnout@mind.be>, Essensium/Mind
6           Maarten Van Es <maarten@mind.be>, Essensium/Mind
7  (C) Septentrio 2008
8
9  The license and distribution terms for this file may be
10  found in the file LICENSE in this distribution or at
11  http://www.rtems.org/license/LICENSE.
12
13
14  To use this functionality, call rtems_bsdnet_do_dhcp_failsafe() or set it
15  as the bootp member of the rtems_bsdnet_config structure.
16
17  The rtems_bsdnet_do_dhcp_failsafe() function provides the following
18  functionalities:
19
20  * It starts DHCP on the first non-loopback, non-point-to-point interface.
21    Before DHCP is started, any existing IP address on that interface is
22    removed, as well as the default route.
23
24  * If DHCP fails to acquire an address for whatever reason, the interface
25    is reconfigured with the static address provided in its
26    rtems_bsdnet_ifconfig structure, and the default route from
27    rtems_bsdnet_config is restored.
28    It is possible to change the address in the rtems_bsdnet_ifconfig structure
29    while the system is running.
30
31  * Optionally, after the interface is configured (either with DHCP or
32    statically), a task is started to monitor it.  When the interface remains
33    disconnected (i.e. its IFF_RUNNING flag is off) for NETWORK_FAIL_TIMEOUT
34    seconds, the dhcp lease renewal is stopped.  As soon as the interface is
35    connected again, DHCP is started again as above.
36    If NETWORK_FAIL_TIMEOUT is set to 0, the monitor task is not started.
37
38  * Optionally, when the interface is disconnected, it is also brought down
39    for NETWORK_DOWN_TIME seconds.  Since this possibly makes the IFF_RUNNING
40    flag unuseable, the interface is brought up again before the flag is
41    polled.
42    If NETWORK_DOWN_TIME is set to 0, the interface is not brought down.
43
44  * Optionally, before DHCP is started, we wait for BROADCAST_DELAY seconds.
45    This is necessary to allow some routers to perform spanning tree discovery.
46
47  Note that DHCP doesn't work well with multiple interfaces: only one of them
48  is configured using DHCP, and we can't guarantee it's the same one that is
49  monitored by this function.
50
51*/
52
53#include <rtems.h>
54#include <rtems/error.h>
55#include <rtems/rtems_bsdnet.h>
56#include <rtems/rtems_bsdnet_internal.h>
57
58#include <string.h>
59#include <stdio.h>
60#include <errno.h>
61
62#include <rtems/dhcp.h>
63
64struct  proc;                   /* Unused parameter of some functions. */
65#include <sys/ioctl.h>
66#include <sys/socket.h>
67#include <net/route.h>
68#include <netinet/in.h>         /* for sockaddr_in */
69#include <net/if.h>             /* for if.h */
70#include <net/if_var.h>         /* for in_var.h */
71#include <netinet/in_var.h>     /* for in_aliasreq */
72#include <sys/sockio.h>         /* for ioctl definitions */
73#include <arpa/inet.h>          /* for inet_addr, inet_ntop */
74
75
76#define NETWORK_FAIL_TIMEOUT 5     /* the number of seconds before the interface is considered disconnected */
77#define NETWORK_DOWN_TIME 30       /* number of seconds the interface remains down */
78#define BROADCAST_DELAY 0          /* Delay (seconds) before broadcasts are sent */
79#define DHCP_MONITOR_PRIORITY 250  /* Low priority */
80
81/*
82 *  returns 0 when successful, negative value for failure
83 */
84static int remove_address(const char *if_name)
85{
86  struct sockaddr_in address;
87  struct in_aliasreq ifra;
88  int retval = 0;
89
90  memset (&address, '\0', sizeof (address));
91  address.sin_len = sizeof (address);
92  address.sin_family = AF_INET;
93  address.sin_addr.s_addr = INADDR_ANY;
94
95  /* Remove old default route to 0.0.0.0 */
96  if (rtems_bsdnet_rtrequest (RTM_DELETE, (struct sockaddr *)&address, NULL,
97                                          (struct sockaddr *)&address,
98                              (RTF_UP | RTF_STATIC), NULL) < 0 ) {
99    printf ("Failed to delete default route: %s (%d)\n", strerror (errno), errno);
100    retval = -1;
101  }
102
103  /* Remove old ip-address */
104  if (rtems_bsdnet_ifconfig(if_name, SIOCGIFADDR, &address) < 0)  {
105    printf ("Failed to get if address: %s (%d)\n", strerror (errno), errno);
106    return -1;
107  }
108
109  strncpy (ifra.ifra_name, if_name, IFNAMSIZ);
110  memcpy (&ifra.ifra_addr, &address, sizeof(address));
111  if (rtems_bsdnet_ifconfig(if_name, SIOCDIFADDR, &ifra)) {
112    printf ("Can't delete if address: %s (%d)\n", strerror (errno), errno);
113    return -1;
114  }
115
116  return retval;
117}
118
119
120#if NETWORK_DOWN_TIME
121static int
122dhcp_if_down (const char* ifname)
123{
124  int flags;
125  if (rtems_bsdnet_ifconfig (ifname, SIOCGIFFLAGS, &flags) < 0) {
126    printf ("Can't get flags for %s: %s\n", ifname, strerror (errno));
127    return -1;
128  }
129  if (flags & IFF_UP) {
130    flags &= ~IFF_UP;
131    if (rtems_bsdnet_ifconfig (ifname, SIOCSIFFLAGS, &flags) < 0) {
132      printf ("Can't bring %s down: %s\n", ifname, strerror (errno));
133      return -1;
134    }
135  }
136
137  return 0;
138}
139#endif
140
141static int
142dhcp_if_up (const char* ifname)
143{
144  int flags;
145  if (rtems_bsdnet_ifconfig (ifname, SIOCGIFFLAGS, &flags) < 0) {
146    printf ("Can't get flags for %s: %s\n", ifname, strerror (errno));
147    return -1;
148  }
149  if (!(flags & IFF_UP)) {
150    flags |= IFF_UP;
151    if (rtems_bsdnet_ifconfig (ifname, SIOCSIFFLAGS, &flags) < 0) {
152      printf ("Can't bring %s up: %s\n", ifname, strerror (errno));
153      return -1;
154    }
155  }
156
157  return 0;
158}
159
160
161static int
162set_static_address (struct rtems_bsdnet_ifconfig *ifp)
163{
164  short flags;
165  struct sockaddr_in address;
166  struct sockaddr_in netmask;
167  struct sockaddr_in broadcast;
168  struct sockaddr_in gateway;
169
170  if (ifp->ip_address != NULL) {
171    printf("Setting static address for interface %s.\n", ifp->name);
172
173    /*
174     * Bring interface up
175     */
176    if (dhcp_if_up (ifp->name) < 0)
177      return -1;
178
179    /*
180     * Set interface netmask
181     */
182    memset (&netmask, '\0', sizeof netmask);
183    netmask.sin_len = sizeof netmask;
184    netmask.sin_family = AF_INET;
185    netmask.sin_addr.s_addr = inet_addr (ifp->ip_netmask);
186    if (rtems_bsdnet_ifconfig (ifp->name, SIOCSIFNETMASK, &netmask) < 0) {
187      printf ("Can't set %s netmask: %s\n", ifp->name, strerror (errno));
188      return -1;
189    }
190
191    /*
192     * Set interface address
193     */
194    memset (&address, '\0', sizeof address);
195    address.sin_len = sizeof address;
196    address.sin_family = AF_INET;
197    address.sin_addr.s_addr = inet_addr (ifp->ip_address);
198    if (rtems_bsdnet_ifconfig (ifp->name, SIOCSIFADDR, &address) < 0) {
199      printf ("Can't set %s address: %s\n", ifp->name, strerror (errno));
200      return -1;
201    }
202
203    /*
204     * Set interface broadcast address if the interface has the
205     * broadcast flag set.
206     */
207    if (rtems_bsdnet_ifconfig (ifp->name, SIOCGIFFLAGS, &flags) < 0) {
208      printf ("Can't read %s flags: %s\n", ifp->name, strerror (errno));
209      return -1;
210    }
211    if (flags & IFF_BROADCAST) {
212      memset (&broadcast, '\0', sizeof broadcast);
213      broadcast.sin_len = sizeof broadcast;
214      broadcast.sin_family = AF_INET;
215      broadcast.sin_addr.s_addr = address.sin_addr.s_addr | ~netmask.sin_addr.s_addr;
216
217      if (rtems_bsdnet_ifconfig (ifp->name, SIOCSIFBRDADDR, &broadcast) < 0) {
218        struct in_addr in_addr;
219        char buf[20];
220
221        in_addr.s_addr = broadcast.sin_addr.s_addr;
222        if (!inet_ntop (AF_INET, &in_addr, buf, sizeof (buf)))
223            strcpy (buf, "?.?.?.?");
224        printf ("Can't set %s broadcast address %s: %s\n", ifp->name, buf, strerror (errno));
225      }
226    }
227  }
228
229  /*
230   * Set default route
231   */
232  if (rtems_bsdnet_config.gateway) {
233    address.sin_addr.s_addr = INADDR_ANY;
234    netmask.sin_addr.s_addr = INADDR_ANY;
235    memset (&gateway, '\0', sizeof gateway);
236    gateway.sin_len = sizeof gateway;
237    gateway.sin_family = AF_INET;
238    gateway.sin_addr.s_addr = inet_addr (rtems_bsdnet_config.gateway);
239
240    if (rtems_bsdnet_rtrequest (RTM_ADD,
241                                (struct sockaddr *) &address,
242                                (struct sockaddr *) &gateway,
243                                (struct sockaddr *) &netmask,
244                                (RTF_UP | RTF_GATEWAY | RTF_STATIC),
245                                NULL
246                              )  < 0) {
247      printf ("Can't set default route: %s\n", strerror (errno));
248      return -1;
249    }
250  }
251
252  return 0;
253}
254
255static void
256do_dhcp_init (struct rtems_bsdnet_ifconfig *ifp)
257{
258#if BROADCAST_DELAY
259  /* Wait before sending broadcast. */
260  rtems_task_wake_after(RTEMS_MILLISECONDS_TO_TICKS(BROADCAST_DELAY * 1000));
261#endif
262
263  printf ("starting dhcp client...\n");
264
265  remove_address(ifp->name);
266  if (rtems_bsdnet_do_dhcp_timeout () != 0) {
267    remove_address(ifp->name);
268    set_static_address (ifp);      /* use static ip-address if dhcp failed */
269  }
270
271}
272
273/*
274 * Main dhcp monitor thread
275 */
276static void dhcp_monitor_task (rtems_task_argument ifp_arg)
277{
278  struct rtems_bsdnet_ifconfig *ifp = (struct rtems_bsdnet_ifconfig *)ifp_arg;
279  char                         *ifname = ifp->name;
280  unsigned int                  downcount = 0;
281  int                           ifflags;
282  int                           must_renew = FALSE;
283
284  while (TRUE) {
285    if (rtems_bsdnet_ifconfig(ifname, SIOCGIFFLAGS, &ifflags) < 0)  {
286      printf ("Failed to get if flags: %s (%d)\n", strerror (errno), errno);
287      goto error_out;
288    }
289
290    if ((ifflags & IFF_RUNNING) != 0) {
291      if(must_renew) {
292        must_renew = FALSE;
293        do_dhcp_init(ifp);
294      }
295      downcount = 0;
296    } else {
297      if (downcount < NETWORK_FAIL_TIMEOUT) {
298        downcount++;
299
300        if (downcount == NETWORK_FAIL_TIMEOUT) {
301          printf ("lost network connection...\n");
302          rtems_bsdnet_dhcp_down ();
303          must_renew = TRUE;
304#if NETWORK_DOWN_TIME
305          dhcp_if_down(ifname);
306          rtems_task_wake_after(RTEMS_MILLISECONDS_TO_TICKS(NETWORK_DOWN_TIME * 1000));
307          dhcp_if_up(ifname);
308          downcount = 0;
309#endif
310        }
311      }
312    }
313
314    rtems_task_wake_after(RTEMS_MILLISECONDS_TO_TICKS(1000));
315  }
316
317error_out:
318  printf("Stopping dhcp monitoring application.\n");
319  rtems_task_delete(RTEMS_SELF);
320}
321
322/*
323* initialize dhcp monitoring application
324*   start dhcp monitoring thread
325*/
326void rtems_bsdnet_do_dhcp_failsafe (void)
327{
328  rtems_status_code             sc;
329  rtems_id                      id;
330  struct rtems_bsdnet_ifconfig *ifp;
331  int                           ifflags;
332
333  /* Find a suitable interface */
334  for (ifp = rtems_bsdnet_config.ifconfig; ifp; ifp = ifp->next) {
335    if (rtems_bsdnet_ifconfig (ifp->name, SIOCGIFFLAGS, &ifflags) < 0)
336      continue;
337    if ((ifflags & (IFF_LOOPBACK | IFF_POINTOPOINT)) == 0)
338      break;
339  }
340  if (ifp == NULL){
341    printf ("dhcpc_failsafe: no suitable interface\n");
342    return;
343  }
344  printf("starting dhcp on interface %s\n", ifp->name);
345  do_dhcp_init(ifp);
346
347#if NETWORK_FAIL_TIMEOUT
348  sc = rtems_task_create (rtems_build_name ('d','h','c','m'),
349                          DHCP_MONITOR_PRIORITY,
350                          2048,
351                          RTEMS_PREEMPT |
352                          RTEMS_NO_TIMESLICE |
353                          RTEMS_NO_ASR |
354                          RTEMS_INTERRUPT_LEVEL (0),
355                          RTEMS_LOCAL,
356                          &id);
357
358  if (sc != RTEMS_SUCCESSFUL) {
359    printf ("Failed to create dhcp monitor task, code %d\n", sc);
360    return;
361  }
362
363  sc = rtems_task_start (id, dhcp_monitor_task, (rtems_task_argument) ifp);
364
365  if (sc != RTEMS_SUCCESSFUL) {
366    printf ("Failed to start dhcp monitor task, code %d\n", sc);
367  }
368#endif
369}
370
Note: See TracBrowser for help on using the repository browser.