Imported Upstream version 1.26
[platform/upstream/connman.git] / src / peer.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2014  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 <errno.h>
27 #include <ctype.h>
28 #include <gdbus.h>
29 #include <gdhcp/gdhcp.h>
30
31 #include <connman/agent.h>
32
33 #include "connman.h"
34
35 static DBusConnection *connection = NULL;
36
37 static GHashTable *peers_table = NULL;
38
39 static struct connman_peer_driver *peer_driver;
40
41 struct _peers_notify {
42         int id;
43         GHashTable *add;
44         GHashTable *remove;
45 } *peers_notify;
46
47 struct _peer_service {
48         enum connman_peer_service_type type;
49         unsigned char *data;
50         int length;
51 };
52
53 struct connman_peer {
54         int refcount;
55         struct connman_device *device;
56         struct connman_device *sub_device;
57         char *identifier;
58         char *name;
59         char *path;
60         enum connman_peer_state state;
61         struct connman_ipconfig *ipconfig;
62         DBusMessage *pending;
63         bool registered;
64         bool connection_master;
65         struct connman_ippool *ip_pool;
66         GDHCPServer *dhcp_server;
67         GSList *services;
68 };
69
70 static void stop_dhcp_server(struct connman_peer *peer)
71 {
72         DBG("");
73
74         if (peer->dhcp_server)
75                 g_dhcp_server_unref(peer->dhcp_server);
76
77         peer->dhcp_server = NULL;
78
79         if (peer->ip_pool)
80                 __connman_ippool_unref(peer->ip_pool);
81         peer->ip_pool = NULL;
82 }
83
84 static void dhcp_server_debug(const char *str, void *data)
85 {
86         connman_info("%s: %s\n", (const char *) data, str);
87 }
88
89 static gboolean dhcp_server_started(gpointer data)
90 {
91         struct connman_peer *peer = data;
92
93         connman_peer_set_state(peer, CONNMAN_PEER_STATE_READY);
94         connman_peer_unref(peer);
95
96         return FALSE;
97 }
98
99 static int start_dhcp_server(struct connman_peer *peer)
100 {
101         const char *start_ip, *end_ip;
102         GDHCPServerError dhcp_error;
103         const char *broadcast;
104         const char *gateway;
105         const char *subnet;
106         int prefixlen;
107         int index;
108         int err;
109
110         DBG("");
111
112         err = -ENOMEM;
113
114         if (peer->sub_device)
115                 index = connman_device_get_index(peer->sub_device);
116         else
117                 index = connman_device_get_index(peer->device);
118
119         peer->ip_pool = __connman_ippool_create(index, 2, 1, NULL, NULL);
120         if (!peer->ip_pool)
121                 goto error;
122
123         gateway = __connman_ippool_get_gateway(peer->ip_pool);
124         subnet = __connman_ippool_get_subnet_mask(peer->ip_pool);
125         broadcast = __connman_ippool_get_broadcast(peer->ip_pool);
126         start_ip = __connman_ippool_get_start_ip(peer->ip_pool);
127         end_ip = __connman_ippool_get_end_ip(peer->ip_pool);
128
129         prefixlen = connman_ipaddress_calc_netmask_len(subnet);
130
131         err = __connman_inet_modify_address(RTM_NEWADDR,
132                                 NLM_F_REPLACE | NLM_F_ACK, index, AF_INET,
133                                 gateway, NULL, prefixlen, broadcast);
134         if (err < 0)
135                 goto error;
136
137         peer->dhcp_server = g_dhcp_server_new(G_DHCP_IPV4, index, &dhcp_error);
138         if (!peer->dhcp_server)
139                 goto error;
140
141         g_dhcp_server_set_debug(peer->dhcp_server,
142                                         dhcp_server_debug, "Peer DHCP server");
143         g_dhcp_server_set_lease_time(peer->dhcp_server, 3600);
144         g_dhcp_server_set_option(peer->dhcp_server, G_DHCP_SUBNET, subnet);
145         g_dhcp_server_set_option(peer->dhcp_server, G_DHCP_ROUTER, gateway);
146         g_dhcp_server_set_option(peer->dhcp_server, G_DHCP_DNS_SERVER, NULL);
147         g_dhcp_server_set_ip_range(peer->dhcp_server, start_ip, end_ip);
148
149         err = g_dhcp_server_start(peer->dhcp_server);
150         if (err < 0)
151                 goto error;
152
153         g_timeout_add_seconds(0, dhcp_server_started, connman_peer_ref(peer));
154
155         return 0;
156
157 error:
158         stop_dhcp_server(peer);
159         return err;
160 }
161
162 static void reply_pending(struct connman_peer *peer, int error)
163 {
164         if (!peer->pending)
165                 return;
166
167         connman_dbus_reply_pending(peer->pending, error, NULL);
168         peer->pending = NULL;
169 }
170
171 static void peer_free(gpointer data)
172 {
173         struct connman_peer *peer = data;
174
175         reply_pending(peer, ENOENT);
176
177         connman_peer_unregister(peer);
178
179         if (peer->path) {
180                 g_free(peer->path);
181                 peer->path = NULL;
182         }
183
184         if (peer->ipconfig) {
185                 __connman_ipconfig_set_ops(peer->ipconfig, NULL);
186                 __connman_ipconfig_set_data(peer->ipconfig, NULL);
187                 __connman_ipconfig_unref(peer->ipconfig);
188                 peer->ipconfig = NULL;
189         }
190
191         stop_dhcp_server(peer);
192
193         if (peer->device) {
194                 connman_device_unref(peer->device);
195                 peer->device = NULL;
196         }
197
198         if (peer->services)
199                 connman_peer_reset_services(peer);
200
201         g_free(peer->identifier);
202         g_free(peer->name);
203
204         g_free(peer);
205 }
206
207 static const char *state2string(enum connman_peer_state state)
208 {
209         switch (state) {
210         case CONNMAN_PEER_STATE_UNKNOWN:
211                 break;
212         case CONNMAN_PEER_STATE_IDLE:
213                 return "idle";
214         case CONNMAN_PEER_STATE_ASSOCIATION:
215                 return "association";
216         case CONNMAN_PEER_STATE_CONFIGURATION:
217                 return "configuration";
218         case CONNMAN_PEER_STATE_READY:
219                 return "ready";
220         case CONNMAN_PEER_STATE_DISCONNECT:
221                 return "disconnect";
222         case CONNMAN_PEER_STATE_FAILURE:
223                 return "failure";
224         }
225
226         return NULL;
227 }
228
229 static bool is_connecting(struct connman_peer *peer)
230 {
231         if (peer->state == CONNMAN_PEER_STATE_ASSOCIATION ||
232                         peer->state == CONNMAN_PEER_STATE_CONFIGURATION ||
233                         peer->pending)
234                 return true;
235
236         return false;
237 }
238
239 static bool is_connected(struct connman_peer *peer)
240 {
241         if (peer->state == CONNMAN_PEER_STATE_READY)
242                 return true;
243
244         return false;
245 }
246
247 static bool allow_property_changed(struct connman_peer *peer)
248 {
249         if (g_hash_table_lookup_extended(peers_notify->add, peer->path,
250                                                                 NULL, NULL))
251                 return false;
252
253         return true;
254 }
255
256 static void append_dhcp_server_ipv4(DBusMessageIter *iter, void *user_data)
257 {
258         struct connman_peer *peer = user_data;
259         const char *str = "dhcp";
260         const char *gateway;
261         const char *subnet;
262
263         if (!peer->ip_pool)
264                 return;
265
266         gateway = __connman_ippool_get_gateway(peer->ip_pool);
267         subnet = __connman_ippool_get_subnet_mask(peer->ip_pool);
268
269         connman_dbus_dict_append_basic(iter, "Method", DBUS_TYPE_STRING, &str);
270         connman_dbus_dict_append_basic(iter, "Address",
271                                                 DBUS_TYPE_STRING, &gateway);
272         connman_dbus_dict_append_basic(iter, "Netmask",
273                                                 DBUS_TYPE_STRING, &subnet);
274         connman_dbus_dict_append_basic(iter, "Gateway",
275                                                 DBUS_TYPE_STRING, &gateway);
276 }
277
278 static void append_ipv4(DBusMessageIter *iter, void *user_data)
279 {
280         struct connman_peer *peer = user_data;
281
282         if (!is_connected(peer))
283                 return;
284
285         if (peer->connection_master)
286                 append_dhcp_server_ipv4(iter, peer);
287         else if (peer->ipconfig)
288                 __connman_ipconfig_append_ipv4(peer->ipconfig, iter);
289 }
290
291 static void append_peer_service(DBusMessageIter *iter,
292                                         struct _peer_service *service)
293 {
294         DBusMessageIter dict;
295
296         connman_dbus_dict_open(iter, &dict);
297
298         switch (service->type) {
299         case CONNMAN_PEER_SERVICE_UNKNOWN:
300                 /* Should never happen */
301                 break;
302         case CONNMAN_PEER_SERVICE_WIFI_DISPLAY:
303                 connman_dbus_dict_append_fixed_array(&dict,
304                                 "WiFiDisplayIEs", DBUS_TYPE_BYTE,
305                                 &service->data, service->length);
306                 break;
307         }
308
309         connman_dbus_dict_close(iter, &dict);
310 }
311
312 static void append_peer_services(DBusMessageIter *iter, void *user_data)
313 {
314         struct connman_peer *peer = user_data;
315         DBusMessageIter container;
316         GSList *list;
317
318         dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT,
319                                                         NULL, &container);
320
321         if (!peer->services) {
322                 DBusMessageIter dict;
323
324                 connman_dbus_dict_open(&container, &dict);
325                 connman_dbus_dict_close(&container, &dict);
326         } else {
327                 for (list = peer->services; list; list = list->next)
328                         append_peer_service(&container, list->data);
329         }
330
331         dbus_message_iter_close_container(iter, &container);
332 }
333
334 static void append_properties(DBusMessageIter *iter, struct connman_peer *peer)
335 {
336         const char *state = state2string(peer->state);
337         DBusMessageIter dict;
338
339         connman_dbus_dict_open(iter, &dict);
340
341         connman_dbus_dict_append_basic(&dict, "State",
342                                         DBUS_TYPE_STRING, &state);
343         connman_dbus_dict_append_basic(&dict, "Name",
344                                         DBUS_TYPE_STRING, &peer->name);
345         connman_dbus_dict_append_dict(&dict, "IPv4", append_ipv4, peer);
346         connman_dbus_dict_append_array(&dict, "Services",
347                                         DBUS_TYPE_DICT_ENTRY,
348                                         append_peer_services, peer);
349         connman_dbus_dict_close(iter, &dict);
350 }
351
352 static void settings_changed(struct connman_peer *peer)
353 {
354         if (!allow_property_changed(peer))
355                 return;
356
357         connman_dbus_property_changed_dict(peer->path,
358                                         CONNMAN_PEER_INTERFACE, "IPv4",
359                                         append_ipv4, peer);
360 }
361
362 static DBusMessage *get_peer_properties(DBusConnection *conn,
363                                                 DBusMessage *msg, void *data)
364 {
365         struct connman_peer *peer = data;
366         DBusMessageIter dict;
367         DBusMessage *reply;
368
369         reply = dbus_message_new_method_return(msg);
370         if (!reply)
371                 return NULL;
372
373         dbus_message_iter_init_append(reply, &dict);
374         append_properties(&dict, peer);
375
376         return reply;
377 }
378
379 static void append_peer_struct(gpointer key, gpointer value,
380                                                 gpointer user_data)
381 {
382         DBusMessageIter *array = user_data;
383         struct connman_peer *peer = value;
384         DBusMessageIter entry;
385
386         dbus_message_iter_open_container(array, DBUS_TYPE_STRUCT,
387                                                         NULL, &entry);
388         dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH,
389                                                         &peer->path);
390         append_properties(&entry, peer);
391         dbus_message_iter_close_container(array, &entry);
392 }
393
394 static void state_changed(struct connman_peer *peer)
395 {
396         const char *state;
397
398         state = state2string(peer->state);
399         if (!state || !allow_property_changed(peer))
400                 return;
401
402         connman_dbus_property_changed_basic(peer->path,
403                                          CONNMAN_PEER_INTERFACE, "State",
404                                          DBUS_TYPE_STRING, &state);
405 }
406
407 static void append_existing_and_new_peers(gpointer key,
408                                         gpointer value, gpointer user_data)
409 {
410         struct connman_peer *peer = value;
411         DBusMessageIter *iter = user_data;
412         DBusMessageIter entry, dict;
413
414         if (!peer || !peer->registered)
415                 return;
416
417         if (g_hash_table_lookup(peers_notify->add, peer->path)) {
418                 DBG("new %s", peer->path);
419
420                 append_peer_struct(key, peer, iter);
421                 g_hash_table_remove(peers_notify->add, peer->path);
422         } else if (!g_hash_table_lookup(peers_notify->remove, peer->path)) {
423                 DBG("existing %s", peer->path);
424
425                 dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT,
426                                                                 NULL, &entry);
427                 dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH,
428                                                                 &peer->path);
429                 connman_dbus_dict_open(&entry, &dict);
430                 connman_dbus_dict_close(&entry, &dict);
431
432                 dbus_message_iter_close_container(iter, &entry);
433         }
434 }
435
436 static void peer_append_all(DBusMessageIter *iter, void *user_data)
437 {
438         g_hash_table_foreach(peers_table, append_existing_and_new_peers, iter);
439 }
440
441 static void append_removed(gpointer key, gpointer value, gpointer user_data)
442 {
443         DBusMessageIter *iter = user_data;
444         char *objpath = key;
445
446         DBG("removed %s", objpath);
447         dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &objpath);
448 }
449
450 static void peer_append_removed(DBusMessageIter *iter, void *user_data)
451 {
452         g_hash_table_foreach(peers_notify->remove, append_removed, iter);
453 }
454
455 static gboolean peer_send_changed(gpointer data)
456 {
457         DBusMessage *signal;
458
459         DBG("");
460
461         peers_notify->id = 0;
462
463         signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
464                                 CONNMAN_MANAGER_INTERFACE, "PeersChanged");
465         if (!signal)
466                 return FALSE;
467
468         __connman_dbus_append_objpath_dict_array(signal,
469                                                 peer_append_all, NULL);
470         __connman_dbus_append_objpath_array(signal,
471                                                 peer_append_removed, NULL);
472
473         dbus_connection_send(connection, signal, NULL);
474         dbus_message_unref(signal);
475
476         g_hash_table_remove_all(peers_notify->remove);
477         g_hash_table_remove_all(peers_notify->add);
478
479         return FALSE;
480 }
481
482 static void peer_schedule_changed(void)
483 {
484         if (peers_notify->id != 0)
485                 return;
486
487         peers_notify->id = g_timeout_add(100, peer_send_changed, NULL);
488 }
489
490 static void peer_added(struct connman_peer *peer)
491 {
492         DBG("peer %p", peer);
493
494         g_hash_table_remove(peers_notify->remove, peer->path);
495         g_hash_table_replace(peers_notify->add, peer->path, peer);
496
497         peer_schedule_changed();
498 }
499
500 static void peer_removed(struct connman_peer *peer)
501 {
502         DBG("peer %p", peer);
503
504         g_hash_table_remove(peers_notify->add, peer->path);
505         g_hash_table_replace(peers_notify->remove, g_strdup(peer->path), NULL);
506
507         peer_schedule_changed();
508 }
509
510 static const char *get_dbus_sender(struct connman_peer *peer)
511 {
512         if (!peer->pending)
513                 return NULL;
514
515         return dbus_message_get_sender(peer->pending);
516 }
517
518 static enum connman_peer_wps_method check_wpspin(struct connman_peer *peer,
519                                                         const char *wpspin)
520 {
521         int len, i;
522
523         if (!wpspin)
524                 return CONNMAN_PEER_WPS_PBC;
525
526         len = strlen(wpspin);
527         if (len == 0)
528                 return CONNMAN_PEER_WPS_PBC;
529
530         if (len != 8)
531                 return CONNMAN_PEER_WPS_UNKNOWN;
532         for (i = 0; i < 8; i++) {
533                 if (!isdigit((unsigned char) wpspin[i]))
534                         return CONNMAN_PEER_WPS_UNKNOWN;
535         }
536
537         return CONNMAN_PEER_WPS_PIN;
538 }
539
540 static void request_authorization_cb(struct connman_peer *peer,
541                                         bool choice_done, const char *wpspin,
542                                         const char *error, void *user_data)
543 {
544         enum connman_peer_wps_method wps_method;
545         int err;
546
547         DBG("RequestInput return, %p", peer);
548
549         if (error) {
550                 if (g_strcmp0(error,
551                                 "net.connman.Agent.Error.Canceled") == 0 ||
552                         g_strcmp0(error,
553                                 "net.connman.Agent.Error.Rejected") == 0) {
554                         err = -EINVAL;
555                         goto out;
556                 }
557         }
558
559         if (!choice_done || !peer_driver->connect) {
560                 err = -EINVAL;
561                 goto out;
562         }
563
564         wps_method = check_wpspin(peer, wpspin);
565
566         err = peer_driver->connect(peer, wps_method, wpspin);
567         if (err == -EINPROGRESS)
568                 return;
569
570 out:
571         reply_pending(peer, EIO);
572         connman_peer_set_state(peer, CONNMAN_PEER_STATE_IDLE);
573 }
574
575 static int peer_connect(struct connman_peer *peer)
576 {
577         int err = -ENOTSUP;
578
579         if (peer_driver->connect)
580                 err = peer_driver->connect(peer,
581                                         CONNMAN_PEER_WPS_UNKNOWN, NULL);
582
583         if (err == -ENOKEY) {
584                 err = __connman_agent_request_peer_authorization(peer,
585                                                 request_authorization_cb, true,
586                                                 get_dbus_sender(peer), NULL);
587         }
588
589         return err;
590 }
591
592 static int peer_disconnect(struct connman_peer *peer)
593 {
594         int err = -ENOTSUP;
595
596         connman_agent_cancel(peer);
597         reply_pending(peer, ECONNABORTED);
598
599         connman_peer_set_state(peer, CONNMAN_PEER_STATE_DISCONNECT);
600
601         if (peer->connection_master)
602                 stop_dhcp_server(peer);
603         else
604                 __connman_dhcp_stop(peer->ipconfig);
605
606         if (peer_driver->disconnect)
607                 err = peer_driver->disconnect(peer);
608
609         connman_peer_set_state(peer, CONNMAN_PEER_STATE_IDLE);
610
611         return err;
612 }
613
614 static DBusMessage *connect_peer(DBusConnection *conn,
615                                         DBusMessage *msg, void *user_data)
616 {
617         struct connman_peer *peer = user_data;
618         GList *list, *start;
619         int err;
620
621         DBG("peer %p", peer);
622
623         if (peer->pending)
624                 return __connman_error_in_progress(msg);
625
626         list = g_hash_table_get_values(peers_table);
627         start = list;
628         for (; list; list = list->next) {
629                 struct connman_peer *temp = list->data;
630
631                 if (temp == peer || temp->device != peer->device)
632                         continue;
633
634                 if (is_connecting(temp) || is_connected(temp)) {
635                         if (peer_disconnect(temp) == -EINPROGRESS) {
636                                 g_list_free(start);
637                                 return __connman_error_in_progress(msg);
638                         }
639                 }
640         }
641
642         g_list_free(start);
643
644         peer->pending = dbus_message_ref(msg);
645
646         err = peer_connect(peer);
647         if (err == -EINPROGRESS)
648                 return NULL;
649
650         if (err < 0) {
651                 dbus_message_unref(peer->pending);
652                 peer->pending = NULL;
653
654                 return __connman_error_failed(msg, -err);
655         }
656
657         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
658 }
659
660 static DBusMessage *disconnect_peer(DBusConnection *conn,
661                                         DBusMessage *msg, void *user_data)
662 {
663         struct connman_peer *peer = user_data;
664         int err;
665
666         DBG("peer %p", peer);
667
668         err = peer_disconnect(peer);
669         if (err < 0 && err != -EINPROGRESS)
670                 return __connman_error_failed(msg, -err);
671
672         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
673 }
674
675 struct connman_peer *connman_peer_create(const char *identifier)
676 {
677         struct connman_peer *peer;
678
679         peer = g_malloc0(sizeof(struct connman_peer));
680         peer->identifier = g_strdup(identifier);
681         peer->state = CONNMAN_PEER_STATE_IDLE;
682
683         peer->refcount = 1;
684
685         return peer;
686 }
687
688 struct connman_peer *connman_peer_ref_debug(struct connman_peer *peer,
689                                 const char *file, int line, const char *caller)
690 {
691         DBG("%p ref %d by %s:%d:%s()", peer, peer->refcount + 1,
692                                                 file, line, caller);
693
694         __sync_fetch_and_add(&peer->refcount, 1);
695
696         return peer;
697 }
698
699 void connman_peer_unref_debug(struct connman_peer *peer,
700                                 const char *file, int line, const char *caller)
701 {
702         DBG("%p ref %d by %s:%d:%s()", peer, peer->refcount - 1,
703                                                 file, line, caller);
704
705         if (__sync_fetch_and_sub(&peer->refcount, 1) != 1)
706                 return;
707
708         if (!peer->registered && !peer->path)
709                 return peer_free(peer);
710
711         g_hash_table_remove(peers_table, peer->path);
712 }
713
714 const char *connman_peer_get_identifier(struct connman_peer *peer)
715 {
716         if (!peer)
717                 return NULL;
718
719         return peer->identifier;
720 }
721
722 void connman_peer_set_name(struct connman_peer *peer, const char *name)
723 {
724         g_free(peer->name);
725         peer->name = g_strdup(name);
726 }
727
728 void connman_peer_set_device(struct connman_peer *peer,
729                                 struct connman_device *device)
730 {
731         if (!peer || !device)
732                 return;
733
734         peer->device = device;
735         connman_device_ref(device);
736 }
737
738 struct connman_device *connman_peer_get_device(struct connman_peer *peer)
739 {
740         if (!peer)
741                 return NULL;
742
743         return peer->device;
744 }
745
746 void connman_peer_set_sub_device(struct connman_peer *peer,
747                                         struct connman_device *device)
748 {
749         if (!peer || !device || peer->sub_device)
750                 return;
751
752         peer->sub_device = device;
753 }
754
755 void connman_peer_set_as_master(struct connman_peer *peer, bool master)
756 {
757         if (!peer || !is_connecting(peer))
758                 return;
759
760         peer->connection_master = master;
761 }
762
763 static void dhcp_callback(struct connman_ipconfig *ipconfig,
764                         struct connman_network *network,
765                         bool success, gpointer data)
766 {
767         struct connman_peer *peer = data;
768         int err;
769
770         if (!success)
771                 goto error;
772
773         DBG("lease acquired for ipconfig %p", ipconfig);
774
775         err = __connman_ipconfig_address_add(ipconfig);
776         if (err < 0)
777                 goto error;
778
779         return;
780
781 error:
782         __connman_ipconfig_address_remove(ipconfig);
783         connman_peer_set_state(peer, CONNMAN_PEER_STATE_FAILURE);
784 }
785
786 static int start_dhcp_client(struct connman_peer *peer)
787 {
788         if (peer->sub_device)
789                 __connman_ipconfig_set_index(peer->ipconfig,
790                                 connman_device_get_index(peer->sub_device));
791
792         __connman_ipconfig_enable(peer->ipconfig);
793
794         return __connman_dhcp_start(peer->ipconfig, NULL, dhcp_callback, peer);
795 }
796
797 static void report_error_cb(void *user_context, bool retry, void *user_data)
798 {
799         struct connman_peer *peer = user_context;
800
801         if (retry) {
802                 int err;
803                 err = peer_connect(peer);
804
805                 if (err == 0 || err == -EINPROGRESS)
806                         return;
807         }
808
809         reply_pending(peer, ENOTCONN);
810
811         peer_disconnect(peer);
812
813         if (!peer->connection_master) {
814                 __connman_dhcp_stop(peer->ipconfig);
815                 __connman_ipconfig_disable(peer->ipconfig);
816         } else
817                 stop_dhcp_server(peer);
818
819         peer->connection_master = false;
820         peer->sub_device = NULL;
821 }
822
823 static int manage_peer_error(struct connman_peer *peer)
824 {
825         int err;
826
827         err = __connman_agent_report_peer_error(peer, peer->path,
828                                         "connect-failed", report_error_cb,
829                                         get_dbus_sender(peer), NULL);
830         if (err != -EINPROGRESS) {
831                 report_error_cb(peer, false, NULL);
832                 return err;
833         }
834
835         return 0;
836 }
837
838 int connman_peer_set_state(struct connman_peer *peer,
839                                         enum connman_peer_state new_state)
840 {
841         enum connman_peer_state old_state = peer->state;
842         int err;
843
844         DBG("peer (%s) old state %d new state %d", peer->name,
845                                 old_state, new_state);
846
847         if (old_state == new_state)
848                 return -EALREADY;
849
850         switch (new_state) {
851         case CONNMAN_PEER_STATE_UNKNOWN:
852                 return -EINVAL;
853         case CONNMAN_PEER_STATE_IDLE:
854                 if (is_connecting(peer) || is_connected(peer))
855                         return peer_disconnect(peer);
856                 peer->sub_device = NULL;
857                 break;
858         case CONNMAN_PEER_STATE_ASSOCIATION:
859                 break;
860         case CONNMAN_PEER_STATE_CONFIGURATION:
861                 if (peer->connection_master)
862                         err = start_dhcp_server(peer);
863                 else
864                         err = start_dhcp_client(peer);
865                 if (err < 0)
866                         return connman_peer_set_state(peer,
867                                                 CONNMAN_PEER_STATE_FAILURE);
868                 break;
869         case CONNMAN_PEER_STATE_READY:
870                 reply_pending(peer, 0);
871                 break;
872         case CONNMAN_PEER_STATE_DISCONNECT:
873                 if (peer->connection_master)
874                         stop_dhcp_server(peer);
875                 peer->connection_master = false;
876                 peer->sub_device = NULL;
877
878                 break;
879         case CONNMAN_PEER_STATE_FAILURE:
880                 if (manage_peer_error(peer) == 0)
881                         return 0;
882                 break;
883         };
884
885         peer->state = new_state;
886         state_changed(peer);
887
888         return 0;
889 }
890
891 int connman_peer_request_connection(struct connman_peer *peer)
892 {
893         return __connman_agent_request_peer_authorization(peer,
894                                         request_authorization_cb, false,
895                                         NULL, NULL);
896 }
897
898 static void peer_service_free(gpointer data)
899 {
900         struct _peer_service *service = data;
901
902         if (!service)
903                 return;
904
905         g_free(service->data);
906         g_free(service);
907 }
908
909 void connman_peer_reset_services(struct connman_peer *peer)
910 {
911         if (!peer)
912                 return;
913
914         g_slist_free_full(peer->services, peer_service_free);
915         peer->services = NULL;
916 }
917
918 void connman_peer_services_changed(struct connman_peer *peer)
919 {
920         if (!peer || !peer->registered || !allow_property_changed(peer))
921                 return;
922
923         connman_dbus_property_changed_array(peer->path,
924                         CONNMAN_PEER_INTERFACE, "Services",
925                         DBUS_TYPE_DICT_ENTRY, append_peer_services, peer);
926 }
927
928 void connman_peer_add_service(struct connman_peer *peer,
929                                 enum connman_peer_service_type type,
930                                 const unsigned char *data, int data_length)
931 {
932         struct _peer_service *service;
933
934         if (!peer || !data || type == CONNMAN_PEER_SERVICE_UNKNOWN)
935                 return;
936
937         service = g_malloc0(sizeof(struct _peer_service));
938         service->type = type;
939         service->data = g_memdup(data, data_length * sizeof(unsigned char));
940         service->length = data_length;
941
942         peer->services = g_slist_prepend(peer->services, service);
943 }
944
945 static void peer_up(struct connman_ipconfig *ipconfig, const char *ifname)
946 {
947         DBG("%s up", ifname);
948 }
949
950 static void peer_down(struct connman_ipconfig *ipconfig, const char *ifname)
951 {
952         DBG("%s down", ifname);
953 }
954
955 static void peer_lower_up(struct connman_ipconfig *ipconfig,
956                                                         const char *ifname)
957 {
958         DBG("%s lower up", ifname);
959 }
960
961 static void peer_lower_down(struct connman_ipconfig *ipconfig,
962                                                         const char *ifname)
963 {
964         struct connman_peer *peer = __connman_ipconfig_get_data(ipconfig);
965
966         DBG("%s lower down", ifname);
967
968         __connman_ipconfig_disable(ipconfig);
969         connman_peer_set_state(peer, CONNMAN_PEER_STATE_DISCONNECT);
970 }
971
972 static void peer_ip_bound(struct connman_ipconfig *ipconfig,
973                                                         const char *ifname)
974 {
975         struct connman_peer *peer = __connman_ipconfig_get_data(ipconfig);
976
977         DBG("%s ip bound", ifname);
978
979         settings_changed(peer);
980         connman_peer_set_state(peer, CONNMAN_PEER_STATE_READY);
981 }
982
983 static void peer_ip_release(struct connman_ipconfig *ipconfig,
984                                                         const char *ifname)
985 {
986         struct connman_peer *peer = __connman_ipconfig_get_data(ipconfig);
987
988         DBG("%s ip release", ifname);
989
990         settings_changed(peer);
991 }
992
993 static const struct connman_ipconfig_ops peer_ip_ops = {
994         .up             = peer_up,
995         .down           = peer_down,
996         .lower_up       = peer_lower_up,
997         .lower_down     = peer_lower_down,
998         .ip_bound       = peer_ip_bound,
999         .ip_release     = peer_ip_release,
1000         .route_set      = NULL,
1001         .route_unset    = NULL,
1002 };
1003
1004 static struct connman_ipconfig *create_ipconfig(int index, void *user_data)
1005 {
1006         struct connman_ipconfig *ipconfig;
1007
1008         ipconfig = __connman_ipconfig_create(index,
1009                                                 CONNMAN_IPCONFIG_TYPE_IPV4);
1010         if (!ipconfig)
1011                 return NULL;
1012
1013         __connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_DHCP);
1014         __connman_ipconfig_set_data(ipconfig, user_data);
1015         __connman_ipconfig_set_ops(ipconfig, &peer_ip_ops);
1016
1017         return ipconfig;
1018 }
1019
1020 static const GDBusMethodTable peer_methods[] = {
1021         { GDBUS_METHOD("GetProperties",
1022                         NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
1023                         get_peer_properties) },
1024         { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, connect_peer) },
1025         { GDBUS_METHOD("Disconnect", NULL, NULL, disconnect_peer) },
1026         { },
1027 };
1028
1029 static const GDBusSignalTable peer_signals[] = {
1030         { GDBUS_SIGNAL("PropertyChanged",
1031                         GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
1032         { },
1033 };
1034
1035 static char *get_peer_path(struct connman_device *device,
1036                                         const char *identifier)
1037 {
1038         return g_strdup_printf("%s/peer/peer_%s_%s", CONNMAN_PATH,
1039                                 connman_device_get_ident(device), identifier);
1040 }
1041
1042 int connman_peer_register(struct connman_peer *peer)
1043 {
1044         int index;
1045
1046         DBG("peer %p", peer);
1047
1048         if (peer->path && peer->registered)
1049                 return -EALREADY;
1050
1051         index = connman_device_get_index(peer->device);
1052         peer->ipconfig = create_ipconfig(index, peer);
1053         if (!peer->ipconfig)
1054                 return -ENOMEM;
1055
1056         peer->path = get_peer_path(peer->device, peer->identifier);
1057         DBG("path %s", peer->path);
1058
1059         g_hash_table_insert(peers_table, peer->path, peer);
1060
1061         g_dbus_register_interface(connection, peer->path,
1062                                         CONNMAN_PEER_INTERFACE,
1063                                         peer_methods, peer_signals,
1064                                         NULL, peer, NULL);
1065         peer->registered = true;
1066         peer_added(peer);
1067
1068         return 0;
1069 }
1070
1071 void connman_peer_unregister(struct connman_peer *peer)
1072 {
1073         DBG("peer %p", peer);
1074
1075         if (!peer->path || !peer->registered)
1076                 return;
1077
1078         connman_agent_cancel(peer);
1079         reply_pending(peer, EIO);
1080
1081         g_dbus_unregister_interface(connection, peer->path,
1082                                         CONNMAN_PEER_INTERFACE);
1083         peer->registered = false;
1084         peer_removed(peer);
1085 }
1086
1087 struct connman_peer *connman_peer_get(struct connman_device *device,
1088                                                 const char *identifier)
1089 {
1090         char *ident = get_peer_path(device, identifier);
1091         struct connman_peer *peer;
1092
1093         peer = g_hash_table_lookup(peers_table, ident);
1094         g_free(ident);
1095
1096         return peer;
1097 }
1098
1099 int connman_peer_driver_register(struct connman_peer_driver *driver)
1100 {
1101         if (peer_driver && peer_driver != driver)
1102                 return -EINVAL;
1103
1104         peer_driver = driver;
1105
1106         __connman_peer_service_set_driver(driver);
1107
1108         return 0;
1109 }
1110
1111 void connman_peer_driver_unregister(struct connman_peer_driver *driver)
1112 {
1113         if (peer_driver != driver)
1114                 return;
1115
1116         peer_driver = NULL;
1117
1118         __connman_peer_service_set_driver(NULL);
1119 }
1120
1121 void __connman_peer_list_struct(DBusMessageIter *array)
1122 {
1123         g_hash_table_foreach(peers_table, append_peer_struct, array);
1124 }
1125
1126 const char *__connman_peer_get_path(struct connman_peer *peer)
1127 {
1128         if (!peer || !peer->registered)
1129                 return NULL;
1130
1131         return peer->path;
1132 }
1133
1134 int __connman_peer_init(void)
1135 {
1136         DBG("");
1137
1138         connection = connman_dbus_get_connection();
1139
1140         peers_table = g_hash_table_new_full(g_str_hash, g_str_equal,
1141                                                         NULL, peer_free);
1142
1143         peers_notify = g_new0(struct _peers_notify, 1);
1144         peers_notify->add = g_hash_table_new(g_str_hash, g_str_equal);
1145         peers_notify->remove = g_hash_table_new_full(g_str_hash, g_str_equal,
1146                                                                 g_free, NULL);
1147         return 0;
1148 }
1149
1150 void __connman_peer_cleanup(void)
1151 {
1152         DBG("");
1153
1154         g_hash_table_destroy(peers_table);
1155         peers_table = NULL;
1156         dbus_connection_unref(connection);
1157         connection = NULL;
1158 }