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