c096fa37b92da38ac62f5f71a833ac3a24f41a5e
[platform/upstream/connman.git] / vpn / vpn-ipconfig.c
1 /*
2  *
3  *  ConnMan VPN daemon
4  *
5  *  Copyright (C) 2012  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 <errno.h>
27 #include <stdio.h>
28 #include <net/if.h>
29 #include <net/if_arp.h>
30 #include <linux/if_link.h>
31 #include <string.h>
32 #include <stdlib.h>
33 #include <stdint.h>
34 #include <arpa/inet.h>
35
36 #ifndef IFF_LOWER_UP
37 #define IFF_LOWER_UP    0x10000
38 #endif
39
40 #include <gdbus.h>
41
42 #include "../src/connman.h"
43
44 #include "vpn.h"
45
46 struct vpn_ipconfig {
47         int refcount;
48         int index;
49         int family;
50         bool enabled;
51         struct connman_ipaddress *address;
52         struct connman_ipaddress *system;
53 };
54
55 struct vpn_ipdevice {
56         int index;
57         char *ifname;
58         unsigned short type;
59         unsigned int flags;
60         char *address;
61         uint16_t mtu;
62
63         GSList *address_list;
64         char *ipv4_gateway;
65         char *ipv6_gateway;
66
67         char *pac;
68 };
69
70 static GHashTable *ipdevice_hash = NULL;
71
72 struct connman_ipaddress *
73 __vpn_ipconfig_get_address(struct vpn_ipconfig *ipconfig)
74 {
75         if (!ipconfig)
76                 return NULL;
77
78         return ipconfig->address;
79 }
80
81 const char *__vpn_ipconfig_get_peer(struct vpn_ipconfig *ipconfig)
82 {
83         if (!ipconfig->address)
84                 return NULL;
85
86         return ipconfig->address->peer;
87 }
88
89 unsigned short __vpn_ipconfig_get_type_from_index(int index)
90 {
91         struct vpn_ipdevice *ipdevice;
92
93         ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(index));
94         if (!ipdevice)
95                 return ARPHRD_VOID;
96
97         return ipdevice->type;
98 }
99
100 unsigned int __vpn_ipconfig_get_flags_from_index(int index)
101 {
102         struct vpn_ipdevice *ipdevice;
103
104         ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(index));
105         if (!ipdevice)
106                 return 0;
107
108         return ipdevice->flags;
109 }
110
111 void __vpn_ipconfig_foreach(void (*function) (int index,
112                                         void *user_data), void *user_data)
113 {
114         GList *list, *keys;
115
116         keys = g_hash_table_get_keys(ipdevice_hash);
117         if (!keys)
118                 return;
119
120         for (list = g_list_first(keys); list; list = g_list_next(list)) {
121                 int index = GPOINTER_TO_INT(list->data);
122
123                 function(index, user_data);
124         }
125
126         g_list_free(keys);
127 }
128
129 void __vpn_ipconfig_set_local(struct vpn_ipconfig *ipconfig,
130                                                         const char *address)
131 {
132         if (!ipconfig->address)
133                 return;
134
135         g_free(ipconfig->address->local);
136         ipconfig->address->local = g_strdup(address);
137 }
138
139 const char *__vpn_ipconfig_get_local(struct vpn_ipconfig *ipconfig)
140 {
141         if (!ipconfig->address)
142                 return NULL;
143
144         return ipconfig->address->local;
145 }
146
147 void __vpn_ipconfig_set_peer(struct vpn_ipconfig *ipconfig,
148                                                         const char *address)
149 {
150         if (!ipconfig->address)
151                 return;
152
153         g_free(ipconfig->address->peer);
154         ipconfig->address->peer = g_strdup(address);
155 }
156
157 void __vpn_ipconfig_set_broadcast(struct vpn_ipconfig *ipconfig,
158                                         const char *broadcast)
159 {
160         if (!ipconfig->address)
161                 return;
162
163         g_free(ipconfig->address->broadcast);
164         ipconfig->address->broadcast = g_strdup(broadcast);
165 }
166
167 void __vpn_ipconfig_set_gateway(struct vpn_ipconfig *ipconfig,
168                                                         const char *gateway)
169 {
170         DBG("");
171
172         if (!ipconfig->address)
173                 return;
174         g_free(ipconfig->address->gateway);
175         ipconfig->address->gateway = g_strdup(gateway);
176 }
177
178 const char *
179 __vpn_ipconfig_get_gateway(struct vpn_ipconfig *ipconfig)
180 {
181         if (!ipconfig->address)
182                 return NULL;
183
184         return ipconfig->address->gateway;
185 }
186
187 void __vpn_ipconfig_set_prefixlen(struct vpn_ipconfig *ipconfig,
188                                         unsigned char prefixlen)
189 {
190         if (!ipconfig->address)
191                 return;
192
193         ipconfig->address->prefixlen = prefixlen;
194 }
195
196 unsigned char
197 __vpn_ipconfig_get_prefixlen(struct vpn_ipconfig *ipconfig)
198 {
199         if (!ipconfig->address)
200                 return 0;
201
202         return ipconfig->address->prefixlen;
203 }
204
205 int __vpn_ipconfig_address_add(struct vpn_ipconfig *ipconfig, int family)
206 {
207         DBG("ipconfig %p family %d", ipconfig, family);
208
209         if (!ipconfig)
210                 return -EINVAL;
211
212         if (family == AF_INET)
213                 return connman_inet_set_address(ipconfig->index,
214                                                 ipconfig->address);
215         else if (family == AF_INET6)
216                 return connman_inet_set_ipv6_address(ipconfig->index,
217                                                         ipconfig->address);
218
219         return 0;
220 }
221
222 int __vpn_ipconfig_gateway_add(struct vpn_ipconfig *ipconfig, int family)
223 {
224         DBG("ipconfig %p family %d", ipconfig, family);
225
226         if (!ipconfig || !ipconfig->address)
227                 return -EINVAL;
228
229         DBG("family %d gw %s peer %s", family,
230                 ipconfig->address->gateway, ipconfig->address->peer);
231
232         if (family == AF_INET)
233                 connman_inet_add_host_route(ipconfig->index,
234                                         ipconfig->address->gateway,
235                                         ipconfig->address->peer);
236         else if (family == AF_INET6)
237                 connman_inet_add_ipv6_host_route(ipconfig->index,
238                                                 ipconfig->address->gateway,
239                                                 ipconfig->address->peer);
240         else
241                 return -EINVAL;
242
243         return 0;
244 }
245
246 void __vpn_ipconfig_unref_debug(struct vpn_ipconfig *ipconfig,
247                                 const char *file, int line, const char *caller)
248 {
249         if (!ipconfig)
250                 return;
251
252         DBG("%p ref %d by %s:%d:%s()", ipconfig, ipconfig->refcount - 1,
253                 file, line, caller);
254
255         if (__sync_fetch_and_sub(&ipconfig->refcount, 1) != 1)
256                 return;
257
258         connman_ipaddress_free(ipconfig->system);
259         connman_ipaddress_free(ipconfig->address);
260         g_free(ipconfig);
261 }
262
263 static struct vpn_ipconfig *create_ipv6config(int index)
264 {
265         struct vpn_ipconfig *ipv6config;
266
267         DBG("index %d", index);
268
269         ipv6config = g_try_new0(struct vpn_ipconfig, 1);
270         if (!ipv6config)
271                 return NULL;
272
273         ipv6config->refcount = 1;
274
275         ipv6config->index = index;
276         ipv6config->enabled = false;
277         ipv6config->family = AF_INET6;
278
279         ipv6config->address = connman_ipaddress_alloc(AF_INET6);
280         if (!ipv6config->address) {
281                 g_free(ipv6config);
282                 return NULL;
283         }
284
285         ipv6config->system = connman_ipaddress_alloc(AF_INET6);
286
287         DBG("ipconfig %p", ipv6config);
288
289         return ipv6config;
290 }
291
292 struct vpn_ipconfig *__vpn_ipconfig_create(int index, int family)
293 {
294         struct vpn_ipconfig *ipconfig;
295
296         if (family == AF_INET6)
297                 return create_ipv6config(index);
298
299         DBG("index %d", index);
300
301         ipconfig = g_try_new0(struct vpn_ipconfig, 1);
302         if (!ipconfig)
303                 return NULL;
304
305         ipconfig->refcount = 1;
306
307         ipconfig->index = index;
308         ipconfig->enabled = false;
309         ipconfig->family = family;
310
311         ipconfig->address = connman_ipaddress_alloc(AF_INET);
312         if (!ipconfig->address) {
313                 g_free(ipconfig);
314                 return NULL;
315         }
316
317         ipconfig->system = connman_ipaddress_alloc(AF_INET);
318
319         DBG("ipconfig %p", ipconfig);
320
321         return ipconfig;
322 }
323
324 void __vpn_ipconfig_set_index(struct vpn_ipconfig *ipconfig, int index)
325 {
326         ipconfig->index = index;
327 }
328
329 static const char *type2str(unsigned short type)
330 {
331         switch (type) {
332         case ARPHRD_ETHER:
333                 return "ETHER";
334         case ARPHRD_LOOPBACK:
335                 return "LOOPBACK";
336         case ARPHRD_PPP:
337                 return "PPP";
338         case ARPHRD_NONE:
339                 return "NONE";
340         case ARPHRD_VOID:
341                 return "VOID";
342         }
343
344         return "";
345 }
346
347 void __vpn_ipconfig_newlink(int index, unsigned short type,
348                                 unsigned int flags,
349                                 const char *address,
350                                 unsigned short mtu,
351                                 struct rtnl_link_stats *stats)
352 {
353         struct vpn_ipdevice *ipdevice;
354         GString *str;
355
356         if (type == ARPHRD_LOOPBACK)
357                 return;
358
359         ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(index));
360         if (ipdevice)
361                 goto update;
362
363         ipdevice = g_try_new0(struct vpn_ipdevice, 1);
364         if (!ipdevice)
365                 return;
366
367         ipdevice->index = index;
368         ipdevice->ifname = connman_inet_ifname(index);
369         ipdevice->type = type;
370
371         ipdevice->address = g_strdup(address);
372
373         g_hash_table_insert(ipdevice_hash, GINT_TO_POINTER(index), ipdevice);
374
375         connman_info("%s {create} index %d type %d <%s>", ipdevice->ifname,
376                                                 index, type, type2str(type));
377
378 update:
379         ipdevice->mtu = mtu;
380
381         if (flags == ipdevice->flags)
382                 return;
383
384         ipdevice->flags = flags;
385
386         str = g_string_new(NULL);
387         if (!str)
388                 return;
389
390         if (flags & IFF_UP)
391                 g_string_append(str, "UP");
392         else
393                 g_string_append(str, "DOWN");
394
395         if (flags & IFF_RUNNING)
396                 g_string_append(str, ",RUNNING");
397
398         if (flags & IFF_LOWER_UP)
399                 g_string_append(str, ",LOWER_UP");
400
401         connman_info("%s {update} flags %u <%s>", ipdevice->ifname,
402                                                         flags, str->str);
403
404         g_string_free(str, TRUE);
405 }
406
407 void __vpn_ipconfig_dellink(int index, struct rtnl_link_stats *stats)
408 {
409         struct vpn_ipdevice *ipdevice;
410
411         DBG("index %d", index);
412
413         ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(index));
414         if (!ipdevice)
415                 return;
416
417         g_hash_table_remove(ipdevice_hash, GINT_TO_POINTER(index));
418 }
419
420 static void free_ipdevice(gpointer data)
421 {
422         struct vpn_ipdevice *ipdevice = data;
423
424         connman_info("%s {remove} index %d", ipdevice->ifname,
425                                                         ipdevice->index);
426
427         g_free(ipdevice->ipv4_gateway);
428         g_free(ipdevice->ipv6_gateway);
429         g_free(ipdevice->pac);
430
431         g_free(ipdevice->address);
432
433         g_free(ipdevice->ifname);
434         g_free(ipdevice);
435 }
436
437 int __vpn_ipconfig_init(void)
438 {
439         DBG("");
440
441         ipdevice_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
442                                                         NULL, free_ipdevice);
443
444         return 0;
445 }
446
447 void __vpn_ipconfig_cleanup(void)
448 {
449         DBG("");
450
451         g_hash_table_destroy(ipdevice_hash);
452         ipdevice_hash = NULL;
453 }