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

4.115
Last change on this file since da10694 was da10694, checked in by Peter Dufault <dufault@…>, on Dec 24, 2014 at 3:14:50 AM

libnetworking: Make rtems_dhcp_failsafe() run time configurable

rtems_dhcp_failsafe() can be configured at compile time with
various options. This change makes it possible to instead configure
it at runtime.

This will make it marginally larger. I haven't measured the difference
but I'll guess it's in the lower hundreds of bytes. The change could be
modified to leave it either compile time or run time configurable,
I prefer the simplicity of a single method.

closes #1905

  • Property mode set to 100644
File size: 11.6 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#include <rtems/rtems_dhcp_failsafe.h>
64
65struct  proc;                   /* Unused parameter of some functions. */
66#include <sys/ioctl.h>
67#include <sys/socket.h>
68#include <net/route.h>
69#include <netinet/in.h>         /* for sockaddr_in */
70#include <net/if.h>             /* for if.h */
71#include <net/if_var.h>         /* for in_var.h */
72#include <netinet/in_var.h>     /* for in_aliasreq */
73#include <sys/sockio.h>         /* for ioctl definitions */
74#include <arpa/inet.h>          /* for inet_addr, inet_ntop */
75
76
77static int network_fail_timeout = RTEMS_DHCP_FAILSAFE_NETWORK_FAIL_TIMEOUT;
78static int network_down_time = RTEMS_DHCP_FAILSAFE_NETWORK_DOWN_TIME;
79static int broadcast_delay = RTEMS_DHCP_FAILSAFE_BROADCAST_DELAY;
80static int dhcp_monitor_priority = RTEMS_DHCP_FAILSAFE_DHCP_MONITOR_PRIORITY;
81
82void rtems_bsdnet_dhcp_failsafe_config(
83  int network_fail_timeout_,
84  int network_down_time_,
85  int broadcast_delay_,
86  int dhcp_monitor_priority_
87)
88{
89  network_fail_timeout = network_fail_timeout_;
90  network_down_time = network_down_time_;
91  broadcast_delay = broadcast_delay_;
92  dhcp_monitor_priority = dhcp_monitor_priority_;
93}
94
95/*
96 *  returns 0 when successful, negative value for failure
97 */
98static int remove_address(const char *if_name)
99{
100  struct sockaddr_in address;
101  struct in_aliasreq ifra;
102  int retval = 0;
103
104  memset (&address, '\0', sizeof (address));
105  address.sin_len = sizeof (address);
106  address.sin_family = AF_INET;
107  address.sin_addr.s_addr = INADDR_ANY;
108
109  /* Remove old default route to 0.0.0.0 */
110  if (rtems_bsdnet_rtrequest (RTM_DELETE, (struct sockaddr *)&address, NULL,
111                                          (struct sockaddr *)&address,
112                              (RTF_UP | RTF_STATIC), NULL) < 0 ) {
113    printf ("Failed to delete default route: %s (%d)\n", strerror (errno), errno);
114    retval = -1;
115  }
116
117  /* Remove old ip-address */
118  if (rtems_bsdnet_ifconfig(if_name, SIOCGIFADDR, &address) < 0)  {
119    printf ("Failed to get if address: %s (%d)\n", strerror (errno), errno);
120    return -1;
121  }
122
123  strncpy (ifra.ifra_name, if_name, IFNAMSIZ);
124  memcpy (&ifra.ifra_addr, &address, sizeof(address));
125  if (rtems_bsdnet_ifconfig(if_name, SIOCDIFADDR, &ifra)) {
126    printf ("Can't delete if address: %s (%d)\n", strerror (errno), errno);
127    return -1;
128  }
129
130  return retval;
131}
132
133
134static int
135dhcp_if_down (const char* ifname)
136{
137  int16_t flags;
138  if (rtems_bsdnet_ifconfig (ifname, SIOCGIFFLAGS, &flags) < 0) {
139    printf ("Can't get flags for %s: %s\n", ifname, strerror (errno));
140    return -1;
141  }
142  if (flags & IFF_UP) {
143    flags &= ~IFF_UP;
144    if (rtems_bsdnet_ifconfig (ifname, SIOCSIFFLAGS, &flags) < 0) {
145      printf ("Can't bring %s down: %s\n", ifname, strerror (errno));
146      return -1;
147    }
148  }
149
150  return 0;
151}
152
153static int
154dhcp_if_up (const char* ifname)
155{
156  int16_t flags;
157  if (rtems_bsdnet_ifconfig (ifname, SIOCGIFFLAGS, &flags) < 0) {
158    printf ("Can't get flags for %s: %s\n", ifname, strerror (errno));
159    return -1;
160  }
161  if (!(flags & IFF_UP)) {
162    flags |= IFF_UP;
163    if (rtems_bsdnet_ifconfig (ifname, SIOCSIFFLAGS, &flags) < 0) {
164      printf ("Can't bring %s up: %s\n", ifname, strerror (errno));
165      return -1;
166    }
167  }
168
169  return 0;
170}
171
172
173static int
174set_static_address (struct rtems_bsdnet_ifconfig *ifp)
175{
176  int16_t flags;
177  struct sockaddr_in address;
178  struct sockaddr_in netmask;
179  struct sockaddr_in broadcast;
180  struct sockaddr_in gateway;
181
182  if (ifp->ip_address != NULL) {
183    printf("Setting static address for interface %s.\n", ifp->name);
184
185    /*
186     * Bring interface up
187     */
188    if (dhcp_if_up (ifp->name) < 0)
189      return -1;
190
191    /*
192     * Set interface netmask
193     */
194    memset (&netmask, '\0', sizeof netmask);
195    netmask.sin_len = sizeof netmask;
196    netmask.sin_family = AF_INET;
197    netmask.sin_addr.s_addr = inet_addr (ifp->ip_netmask);
198    if (rtems_bsdnet_ifconfig (ifp->name, SIOCSIFNETMASK, &netmask) < 0) {
199      printf ("Can't set %s netmask: %s\n", ifp->name, strerror (errno));
200      return -1;
201    }
202
203    /*
204     * Set interface address
205     */
206    memset (&address, '\0', sizeof address);
207    address.sin_len = sizeof address;
208    address.sin_family = AF_INET;
209    address.sin_addr.s_addr = inet_addr (ifp->ip_address);
210    if (rtems_bsdnet_ifconfig (ifp->name, SIOCSIFADDR, &address) < 0) {
211      printf ("Can't set %s address: %s\n", ifp->name, strerror (errno));
212      return -1;
213    }
214
215    /*
216     * Set interface broadcast address if the interface has the
217     * broadcast flag set.
218     */
219    if (rtems_bsdnet_ifconfig (ifp->name, SIOCGIFFLAGS, &flags) < 0) {
220      printf ("Can't read %s flags: %s\n", ifp->name, strerror (errno));
221      return -1;
222    }
223    if (flags & IFF_BROADCAST) {
224      memset (&broadcast, '\0', sizeof broadcast);
225      broadcast.sin_len = sizeof broadcast;
226      broadcast.sin_family = AF_INET;
227      broadcast.sin_addr.s_addr = address.sin_addr.s_addr | ~netmask.sin_addr.s_addr;
228
229      if (rtems_bsdnet_ifconfig (ifp->name, SIOCSIFBRDADDR, &broadcast) < 0) {
230        struct in_addr in_addr;
231        char buf[20];
232
233        in_addr.s_addr = broadcast.sin_addr.s_addr;
234        if (!inet_ntop (AF_INET, &in_addr, buf, sizeof (buf)))
235            strcpy (buf, "?.?.?.?");
236        printf ("Can't set %s broadcast address %s: %s\n", ifp->name, buf, strerror (errno));
237      }
238    }
239  }
240
241  /*
242   * Set default route
243   */
244  if (rtems_bsdnet_config.gateway) {
245    address.sin_addr.s_addr = INADDR_ANY;
246    netmask.sin_addr.s_addr = INADDR_ANY;
247    memset (&gateway, '\0', sizeof gateway);
248    gateway.sin_len = sizeof gateway;
249    gateway.sin_family = AF_INET;
250    gateway.sin_addr.s_addr = inet_addr (rtems_bsdnet_config.gateway);
251
252    if (rtems_bsdnet_rtrequest (RTM_ADD,
253                                (struct sockaddr *) &address,
254                                (struct sockaddr *) &gateway,
255                                (struct sockaddr *) &netmask,
256                                (RTF_UP | RTF_GATEWAY | RTF_STATIC),
257                                NULL
258                              )  < 0) {
259      printf ("Can't set default route: %s\n", strerror (errno));
260      return -1;
261    }
262  }
263
264  return 0;
265}
266
267static void
268do_dhcp_init (struct rtems_bsdnet_ifconfig *ifp)
269{
270  if (broadcast_delay) {
271    /* Wait before sending broadcast. */
272    rtems_task_wake_after(RTEMS_MILLISECONDS_TO_TICKS(broadcast_delay * 1000));
273  }
274
275  printf ("starting dhcp client...\n");
276
277  remove_address(ifp->name);
278  if (rtems_bsdnet_do_dhcp_timeout () != 0) {
279    remove_address(ifp->name);
280    set_static_address (ifp);      /* use static ip-address if dhcp failed */
281  }
282
283}
284
285/*
286 * Main dhcp monitor thread
287 */
288static void dhcp_monitor_task (rtems_task_argument ifp_arg)
289{
290  struct rtems_bsdnet_ifconfig *ifp = (struct rtems_bsdnet_ifconfig *)ifp_arg;
291  char                         *ifname = ifp->name;
292  unsigned int                  downcount = 0;
293  int16_t                       ifflags;
294  int                           must_renew = FALSE;
295
296  while (TRUE) {
297    if (rtems_bsdnet_ifconfig(ifname, SIOCGIFFLAGS, &ifflags) < 0)  {
298      printf ("Failed to get if flags: %s (%d)\n", strerror (errno), errno);
299      goto error_out;
300    }
301
302    if ((ifflags & IFF_RUNNING) != 0) {
303      if(must_renew) {
304        must_renew = FALSE;
305        do_dhcp_init(ifp);
306      }
307      downcount = 0;
308    } else {
309      if (downcount < network_fail_timeout) {
310        downcount++;
311
312        if (downcount == network_fail_timeout) {
313          printf ("lost network connection...\n");
314          rtems_bsdnet_dhcp_down ();
315          must_renew = TRUE;
316          dhcp_if_down(ifname);
317          rtems_task_wake_after(RTEMS_MILLISECONDS_TO_TICKS(network_down_time * 1000));
318          dhcp_if_up(ifname);
319          downcount = 0;
320        }
321      }
322    }
323
324    rtems_task_wake_after(RTEMS_MILLISECONDS_TO_TICKS(1000));
325  }
326
327error_out:
328  printf("Stopping dhcp monitoring application.\n");
329  rtems_task_delete(RTEMS_SELF);
330}
331
332/*
333* initialize dhcp monitoring application
334*   start dhcp monitoring thread
335*/
336void rtems_bsdnet_do_dhcp_failsafe (void)
337{
338  rtems_status_code             sc;
339  rtems_id                      id;
340  struct rtems_bsdnet_ifconfig *ifp;
341  int16_t                       ifflags;
342
343  /* Find a suitable interface */
344  for (ifp = rtems_bsdnet_config.ifconfig; ifp; ifp = ifp->next) {
345    if (rtems_bsdnet_ifconfig (ifp->name, SIOCGIFFLAGS, &ifflags) < 0)
346      continue;
347    if ((ifflags & (IFF_LOOPBACK | IFF_POINTOPOINT)) == 0)
348      break;
349  }
350  if (ifp == NULL){
351    printf ("dhcpc_failsafe: no suitable interface\n");
352    return;
353  }
354  printf("starting dhcp on interface %s\n", ifp->name);
355  do_dhcp_init(ifp);
356
357  if (network_fail_timeout) {
358    sc = rtems_task_create (rtems_build_name ('d','h','c','m'),
359                            dhcp_monitor_priority,
360                            2048,
361                            RTEMS_PREEMPT |
362                            RTEMS_NO_TIMESLICE |
363                            RTEMS_NO_ASR |
364                            RTEMS_INTERRUPT_LEVEL (0),
365                            RTEMS_LOCAL,
366                            &id);
367
368    if (sc != RTEMS_SUCCESSFUL) {
369      printf ("Failed to create dhcp monitor task, code %d\n", sc);
370      return;
371    }
372
373    sc = rtems_task_start (id, dhcp_monitor_task, (rtems_task_argument) ifp);
374
375    if (sc != RTEMS_SUCCESSFUL) {
376      printf ("Failed to start dhcp monitor task, code %d\n", sc);
377    }
378  }
379}
380
Note: See TracBrowser for help on using the repository browser.