tethering: Set tethering_status earlier
[platform/upstream/connman.git] / src / tethering.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
26 #include <unistd.h>
27 #include <stdio.h>
28 #include <sys/ioctl.h>
29 #include <net/if.h>
30 #include <linux/sockios.h>
31
32 #include "connman.h"
33
34 #include <gdhcp/gdhcp.h>
35
36 #define BRIDGE_NAME "tether"
37 #define BRIDGE_IP "192.168.218.1"
38 #define BRIDGE_BCAST "192.168.218.255"
39 #define BRIDGE_SUBNET "255.255.255.0"
40 #define BRIDGE_IP_START "192.168.218.100"
41 #define BRIDGE_IP_END "192.168.218.200"
42 #define BRIDGE_DNS "8.8.8.8"
43
44 static connman_bool_t tethering_status = FALSE;
45 static char *default_interface = NULL;
46 static volatile gint tethering_enabled;
47 static GDHCPServer *tethering_dhcp_server = NULL;
48
49 connman_bool_t __connman_tethering_get_status(void)
50 {
51         return tethering_status;
52 }
53
54
55 static void dhcp_server_debug(const char *str, void *data)
56 {
57         connman_info("%s: %s\n", (const char *) data, str);
58 }
59
60 static void dhcp_server_error(GDHCPServerError error)
61 {
62         switch (error) {
63         case G_DHCP_SERVER_ERROR_NONE:
64                 connman_error("OK");
65                 break;
66         case G_DHCP_SERVER_ERROR_INTERFACE_UNAVAILABLE:
67                 connman_error("Interface unavailable");
68                 break;
69         case G_DHCP_SERVER_ERROR_INTERFACE_IN_USE:
70                 connman_error("Interface in use");
71                 break;
72         case G_DHCP_SERVER_ERROR_INTERFACE_DOWN:
73                 connman_error("Interface down");
74                 break;
75         case G_DHCP_SERVER_ERROR_NOMEM:
76                 connman_error("No memory");
77                 break;
78         case G_DHCP_SERVER_ERROR_INVALID_INDEX:
79                 connman_error("Invalid index");
80                 break;
81         case G_DHCP_SERVER_ERROR_INVALID_OPTION:
82                 connman_error("Invalid option");
83                 break;
84         case G_DHCP_SERVER_ERROR_IP_ADDRESS_INVALID:
85                 connman_error("Invalid address");
86                 break;
87         }
88 }
89
90 static GDHCPServer *dhcp_server_start(const char *bridge,
91                                 const char *router, const char* subnet,
92                                 const char *start_ip, const char *end_ip,
93                                 unsigned int lease_time, const char *dns)
94 {
95         GDHCPServerError error;
96         GDHCPServer *dhcp_server;
97         int index;
98
99         DBG("");
100
101         index = connman_inet_ifindex(bridge);
102         if (index < 0)
103                 return NULL;
104
105         dhcp_server = g_dhcp_server_new(G_DHCP_IPV4, index, &error);
106         if (dhcp_server == NULL) {
107                 dhcp_server_error(error);
108                 return NULL;
109         }
110
111         g_dhcp_server_set_debug(dhcp_server, dhcp_server_debug, "DHCP server");
112
113         g_dhcp_server_set_lease_time(dhcp_server, lease_time);
114         g_dhcp_server_set_option(dhcp_server, G_DHCP_SUBNET, subnet);
115         g_dhcp_server_set_option(dhcp_server, G_DHCP_ROUTER, router);
116         g_dhcp_server_set_option(dhcp_server, G_DHCP_DNS_SERVER, dns);
117         g_dhcp_server_set_ip_range(dhcp_server, start_ip, end_ip);
118
119         g_dhcp_server_start(dhcp_server);
120
121         return dhcp_server;
122 }
123
124 static void dhcp_server_stop(GDHCPServer *server)
125 {
126         if (server == NULL)
127                 return;
128
129         g_dhcp_server_unref(server);
130 }
131
132 static int set_forward_delay(const char *name, unsigned int delay)
133 {
134         FILE *f;
135         char *forward_delay_path;
136
137         forward_delay_path =
138                 g_strdup_printf("/sys/class/net/%s/bridge/forward_delay", name);
139
140         if (forward_delay_path == NULL)
141                 return -ENOMEM;
142
143         f = fopen(forward_delay_path, "r+");
144
145         g_free(forward_delay_path);
146
147         if (f == NULL)
148                 return -errno;
149
150         fprintf(f, "%d", delay);
151
152         fclose(f);
153
154         return 0;
155 }
156
157 static int create_bridge(const char *name)
158 {
159         int sk, err;
160
161         DBG("name %s", name);
162
163         sk = socket(AF_INET, SOCK_STREAM, 0);
164         if (sk < 0)
165                 return -EOPNOTSUPP;
166
167         err = ioctl(sk, SIOCBRADDBR, name);
168
169         if (err < 0)
170                 return -EOPNOTSUPP;
171
172         err = set_forward_delay(name, 0);
173
174         if (err < 0)
175                 ioctl(sk, SIOCBRDELBR, name);
176
177         close(sk);
178
179         return err;
180 }
181
182 static int remove_bridge(const char *name)
183 {
184         int sk, err;
185
186         DBG("name %s", name);
187
188         sk = socket(AF_INET, SOCK_STREAM, 0);
189         if (sk < 0)
190                 return -EOPNOTSUPP;
191
192         err = ioctl(sk, SIOCBRDELBR, name);
193
194         close(sk);
195
196         if (err < 0)
197                 return -EOPNOTSUPP;
198
199         return 0;
200 }
201
202 static int enable_bridge(const char *name)
203 {
204         int err, index;
205
206         index = connman_inet_ifindex(name);
207         if (index < 0)
208                 return index;
209
210         err = __connman_inet_modify_address(RTM_NEWADDR,
211                         NLM_F_REPLACE | NLM_F_ACK, index, AF_INET,
212                                             BRIDGE_IP, NULL, 24, BRIDGE_BCAST);
213         if (err < 0)
214                 return err;
215
216         return connman_inet_ifup(index);
217 }
218
219 static int disable_bridge(const char *name)
220 {
221         int index;
222
223         index = connman_inet_ifindex(name);
224         if (index < 0)
225                 return index;
226
227         return connman_inet_ifdown(index);
228 }
229
230 static int enable_ip_forward(connman_bool_t enable)
231 {
232
233         FILE *f;
234
235         f = fopen("/proc/sys/net/ipv4/ip_forward", "r+");
236         if (f == NULL)
237                 return -errno;
238
239         if (enable == TRUE)
240                 fprintf(f, "1");
241         else
242                 fprintf(f, "0");
243
244         fclose(f);
245
246         return 0;
247 }
248
249 static int enable_nat(const char *interface)
250 {
251         int err;
252
253         if (interface == NULL)
254                 return 0;
255
256         /* Enable IPv4 forwarding */
257         err = enable_ip_forward(TRUE);
258         if (err < 0)
259                 return err;
260
261         /* POSTROUTING flush */
262         err = __connman_iptables_command("-t nat -F POSTROUTING");
263         if (err < 0)
264                 return err;
265
266         /* Enable masquerading */
267         err = __connman_iptables_command("-t nat -A POSTROUTING "
268                                         "-o %s -j MASQUERADE", interface);
269         if (err < 0)
270                 return err;
271
272         return __connman_iptables_commit("nat");
273 }
274
275 static void disable_nat(const char *interface)
276 {
277         int err;
278
279         /* Disable IPv4 forwarding */
280         enable_ip_forward(FALSE);
281
282         /* POSTROUTING flush */
283         err = __connman_iptables_command("-t nat -F POSTROUTING");
284         if (err < 0)
285                 return;
286
287         __connman_iptables_commit("nat");
288 }
289
290 void __connman_tethering_set_enabled(void)
291 {
292         int err;
293
294         if (tethering_status == FALSE)
295                 return;
296
297         DBG("enabled %d", tethering_enabled + 1);
298
299         if (g_atomic_int_exchange_and_add(&tethering_enabled, 1) == 0) {
300                 err = create_bridge(BRIDGE_NAME);
301                 if (err < 0)
302                         return;
303
304                 err = enable_bridge(BRIDGE_NAME);
305                 if (err < 0) {
306                         remove_bridge(BRIDGE_NAME);
307                         return;
308                 }
309
310                 tethering_dhcp_server =
311                         dhcp_server_start(BRIDGE_NAME,
312                                                 BRIDGE_IP, BRIDGE_SUBNET,
313                                                 BRIDGE_IP_START, BRIDGE_IP_END,
314                                                         24 * 3600, BRIDGE_DNS);
315                 if (tethering_dhcp_server == NULL) {
316                         disable_bridge(BRIDGE_NAME);
317                         remove_bridge(BRIDGE_NAME);
318                         return;
319                 }
320
321                 enable_nat(default_interface);
322
323                 DBG("tethering started");
324         }
325 }
326
327 void __connman_tethering_set_disabled(void)
328 {
329         if (tethering_status == TRUE)
330                 return;
331
332         DBG("enabled %d", tethering_enabled - 1);
333
334         if (g_atomic_int_dec_and_test(&tethering_enabled) == TRUE) {
335                 disable_nat(default_interface);
336
337                 dhcp_server_stop(tethering_dhcp_server);
338
339                 disable_bridge(BRIDGE_NAME);
340
341                 remove_bridge(BRIDGE_NAME);
342
343                 DBG("tethering stopped");
344         }
345 }
346
347 int __connman_tethering_set_status(connman_bool_t status)
348 {
349         if (status == tethering_status)
350                 return -EALREADY;
351
352         tethering_status = status;
353
354         if (status == TRUE)
355                 __connman_technology_enable_tethering(BRIDGE_NAME);
356         else
357                 __connman_technology_disable_tethering(BRIDGE_NAME);
358
359         return 0;
360 }
361
362 void __connman_tethering_update_interface(const char *interface)
363 {
364         DBG("interface %s", interface);
365
366         g_free(default_interface);
367
368         if (interface == NULL) {
369                 disable_nat(interface);
370                 default_interface = NULL;
371
372                 return;
373         }
374
375         default_interface = g_strdup(interface);
376
377         if (tethering_status == FALSE ||
378                         !g_atomic_int_get(&tethering_enabled))
379                 return;
380
381         enable_nat(interface);
382 }
383
384 int __connman_tethering_init(void)
385 {
386         DBG("");
387
388         tethering_enabled = 0;
389
390         return 0;
391 }
392
393 void __connman_tethering_cleanup(void)
394 {
395         DBG("");
396
397         if (tethering_status == TRUE) {
398                 if (tethering_dhcp_server)
399                         dhcp_server_stop(tethering_dhcp_server);
400                 disable_bridge(BRIDGE_NAME);
401                 remove_bridge(BRIDGE_NAME);
402         }
403 }