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/prot/dhcp.h>
21#include <lwip/timeouts.h>
22
23#include <octopus/octopus.h>
24
25
26#include <net_interfaces/flags.h>
27#include "networking_internal.h"
28
29///< the debug subsystem
30//
31#define NETDEBUG_SUBSYSTEM "dhcpd"
32
33///< the DHCP timeout in milli seconds
34#define DHCP_TIMEOUT_MSECS (120UL * 1000)
35
36
37static void dhcpd_timer_callback(void *data)
38{
39    struct net_state *st = data;
40
41    dhcp_fine_tmr();
42    if ((st->dhcp_ticks % (DHCP_COARSE_TIMER_MSECS / DHCP_FINE_TIMER_MSECS)) == 0) {
43        dhcp_coarse_tmr();
44    }
45    st->dhcp_ticks++;
46}
47
48/*
49static void dhcpd_timer_callback_polling(void *data)
50{
51    dhcpd_timer_callback(data);
52    sys_timeout(DHCP_FINE_TIMER_MSECS, dhcpd_timer_callback_polling, data);
53}
54*/
55
56static bool dhcpd_has_ip(void)
57{
58    struct net_state *st = get_default_net_state();
59    return !ip_addr_cmp(&st->netif.ip_addr, IP_ADDR_ANY);
60}
61
62
63/**
64 * @brief starts the dhcpd service on the interface
65 *
66 * @param flags flags to provide
67 *
68 * @return SYS_ERR_OK on success, errval on failure
69 */
70errval_t dhcpd_start(net_flags_t flags)
71{
72    errval_t err;
73
74    struct net_state *st = get_default_net_state();
75
76    // initialize octopus if not already done
77    err = oct_init();
78    if (err_is_fail(err)) {
79        return err;
80    }
81
82    debug_printf("starting DHCP...\n");
83    err_t lwip_err = dhcp_start(&st->netif);
84    if(lwip_err != ERR_OK) {
85        printf("ERRRRRR dhcp start: %i\n", lwip_err);
86        return -1;
87    }
88
89    st->dhcp_ticks = 1;
90    st->dhcp_running = 1;
91
92    /* DHCP fine timer */
93    err = periodic_event_create(&st->dhcp_timer, st->waitset,
94                                (DHCP_FINE_TIMER_MSECS * 1000),
95                                MKCLOSURE(dhcpd_timer_callback, st));
96
97
98    if (err_is_fail(err)) {
99        dhcp_stop(&st->netif);
100        return err;
101    }
102
103    if (flags & NET_FLAGS_BLOCKING_INIT) {
104        printf("waiting for DHCP to complete \n");
105        while (!dhcpd_has_ip()) {
106            // will call event_dispatch()/event_dispatch_nonblock()
107            networking_poll();
108            if (st->dhcp_ticks > DHCP_TIMEOUT_MSECS / DHCP_FINE_TIMER_MSECS) {
109                dhcpd_stop();
110                return -1;
111            }
112        }
113        printf("OK\nDHCP completed.\n");
114    }
115
116    return SYS_ERR_OK;
117}
118
119
120/**
121 * @brief stops the dhcpd service
122 */
123errval_t dhcpd_stop(void)
124{
125    struct net_state *st = get_default_net_state();
126
127    periodic_event_cancel(&st->dhcp_timer);
128
129    dhcp_stop(&st->netif);
130
131    st->dhcp_ticks = 0;
132    st->dhcp_running = 0;
133
134    return SYS_ERR_OK;
135}
136
137
138/**
139 * @brief queries the current ip setting of the machine
140 *
141 * @return SYS_ERR_OK on success, errval on failure
142 */
143errval_t net_config_current_ip_query(net_flags_t flags, uint32_t* ip_address)
144{
145    errval_t err;
146
147    NETDEBUG("query current IP...\n");
148
149    // initialize octopus if not already done
150    err = oct_init();
151    if (err_is_fail(err)) {
152        return err;
153    }
154
155    char* record = NULL;
156    err = oct_get(&record, "net.current_ip");
157    if (err_no(err) == OCT_ERR_NO_RECORD && (flags & NET_FLAGS_BLOCKING_INIT)) {
158        printf("waiting for DHCP to complete \n");
159        err = oct_wait_for(&record, NET_CONFIG_CURRENT_IP_RECORD_REGEX);
160        if (err_is_fail(err)) {
161            return err;
162        }
163    } else if (err_is_fail(err)) {
164        DEBUG_ERR(err, "cannot get static ip record\n");
165        return err;
166    }
167
168    uint64_t ip, nm, gw;
169    err = oct_read(record, "_" NET_CONFIG_IP_RECORD_FIELDS, &ip, &gw, &nm);
170    if (err_is_fail(err)) {
171        DEBUG_ERR(err, "cannot read current ip record '%s\n", record);
172        free(record);
173        return err;
174    }
175    free(record);
176
177    struct in_addr ipaddr, netmask, gateway;
178    ipaddr.s_addr = (uint32_t)ip;
179    netmask.s_addr = (uint32_t)nm;
180    gateway.s_addr = (uint32_t)gw;
181    *ip_address = (uint32_t)ip;
182
183    debug_printf("Got current IP set: %s\n", inet_ntoa(ipaddr));
184    debug_printf("Got current GW set: %s\n", inet_ntoa(gateway));
185    debug_printf("Got current NM set: %s\n", inet_ntoa(netmask));
186
187    return SYS_ERR_OK;
188}
189
190
191/**
192 * @brief queries the static ip setting of the machine and sets it
193 *
194 * @return SYS_ERR_OK on success, errval on failure
195 */
196errval_t net_config_static_ip_query(net_flags_t flags)
197{
198    errval_t err;
199
200    NETDEBUG("query static IP...\n");
201
202    // initialize octopus if not already done
203    err = oct_init();
204    if (err_is_fail(err)) {
205        return err;
206    }
207
208    struct net_state *st = get_default_net_state();
209    assert(st);
210
211    char* record = NULL;
212    err = oct_get(&record, "net.static_ip");
213    if (err_is_fail(err)) {
214        DEBUG_ERR(err, "cannot get static ip record\n");
215        return err;
216    }
217
218    uint64_t ip, nm, gw;
219    err = oct_read(record, "_" NET_CONFIG_IP_RECORD_FIELDS, &ip, &gw, &nm);
220    if (err_is_fail(err)) {
221        DEBUG_ERR(err, "cannot read static ip record '%s\n", record);
222        free(record);
223        return err;
224    }
225    free(record);
226
227    struct in_addr ipaddr, netmask, gateway;
228    ipaddr.s_addr = (uint32_t)ip;
229    netmask.s_addr = (uint32_t)nm;
230    gateway.s_addr = (uint32_t)gw;
231
232    debug_printf("Got static IP set: %s\n", inet_ntoa(ipaddr));
233    debug_printf("Got static GW set: %s\n", inet_ntoa(gateway));
234    debug_printf("Got static NM set: %s\n", inet_ntoa(netmask));
235
236    err = netif_set_ipconfig(&ipaddr, &gateway, &netmask);
237    if (err_is_fail(err)) {
238        DEBUG_ERR(err, "cannot set static ip\n");
239        return err;
240    }
241
242    return SYS_ERR_OK;
243}
244
245
246/**
247 * @brief returns the IP configuration
248 *
249 * @param ip    return the IP address
250 * @param gw    returns the gateway
251 * @param nm    returns the netmask
252 *
253 * @return
254 */
255errval_t netif_get_ipconfig(struct in_addr *ip, struct in_addr *gw, struct in_addr *nm)
256{
257    struct net_state *st = get_default_net_state();
258    if (ip) {
259        ip->s_addr = netif_ip4_addr(&st->netif)->addr;
260    }
261
262    if (gw) {
263        gw->s_addr = netif_ip4_gw(&st->netif)->addr;
264    }
265
266    if (nm) {
267        nm->s_addr = netif_ip4_netmask(&st->netif)->addr;
268    }
269
270    return SYS_ERR_OK;
271}
272
273/**
274 * @brief sets the IP configuration, overrides DHCP
275 *
276 * @param ip    the IP address
277 * @param gw    the Gateway
278 * @param nm    the Netmask
279 *
280 * @return SYS_ERR_OK on success, errval on failure
281 */
282errval_t netif_set_ipconfig(struct in_addr *ip, struct in_addr *gw, struct in_addr *nm)
283{
284    errval_t err;
285    struct net_state *st = get_default_net_state();
286
287    if (st->dhcp_running == 1) { // stop dhcp, if it's running
288        err = dhcpd_stop();
289        if (err_is_fail(err)) {
290            return err;
291        }
292    }
293
294    ip_addr_t _ipaddr, _netmask, _gateway;
295    _ipaddr.addr = ip->s_addr;
296    _netmask.addr = nm->s_addr;
297    _gateway.addr = gw->s_addr;
298    netif_set_addr(&st->netif, &_ipaddr, &_netmask, &_gateway);
299    netif_set_up(&st->netif);
300
301    return SYS_ERR_OK;
302}
303