Implement sending 'enable ethernet' to the ConnMan
[platform/core/connectivity/wifi-mesh-manager.git] / src / mesh-softap.c
1 /*
2  * Network Configuration Module
3  *
4  * Copyright (c) 2017 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  */
19 #include <glib.h>
20 #include <tzplatform_config.h>
21 #include <openssl/evp.h>
22 #include <openssl/sha.h>
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <netinet/in.h>
31 #include <sys/socket.h>
32 #include <sys/un.h>
33 #include <sys/wait.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36
37 #include "mesh.h"
38 #include "mesh-log.h"
39 #include "mesh-util.h"
40 #include "mesh-service.h"
41 #include "mesh-softap.h"
42
43 #define MOBILE_AP_WIFI_KEY_MAX_LEN      64      /**< Maximum length of wifi hash key */
44
45 #define HOSTAPD_VENDOR_ELEMENTS_WIFI_AP "DD050016321000"        /* Specific application mode AP (e.g. GroupPlay) */
46 #define HOSTAPD_CONF            "interface=%s\n" \
47                                 "driver=nl80211\n" \
48                                 "ctrl_interface=%s\n" \
49                                 "ssid=%s\n" \
50                                 "channel=%d\n" \
51                                 "ignore_broadcast_ssid=%d\n" \
52                                 "hw_mode=%s\n" \
53                                 "max_num_sta=%d\n" \
54                                 "macaddr_acl=%d\n" \
55                                 "accept_mac_file=%s\n" \
56                                 "deny_mac_file=%s\n" \
57                                 "ieee80211n=1\n"
58 #define HOSTAPD_CONF_LEN        1024
59 #define HOSTAPD_BIN             "/usr/sbin/hostapd"
60 #define HOSTAPD_DEBUG_FILE      "/var/log/mesh_hostapd.log"
61 #define HOSTAPD_ENTROPY_FILE    tzplatform_mkpath(TZ_SYS_VAR, "/lib/misc/hostapd.bin")
62 #define HOSTAPD_MESH_CONF_FILE  tzplatform_mkpath(TZ_SYS_VAR, "/lib/mesh/mesh_hostapd.conf")
63 #define HOSTAPD_CTRL_INTF_DIR   tzplatform_mkpath(TZ_SYS_RUN, "/hostapd")
64 #define HOSTAPD_PID_FILE                tzplatform_mkpath(TZ_SYS_RUN, "/.mesh_hostapd.pid")
65 #define HOSTAPD_ALLOWED_LIST    tzplatform_mkpath(TZ_SYS_VAR, "/lib/hostapd/hostapd.accept")
66 #define HOSTAPD_BLOCKED_LIST    tzplatform_mkpath(TZ_SYS_VAR, "/lib/hostapd/hostapd.deny")
67 #define HOSTAPD_RETRY_MAX       5
68 #define HOSTAPD_RETRY_DELAY     500000  /* us */
69
70 #define MH_CTRL_INTF            "/tmp/mesh_hostapd_wpa_ctrl"
71
72 #define PSK_ITERATION_COUNT     4096
73 #define MAX_BUF_SIZE            (256u)
74
75 static int hostapd_ctrl_fd = 0;
76
77 static int __get_psk_hexascii(const char *pass, const unsigned char *salt,
78                 char *psk, unsigned int psk_len)
79 {
80         int i = 0;
81         int d_16 = 0;
82         int r_16 = 0;
83         unsigned char buf[SHA256_DIGEST_LENGTH] = {0, };
84
85         if (pass == NULL || salt == NULL || psk == NULL
86                         || psk_len < (SHA256_DIGEST_LENGTH * 2 + 1)) {
87                 MESH_LOGE("Invalid parameter");
88                 return MESHD_ERROR_INVALID_PARAMETER;
89         }
90
91         if (!PKCS5_PBKDF2_HMAC_SHA1(pass, strlen(pass),
92                                 salt, strlen((const char *)salt),
93                                 PSK_ITERATION_COUNT, sizeof(buf), buf)) {
94                 MESH_LOGE("Getting psk is failed");
95                 return MESHD_ERROR_OPERATION_FAILED;
96         }
97
98         for (i = 0; i < SHA256_DIGEST_LENGTH; i++) {
99                 d_16 = buf[i] >> 4;
100                 r_16 = buf[i] & 0xf;
101
102                 psk[i << 1] = d_16 < 10 ? d_16 + '0' : d_16 - 10 + 'a';
103                 psk[(i << 1) + 1] = r_16 < 10 ? r_16 + '0' : r_16 - 10 + 'a';
104         }
105         psk[i << 1] = '\0';
106
107         return MESHD_ERROR_NONE;
108 }
109
110 static int __config_hostapd(const char *softap_interface, const char *ssid,
111                 const char *security, const char *passphrase, const char* mode,
112                 int channel, int visibility, int mac_filter, int max_sta)
113 {
114         char *conf = NULL;
115         char *old_conf;
116         char buf[HOSTAPD_CONF_LEN] = "";
117         FILE *fp = NULL;
118         int ret;
119         char key[MOBILE_AP_WIFI_KEY_MAX_LEN + 1];
120         char *hw_mode = NULL;
121
122         if (mode == NULL)
123                 hw_mode = g_strdup("g");
124         else
125                 hw_mode = g_strdup(mode);
126
127         /* Default conf. */
128         snprintf(buf, sizeof(buf), HOSTAPD_CONF,
129                         softap_interface,
130                         HOSTAPD_CTRL_INTF_DIR,
131                         ssid,
132                         channel,
133                         (visibility ? 0 : 2),
134                         hw_mode,
135                         max_sta,
136                         mac_filter,
137                         HOSTAPD_ALLOWED_LIST,
138                         HOSTAPD_BLOCKED_LIST);
139         conf = g_strdup(buf);
140
141         free(hw_mode);
142
143         /* Vendor elements conf. */
144         snprintf(buf, sizeof(buf),
145                         "vendor_elements=%s\n", HOSTAPD_VENDOR_ELEMENTS_WIFI_AP);
146         old_conf = conf;
147         conf = g_strconcat(old_conf, buf, NULL);
148         g_free(old_conf);
149
150         /* Security conf. */
151         if (security != NULL && g_strcmp0(security, "wpa2-psk") == 0) {
152                 ret = __get_psk_hexascii(passphrase,
153                                 (const unsigned char *)ssid, key, sizeof(key));
154                 if (ret != MESHD_ERROR_NONE) {
155                         g_free(conf);
156                         MESH_LOGE("hex conversion failed");
157                         return MESHD_ERROR_OPERATION_FAILED;
158                 }
159                 snprintf(buf, sizeof(buf),
160                                 "wpa=2\nrsn_pairwise=CCMP\nwpa_psk=%s\n", key);
161
162                 old_conf = conf;
163                 conf = g_strconcat(old_conf, buf, NULL);
164                 g_free(old_conf);
165         } else {
166                 MESH_LOGD("Open connection [%s]", security);
167         }
168
169         fp = fopen(HOSTAPD_MESH_CONF_FILE, "w");
170         if (NULL == fp) {
171                 MESH_LOGE("Could not create the file.");
172                 g_free(conf);
173                 return MESHD_ERROR_IO_ERROR;
174         }
175
176         if (conf) {
177                 fputs(conf, fp);
178                 g_free(conf);
179         }
180         fclose(fp);
181
182         return MESHD_ERROR_NONE;
183 }
184
185 static int __open_hostapd_intf(const char* softap_interface, int *fd,
186                 const char *intf)
187 {
188         int retry = 0;
189         char ctrl_intf[255] = {0, };
190         struct sockaddr_un src;
191         struct sockaddr_un dest;
192         struct stat stat_buf;
193
194         if (fd == NULL || intf == NULL) {
195                 MESH_LOGE("fd is NULL");
196                 return MESHD_ERROR_INVALID_PARAMETER;
197         }
198
199         *fd = socket(PF_UNIX, SOCK_DGRAM, 0);
200         if (*fd < 0) {
201                 MESH_LOGE("socket is failed");
202                 return MESHD_ERROR_OPERATION_FAILED;
203         }
204
205         src.sun_family = AF_UNIX;
206         g_strlcpy(src.sun_path, intf, sizeof(src.sun_path));
207
208         if (stat(src.sun_path, &stat_buf) == 0)
209                 unlink(src.sun_path);
210
211         if (bind(*fd, (struct sockaddr *)&src, sizeof(src)) < 0) {
212                 MESH_LOGE("bind is failed");
213                 close(*fd);
214                 *fd = -1;
215                 unlink(src.sun_path);
216                 return MESHD_ERROR_OPERATION_FAILED;
217         }
218
219         snprintf(ctrl_intf, sizeof(ctrl_intf), "%s/%s",
220                         HOSTAPD_CTRL_INTF_DIR, softap_interface);
221         dest.sun_family = AF_UNIX;
222         g_strlcpy(dest.sun_path, ctrl_intf, sizeof(dest.sun_path));
223
224         while (connect(*fd, (struct sockaddr *)&dest, sizeof(dest)) < 0) {
225                 if (++retry >= HOSTAPD_RETRY_MAX)
226                         goto FAIL;
227                 usleep(HOSTAPD_RETRY_DELAY);
228         }
229         MESH_LOGD("Successfully open interface[%s] to hostapd", intf);
230
231         return MESHD_ERROR_NONE;
232
233 FAIL:
234         MESH_LOGE("Cannot make connection to hostapd");
235         close(*fd);
236         *fd = -1;
237         unlink(src.sun_path);
238
239         return MESHD_ERROR_OPERATION_FAILED;
240 }
241
242 static int __close_hostapd_intf(int *fd)
243 {
244         if (fd == NULL) {
245                 MESH_LOGE("fd is NULL");
246                 return MESHD_ERROR_INVALID_PARAMETER;
247         }
248
249         if (*fd > 0)
250                 close(*fd);
251         *fd = -1;
252
253         return MESHD_ERROR_NONE;
254 }
255
256 static int __get_pid_of_hostapd(pid_t *pid)
257 {
258         FILE *fd = NULL;
259         int ret = -1;
260         int rv = 0;
261         char error_buf[256] = {0, };
262         char file_path[256] = {0, };
263
264         snprintf(file_path, 256, "%s", HOSTAPD_PID_FILE);
265
266         if (0 == access(file_path, F_OK)) {
267                 fd = fopen(file_path, "r");
268                 if (fd == NULL) {
269                         MESH_LOGE("Error! Could not open pid file");
270                         return MESHD_ERROR_IO_ERROR;
271                 }
272         } else {
273                 MESH_LOGE("Error! Could not access pid file");
274                 return MESHD_ERROR_IO_ERROR;
275         }
276
277         errno = 0;
278         rv = fscanf(fd, "%d", &ret);
279         if (rv < 0) {
280                 strerror_r(errno, error_buf, 256);
281                 MESH_LOGE("Error! Failed to read from file, rv:[%d], error:[%s]",
282                                 rv, error_buf);
283                 fclose(fd);
284                 return MESHD_ERROR_IO_ERROR;
285         }
286
287         *pid = (pid_t)ret;
288         MESH_LOGD("  PID: [%d]", ret);
289
290         fclose(fd);
291         return MESHD_ERROR_NONE;
292 }
293
294 static int __terminate_hostapd()
295 {
296         int ret;
297         pid_t hostapd_pid = 0;
298
299         /* Get pid */
300         ret = __get_pid_of_hostapd(&hostapd_pid);
301         if (MESHD_ERROR_NONE != ret) {
302                 MESH_LOGE("There is no hostapd");
303                 return MESHD_ERROR_NONE;
304         }
305
306         if (hostapd_pid == 0) {
307                 MESH_LOGE("There is no hostapd");
308                 return MESHD_ERROR_NONE;
309         }
310
311         ret = __close_hostapd_intf(&hostapd_ctrl_fd);
312         if (ret != MESHD_ERROR_NONE)
313                 MESH_LOGE("__close_hostapd_intf is failed");
314
315         kill(hostapd_pid, SIGTERM);
316
317         return MESHD_ERROR_NONE;
318 }
319
320 static int __execute_hostapd()
321 {
322         pid_t pid = 0;
323         int status;
324         int ret = MESHD_ERROR_NONE;
325
326         ret = __get_pid_of_hostapd(&pid);
327         if (0 != pid) {
328                 MESH_LOGE("hostapd is running already");
329                 return MESHD_ERROR_NONE;
330         }
331
332         pid = fork();
333         if (pid < 0) {
334                 MESH_LOGE("fork failed");
335                 return MESHD_ERROR_IO_ERROR;
336         }
337
338         if (pid == 0) {
339                 if (execl(HOSTAPD_BIN, HOSTAPD_BIN, "-e", HOSTAPD_ENTROPY_FILE,
340                                         HOSTAPD_MESH_CONF_FILE,
341                                         "-f", HOSTAPD_DEBUG_FILE,
342                                         "-P", HOSTAPD_PID_FILE,
343                                         "-ddd", "-B",
344                                         (char *)NULL)) {
345                         MESH_LOGE("execl failed");
346                 }
347
348                 MESH_LOGE("Should not get here!");
349                 exit(1);
350         } else {
351                 /* Reap child */
352                 waitpid(pid, &status, 0);
353                 MESH_LOGD("  child [%d] reaped with status(%d)", pid, status);
354         }
355
356         return MESHD_ERROR_NONE;
357 }
358
359 int mesh_softap_set_configuration(const char* softap_interface,
360                 const char *ssid, const char* mode, int channel, int visibility,
361                 int max_sta, int security, const char* passphrase)
362 {
363         int ret = MESHD_ERROR_NONE;
364         const char *sec = (security == 0) ? NULL : "wpa2-psk";
365         int mac_filter = 0;
366
367         ret = __config_hostapd(softap_interface, ssid, sec, passphrase,
368                         mode, channel, visibility, mac_filter, max_sta);
369
370         return ret;
371 }
372
373 int mesh_softap_enable_softap(const char* softap_interface)
374 {
375         int ret = MESHD_ERROR_NONE;
376
377         if (NULL == softap_interface) {
378                 MESH_LOGE("Invalid parameter");
379                 return MESHD_ERROR_INVALID_PARAMETER;
380         }
381
382         ret = __execute_hostapd();
383         if (ret != MESHD_ERROR_NONE) {
384                 MESH_LOGE("__execute_hostapd is failed");
385                 return MESHD_ERROR_OPERATION_FAILED;
386         }
387
388         ret = __open_hostapd_intf(softap_interface,
389                         &hostapd_ctrl_fd, MH_CTRL_INTF);
390         if (ret != MESHD_ERROR_NONE) {
391                 MESH_LOGE("__open_hostapd_intf is failed");
392                 __terminate_hostapd();
393         }
394
395         return ret;
396 }
397
398 int mesh_softap_disable_softap()
399 {
400         int ret = MESHD_ERROR_NONE;
401
402         ret = __terminate_hostapd();
403         if (ret != MESHD_ERROR_NONE) {
404                 MESH_LOGE("__execute_hostapd is failed");
405                 return MESHD_ERROR_OPERATION_FAILED;
406         }
407
408         return ret;
409 }