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