Add initial implementation for uDHCP support
[connman] / plugins / ipv4.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2008  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
26 #include <unistd.h>
27 #include <string.h>
28 #include <sys/ioctl.h>
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 #include <net/if.h>
33 #include <net/route.h>
34
35 #include <linux/netlink.h>
36 #include <linux/rtnetlink.h>
37
38 #include <connman/plugin.h>
39 #include <connman/driver.h>
40 #include <connman/resolver.h>
41 #include <connman/rtnl.h>
42 #include <connman/log.h>
43
44 #include "inet.h"
45
46 enum connman_ipv4_method {
47         CONNMAN_IPV4_METHOD_UNKNOWN = 0,
48         CONNMAN_IPV4_METHOD_OFF     = 1,
49         CONNMAN_IPV4_METHOD_STATIC  = 2,
50         CONNMAN_IPV4_METHOD_DHCP    = 3,
51 };
52
53 struct connman_ipv4 {
54         enum connman_ipv4_method method;
55         struct in_addr address;
56         struct in_addr netmask;
57         struct in_addr broadcast;
58 };
59
60 struct gateway_data {
61         int index;
62         char *gateway;
63 };
64
65 static GSList *gateway_list = NULL;
66
67 static struct gateway_data *find_gateway(int index, const char *gateway)
68 {
69         GSList *list;
70
71         if (gateway == NULL)
72                 return NULL;
73
74         for (list = gateway_list; list; list = list->next) {
75                 struct gateway_data *data = list->data;
76
77                 if (data->gateway == NULL)
78                         continue;
79
80                 if (data->index == index &&
81                                 g_str_equal(data->gateway, gateway) == TRUE)
82                         return data;
83         }
84
85         return NULL;
86 }
87
88 static int set_ipv4(struct connman_element *element,
89                         struct connman_ipv4 *ipv4, const char *nameserver)
90 {
91         struct ifreq ifr;
92         struct sockaddr_in *addr;
93         int sk, err;
94
95         DBG("element %p ipv4 %p", element, ipv4);
96
97         sk = socket(PF_INET, SOCK_DGRAM, 0);
98         if (sk < 0)
99                 return -1;
100
101         memset(&ifr, 0, sizeof(ifr));
102         ifr.ifr_ifindex = element->index;
103
104         if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
105                 close(sk);
106                 return -1;
107         }
108
109         DBG("ifname %s", ifr.ifr_name);
110
111         addr = (struct sockaddr_in *) &ifr.ifr_addr;
112         addr->sin_family = AF_INET;
113         addr->sin_addr = ipv4->address;
114
115         err = ioctl(sk, SIOCSIFADDR, &ifr);
116
117         if (err < 0)
118                 DBG("address setting failed (%s)", strerror(errno));
119
120         addr = (struct sockaddr_in *) &ifr.ifr_netmask;
121         addr->sin_family = AF_INET;
122         addr->sin_addr = ipv4->netmask;
123
124         err = ioctl(sk, SIOCSIFNETMASK, &ifr);
125
126         if (err < 0)
127                 DBG("netmask setting failed (%s)", strerror(errno));
128
129         addr = (struct sockaddr_in *) &ifr.ifr_broadaddr;
130         addr->sin_family = AF_INET;
131         addr->sin_addr = ipv4->broadcast;
132
133         err = ioctl(sk, SIOCSIFBRDADDR, &ifr);
134
135         if (err < 0)
136                 DBG("broadcast setting failed (%s)", strerror(errno));
137
138         close(sk);
139
140         connman_resolver_append(ifr.ifr_name, NULL, nameserver);
141
142         return 0;
143 }
144
145 static int clear_ipv4(struct connman_element *element)
146 {
147         struct ifreq ifr;
148         struct sockaddr_in *addr;
149         int sk, err;
150
151         DBG("element %p", element);
152
153         sk = socket(PF_INET, SOCK_DGRAM, 0);
154         if (sk < 0)
155                 return -1;
156
157         memset(&ifr, 0, sizeof(ifr));
158         ifr.ifr_ifindex = element->index;
159
160         if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
161                 close(sk);
162                 return -1;
163         }
164
165         DBG("ifname %s", ifr.ifr_name);
166
167         connman_resolver_remove_all(ifr.ifr_name);
168
169         addr = (struct sockaddr_in *) &ifr.ifr_addr;
170         addr->sin_family = AF_INET;
171         addr->sin_addr.s_addr = INADDR_ANY;
172
173         //err = ioctl(sk, SIOCDIFADDR, &ifr);
174         err = ioctl(sk, SIOCSIFADDR, &ifr);
175
176         close(sk);
177
178         if (err < 0 && errno != EADDRNOTAVAIL) {
179                 DBG("address removal failed (%s)", strerror(errno));
180                 return -1;
181         }
182
183         return 0;
184 }
185
186 static int set_route(struct connman_element *element, const char *gateway)
187 {
188         struct ifreq ifr;
189         struct rtentry rt;
190         struct sockaddr_in *addr;
191         int sk, err;
192
193         DBG("element %p", element);
194
195         sk = socket(PF_INET, SOCK_DGRAM, 0);
196         if (sk < 0)
197                 return -1;
198
199         memset(&ifr, 0, sizeof(ifr));
200         ifr.ifr_ifindex = element->index;
201
202         if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
203                 close(sk);
204                 return -1;
205         }
206
207         DBG("ifname %s", ifr.ifr_name);
208
209         memset(&rt, 0, sizeof(rt));
210         rt.rt_flags = RTF_UP | RTF_GATEWAY;
211
212         addr = (struct sockaddr_in *) &rt.rt_dst;
213         addr->sin_family = AF_INET;
214         addr->sin_addr.s_addr = INADDR_ANY;
215
216         addr = (struct sockaddr_in *) &rt.rt_gateway;
217         addr->sin_family = AF_INET;
218         addr->sin_addr.s_addr = inet_addr(gateway);
219
220         addr = (struct sockaddr_in *) &rt.rt_genmask;
221         addr->sin_family = AF_INET;
222         addr->sin_addr.s_addr = INADDR_ANY;
223
224         err = ioctl(sk, SIOCADDRT, &rt);
225         if (err < 0)
226                 DBG("default route setting failed (%s)", strerror(errno));
227
228         close(sk);
229
230         return err;
231 }
232
233 static int del_route(struct connman_element *element, const char *gateway)
234 {
235         struct ifreq ifr;
236         struct rtentry rt;
237         struct sockaddr_in *addr;
238         int sk, err;
239
240         DBG("element %p", element);
241
242         sk = socket(PF_INET, SOCK_DGRAM, 0);
243         if (sk < 0)
244                 return -1;
245
246         memset(&ifr, 0, sizeof(ifr));
247         ifr.ifr_ifindex = element->index;
248
249         if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
250                 close(sk);
251                 return -1;
252         }
253
254         DBG("ifname %s", ifr.ifr_name);
255
256         memset(&rt, 0, sizeof(rt));
257         rt.rt_flags = RTF_UP | RTF_GATEWAY;
258
259         addr = (struct sockaddr_in *) &rt.rt_dst;
260         addr->sin_family = AF_INET;
261         addr->sin_addr.s_addr = INADDR_ANY;
262
263         addr = (struct sockaddr_in *) &rt.rt_gateway;
264         addr->sin_family = AF_INET;
265         addr->sin_addr.s_addr = inet_addr(gateway);
266
267         addr = (struct sockaddr_in *) &rt.rt_genmask;
268         addr->sin_family = AF_INET;
269         addr->sin_addr.s_addr = INADDR_ANY;
270
271         err = ioctl(sk, SIOCDELRT, &rt);
272         if (err < 0)
273                 DBG("default route removal failed (%s)", strerror(errno));
274
275         close(sk);
276
277         return err;
278 }
279
280 static int conn_probe(struct connman_element *element)
281 {
282         const char *gateway = NULL;
283
284         DBG("element %p name %s", element, element->name);
285
286         if (element->parent == NULL)
287                 return -ENODEV;
288
289         if (element->parent->type != CONNMAN_ELEMENT_TYPE_IPV4)
290                 return -ENODEV;
291
292         connman_element_get_value(element,
293                                 CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
294
295         DBG("gateway %s", gateway);
296
297         if (gateway == NULL)
298                 return 0;
299
300         if (g_slist_length(gateway_list) > 0) {
301                 DBG("default already present");
302                 return 0;
303         }
304
305         set_route(element, gateway);
306
307         connman_element_set_enabled(element, TRUE);
308
309         return 0;
310 }
311
312 static void conn_remove(struct connman_element *element)
313 {
314         DBG("element %p name %s", element, element->name);
315 }
316
317 static int conn_enable(struct connman_element *element)
318 {
319         const char *gateway = NULL;
320
321         DBG("element %p name %s", element, element->name);
322
323         connman_element_get_value(element,
324                                 CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
325
326         DBG("gateway %s", gateway);
327
328         if (gateway == NULL)
329                 return -EINVAL;
330
331         set_route(element, gateway);
332
333         return 0;
334 }
335
336 static int conn_disable(struct connman_element *element)
337 {
338         const char *gateway = NULL;
339
340         DBG("element %p name %s", element, element->name);
341
342         connman_element_get_value(element,
343                                 CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
344
345         DBG("gateway %s", gateway);
346
347         if (gateway == NULL)
348                 return -EINVAL;
349
350         del_route(element, gateway);
351
352         return 0;
353 }
354
355 static struct connman_driver conn_driver = {
356         .name           = "ipv4-connection",
357         .type           = CONNMAN_ELEMENT_TYPE_CONNECTION,
358         .probe          = conn_probe,
359         .remove         = conn_remove,
360         .enable         = conn_enable,
361         .disable        = conn_disable,
362 };
363
364 static int ipv4_probe(struct connman_element *element)
365 {
366         struct connman_element *connection;
367         struct connman_ipv4 ipv4;
368         const char *address = NULL, *netmask = NULL, *broadcast = NULL;
369         const char *nameserver = NULL;
370
371         DBG("element %p name %s", element, element->name);
372
373         connman_element_get_value(element,
374                                 CONNMAN_PROPERTY_ID_IPV4_ADDRESS, &address);
375         connman_element_get_value(element,
376                                 CONNMAN_PROPERTY_ID_IPV4_NETMASK, &netmask);
377         connman_element_get_value(element,
378                                 CONNMAN_PROPERTY_ID_IPV4_BROADCAST, &broadcast);
379
380         connman_element_get_value(element,
381                         CONNMAN_PROPERTY_ID_IPV4_NAMESERVER, &nameserver);
382
383         DBG("address %s", address);
384         DBG("netmask %s", netmask);
385         DBG("broadcast %s", broadcast);
386
387         if (address == NULL || netmask == NULL)
388                 return -EINVAL;
389
390         memset(&ipv4, 0, sizeof(ipv4));
391         ipv4.address.s_addr = inet_addr(address);
392         ipv4.netmask.s_addr = inet_addr(netmask);
393         ipv4.broadcast.s_addr = inet_addr(broadcast);
394
395         set_ipv4(element, &ipv4, nameserver);
396
397         connection = connman_element_create(NULL);
398
399         connection->type = CONNMAN_ELEMENT_TYPE_CONNECTION;
400         connection->index = element->index;
401
402         if (connman_element_register(connection, element) < 0)
403                 connman_element_unref(connection);
404
405         return 0;
406 }
407
408 static void ipv4_remove(struct connman_element *element)
409 {
410         DBG("element %p name %s", element, element->name);
411
412         clear_ipv4(element);
413 }
414
415 static struct connman_driver ipv4_driver = {
416         .name           = "ipv4-address",
417         .type           = CONNMAN_ELEMENT_TYPE_IPV4,
418         .probe          = ipv4_probe,
419         .remove         = ipv4_remove,
420 };
421
422 static void ipv4_newgateway(int index, const char *gateway)
423 {
424         struct gateway_data *data;
425
426         DBG("index %d gateway %s", index, gateway);
427
428         data = find_gateway(index, gateway);
429         if (data != NULL)
430                 return;
431
432         data = g_try_new0(struct gateway_data, 1);
433         if (data == NULL)
434                 return;
435
436         data->index = index;
437         data->gateway = g_strdup(gateway);
438
439         gateway_list = g_slist_append(gateway_list, data);
440 }
441
442 static void ipv4_delgateway(int index, const char *gateway)
443 {
444         struct gateway_data *data;
445
446         DBG("index %d gateway %s", index, gateway);
447
448         data = find_gateway(index, gateway);
449         if (data == NULL)
450                 return;
451
452         gateway_list = g_slist_remove(gateway_list, data);
453
454         g_free(data->gateway);
455         g_free(data);
456 }
457
458 static struct connman_rtnl ipv4_rtnl = {
459         .name           = "ipv4-rtnl",
460         .newgateway     = ipv4_newgateway,
461         .delgateway     = ipv4_delgateway,
462 };
463
464 static int ipv4_init(void)
465 {
466         int err;
467
468         err = connman_rtnl_register(&ipv4_rtnl);
469         if (err < 0)
470                 return err;
471
472         connman_rtnl_send_getroute();
473
474         err = connman_driver_register(&conn_driver);
475         if (err < 0) {
476                 connman_rtnl_unregister(&ipv4_rtnl);
477                 return err;
478         }
479
480         err = connman_driver_register(&ipv4_driver);
481         if (err < 0) {
482                 connman_driver_unregister(&conn_driver);
483                 connman_rtnl_unregister(&ipv4_rtnl);
484         }
485
486         return err;
487 }
488
489 static void ipv4_exit(void)
490 {
491         GSList *list;
492
493         connman_driver_unregister(&conn_driver);
494         connman_driver_unregister(&ipv4_driver);
495
496         connman_rtnl_unregister(&ipv4_rtnl);
497
498         for (list = gateway_list; list; list = list->next) {
499                 struct gateway_data *data = list->data;
500
501                 DBG("index %d gateway %s", data->index, data->gateway);
502
503                 g_free(data->gateway);
504                 g_free(data);
505                 list->data = NULL;
506         }
507
508         g_slist_free(gateway_list);
509         gateway_list = NULL;
510 }
511
512 CONNMAN_PLUGIN_DEFINE(ipv4, "IPv4 configuration plugin", VERSION,
513                                                         ipv4_init, ipv4_exit)