[connman] Added Tizen Wi-Fi Mesh
[platform/upstream/connman.git] / src / mesh.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *
6  *  Copyright (C) 2017 Samsung Electronics Co., Ltd.
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License version 2 as
10  *  published by the Free Software Foundation.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20  *  02110-1301  USA
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <errno.h>
28 #include <gdbus.h>
29
30 #include <connman/storage.h>
31 #include "connman.h"
32 #include <sys/types.h>
33 #include <dirent.h>
34 #include <linux/if_bridge.h>
35 #include <sys/ioctl.h>
36 #include <net/if.h>
37 #include <unistd.h>
38 #include "mesh-netlink.h"
39
40 static DBusConnection *connection;
41
42 static GHashTable *mesh_table;
43 static GHashTable *connected_peer_table;
44 static GHashTable *disconnected_peer_table;
45
46 static struct connman_mesh_driver *mesh_driver;
47 static struct connman_mesh_eth_driver *mesh_eth_driver;
48
49 char *mesh_ifname;
50 char *bridge_interface;
51 static unsigned int mesh_autoconnect_timeout;
52 static bool is_mesh_if_created;
53 bool eth_if_bridged;
54 mesh_nl80211_global *nl80211_global;
55
56 struct connman_mesh {
57         int refcount;
58         char *identifier;
59         char *name;
60         char *path;
61         char *address;
62         char *interface_addr;
63         enum connman_mesh_security security;
64         char *passphrase;
65         enum connman_mesh_state state;
66         enum connman_mesh_peer_type peer_type;
67         enum connman_mesh_peer_disconnect_reason disconnect_reason;
68         uint16_t frequency;
69         uint8_t strength;
70         bool registered;
71         bool favorite;
72         DBusMessage *pending;
73         int index;
74         int br_index;
75         uint16_t ieee80211w;
76         struct connman_ipconfig *ipconfig;
77 };
78
79 struct connman_mesh_connected_peer {
80         char *peer_address;
81 };
82
83 struct connman_mesh_disconnected_peer {
84         char *peer_address;
85         enum connman_mesh_peer_disconnect_reason disconnect_reason;
86 };
87
88 struct connman_mesh_change_peer_data {
89         DBusMessage *pending;
90         char *peer_address;
91         enum connman_mesh_peer_status status;
92 };
93
94 static void mesh_dhcp_callback(struct connman_ipconfig *ipconfig,
95                                 struct connman_network *network, bool success, gpointer data);
96
97 static void mesh_free(gpointer data)
98 {
99         struct connman_mesh *mesh = data;
100
101         connman_mesh_unregister(mesh);
102
103         g_free(mesh->path);
104
105         if (mesh->state == CONNMAN_MESH_STATE_CONFIGURATION ||
106                         mesh->state == CONNMAN_MESH_STATE_READY)
107                 __connman_dhcp_stop(mesh->ipconfig);
108
109         if (mesh->ipconfig) {
110                 __connman_ipconfig_set_ops(mesh->ipconfig, NULL);
111                 __connman_ipconfig_set_data(mesh->ipconfig, NULL);
112                 __connman_ipconfig_unref(mesh->ipconfig);
113                 mesh->ipconfig = NULL;
114         }
115         g_free(mesh->identifier);
116         g_free(mesh->name);
117         g_free(mesh->passphrase);
118         g_free(mesh->interface_addr);
119         g_free(mesh->address);
120         g_free(mesh);
121 }
122
123 static void mesh_connected_peer_free(gpointer data)
124 {
125         struct connman_mesh_connected_peer *peer = data;
126
127         g_free(peer->peer_address);
128         g_free(peer);
129 }
130
131 static void mesh_disconnected_peer_free(gpointer data)
132 {
133         struct connman_mesh_disconnected_peer *peer = data;
134
135         g_free(peer->peer_address);
136         g_free(peer);
137 }
138
139 static void __mesh_load_and_create_network(char *mesh_id)
140 {
141         GKeyFile *keyfile;
142         GString *str;
143         struct connman_mesh *connman_mesh;
144         gchar *name, *passphrase, *peer_type;
145         char *identifier, *group, *address;
146         const char *sec_type, *mesh_ifname;
147         int freq, i;
148
149         keyfile = connman_storage_load_service(mesh_id);
150         if (!keyfile) {
151                 DBG("Mesh profile doesn't exist");
152                 return;
153         }
154
155         peer_type = g_key_file_get_string(keyfile, mesh_id, "PeerType", NULL);
156         if (g_strcmp0(peer_type, "created")) {
157                 DBG("Mesh Profile was not created");
158                 goto done;
159         }
160
161         name = g_key_file_get_string(keyfile, mesh_id, "Name", NULL);
162         if (!name) {
163                 DBG("Failed to get Mesh Profile Name");
164                 goto done;
165         }
166
167         passphrase = g_key_file_get_string(keyfile, mesh_id, "Passphrase", NULL);
168         if (passphrase)
169                 sec_type = "sae";
170         else
171                 sec_type = "none";
172
173         freq = g_key_file_get_integer(keyfile, mesh_id, "Frequency", NULL);
174
175         mesh_ifname = connman_mesh_get_interface_name();
176
177         str = g_string_sized_new((strlen(name) * 2) + 24);
178
179         for (i = 0; name[i]; i++)
180                 g_string_append_printf(str, "%02x", name[i]);
181
182         g_string_append_printf(str, "_mesh");
183
184         if (g_strcmp0(sec_type, "none") == 0)
185                 g_string_append_printf(str, "_none");
186         else if (g_strcmp0(sec_type, "sae") == 0)
187                 g_string_append_printf(str, "_sae");
188
189         group = g_string_free(str, FALSE);
190
191         identifier = connman_inet_ifaddr(mesh_ifname);
192         address = connman_inet_ifname2addr(mesh_ifname);
193
194         connman_mesh = connman_mesh_create(identifier, group);
195         connman_mesh_set_name(connman_mesh, name);
196         connman_mesh_set_address(connman_mesh, address);
197         connman_mesh_set_security(connman_mesh, sec_type);
198         connman_mesh_set_frequency(connman_mesh, freq);
199         connman_mesh_set_index(connman_mesh, connman_inet_ifindex(mesh_ifname));
200         connman_mesh_set_peer_type(connman_mesh, CONNMAN_MESH_PEER_TYPE_CREATED);
201
202         connman_mesh_register(connman_mesh);
203         g_free(group);
204         g_free(identifier);
205         g_free(address);
206 done:
207         g_key_file_free(keyfile);
208 }
209
210 static bool is_connected(struct connman_mesh *mesh)
211 {
212         if (mesh->state == CONNMAN_MESH_STATE_READY)
213                 return true;
214
215         return false;
216 }
217
218 static void mesh_peer_dhcp_refresh(gpointer key, gpointer value,
219                                                                    gpointer user_data)
220 {
221         struct connman_mesh *mesh = value;
222
223         DBG("mesh %p state %d", mesh, mesh->state);
224
225         if (is_connected(mesh))
226                 __connman_mesh_dhcp_start(mesh->ipconfig, mesh_dhcp_callback, mesh);
227 }
228
229 int connman_inet_set_stp(int stp)
230 {
231         int sk, err = 0;
232         struct ifreq ifr;
233         unsigned long args[4];
234
235         if (!bridge_interface)
236                 return -EINVAL;
237
238         sk = socket(AF_LOCAL, SOCK_STREAM, 0);
239         if (sk < 0) {
240                 err = -errno;
241                 goto out;
242         }
243
244         args[0] = BRCTL_SET_BRIDGE_STP_STATE;
245         args[1] = stp;
246         args[2] = args[3] = 0;
247         memset(&ifr, 0, sizeof(struct ifreq));
248         strncpy(ifr.ifr_name, bridge_interface, sizeof(ifr.ifr_name) - 1);
249         ifr.ifr_data = (char *)args;
250
251         if (ioctl(sk, SIOCDEVPRIVATE, &ifr) < 0)
252                 err = -errno;
253
254         close(sk);
255
256 out:
257         if (err < 0)
258                 DBG("Set STP Failed error %s", strerror(-err));
259
260         return err;
261 }
262
263 int __connman_mesh_set_stp_gate_announce(bool gate_announce, int hwmp_rootmode,
264                                                 int stp)
265 {
266         int err;
267
268         if (!mesh_ifname)
269                 return -EINVAL;
270
271         err = connman_inet_set_stp(stp);
272         if (err < 0)
273                 return err;
274
275         err = __connman_mesh_netlink_set_gate_announce(nl80211_global,
276                                                 connman_inet_ifindex(mesh_ifname), gate_announce,
277                                                 hwmp_rootmode);
278
279         return err;
280 }
281
282 void __connman_mesh_add_ethernet_to_bridge(void)
283 {
284         if (is_mesh_if_created) {
285                 DBG("");
286                 mesh_eth_driver->add_to_bridge(bridge_interface);
287                 eth_if_bridged = true;
288                 g_hash_table_foreach(mesh_table, mesh_peer_dhcp_refresh, NULL);
289                 connman_inet_set_stp(1);
290                 __connman_mesh_netlink_set_gate_announce(nl80211_global,
291                                                         connman_inet_ifindex(mesh_ifname), true,
292                                                         MESH_HWMP_ROOTMODE_RANN);
293         }
294 }
295
296 void __connman_mesh_remove_ethernet_from_bridge(void)
297 {
298         if (eth_if_bridged) {
299                 DBG("");
300                 mesh_eth_driver->remove_from_bridge(bridge_interface);
301                 eth_if_bridged = false;
302                 g_hash_table_foreach(mesh_table, mesh_peer_dhcp_refresh, NULL);
303                 connman_inet_set_stp(0);
304                 __connman_mesh_netlink_set_gate_announce(nl80211_global,
305                                                         connman_inet_ifindex(mesh_ifname), false,
306                                                         MESH_HWMP_ROOTMODE_NO_ROOT);
307         }
308 }
309
310 int connman_mesh_notify_interface_create(bool success)
311 {
312         int ret;
313         int index;
314         const char *error = NULL;
315         DIR *dir;
316         struct dirent *d;
317
318         if (!success) {
319                 error = "Operation Failed";
320                 goto done;
321         }
322
323         if (!bridge_interface) {
324                 DBG("Don't create bridge interface");
325                 goto done;
326         }
327
328         DBG("Creating bridge [%s]", bridge_interface);
329
330         /* Create bridge interface */
331         ret = __connman_bridge_create(bridge_interface);
332         if (0 != ret) {
333                 DBG("Failed to create bridge [%s] : [%s]", bridge_interface,
334                                 strerror(-ret));
335                 error = "Bridge Creation";
336                 success = false;
337                 goto done;
338         }
339
340         /* Get Mesh Interface Index */
341         index = connman_inet_ifindex(mesh_ifname);
342         if (index < 0) {
343                 DBG("Failed to get interface index for %s", mesh_ifname);
344                 error = "Operation Failed";
345                 success = false;
346                 goto done;
347         }
348
349         /* Add mesh interface into bridge */
350         ret = connman_inet_add_to_bridge(index, bridge_interface);
351         if (0 != ret) {
352                 DBG("Failed to add interface[%s] into bridge[%s]", mesh_ifname,
353                                         bridge_interface);
354                 error = "Add Mesh into bridge";
355                 success = false;
356                 goto done;
357         }
358
359         if (__connman_technology_get_connected(CONNMAN_SERVICE_TYPE_ETHERNET)) {
360                 mesh_eth_driver->add_to_bridge(bridge_interface);
361                 eth_if_bridged = true;
362         }
363
364         index = connman_inet_ifindex(bridge_interface);
365         if (index < 0) {
366                 DBG("Failed to get interface index for %s", bridge_interface);
367                 error = "Operation Failed";
368                 success = false;
369                 goto done;
370         }
371
372         /* Make bridge interface UP */
373         ret = connman_inet_ifup(index);
374         if (0 != ret) {
375                 DBG("Failed to change bridge interface state");
376                 error = "Make bridge interface UP";
377                 success = false;
378         }
379
380 done:
381         if (success) {
382                 is_mesh_if_created = true;
383
384                 /* Load previously created mesh profiles */
385                 dir = opendir(STORAGEDIR);
386                 if (!dir) {
387                         DBG("Failed to open %s directory", STORAGEDIR);
388                         __connman_technology_mesh_interface_create_finished(
389                                 CONNMAN_SERVICE_TYPE_MESH, success, error);
390                         return 0;
391                 }
392
393                 while ((d = readdir(dir))) {
394                         if (g_str_has_prefix(d->d_name, "mesh_")) {
395                                 DBG("%s is a mesh profile", d->d_name);
396                                 __mesh_load_and_create_network(d->d_name);
397                                 __connman_mesh_auto_connect();
398                         }
399                 }
400
401                 closedir(dir);
402
403         } else {
404                 if (eth_if_bridged)
405                         mesh_eth_driver->remove_from_bridge(bridge_interface);
406
407                 __connman_bridge_disable(bridge_interface);
408
409                 __connman_bridge_remove(bridge_interface);
410
411                 mesh_driver->remove_interface(mesh_ifname);
412         }
413         __connman_technology_mesh_interface_create_finished(
414                             CONNMAN_SERVICE_TYPE_MESH, success, error);
415         return 0;
416 }
417
418 int __connman_mesh_add_virtual_interface(const char *ifname,
419                                                 const char *parent_ifname, const char *bridge_ifname)
420 {
421         int ret;
422
423         if (!ifname || !parent_ifname)
424                 return -EINVAL;
425
426         ret = mesh_driver->add_interface(ifname, parent_ifname);
427         if (ret != -EINPROGRESS) {
428                 DBG("Failed to add virtual mesh interface");
429                 return ret;
430         }
431
432         mesh_ifname = g_strdup(ifname);
433         bridge_interface = g_strdup(bridge_ifname);
434         DBG("Success adding virtual mesh interface");
435         return 0;
436 }
437
438 int connman_mesh_notify_interface_remove(bool success)
439 {
440         struct connman_device *device;
441         int index;
442         if (success) {
443                 g_free(mesh_ifname);
444                 mesh_ifname = NULL;
445                 g_hash_table_remove_all(mesh_table);
446                 is_mesh_if_created = false;
447
448                 if (eth_if_bridged) {
449                         if (bridge_interface)
450                                 mesh_eth_driver->remove_from_bridge(bridge_interface);
451
452                         device = __connman_device_find_device(
453                                                                         CONNMAN_SERVICE_TYPE_ETHERNET);
454                         if (device) {
455                                 index = connman_device_get_index(device);
456                                 connman_inet_ifup(index);
457                         }
458                         eth_if_bridged = false;
459                 }
460
461                 if (bridge_interface) {
462                         __connman_bridge_disable(bridge_interface);
463                         if (__connman_bridge_remove(bridge_interface))
464                                 DBG("Failed to remove bridge [%s]", bridge_interface);
465
466                         g_free(bridge_interface);
467                         bridge_interface = NULL;
468                 }
469         }
470
471         __connman_technology_mesh_interface_remove_finished(
472                                                 CONNMAN_SERVICE_TYPE_MESH, success);
473         return 0;
474 }
475
476 int __connman_mesh_remove_virtual_interface(const char *ifname)
477 {
478         int ret;
479         int index;
480
481         if (!ifname)
482                 return -EINVAL;
483
484         if (bridge_interface) {
485                 index = connman_inet_ifindex(mesh_ifname);
486                 if (index < 0) {
487                         DBG("Failed to get interface index for %s", mesh_ifname);
488                         return -EINVAL;
489                 }
490
491                 ret = connman_inet_remove_from_bridge(index, bridge_interface);
492                 if (0 != ret) {
493                         DBG("Failed to remove interface[%s] freom bridge[%s]", mesh_ifname,
494                                 bridge_interface);
495                         return -EINVAL;
496                 }
497
498                 if (eth_if_bridged)
499                         mesh_eth_driver->remove_from_bridge(bridge_interface);
500
501                 __connman_bridge_disable(bridge_interface);
502
503                 ret = __connman_bridge_remove(bridge_interface);
504                 if (0 != ret) {
505                         DBG("Failed to remove bridge [%s]", bridge_interface);
506                         return -EINVAL;
507                 }
508
509                 g_free(bridge_interface);
510                 bridge_interface = NULL;
511         }
512
513         ret = mesh_driver->remove_interface(ifname);
514         if (ret != -EINPROGRESS) {
515                 DBG("Failed to remove virtual mesh interface");
516                 return ret;
517         }
518
519         DBG("Success removing virtual mesh interface");
520         return 0;
521 }
522
523 const char *connman_mesh_get_interface_name(void)
524 {
525         return mesh_ifname;
526 }
527
528 bool connman_mesh_is_interface_created(void)
529 {
530         DBG("Mesh interface is %screated", is_mesh_if_created ? "" : "not ");
531         return is_mesh_if_created;
532 }
533
534 struct connman_mesh *connman_mesh_create(const char *interface_addr,
535                                           const char *identifier)
536 {
537         struct connman_mesh *mesh;
538
539         mesh = g_malloc0(sizeof(struct connman_mesh));
540         mesh->identifier = g_strdup_printf("mesh_%s_%s", interface_addr,
541                                            identifier);
542         mesh->interface_addr = g_strdup(interface_addr);
543         mesh->state = CONNMAN_MESH_STATE_IDLE;
544
545         mesh->refcount = 1;
546
547         return mesh;
548 }
549
550 void connman_mesh_set_name(struct connman_mesh *mesh, const char *name)
551 {
552         g_free(mesh->name);
553         mesh->name = g_strdup(name);
554 }
555
556 const char *connman_mesh_get_name(struct connman_mesh *mesh)
557 {
558         return mesh->name;
559 }
560
561 void connman_mesh_set_passphrase(struct connman_mesh *mesh,
562                                   const char *passphrase)
563 {
564         g_free(mesh->passphrase);
565         mesh->passphrase = g_strdup(passphrase);
566 }
567
568 const char *connman_mesh_get_passphrase(struct connman_mesh *mesh)
569 {
570         return mesh->passphrase;
571 }
572
573 void connman_mesh_set_address(struct connman_mesh *mesh, const char *address)
574 {
575         g_free(mesh->address);
576         mesh->address = g_strdup(address);
577 }
578
579 void connman_mesh_set_security(struct connman_mesh *mesh, const char *security)
580 {
581         if (!g_strcmp0(security, "none"))
582                 mesh->security = CONNMAN_MESH_SECURITY_NONE;
583         else if (!g_strcmp0(security, "sae"))
584                 mesh->security = CONNMAN_MESH_SECURITY_SAE;
585         else
586                 mesh->security = CONNMAN_MESH_SECURITY_UNKNOWN;
587 }
588
589 static const char *security2string(enum connman_mesh_security security)
590 {
591         switch (security) {
592         case CONNMAN_MESH_SECURITY_UNKNOWN:
593                 break;
594         case CONNMAN_MESH_SECURITY_NONE:
595                 return "none";
596         case CONNMAN_MESH_SECURITY_SAE:
597                 return "sae";
598         }
599
600         return NULL;
601 }
602
603 const char *connman_mesh_get_security(struct connman_mesh *mesh)
604 {
605         return security2string(mesh->security);
606 }
607
608 void connman_mesh_set_frequency(struct connman_mesh *mesh, uint16_t frequency)
609 {
610         mesh->frequency = frequency;
611 }
612
613 uint16_t connman_mesh_get_frequency(struct connman_mesh *mesh)
614 {
615         return mesh->frequency;
616 }
617
618 void connman_mesh_set_ieee80211w(struct connman_mesh *mesh, uint16_t ieee80211w)
619 {
620         mesh->ieee80211w = ieee80211w;
621 }
622
623 uint16_t connman_mesh_get_ieee80211w(struct connman_mesh *mesh)
624 {
625         return mesh->ieee80211w;
626 }
627
628 void connman_mesh_set_index(struct connman_mesh *mesh, int index)
629 {
630         mesh->index = index;
631
632         if (bridge_interface)
633                 mesh->br_index = connman_inet_ifindex(bridge_interface);
634 }
635
636 void connman_mesh_set_strength(struct connman_mesh *mesh, uint8_t strength)
637 {
638         mesh->strength = strength;
639 }
640
641 static const char *peertype2string(enum connman_mesh_peer_type type)
642 {
643         switch (type) {
644         case CONNMAN_MESH_PEER_TYPE_CREATED:
645                 return "created";
646         case CONNMAN_MESH_PEER_TYPE_DISCOVERED:
647                 return "discovered";
648         }
649
650         return NULL;
651 }
652
653 void connman_mesh_set_peer_type(struct connman_mesh *mesh,
654                                                                 enum connman_mesh_peer_type type)
655 {
656         mesh->peer_type = type;
657 }
658
659 static const char *state2string(enum connman_mesh_state state)
660 {
661         switch (state) {
662         case CONNMAN_MESH_STATE_UNKNOWN:
663                 break;
664         case CONNMAN_MESH_STATE_IDLE:
665                 return "idle";
666         case CONNMAN_MESH_STATE_ASSOCIATION:
667                 return "association";
668         case CONNMAN_MESH_STATE_CONFIGURATION:
669                 return "configuration";
670         case CONNMAN_MESH_STATE_READY:
671                 return "ready";
672         case CONNMAN_MESH_STATE_DISCONNECT:
673                 return "disconnect";
674         case CONNMAN_MESH_STATE_FAILURE:
675                 return "failure";
676         }
677
678         return NULL;
679 }
680
681 static enum connman_mesh_peer_disconnect_reason convert_to_disconnect_reason(
682                                                                         int reason)
683 {
684         switch (reason) {
685         case 3:
686                 return CONNMAN_MESH_DEAUTH_LEAVING;
687         case 52:
688                 return CONNMAN_MESH_PEERING_CANCELLED;
689         case 53:
690                 return CONNMAN_MESH_MAX_PEERS;
691         case 54:
692                 return CONNMAN_MESH_CONFIG_POLICY_VIOLATION;
693         case 55:
694                 return CONNMAN_MESH_CLOSE_RCVD;
695         case 56:
696                 return CONNMAN_MESH_MAX_RETRIES;
697         case 57:
698                 return CONNMAN_MESH_CONFIRM_TIMEOUT;
699         case 58:
700                 return CONNMAN_MESH_INVALID_GTK;
701         case 59:
702                 return CONNMAN_MESH_INCONSISTENT_PARAMS;
703         case 60:
704                 return CONNMAN_MESH_INVALID_SECURITY_CAP;
705         }
706
707         return CONNMAN_MESH_REASON_UNKNOWN;
708 }
709
710 void connman_mesh_peer_set_disconnect_reason(struct connman_mesh *mesh,
711                                                 int disconnect_reason)
712 {
713         mesh->disconnect_reason = convert_to_disconnect_reason(disconnect_reason);
714 }
715
716 static bool is_connecting(struct connman_mesh *mesh)
717 {
718         if (mesh->state == CONNMAN_MESH_STATE_ASSOCIATION ||
719                         mesh->state == CONNMAN_MESH_STATE_CONFIGURATION)
720                 return true;
721
722         return false;
723 }
724
725 static int mesh_load(struct connman_mesh *mesh)
726 {
727         GKeyFile *keyfile;
728         bool favorite;
729         GError *error = NULL;
730         gchar *str;
731
732         keyfile = connman_storage_load_service(mesh->identifier);
733         if (!keyfile) {
734                 DBG("Mesh profile is new");
735                 return -EIO;
736         }
737
738         favorite = g_key_file_get_boolean(keyfile,
739                                 mesh->identifier, "Favorite", &error);
740
741         if (!error)
742                 mesh->favorite = favorite;
743
744         g_clear_error(&error);
745
746         str = g_key_file_get_string(keyfile, mesh->identifier, "Passphrase", NULL);
747
748         if (str) {
749                 g_free(mesh->passphrase);
750                 mesh->passphrase = str;
751         }
752
753         return 0;
754 }
755
756 static int mesh_save(struct connman_mesh *mesh)
757 {
758         GKeyFile *keyfile;
759
760         keyfile = __connman_storage_open_service(mesh->identifier);
761         if (!keyfile)
762                 return -EIO;
763
764         g_key_file_set_string(keyfile, mesh->identifier, "Name", mesh->name);
765         g_key_file_set_integer(keyfile, mesh->identifier, "Frequency",
766                                                                 mesh->frequency);
767         g_key_file_set_boolean(keyfile, mesh->identifier, "Favorite",
768                                                                 mesh->favorite);
769
770         if (mesh->passphrase)
771                 g_key_file_set_string(keyfile, mesh->identifier, "Passphrase",
772                                                           mesh->passphrase);
773
774         g_key_file_set_string(keyfile, mesh->identifier, "PeerType",
775                                                                 peertype2string(mesh->peer_type));
776
777         __connman_storage_save_service(keyfile, mesh->identifier);
778
779         g_key_file_free(keyfile);
780
781         return 0;
782 }
783
784 static void reply_pending(struct connman_mesh *mesh, int error)
785 {
786         if (!mesh->pending)
787                 return;
788
789         connman_dbus_reply_pending(mesh->pending, error, NULL);
790         mesh->pending = NULL;
791 }
792
793 static void state_changed(struct connman_mesh *mesh)
794 {
795         const char *state;
796
797         state = state2string(mesh->state);
798         if (!state)
799                 return;
800
801         connman_dbus_property_changed_basic(mesh->path,
802                                          CONNMAN_MESH_INTERFACE, "State",
803                                          DBUS_TYPE_STRING, &state);
804 }
805
806 static void mesh_dhcp_callback(struct connman_ipconfig *ipconfig,
807                                 struct connman_network *network, bool success, gpointer data)
808 {
809         struct connman_mesh *mesh = data;
810         int err;
811
812         if (!success)
813                 goto error;
814
815         err = __connman_ipconfig_address_add(ipconfig);
816         if (err < 0)
817                 goto error;
818
819         return;
820
821 error:
822         connman_mesh_peer_set_state(mesh, CONNMAN_MESH_STATE_FAILURE);
823 }
824
825 static int mesh_start_dhcp_client(struct connman_mesh *mesh)
826 {
827         DBG("");
828
829         __connman_ipconfig_enable(mesh->ipconfig);
830
831         return __connman_mesh_dhcp_start(mesh->ipconfig, mesh_dhcp_callback, mesh);
832 }
833
834 static void mesh_remove_connected_peer(gpointer key, gpointer value,
835                                                 gpointer user_data)
836 {
837         struct connman_mesh_connected_peer *peer = value;
838
839         DBG("Remove Peer %s", peer->peer_address);
840         g_hash_table_remove(connected_peer_table, key);
841 }
842
843 static void mesh_remove_disconnected_peer(gpointer key, gpointer value,
844                                                 gpointer user_data)
845 {
846         struct connman_mesh_disconnected_peer *peer = value;
847
848         DBG("Remove Peer %s", peer->peer_address);
849         g_hash_table_remove(disconnected_peer_table, key);
850 }
851
852 int connman_mesh_peer_set_state(struct connman_mesh *mesh,
853                                                 enum connman_mesh_state new_state)
854 {
855         enum connman_mesh_state old_state = mesh->state;
856
857         DBG("mesh peer %s old state %s new state %s", mesh->name,
858                                         state2string(old_state), state2string(new_state));
859
860         if (old_state == new_state)
861                 return -EALREADY;
862
863         switch (new_state) {
864         case CONNMAN_MESH_STATE_UNKNOWN:
865                 return -EINVAL;
866         case CONNMAN_MESH_STATE_IDLE:
867         case CONNMAN_MESH_STATE_ASSOCIATION:
868                 break;
869         case CONNMAN_MESH_STATE_CONFIGURATION:
870                 /* Start Link Local IP Address */
871                 mesh_start_dhcp_client(mesh);
872                 break;
873         case CONNMAN_MESH_STATE_READY:
874                 reply_pending(mesh, 0);
875                 mesh->favorite = true;
876                 __connman_notifier_connect(CONNMAN_SERVICE_TYPE_MESH);
877
878                 /* Set Gate Announce option */
879                 if (eth_if_bridged) {
880                         connman_inet_set_stp(1);
881                         __connman_mesh_netlink_set_gate_announce(nl80211_global,
882                                                                 connman_inet_ifindex(mesh_ifname), true,
883                                                                 MESH_HWMP_ROOTMODE_RANN);
884                 }
885
886                 mesh_save(mesh);
887                 break;
888         case CONNMAN_MESH_STATE_DISCONNECT:
889                 __connman_dhcp_stop(mesh->ipconfig);
890                 g_hash_table_foreach(connected_peer_table, mesh_remove_connected_peer,
891                                                          NULL);
892                 g_hash_table_foreach(disconnected_peer_table,
893                                                          mesh_remove_disconnected_peer, NULL);
894                 __connman_notifier_disconnect(CONNMAN_SERVICE_TYPE_MESH);
895                 break;
896         case CONNMAN_MESH_STATE_FAILURE:
897                 reply_pending(mesh, ECONNABORTED);
898                 break;
899         }
900
901         mesh->state = new_state;
902         state_changed(mesh);
903
904         return 0;
905 }
906
907 bool connman_mesh_peer_is_connected_state(struct connman_mesh *mesh)
908 {
909         switch (mesh->state) {
910         case CONNMAN_MESH_STATE_UNKNOWN:
911         case CONNMAN_MESH_STATE_IDLE:
912         case CONNMAN_MESH_STATE_ASSOCIATION:
913         case CONNMAN_MESH_STATE_CONFIGURATION:
914         case CONNMAN_MESH_STATE_DISCONNECT:
915         case CONNMAN_MESH_STATE_FAILURE:
916                 break;
917         case CONNMAN_MESH_STATE_READY:
918                 return true;
919         }
920
921         return false;
922 }
923
924 struct connman_mesh *connman_get_connected_mesh_from_name(char *name)
925 {
926         GList *list, *start;
927
928         list = g_hash_table_get_values(mesh_table);
929         start = list;
930         for (; list; list = list->next) {
931                 struct connman_mesh *mesh = list->data;
932
933                 if (!g_strcmp0(mesh->name, name) &&
934                                         mesh->state == CONNMAN_MESH_STATE_READY) {
935                         g_list_free(start);
936                         return mesh;
937                 }
938         }
939
940         g_list_free(start);
941
942         return NULL;
943 }
944
945 struct connman_mesh *connman_get_connecting_mesh_from_name(char *name)
946 {
947         GList *list, *start;
948
949         list = g_hash_table_get_values(mesh_table);
950         start = list;
951         for (; list; list = list->next) {
952                 struct connman_mesh *mesh = list->data;
953
954                 if (!g_strcmp0(mesh->name, name) && is_connecting(mesh)) {
955                         g_list_free(start);
956                         return mesh;
957                 }
958         }
959
960         g_list_free(start);
961
962         return NULL;
963 }
964
965 static void mesh_append_ethernet(DBusMessageIter *iter, void *user_data)
966 {
967         struct connman_mesh *mesh = user_data;
968
969         if (mesh->ipconfig)
970                 __connman_ipconfig_append_ethernet(mesh->ipconfig, iter);
971 }
972
973 static void mesh_append_ipv4(DBusMessageIter *iter, void *user_data)
974 {
975         struct connman_mesh *mesh = user_data;
976
977         if (!is_connected(mesh))
978                 return;
979
980         if (mesh->ipconfig)
981                 __connman_ipconfig_append_ipv4(mesh->ipconfig, iter);
982 }
983
984 static void mesh_append_ipv4config(DBusMessageIter *iter, void *user_data)
985 {
986         struct connman_mesh *mesh = user_data;
987
988         if (mesh->ipconfig)
989                 __connman_ipconfig_append_ipv4config(mesh->ipconfig, iter);
990 }
991
992 static void append_properties(DBusMessageIter *iter, struct connman_mesh *mesh)
993 {
994         const char *state = state2string(mesh->state);
995         const char *security = security2string(mesh->security);
996         const char *peer_type = peertype2string(mesh->peer_type);
997         const char *type = "mesh";
998         DBusMessageIter dict;
999
1000         connman_dbus_dict_open(iter, &dict);
1001
1002         connman_dbus_dict_append_basic(&dict, "Type", DBUS_TYPE_STRING, &type);
1003         connman_dbus_dict_append_basic(&dict, "Name",
1004                                         DBUS_TYPE_STRING, &mesh->name);
1005         connman_dbus_dict_append_basic(&dict, "BSSID",
1006                                         DBUS_TYPE_STRING, &mesh->address);
1007         connman_dbus_dict_append_basic(&dict, "State", DBUS_TYPE_STRING, &state);
1008         if (security)
1009                 connman_dbus_dict_append_basic(&dict, "Security",
1010                                                                 DBUS_TYPE_STRING, &security);
1011         connman_dbus_dict_append_basic(&dict, "Frequency",
1012                                         DBUS_TYPE_UINT16, &mesh->frequency);
1013         connman_dbus_dict_append_basic(&dict, "Favorite",
1014                                         DBUS_TYPE_BOOLEAN, &mesh->favorite);
1015         connman_dbus_dict_append_basic(&dict, "Strength",
1016                                         DBUS_TYPE_BYTE, &mesh->strength);
1017         connman_dbus_dict_append_basic(&dict, "PeerType",
1018                                         DBUS_TYPE_STRING, &peer_type);
1019         connman_dbus_dict_append_basic(&dict, "DisconnectReason",
1020                                         DBUS_TYPE_INT32, &mesh->disconnect_reason);
1021
1022         connman_dbus_dict_append_dict(&dict, "Ethernet", mesh_append_ethernet,
1023                                         mesh);
1024
1025         connman_dbus_dict_append_dict(&dict, "IPv4", mesh_append_ipv4, mesh);
1026
1027         connman_dbus_dict_append_dict(&dict, "IPv4.Configuration",
1028                                         mesh_append_ipv4config, mesh);
1029
1030         connman_dbus_dict_close(iter, &dict);
1031 }
1032
1033 static void append_mesh_peer_struct(gpointer key, gpointer value,
1034                                                 gpointer user_data)
1035 {
1036         DBusMessageIter *array = user_data;
1037         struct connman_mesh *mesh = value;
1038         DBusMessageIter entry;
1039
1040         DBG("Mesh Peer path %s", mesh->path);
1041         dbus_message_iter_open_container(array, DBUS_TYPE_STRUCT,
1042                                                         NULL, &entry);
1043         dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH,
1044                                                         &mesh->path);
1045         append_properties(&entry, mesh);
1046         dbus_message_iter_close_container(array, &entry);
1047 }
1048
1049 void __connman_mesh_peer_list_struct(DBusMessageIter *array)
1050 {
1051         g_hash_table_foreach(mesh_table, append_mesh_peer_struct, array);
1052 }
1053
1054 static DBusMessage *get_mesh_peer_properties(DBusConnection *conn,
1055                                                 DBusMessage *msg, void *data)
1056 {
1057         struct connman_mesh *mesh = data;
1058         DBusMessageIter dict;
1059         DBusMessage *reply;
1060
1061         reply = dbus_message_new_method_return(msg);
1062         if (!reply)
1063                 return NULL;
1064
1065         dbus_message_iter_init_append(reply, &dict);
1066         append_properties(&dict, mesh);
1067
1068         return reply;
1069 }
1070
1071 static void append_mesh_disconnected_peer_struct(gpointer key, gpointer value,
1072                                                 gpointer user_data)
1073 {
1074         DBusMessageIter *array = user_data;
1075         struct connman_mesh_disconnected_peer *peer = value;
1076         DBusMessageIter entry;
1077         DBusMessageIter dict;
1078
1079         dbus_message_iter_open_container(array, DBUS_TYPE_STRUCT,
1080                                                         NULL, &entry);
1081
1082         connman_dbus_dict_open(&entry, &dict);
1083
1084         connman_dbus_dict_append_basic(&dict, "PeerAddress",
1085                                         DBUS_TYPE_STRING, &peer->peer_address);
1086
1087         connman_dbus_dict_append_basic(&dict, "DisconnectReason",
1088                                         DBUS_TYPE_INT32, &peer->disconnect_reason);
1089
1090         connman_dbus_dict_close(&entry, &dict);
1091         dbus_message_iter_close_container(array, &entry);
1092 }
1093
1094 void __connman_mesh_disconnected_peer_list_struct(DBusMessageIter *array)
1095 {
1096         g_hash_table_foreach(disconnected_peer_table,
1097                                                 append_mesh_disconnected_peer_struct, array);
1098 }
1099
1100 static void append_mesh_connected_peer_struct(gpointer key, gpointer value,
1101                                                 gpointer user_data)
1102 {
1103         DBusMessageIter *array = user_data;
1104         struct connman_mesh_connected_peer *peer = value;
1105         DBusMessageIter entry;
1106         DBusMessageIter dict;
1107
1108         dbus_message_iter_open_container(array, DBUS_TYPE_STRUCT,
1109                                                         NULL, &entry);
1110
1111         connman_dbus_dict_open(&entry, &dict);
1112
1113         connman_dbus_dict_append_basic(&dict, "PeerAddress",
1114                                         DBUS_TYPE_STRING, &peer->peer_address);
1115
1116         connman_dbus_dict_close(&entry, &dict);
1117         dbus_message_iter_close_container(array, &entry);
1118 }
1119
1120 void __connman_mesh_connected_peer_list_struct(DBusMessageIter *array)
1121 {
1122         g_hash_table_foreach(connected_peer_table,
1123                                                 append_mesh_connected_peer_struct, array);
1124 }
1125
1126 int connman_mesh_add_connected_peer(const char *peer_address)
1127 {
1128         struct connman_mesh_connected_peer *peer;
1129         struct connman_mesh_connected_peer *temp_peer;
1130         struct connman_mesh_disconnected_peer *disconn_peer;
1131
1132         temp_peer = g_hash_table_lookup(connected_peer_table, peer_address);
1133
1134         if (temp_peer) {
1135                 DBG("Mesh Peer %s is already connected", peer_address);
1136                 return 0;
1137         }
1138
1139         peer = g_malloc0(sizeof(struct connman_mesh_connected_peer));
1140         peer->peer_address = g_strdup(peer_address);
1141         DBG("Peer %s", peer->peer_address);
1142
1143         g_hash_table_insert(connected_peer_table, peer->peer_address, peer);
1144
1145         /* Remove from disconnected Peer Table */
1146         disconn_peer = g_hash_table_lookup(disconnected_peer_table, peer_address);
1147         if (!disconn_peer) {
1148                 DBG("Peer %s was never disconnected", peer_address);
1149                 goto done;
1150         }
1151
1152         g_hash_table_remove(disconnected_peer_table, peer_address);
1153 done:
1154         return 0;
1155 }
1156
1157 int connman_mesh_remove_connected_peer(const char *peer_address, int reason)
1158 {
1159         struct connman_mesh_connected_peer *peer;
1160         struct connman_mesh_disconnected_peer *disconn_peer;
1161
1162         peer = g_hash_table_lookup(connected_peer_table, peer_address);
1163
1164         if (!peer) {
1165                 DBG("Peer %s not connected", peer_address);
1166                 return 0;
1167         }
1168
1169         g_hash_table_remove(connected_peer_table, peer_address);
1170
1171         /* Add to Disconnected Peer Table */
1172         disconn_peer = g_malloc0(sizeof(struct connman_mesh_disconnected_peer));
1173         disconn_peer->peer_address = g_strdup(peer_address);
1174         disconn_peer->disconnect_reason = convert_to_disconnect_reason(reason);
1175
1176         g_hash_table_insert(disconnected_peer_table, disconn_peer->peer_address,
1177                                                 disconn_peer);
1178
1179         DBG("Mesh Peer %s removed due to reason %d", peer_address, reason);
1180         return 0;
1181 }
1182
1183 static void __mesh_change_peer_status_cb(int result, void *user_data)
1184 {
1185         struct connman_mesh_change_peer_data *data = user_data;
1186
1187         DBG("Status %d Peer Address %s result %d", data->status, data->peer_address,
1188                                                                                 result);
1189
1190         connman_dbus_reply_pending(data->pending, -result, NULL);
1191
1192         data->pending = NULL;
1193         g_free(data->peer_address);
1194         g_free(data);
1195 }
1196
1197 int __connman_mesh_change_peer_status(DBusMessage *msg,
1198                                                                           const char *peer_address,
1199                                                                           enum connman_mesh_peer_status status)
1200 {
1201         struct connman_mesh_connected_peer *conn_peer;
1202         struct connman_mesh_disconnected_peer *disconn_peer;
1203         int err = -ENOTSUP;
1204         struct connman_mesh_change_peer_data *data;
1205
1206         switch (status) {
1207         case CONNMAN_MESH_PEER_ADD:
1208                 conn_peer = g_hash_table_lookup(connected_peer_table, peer_address);
1209
1210                 if (conn_peer) {
1211                         DBG("Peer %s already connected", peer_address);
1212                         return -EEXIST;
1213                 }
1214
1215                 break;
1216
1217         case CONNMAN_MESH_PEER_REMOVE:
1218                 disconn_peer = g_hash_table_lookup(disconnected_peer_table,
1219                                                                         peer_address);
1220
1221                 if (disconn_peer) {
1222                         DBG("Peer %s already disconnected", peer_address);
1223                         return -EEXIST;
1224                 }
1225
1226                 break;
1227
1228         default:
1229                 DBG("Invalid Status type");
1230                 return err;
1231         }
1232
1233         if (mesh_driver->change_peer_status) {
1234                 data = g_try_malloc0(sizeof(struct connman_mesh_disconnected_peer));
1235                 if (data == NULL) {
1236                         DBG("Memory allocation failed");
1237                         return -ENOMEM;
1238                 }
1239
1240                 data->pending = dbus_message_ref(msg);
1241                 data->peer_address = g_strdup(peer_address);
1242                 data->status = status;
1243
1244                 err = mesh_driver->change_peer_status(peer_address, status,
1245                                                                 __mesh_change_peer_status_cb, data);
1246
1247                 if (err < 0) {
1248                         dbus_message_unref(data->pending);
1249                         g_free(data->peer_address);
1250                         g_free(data);
1251                 }
1252         }
1253
1254         return err;
1255 }
1256
1257 static int mesh_peer_connect(struct connman_mesh *mesh)
1258 {
1259         int err = -ENOTSUP;
1260         if (mesh_driver->connect)
1261                 err = mesh_driver->connect(mesh);
1262
1263         /* Reset Disconnect Reason */
1264         mesh->disconnect_reason = CONNMAN_MESH_REASON_UNKNOWN;
1265         return err;
1266 }
1267
1268 static DBusMessage *connect_mesh_peer(DBusConnection *conn,
1269                                         DBusMessage *msg, void *user_data)
1270 {
1271         struct connman_mesh *mesh = user_data;
1272         int err;
1273
1274         DBG("mesh %p", mesh);
1275
1276         if (mesh->state == CONNMAN_MESH_STATE_READY) {
1277                 DBG("mesh %s already connected", mesh->name);
1278                 return __connman_error_already_exists(msg);
1279         }
1280
1281         if (mesh->pending)
1282                 return __connman_error_in_progress(msg);
1283
1284         mesh->pending = dbus_message_ref(msg);
1285
1286         err = mesh_peer_connect(mesh);
1287         if (err == -EINPROGRESS)
1288                 return NULL;
1289
1290         if (err < 0) {
1291                 dbus_message_unref(mesh->pending);
1292                 mesh->pending = NULL;
1293                 return __connman_error_failed(msg, -err);
1294         }
1295
1296         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1297 }
1298
1299 static void auto_connect_mesh_peer(gpointer key, gpointer value,
1300                                                 gpointer user_data)
1301 {
1302         bool *conn_started = user_data;
1303         struct connman_mesh *mesh = value;
1304         int err;
1305
1306         if (*conn_started)
1307                 return;
1308
1309         if (!mesh->favorite || mesh->state != CONNMAN_MESH_STATE_IDLE)
1310                 return;
1311
1312         err = mesh_peer_connect(mesh);
1313         if (err == -EINPROGRESS)
1314                 *conn_started = 1;
1315 }
1316
1317 static gboolean run_mesh_auto_connect(gpointer data)
1318 {
1319         bool conn_started;
1320
1321         mesh_autoconnect_timeout = 0;
1322         DBG("");
1323
1324         conn_started = false;
1325         g_hash_table_foreach(mesh_table, auto_connect_mesh_peer, &conn_started);
1326         return FALSE;
1327 }
1328
1329 void __connman_mesh_auto_connect(void)
1330 {
1331         DBG("");
1332
1333         if (mesh_autoconnect_timeout != 0)
1334                 return;
1335
1336         mesh_autoconnect_timeout = g_idle_add(run_mesh_auto_connect, NULL);
1337 }
1338
1339 static void mesh_peer_up(struct connman_ipconfig *ipconfig, const char *ifname)
1340 {
1341         DBG("%s up", ifname);
1342 }
1343
1344 static void mesh_peer_down(struct connman_ipconfig *ipconfig,
1345                                                 const char *ifname)
1346 {
1347         DBG("%s down", ifname);
1348 }
1349
1350 static void mesh_peer_lower_up(struct connman_ipconfig *ipconfig,
1351                                                         const char *ifname)
1352 {
1353         DBG("%s lower up", ifname);
1354 }
1355
1356 static void mesh_peer_lower_down(struct connman_ipconfig *ipconfig,
1357                                                         const char *ifname)
1358 {
1359         DBG("%s lower down", ifname);
1360 }
1361
1362 static void mesh_peer_ip_bound(struct connman_ipconfig *ipconfig,
1363                                                         const char *ifname)
1364 {
1365         struct connman_mesh *mesh = __connman_ipconfig_get_data(ipconfig);
1366
1367         DBG("%s ip bound", ifname);
1368
1369         connman_mesh_peer_set_state(mesh, CONNMAN_MESH_STATE_READY);
1370 }
1371
1372 static void mesh_peer_ip_release(struct connman_ipconfig *ipconfig,
1373                                                         const char *ifname)
1374 {
1375         DBG("%s ip release", ifname);
1376 }
1377
1378 static const struct connman_ipconfig_ops mesh_peer_ip_ops = {
1379         .up                     = mesh_peer_up,
1380         .down           = mesh_peer_down,
1381         .lower_up       = mesh_peer_lower_up,
1382         .lower_down     = mesh_peer_lower_down,
1383         .ip_bound       = mesh_peer_ip_bound,
1384         .ip_release     = mesh_peer_ip_release,
1385         .route_set      = NULL,
1386         .route_unset    = NULL,
1387 };
1388
1389 static struct connman_ipconfig *create_ipconfig(int index, void *user_data)
1390 {
1391         struct connman_ipconfig *ipconfig;
1392
1393         ipconfig = __connman_ipconfig_create(index,
1394                                                 CONNMAN_IPCONFIG_TYPE_IPV4);
1395         if (!ipconfig)
1396                 return NULL;
1397
1398         __connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_DHCP);
1399         __connman_ipconfig_set_data(ipconfig, user_data);
1400         __connman_ipconfig_set_ops(ipconfig, &mesh_peer_ip_ops);
1401
1402         return ipconfig;
1403 }
1404
1405 static int __connman_mesh_peer_disconnect(struct connman_mesh *mesh)
1406 {
1407         int err;
1408
1409         reply_pending(mesh, ECONNABORTED);
1410
1411         if (!is_connected(mesh) && !is_connecting(mesh))
1412                 return -ENOTCONN;
1413
1414         err = mesh_driver->disconnect(mesh);
1415         if (err < 0 && err != -EINPROGRESS)
1416                 return err;
1417
1418         return err;
1419 }
1420
1421 static DBusMessage *disconnect_mesh_peer(DBusConnection *conn,
1422                                         DBusMessage *msg, void *user_data)
1423 {
1424         struct connman_mesh *mesh = user_data;
1425         int err;
1426
1427         DBG("mesh %p", mesh);
1428         err = __connman_mesh_peer_disconnect(mesh);
1429         if (err < 0 && err != -EINPROGRESS)
1430                 return __connman_error_failed(msg, -err);
1431
1432         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1433 }
1434
1435 static bool __connman_mesh_peer_remove(struct connman_mesh *mesh)
1436 {
1437         if (!mesh->favorite)
1438                 return false;
1439
1440         __connman_mesh_peer_disconnect(mesh);
1441
1442         mesh->favorite = false;
1443
1444         __connman_storage_remove_service(mesh->identifier);
1445
1446         return true;
1447 }
1448
1449 static DBusMessage *remove_mesh_peer(DBusConnection *conn,
1450                                         DBusMessage *msg, void *user_data)
1451 {
1452         struct connman_mesh *mesh = user_data;
1453
1454         DBG("mesh %p", mesh);
1455
1456         if (!__connman_mesh_peer_remove(mesh))
1457                 return __connman_error_not_supported(msg);
1458
1459         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1460 }
1461
1462 static DBusMessage *set_mesh_peer_property(DBusConnection *conn,
1463                                         DBusMessage *msg, void *user_data)
1464 {
1465         struct connman_mesh *mesh = user_data;
1466         DBusMessageIter iter, value;
1467         const char *name;
1468         int type;
1469
1470         DBG("mesh %p", mesh);
1471
1472         if (!dbus_message_iter_init(msg, &iter))
1473                 return __connman_error_invalid_arguments(msg);
1474
1475         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
1476                 return __connman_error_invalid_arguments(msg);
1477
1478         dbus_message_iter_get_basic(&iter, &name);
1479         dbus_message_iter_next(&iter);
1480
1481         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
1482                 return __connman_error_invalid_arguments(msg);
1483
1484         dbus_message_iter_recurse(&iter, &value);
1485
1486         type = dbus_message_iter_get_arg_type(&value);
1487
1488         if (g_str_equal(name, "Passphrase")) {
1489                 char *passphrase;
1490
1491                 if (type != DBUS_TYPE_STRING)
1492                         return __connman_error_invalid_arguments(msg);
1493
1494                 dbus_message_iter_get_basic(&value, &passphrase);
1495
1496                 connman_mesh_set_passphrase(mesh, passphrase);
1497         } else {
1498                 DBG("Invalid Property %s", name);
1499                 return __connman_error_invalid_property(msg);
1500         }
1501
1502         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1503 }
1504
1505 static const GDBusMethodTable mesh_methods[] = {
1506         { GDBUS_METHOD("GetProperties",
1507                         NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
1508                         get_mesh_peer_properties) },
1509         { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, connect_mesh_peer) },
1510         { GDBUS_METHOD("Disconnect", NULL, NULL, disconnect_mesh_peer) },
1511         { GDBUS_METHOD("Remove", NULL, NULL, remove_mesh_peer) },
1512         { GDBUS_METHOD("SetProperty",
1513                         GDBUS_ARGS({ "name", "s" }, { "value", "v" }),
1514                         NULL, set_mesh_peer_property) },
1515         { },
1516 };
1517
1518 static const GDBusSignalTable mesh_signals[] = {
1519         { GDBUS_SIGNAL("PropertyChanged",
1520                         GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
1521         { },
1522 };
1523
1524 int connman_mesh_register(struct connman_mesh *mesh)
1525 {
1526         struct connman_mesh *temp;
1527         DBG("mesh %p", mesh);
1528
1529         if (mesh->path)
1530                 return -EALREADY;
1531
1532         mesh->path = g_strdup_printf("%s/mesh/%s", CONNMAN_PATH,
1533                                      mesh->identifier);
1534         DBG("path %s", mesh->path);
1535
1536         temp = g_hash_table_lookup(mesh_table, mesh->path);
1537         if (temp) {
1538                 DBG("mesh path %s already exists", mesh->path);
1539
1540                 if (mesh->frequency != temp->frequency) {
1541                         DBG("Update frequency for mesh network %s", mesh->name);
1542                         connman_mesh_set_frequency(temp, mesh->frequency);
1543                 }
1544
1545                 mesh_free(mesh);
1546                 return -EALREADY;
1547         }
1548
1549         if (mesh->br_index > 0)
1550                 mesh->ipconfig = create_ipconfig(mesh->br_index, mesh);
1551         else
1552                 mesh->ipconfig = create_ipconfig(mesh->index, mesh);
1553
1554         if (!mesh->ipconfig)
1555                 return -ENOMEM;
1556
1557         g_hash_table_insert(mesh_table, mesh->path, mesh);
1558
1559         mesh_load(mesh);
1560
1561         g_dbus_register_interface(connection, mesh->path,
1562                                         CONNMAN_MESH_INTERFACE,
1563                                         mesh_methods, mesh_signals,
1564                                         NULL, mesh, NULL);
1565         mesh->registered = true;
1566         return 0;
1567 }
1568
1569 void connman_mesh_unregister(struct connman_mesh *mesh)
1570 {
1571         DBG("mesh %p", mesh);
1572
1573         if (!mesh->path || !mesh->registered)
1574                 return;
1575
1576         g_dbus_unregister_interface(connection, mesh->path,
1577                                                                 CONNMAN_MESH_INTERFACE);
1578         mesh->registered = false;
1579
1580         g_hash_table_remove(mesh_table, mesh->path);
1581 }
1582
1583 struct connman_mesh *connman_mesh_get(const char *interface_addr,
1584                                                                         const char *identifier)
1585 {
1586         char *ident = g_strdup_printf("%s/mesh/mesh_%s_%s", CONNMAN_PATH,
1587                                                                         interface_addr, identifier);
1588         struct connman_mesh *mesh;
1589
1590         mesh = g_hash_table_lookup(mesh_table, ident);
1591         g_free(ident);
1592
1593         return mesh;
1594 }
1595
1596 int connman_mesh_driver_register(struct connman_mesh_driver *driver)
1597 {
1598         if (mesh_driver && mesh_driver != driver)
1599                 return -EINVAL;
1600
1601         mesh_driver = driver;
1602
1603         return 0;
1604 }
1605
1606 void connman_mesh_driver_unregister(struct connman_mesh_driver *driver)
1607 {
1608         if (mesh_driver != driver)
1609                 return;
1610
1611         mesh_driver = NULL;
1612 }
1613
1614 int connman_mesh_eth_driver_register(struct connman_mesh_eth_driver *driver)
1615 {
1616         if (mesh_eth_driver && mesh_eth_driver != driver)
1617                 return -EINVAL;
1618
1619         mesh_eth_driver = driver;
1620
1621         return 0;
1622 }
1623
1624 void connman_mesh_eth_driver_unregister(struct connman_mesh_eth_driver *driver)
1625 {
1626         if (mesh_eth_driver != driver)
1627                 return;
1628
1629         mesh_eth_driver = NULL;
1630 }
1631
1632 int __connman_mesh_init(void)
1633 {
1634         DBG("");
1635
1636         connection = connman_dbus_get_connection();
1637
1638         mesh_table = g_hash_table_new_full(g_str_hash, g_str_equal,
1639                                                         NULL, mesh_free);
1640
1641         connected_peer_table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
1642                                                         mesh_connected_peer_free);
1643
1644         disconnected_peer_table = g_hash_table_new_full(g_str_hash, g_str_equal,
1645                                                         NULL, mesh_disconnected_peer_free);
1646
1647         nl80211_global = __connman_mesh_nl80211_global_init();
1648         return 0;
1649 }
1650
1651 void __connman_mesh_cleanup(void)
1652 {
1653         DBG("");
1654
1655         __connman_mesh_nl80211_global_deinit(nl80211_global);
1656         g_hash_table_destroy(mesh_table);
1657         g_hash_table_destroy(connected_peer_table);
1658         g_hash_table_destroy(disconnected_peer_table);
1659         dbus_connection_unref(connection);
1660 }