Add generic setting of IPv4 addresses and the default route
[connman] / src / iface.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007  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 <stdio.h>
27 #include <errno.h>
28 #include <unistd.h>
29 #include <string.h>
30 #include <sys/ioctl.h>
31 #include <sys/socket.h>
32 #include <netinet/in.h>
33 #include <arpa/inet.h>
34 #include <net/if.h>
35 #include <net/route.h>
36
37 #include <linux/netlink.h>
38 #include <linux/rtnetlink.h>
39
40 #include <glib.h>
41 #include <gdbus.h>
42
43 #include <hal/libhal.h>
44
45 #include "connman.h"
46
47 static GSList *drivers = NULL;
48
49 int connman_iface_register(struct connman_iface_driver *driver)
50 {
51         DBG("driver %p", driver);
52
53         drivers = g_slist_append(drivers, driver);
54
55         return 0;
56 }
57
58 void connman_iface_unregister(struct connman_iface_driver *driver)
59 {
60         DBG("driver %p", driver);
61
62         drivers = g_slist_remove(drivers, driver);
63 }
64
65 static GSList *interfaces = NULL;
66
67 struct connman_iface *__connman_iface_find(int index)
68 {
69         GSList *list;
70
71         for (list = interfaces; list; list = list->next) {
72                 struct connman_iface *iface = list->data;
73
74                 if (iface->index == index)
75                         return iface;
76         }
77
78         return NULL;
79 }
80
81 void __connman_iface_list(DBusMessageIter *iter)
82 {
83         GSList *list;
84
85         DBG("");
86
87         for (list = interfaces; list; list = list->next) {
88                 struct connman_iface *iface = list->data;
89
90                 dbus_message_iter_append_basic(iter,
91                                 DBUS_TYPE_OBJECT_PATH, &iface->path);
92         }
93 }
94
95 int connman_iface_update(struct connman_iface *iface,
96                                         enum connman_iface_state state)
97 {
98         switch (state) {
99         case CONNMAN_IFACE_STATE_ACTIVE:
100                 if (iface->type == CONNMAN_IFACE_TYPE_80211) {
101                         if (iface->driver->scan)
102                                 iface->driver->scan(iface);
103
104                         if (iface->driver->connect)
105                                 iface->driver->connect(iface, NULL);
106                 }
107                 break;
108
109         case CONNMAN_IFACE_STATE_CONNECTED:
110                 __connman_dhcp_request(iface);
111                 break;
112
113         default:
114                 break;
115         }
116
117         iface->state = state;
118
119         return 0;
120 }
121
122 void connman_iface_indicate_carrier(struct connman_iface *iface, int carrier)
123 {
124         DBG("iface %p carrier %d", iface, carrier);
125 }
126
127 int connman_iface_get_ipv4(struct connman_iface *iface,
128                                                 struct connman_ipv4 *ipv4)
129 {
130         struct {
131                 struct nlmsghdr hdr;
132                 struct rtgenmsg msg;
133         } req;
134
135         if ((iface->flags & CONNMAN_IFACE_FLAG_RTNL) == 0)
136                 return -1;
137
138         DBG("iface %p ipv4 %p", iface, ipv4);
139
140         memset(&req, 0, sizeof(req));
141         req.hdr.nlmsg_len = sizeof(req.hdr) + sizeof(req.msg);
142         req.hdr.nlmsg_type = RTM_GETADDR;
143         req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
144         req.hdr.nlmsg_pid = 0;
145         req.hdr.nlmsg_seq = 4711;
146         req.msg.rtgen_family = AF_INET;
147
148         __connman_rtnl_send(&req, sizeof(req));
149
150         return 0;
151 }
152
153 int connman_iface_set_ipv4(struct connman_iface *iface,
154                                                 struct connman_ipv4 *ipv4)
155 {
156         struct ifreq ifr;
157         struct rtentry rt;
158         struct sockaddr_in *addr;
159         int sk, err;
160
161         if ((iface->flags & CONNMAN_IFACE_FLAG_RTNL) == 0)
162                 return -1;
163
164         DBG("iface %p ipv4 %p", iface, ipv4);
165
166         sk = socket(PF_INET, SOCK_DGRAM, 0);
167         if (sk < 0)
168                 return -1;
169
170         memset(&ifr, 0, sizeof(ifr));
171         ifr.ifr_ifindex = iface->index;
172
173         if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
174                 close(sk);
175                 return -1;
176         }
177
178         DBG("ifname %s", ifr.ifr_name);
179
180         addr = (struct sockaddr_in *) &ifr.ifr_addr;
181         addr->sin_family = AF_INET;
182         addr->sin_addr = ipv4->address;
183
184         err = ioctl(sk, SIOCSIFADDR, &ifr);
185
186         if (err < 0)
187                 DBG("address setting failed (%s)", strerror(errno));
188
189         addr = (struct sockaddr_in *) &ifr.ifr_netmask;
190         addr->sin_family = AF_INET;
191         addr->sin_addr = ipv4->netmask;
192
193         err = ioctl(sk, SIOCSIFNETMASK, &ifr);
194
195         if (err < 0)
196                 DBG("netmask setting failed (%s)", strerror(errno));
197
198         addr = (struct sockaddr_in *) &ifr.ifr_broadaddr;
199         addr->sin_family = AF_INET;
200         addr->sin_addr = ipv4->broadcast;
201
202         err = ioctl(sk, SIOCSIFBRDADDR, &ifr);
203
204         if (err < 0)
205                 DBG("broadcast setting failed (%s)", strerror(errno));
206
207         memset(&rt, 0, sizeof(rt));
208         rt.rt_flags = RTF_UP | RTF_GATEWAY;
209
210         addr = (struct sockaddr_in *) &rt.rt_dst;
211         addr->sin_family = AF_INET;
212         addr->sin_addr.s_addr = INADDR_ANY;
213
214         addr = (struct sockaddr_in *) &rt.rt_gateway;
215         addr->sin_family = AF_INET;
216         addr->sin_addr = ipv4->gateway;
217
218         addr = (struct sockaddr_in *) &rt.rt_genmask;
219         addr->sin_family = AF_INET;
220         addr->sin_addr.s_addr = INADDR_ANY;
221
222         err = ioctl(sk, SIOCADDRT, &rt);
223
224         close(sk);
225
226         if (err < 0) {
227                 DBG("default route failed (%s)", strerror(errno));
228                 return -1;
229         }
230
231         return 0;
232 }
233
234 int connman_iface_clear_ipv4(struct connman_iface *iface)
235 {
236         struct ifreq ifr;
237         struct sockaddr_in *addr;
238         int sk, err;
239
240         if ((iface->flags & CONNMAN_IFACE_FLAG_RTNL) == 0)
241                 return -1;
242
243         DBG("iface %p", iface);
244
245         sk = socket(PF_INET, SOCK_DGRAM, 0);
246         if (sk < 0)
247                 return -1;
248
249         memset(&ifr, 0, sizeof(ifr));
250         ifr.ifr_ifindex = iface->index;
251
252         if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
253                 close(sk);
254                 return -1;
255         }
256
257         DBG("ifname %s", ifr.ifr_name);
258
259         addr = (struct sockaddr_in *) &ifr.ifr_addr;
260         addr->sin_family = AF_INET;
261         addr->sin_addr.s_addr = INADDR_ANY;
262
263         //err = ioctl(sk, SIOCDIFADDR, &ifr);
264         err = ioctl(sk, SIOCSIFADDR, &ifr);
265
266         close(sk);
267
268         if (err < 0 && errno != EADDRNOTAVAIL) {
269                 DBG("address removal failed (%s)", strerror(errno));
270                 return -1;
271         }
272
273         return 0;
274 }
275
276 static DBusMessage *enable_iface(DBusConnection *conn,
277                                         DBusMessage *msg, void *data)
278 {
279         struct connman_iface *iface = data;
280         struct connman_iface_driver *driver = iface->driver;
281         DBusMessage *reply;
282
283         DBG("conn %p", conn);
284
285         reply = dbus_message_new_method_return(msg);
286         if (reply == NULL)
287                 return NULL;
288
289         if (driver->activate)
290                 driver->activate(iface);
291
292         dbus_message_append_args(reply, DBUS_TYPE_INVALID);
293
294         return reply;
295 }
296
297 static GDBusMethodTable iface_methods[] = {
298         { "Enable", "", "", enable_iface },
299         { },
300 };
301
302 static dbus_bool_t get_type(DBusConnection *conn,
303                                         DBusMessageIter *iter, void *data)
304 {
305         struct connman_iface *iface = data;
306         const char *type;
307
308         DBG("iface %p", iface);
309
310         switch (iface->type) {
311         case CONNMAN_IFACE_TYPE_80203:
312                 type = "80203";
313                 break;
314         case CONNMAN_IFACE_TYPE_80211:
315                 type = "80211";
316                 break;
317         case CONNMAN_IFACE_TYPE_WIMAX:
318                 type = "wimax";
319                 break;
320         case CONNMAN_IFACE_TYPE_BLUETOOTH:
321                 type = "bluetooth";
322                 break;
323         default:
324                 type = "unknown";
325                 break;
326         }
327
328         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &type);
329
330         return TRUE;
331 }
332
333 static GDBusPropertyTable iface_properties[] = {
334         { "Type", "s", get_type },
335         { },
336 };
337
338 static void device_free(void *data)
339 {
340         struct connman_iface *iface = data;
341
342         DBG("iface %p", iface);
343
344         connman_iface_clear_ipv4(iface);
345
346         if (iface->driver && iface->driver->remove)
347                 iface->driver->remove(iface);
348
349         g_free(iface->path);
350         g_free(iface->udi);
351         g_free(iface->sysfs);
352         g_free(iface);
353 }
354
355 static int probe_device(LibHalContext *ctx,
356                         struct connman_iface_driver *driver, const char *udi)
357 {
358         DBusConnection *conn;
359         struct connman_iface *iface;
360         char *temp, *sysfs;
361         int err;
362
363         DBG("ctx %p driver %p udi %s", ctx, driver, udi);
364
365         if (!driver->probe)
366                 return -1;
367
368         iface = g_try_new0(struct connman_iface, 1);
369         if (iface == NULL)
370                 return -1;
371
372         temp = g_path_get_basename(udi);
373         iface->path = g_strdup_printf("%s/%s", CONNMAN_IFACE_BASEPATH, temp);
374         g_free(temp);
375
376         iface->udi = g_strdup(udi);
377
378         DBG("path %s", iface->path);
379
380         sysfs = libhal_device_get_property_string(ctx, udi,
381                                                 "linux.sysfs_path", NULL);
382         if (sysfs != NULL)
383                 iface->sysfs = g_strdup(sysfs);
384
385         iface->index = -1;
386
387         if (g_str_has_prefix(driver->capability, "net") == TRUE)
388                 iface->index = libhal_device_get_property_int(ctx, udi,
389                                                 "net.linux.ifindex", NULL);
390
391         iface->type = CONNMAN_IFACE_TYPE_UNKNOWN;
392         iface->flags = 0;
393         iface->state = CONNMAN_IFACE_STATE_UNKNOWN;
394
395         DBG("iface %p", iface);
396
397         err = driver->probe(iface);
398         if (err < 0) {
399                 device_free(iface);
400                 return -1;
401         }
402
403         iface->driver = driver;
404
405         conn = libhal_ctx_get_dbus_connection(ctx);
406
407         g_dbus_register_object(conn, iface->path, iface, device_free);
408
409         interfaces = g_slist_append(interfaces, iface);
410
411         if (iface->flags & CONNMAN_IFACE_FLAG_IPV4) {
412                 if (driver->get_ipv4)
413                         driver->get_ipv4(iface, &iface->ipv4);
414                 else
415                         connman_iface_get_ipv4(iface, &iface->ipv4);
416
417                 DBG("address %s", inet_ntoa(iface->ipv4.address));
418         }
419
420         g_dbus_register_interface(conn, iface->path,
421                                         CONNMAN_IFACE_INTERFACE,
422                                         iface_methods, NULL, iface_properties);
423
424         g_dbus_emit_signal(conn, CONNMAN_MANAGER_PATH,
425                                         CONNMAN_MANAGER_INTERFACE,
426                                         "InterfaceAdded",
427                                         DBUS_TYPE_OBJECT_PATH, &iface->path,
428                                         DBUS_TYPE_INVALID);
429
430         return 0;
431 }
432
433 static void device_added(LibHalContext *ctx, const char *udi)
434 {
435         GSList *list;
436
437         DBG("ctx %p udi %s", ctx, udi);
438
439         for (list = drivers; list; list = list->next) {
440                 struct connman_iface_driver *driver = list->data;
441
442                 if (driver->capability == NULL)
443                         continue;
444
445                 if (libhal_device_query_capability(ctx, udi,
446                                         driver->capability, NULL) == TRUE) {
447                         if (probe_device(ctx, driver, udi) == 0)
448                                 break;
449                 }
450         }
451 }
452
453 static void device_removed(LibHalContext *ctx, const char *udi)
454 {
455         DBusConnection *conn;
456         GSList *list;
457
458         DBG("ctx %p udi %s", ctx, udi);
459
460         conn = libhal_ctx_get_dbus_connection(ctx);
461
462         for (list = interfaces; list; list = list->next) {
463                 struct connman_iface *iface = list->data;
464
465                 if (strcmp(udi, iface->udi) == 0) {
466                         g_dbus_emit_signal(conn, CONNMAN_MANAGER_PATH,
467                                         CONNMAN_MANAGER_INTERFACE,
468                                         "InterfaceRemoved",
469                                         DBUS_TYPE_OBJECT_PATH, &iface->path,
470                                         DBUS_TYPE_INVALID);
471                         interfaces = g_slist_remove(interfaces, iface);
472                         g_dbus_unregister_interface(conn, iface->path,
473                                                 CONNMAN_IFACE_INTERFACE);
474                         g_dbus_unregister_object(conn, iface->path);
475                         break;
476                 }
477         }
478 }
479
480 static void probe_driver(LibHalContext *ctx,
481                                 struct connman_iface_driver *driver)
482 {
483         char **list;
484         int num;
485
486         DBG("ctx %p driver %p", ctx, driver);
487
488         list = libhal_find_device_by_capability(ctx,
489                                         driver->capability, &num, NULL);
490         if (list) {
491                 char **tmp = list;
492
493                 while (*tmp) {
494                         probe_device(ctx, driver, *tmp);
495                         tmp++;
496                 }
497
498                 libhal_free_string_array(list);
499         }
500 }
501
502 static void find_devices(LibHalContext *ctx)
503 {
504         GSList *list;
505
506         DBG("ctx %p", ctx);
507
508         for (list = drivers; list; list = list->next) {
509                 struct connman_iface_driver *driver = list->data;
510
511                 DBG("driver %p", driver);
512
513                 if (driver->capability == NULL)
514                         continue;
515
516                 probe_driver(ctx, driver);
517         }
518 }
519
520 static LibHalContext *hal_ctx = NULL;
521
522 static void hal_init(void *data)
523 {
524         DBusConnection *conn = data;
525
526         DBG("conn %p", conn);
527
528         if (hal_ctx != NULL)
529                 return;
530
531         hal_ctx = libhal_ctx_new();
532         if (hal_ctx == NULL)
533                 return;
534
535         if (libhal_ctx_set_dbus_connection(hal_ctx, conn) == FALSE) {
536                 libhal_ctx_free(hal_ctx);
537                 return;
538         }
539
540         if (libhal_ctx_init(hal_ctx, NULL) == FALSE) {
541                 libhal_ctx_free(hal_ctx);
542                 return ;
543         }
544
545         libhal_ctx_set_device_added(hal_ctx, device_added);
546         libhal_ctx_set_device_removed(hal_ctx, device_removed);
547
548         //libhal_ctx_set_device_new_capability(hal_ctx, new_capability);
549         //libhal_ctx_set_device_lost_capability(hal_ctx, lost_capability);
550
551         find_devices(hal_ctx);
552 }
553
554 static void hal_cleanup(void *data)
555 {
556         DBusConnection *conn = data;
557         GSList *list;
558
559         DBG("conn %p", conn);
560
561         if (hal_ctx == NULL)
562                 return;
563
564         for (list = interfaces; list; list = list->next) {
565                 struct connman_iface *iface = list->data;
566
567                 DBG("path %s", iface->path);
568
569                 g_dbus_emit_signal(conn, CONNMAN_MANAGER_PATH,
570                                         CONNMAN_MANAGER_INTERFACE,
571                                         "InterfaceRemoved",
572                                         DBUS_TYPE_OBJECT_PATH, &iface->path,
573                                         DBUS_TYPE_INVALID);
574
575                 g_dbus_unregister_interface(conn, iface->path,
576                                                 CONNMAN_IFACE_INTERFACE);
577
578                 g_dbus_unregister_object(conn, iface->path);
579         }
580
581         g_slist_free(interfaces);
582
583         interfaces = NULL;
584
585         libhal_ctx_shutdown(hal_ctx, NULL);
586
587         libhal_ctx_free(hal_ctx);
588
589         hal_ctx = NULL;
590 }
591
592 static DBusConnection *connection = NULL;
593 static guint hal_watch = 0;
594
595 int __connman_iface_init(DBusConnection *conn)
596 {
597         DBG("conn %p", conn);
598
599         connection = dbus_connection_ref(conn);
600         if (connection == NULL)
601                 return -1;
602
603         hal_init(connection);
604
605         hal_watch = g_dbus_add_watch(connection, "org.freedesktop.Hal",
606                                 hal_init, hal_cleanup, connection, NULL);
607
608         return 0;
609 }
610
611 void __connman_iface_cleanup(void)
612 {
613         DBG("conn %p", connection);
614
615         g_dbus_remove_watch(connection, hal_watch);
616
617         hal_cleanup(connection);
618
619         dbus_connection_unref(connection);
620 }