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