dhcp: Move plugins/dhcp in to core
[framework/connectivity/connman.git] / src / dhcp.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 #include <stdio.h>
26 #include <errno.h>
27 #include <string.h>
28 #include <stdlib.h>
29
30 #include <gdhcp/gdhcp.h>
31
32 #include <glib.h>
33
34 #include "connman.h"
35
36 enum connman_dhcp_state {
37         CONNMAN_DHCP_STATE_UNKNOWN  = 0,
38         CONNMAN_DHCP_STATE_IDLE     = 1,
39         CONNMAN_DHCP_STATE_BOUND    = 2,
40         CONNMAN_DHCP_STATE_RENEW    = 3,
41         CONNMAN_DHCP_STATE_FAIL     = 4,
42 };
43
44 struct connman_dhcp {
45         GDHCPClient *dhcp_client;
46
47         int index;
48         enum connman_dhcp_state state;
49
50         struct connman_element *element;
51 };
52
53 static void dhcp_set_value(struct connman_dhcp *dhcp,
54                                 const char *key, const char *value)
55 {
56         char **nameservers;
57
58         if (g_strcmp0(key, "Address") == 0) {
59                 g_free(dhcp->element->ipv4.address);
60                 dhcp->element->ipv4.address = g_strdup(value);
61         } else if (g_strcmp0(key, "Netmask") == 0) {
62                 g_free(dhcp->element->ipv4.netmask);
63                 dhcp->element->ipv4.netmask = g_strdup(value);
64         } else if (g_strcmp0(key, "Gateway") == 0) {
65                 g_free(dhcp->element->ipv4.gateway);
66                 dhcp->element->ipv4.gateway = g_strdup(value);
67         } else if (g_strcmp0(key, "Network") == 0) {
68                 g_free(dhcp->element->ipv4.network);
69                 dhcp->element->ipv4.network = g_strdup(value);
70         } else if (g_strcmp0(key, "Broadcast") == 0) {
71                 g_free(dhcp->element->ipv4.broadcast);
72                 dhcp->element->ipv4.broadcast = g_strdup(value);
73         } else if (g_strcmp0(key, "Nameserver") == 0) {
74                 g_free(dhcp->element->ipv4.nameserver);
75                 nameservers = g_strsplit_set(value, " ", 0);
76                 /* FIXME: The ipv4 structure can only hold one nameserver, so
77                  * we are only able to pass along the first nameserver sent by
78                  * the DHCP server.  If this situation changes, we should
79                  * retain all of them.
80                  */
81                 dhcp->element->ipv4.nameserver = g_strdup(nameservers[0]);
82                 g_strfreev(nameservers);
83         } else if (g_strcmp0(key, "Domainname") == 0) {
84                 g_free(dhcp->element->domainname);
85                 dhcp->element->domainname = g_strdup(value);
86
87                 __connman_utsname_set_domainname(value);
88         } else if (g_strcmp0(key, "Hostname") == 0) {
89                 g_free(dhcp->element->hostname);
90                 dhcp->element->hostname = g_strdup(value);
91
92                 __connman_utsname_set_hostname(value);
93         } else if (g_strcmp0(key, "Timeserver") == 0) {
94                 connman_info("Timeserver %s", value);
95
96                 g_free(dhcp->element->ipv4.timeserver);
97                 dhcp->element->ipv4.timeserver = g_strdup(value);
98         } else if (g_strcmp0(key, "MTU") == 0) {
99         } else if (g_strcmp0(key, "PAC") == 0) {
100                 connman_info("PAC configuration %s", value);
101
102                 g_free(dhcp->element->ipv4.pac);
103                 dhcp->element->ipv4.pac = g_strdup(value);
104         }
105 }
106
107 static void dhcp_bound(struct connman_dhcp *dhcp)
108 {
109         struct connman_element *element;
110
111         DBG("dhcp %p", dhcp);
112
113         element = connman_element_create(NULL);
114         if (element == NULL)
115                 return;
116
117         element->type = CONNMAN_ELEMENT_TYPE_IPV4;
118         element->index = dhcp->index;
119
120         connman_element_update(dhcp->element);
121
122         if (connman_element_register(element, dhcp->element) < 0)
123                 connman_element_unref(element);
124 }
125
126 static void no_lease_cb(GDHCPClient *dhcp_client, gpointer user_data)
127 {
128         struct connman_dhcp *dhcp = user_data;
129
130         DBG("No lease available");
131
132         connman_element_set_error(dhcp->element,
133                                         CONNMAN_ELEMENT_ERROR_FAILED);
134 }
135
136 static void lease_lost_cb(GDHCPClient *dhcp_client, gpointer user_data)
137 {
138         DBG("Lease lost");
139 }
140
141 static void ipv4ll_lost_cb(GDHCPClient *dhcp_client, gpointer user_data)
142 {
143         struct connman_dhcp *dhcp = user_data;
144
145         DBG("Lease lost");
146
147         connman_element_unregister_children(dhcp->element);
148 }
149
150 static void lease_available_cb(GDHCPClient *dhcp_client, gpointer user_data)
151 {
152         struct connman_dhcp *dhcp = user_data;
153         GList *list, *option = NULL;
154         char *address, *nameservers;
155         size_t ns_strlen = 0;
156
157         DBG("Lease available");
158
159         address = g_dhcp_client_get_address(dhcp_client);
160         if (address != NULL)
161                 dhcp_set_value(dhcp, "Address", address);
162         g_free(address);
163
164         option = g_dhcp_client_get_option(dhcp_client, G_DHCP_SUBNET);
165         if (option != NULL)
166                 dhcp_set_value(dhcp, "Netmask", option->data);
167
168         option = g_dhcp_client_get_option(dhcp_client, G_DHCP_DNS_SERVER);
169         for (list = option; list; list = list->next)
170                 ns_strlen += strlen((char *) list->data) + 2;
171         nameservers = g_try_malloc0(ns_strlen);
172         if (nameservers) {
173                 char *ns_index = nameservers;
174
175                 for (list = option; list; list = list->next) {
176                         sprintf(ns_index, "%s ", (char *) list->data);
177                         ns_index += strlen((char *) list->data) + 1;
178                 }
179
180                 dhcp_set_value(dhcp, "Nameserver", nameservers);
181         }
182         g_free(nameservers);
183
184         option = g_dhcp_client_get_option(dhcp_client, G_DHCP_DOMAIN_NAME);
185         if (option != NULL)
186                 dhcp_set_value(dhcp, "Domainname", option->data);
187
188         option = g_dhcp_client_get_option(dhcp_client, G_DHCP_ROUTER);
189         if (option != NULL)
190                 dhcp_set_value(dhcp, "Gateway", option->data);
191
192         option = g_dhcp_client_get_option(dhcp_client, G_DHCP_HOST_NAME);
193         if (option != NULL)
194                 dhcp_set_value(dhcp, "Hostname", option->data);
195
196         option = g_dhcp_client_get_option(dhcp_client, G_DHCP_NTP_SERVER);
197         if (option != NULL)
198                 dhcp_set_value(dhcp, "Timeserver", option->data);
199
200         option = g_dhcp_client_get_option(dhcp_client, 252);
201         if (option != NULL)
202                 dhcp_set_value(dhcp, "PAC", option->data);
203
204         dhcp_bound(dhcp);
205 }
206
207 static void ipv4ll_available_cb(GDHCPClient *dhcp_client, gpointer user_data)
208 {
209         struct connman_dhcp *dhcp = user_data;
210         char *address, *netmask;
211
212         DBG("IPV4LL available");
213
214         address = g_dhcp_client_get_address(dhcp_client);
215         if (address != NULL)
216                 dhcp_set_value(dhcp, "Address", address);
217
218         netmask = g_dhcp_client_get_netmask(dhcp_client);
219         if (netmask != NULL)
220                 dhcp_set_value(dhcp, "Netmask", netmask);
221
222         g_free(address);
223         g_free(netmask);
224
225         dhcp_bound(dhcp);
226 }
227
228 static void dhcp_debug(const char *str, void *data)
229 {
230         connman_info("%s: %s\n", (const char *) data, str);
231 }
232
233 static int dhcp_request(struct connman_dhcp *dhcp)
234 {
235         GDHCPClient *dhcp_client;
236         GDHCPClientError error;
237         const char *hostname;
238         int index;
239
240         DBG("dhcp %p", dhcp);
241
242         index = dhcp->index;
243
244         dhcp_client = g_dhcp_client_new(G_DHCP_IPV4, index, &error);
245         if (error != G_DHCP_CLIENT_ERROR_NONE)
246                 return -EINVAL;
247
248         if (getenv("CONNMAN_DHCP_DEBUG"))
249                 g_dhcp_client_set_debug(dhcp_client, dhcp_debug, "DHCP");
250
251         hostname = connman_utsname_get_hostname();
252         if (hostname != NULL)
253                 g_dhcp_client_set_send(dhcp_client, G_DHCP_HOST_NAME, hostname);
254
255         g_dhcp_client_set_request(dhcp_client, G_DHCP_HOST_NAME);
256         g_dhcp_client_set_request(dhcp_client, G_DHCP_SUBNET);
257         g_dhcp_client_set_request(dhcp_client, G_DHCP_DNS_SERVER);
258         g_dhcp_client_set_request(dhcp_client, G_DHCP_DOMAIN_NAME);
259         g_dhcp_client_set_request(dhcp_client, G_DHCP_NTP_SERVER);
260         g_dhcp_client_set_request(dhcp_client, G_DHCP_ROUTER);
261         g_dhcp_client_set_request(dhcp_client, 252);
262
263         g_dhcp_client_register_event(dhcp_client,
264                         G_DHCP_CLIENT_EVENT_LEASE_AVAILABLE,
265                                                 lease_available_cb, dhcp);
266
267         g_dhcp_client_register_event(dhcp_client,
268                         G_DHCP_CLIENT_EVENT_IPV4LL_AVAILABLE,
269                                                 ipv4ll_available_cb, dhcp);
270
271         g_dhcp_client_register_event(dhcp_client,
272                         G_DHCP_CLIENT_EVENT_LEASE_LOST, lease_lost_cb, dhcp);
273
274         g_dhcp_client_register_event(dhcp_client,
275                         G_DHCP_CLIENT_EVENT_IPV4LL_LOST, ipv4ll_lost_cb, dhcp);
276
277         g_dhcp_client_register_event(dhcp_client,
278                         G_DHCP_CLIENT_EVENT_NO_LEASE, no_lease_cb, dhcp);
279
280         dhcp->dhcp_client = dhcp_client;
281
282         return g_dhcp_client_start(dhcp_client);
283 }
284
285 static int dhcp_release(struct connman_dhcp *dhcp)
286 {
287         DBG("dhcp %p", dhcp);
288
289         g_dhcp_client_stop(dhcp->dhcp_client);
290         g_dhcp_client_unref(dhcp->dhcp_client);
291
292         return 0;
293 }
294
295 static int dhcp_probe(struct connman_element *element)
296 {
297         struct connman_dhcp *dhcp;
298
299         DBG("element %p name %s", element, element->name);
300
301         dhcp = g_try_new0(struct connman_dhcp, 1);
302         if (dhcp == NULL)
303                 return -ENOMEM;
304
305         dhcp->index = element->index;
306         dhcp->state = CONNMAN_DHCP_STATE_IDLE;
307
308         dhcp->element = element;
309
310         connman_element_set_data(element, dhcp);
311
312         dhcp_request(dhcp);
313
314         return 0;
315 }
316
317 static void dhcp_remove(struct connman_element *element)
318 {
319         struct connman_dhcp *dhcp = connman_element_get_data(element);
320
321         DBG("element %p name %s", element, element->name);
322
323         connman_element_set_data(element, NULL);
324
325         dhcp_release(dhcp);
326         g_free(dhcp);
327
328         connman_element_unref(element);
329 }
330
331 static void dhcp_change(struct connman_element *element)
332 {
333         DBG("element %p name %s", element, element->name);
334
335         if (element->state == CONNMAN_ELEMENT_STATE_ERROR)
336                 connman_element_set_error(element->parent,
337                                         CONNMAN_ELEMENT_ERROR_DHCP_FAILED);
338 }
339
340 static struct connman_driver dhcp_driver = {
341         .name           = "dhcp",
342         .type           = CONNMAN_ELEMENT_TYPE_DHCP,
343         .priority       = CONNMAN_DRIVER_PRIORITY_LOW,
344         .probe          = dhcp_probe,
345         .remove         = dhcp_remove,
346         .change         = dhcp_change,
347 };
348
349 int __connman_dhcp_init(void)
350 {
351         return connman_driver_register(&dhcp_driver);
352 }
353
354 void __connman_dhcp_cleanup(void)
355 {
356         connman_driver_unregister(&dhcp_driver);
357 }