9ede7dbb606b6bbd0fbd913b70a1e619be1dcd55
[connman] / src / detect.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 <stdio.h>
27 #include <errno.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/stat.h>
32 #include <sys/ioctl.h>
33 #include <sys/socket.h>
34 #include <net/ethernet.h>
35 #include <linux/if_arp.h>
36 #include <linux/wireless.h>
37
38 #include <glib.h>
39
40 #include <connman/device.h>
41 #include <connman/rtnl.h>
42 #include <connman/log.h>
43
44 #include "connman.h"
45
46 static GSList *device_list = NULL;
47
48 static struct connman_device *find_device(int index)
49 {
50         GSList *list;
51
52         for (list = device_list; list; list = list->next) {
53                 struct connman_device *device = list->data;
54
55                 if (connman_device_get_index(device) == index)
56                         return device;
57         }
58
59         return NULL;
60 }
61
62 static char *index2name(int index)
63 {
64         struct ifreq ifr;
65         int sk, err;
66
67         if (index < 0)
68                 return NULL;
69
70         sk = socket(PF_INET, SOCK_DGRAM, 0);
71         if (sk < 0)
72                 return NULL;
73
74         memset(&ifr, 0, sizeof(ifr));
75         ifr.ifr_ifindex = index;
76
77         err = ioctl(sk, SIOCGIFNAME, &ifr);
78
79         close(sk);
80
81         if (err < 0)
82                 return NULL;
83
84         return strdup(ifr.ifr_name);
85 }
86
87 static char *index2ident(int index, const char *prefix)
88 {
89         struct ifreq ifr;
90         struct ether_addr *eth;
91         char *str;
92         int sk, err, len;
93
94         if (index < 0)
95                 return NULL;
96
97         sk = socket(PF_INET, SOCK_DGRAM, 0);
98         if (sk < 0)
99                 return NULL;
100
101         memset(&ifr, 0, sizeof(ifr));
102         ifr.ifr_ifindex = index;
103
104         err = ioctl(sk, SIOCGIFNAME, &ifr);
105
106         if (err == 0)
107                 err = ioctl(sk, SIOCGIFHWADDR, &ifr);
108
109         close(sk);
110
111         if (err < 0)
112                 return NULL;
113
114         len = prefix ? strlen(prefix) + 18 : 18;
115
116         str = malloc(len);
117         if (!str)
118                 return NULL;
119
120         eth = (void *) &ifr.ifr_hwaddr.sa_data;
121         snprintf(str, len, "%s%02X_%02X_%02X_%02X_%02X_%02X",
122                                                 prefix ? prefix : "",
123                                                 eth->ether_addr_octet[0],
124                                                 eth->ether_addr_octet[1],
125                                                 eth->ether_addr_octet[2],
126                                                 eth->ether_addr_octet[3],
127                                                 eth->ether_addr_octet[4],
128                                                 eth->ether_addr_octet[5]);
129
130         return str;
131 }
132
133 static void detect_newlink(unsigned short type, int index,
134                                         unsigned flags, unsigned change)
135 {
136         enum connman_device_type devtype = CONNMAN_DEVICE_TYPE_UNKNOWN;
137         struct connman_device *device;
138         gchar *name, *devname;
139
140         DBG("type %d index %d", type, index);
141
142         device = find_device(index);
143         if (device != NULL)
144                 return;
145
146         devname = index2name(index);
147
148         if (type == ARPHRD_ETHER) {
149                 char bridge_path[PATH_MAX], wimax_path[PATH_MAX];
150                 struct stat st;
151                 struct iwreq iwr;
152                 int sk;
153
154                 snprintf(bridge_path, PATH_MAX,
155                                         "/sys/class/net/%s/bridge", devname);
156                 snprintf(wimax_path, PATH_MAX,
157                                         "/sys/class/net/%s/wimax", devname);
158
159                 memset(&iwr, 0, sizeof(iwr));
160                 strncpy(iwr.ifr_ifrn.ifrn_name, devname, IFNAMSIZ);
161
162                 sk = socket(PF_INET, SOCK_DGRAM, 0);
163
164                 if (g_str_has_prefix(devname, "bnep") == TRUE)
165                         devtype = CONNMAN_DEVICE_TYPE_UNKNOWN;
166                 else if (stat(bridge_path, &st) == 0 && (st.st_mode & S_IFDIR))
167                         devtype = CONNMAN_DEVICE_TYPE_UNKNOWN;
168                 else if (stat(wimax_path, &st) == 0 && (st.st_mode & S_IFDIR))
169                         devtype = CONNMAN_DEVICE_TYPE_WIMAX;
170                 else if (ioctl(sk, SIOCGIWNAME, &iwr) == 0)
171                         devtype = CONNMAN_DEVICE_TYPE_UNKNOWN;
172                 else
173                         devtype = CONNMAN_DEVICE_TYPE_ETHERNET;
174
175                 close(sk);
176         } else if (type == ARPHRD_NONE) {
177                 if (g_str_has_prefix(devname, "hso") == TRUE)
178                         devtype = CONNMAN_DEVICE_TYPE_HSO;
179         }
180
181         if (devtype == CONNMAN_DEVICE_TYPE_UNKNOWN) {
182                 g_free(devname);
183                 return;
184         }
185
186         switch (devtype) {
187         case CONNMAN_DEVICE_TYPE_HSO:
188                 name = strdup(devname);
189                 break;
190         default:
191                 name = index2ident(index, "dev_");
192                 break;
193         }
194
195         device = connman_device_create(name, devtype);
196         if (device == NULL) {
197                 g_free(devname);
198                 g_free(name);
199                 return;
200         }
201
202         switch (devtype) {
203         case CONNMAN_DEVICE_TYPE_HSO:
204                 connman_device_set_policy(device, CONNMAN_DEVICE_POLICY_MANUAL);
205                 connman_device_set_mode(device,
206                                         CONNMAN_DEVICE_MODE_SINGLE_NETWORK);
207                 break;
208         default:
209                 break;
210         }
211
212         connman_device_set_index(device, index);
213         connman_device_set_interface(device, devname);
214
215         g_free(devname);
216         g_free(name);
217
218         if (connman_device_register(device) < 0) {
219                 connman_device_unref(device);
220                 return;
221         }
222
223         device_list = g_slist_append(device_list, device);
224 }
225
226 static void detect_dellink(unsigned short type, int index,
227                                         unsigned flags, unsigned change)
228 {
229         struct connman_device *device;
230
231         DBG("type %d index %d", type, index);
232
233         device = find_device(index);
234         if (device == NULL)
235                 return;
236
237         device_list = g_slist_remove(device_list, device);
238
239         connman_device_unregister(device);
240         connman_device_unref(device);
241 }
242
243 static struct connman_rtnl detect_rtnl = {
244         .name           = "detect",
245         .priority       = CONNMAN_RTNL_PRIORITY_LOW,
246         .newlink        = detect_newlink,
247         .dellink        = detect_dellink,
248 };
249
250 int __connman_detect_init(void)
251 {
252         int err;
253
254         err = connman_rtnl_register(&detect_rtnl);
255         if (err < 0)
256                 return err;
257
258         connman_rtnl_send_getlink();
259
260         return 0;
261 }
262
263 void __connman_detect_cleanup(void)
264 {
265         GSList *list;
266
267         connman_rtnl_unregister(&detect_rtnl);
268
269         for (list = device_list; list; list = list->next) {
270                 struct connman_device *device = list->data;
271
272                 connman_device_unregister(device);
273                 connman_device_unref(device);
274         }
275
276         g_slist_free(device_list);
277         device_list = NULL;
278 }