068e60f04da17c28ebbec4f5aa9729f06783871a
[barrelfish] / lib / net / dhcp.c
1 /*
2  * Copyright (c) 2017, ETH Zurich.
3  * All rights reserved.
4  *
5  * This file is distributed under the terms in the attached LICENSE file.
6  * If you do not find this file, copies can be found by writing to:
7  * ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group.
8  */
9
10 // stdlib includes
11 #include <sys/socket.h>
12 #include <netinet/in.h>
13 #include <arpa/inet.h>
14
15 // barrelfish includes
16
17 // lwip includes
18 #include <lwip/ip.h>
19 #include <lwip/dhcp.h>
20 #include <lwip/timeouts.h>
21
22 #include <octopus/octopus.h>
23 #include <octopus_server/service.h>
24 #include <octopus/trigger.h>
25
26 #include <net_interfaces/flags.h>
27 #include "networking_internal.h"
28
29 ///< the debug subsystem
30 #define debug_printf_SUBSYSTEM "dhcpd"
31
32 ///< the DHCP timeout in milli seconds
33 #define DHCP_TIMEOUT_MSECS (120UL * 1000)
34
35 #define DHCP_RECORD_FIELDS "{ ip: %d, gw: %d, netmask: %d }"
36
37
38 #define DHCP_RECORD_FORMAT "net.ipconfig " DHCP_RECORD_FIELDS
39
40 #define DHCP_RECORD_REGEX "net.ipconfig  {ip: _,  gw: _, netmask: _}"
41
42
43 static void dhcpd_timer_callback(void *data)
44 {
45     errval_t err;
46
47     struct net_state *st = data;
48
49     dhcp_fine_tmr();
50
51     if ((st->dhcp_ticks % (DHCP_COARSE_TIMER_MSECS / DHCP_FINE_TIMER_MSECS)) == 0) {
52         dhcp_coarse_tmr();
53     }
54
55     if (!ip_addr_cmp(&st->netif.ip_addr, IP_ADDR_ANY) && st->dhcp_done == 0) {
56
57         NETDEBUG("setting system DHCP record to IP: %s\n",
58                 ip4addr_ntoa(netif_ip4_addr(&st->netif)));
59
60         /* register IP with octopus */
61         err = oct_set(DHCP_RECORD_FORMAT, netif_ip4_addr(&st->netif)->addr,
62                       netif_ip4_gw(&st->netif)->addr,
63                       netif_ip4_netmask(&st->netif)->addr);
64         if (err_is_fail(err)) {
65             DEBUG_ERR(err, "failed to set the DHCP record\n");
66         }
67         st->dhcp_done = 1;
68     }
69
70     st->dhcp_ticks++;
71 }
72
73 static void dhcpd_timer_callback_polling(void *data)
74 {
75     dhcpd_timer_callback(data);
76     sys_timeout(DHCP_FINE_TIMER_MSECS, dhcpd_timer_callback_polling, data);
77 }
78
79 static bool dhcpd_has_ip(void)
80 {
81     struct net_state *st = get_default_net_state();
82     return !ip_addr_cmp(&st->netif.ip_addr, IP_ADDR_ANY);
83 }
84
85
86 /**
87  * @brief starts the dhcpd service on the interface
88  *
89  * @param flags flags to provide
90  *
91  * @return SYS_ERR_OK on success, errval on failure
92  */
93 errval_t dhcpd_start(net_flags_t flags)
94 {
95     errval_t err;
96
97     struct net_state *st = get_default_net_state();
98
99     // initialize octopus if not already done
100     err = oct_init();
101     if (err_is_fail(err)) {
102         return err;
103     }
104
105     debug_printf("starting DHCP...\n");
106     err_t lwip_err = dhcp_start(&st->netif);
107     if(lwip_err != ERR_OK) {
108         printf("ERRRRRR dhcp start: %i\n", lwip_err);
109         return -1;
110     }
111
112     st->dhcp_ticks = 1;
113     st->dhcp_running = 1;
114
115     if (flags & NET_FLAGS_POLLING) {
116         sys_timeout(DHCP_FINE_TIMER_MSECS, dhcpd_timer_callback_polling, st);
117     } else {
118         /* DHCP fine timer */
119         err = periodic_event_create(&st->dhcp_timer, st->waitset,
120                                     (DHCP_FINE_TIMER_MSECS * 1000),
121                                     MKCLOSURE(dhcpd_timer_callback, st));
122     }
123
124
125     if (err_is_fail(err)) {
126         dhcp_stop(&st->netif);
127         return err;
128     }
129
130     if (flags & NET_FLAGS_BLOCKING_INIT) {
131         printf("waiting for DHCP to complete \n");
132         while(!dhcpd_has_ip()) {
133             networking_poll();
134             if (st->dhcp_ticks > DHCP_TIMEOUT_MSECS / DHCP_FINE_TIMER_MSECS) {
135                 dhcpd_stop();
136                 return -1;
137             }
138         }
139         printf("OK\nDHCP completed.\n");
140     }
141
142     return SYS_ERR_OK;
143 }
144
145
146 /**
147  * @brief stops the dhcpd service
148  */
149 errval_t dhcpd_stop(void)
150 {
151     struct net_state *st = get_default_net_state();
152
153     periodic_event_cancel(&st->dhcp_timer);
154
155     dhcp_stop(&st->netif);
156
157     st->dhcp_ticks = 0;
158     st->dhcp_running = 0;
159
160     return SYS_ERR_OK;
161 }
162
163
164 /* functions for querying the current settings */
165
166
167 static void dhcpd_change_event(octopus_mode_t mode, const char* record, void* arg)
168 {
169     errval_t err;
170
171     struct net_state *st = arg;
172
173     debug_printf("DHCP change event: %s\n", record);
174
175     if (mode & OCT_ON_SET) {
176
177         uint64_t ip, nm, gw;
178         err = oct_read(record, "_" DHCP_RECORD_FIELDS, &ip, &gw, &nm);
179         if (err_is_fail(err)) {
180             DEBUG_ERR(err, "cannot read DHCPD record '%s\n", record);
181             return;
182         }
183
184         struct in_addr ipaddr, netmask, gateway;
185         ipaddr.s_addr = (uint32_t)ip;
186         netmask.s_addr = (uint32_t)nm;
187         gateway.s_addr = (uint32_t)gw;
188
189         debug_printf("DHCP got ip set: %s\n", inet_ntoa(ipaddr));
190         debug_printf("DHCP got gw set: %s\n", inet_ntoa(gateway));
191         debug_printf("DHCP got nm set: %s\n", inet_ntoa(netmask));
192
193         ip_addr_t _ipaddr, _netmask, _gateway;
194         _ipaddr.addr = ipaddr.s_addr;
195         _netmask.addr = netmask.s_addr;
196         _gateway.addr = gateway.s_addr;
197         netif_set_addr(&st->netif, &_ipaddr, &_netmask, &_gateway);
198         netif_set_up(&st->netif);
199
200         st->dhcp_done = true;
201     }
202
203     if (mode & OCT_ON_DEL) {
204
205         /* DHCP has been removed */
206         netif_set_down(&st->netif);
207     }
208 }
209
210 /**
211  * @brief queries the DHCPD settings of the machine
212  *
213  * @return SYS_ERR_OK on success, errval on failure
214  */
215 errval_t dhcpd_query(net_flags_t flags)
216 {
217     errval_t err;
218
219     NETDEBUG("query DHCPD for IP...\n");
220
221     // initialize octopus if not already done
222     err = oct_init();
223     if (err_is_fail(err)) {
224         return err;
225     }
226
227     struct net_state *st = get_default_net_state();
228     assert(st);
229
230     st->dhcp_ticks = 1;
231     st->dhcp_running = 1;
232
233     err = oct_trigger_existing_and_watch(DHCP_RECORD_REGEX, dhcpd_change_event,
234                                          st, &st->dhcp_triggerid);
235     if (err_is_fail(err)) {
236         return err;
237     }
238
239     if (flags & NET_FLAGS_BLOCKING_INIT) {
240         printf("waiting for DHCP to complete");
241         while(!dhcpd_has_ip()) {
242             event_dispatch(get_default_waitset());
243         }
244     }
245
246     return SYS_ERR_OK;
247 }
248
249
250 /**
251  * @brief returns the IP configuration
252  *
253  * @param ip    return the IP address
254  * @param gw    returns the gateway
255  * @param nm    returns the netmask
256  *
257  * @return
258  */
259 errval_t dhcpd_get_ipconfig(struct in_addr *ip, struct in_addr *gw, struct in_addr *nm)
260 {
261     struct net_state *st = get_default_net_state();
262     if (ip) {
263         ip->s_addr = netif_ip4_addr(&st->netif)->addr;
264     }
265
266     if (gw) {
267         gw->s_addr = netif_ip4_gw(&st->netif)->addr;
268     }
269
270     if (nm) {
271         nm->s_addr = netif_ip4_netmask(&st->netif)->addr;
272     }
273
274     return SYS_ERR_OK;
275 }
276
277 /**
278  * @brief sets the IP configuration
279  *
280  * @param ip    the IP address
281  * @param gw    the Gateway
282  * @param nm    the Netmask
283  *
284  * @return SYS_ERR_OK on success, errval on failure
285  */
286 errval_t dhcpd_set_ipconfig(struct in_addr *ip, struct in_addr *gw, struct in_addr *nm)
287 {
288     errval_t err;
289     struct net_state *st = get_default_net_state();
290
291     if (st->dhcp_running == 1) {
292         if (st->dhcp_triggerid) {
293             err = oct_remove_trigger(st->dhcp_triggerid);
294         } else {
295             err = dhcpd_stop();
296         }
297         if (err_is_fail(err)) {
298             return err;
299         }
300     }
301
302     ip_addr_t _ipaddr, _netmask, _gateway;
303     _ipaddr.addr = ip->s_addr;
304     _netmask.addr = nm->s_addr;
305     _gateway.addr = gw->s_addr;
306     netif_set_addr(&st->netif, &_ipaddr, &_netmask, &_gateway);
307     netif_set_up(&st->netif);
308
309     st->dhcp_done = true;
310
311     return SYS_ERR_OK;
312 }