provider: Report VPN error code
[platform/upstream/connman.git] / src / provider.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2010  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 <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <gdbus.h>
30
31 #include "connman.h"
32
33 static DBusConnection *connection = NULL;
34
35 static GHashTable *provider_hash = NULL;
36
37 static GSList *driver_list = NULL;
38
39 struct connman_route {
40         int family;
41         char *host;
42         char *netmask;
43         char *gateway;
44 };
45
46 struct connman_provider {
47         struct connman_element element;
48         struct connman_service *vpn_service;
49         char *identifier;
50         char *name;
51         char *type;
52         char *host;
53         char *dns;
54         char *domain;
55         GHashTable *routes;
56         struct connman_provider_driver *driver;
57         void *driver_data;
58 };
59
60 void __connman_provider_append_properties(struct connman_provider *provider,
61                                                         DBusMessageIter *iter)
62 {
63         if (provider->host != NULL)
64                 connman_dbus_dict_append_basic(iter, "Host",
65                                         DBUS_TYPE_STRING, &provider->host);
66
67         if (provider->domain != NULL)
68                 connman_dbus_dict_append_basic(iter, "Domain",
69                                         DBUS_TYPE_STRING, &provider->domain);
70
71         if (provider->type != NULL)
72                 connman_dbus_dict_append_basic(iter, "Type", DBUS_TYPE_STRING,
73                                                  &provider->type);
74 }
75
76 static struct connman_provider *connman_provider_lookup(const char *identifier)
77 {
78         struct connman_provider *provider = NULL;
79
80         provider = g_hash_table_lookup(provider_hash, identifier);
81
82         return provider;
83 }
84
85 static int connman_provider_setup_vpn_ipv4(struct connman_provider *provider,
86                                                 struct connman_element *element)
87 {
88         if (element == NULL || provider == NULL)
89                 return -EINVAL;
90
91         DBG("set vpn type %d", element->type);
92
93         g_free(element->ipv4.address);
94         element->ipv4.address = g_strdup(provider->element.ipv4.address);
95
96         g_free(element->ipv4.peer);
97         element->ipv4.peer = g_strdup(provider->element.ipv4.peer);
98
99         g_free(element->ipv4.netmask);
100         element->ipv4.netmask = g_strdup(provider->element.ipv4.netmask);
101
102         g_free(element->ipv4.gateway);
103         element->ipv4.gateway = g_strdup(provider->element.ipv4.gateway);
104
105         g_free(element->ipv4.broadcast);
106         element->ipv4.broadcast = g_strdup(provider->element.ipv4.broadcast);
107
108         g_free(element->ipv4.pac);
109         element->ipv4.pac = g_strdup(provider->element.ipv4.pac);
110
111         return connman_element_register(element, &provider->element);
112 }
113
114 struct connman_provider *connman_provider_ref(struct connman_provider *provider)
115 {
116         DBG("provider %p", provider);
117
118         if (connman_element_ref(&provider->element) == NULL)
119                 return NULL;
120
121         return provider;
122 }
123
124 void connman_provider_unref(struct connman_provider *provider)
125 {
126         DBG("provider %p", provider);
127
128         connman_element_unref(&provider->element);
129 }
130
131 static gboolean match_driver(struct connman_provider *provider,
132                                 struct connman_provider_driver *driver)
133 {
134         if (g_strcmp0(driver->name, provider->type) == 0)
135                 return TRUE;
136
137         return FALSE;
138 }
139
140 static int provider_probe(struct connman_provider *provider)
141 {
142         GSList *list;
143
144         DBG("provider %p name %s", provider, provider->name);
145
146         if (provider->driver != NULL)
147                 return -EALREADY;
148
149         for (list = driver_list; list; list = list->next) {
150                 struct connman_provider_driver *driver = list->data;
151
152                 if (match_driver(provider, driver) == FALSE)
153                         continue;
154
155                 DBG("driver %p name %s", driver, driver->name);
156
157                 if (driver->probe != NULL && driver->probe(provider) == 0) {
158                         provider->driver = driver;
159                         break;
160                 }
161         }
162
163         if (provider->driver == NULL)
164                 return -ENODEV;
165
166         return 0;
167 }
168
169 int __connman_provider_disconnect(struct connman_provider *provider)
170 {
171         int err;
172
173         DBG("provider %p", provider);
174
175         if (provider->driver != NULL && provider->driver->disconnect != NULL)
176                 err = provider->driver->disconnect(provider);
177         else
178                 return -EOPNOTSUPP;
179
180         if (provider->vpn_service != NULL)
181                 __connman_service_indicate_state(provider->vpn_service,
182                                         CONNMAN_SERVICE_STATE_DISCONNECT);
183         if (err < 0) {
184                 if (err != -EINPROGRESS)
185                         return err;
186
187                 return -EINPROGRESS;
188         }
189
190         return 0;
191 }
192
193 int __connman_provider_connect(struct connman_provider *provider)
194 {
195         int err;
196
197         DBG("provider %p", provider);
198
199         g_free(provider->element.ipv4.address);
200         g_free(provider->element.ipv4.peer);
201         g_free(provider->element.ipv4.netmask);
202         g_free(provider->element.ipv4.gateway);
203         g_free(provider->element.ipv4.broadcast);
204         g_free(provider->element.ipv4.pac);
205
206         provider->element.ipv4.address = NULL;
207         provider->element.ipv4.peer = NULL;
208         provider->element.ipv4.netmask = NULL;
209         provider->element.ipv4.gateway = NULL;
210         provider->element.ipv4.broadcast = NULL;
211         provider->element.ipv4.pac = NULL;
212
213         if (provider->driver != NULL && provider->driver->connect != NULL)
214                 err = provider->driver->connect(provider);
215         else
216                 return -EOPNOTSUPP;
217
218         if (err < 0) {
219                 if (err != -EINPROGRESS)
220                         return err;
221
222                 __connman_service_indicate_state(provider->vpn_service,
223                                         CONNMAN_SERVICE_STATE_ASSOCIATION);
224                 return -EINPROGRESS;
225         }
226
227         return 0;
228 }
229
230 int __connman_provider_remove(const char *path)
231 {
232         struct connman_provider *provider;
233         GHashTableIter iter;
234         gpointer value, key;
235
236         DBG("path %s", path);
237
238         g_hash_table_iter_init(&iter, provider_hash);
239         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
240                 const char *srv_path;
241                 provider = value;
242
243                 if (provider->vpn_service == NULL)
244                         continue;
245
246                 srv_path = __connman_service_get_path(provider->vpn_service);
247
248                 if (g_strcmp0(srv_path, path) == 0) {
249                         DBG("Removing VPN %s", provider->identifier);
250                         g_hash_table_remove(provider_hash,
251                                                 provider->identifier);
252                         return 0;
253                 }
254         }
255
256         return -ENXIO;
257 }
258
259 static void provider_append_routes(gpointer key, gpointer value,
260                                         gpointer user_data)
261 {
262         struct connman_route *route = value;
263         struct connman_provider *provider = user_data;
264         int index = provider->element.index;
265
266         if (route->family == AF_INET6) {
267                 unsigned char prefix_len = atoi(route->netmask);
268
269                 connman_inet_add_ipv6_network_route(index, route->host,
270                                                         route->gateway,
271                                                         prefix_len);
272         } else {
273                 connman_inet_add_network_route(index, route->host,
274                                                 route->gateway,
275                                                 route->netmask);
276         }
277 }
278
279 static void provider_set_nameservers(struct connman_provider *provider)
280 {
281         struct connman_service *service = provider->vpn_service;
282
283         char *nameservers = NULL, *name = NULL;
284         const char *value;
285         char *second_ns;
286
287         if (service == NULL)
288                 return;
289
290         if (provider->dns == NULL)
291                 return;
292
293         name = connman_inet_ifname(provider->element.index);
294
295         nameservers = g_strdup(provider->dns);
296         value = nameservers;
297         second_ns = strchr(value, ' ');
298         if (second_ns)
299                 *(second_ns++) = 0;
300         __connman_service_append_nameserver(service, value);
301         value = second_ns;
302
303         while (value) {
304                 char *next = strchr(value, ' ');
305                 if (next)
306                         *(next++) = 0;
307
308                 connman_resolver_append(name, provider->domain, value);
309                 value = next;
310         }
311
312         g_free(nameservers);
313         g_free(name);
314 }
315
316 static int set_connected(struct connman_provider *provider,
317                                         connman_bool_t connected)
318 {
319         struct connman_service *service = provider->vpn_service;
320
321         if (service == NULL)
322                 return -ENODEV;
323
324         if (connected == TRUE) {
325                 enum connman_element_type type = CONNMAN_ELEMENT_TYPE_UNKNOWN;
326                 struct connman_element *element;
327                 int err;
328
329                 __connman_service_indicate_state(provider->vpn_service,
330                                         CONNMAN_SERVICE_STATE_CONFIGURATION);
331
332                 type = CONNMAN_ELEMENT_TYPE_IPV4;
333
334                 element = connman_element_create(NULL);
335                 if (element == NULL)
336                         return -ENOMEM;
337
338                 element->type  = type;
339                 element->index = provider->element.index;
340
341                 err = connman_provider_setup_vpn_ipv4(provider, element);
342                 if (err < 0) {
343                         connman_element_unref(element);
344
345                         __connman_service_indicate_state(service,
346                                                 CONNMAN_SERVICE_STATE_FAILURE);
347
348                         return err;
349                 }
350
351                 __connman_service_indicate_state(service,
352                                                 CONNMAN_SERVICE_STATE_READY);
353
354                 __connman_service_set_domainname(service, provider->domain);
355
356                 provider_set_nameservers(provider);
357
358                 g_hash_table_foreach(provider->routes, provider_append_routes,
359                                         provider);
360         } else {
361                 connman_element_unregister_children(&provider->element);
362                 __connman_service_indicate_state(service,
363                                                 CONNMAN_SERVICE_STATE_IDLE);
364         }
365
366         return 0;
367 }
368
369 int connman_provider_set_state(struct connman_provider *provider,
370                                         enum connman_provider_state state)
371 {
372         if (provider == NULL || provider->vpn_service == NULL)
373                 return -EINVAL;
374
375         switch (state) {
376         case CONNMAN_PROVIDER_STATE_UNKNOWN:
377                 return -EINVAL;
378         case CONNMAN_PROVIDER_STATE_IDLE:
379                 return set_connected(provider, FALSE);
380         case CONNMAN_PROVIDER_STATE_CONNECT:
381                 return __connman_service_indicate_state(provider->vpn_service,
382                                         CONNMAN_SERVICE_STATE_ASSOCIATION);
383         case CONNMAN_PROVIDER_STATE_READY:
384                 return set_connected(provider, TRUE);
385         case CONNMAN_PROVIDER_STATE_DISCONNECT:
386                 return __connman_service_indicate_state(provider->vpn_service,
387                                         CONNMAN_SERVICE_STATE_DISCONNECT);
388         case CONNMAN_PROVIDER_STATE_FAILURE:
389                 return __connman_service_indicate_state(provider->vpn_service,
390                                         CONNMAN_SERVICE_STATE_FAILURE);
391         }
392
393         return -EINVAL;
394 }
395
396 int connman_provider_indicate_error(struct connman_provider *provider,
397                                         enum connman_provider_error error)
398 {
399         enum connman_service_error service_error;
400
401         switch (error) {
402         case CONNMAN_PROVIDER_ERROR_LOGIN_FAILED:
403                 service_error = CONNMAN_SERVICE_ERROR_LOGIN_FAILED;
404                 break;
405         case CONNMAN_PROVIDER_ERROR_AUTH_FAILED:
406                 service_error = CONNMAN_SERVICE_ERROR_AUTH_FAILED;
407                 break;
408         case CONNMAN_PROVIDER_ERROR_CONNECT_FAILED:
409                 service_error = CONNMAN_SERVICE_ERROR_CONNECT_FAILED;
410                 break;
411         default:
412                 service_error = CONNMAN_SERVICE_ERROR_UNKNOWN;
413                 break;
414         }
415
416         return __connman_service_indicate_error(provider->vpn_service,
417                                                         service_error);
418 }
419
420 static void unregister_provider(gpointer data)
421 {
422         struct connman_provider *provider = data;
423         struct connman_service *service = provider->vpn_service;
424
425         DBG("provider %p", provider);
426
427         provider->vpn_service = NULL;
428         __connman_service_put(service);
429
430         connman_element_unregister(&provider->element);
431         connman_provider_unref(provider);
432 }
433
434 static void provider_destruct(struct connman_element *element)
435 {
436         struct connman_provider *provider = element->private;
437
438         DBG("provider %p", provider);
439
440         g_free(provider->name);
441         g_free(provider->type);
442         g_free(provider->domain);
443         g_free(provider->identifier);
444         g_free(provider->dns);
445         g_hash_table_destroy(provider->routes);
446 }
447
448 static void destroy_route(gpointer user_data)
449 {
450         struct connman_route *route = user_data;
451
452         g_free(route->host);
453         g_free(route->netmask);
454         g_free(route->gateway);
455         g_free(route);
456 }
457
458 static void provider_initialize(struct connman_provider *provider)
459 {
460         DBG("provider %p", provider);
461
462         __connman_element_initialize(&provider->element);
463
464         provider->element.private = provider;
465         provider->element.destruct = provider_destruct;
466
467         provider->element.ipv4.address = NULL;
468         provider->element.ipv4.netmask = NULL;
469         provider->element.ipv4.gateway = NULL;
470         provider->element.ipv4.broadcast = NULL;
471         provider->element.ipv4.pac = NULL;
472
473         provider->name = NULL;
474         provider->type = NULL;
475         provider->dns = NULL;
476         provider->domain = NULL;
477         provider->identifier = NULL;
478         provider->routes = g_hash_table_new_full(g_direct_hash, g_direct_equal,
479                                         NULL, destroy_route);
480 }
481
482 static struct connman_provider *connman_provider_new(void)
483 {
484         struct connman_provider *provider;
485
486         provider = g_try_new0(struct connman_provider, 1);
487         if (provider == NULL)
488                 return NULL;
489
490         DBG("provider %p", provider);
491         provider_initialize(provider);
492
493         return provider;
494 }
495
496 static struct connman_provider *connman_provider_get(const char *identifier)
497 {
498         struct connman_provider *provider;
499
500         provider = g_hash_table_lookup(provider_hash, identifier);
501         if (provider != NULL)
502                 return provider;
503
504         provider = connman_provider_new();
505         if (provider == NULL)
506                 return NULL;
507
508         DBG("provider %p", provider);
509
510         provider->identifier = g_strdup(identifier);
511
512         g_hash_table_insert(provider_hash, provider->identifier, provider);
513
514         provider->element.name = g_strdup(identifier);
515         connman_element_register(&provider->element, NULL);
516
517         return provider;
518 }
519
520 static void provider_dbus_ident(char *ident)
521 {
522         int i, len = strlen(ident);
523
524         for (i = 0; i < len; i++) {
525                 if (ident[i] >= '0' && ident[i] <= '9')
526                         continue;
527                 if (ident[i] >= 'a' && ident[i] <= 'z')
528                         continue;
529                 if (ident[i] >= 'A' && ident[i] <= 'Z')
530                         continue;
531                 ident[i] = '_';
532         }
533 }
534
535 int __connman_provider_create_and_connect(DBusMessage *msg)
536 {
537         struct connman_provider *provider;
538         DBusMessageIter iter, array;
539         const char *type = NULL, *name = NULL, *service_path = NULL;
540         const char *host = NULL, *domain = NULL;
541         char *ident;
542         gboolean created = FALSE;
543         int err;
544
545         dbus_message_iter_init(msg, &iter);
546         dbus_message_iter_recurse(&iter, &array);
547
548         while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
549                 DBusMessageIter entry, value;
550                 const char *key;
551
552                 dbus_message_iter_recurse(&array, &entry);
553                 dbus_message_iter_get_basic(&entry, &key);
554
555                 dbus_message_iter_next(&entry);
556                 dbus_message_iter_recurse(&entry, &value);
557
558                 switch (dbus_message_iter_get_arg_type(&value)) {
559                 case DBUS_TYPE_STRING:
560                         if (g_str_equal(key, "Type") == TRUE)
561                                 dbus_message_iter_get_basic(&value, &type);
562                         else if (g_str_equal(key, "Name") == TRUE)
563                                 dbus_message_iter_get_basic(&value, &name);
564                         else if (g_str_equal(key, "Host") == TRUE)
565                                 dbus_message_iter_get_basic(&value, &host);
566                         else if (g_str_equal(key, "VPN.Domain") == TRUE)
567                                 dbus_message_iter_get_basic(&value, &domain);
568                         break;
569                 }
570
571                 dbus_message_iter_next(&array);
572         }
573
574         if (host == NULL || domain == NULL) {
575                 err = -EINVAL;
576                 goto failed;
577         }
578
579         DBG("Type %s name %s", type, name);
580
581         if (type == NULL || name == NULL) {
582                 err = -EOPNOTSUPP;
583                 goto failed;
584         }
585
586         ident = g_strdup_printf("%s_%s", host, domain);
587         provider_dbus_ident(ident);
588
589         DBG("ident %s", ident);
590
591         provider = connman_provider_lookup(ident);
592
593         if (provider == NULL) {
594                 created = TRUE;
595                 provider = connman_provider_get(ident);
596                 if (provider) {
597                         provider->host = g_strdup(host);
598                         provider->domain = g_strdup(domain);
599                         provider->name = g_strdup(name);
600                         provider->type = g_strdup(type);
601                 }
602         }
603
604         if (provider == NULL) {
605                 DBG("can not create provider");
606                 err = -EOPNOTSUPP;
607                 goto failed;
608         }
609         dbus_message_iter_init(msg, &iter);
610         dbus_message_iter_recurse(&iter, &array);
611
612         while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
613                 DBusMessageIter entry, value;
614                 const char *key, *str;
615
616                 dbus_message_iter_recurse(&array, &entry);
617                 dbus_message_iter_get_basic(&entry, &key);
618
619                 dbus_message_iter_next(&entry);
620                 dbus_message_iter_recurse(&entry, &value);
621
622                 switch (dbus_message_iter_get_arg_type(&value)) {
623                 case DBUS_TYPE_STRING:
624                         dbus_message_iter_get_basic(&value, &str);
625                         connman_provider_set_string(provider, key, str);
626                         break;
627                 }
628
629                 dbus_message_iter_next(&array);
630         }
631
632         g_free(ident);
633
634         if (provider == NULL) {
635                 err = -EOPNOTSUPP;
636                 goto failed;
637         }
638
639         if (created == TRUE)
640                 provider_probe(provider);
641
642         if (provider->vpn_service == NULL)
643                 provider->vpn_service =
644                         __connman_service_create_from_provider(provider);
645         if (provider->vpn_service == NULL) {
646                 err = -EOPNOTSUPP;
647                 goto failed;
648         }
649
650         err = __connman_service_connect(provider->vpn_service);
651         if (err < 0 && err != -EINPROGRESS)
652                 goto failed;
653
654         service_path = __connman_service_get_path(provider->vpn_service);
655         g_dbus_send_reply(connection, msg,
656                                 DBUS_TYPE_OBJECT_PATH, &service_path,
657                                                         DBUS_TYPE_INVALID);
658         return 0;
659
660 failed:
661         if (provider != NULL && created == TRUE) {
662                 DBG("can not connect delete provider");
663                 connman_provider_unref(provider);
664
665                 if (provider->vpn_service != NULL) {
666                         __connman_service_put(provider->vpn_service);
667                         provider->vpn_service = NULL;
668                 }
669         }
670
671         return err;
672 }
673
674 const char * __connman_provider_get_ident(struct connman_provider *provider)
675 {
676         if (provider == NULL)
677                 return NULL;
678
679         return provider->identifier;
680 }
681
682 int connman_provider_set_string(struct connman_provider *provider,
683                                         const char *key, const char *value)
684 {
685         DBG("provider %p key %s value %s", provider, key, value);
686
687         if (g_str_equal(key, "Type") == TRUE) {
688                 g_free(provider->type);
689                 provider->type = g_strdup(value);
690         } else if (g_str_equal(key, "Name") == TRUE) {
691                 g_free(provider->name);
692                 provider->name = g_strdup(value);
693         } else if (g_str_equal(key, "Gateway") == TRUE) {
694                 g_free(provider->element.ipv4.gateway);
695                 provider->element.ipv4.gateway = g_strdup(value);
696         } else if (g_str_equal(key, "Address") == TRUE) {
697                 g_free(provider->element.ipv4.address);
698                 provider->element.ipv4.address = g_strdup(value);
699         } else if (g_str_equal(key, "Peer") == TRUE) {
700                 g_free(provider->element.ipv4.peer);
701                 provider->element.ipv4.peer = g_strdup(value);
702         } else if (g_str_equal(key, "Netmask") == TRUE) {
703                 g_free(provider->element.ipv4.netmask);
704                 provider->element.ipv4.netmask = g_strdup(value);
705         } else if (g_str_equal(key, "PAC") == TRUE) {
706                 g_free(provider->element.ipv4.pac);
707                 provider->element.ipv4.pac = g_strdup(value);
708                 __connman_service_set_proxy_autoconfig(provider->vpn_service,
709                                                                         value);
710         } else if (g_str_equal(key, "DNS") == TRUE) {
711                 g_free(provider->dns);
712                 provider->dns = g_strdup(value);
713         } else if (g_str_equal(key, "Domain") == TRUE) {
714                 g_free(provider->domain);
715                 provider->domain = g_strdup(value);
716         }
717
718         return connman_element_set_string(&provider->element, key, value);
719 }
720
721 const char *connman_provider_get_string(struct connman_provider *provider,
722                                                         const char *key)
723 {
724         DBG("provider %p key %s", provider, key);
725
726         if (g_str_equal(key, "Type") == TRUE)
727                 return provider->type;
728         else if (g_str_equal(key, "Name") == TRUE)
729                 return provider->name;
730
731         return connman_element_get_string(&provider->element, key);
732 }
733
734 void *connman_provider_get_data(struct connman_provider *provider)
735 {
736         return provider->driver_data;
737 }
738
739 void connman_provider_set_data(struct connman_provider *provider, void *data)
740 {
741         provider->driver_data = data;
742 }
743
744 void connman_provider_set_index(struct connman_provider *provider, int index)
745 {
746         struct connman_service *service = provider->vpn_service;
747         struct connman_ipconfig *ipconfig;
748
749         DBG("");
750
751         if (service == NULL)
752                 return;
753
754         ipconfig = __connman_service_get_ip4config(service);
755
756         if (ipconfig == NULL) {
757                 __connman_service_create_ip4config(service, index);
758
759                 ipconfig = __connman_service_get_ip4config(service);
760                 if (ipconfig == NULL) {
761                         DBG("Couldnt create ipconfig");
762                         goto done;
763                 }
764         }
765
766         connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_FIXED);
767         __connman_ipconfig_set_index(ipconfig, index);
768
769
770         ipconfig = __connman_service_get_ip6config(service);
771
772         if (ipconfig == NULL) {
773                 __connman_service_create_ip6config(service, index);
774
775                 ipconfig = __connman_service_get_ip6config(service);
776                 if (ipconfig == NULL) {
777                         DBG("Couldnt create ipconfig for IPv6");
778                         goto done;
779                 }
780         }
781
782         connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_OFF);
783         __connman_ipconfig_set_index(ipconfig, index);
784
785 done:
786         provider->element.index = index;
787 }
788
789 int connman_provider_get_index(struct connman_provider *provider)
790 {
791         return provider->element.index;
792 }
793
794 enum provider_route_type {
795         PROVIDER_ROUTE_TYPE_NONE = 0,
796         PROVIDER_ROUTE_TYPE_MASK = 1,
797         PROVIDER_ROUTE_TYPE_ADDR = 2,
798         PROVIDER_ROUTE_TYPE_GW   = 3,
799 };
800
801 static int route_env_parse(struct connman_provider *provider, const char *key,
802                                 int *family, unsigned long *idx,
803                                 enum provider_route_type *type)
804 {
805         char *end;
806         const char *start;
807
808         DBG("name %s", provider->name);
809
810         if (!strcmp(provider->type, "openvpn")) {
811                 if (g_str_has_prefix(key, "route_network_") == TRUE) {
812                         start = key + strlen("route_network_");
813                         *type = PROVIDER_ROUTE_TYPE_ADDR;
814                 } else if (g_str_has_prefix(key, "route_netmask_") == TRUE) {
815                         start = key + strlen("route_netmask_");
816                         *type = PROVIDER_ROUTE_TYPE_MASK;
817                 } else if (g_str_has_prefix(key, "route_gateway_") == TRUE) {
818                         start = key + strlen("route_gateway_");
819                         *type = PROVIDER_ROUTE_TYPE_GW;
820                 } else
821                         return -EINVAL;
822
823                 *family = AF_INET;
824                 *idx = g_ascii_strtoull(start, &end, 10);
825
826         } else if (!strcmp(provider->type, "openconnect")) {
827                 if (g_str_has_prefix(key, "CISCO_SPLIT_INC_") == TRUE) {
828                         *family = AF_INET;
829                         start = key + strlen("CISCO_SPLIT_INC_");
830                 } else if (g_str_has_prefix(key, "CISCO_IPV6_SPLIT_INC_") == TRUE) {
831                         *family = AF_INET6;
832                         start = key + strlen("CISCO_IPV6_SPLIT_INC_");
833                 } else
834                         return -EINVAL;
835
836                 *idx = g_ascii_strtoull(start, &end, 10);
837
838                 if (strncmp(end, "_ADDR", 5) == 0)
839                         *type = PROVIDER_ROUTE_TYPE_ADDR;
840                 else if (strncmp(end, "_MASK", 5) == 0)
841                         *type = PROVIDER_ROUTE_TYPE_MASK;
842                 else if (strncmp(end, "_MASKLEN", 8) == 0 &&
843                                 *family == AF_INET6) {
844                         *type = PROVIDER_ROUTE_TYPE_MASK;
845                 } else
846                         return -EINVAL;
847         }
848
849         return 0;
850 }
851
852 int connman_provider_append_route(struct connman_provider *provider,
853                                         const char *key, const char *value)
854 {
855         struct connman_route *route;
856         int ret, family = 0;
857         unsigned long idx = 0;
858         enum provider_route_type type = PROVIDER_ROUTE_TYPE_NONE;
859
860         DBG("key %s value %s", key, value);
861
862         ret = route_env_parse(provider, key, &family, &idx, &type);
863         if (ret < 0)
864                 return ret;
865
866         DBG("idx %lu family %d type %d", idx, family, type);
867
868         route = g_hash_table_lookup(provider->routes, GINT_TO_POINTER(idx));
869         if (route == NULL) {
870                 route = g_try_new0(struct connman_route, 1);
871                 if (route == NULL) {
872                         connman_error("out of memory");
873                         return -ENOMEM;
874                 }
875
876                 route->family = family;
877
878                 g_hash_table_replace(provider->routes, GINT_TO_POINTER(idx),
879                                                 route);
880         }
881
882         switch (type) {
883         case PROVIDER_ROUTE_TYPE_NONE:
884                 break;
885         case PROVIDER_ROUTE_TYPE_MASK:
886                 route->netmask = g_strdup(value);
887                 break;
888         case PROVIDER_ROUTE_TYPE_ADDR:
889                 route->host = g_strdup(value);
890                 break;
891         case PROVIDER_ROUTE_TYPE_GW:
892                 route->gateway = g_strdup(value);
893                 break;
894         }
895
896         return 0;
897 }
898
899 const char *connman_provider_get_driver_name(struct connman_provider *provider)
900 {
901         return provider->driver->name;
902 }
903
904 static gint compare_priority(gconstpointer a, gconstpointer b)
905 {
906         return 0;
907 }
908
909 static void clean_provider(gpointer key, gpointer value, gpointer user_data)
910 {
911         struct connman_provider *provider = value;
912
913         if (provider->driver != NULL && provider->driver->remove)
914                 provider->driver->remove(provider);
915 }
916
917 int connman_provider_driver_register(struct connman_provider_driver *driver)
918 {
919         DBG("driver %p name %s", driver, driver->name);
920
921         driver_list = g_slist_insert_sorted(driver_list, driver,
922                                                         compare_priority);
923         return 0;
924 }
925
926 void connman_provider_driver_unregister(struct connman_provider_driver *driver)
927 {
928         DBG("driver %p name %s", driver, driver->name);
929
930         driver_list = g_slist_remove(driver_list, driver);
931 }
932
933 static void provider_remove(gpointer key, gpointer value,
934                                                 gpointer user_data)
935 {
936         struct connman_provider *provider = value;
937
938         g_hash_table_remove(provider_hash, provider->identifier);
939 }
940
941 static void provider_offline_mode(connman_bool_t enabled)
942 {
943         DBG("enabled %d", enabled);
944
945         if (enabled == TRUE)
946                 g_hash_table_foreach(provider_hash, provider_remove, NULL);
947
948 }
949
950 static struct connman_notifier provider_notifier = {
951         .name                   = "provider",
952         .offline_mode           = provider_offline_mode,
953 };
954
955 int __connman_provider_init(void)
956 {
957         int err;
958
959         DBG("");
960
961         connection = connman_dbus_get_connection();
962
963         provider_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
964                                                 NULL, unregister_provider);
965
966         err = connman_notifier_register(&provider_notifier);
967         if (err < 0) {
968                 g_hash_table_destroy(provider_hash);
969                 dbus_connection_unref(connection);
970         }
971
972         return err;
973 }
974
975 void __connman_provider_cleanup(void)
976 {
977         DBG("");
978
979         connman_notifier_unregister(&provider_notifier);
980
981         g_hash_table_foreach(provider_hash, clean_provider, NULL);
982
983         g_hash_table_destroy(provider_hash);
984         provider_hash = NULL;
985
986         dbus_connection_unref(connection);
987 }