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