device: Combine two if statements with identical outcome
[framework/connectivity/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 void flush_nat(void)
61 {
62         int err;
63
64         err = __connman_iptables_command("-t nat -F POSTROUTING");
65         if (err < 0) {
66                 DBG("Flushing the nat table failed");
67
68                 return;
69         }
70
71         __connman_iptables_commit("nat");
72 }
73
74 static int enable_nat(struct connman_nat *nat)
75 {
76         int err;
77
78         g_free(nat->interface);
79         nat->interface = g_strdup(default_interface);
80
81         if (nat->interface == NULL)
82                 return 0;
83
84         /* Enable masquerading */
85         err = __connman_iptables_command("-t nat -A POSTROUTING "
86                                         "-s %s/%d -o %s -j MASQUERADE",
87                                         nat->address,
88                                         nat->prefixlen,
89                                         nat->interface);
90         if (err < 0)
91                 return err;
92
93         return __connman_iptables_commit("nat");
94 }
95
96 static void disable_nat(struct connman_nat *nat)
97 {
98         int err;
99
100         if (nat->interface == NULL)
101                 return;
102
103         /* Disable masquerading */
104         err = __connman_iptables_command("-t nat -D POSTROUTING "
105                                         "-s %s/%d -o %s -j MASQUERADE",
106                                         nat->address,
107                                         nat->prefixlen,
108                                         nat->interface);
109         if (err < 0)
110                 return;
111
112         __connman_iptables_commit("nat");
113 }
114
115 int __connman_nat_enable(const char *name, const char *address,
116                                 unsigned char prefixlen)
117 {
118         struct connman_nat *nat;
119         int err;
120
121         if (g_hash_table_size(nat_hash) == 0) {
122                 err = enable_ip_forward(TRUE);
123                 if (err < 0)
124                         return err;
125         }
126
127         nat = g_try_new0(struct connman_nat, 1);
128         if (nat == NULL) {
129                 if (g_hash_table_size(nat_hash) == 0)
130                         enable_ip_forward(FALSE);
131
132                 return -ENOMEM;
133         }
134
135         nat->address = g_strdup(address);
136         nat->prefixlen = prefixlen;
137
138         g_hash_table_replace(nat_hash, g_strdup(name), nat);
139
140         return enable_nat(nat);
141 }
142
143 void __connman_nat_disable(const char *name)
144 {
145         struct connman_nat *nat;
146
147         nat = g_hash_table_lookup(nat_hash, name);
148         if (nat == NULL)
149                 return;
150
151         disable_nat(nat);
152
153         g_hash_table_remove(nat_hash, name);
154
155         if (g_hash_table_size(nat_hash) == 0)
156                 enable_ip_forward(FALSE);
157 }
158
159 static void update_default_interface(struct connman_service *service)
160 {
161         GHashTableIter iter;
162         gpointer key, value;
163         char *interface;
164         int err;
165
166         interface = connman_service_get_interface(service);
167
168         DBG("interface %s", interface);
169
170         g_free(default_interface);
171         default_interface = interface;
172
173         g_hash_table_iter_init(&iter, nat_hash);
174
175         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
176                 const char *name = key;
177                 struct connman_nat *nat = value;
178
179                 disable_nat(nat);
180                 err = enable_nat(nat);
181                 if (err < 0)
182                         DBG("Failed to enable nat for %s", name);
183         }
184 }
185
186 static void shutdown_nat(gpointer key, gpointer value, gpointer user_data)
187 {
188         const char *name = key;
189
190         __connman_nat_disable(name);
191 }
192
193 static void cleanup_nat(gpointer data)
194 {
195         struct connman_nat *nat = data;
196
197         g_free(nat->address);
198         g_free(nat->interface);
199 }
200
201 static struct connman_notifier nat_notifier = {
202         .name                   = "nat",
203         .default_changed        = update_default_interface,
204 };
205
206 int __connman_nat_init(void)
207 {
208         int err;
209
210         DBG("");
211
212         err = connman_notifier_register(&nat_notifier);
213         if (err < 0)
214                 return err;
215
216         nat_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
217                                                 g_free, cleanup_nat);
218
219         flush_nat();
220
221         return 0;
222 }
223
224 void __connman_nat_cleanup(void)
225 {
226         DBG("");
227
228         g_hash_table_foreach(nat_hash, shutdown_nat, NULL);
229         g_hash_table_destroy(nat_hash);
230         nat_hash = NULL;
231
232         flush_nat();
233
234         connman_notifier_unregister(&nat_notifier);
235 }