provider: Factorize VPN routing environment variables parsing
[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 static void unregister_provider(gpointer data)
397 {
398         struct connman_provider *provider = data;
399         struct connman_service *service = provider->vpn_service;
400
401         DBG("provider %p", provider);
402
403         provider->vpn_service = NULL;
404         __connman_service_put(service);
405
406         connman_element_unregister(&provider->element);
407         connman_provider_unref(provider);
408 }
409
410 static void provider_destruct(struct connman_element *element)
411 {
412         struct connman_provider *provider = element->private;
413
414         DBG("provider %p", provider);
415
416         g_free(provider->name);
417         g_free(provider->type);
418         g_free(provider->domain);
419         g_free(provider->identifier);
420         g_free(provider->dns);
421         g_hash_table_destroy(provider->routes);
422 }
423
424 static void destroy_route(gpointer user_data)
425 {
426         struct connman_route *route = user_data;
427
428         g_free(route->host);
429         g_free(route->netmask);
430         g_free(route->gateway);
431         g_free(route);
432 }
433
434 static void provider_initialize(struct connman_provider *provider)
435 {
436         DBG("provider %p", provider);
437
438         __connman_element_initialize(&provider->element);
439
440         provider->element.private = provider;
441         provider->element.destruct = provider_destruct;
442
443         provider->element.ipv4.address = NULL;
444         provider->element.ipv4.netmask = NULL;
445         provider->element.ipv4.gateway = NULL;
446         provider->element.ipv4.broadcast = NULL;
447         provider->element.ipv4.pac = NULL;
448
449         provider->name = NULL;
450         provider->type = NULL;
451         provider->dns = NULL;
452         provider->domain = NULL;
453         provider->identifier = NULL;
454         provider->routes = g_hash_table_new_full(g_direct_hash, g_direct_equal,
455                                         NULL, destroy_route);
456 }
457
458 static struct connman_provider *connman_provider_new(void)
459 {
460         struct connman_provider *provider;
461
462         provider = g_try_new0(struct connman_provider, 1);
463         if (provider == NULL)
464                 return NULL;
465
466         DBG("provider %p", provider);
467         provider_initialize(provider);
468
469         return provider;
470 }
471
472 static struct connman_provider *connman_provider_get(const char *identifier)
473 {
474         struct connman_provider *provider;
475
476         provider = g_hash_table_lookup(provider_hash, identifier);
477         if (provider != NULL)
478                 return provider;
479
480         provider = connman_provider_new();
481         if (provider == NULL)
482                 return NULL;
483
484         DBG("provider %p", provider);
485
486         provider->identifier = g_strdup(identifier);
487
488         g_hash_table_insert(provider_hash, provider->identifier, provider);
489
490         provider->element.name = g_strdup(identifier);
491         connman_element_register(&provider->element, NULL);
492
493         return provider;
494 }
495
496 static void provider_dbus_ident(char *ident)
497 {
498         int i, len = strlen(ident);
499
500         for (i = 0; i < len; i++) {
501                 if (ident[i] >= '0' && ident[i] <= '9')
502                         continue;
503                 if (ident[i] >= 'a' && ident[i] <= 'z')
504                         continue;
505                 if (ident[i] >= 'A' && ident[i] <= 'Z')
506                         continue;
507                 ident[i] = '_';
508         }
509 }
510
511 int __connman_provider_create_and_connect(DBusMessage *msg)
512 {
513         struct connman_provider *provider;
514         DBusMessageIter iter, array;
515         const char *type = NULL, *name = NULL, *service_path = NULL;
516         const char *host = NULL, *domain = NULL;
517         char *ident;
518         gboolean created = FALSE;
519         int err;
520
521         dbus_message_iter_init(msg, &iter);
522         dbus_message_iter_recurse(&iter, &array);
523
524         while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
525                 DBusMessageIter entry, value;
526                 const char *key;
527
528                 dbus_message_iter_recurse(&array, &entry);
529                 dbus_message_iter_get_basic(&entry, &key);
530
531                 dbus_message_iter_next(&entry);
532                 dbus_message_iter_recurse(&entry, &value);
533
534                 switch (dbus_message_iter_get_arg_type(&value)) {
535                 case DBUS_TYPE_STRING:
536                         if (g_str_equal(key, "Type") == TRUE)
537                                 dbus_message_iter_get_basic(&value, &type);
538                         else if (g_str_equal(key, "Name") == TRUE)
539                                 dbus_message_iter_get_basic(&value, &name);
540                         else if (g_str_equal(key, "Host") == TRUE)
541                                 dbus_message_iter_get_basic(&value, &host);
542                         else if (g_str_equal(key, "VPN.Domain") == TRUE)
543                                 dbus_message_iter_get_basic(&value, &domain);
544                         break;
545                 }
546
547                 dbus_message_iter_next(&array);
548         }
549
550         if (host == NULL || domain == NULL) {
551                 err = -EINVAL;
552                 goto failed;
553         }
554
555         DBG("Type %s name %s", type, name);
556
557         if (type == NULL || name == NULL) {
558                 err = -EOPNOTSUPP;
559                 goto failed;
560         }
561
562         ident = g_strdup_printf("%s_%s", host, domain);
563         provider_dbus_ident(ident);
564
565         DBG("ident %s", ident);
566
567         provider = connman_provider_lookup(ident);
568
569         if (provider == NULL) {
570                 created = TRUE;
571                 provider = connman_provider_get(ident);
572                 if (provider) {
573                         provider->host = g_strdup(host);
574                         provider->domain = g_strdup(domain);
575                         provider->name = g_strdup(name);
576                         provider->type = g_strdup(type);
577                 }
578         }
579
580         if (provider == NULL) {
581                 DBG("can not create provider");
582                 err = -EOPNOTSUPP;
583                 goto failed;
584         }
585         dbus_message_iter_init(msg, &iter);
586         dbus_message_iter_recurse(&iter, &array);
587
588         while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
589                 DBusMessageIter entry, value;
590                 const char *key, *str;
591
592                 dbus_message_iter_recurse(&array, &entry);
593                 dbus_message_iter_get_basic(&entry, &key);
594
595                 dbus_message_iter_next(&entry);
596                 dbus_message_iter_recurse(&entry, &value);
597
598                 switch (dbus_message_iter_get_arg_type(&value)) {
599                 case DBUS_TYPE_STRING:
600                         dbus_message_iter_get_basic(&value, &str);
601                         connman_provider_set_string(provider, key, str);
602                         break;
603                 }
604
605                 dbus_message_iter_next(&array);
606         }
607
608         g_free(ident);
609
610         if (provider == NULL) {
611                 err = -EOPNOTSUPP;
612                 goto failed;
613         }
614
615         if (created == TRUE)
616                 provider_probe(provider);
617
618         if (provider->vpn_service == NULL)
619                 provider->vpn_service =
620                         __connman_service_create_from_provider(provider);
621         if (provider->vpn_service == NULL) {
622                 err = -EOPNOTSUPP;
623                 goto failed;
624         }
625
626         err = __connman_service_connect(provider->vpn_service);
627         if (err < 0 && err != -EINPROGRESS)
628                 goto failed;
629
630         service_path = __connman_service_get_path(provider->vpn_service);
631         g_dbus_send_reply(connection, msg,
632                                 DBUS_TYPE_OBJECT_PATH, &service_path,
633                                                         DBUS_TYPE_INVALID);
634         return 0;
635
636 failed:
637         if (provider != NULL && created == TRUE) {
638                 DBG("can not connect delete provider");
639                 connman_provider_unref(provider);
640
641                 if (provider->vpn_service != NULL) {
642                         __connman_service_put(provider->vpn_service);
643                         provider->vpn_service = NULL;
644                 }
645         }
646
647         return err;
648 }
649
650 const char * __connman_provider_get_ident(struct connman_provider *provider)
651 {
652         if (provider == NULL)
653                 return NULL;
654
655         return provider->identifier;
656 }
657
658 int connman_provider_set_string(struct connman_provider *provider,
659                                         const char *key, const char *value)
660 {
661         DBG("provider %p key %s value %s", provider, key, value);
662
663         if (g_str_equal(key, "Type") == TRUE) {
664                 g_free(provider->type);
665                 provider->type = g_strdup(value);
666         } else if (g_str_equal(key, "Name") == TRUE) {
667                 g_free(provider->name);
668                 provider->name = g_strdup(value);
669         } else if (g_str_equal(key, "Gateway") == TRUE) {
670                 g_free(provider->element.ipv4.gateway);
671                 provider->element.ipv4.gateway = g_strdup(value);
672         } else if (g_str_equal(key, "Address") == TRUE) {
673                 g_free(provider->element.ipv4.address);
674                 provider->element.ipv4.address = g_strdup(value);
675         } else if (g_str_equal(key, "Peer") == TRUE) {
676                 g_free(provider->element.ipv4.peer);
677                 provider->element.ipv4.peer = g_strdup(value);
678         } else if (g_str_equal(key, "Netmask") == TRUE) {
679                 g_free(provider->element.ipv4.netmask);
680                 provider->element.ipv4.netmask = g_strdup(value);
681         } else if (g_str_equal(key, "PAC") == TRUE) {
682                 g_free(provider->element.ipv4.pac);
683                 provider->element.ipv4.pac = g_strdup(value);
684                 __connman_service_set_proxy_autoconfig(provider->vpn_service,
685                                                                         value);
686         } else if (g_str_equal(key, "DNS") == TRUE) {
687                 g_free(provider->dns);
688                 provider->dns = g_strdup(value);
689         } else if (g_str_equal(key, "Domain") == TRUE) {
690                 g_free(provider->domain);
691                 provider->domain = g_strdup(value);
692         }
693
694         return connman_element_set_string(&provider->element, key, value);
695 }
696
697 const char *connman_provider_get_string(struct connman_provider *provider,
698                                                         const char *key)
699 {
700         DBG("provider %p key %s", provider, key);
701
702         if (g_str_equal(key, "Type") == TRUE)
703                 return provider->type;
704         else if (g_str_equal(key, "Name") == TRUE)
705                 return provider->name;
706
707         return connman_element_get_string(&provider->element, key);
708 }
709
710 void *connman_provider_get_data(struct connman_provider *provider)
711 {
712         return provider->driver_data;
713 }
714
715 void connman_provider_set_data(struct connman_provider *provider, void *data)
716 {
717         provider->driver_data = data;
718 }
719
720 void connman_provider_set_index(struct connman_provider *provider, int index)
721 {
722         struct connman_service *service = provider->vpn_service;
723         struct connman_ipconfig *ipconfig;
724
725         DBG("");
726
727         if (service == NULL)
728                 return;
729
730         ipconfig = __connman_service_get_ip4config(service);
731
732         if (ipconfig == NULL) {
733                 __connman_service_create_ip4config(service, index);
734
735                 ipconfig = __connman_service_get_ip4config(service);
736                 if (ipconfig == NULL) {
737                         DBG("Couldnt create ipconfig");
738                         goto done;
739                 }
740         }
741
742         connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_FIXED);
743         __connman_ipconfig_set_index(ipconfig, index);
744
745
746         ipconfig = __connman_service_get_ip6config(service);
747
748         if (ipconfig == NULL) {
749                 __connman_service_create_ip6config(service, index);
750
751                 ipconfig = __connman_service_get_ip6config(service);
752                 if (ipconfig == NULL) {
753                         DBG("Couldnt create ipconfig for IPv6");
754                         goto done;
755                 }
756         }
757
758         connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_OFF);
759         __connman_ipconfig_set_index(ipconfig, index);
760
761 done:
762         provider->element.index = index;
763 }
764
765 int connman_provider_get_index(struct connman_provider *provider)
766 {
767         return provider->element.index;
768 }
769
770 enum provider_route_type {
771         PROVIDER_ROUTE_TYPE_NONE = 0,
772         PROVIDER_ROUTE_TYPE_MASK = 1,
773         PROVIDER_ROUTE_TYPE_ADDR = 2,
774         PROVIDER_ROUTE_TYPE_GW   = 3,
775 };
776
777 static int route_env_parse(struct connman_provider *provider, const char *key,
778                                 int *family, unsigned long *idx,
779                                 enum provider_route_type *type)
780 {
781         char *end;
782         const char *start;
783
784         DBG("name %s", provider->name);
785
786         if (!strcmp(provider->name, "openvpn")) {
787                 if (g_str_has_prefix(key, "route_network_") == TRUE) {
788                         start = key + strlen("route_network_");
789                         *type = PROVIDER_ROUTE_TYPE_ADDR;
790                 } else if (g_str_has_prefix(key, "route_netmask_") == TRUE) {
791                         start = key + strlen("route_netmask_");
792                         *type = PROVIDER_ROUTE_TYPE_MASK;
793                 } else if (g_str_has_prefix(key, "route_gateway_") == TRUE) {
794                         start = key + strlen("route_gateway_");
795                         *type = PROVIDER_ROUTE_TYPE_GW;
796                 } else
797                         return -EINVAL;
798
799                 *family = AF_INET;
800                 *idx = g_ascii_strtoull(start, &end, 10);
801
802         } else if (!strcmp(provider->name, "openconnect")) {
803                 if (g_str_has_prefix(key, "CISCO_SPLIT_INC_") == TRUE) {
804                         *family = AF_INET;
805                         start = key + strlen("CISCO_SPLIT_INC_");
806                 } else if (g_str_has_prefix(key, "CISCO_IPV6_SPLIT_INC_") == TRUE) {
807                         *family = AF_INET6;
808                         start = key + strlen("CISCO_IPV6_SPLIT_INC_");
809                 } else
810                         return -EINVAL;
811
812                 *idx = g_ascii_strtoull(start, &end, 10);
813
814                 if (strncmp(end, "_ADDR", 5) == 0)
815                         *type = PROVIDER_ROUTE_TYPE_ADDR;
816                 else if (strncmp(end, "_MASK", 5) == 0)
817                         *type = PROVIDER_ROUTE_TYPE_MASK;
818                 else if (strncmp(end, "_MASKLEN", 8) == 0 &&
819                                 *family == AF_INET6) {
820                         *type = PROVIDER_ROUTE_TYPE_MASK;
821                 } else
822                         return -EINVAL;
823         }
824
825         return 0;
826 }
827
828 int connman_provider_append_route(struct connman_provider *provider,
829                                         const char *key, const char *value)
830 {
831         struct connman_route *route;
832         int ret, family = 0;
833         unsigned long idx = 0;
834         enum provider_route_type type = PROVIDER_ROUTE_TYPE_NONE;
835
836         DBG("key %s value %s", key, value);
837
838         ret = route_env_parse(provider, key, &family, &idx, &type);
839         if (ret < 0)
840                 return ret;
841
842         DBG("idx %lu family %d type %d", idx, family, type);
843
844         route = g_hash_table_lookup(provider->routes, GINT_TO_POINTER(idx));
845         if (route == NULL) {
846                 route = g_try_new0(struct connman_route, 1);
847                 if (route == NULL) {
848                         connman_error("out of memory");
849                         return -ENOMEM;
850                 }
851
852                 route->family = family;
853
854                 g_hash_table_replace(provider->routes, GINT_TO_POINTER(idx),
855                                                 route);
856         }
857
858         switch (type) {
859         case PROVIDER_ROUTE_TYPE_NONE:
860                 break;
861         case PROVIDER_ROUTE_TYPE_MASK:
862                 route->netmask = g_strdup(value);
863                 break;
864         case PROVIDER_ROUTE_TYPE_ADDR:
865                 route->host = g_strdup(value);
866                 break;
867         case PROVIDER_ROUTE_TYPE_GW:
868                 route->gateway = g_strdup(value);
869                 break;
870         }
871
872         return 0;
873 }
874
875 const char *connman_provider_get_driver_name(struct connman_provider *provider)
876 {
877         return provider->driver->name;
878 }
879
880 static gint compare_priority(gconstpointer a, gconstpointer b)
881 {
882         return 0;
883 }
884
885 static void clean_provider(gpointer key, gpointer value, gpointer user_data)
886 {
887         struct connman_provider *provider = value;
888
889         if (provider->driver != NULL && provider->driver->remove)
890                 provider->driver->remove(provider);
891 }
892
893 int connman_provider_driver_register(struct connman_provider_driver *driver)
894 {
895         DBG("driver %p name %s", driver, driver->name);
896
897         driver_list = g_slist_insert_sorted(driver_list, driver,
898                                                         compare_priority);
899         return 0;
900 }
901
902 void connman_provider_driver_unregister(struct connman_provider_driver *driver)
903 {
904         DBG("driver %p name %s", driver, driver->name);
905
906         driver_list = g_slist_remove(driver_list, driver);
907 }
908
909 static void provider_remove(gpointer key, gpointer value,
910                                                 gpointer user_data)
911 {
912         struct connman_provider *provider = value;
913
914         g_hash_table_remove(provider_hash, provider->identifier);
915 }
916
917 static void provider_offline_mode(connman_bool_t enabled)
918 {
919         DBG("enabled %d", enabled);
920
921         if (enabled == TRUE)
922                 g_hash_table_foreach(provider_hash, provider_remove, NULL);
923
924 }
925
926 static struct connman_notifier provider_notifier = {
927         .name                   = "provider",
928         .offline_mode           = provider_offline_mode,
929 };
930
931 int __connman_provider_init(void)
932 {
933         int err;
934
935         DBG("");
936
937         connection = connman_dbus_get_connection();
938
939         provider_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
940                                                 NULL, unregister_provider);
941
942         err = connman_notifier_register(&provider_notifier);
943         if (err < 0) {
944                 g_hash_table_destroy(provider_hash);
945                 dbus_connection_unref(connection);
946         }
947
948         return err;
949 }
950
951 void __connman_provider_cleanup(void)
952 {
953         DBG("");
954
955         connman_notifier_unregister(&provider_notifier);
956
957         g_hash_table_foreach(provider_hash, clean_provider, NULL);
958
959         g_hash_table_destroy(provider_hash);
960         provider_hash = NULL;
961
962         dbus_connection_unref(connection);
963 }