Add cleaner handling of IP address details
[platform/upstream/connman.git] / src / ipconfig.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2009  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 <net/if.h>
27 #include <net/if_arp.h>
28
29 #ifndef IFF_LOWER_UP
30 #define IFF_LOWER_UP    0x10000
31 #endif
32
33 #include <gdbus.h>
34
35 #include "connman.h"
36
37 struct connman_ipconfig {
38         gint refcount;
39         int index;
40
41         const struct connman_ipconfig_ops *ops;
42         void *ops_data;
43
44         enum connman_ipconfig_method method;
45         struct connman_ipaddress *address;
46 };
47
48 struct connman_ipdevice {
49         int index;
50         char *ifname;
51         unsigned short type;
52         unsigned int flags;
53
54         GSList *address_list;
55         char *gateway;
56
57         struct connman_ipconfig *config;
58         struct connman_ipconfig_driver *driver;
59 };
60
61 static GHashTable *ipdevice_hash = NULL;
62 static GList *ipconfig_list = NULL;
63
64 struct connman_ipaddress *connman_ipaddress_alloc(void)
65 {
66         struct connman_ipaddress *ipaddress;
67
68         ipaddress = g_try_new0(struct connman_ipaddress, 1);
69         if (ipaddress == NULL)
70                 return NULL;
71
72         return ipaddress;
73 }
74
75 void connman_ipaddress_free(struct connman_ipaddress *ipaddress)
76 {
77         g_free(ipaddress->broadcast);
78         g_free(ipaddress->peer);
79         g_free(ipaddress->local);
80         g_free(ipaddress);
81 }
82
83 void connman_ipaddress_copy(struct connman_ipaddress *ipaddress,
84                                         struct connman_ipaddress *source)
85 {
86         ipaddress->prefixlen = source->prefixlen;
87
88         g_free(ipaddress->local);
89         ipaddress->local = g_strdup(source->local);
90
91         g_free(ipaddress->peer);
92         ipaddress->peer = g_strdup(source->peer);
93
94         g_free(ipaddress->broadcast);
95         ipaddress->broadcast = g_strdup(source->broadcast);
96 }
97
98 static void free_address_list(struct connman_ipdevice *ipdevice)
99 {
100         GSList *list;
101
102         for (list = ipdevice->address_list; list; list = list->next) {
103                 struct connman_ipaddress *ipaddress = list->data;
104
105                 connman_ipaddress_free(ipaddress);
106                 list->data = NULL;
107         }
108
109         g_slist_free(ipdevice->address_list);
110         ipdevice->address_list = NULL;
111 }
112
113 static struct connman_ipaddress *find_ipaddress(struct connman_ipdevice *ipdevice,
114                                 unsigned char prefixlen, const char *local)
115 {
116         GSList *list;
117
118         for (list = ipdevice->address_list; list; list = list->next) {
119                 struct connman_ipaddress *ipaddress = list->data;
120
121                 if (g_strcmp0(ipaddress->local, local) == 0 &&
122                                         ipaddress->prefixlen == prefixlen)
123                         return ipaddress;
124         }
125
126         return NULL;
127 }
128
129 static const char *type2str(unsigned short type)
130 {
131         switch (type) {
132         case ARPHRD_ETHER:
133                 return "ETHER";
134         case ARPHRD_LOOPBACK:
135                 return "LOOPBACK";
136         case ARPHRD_PPP:
137                 return "PPP";
138         case ARPHRD_NONE:
139                 return "NONE";
140         case ARPHRD_VOID:
141                 return "VOID";
142         }
143
144         return "";
145 }
146
147 static const char *scope2str(unsigned char scope)
148 {
149         switch (scope) {
150         case 0:
151                 return "UNIVERSE";
152         case 253:
153                 return "LINK";
154         }
155
156         return "";
157 }
158
159 static void free_ipdevice(gpointer data)
160 {
161         struct connman_ipdevice *ipdevice = data;
162
163         connman_info("%s {remove} index %d", ipdevice->ifname,
164                                                         ipdevice->index);
165
166         if (ipdevice->config != NULL)
167                 connman_ipconfig_unref(ipdevice->config);
168
169         free_address_list(ipdevice);
170         g_free(ipdevice->gateway);
171
172         g_free(ipdevice->ifname);
173         g_free(ipdevice);
174 }
175
176 static GSList *driver_list = NULL;
177
178 static gint compare_priority(gconstpointer a, gconstpointer b)
179 {
180         const struct connman_ipconfig_driver *driver1 = a;
181         const struct connman_ipconfig_driver *driver2 = b;
182
183         return driver2->priority - driver1->priority;
184 }
185
186 /**
187  * connman_ipconfig_driver_register:
188  * @driver: IP configuration driver
189  *
190  * Register a new IP configuration driver
191  *
192  * Returns: %0 on success
193  */
194 int connman_ipconfig_driver_register(struct connman_ipconfig_driver *driver)
195 {
196         DBG("driver %p name %s", driver, driver->name);
197
198         driver_list = g_slist_insert_sorted(driver_list, driver,
199                                                         compare_priority);
200
201         return 0;
202 }
203
204 /**
205  * connman_ipconfig_driver_unregister:
206  * @driver: IP configuration driver
207  *
208  * Remove a previously registered IP configuration driver.
209  */
210 void connman_ipconfig_driver_unregister(struct connman_ipconfig_driver *driver)
211 {
212         DBG("driver %p name %s", driver, driver->name);
213
214         driver_list = g_slist_remove(driver_list, driver);
215 }
216
217 static void __connman_ipconfig_lower_up(struct connman_ipdevice *ipdevice)
218 {
219         GSList *list;
220
221         DBG("ipconfig %p", ipdevice->config);
222
223         if (ipdevice->config == NULL)
224                 return;
225
226         switch (ipdevice->config->method) {
227         case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
228         case CONNMAN_IPCONFIG_METHOD_IGNORE:
229         case CONNMAN_IPCONFIG_METHOD_STATIC:
230                 return;
231         case CONNMAN_IPCONFIG_METHOD_DHCP:
232                 break;
233         }
234
235         if (ipdevice->driver != NULL)
236                 return;
237
238         for (list = driver_list; list; list = list->next) {
239                 struct connman_ipconfig_driver *driver = list->data;
240
241                 if (driver->request(ipdevice->config) == 0) {
242                         ipdevice->driver = driver;
243                         break;
244                 }
245         }
246 }
247
248 static void __connman_ipconfig_lower_down(struct connman_ipdevice *ipdevice)
249 {
250         DBG("ipconfig %p", ipdevice->config);
251
252         if (ipdevice->config == NULL)
253                 return;
254
255         if (ipdevice->driver == NULL)
256                 return;
257
258         ipdevice->driver->release(ipdevice->config);
259 }
260
261 void __connman_ipconfig_newlink(int index, unsigned short type,
262                                                         unsigned int flags)
263 {
264         struct connman_ipdevice *ipdevice;
265         GList *list;
266         GString *str;
267         gboolean up = FALSE, down = FALSE;
268         gboolean lower_up = FALSE, lower_down = FALSE;
269
270         DBG("index %d", index);
271
272         ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(index));
273         if (ipdevice != NULL)
274                 goto update;
275
276         ipdevice = g_try_new0(struct connman_ipdevice, 1);
277         if (ipdevice == NULL)
278                 return;
279
280         ipdevice->index = index;
281         ipdevice->ifname = connman_inet_ifname(index);
282         ipdevice->type = type;
283
284         ipdevice->config = connman_ipconfig_create(index);
285         if (ipdevice->config == NULL) {
286                 g_free(ipdevice->ifname);
287                 g_free(ipdevice);
288                 return;
289         }
290
291         if (type == ARPHRD_ETHER)
292                 connman_ipconfig_set_method(ipdevice->config,
293                                         CONNMAN_IPCONFIG_METHOD_IGNORE);
294
295         g_hash_table_insert(ipdevice_hash, GINT_TO_POINTER(index), ipdevice);
296
297         connman_info("%s {create} index %d type %d <%s>", ipdevice->ifname,
298                                                 index, type, type2str(type));
299
300 update:
301         if (flags == ipdevice->flags)
302                 return;
303
304         if ((ipdevice->flags & IFF_UP) != (flags & IFF_UP)) {
305                 if (flags & IFF_UP)
306                         up = TRUE;
307                 else
308                         down = TRUE;
309         }
310
311         if ((ipdevice->flags & (IFF_RUNNING | IFF_LOWER_UP)) !=
312                                 (flags & (IFF_RUNNING | IFF_LOWER_UP))) {
313                 if ((flags & (IFF_RUNNING | IFF_LOWER_UP)) ==
314                                         (IFF_RUNNING | IFF_LOWER_UP))
315                         lower_up = TRUE;
316                 else if ((flags & (IFF_RUNNING | IFF_LOWER_UP)) == 0)
317                         lower_down = TRUE;
318         }
319
320         ipdevice->flags = flags;
321
322         str = g_string_new(NULL);
323         if (str == NULL)
324                 return;
325
326         if (flags & IFF_UP)
327                 g_string_append(str, "UP");
328         else
329                 g_string_append(str, "DOWN");
330
331         if (flags & IFF_RUNNING)
332                 g_string_append(str, ",RUNNING");
333
334         if (flags & IFF_LOWER_UP)
335                 g_string_append(str, ",LOWER_UP");
336
337         connman_info("%s {update} flags %u <%s>", ipdevice->ifname,
338                                                         flags, str->str);
339
340         g_string_free(str, TRUE);
341
342         for (list = g_list_first(ipconfig_list); list;
343                                                 list = g_list_next(list)) {
344                 struct connman_ipconfig *ipconfig = list->data;
345
346                 if (index != ipconfig->index)
347                         continue;
348
349                 if (ipconfig->ops) {
350                         if (up == TRUE && ipconfig->ops->up)
351                                 ipconfig->ops->up(ipconfig);
352                         if (lower_up == TRUE && ipconfig->ops->lower_up)
353                                 ipconfig->ops->lower_up(ipconfig);
354
355                         if (lower_down == TRUE && ipconfig->ops->lower_down)
356                                 ipconfig->ops->lower_down(ipconfig);
357                         if (down == TRUE && ipconfig->ops->down)
358                                 ipconfig->ops->down(ipconfig);
359                 }
360         }
361
362         if (lower_up)
363                 __connman_ipconfig_lower_up(ipdevice);
364         if (lower_down)
365                 __connman_ipconfig_lower_down(ipdevice);
366 }
367
368 void __connman_ipconfig_dellink(int index)
369 {
370         struct connman_ipdevice *ipdevice;
371         GList *list;
372
373         DBG("index %d", index);
374
375         ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(index));
376         if (ipdevice == NULL)
377                 return;
378
379         for (list = g_list_first(ipconfig_list); list;
380                                                 list = g_list_next(list)) {
381                 struct connman_ipconfig *ipconfig = list->data;
382
383                 if (index != ipconfig->index)
384                         continue;
385
386                 ipconfig->index = -1;
387
388                 if (ipconfig->ops && ipconfig->ops->lower_down)
389                         ipconfig->ops->lower_down(ipconfig);
390                 if (ipconfig->ops && ipconfig->ops->down)
391                         ipconfig->ops->down(ipconfig);
392         }
393
394         __connman_ipconfig_lower_down(ipdevice);
395
396         g_hash_table_remove(ipdevice_hash, GINT_TO_POINTER(index));
397 }
398
399 void __connman_ipconfig_newaddr(int index, const char *label,
400                                 unsigned char prefixlen, const char *address)
401 {
402         struct connman_ipdevice *ipdevice;
403         struct connman_ipaddress *ipaddress;
404
405         DBG("index %d", index);
406
407         ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(index));
408         if (ipdevice == NULL)
409                 return;
410
411         ipaddress = connman_ipaddress_alloc();
412         if (ipaddress == NULL)
413                 return;
414
415         ipaddress->prefixlen = prefixlen;
416         ipaddress->local = g_strdup(address);
417
418         ipdevice->address_list = g_slist_append(ipdevice->address_list,
419                                                                 ipaddress);
420
421         connman_info("%s {add} address %s/%u label %s", ipdevice->ifname,
422                                                 address, prefixlen, label);
423 }
424
425 void __connman_ipconfig_deladdr(int index, const char *label,
426                                 unsigned char prefixlen, const char *address)
427 {
428         struct connman_ipdevice *ipdevice;
429         struct connman_ipaddress *ipaddress;
430
431         DBG("index %d", index);
432
433         ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(index));
434         if (ipdevice == NULL)
435                 return;
436
437         ipaddress = find_ipaddress(ipdevice, prefixlen, address);
438         if (ipaddress == NULL)
439                 return;
440
441         ipdevice->address_list = g_slist_remove(ipdevice->address_list,
442                                                                 ipaddress);
443
444         connman_ipaddress_free(ipaddress);
445
446         connman_info("%s {del} address %s/%u label %s", ipdevice->ifname,
447                                                 address, prefixlen, label);
448 }
449
450 void __connman_ipconfig_newroute(int index, unsigned char scope,
451                                         const char *dst, const char *gateway)
452 {
453         struct connman_ipdevice *ipdevice;
454
455         DBG("index %d", index);
456
457         ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(index));
458         if (ipdevice == NULL)
459                 return;
460
461         if (scope == 0 && g_strcmp0(dst, "0.0.0.0") == 0) {
462                 g_free(ipdevice->gateway);
463                 ipdevice->gateway = g_strdup(gateway);
464         }
465
466         connman_info("%s {add} route %s gw %s scope %u <%s>",
467                                         ipdevice->ifname, dst, gateway,
468                                                 scope, scope2str(scope));
469 }
470
471 void __connman_ipconfig_delroute(int index, unsigned char scope,
472                                         const char *dst, const char *gateway)
473 {
474         struct connman_ipdevice *ipdevice;
475
476         DBG("index %d", index);
477
478         ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(index));
479         if (ipdevice == NULL)
480                 return;
481
482         if (scope == 0 && g_strcmp0(dst, "0.0.0.0") == 0) {
483                 g_free(ipdevice->gateway);
484                 ipdevice->gateway = NULL;
485         }
486
487         connman_info("%s {del} route %s gw %s scope %u <%s>",
488                                         ipdevice->ifname, dst, gateway,
489                                                 scope, scope2str(scope));
490 }
491
492 void __connman_ipconfig_foreach(void (*function) (int index, void *user_data),
493                                                         void *user_data)
494 {
495         GList *list, *keys;
496
497         keys = g_hash_table_get_keys(ipdevice_hash);
498         if (keys == NULL)
499                 return;
500
501         for (list = g_list_first(keys); list; list = g_list_next(list)) {
502                 int index = GPOINTER_TO_INT(list->data);
503
504                 function(index, user_data);
505         }
506
507         g_list_free(keys);
508 }
509
510 unsigned short __connman_ipconfig_get_type(int index)
511 {
512         struct connman_ipdevice *ipdevice;
513
514         ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(index));
515         if (ipdevice == NULL)
516                 return ARPHRD_VOID;
517
518         return ipdevice->type;
519 }
520
521 unsigned int __connman_ipconfig_get_flags(int index)
522 {
523         struct connman_ipdevice *ipdevice;
524
525         ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(index));
526         if (ipdevice == NULL)
527                 return 0;
528
529         return ipdevice->flags;
530 }
531
532 const char *__connman_ipconfig_get_gateway(int index)
533 {
534         struct connman_ipdevice *ipdevice;
535
536         ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(index));
537         if (ipdevice == NULL)
538                 return NULL;
539
540         return ipdevice->gateway;
541 }
542
543 /**
544  * connman_ipconfig_create:
545  *
546  * Allocate a new ipconfig structure.
547  *
548  * Returns: a newly-allocated #connman_ipconfig structure
549  */
550 struct connman_ipconfig *connman_ipconfig_create(int index)
551 {
552         struct connman_ipconfig *ipconfig;
553
554         DBG("index %d", index);
555
556         ipconfig = g_try_new0(struct connman_ipconfig, 1);
557         if (ipconfig == NULL)
558                 return NULL;
559
560         ipconfig->refcount = 1;
561
562         ipconfig->index = index;
563
564         ipconfig->address = connman_ipaddress_alloc();
565         if (ipconfig->address == NULL) {
566                 g_free(ipconfig);
567                 return NULL;
568         }
569
570         DBG("ipconfig %p", ipconfig);
571
572         ipconfig_list = g_list_append(ipconfig_list, ipconfig);
573
574         return ipconfig;
575 }
576
577 /**
578  * connman_ipconfig_ref:
579  * @ipconfig: ipconfig structure
580  *
581  * Increase reference counter of ipconfig
582  */
583 struct connman_ipconfig *connman_ipconfig_ref(struct connman_ipconfig *ipconfig)
584 {
585         g_atomic_int_inc(&ipconfig->refcount);
586
587         return ipconfig;
588 }
589
590 /**
591  * connman_ipconfig_unref:
592  * @ipconfig: ipconfig structure
593  *
594  * Decrease reference counter of ipconfig
595  */
596 void connman_ipconfig_unref(struct connman_ipconfig *ipconfig)
597 {
598         if (g_atomic_int_dec_and_test(&ipconfig->refcount) == TRUE) {
599                 ipconfig_list = g_list_remove(ipconfig_list, ipconfig);
600
601                 connman_ipaddress_free(ipconfig->address);
602                 g_free(ipconfig);
603         }
604 }
605
606 /**
607  * connman_ipconfig_get_data:
608  * @ipconfig: ipconfig structure
609  *
610  * Get private data pointer
611  */
612 void *connman_ipconfig_get_data(struct connman_ipconfig *ipconfig)
613 {
614         return ipconfig->ops_data;
615 }
616
617 /**
618  * connman_ipconfig_set_data:
619  * @ipconfig: ipconfig structure
620  * @data: data pointer
621  *
622  * Set private data pointer
623  */
624 void connman_ipconfig_set_data(struct connman_ipconfig *ipconfig, void *data)
625 {
626         ipconfig->ops_data = data;
627 }
628
629 /**
630  * connman_ipconfig_get_index:
631  * @ipconfig: ipconfig structure
632  *
633  * Get interface index
634  */
635 int connman_ipconfig_get_index(struct connman_ipconfig *ipconfig)
636 {
637         return ipconfig->index;
638 }
639
640 /**
641  * connman_ipconfig_get_ifname:
642  * @ipconfig: ipconfig structure
643  *
644  * Get interface name
645  */
646 const char *connman_ipconfig_get_ifname(struct connman_ipconfig *ipconfig)
647 {
648         struct connman_ipdevice *ipdevice;
649
650         if (ipconfig->index < 0)
651                 return NULL;
652
653         ipdevice = g_hash_table_lookup(ipdevice_hash,
654                                         GINT_TO_POINTER(ipconfig->index));
655         if (ipdevice == NULL)
656                 return NULL;
657
658         return ipdevice->ifname;
659 }
660
661 /**
662  * connman_ipconfig_set_ops:
663  * @ipconfig: ipconfig structure
664  * @ops: operation callbacks
665  *
666  * Set the operation callbacks
667  */
668 void connman_ipconfig_set_ops(struct connman_ipconfig *ipconfig,
669                                 const struct connman_ipconfig_ops *ops)
670 {
671         ipconfig->ops = ops;
672 }
673
674 /**
675  * connman_ipconfig_set_method:
676  * @ipconfig: ipconfig structure
677  * @method: configuration method
678  *
679  * Set the configuration method
680  */
681 int connman_ipconfig_set_method(struct connman_ipconfig *ipconfig,
682                                         enum connman_ipconfig_method method)
683 {
684         ipconfig->method = method;
685
686         return 0;
687 }
688
689 /**
690  * connman_ipconfig_bind:
691  * @ipconfig: ipconfig structure
692  * @ipaddress: ipaddress structure
693  *
694  * Bind IP address details to configuration
695  */
696 void connman_ipconfig_bind(struct connman_ipconfig *ipconfig,
697                                         struct connman_ipaddress *ipaddress)
698 {
699         connman_ipaddress_copy(ipconfig->address, ipaddress);
700 }
701
702 const char *__connman_ipconfig_method2string(enum connman_ipconfig_method method)
703 {
704         switch (method) {
705         case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
706                 break;
707         case CONNMAN_IPCONFIG_METHOD_IGNORE:
708                 return "ignore";
709         case CONNMAN_IPCONFIG_METHOD_STATIC:
710                 return "static";
711         case CONNMAN_IPCONFIG_METHOD_DHCP:
712                 return "dhcp";
713         }
714
715         return NULL;
716 }
717
718 enum connman_ipconfig_method __connman_ipconfig_string2method(const char *method)
719 {
720         if (g_strcmp0(method, "ignore") == 0)
721                 return CONNMAN_IPCONFIG_METHOD_IGNORE;
722         else if (g_strcmp0(method, "static") == 0)
723                 return CONNMAN_IPCONFIG_METHOD_STATIC;
724         else if (g_strcmp0(method, "dhcp") == 0)
725                 return CONNMAN_IPCONFIG_METHOD_DHCP;
726         else
727                 return CONNMAN_IPCONFIG_METHOD_UNKNOWN;
728 }
729
730 static void append_variant(DBusMessageIter *iter, const char *prefix,
731                                         const char *key, int type, void *val)
732 {
733         char *str;
734
735         if (prefix == NULL) {
736                 connman_dbus_dict_append_variant(iter, key, type, val);
737                 return;
738         }
739
740         str = g_strdup_printf("%s%s", prefix, key);
741         if (str != NULL)
742                 connman_dbus_dict_append_variant(iter, str, type, val);
743
744         g_free(str);
745 }
746
747 void __connman_ipconfig_append_ipv4(struct connman_ipconfig *ipconfig,
748                                 DBusMessageIter *iter, const char *prefix)
749 {
750         const char *str;
751
752         str = __connman_ipconfig_method2string(ipconfig->method);
753         if (str == NULL)
754                 return;
755
756         append_variant(iter, prefix, "Method", DBUS_TYPE_STRING, &str);
757 }
758
759 int __connman_ipconfig_set_ipv4(struct connman_ipconfig *ipconfig,
760                                 const char *key, DBusMessageIter *value)
761 {
762         int type = dbus_message_iter_get_arg_type(value);
763
764         DBG("ipconfig %p key %s type %d", ipconfig, key, type);
765
766         if (g_strcmp0(key, "Method") == 0) {
767                 const char *method;
768
769                 if (type != DBUS_TYPE_STRING)
770                         return -EINVAL;
771
772                 dbus_message_iter_get_basic(value, &method);
773
774                 ipconfig->method = __connman_ipconfig_string2method(method);
775         } else
776                 return -EINVAL;
777
778         return 0;
779 }
780
781 int __connman_ipconfig_load(struct connman_ipconfig *ipconfig,
782                 GKeyFile *keyfile, const char *identifier, const char *prefix)
783 {
784         DBG("ipconfig %p identifier %s", ipconfig, identifier);
785
786         return 0;
787 }
788
789 int __connman_ipconfig_save(struct connman_ipconfig *ipconfig,
790                 GKeyFile *keyfile, const char *identifier, const char *prefix)
791 {
792         DBG("ipconfig %p identifier %s", ipconfig, identifier);
793
794         return 0;
795 }
796
797 int __connman_ipconfig_init(void)
798 {
799         DBG("");
800
801         ipdevice_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
802                                                         NULL, free_ipdevice);
803
804         return 0;
805 }
806
807 void __connman_ipconfig_cleanup(void)
808 {
809         DBG("");
810
811         g_hash_table_destroy(ipdevice_hash);
812         ipdevice_hash = NULL;
813 }