tethering: Enable NAT
[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 <connman/tethering.h>
35
36 #define BRIDGE_NAME "tether"
37
38 static connman_bool_t tethering_status = FALSE;
39 static const char *default_interface = NULL;
40 gint tethering_enabled;
41
42 connman_bool_t __connman_tethering_get_status(void)
43 {
44         return tethering_status;
45 }
46
47 static int create_bridge(const char *name)
48 {
49         int sk, err;
50
51         DBG("name %s", name);
52
53         sk = socket(AF_INET, SOCK_STREAM, 0);
54         if (sk < 0)
55                 return -EOPNOTSUPP;
56
57         err = ioctl(sk, SIOCBRADDBR, name);
58
59         close(sk);
60
61         if (err < 0)
62                 return -EOPNOTSUPP;
63
64         return 0;
65 }
66
67 static int remove_bridge(const char *name)
68 {
69         int sk, err;
70
71         DBG("name %s", name);
72
73         sk = socket(AF_INET, SOCK_STREAM, 0);
74         if (sk < 0)
75                 return -EOPNOTSUPP;
76
77         err = ioctl(sk, SIOCBRDELBR, name);
78
79         close(sk);
80
81         if (err < 0)
82                 return -EOPNOTSUPP;
83
84         return 0;
85 }
86
87 static int enable_ip_forward(connman_bool_t enable)
88 {
89
90         FILE *f;
91         int ip_forward = enable ? 1 : 0;
92
93         f = fopen("/proc/sys/net/ipv4/ip_forward", "r+");
94
95         fprintf(f, "%d", ip_forward);
96
97         fclose(f);
98
99         return 0;
100 }
101
102 static int enable_nat(const char *interface)
103 {
104         int ret;
105
106         if (interface == NULL)
107                 return 0;
108
109         /* Enable IPv4 forwarding */
110         ret = enable_ip_forward(TRUE);
111         if (ret < 0)
112                 return ret;
113
114         /* TODO: Flush nat POSTROUTING chain */
115         /* Enable masquerading */
116         ret = __connman_iptables_command("-t nat -A POSTROUTING -o %s -j MASQUERADE", interface);
117         if (ret < 0)
118                 return ret;
119
120         return __connman_iptables_commit("nat");
121 }
122
123 static void disable_nat(const char *interface)
124 {
125         /* Disable IPv4 forwarding */
126         enable_ip_forward(FALSE);
127
128         /* TODO: Flush nat POSTROUTING chain */
129 }
130
131 void connman_tethering_enabled(void)
132 {
133         if (tethering_status == FALSE)
134                 return;
135
136         DBG("enabled %d", tethering_enabled + 1);
137
138         if (g_atomic_int_exchange_and_add(&tethering_enabled, 1) == 0) {
139                 /* TODO Start DHCP server and DNS proxy on the bridge */
140
141                 enable_nat(default_interface);
142                 DBG("tethering started");
143         }
144 }
145
146 void connman_tethering_disabled(void)
147 {
148         if (tethering_status == FALSE)
149                 return;
150
151         DBG("enabled %d", tethering_enabled - 1);
152
153         if (g_atomic_int_dec_and_test(&tethering_enabled) == 0) {
154                 /* TODO Stop DHCP server and DNS proxy on the bridge */
155
156                 disable_nat(default_interface);
157                 DBG("tethering stopped");
158         }
159 }
160
161 int __connman_tethering_set_status(connman_bool_t status)
162 {
163         if (status == tethering_status)
164                 return -EALREADY;
165
166         if (status == TRUE) {
167                 create_bridge(BRIDGE_NAME);
168                 __connman_technology_enable_tethering(BRIDGE_NAME);
169         } else {
170                 __connman_technology_disable_tethering(BRIDGE_NAME);
171                 remove_bridge(BRIDGE_NAME);
172         }
173
174         tethering_status = status;
175
176         return 0;
177 }
178
179 void __connman_tethering_update_interface(const char *interface)
180 {
181         DBG("interface %s", interface);
182
183         default_interface = interface;
184
185         if (interface == NULL) {
186                 disable_nat(interface);
187
188                 return;
189         }
190
191         if (tethering_status == FALSE ||
192                         !g_atomic_int_get(&tethering_enabled))
193                 return;
194
195         enable_nat(interface);
196 }
197
198 int __connman_tethering_init(void)
199 {
200         DBG("");
201
202         tethering_enabled = 0;
203
204         return 0;
205 }
206
207 void __connman_tethering_cleanup(void)
208 {
209         DBG("");
210
211         if (tethering_status == TRUE)
212                 remove_bridge(BRIDGE_NAME);
213 }