Updated connman to version 1.35
[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_unref(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);
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 (peer_driver->connect)
610                 err = peer_driver->connect(peer,
611                                         CONNMAN_PEER_WPS_UNKNOWN, NULL);
612
613         if (err == -ENOKEY) {
614                 err = __connman_agent_request_peer_authorization(peer,
615                                                 request_authorization_cb, true,
616                                                 get_dbus_sender(peer), NULL);
617         }
618
619         return err;
620 }
621
622 static int peer_disconnect(struct connman_peer *peer)
623 {
624         int err = -ENOTSUP;
625
626         connman_agent_cancel(peer);
627         reply_pending(peer, ECONNABORTED);
628
629         connman_peer_set_state(peer, CONNMAN_PEER_STATE_DISCONNECT);
630
631         if (peer->connection_master)
632                 stop_dhcp_server(peer);
633         else
634                 __connman_dhcp_stop(peer->ipconfig);
635
636         if (peer_driver->disconnect)
637                 err = peer_driver->disconnect(peer);
638
639         connman_peer_set_state(peer, CONNMAN_PEER_STATE_IDLE);
640
641         return err;
642 }
643
644 static DBusMessage *connect_peer(DBusConnection *conn,
645                                         DBusMessage *msg, void *user_data)
646 {
647         struct connman_peer *peer = user_data;
648         GList *list, *start;
649         int err;
650
651         DBG("peer %p", peer);
652
653         if (peer->pending)
654                 return __connman_error_in_progress(msg);
655
656         list = g_hash_table_get_values(peers_table);
657         start = list;
658         for (; list; list = list->next) {
659                 struct connman_peer *temp = list->data;
660
661                 if (temp == peer || temp->device != peer->device)
662                         continue;
663
664                 if (is_connecting(temp) || is_connected(temp)) {
665                         if (peer_disconnect(temp) == -EINPROGRESS) {
666                                 g_list_free(start);
667                                 return __connman_error_in_progress(msg);
668                         }
669                 }
670         }
671
672         g_list_free(start);
673
674         peer->pending = dbus_message_ref(msg);
675
676         err = peer_connect(peer);
677         if (err == -EINPROGRESS)
678                 return NULL;
679
680         if (err < 0) {
681                 dbus_message_unref(peer->pending);
682                 peer->pending = NULL;
683
684                 return __connman_error_failed(msg, -err);
685         }
686
687         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
688 }
689
690 static DBusMessage *disconnect_peer(DBusConnection *conn,
691                                         DBusMessage *msg, void *user_data)
692 {
693         struct connman_peer *peer = user_data;
694         int err;
695
696         DBG("peer %p", peer);
697
698         err = peer_disconnect(peer);
699         if (err < 0 && err != -EINPROGRESS)
700                 return __connman_error_failed(msg, -err);
701
702         return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
703 }
704
705 struct connman_peer *connman_peer_create(const char *identifier)
706 {
707         struct connman_peer *peer;
708
709         peer = g_malloc0(sizeof(struct connman_peer));
710         peer->identifier = g_strdup(identifier);
711         peer->state = CONNMAN_PEER_STATE_IDLE;
712
713         peer->refcount = 1;
714
715         return peer;
716 }
717
718 struct connman_peer *connman_peer_ref_debug(struct connman_peer *peer,
719                                 const char *file, int line, const char *caller)
720 {
721         DBG("%p ref %d by %s:%d:%s()", peer, peer->refcount + 1,
722                                                 file, line, caller);
723
724         __sync_fetch_and_add(&peer->refcount, 1);
725
726         return peer;
727 }
728
729 void connman_peer_unref_debug(struct connman_peer *peer,
730                                 const char *file, int line, const char *caller)
731 {
732         DBG("%p ref %d by %s:%d:%s()", peer, peer->refcount - 1,
733                                                 file, line, caller);
734
735         if (__sync_fetch_and_sub(&peer->refcount, 1) != 1)
736                 return;
737
738         if (!peer->registered && !peer->path)
739                 return peer_free(peer);
740
741         g_hash_table_remove(peers_table, peer->path);
742 }
743
744 const char *connman_peer_get_identifier(struct connman_peer *peer)
745 {
746         if (!peer)
747                 return NULL;
748
749         return peer->identifier;
750 }
751
752 void connman_peer_set_name(struct connman_peer *peer, const char *name)
753 {
754         g_free(peer->name);
755         peer->name = g_strdup(name);
756 }
757
758 void connman_peer_set_iface_address(struct connman_peer *peer,
759                                         const unsigned char *iface_address)
760 {
761         memset(peer->iface_address, 0, sizeof(peer->iface_address));
762         memcpy(peer->iface_address, iface_address, ETH_ALEN);
763 }
764
765 void connman_peer_set_device(struct connman_peer *peer,
766                                 struct connman_device *device)
767 {
768         if (!peer || !device)
769                 return;
770
771         peer->device = device;
772         connman_device_ref(device);
773 }
774
775 struct connman_device *connman_peer_get_device(struct connman_peer *peer)
776 {
777         if (!peer)
778                 return NULL;
779
780         return peer->device;
781 }
782
783 void connman_peer_set_sub_device(struct connman_peer *peer,
784                                         struct connman_device *device)
785 {
786         if (!peer || !device || peer->sub_device)
787                 return;
788
789         peer->sub_device = device;
790 }
791
792 void connman_peer_set_as_master(struct connman_peer *peer, bool master)
793 {
794         if (!peer || !is_connecting(peer))
795                 return;
796
797         peer->connection_master = master;
798 }
799
800 static void dhcp_callback(struct connman_ipconfig *ipconfig,
801                         struct connman_network *network,
802                         bool success, gpointer data)
803 {
804         struct connman_peer *peer = data;
805         int err;
806
807         if (!success)
808                 goto error;
809
810         DBG("lease acquired for ipconfig %p", ipconfig);
811
812         err = __connman_ipconfig_address_add(ipconfig);
813         if (err < 0)
814                 goto error;
815
816         return;
817
818 error:
819         __connman_ipconfig_address_remove(ipconfig);
820         connman_peer_set_state(peer, CONNMAN_PEER_STATE_FAILURE);
821 }
822
823 static int start_dhcp_client(struct connman_peer *peer)
824 {
825         if (peer->sub_device)
826                 __connman_ipconfig_set_index(peer->ipconfig,
827                                 connman_device_get_index(peer->sub_device));
828
829         __connman_ipconfig_enable(peer->ipconfig);
830
831         return __connman_dhcp_start(peer->ipconfig, NULL, dhcp_callback, peer);
832 }
833
834 static void report_error_cb(void *user_context, bool retry, void *user_data)
835 {
836         struct connman_peer *peer = user_context;
837
838         if (retry) {
839                 int err;
840                 err = peer_connect(peer);
841
842                 if (err == 0 || err == -EINPROGRESS)
843                         return;
844         }
845
846         reply_pending(peer, ENOTCONN);
847
848         peer_disconnect(peer);
849
850         if (!peer->connection_master) {
851                 __connman_dhcp_stop(peer->ipconfig);
852                 __connman_ipconfig_disable(peer->ipconfig);
853         } else
854                 stop_dhcp_server(peer);
855
856         peer->connection_master = false;
857         peer->sub_device = NULL;
858 }
859
860 static int manage_peer_error(struct connman_peer *peer)
861 {
862         int err;
863
864         err = __connman_agent_report_peer_error(peer, peer->path,
865                                         "connect-failed", report_error_cb,
866                                         get_dbus_sender(peer), NULL);
867         if (err != -EINPROGRESS) {
868                 report_error_cb(peer, false, NULL);
869                 return err;
870         }
871
872         return 0;
873 }
874
875 int connman_peer_set_state(struct connman_peer *peer,
876                                         enum connman_peer_state new_state)
877 {
878         enum connman_peer_state old_state = peer->state;
879         int err;
880
881         DBG("peer (%s) old state %d new state %d", peer->name,
882                                 old_state, new_state);
883
884         if (old_state == new_state)
885                 return -EALREADY;
886
887         switch (new_state) {
888         case CONNMAN_PEER_STATE_UNKNOWN:
889                 return -EINVAL;
890         case CONNMAN_PEER_STATE_IDLE:
891                 if (is_connecting(peer) || is_connected(peer))
892                         return peer_disconnect(peer);
893                 peer->sub_device = NULL;
894                 break;
895         case CONNMAN_PEER_STATE_ASSOCIATION:
896                 break;
897         case CONNMAN_PEER_STATE_CONFIGURATION:
898                 if (peer->connection_master)
899                         err = start_dhcp_server(peer);
900                 else
901                         err = start_dhcp_client(peer);
902                 if (err < 0)
903                         return connman_peer_set_state(peer,
904                                                 CONNMAN_PEER_STATE_FAILURE);
905                 break;
906         case CONNMAN_PEER_STATE_READY:
907                 reply_pending(peer, 0);
908                 __connman_technology_set_connected(CONNMAN_SERVICE_TYPE_P2P, true);
909                 break;
910         case CONNMAN_PEER_STATE_DISCONNECT:
911                 if (peer->connection_master)
912                         stop_dhcp_server(peer);
913                 else
914                         __connman_dhcp_stop(peer->ipconfig);
915                 peer->connection_master = false;
916                 peer->sub_device = NULL;
917                 __connman_technology_set_connected(CONNMAN_SERVICE_TYPE_P2P, false);
918                 break;
919         case CONNMAN_PEER_STATE_FAILURE:
920                 if (manage_peer_error(peer) == 0)
921                         return 0;
922                 break;
923         };
924
925         peer->state = new_state;
926         state_changed(peer);
927
928         if (peer->state == CONNMAN_PEER_STATE_READY ||
929                                 peer->state == CONNMAN_PEER_STATE_DISCONNECT)
930                 settings_changed(peer);
931
932         return 0;
933 }
934
935 int connman_peer_request_connection(struct connman_peer *peer)
936 {
937         return __connman_agent_request_peer_authorization(peer,
938                                         request_authorization_cb, false,
939                                         NULL, NULL);
940 }
941
942 static void peer_service_free(gpointer data)
943 {
944         struct _peer_service *service = data;
945
946         if (!service)
947                 return;
948
949         g_free(service->data);
950         g_free(service);
951 }
952
953 void connman_peer_reset_services(struct connman_peer *peer)
954 {
955         if (!peer)
956                 return;
957
958         g_slist_free_full(peer->services, peer_service_free);
959         peer->services = NULL;
960 }
961
962 void connman_peer_services_changed(struct connman_peer *peer)
963 {
964         if (!peer || !peer->registered || !allow_property_changed(peer))
965                 return;
966
967         connman_dbus_property_changed_array(peer->path,
968                         CONNMAN_PEER_INTERFACE, "Services",
969                         DBUS_TYPE_DICT_ENTRY, append_peer_services, peer);
970 }
971
972 void connman_peer_add_service(struct connman_peer *peer,
973                                 enum connman_peer_service_type type,
974                                 const unsigned char *data, int data_length)
975 {
976         struct _peer_service *service;
977
978         if (!peer || !data || type == CONNMAN_PEER_SERVICE_UNKNOWN)
979                 return;
980
981         service = g_malloc0(sizeof(struct _peer_service));
982         service->type = type;
983         service->data = g_memdup(data, data_length * sizeof(unsigned char));
984         service->length = data_length;
985
986         peer->services = g_slist_prepend(peer->services, service);
987 }
988
989 static void peer_up(struct connman_ipconfig *ipconfig, const char *ifname)
990 {
991         DBG("%s up", ifname);
992 }
993
994 static void peer_down(struct connman_ipconfig *ipconfig, const char *ifname)
995 {
996         DBG("%s down", ifname);
997 }
998
999 static void peer_lower_up(struct connman_ipconfig *ipconfig,
1000                                                         const char *ifname)
1001 {
1002         DBG("%s lower up", ifname);
1003 }
1004
1005 static void peer_lower_down(struct connman_ipconfig *ipconfig,
1006                                                         const char *ifname)
1007 {
1008         struct connman_peer *peer = __connman_ipconfig_get_data(ipconfig);
1009
1010         DBG("%s lower down", ifname);
1011
1012         __connman_ipconfig_disable(ipconfig);
1013         connman_peer_set_state(peer, CONNMAN_PEER_STATE_DISCONNECT);
1014 }
1015
1016 static void peer_ip_bound(struct connman_ipconfig *ipconfig,
1017                                                         const char *ifname)
1018 {
1019         struct connman_peer *peer = __connman_ipconfig_get_data(ipconfig);
1020
1021         DBG("%s ip bound", ifname);
1022
1023         if (peer->state == CONNMAN_PEER_STATE_READY)
1024                 settings_changed(peer);
1025         connman_peer_set_state(peer, CONNMAN_PEER_STATE_READY);
1026 }
1027
1028 static void peer_ip_release(struct connman_ipconfig *ipconfig,
1029                                                         const char *ifname)
1030 {
1031         struct connman_peer *peer = __connman_ipconfig_get_data(ipconfig);
1032
1033         DBG("%s ip release", ifname);
1034
1035         if (peer->state == CONNMAN_PEER_STATE_READY)
1036                 settings_changed(peer);
1037 }
1038
1039 static const struct connman_ipconfig_ops peer_ip_ops = {
1040         .up             = peer_up,
1041         .down           = peer_down,
1042         .lower_up       = peer_lower_up,
1043         .lower_down     = peer_lower_down,
1044         .ip_bound       = peer_ip_bound,
1045         .ip_release     = peer_ip_release,
1046         .route_set      = NULL,
1047         .route_unset    = NULL,
1048 };
1049
1050 static struct connman_ipconfig *create_ipconfig(int index, void *user_data)
1051 {
1052         struct connman_ipconfig *ipconfig;
1053
1054         ipconfig = __connman_ipconfig_create(index,
1055                                                 CONNMAN_IPCONFIG_TYPE_IPV4);
1056         if (!ipconfig)
1057                 return NULL;
1058
1059         __connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_DHCP);
1060         __connman_ipconfig_set_data(ipconfig, user_data);
1061         __connman_ipconfig_set_ops(ipconfig, &peer_ip_ops);
1062
1063         return ipconfig;
1064 }
1065
1066 static const GDBusMethodTable peer_methods[] = {
1067         { GDBUS_METHOD("GetProperties",
1068                         NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
1069                         get_peer_properties) },
1070         { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, connect_peer) },
1071         { GDBUS_METHOD("Disconnect", NULL, NULL, disconnect_peer) },
1072         { },
1073 };
1074
1075 static const GDBusSignalTable peer_signals[] = {
1076         { GDBUS_SIGNAL("PropertyChanged",
1077                         GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
1078         { },
1079 };
1080
1081 static char *get_peer_path(struct connman_device *device,
1082                                         const char *identifier)
1083 {
1084         return g_strdup_printf("%s/peer/peer_%s_%s", CONNMAN_PATH,
1085                                 connman_device_get_ident(device), identifier);
1086 }
1087
1088 int connman_peer_register(struct connman_peer *peer)
1089 {
1090         int index;
1091
1092         DBG("peer %p", peer);
1093
1094         if (peer->path && peer->registered)
1095                 return -EALREADY;
1096
1097         index = connman_device_get_index(peer->device);
1098         peer->ipconfig = create_ipconfig(index, peer);
1099         if (!peer->ipconfig)
1100                 return -ENOMEM;
1101
1102         peer->path = get_peer_path(peer->device, peer->identifier);
1103         DBG("path %s", peer->path);
1104
1105         g_hash_table_insert(peers_table, peer->path, peer);
1106
1107         g_dbus_register_interface(connection, peer->path,
1108                                         CONNMAN_PEER_INTERFACE,
1109                                         peer_methods, peer_signals,
1110                                         NULL, peer, NULL);
1111         peer->registered = true;
1112         peer_added(peer);
1113
1114         return 0;
1115 }
1116
1117 void connman_peer_unregister(struct connman_peer *peer)
1118 {
1119         DBG("peer %p", peer);
1120
1121         if (!peer->path || !peer->registered)
1122                 return;
1123
1124         connman_agent_cancel(peer);
1125         reply_pending(peer, EIO);
1126
1127         g_dbus_unregister_interface(connection, peer->path,
1128                                         CONNMAN_PEER_INTERFACE);
1129         peer->registered = false;
1130         peer_removed(peer);
1131 }
1132
1133 struct connman_peer *connman_peer_get(struct connman_device *device,
1134                                                 const char *identifier)
1135 {
1136         char *ident = get_peer_path(device, identifier);
1137         struct connman_peer *peer;
1138
1139         peer = g_hash_table_lookup(peers_table, ident);
1140         g_free(ident);
1141
1142         return peer;
1143 }
1144
1145 int connman_peer_driver_register(struct connman_peer_driver *driver)
1146 {
1147         if (peer_driver && peer_driver != driver)
1148                 return -EINVAL;
1149
1150         peer_driver = driver;
1151
1152         __connman_peer_service_set_driver(driver);
1153
1154         return 0;
1155 }
1156
1157 void connman_peer_driver_unregister(struct connman_peer_driver *driver)
1158 {
1159         if (peer_driver != driver)
1160                 return;
1161
1162         peer_driver = NULL;
1163
1164         __connman_peer_service_set_driver(NULL);
1165 }
1166
1167 void __connman_peer_list_struct(DBusMessageIter *array)
1168 {
1169         g_hash_table_foreach(peers_table, append_peer_struct, array);
1170 }
1171
1172 const char *__connman_peer_get_path(struct connman_peer *peer)
1173 {
1174         if (!peer || !peer->registered)
1175                 return NULL;
1176
1177         return peer->path;
1178 }
1179
1180 int __connman_peer_init(void)
1181 {
1182         DBG("");
1183
1184         connection = connman_dbus_get_connection();
1185
1186         peers_table = g_hash_table_new_full(g_str_hash, g_str_equal,
1187                                                         NULL, peer_free);
1188
1189         peers_notify = g_new0(struct _peers_notify, 1);
1190         peers_notify->add = g_hash_table_new(g_str_hash, g_str_equal);
1191         peers_notify->remove = g_hash_table_new_full(g_str_hash, g_str_equal,
1192                                                                 g_free, NULL);
1193         return 0;
1194 }
1195
1196 void __connman_peer_cleanup(void)
1197 {
1198         DBG("");
1199
1200         g_hash_table_destroy(peers_notify->remove);
1201         g_hash_table_destroy(peers_notify->add);
1202         g_free(peers_notify);
1203
1204         g_hash_table_destroy(peers_table);
1205         peers_table = NULL;
1206         dbus_connection_unref(connection);
1207         connection = NULL;
1208 }