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