5447eb71088e5c74824bbddb6c29ed4d2b8fd873
[platform/upstream/connman.git] / src / nat.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
6  *  Copyright (C) 2012  BMW Car IT GmbH. All rights reserved.
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License version 2 as
10  *  published by the Free Software Foundation.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  *
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <errno.h>
28 #include <stdio.h>
29
30 #include "connman.h"
31
32 static char *default_interface;
33 static GHashTable *nat_hash;
34
35 struct connman_nat {
36         char *address;
37         unsigned char prefixlen;
38
39         char *interface;
40 };
41
42 static int enable_ip_forward(connman_bool_t enable)
43 {
44         FILE *f;
45
46         f = fopen("/proc/sys/net/ipv4/ip_forward", "r+");
47         if (f == NULL)
48                 return -errno;
49
50         if (enable == TRUE)
51                 fprintf(f, "1");
52         else
53                 fprintf(f, "0");
54
55         fclose(f);
56
57         return 0;
58 }
59
60 static int enable_nat(struct connman_nat *nat)
61 {
62         char *cmd;
63         int err;
64
65         g_free(nat->interface);
66         nat->interface = g_strdup(default_interface);
67
68         if (nat->interface == NULL)
69                 return 0;
70
71         /* Enable masquerading */
72         cmd = g_strdup_printf("-s %s/%d -o %s -j MASQUERADE",
73                                         nat->address,
74                                         nat->prefixlen,
75                                         nat->interface);
76         err = __connman_iptables_append("nat", "POSTROUTING", cmd);
77         g_free(cmd);
78         if (err < 0)
79                 return err;
80
81         return __connman_iptables_commit("nat");
82 }
83
84 static void disable_nat(struct connman_nat *nat)
85 {
86         char *cmd;
87         int err;
88
89         if (nat->interface == NULL)
90                 return;
91
92         /* Disable masquerading */
93         cmd = g_strdup_printf("-s %s/%d -o %s -j MASQUERADE",
94                                         nat->address,
95                                         nat->prefixlen,
96                                         nat->interface);
97         err = __connman_iptables_delete("nat", "POSTROUTING", cmd);
98         g_free(cmd);
99         if (err < 0)
100                 return;
101
102         __connman_iptables_commit("nat");
103 }
104
105 int __connman_nat_enable(const char *name, const char *address,
106                                 unsigned char prefixlen)
107 {
108         struct connman_nat *nat;
109         int err;
110
111         if (g_hash_table_size(nat_hash) == 0) {
112                 err = enable_ip_forward(TRUE);
113                 if (err < 0)
114                         return err;
115         }
116
117         nat = g_try_new0(struct connman_nat, 1);
118         if (nat == NULL) {
119                 if (g_hash_table_size(nat_hash) == 0)
120                         enable_ip_forward(FALSE);
121
122                 return -ENOMEM;
123         }
124
125         nat->address = g_strdup(address);
126         nat->prefixlen = prefixlen;
127
128         g_hash_table_replace(nat_hash, g_strdup(name), nat);
129
130         return enable_nat(nat);
131 }
132
133 void __connman_nat_disable(const char *name)
134 {
135         struct connman_nat *nat;
136
137         nat = g_hash_table_lookup(nat_hash, name);
138         if (nat == NULL)
139                 return;
140
141         disable_nat(nat);
142
143         g_hash_table_remove(nat_hash, name);
144
145         if (g_hash_table_size(nat_hash) == 0)
146                 enable_ip_forward(FALSE);
147 }
148
149 static void update_default_interface(struct connman_service *service)
150 {
151         GHashTableIter iter;
152         gpointer key, value;
153         char *interface;
154         int err;
155
156         interface = connman_service_get_interface(service);
157
158         DBG("interface %s", interface);
159
160         g_free(default_interface);
161         default_interface = interface;
162
163         g_hash_table_iter_init(&iter, nat_hash);
164
165         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
166                 const char *name = key;
167                 struct connman_nat *nat = value;
168
169                 disable_nat(nat);
170                 err = enable_nat(nat);
171                 if (err < 0)
172                         DBG("Failed to enable nat for %s", name);
173         }
174 }
175
176 static void shutdown_nat(gpointer key, gpointer value, gpointer user_data)
177 {
178         const char *name = key;
179
180         __connman_nat_disable(name);
181 }
182
183 static void cleanup_nat(gpointer data)
184 {
185         struct connman_nat *nat = data;
186
187         g_free(nat->address);
188         g_free(nat->interface);
189         g_free(nat);
190 }
191
192 static struct connman_notifier nat_notifier = {
193         .name                   = "nat",
194         .default_changed        = update_default_interface,
195 };
196
197 int __connman_nat_init(void)
198 {
199         int err;
200
201         DBG("");
202
203         err = connman_notifier_register(&nat_notifier);
204         if (err < 0)
205                 return err;
206
207         nat_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
208                                                 g_free, cleanup_nat);
209
210         return 0;
211 }
212
213 void __connman_nat_cleanup(void)
214 {
215         DBG("");
216
217         g_hash_table_foreach(nat_hash, shutdown_nat, NULL);
218         g_hash_table_destroy(nat_hash);
219         nat_hash = NULL;
220
221         connman_notifier_unregister(&nat_notifier);
222 }