Merge tag 'upstream/1.40' into tizen.
[platform/upstream/connman.git] / vpn / plugins / wireguard.c
1 /*
2  *  ConnMan VPN daemon
3  *
4  *  Copyright (C) 2019  Daniel Wagner. All rights reserved.
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License version 2 as
8  *  published by the Free Software Foundation.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <stdlib.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <unistd.h>
28 #include <net/if.h>
29 #include <arpa/inet.h>
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 #include <netdb.h>
33
34 #include <glib.h>
35
36 #define CONNMAN_API_SUBJECT_TO_CHANGE
37 #include <connman/plugin.h>
38 #include <connman/log.h>
39 #include <connman/task.h>
40 #include <connman/ipconfig.h>
41 #include <connman/inet.h>
42 #include <connman/dbus.h>
43 #include <connman/setting.h>
44 #include <connman/vpn-dbus.h>
45
46 #include "../vpn-provider.h"
47 #include "../vpn.h"
48
49 #include "vpn.h"
50 #include "wireguard.h"
51
52 #define DNS_RERESOLVE_TIMEOUT 20
53 #if defined TIZEN_EXT
54 #define ADD_ALLOWED_IP_ROUTE_TIMEOUT 2
55 #endif /* TIZEN_EXT */
56
57 struct wireguard_info {
58         struct wg_device device;
59         struct wg_peer peer;
60         char *endpoint_fqdn;
61         char *port;
62         int reresolve_id;
63 #if defined TIZEN_EXT
64         int allowed_ip_route_id;
65 #endif /* TIZEN_EXT */
66 };
67
68 struct sockaddr_u {
69         union {
70                 struct sockaddr sa;
71                 struct sockaddr_in sin;
72                 struct sockaddr_in6 sin6;
73         };
74 };
75
76 static int parse_key(const char *str, wg_key key)
77 {
78         unsigned char *buf;
79         size_t len;
80
81         buf = g_base64_decode(str, &len);
82
83         if (len != 32) {
84                 g_free(buf);
85                 return -EINVAL;
86         }
87
88         memcpy(key, buf, 32);
89
90         g_free(buf);
91         return 0;
92 }
93
94 static int parse_allowed_ips(const char *allowed_ips, wg_peer *peer)
95 {
96         struct wg_allowedip *curaip, *allowedip;
97         char buf[INET6_ADDRSTRLEN];
98         char **tokens, **toks;
99         char *send;
100         int i;
101
102         curaip = NULL;
103 #if defined TIZEN_EXT
104         /**
105          * Note: At API level we donot check Wireguard specific configurations,
106          * User can provide any space separated string for IP.
107          * Ideally it should be using "10.0.0.2/32, 10.0.0.3/32"
108          * However there can be cases like "10.0.0.2/32,10.0.0.3/32"
109          *
110          * So we need to parse the allowed_ips configuration properly.
111          */
112         tokens = g_strsplit(allowed_ips, ",", -1);
113 #else
114         tokens = g_strsplit(allowed_ips, ", ", -1);
115 #endif
116         for (i = 0; tokens[i]; i++) {
117 #if defined TIZEN_EXT
118                 int len = strlen(tokens[i]);
119                 char *ptr = tokens[i];
120                 int j = -1;
121
122                 //skip forward spaces
123                 while (++j < len && ptr[j] == ' ')
124                         ;// Do Nothing
125
126                 if (!ptr[j])
127                         continue;
128
129                 toks = g_strsplit(ptr + j, "/", -1);
130                 if (g_strv_length(toks) != 2) {
131                         DBG("Ignore AllowedIPs value [%s]", ptr + j);
132                         g_strfreev(toks);
133                         continue;
134                 }
135
136                 DBG("Parsed AllowedIPs [%s]", ptr + j);
137 #else
138
139                 toks = g_strsplit(tokens[i], "/", -1);
140                 if (g_strv_length(toks) != 2) {
141                         DBG("Ignore AllowedIPs value %s", tokens[i]);
142                         g_strfreev(toks);
143                         continue;
144                 }
145 #endif
146
147                 allowedip = g_malloc0(sizeof(*allowedip));
148
149                 if (inet_pton(AF_INET, toks[0], buf) == 1) {
150                         allowedip->family = AF_INET;
151                         memcpy(&allowedip->ip4, buf, sizeof(allowedip->ip4));
152                 } else if (inet_pton(AF_INET6, toks[0], buf) == 1) {
153                         allowedip->family = AF_INET6;
154                         memcpy(&allowedip->ip6, buf, sizeof(allowedip->ip6));
155                 } else {
156 #if defined TIZEN_EXT
157                         DBG("Ignore AllowedIPs value [%s]", ptr + j);
158 #else
159                         DBG("Ignore AllowedIPs value %s", tokens[i]);
160 #endif
161                         g_free(allowedip);
162                         g_strfreev(toks);
163                         continue;
164                 }
165
166                 allowedip->cidr = g_ascii_strtoull(toks[1], &send, 10);
167
168                 if (!curaip)
169                         peer->first_allowedip = allowedip;
170                 else
171                         curaip->next_allowedip = allowedip;
172
173                 curaip = allowedip;
174 #if defined TIZEN_EXT
175                 g_strfreev(toks);
176 #endif
177         }
178
179         peer->last_allowedip = curaip;
180         g_strfreev(tokens);
181
182         return 0;
183 }
184
185 static int parse_endpoint(const char *host, const char *port, struct sockaddr_u *addr)
186 {
187         struct addrinfo hints;
188         struct addrinfo *result, *rp;
189         int sk;
190
191         memset(&hints, 0, sizeof(struct addrinfo));
192         hints.ai_family = AF_UNSPEC;
193         hints.ai_socktype = SOCK_DGRAM;
194         hints.ai_flags = 0;
195         hints.ai_protocol = 0;
196
197         if (getaddrinfo(host, port, &hints, &result) < 0) {
198                 DBG("Failed to resolve host address");
199                 return -EINVAL;
200         }
201
202         for (rp = result; rp; rp = rp->ai_next) {
203                 sk = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
204                 if (sk < 0)
205                         continue;
206                 if (connect(sk, rp->ai_addr, rp->ai_addrlen) != -1) {
207                         /* success */
208                         close(sk);
209                         break;
210                 }
211
212                 close(sk);
213         }
214
215         if (!rp) {
216                 freeaddrinfo(result);
217                 return -EINVAL;
218         }
219
220         memcpy(addr, rp->ai_addr, rp->ai_addrlen);
221         freeaddrinfo(result);
222
223         return 0;
224 }
225
226 static int parse_address(const char *address, const char *gateway,
227                 struct connman_ipaddress **ipaddress)
228 {
229         char buf[INET6_ADDRSTRLEN];
230         unsigned char prefixlen;
231         char **tokens;
232         char *end, *netmask;
233         int err;
234
235         tokens = g_strsplit(address, "/", -1);
236         if (g_strv_length(tokens) != 2) {
237                 g_strfreev(tokens);
238                 return -EINVAL;
239         }
240
241         prefixlen = g_ascii_strtoull(tokens[1], &end, 10);
242
243         if (inet_pton(AF_INET, tokens[0], buf) == 1) {
244                 netmask = g_strdup_printf("%d.%d.%d.%d",
245                                 ((0xffffffff << (32 - prefixlen)) >> 24) & 0xff,
246                                 ((0xffffffff << (32 - prefixlen)) >> 16) & 0xff,
247                                 ((0xffffffff << (32 - prefixlen)) >> 8) & 0xff,
248                                 ((0xffffffff << (32 - prefixlen)) >> 0) & 0xff);
249
250                 *ipaddress = connman_ipaddress_alloc(AF_INET);
251                 err = connman_ipaddress_set_ipv4(*ipaddress, tokens[0],
252                                                 netmask, gateway);
253                 g_free(netmask);
254         } else if (inet_pton(AF_INET6, tokens[0], buf) == 1) {
255                 *ipaddress = connman_ipaddress_alloc(AF_INET6);
256                 err = connman_ipaddress_set_ipv6(*ipaddress, tokens[0],
257                                                 prefixlen, gateway);
258         } else {
259                 DBG("Invalid Wireguard.Address value");
260                 err = -EINVAL;
261         }
262
263         connman_ipaddress_set_p2p(*ipaddress, true);
264
265         g_strfreev(tokens);
266         if (err)
267                 connman_ipaddress_free(*ipaddress);
268
269         return err;
270 }
271
272 struct ifname_data {
273         char *ifname;
274         bool found;
275 };
276
277 static void ifname_check_cb(int index, void *user_data)
278 {
279         struct ifname_data *data = (struct ifname_data *)user_data;
280         char *ifname;
281
282         ifname = connman_inet_ifname(index);
283
284         if (!g_strcmp0(ifname, data->ifname))
285                 data->found = true;
286 }
287
288 static char *get_ifname(void)
289 {
290         struct ifname_data data;
291         int i;
292
293         for (i = 0; i < 256; i++) {
294                 data.ifname = g_strdup_printf("wg%d", i);
295                 data.found = false;
296                 vpn_ipconfig_foreach(ifname_check_cb, &data);
297
298                 if (!data.found)
299                         return data.ifname;
300
301                 g_free(data.ifname);
302         }
303
304         return NULL;
305 }
306
307 static bool sockaddr_cmp_addr(struct sockaddr_u *a, struct sockaddr_u *b)
308 {
309         if (a->sa.sa_family != b->sa.sa_family)
310                 return false;
311
312         if (a->sa.sa_family == AF_INET)
313                 return !memcmp(&a->sin, &b->sin, sizeof(struct sockaddr_in));
314         else if (a->sa.sa_family == AF_INET6)
315                 return !memcmp(a->sin6.sin6_addr.s6_addr,
316                                 b->sin6.sin6_addr.s6_addr,
317                                 sizeof(a->sin6.sin6_addr.s6_addr));
318
319         return false;
320 }
321
322 #if defined TIZEN_EXT
323 #include <unistd.h>
324 #include <sys/types.h>
325 #include <sys/wait.h>
326 #include <stdio.h>
327
328 #define MAX_SIZE_ERROR_BUFFER 256
329 #define MAX_CMD_SIZE 80
330
331 typedef struct {
332         struct vpn_provider *provider;
333         struct wireguard_info *info;
334 } wg_allowed_ip_route_cb_data_s;
335
336 static int wg_execute_cmd(const char *format, ...)
337 {
338         int status = 0;
339         int rv = 0;
340         char cmd[MAX_CMD_SIZE] = { 0, };
341         char error_buf[MAX_SIZE_ERROR_BUFFER] = {0, };
342         pid_t pid = 0;
343         va_list va_ptr;
344         gchar **args = NULL;
345
346         va_start(va_ptr, format);
347         vsnprintf(cmd, (unsigned int) MAX_CMD_SIZE, format, va_ptr);
348         va_end(va_ptr);
349
350         if (strlen(cmd) == 0)
351                 return -EIO;
352
353         DBG("command: %s", cmd);
354
355         args = g_strsplit_set(cmd, " ", -1);
356
357         errno = 0;
358         if (!(pid = fork())) {
359                 DBG("pid(%d), ppid (%d)", getpid(), getppid());
360
361                 errno = 0;
362                 if (execv(args[0], args) == -1) {
363                         DBG("Fail to execute command (%s)",
364                                         strerror_r(errno, error_buf, MAX_SIZE_ERROR_BUFFER));
365                         g_strfreev(args);
366                         exit(1);
367                 }
368         } else if (pid > 0) {
369                 if (waitpid(pid, &status, 0) == -1)
370                         DBG("wait pid (%u) status (%d)", pid, status);
371
372                 if (WIFEXITED(status)) {
373                         rv = WEXITSTATUS(status);
374                         DBG("exited, status=%d", rv);
375
376                 } else if (WIFSIGNALED(status)) {
377                         DBG("killed by signal %d", WTERMSIG(status));
378
379                 } else if (WIFSTOPPED(status)) {
380                         DBG("stopped by signal %d", WSTOPSIG(status));
381
382                 } else if (WIFCONTINUED(status)) {
383                         DBG("continued");
384                 }
385
386                 g_strfreev(args);
387                 return rv;
388         }
389
390         DBG("failed to fork(%s)", strerror_r(errno, error_buf, MAX_SIZE_ERROR_BUFFER));
391         g_strfreev(args);
392
393         return -EIO;
394 }
395
396 static gboolean wg_add_allowed_ip_route_cb(gpointer user_data)
397 {
398         wg_allowed_ip_route_cb_data_s *cb_data = user_data;
399         const char *allowed_ips;
400         char **tokens = NULL;
401         char **toks = NULL;
402         int i;
403
404         DBG("");
405
406         allowed_ips = vpn_provider_get_string(cb_data->provider, "WireGuard.AllowedIPs");
407         if (!allowed_ips) {
408                 DBG("WireGuard.AllowedIPs is missing");
409                 goto done;
410         }
411
412         tokens = g_strsplit(allowed_ips, ",", -1);
413         if (g_strv_length(tokens) == 0)
414                 goto done;
415
416         for (i = 0; tokens[i]; i++) {
417                 int len = strlen(tokens[i]);
418                 char *ptr = tokens[i];
419                 int j = -1;
420
421                 //skip forward spaces
422                 while (++j < len && ptr[j] == ' ')
423                         ;// Do Nothing
424
425                 if (!ptr[j])
426                         continue;
427
428                 toks = g_strsplit(ptr + j, "/", -1);
429                 if (g_strv_length(toks) != 2) {
430                         DBG("Ignore AllowedIPs value [%s]", ptr + j);
431                         g_strfreev(toks);
432                         continue;
433                 }
434                 g_strfreev(toks);
435
436                 DBG("Allowed IP: [%s], Device Name: [%s]", ptr + j, cb_data->info->device.name);
437
438                 // TODO: Remove system call to add route entry present in wg_execute_cmd()
439                 if (!g_strcmp0("0.0.0.0/0", ptr + j)) {
440                         // TODO: Update default route because user wants all data to be routed from wg0,
441                         // when 0.0.0.0/0 is passed in allowed ips list.
442                         // Should we replace the default route?
443                         // If yes, then how to recover default route when wg0 tunnel is removed.
444                 } else {
445                         wg_execute_cmd("/usr/sbin/ip route add %s dev %s", ptr + j, cb_data->info->device.name);
446                 }
447         }
448
449 done:
450         if (tokens)
451                 g_strfreev(tokens);
452
453         free(cb_data);
454
455         return FALSE;
456 }
457 #endif /* TIZEN_EXT */
458
459 static gboolean wg_dns_reresolve_cb(gpointer user_data)
460 {
461         struct wireguard_info *info = user_data;
462         struct sockaddr_u addr;
463         int err;
464
465         DBG("");
466
467         err = parse_endpoint(info->endpoint_fqdn,
468                         info->port, &addr);
469         if (err)
470                 return TRUE;
471
472         if (sockaddr_cmp_addr(&addr,
473                         (struct sockaddr_u *)&info->peer.endpoint.addr))
474                 return TRUE;
475
476         if (addr.sa.sa_family == AF_INET)
477                 memcpy(&info->peer.endpoint.addr, &addr.sin,
478                         sizeof(info->peer.endpoint.addr4));
479         else
480                 memcpy(&info->peer.endpoint.addr, &addr.sin6,
481                         sizeof(info->peer.endpoint.addr6));
482
483         DBG("Endpoint address has changed, udpate WireGuard device");
484         err = wg_set_device(&info->device);
485         if (err)
486                 DBG("Failed to update Endpoint address for WireGuard device %s",
487                         info->device.name);
488
489         return TRUE;
490 }
491
492 static int wg_connect(struct vpn_provider *provider,
493                         struct connman_task *task, const char *if_name,
494                         vpn_provider_connect_cb_t cb,
495                         const char *dbus_sender, void *user_data)
496 {
497         struct connman_ipaddress *ipaddress = NULL;
498         struct wireguard_info *info;
499         const char *option, *gateway;
500         char *ifname;
501         int err = -EINVAL;
502
503         info = g_malloc0(sizeof(struct wireguard_info));
504         info->peer.flags = WGPEER_HAS_PUBLIC_KEY | WGPEER_REPLACE_ALLOWEDIPS;
505         info->device.flags = WGDEVICE_HAS_PRIVATE_KEY;
506         info->device.first_peer = &info->peer;
507         info->device.last_peer = &info->peer;
508
509         vpn_provider_set_plugin_data(provider, info);
510
511         option = vpn_provider_get_string(provider, "WireGuard.ListenPort");
512         if (option) {
513                 char *end;
514                 info->device.listen_port = g_ascii_strtoull(option, &end, 10);
515                 info->device.flags |= WGDEVICE_HAS_LISTEN_PORT;
516         }
517
518         option = vpn_provider_get_string(provider, "WireGuard.DNS");
519         if (option) {
520                 err = vpn_provider_set_nameservers(provider, option);
521                 if (err)
522                         goto done;
523         }
524
525         option = vpn_provider_get_string(provider, "WireGuard.PrivateKey");
526         if (!option) {
527                 DBG("WireGuard.PrivateKey is missing");
528                 goto done;
529         }
530         err = parse_key(option, info->device.private_key);
531         if (err)
532                 goto done;
533
534         option = vpn_provider_get_string(provider, "WireGuard.PublicKey");
535         if (!option) {
536                 DBG("WireGuard.PublicKey is missing");
537                 goto done;
538         }
539         err = parse_key(option, info->peer.public_key);
540         if (err)
541                 goto done;
542
543         option = vpn_provider_get_string(provider, "WireGuard.PresharedKey");
544         if (option) {
545                 info->peer.flags |= WGPEER_HAS_PRESHARED_KEY;
546                 err = parse_key(option, info->peer.preshared_key);
547                 if (err)
548                         goto done;
549         }
550
551         option = vpn_provider_get_string(provider, "WireGuard.AllowedIPs");
552         if (!option) {
553                 DBG("WireGuard.AllowedIPs is missing");
554                 goto done;
555         }
556         err = parse_allowed_ips(option, &info->peer);
557         if (err)
558                 goto done;
559
560         option = vpn_provider_get_string(provider,
561                                         "WireGuard.PersistentKeepalive");
562         if (option) {
563                 char *end;
564                 info->peer.persistent_keepalive_interval =
565                         g_ascii_strtoull(option, &end, 10);
566                 info->peer.flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
567         }
568
569         option = vpn_provider_get_string(provider, "WireGuard.EndpointPort");
570         if (!option)
571                 option = "51820";
572
573         gateway = vpn_provider_get_string(provider, "Host");
574         err = parse_endpoint(gateway, option,
575                         (struct sockaddr_u *)&info->peer.endpoint.addr);
576         if (err)
577                 goto done;
578
579         info->endpoint_fqdn = g_strdup(gateway);
580         info->port = g_strdup(option);
581
582         option = vpn_provider_get_string(provider, "WireGuard.Address");
583         if (!option) {
584                 DBG("Missing WireGuard.Address configuration");
585                 goto done;
586         }
587         err = parse_address(option, gateway, &ipaddress);
588         if (err)
589                 goto done;
590
591         ifname = get_ifname();
592         if (!ifname) {
593                 DBG("Failed to find an usable device name");
594                 err = -ENOENT;
595                 goto done;
596         }
597         stpncpy(info->device.name, ifname, sizeof(info->device.name) - 1);
598         g_free(ifname);
599
600         err = wg_add_device(info->device.name);
601         if (err) {
602                 DBG("Failed to creating WireGuard device %s", info->device.name);
603                 goto done;
604         }
605
606         err = wg_set_device(&info->device);
607         if (err) {
608                 DBG("Failed to configure WireGuard device %s", info->device.name);
609                 wg_del_device(info->device.name);
610         }
611
612         vpn_set_ifname(provider, info->device.name);
613         if (ipaddress)
614                 vpn_provider_set_ipaddress(provider, ipaddress);
615
616 done:
617         if (cb)
618                 cb(provider, user_data, err);
619
620         connman_ipaddress_free(ipaddress);
621
622 #if defined TIZEN_EXT
623         if (!err) {
624                 const char *allowed_ips = vpn_provider_get_string(provider, "WireGuard.AllowedIPs");
625                 if (allowed_ips) {
626                         wg_allowed_ip_route_cb_data_s *cb_data =
627                                 (wg_allowed_ip_route_cb_data_s *) calloc(1, sizeof(wg_allowed_ip_route_cb_data_s));
628                         if (cb_data) {
629                                 cb_data->provider = provider;
630                                 cb_data->info = info;
631
632                                 info->allowed_ip_route_id =
633                                         g_timeout_add_seconds(ADD_ALLOWED_IP_ROUTE_TIMEOUT,
634                                                         wg_add_allowed_ip_route_cb, cb_data);
635                         }
636                 }
637         }
638 #endif /* TIZEN_EXT */
639
640         if (!err)
641                 info->reresolve_id =
642                         g_timeout_add_seconds(DNS_RERESOLVE_TIMEOUT,
643                                                 wg_dns_reresolve_cb, info);
644
645         return err;
646 }
647
648 static void wg_disconnect(struct vpn_provider *provider)
649 {
650         struct wireguard_info *info;
651
652         info = vpn_provider_get_plugin_data(provider);
653         if (!info)
654                 return;
655
656 #if defined TIZEN_EXT
657         if (info->allowed_ip_route_id > 0)
658                 g_source_remove(info->allowed_ip_route_id);
659 #endif /* TIZEN_EXT */
660
661         if (info->reresolve_id > 0)
662                 g_source_remove(info->reresolve_id);
663
664         vpn_provider_set_plugin_data(provider, NULL);
665
666         wg_del_device(info->device.name);
667
668         g_free(info->endpoint_fqdn);
669         g_free(info->port);
670         g_free(info);
671 }
672
673 #if defined TIZEN_EXT
674 static int wg_save(struct vpn_provider *provider, GKeyFile *keyfile)
675 {
676         const char *option;
677
678         DBG("");
679
680         /*
681          * The client/own device listen port.
682          */
683         option = vpn_provider_get_string(provider, "WireGuard.ListenPort");
684         if (option)
685                 g_key_file_set_string(keyfile,
686                                 vpn_provider_get_save_group(provider),
687                                 "WireGuard.ListenPort",
688                                 option);
689
690         /*
691          * comma separated DNS.
692          */
693         option = vpn_provider_get_string(provider, "WireGuard.DNS");
694         if (option)
695                 g_key_file_set_string(keyfile,
696                                 vpn_provider_get_save_group(provider),
697                                 "WireGuard.DNS",
698                                 option);
699
700         /*
701          * The client private key.
702          */
703         option = vpn_provider_get_string(provider, "WireGuard.PrivateKey");
704         if (option)
705                 g_key_file_set_string(keyfile,
706                                 vpn_provider_get_save_group(provider),
707                                 "WireGuard.PrivateKey",
708                                 option);
709
710         /*
711          * The server public key.
712          */
713         option = vpn_provider_get_string(provider, "WireGuard.PublicKey");
714         if (option)
715                 g_key_file_set_string(keyfile,
716                                 vpn_provider_get_save_group(provider),
717                                 "WireGuard.PublicKey",
718                                 option);
719
720         /*
721          * The preshared key.
722          */
723         option = vpn_provider_get_string(provider, "WireGuard.PresharedKey");
724         if (option)
725                 g_key_file_set_string(keyfile,
726                                 vpn_provider_get_save_group(provider),
727                                 "WireGuard.PresharedKey",
728                                 option);
729
730         /*
731          * Subnets accessed via VPN tunnel, 0.0.0.0/0 routes all traffic.
732          */
733         option = vpn_provider_get_string(provider, "WireGuard.AllowedIPs");
734         if (option)
735                 g_key_file_set_string(keyfile,
736                                 vpn_provider_get_save_group(provider),
737                                 "WireGuard.AllowedIPs",
738                                 option);
739
740         /*
741          * The time in seconds to emit periodic keep alive message.
742          */
743         option = vpn_provider_get_string(provider, "WireGuard.PersistentKeepalive");
744         if (option)
745                 g_key_file_set_string(keyfile,
746                                 vpn_provider_get_save_group(provider),
747                                 "WireGuard.PersistentKeepalive",
748                                 option);
749
750         /*
751          * The server listen port, default: 51820
752          */
753         option = vpn_provider_get_string(provider, "WireGuard.EndpointPort");
754         if (option)
755                 g_key_file_set_string(keyfile,
756                                 vpn_provider_get_save_group(provider),
757                                 "WireGuard.EndpointPort",
758                                 option);
759
760         /*
761          * Save Address: The internal IP of the client node
762          */
763         option = vpn_provider_get_string(provider, "WireGuard.Address");
764         if (option)
765                 g_key_file_set_string(keyfile,
766                                 vpn_provider_get_save_group(provider),
767                                 "WireGuard.Address",
768                                 option);
769
770         return 0;
771 }
772 #endif
773
774 static struct vpn_driver vpn_driver = {
775         .flags          = VPN_FLAG_NO_TUN | VPN_FLAG_NO_DAEMON,
776         .connect        = wg_connect,
777         .disconnect     = wg_disconnect,
778 #if defined TIZEN_EXT
779         .save           = wg_save,
780 #endif
781 };
782
783 static int wg_init(void)
784 {
785         return vpn_register("wireguard", &vpn_driver, NULL);
786 }
787
788 static void wg_exit(void)
789 {
790         vpn_unregister("wireguard");
791 }
792
793 CONNMAN_PLUGIN_DEFINE(wireguard, "WireGuard VPN plugin", VERSION,
794         CONNMAN_PLUGIN_PRIORITY_DEFAULT, wg_init, wg_exit)