4d23550457a63a4980c963758d8f302406ff5e38
[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         struct firewall_context *fw;
39
40         char *interface;
41 };
42
43 static int enable_ip_forward(bool enable)
44 {
45         FILE *f;
46
47         f = fopen("/proc/sys/net/ipv4/ip_forward", "r+");
48         if (!f)
49                 return -errno;
50
51         if (enable)
52                 fprintf(f, "1");
53         else
54                 fprintf(f, "0");
55
56         fclose(f);
57
58         return 0;
59 }
60
61 static int enable_nat(struct connman_nat *nat)
62 {
63         char *cmd;
64         int err;
65
66         g_free(nat->interface);
67         nat->interface = g_strdup(default_interface);
68
69         if (!nat->interface)
70                 return 0;
71
72         /* Enable masquerading */
73         cmd = g_strdup_printf("-s %s/%d -o %s -j MASQUERADE",
74                                         nat->address,
75                                         nat->prefixlen,
76                                         nat->interface);
77
78         err = __connman_firewall_add_rule(nat->fw, "nat",
79                                 "POSTROUTING", cmd);
80         g_free(cmd);
81         if (err < 0)
82                 return err;
83
84         return __connman_firewall_enable(nat->fw);
85 }
86
87 static void disable_nat(struct connman_nat *nat)
88 {
89         if (!nat->interface)
90                 return;
91
92         /* Disable masquerading */
93         __connman_firewall_disable(nat->fw);
94 }
95
96 int __connman_nat_enable(const char *name, const char *address,
97                                 unsigned char prefixlen)
98 {
99         struct connman_nat *nat;
100         int err;
101
102         if (g_hash_table_size(nat_hash) == 0) {
103                 err = enable_ip_forward(true);
104                 if (err < 0)
105                         return err;
106         }
107
108         nat = g_try_new0(struct connman_nat, 1);
109         if (!nat)
110                 goto err;
111
112         nat->fw = __connman_firewall_create();
113         if (!nat->fw)
114                 goto err;
115
116         nat->address = g_strdup(address);
117         nat->prefixlen = prefixlen;
118
119         g_hash_table_replace(nat_hash, g_strdup(name), nat);
120
121         return enable_nat(nat);
122
123 err:
124         if (nat) {
125                 if (nat->fw)
126                         __connman_firewall_destroy(nat->fw);
127                 g_free(nat);
128         }
129
130         if (g_hash_table_size(nat_hash) == 0)
131                 enable_ip_forward(false);
132
133         return -ENOMEM;
134 }
135
136 void __connman_nat_disable(const char *name)
137 {
138         struct connman_nat *nat;
139
140         nat = g_hash_table_lookup(nat_hash, name);
141         if (!nat)
142                 return;
143
144         disable_nat(nat);
145
146         g_hash_table_remove(nat_hash, name);
147
148         if (g_hash_table_size(nat_hash) == 0)
149                 enable_ip_forward(false);
150 }
151
152 static void update_default_interface(struct connman_service *service)
153 {
154         GHashTableIter iter;
155         gpointer key, value;
156         char *interface;
157         int err;
158
159         interface = connman_service_get_interface(service);
160
161         DBG("interface %s", interface);
162
163         g_free(default_interface);
164         default_interface = interface;
165
166         g_hash_table_iter_init(&iter, nat_hash);
167
168         while (g_hash_table_iter_next(&iter, &key, &value)) {
169                 const char *name = key;
170                 struct connman_nat *nat = value;
171
172                 disable_nat(nat);
173                 err = enable_nat(nat);
174                 if (err < 0)
175                         DBG("Failed to enable nat for %s", name);
176         }
177 }
178
179 static void shutdown_nat(gpointer key, gpointer value, gpointer user_data)
180 {
181         const char *name = key;
182
183         __connman_nat_disable(name);
184 }
185
186 static void cleanup_nat(gpointer data)
187 {
188         struct connman_nat *nat = data;
189
190         __connman_firewall_destroy(nat->fw);
191         g_free(nat->address);
192         g_free(nat->interface);
193         g_free(nat);
194 }
195
196 static struct connman_notifier nat_notifier = {
197         .name                   = "nat",
198         .default_changed        = update_default_interface,
199 };
200
201 int __connman_nat_init(void)
202 {
203         int err;
204
205         DBG("");
206
207         err = connman_notifier_register(&nat_notifier);
208         if (err < 0)
209                 return err;
210
211         nat_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
212                                                 g_free, cleanup_nat);
213
214         return 0;
215 }
216
217 void __connman_nat_cleanup(void)
218 {
219         DBG("");
220
221         g_hash_table_foreach(nat_hash, shutdown_nat, NULL);
222         g_hash_table_destroy(nat_hash);
223         nat_hash = NULL;
224
225         connman_notifier_unregister(&nat_notifier);
226 }