blob: 8207ed27e3d5b994a7948472ead43f0d0d48b072 [file] [log] [blame]
/*
* If not stated otherwise in this file or this component's LICENSE file the
* following copyright and licenses apply:
*
* Copyright 2019 RDK Management
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdbool.h>
#include "wifi_hal.h"
#include "client_wifi_hal.h"
#include <ev.h>
#include <wpa_ctrl.h>
#include <errno.h>
#include <net/if.h>
#define CTRL_INTERFACE "/var/run/wpa_supplicant"
/* Helper wpa_supplicant events */
#ifndef container_of
#define offsetof(st, m) ((size_t)&(((st *)0)->m))
#define container_of(ptr, type, member) \
((type *)((char *)ptr - offsetof(type, member)))
#endif /* container_of */
static int _syscmd(char *cmd, char *retBuf, int retBufSize)
{
FILE *f;
char *ptr = retBuf;
int bufSize=retBufSize, bufbytes=0, readbytes=0, cmd_ret=0;
if((f = popen(cmd, "r")) == NULL) {
fprintf(stderr,"\npopen %s error\n", cmd);
return RETURN_ERR;
}
while(!feof(f))
{
*ptr = 0;
if(bufSize>=128) {
bufbytes=128;
} else {
bufbytes=bufSize-1;
}
fgets(ptr,bufbytes,f);
readbytes=strlen(ptr);
if(!readbytes)
break;
bufSize-=readbytes;
ptr += readbytes;
}
cmd_ret = pclose(f);
retBuf[retBufSize-1]=0;
return cmd_ret >> 8;
}
struct ctrl {
char sockpath[128];
char sockdir[128];
char bss[IFNAMSIZ];
int ssid_index;
void (*cb)(struct ctrl *ctrl, int level, const char *buf, size_t len);
void (*overrun)(struct ctrl *ctrl);
void (*closed)(struct ctrl *ctrl);
struct wpa_ctrl *wpa;
unsigned int ovfl;
int initialized;
ev_timer retry;
ev_stat stat;
ev_io io;
char reply[4096];
size_t reply_len;
ev_timer watchdog;
};
static wifi_client_event_callback clients_connect_cb;
static struct ctrl wpa_ctrl[2];
static int client_initialized;
static void ctrl_close(struct ctrl *ctrl)
{
if (ctrl->io.cb)
ev_io_stop(EV_DEFAULT_ &ctrl->io);
if (ctrl->retry.cb)
ev_timer_stop(EV_DEFAULT_ &ctrl->retry);
if (ctrl->watchdog.cb)
ev_timer_stop(EV_DEFAULT_ &ctrl->watchdog);
if (!ctrl->wpa)
return;
wpa_ctrl_detach(ctrl->wpa);
wpa_ctrl_close(ctrl->wpa);
ctrl->wpa = NULL;
if (ctrl->closed)
ctrl->closed(ctrl);
}
static void ctrl_process(struct ctrl *ctrl)
{
char *str;
size_t len;
char buf[1024];
int drops;
int level;
int err;
char * k;
char * v;
char *kv;
wifi_client_associated_dev_t ap;
memset(&ap, 0, sizeof(ap));
/* Example events:
*
* CTRL-EVENT-CONNECTED - Connection to 00:1d:73:73:88:ea completed [id=0 id_str=]
* CTRL-EVENT-DISCONNECTED bssid=00:1d:73:73:88:ea reason=3 locally_generated=1
*/
if (!(str = index(ctrl->reply, '>')))
return;
if (sscanf(ctrl->reply, "<%d>", &level) != 1)
return;
str++;
if (strncmp("CTRL-EVENT-CONNECTED ", str, 21) == 0) {
strsep(&str, " "); /* "-" */
strsep(&str, " "); /* "Connection" */
strsep(&str, " "); /* "to" */
sscanf(str, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
&ap.MACAddress[0], &ap.MACAddress[1], &ap.MACAddress[2],
&ap.MACAddress[3], &ap.MACAddress[4], &ap.MACAddress[5]);
strsep(&str, " [id="); // completed
ap.NetworkID = atoi(str);
ap.connected = true;
(clients_connect_cb)(ctrl->ssid_index, &ap);
goto handled;
}
if (strncmp("CTRL-EVENT-DISCONNECTED ", str, 24) == 0) {
while ((kv = strsep(&str, " "))) {
if ((k = strsep(&kv, "=")) &&
(v = strsep(&kv, ""))) {
if (!strcmp(k, "bssid"))
sscanf(v, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
&ap.MACAddress[0], &ap.MACAddress[1], &ap.MACAddress[2],
&ap.MACAddress[3], &ap.MACAddress[4], &ap.MACAddress[5]);
else if (!strcmp(k, "reason"))
ap.reason = atoi(v);
else if (!strcmp(k, "locally_generated"))
ap.locally_generated = atoi(v);
}
}
ap.connected = false;
(clients_connect_cb)(ctrl->ssid_index, &ap);
goto handled;
}
handled:
return;
}
static void ctrl_ev_cb(EV_P_ struct ev_io *io, int events) {
struct ctrl *ctrl = container_of(io, struct ctrl, io);
int err;
ctrl->reply_len = sizeof(ctrl->reply) - 1;
err = wpa_ctrl_recv(ctrl->wpa, ctrl->reply, &ctrl->reply_len);
ctrl->reply[ctrl->reply_len] = 0;
if (err < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK)
return;
ctrl_close(ctrl);
ev_timer_again(EV_A_ &ctrl->retry);
return;
}
ctrl_process(ctrl);
}
static int ctrl_open(struct ctrl *ctrl)
{
int fd;
if (ctrl->wpa)
return 0;
ctrl->wpa = wpa_ctrl_open(ctrl->sockpath);
if (!ctrl->wpa)
goto err;
if (wpa_ctrl_attach(ctrl->wpa) < 0)
goto err_close;
fd = wpa_ctrl_get_fd(ctrl->wpa);
if (fd < 0)
goto err_detach;
ev_io_init(&ctrl->io, ctrl_ev_cb, fd, EV_READ);
ev_io_start(EV_DEFAULT_ &ctrl->io);
ev_timer_again(EV_DEFAULT_ &ctrl->watchdog);
return 0;
err_detach:
wpa_ctrl_detach(ctrl->wpa);
err_close:
wpa_ctrl_close(ctrl->wpa);
err:
ctrl->wpa = NULL;
return -1;
}
static void ctrl_msg_cb(char *buf, size_t len)
{
struct ctrl *ctrl = container_of(buf, struct ctrl, reply);
ctrl_process(ctrl);
}
static int ctrl_request(struct ctrl *ctrl, const char *cmd, size_t cmd_len, char *reply, size_t *reply_len)
{
int err;
if (!ctrl->wpa)
return -1;
if (*reply_len < 2)
return -1;
(*reply_len)--;
ctrl->reply_len = sizeof(ctrl->reply);
err = wpa_ctrl_request(ctrl->wpa, cmd, cmd_len, ctrl->reply, &ctrl->reply_len, ctrl_msg_cb);
if (err < 0)
return err;
if (ctrl->reply_len > *reply_len)
ctrl->reply_len = *reply_len;
*reply_len = ctrl->reply_len;
memcpy(reply, ctrl->reply, *reply_len);
reply[*reply_len] = 0;
return 0;
}
static void
ctrl_watchdog_cb(EV_P_ ev_timer *timer, int events)
{
struct ctrl *ctrl = container_of(timer, struct ctrl, watchdog);
const char *pong = "PONG";
const char *ping = "PING";
char reply[1024];
size_t len = sizeof(reply);
int err;
err = ctrl_request(ctrl, ping, strlen(ping), reply, &len);
if (err == 0 && len > strlen(pong) && !strncmp(reply, pong, strlen(pong))) {
return;
}
ctrl_close(ctrl);
ev_timer_again(EV_A_ &ctrl->retry);
}
static void ctrl_stat_cb(EV_P_ ev_stat *stat, int events)
{
struct ctrl *ctrl = container_of(stat, struct ctrl, stat);
printf("%s: file state changed", ctrl->bss);
ctrl_open(ctrl);
}
static void ctrl_retry_cb(EV_P_ ev_timer *timer, int events)
{
struct ctrl *ctrl = container_of(timer, struct ctrl, retry);
printf("CTRL %s: retrying", ctrl->bss);
if (ctrl_open(ctrl) < 0)
ev_timer_again(EV_DEFAULT_ &ctrl->retry);
}
static int ctrl_enable(struct ctrl *ctrl)
{
if (ctrl->wpa)
return 0;
if (!ctrl->stat.cb) {
ev_stat_init(&ctrl->stat, ctrl_stat_cb, ctrl->sockpath, 0.);
ev_stat_start(EV_DEFAULT_ &ctrl->stat);
}
if (!ctrl->retry.cb)
ev_timer_init(&ctrl->retry, ctrl_retry_cb, 0., 5.);
if (!ctrl->watchdog.cb)
ev_timer_init(&ctrl->watchdog, ctrl_watchdog_cb, 0., 30.);
return ctrl_open(ctrl);
}
/* client API */
INT wifi_getSTANumberOfEntries(ULONG *output) //Tr181
{
char cmd[128] = {0};
char buf[16] = {0};
if (NULL == output)
return RETURN_ERR;
snprintf(cmd, sizeof(cmd), "wpa_cli -g%s-global status | grep ^ifname | wc -l", CTRL_INTERFACE);
_syscmd(cmd, buf, sizeof(buf));
// *output = 2;
*output = strtoll(buf, NULL, 10);
return RETURN_OK;
}
INT wifi_getSTAName(INT apIndex, CHAR *output_string)
{
int radioIndex = 0;
int staIndex = 0;
if (NULL == output_string || apIndex < 0)
return RETURN_ERR;
wifi_getSTARadioIndex(apIndex, &radioIndex);
staIndex = apIndex % radioIndex;
snprintf(output_string, 33, "radio%d-sta%d", radioIndex, staIndex);
return RETURN_OK;
}
INT wifi_getSTARadioIndex(INT ssidIndex, INT *radioIndex)
{
if (NULL == radioIndex)
return RETURN_ERR;
*radioIndex = ssidIndex%MAX_NUM_RADIOS;
return RETURN_OK;
}
INT wifi_getSTAMAC(INT ssidIndex, CHAR *output_string)
{
char cmd[128] = {0};
int ret = 0;
char ssid_ifname[128];
if (NULL == output_string)
return RETURN_ERR;
ret = wifi_getSTAName(ssidIndex, ssid_ifname);
if (ret != RETURN_OK)
{
return RETURN_ERR;
}
sprintf(cmd, "wpa_cli -i%s status |grep '^address' | cut -f 2 -d =", ssid_ifname);
_syscmd(cmd, output_string, 64);
return RETURN_OK;
}
INT wifi_getSTABSSID(INT ssidIndex, CHAR *output_string)
{
char cmd[128] = {0};
int ret = 0;
char ssid_ifname[128];
if (NULL == output_string)
return RETURN_ERR;
ret = wifi_getSTAName(ssidIndex, ssid_ifname);
if (ret != RETURN_OK)
{
return RETURN_ERR;
}
sprintf(cmd, "wpa_cli -i%s status |grep bssid | cut -f 2 -d =", ssid_ifname);
_syscmd(cmd, output_string, 64);
return RETURN_OK;
}
INT wifi_setSTABSSID(INT ssidIndex, CHAR *input_string)
{
char cmd[128] = {0};
int ret = 0;
char ssid_ifname[128];
char buf[128] = {0};
ret = wifi_getSTAName(ssidIndex, ssid_ifname);
if (ret != RETURN_OK)
return RETURN_ERR;
snprintf(cmd, sizeof(cmd), "ip link set dev %s address %s", ssid_ifname, input_string);
_syscmd(cmd, buf, sizeof(buf));
return RETURN_OK;
}
INT wifi_getSTASSID(INT ssidIndex, CHAR *output_string)
{
char cmd[128] = {0};
int ret = 0;
char ssid_ifname[128];
if (NULL == output_string)
return RETURN_ERR;
ret = wifi_getSTAName(ssidIndex, ssid_ifname);
if (ret != RETURN_OK)
{
return RETURN_ERR;
}
sprintf(cmd, "wpa_cli -i%s status |grep ^ssid | cut -f 2 -d = | tr -d '\n'", ssid_ifname);
_syscmd(cmd, output_string, 64);
return RETURN_OK;
}
INT wifi_getSTACredentials(INT ssidIndex, CHAR *output_string)
{
char cmd[128] = {0};
int ret = 0;
char ssid_ifname[128];
if (NULL == output_string)
return RETURN_ERR;
ret = wifi_getSTAName(ssidIndex, ssid_ifname);
if (ret != RETURN_OK)
{
return RETURN_ERR;
}
sprintf(cmd, "wpa_cli -i%s status |grep ssid | cut -f 2 -d =", ssid_ifname);
_syscmd(cmd, output_string, 64);
return RETURN_OK;
}
static int init_client_wpa()
{
int ret = 0, i = 0;
ULONG s, snum;
char * sock_path;
char ssid_ifname[128];
ret = wifi_getSTANumberOfEntries(&snum);
if (ret != RETURN_OK) {
printf("%s: failed to get SSID count", __func__);
return RETURN_ERR;
}
for (s = 0; s < snum; s++) {
ret = wifi_getSTAName(s, ssid_ifname);
if (ret != RETURN_OK)
{
return RETURN_ERR;
}
sprintf(wpa_ctrl[s].sockpath, "%s/%s", CTRL_INTERFACE, ssid_ifname);
wpa_ctrl[s].ssid_index = s;
printf("Opening ctrl for %s\n", ssid_ifname);
if (ctrl_enable(&wpa_ctrl[s]))
{
return RETURN_ERR;
}
}
client_initialized = 1;
return RETURN_OK;
}
void wifi_client_event_callback_register(wifi_client_event_callback callback_proc)
{
clients_connect_cb = callback_proc;
printf("Registering callback STA\n");
if (!client_initialized)
init_client_wpa();
}
INT wifi_getSTANetworks(INT apIndex, wifi_sta_network_t **out_staNetworks_array, INT out_array_size, BOOL *out_scan_cur_freq)
{
FILE *fd = NULL;
char fname[100];
char * line = NULL;
char * pos = NULL;
size_t len = 0;
ssize_t read = 0;
int id = 0;
int ret = 0;
char * k;
char * v;
char *kv;
wifi_sta_network_t * staNetwork;
if(out_array_size <= 0 )
return RETURN_ERR;
char ssid_ifname[128];
ret = wifi_getSTAName(apIndex, ssid_ifname);
if (ret != RETURN_OK)
return RETURN_ERR;
snprintf(fname, sizeof(fname), "/nvram/%s.conf", ssid_ifname);
fd = fopen(fname, "r");
if (!fd)
return RETURN_ERR;
staNetwork= *out_staNetworks_array;
while ((read = getline(&line, &len, fd)) != -1) {
if(!strncmp(line, "network={",strlen("network={"))) {
read = getline(&line, &len, fd) ;
staNetwork->id = id;
while (strncmp(line,"}",1)) {
if ((k = strsep(&line, "=")) &&
(v = strsep(&line, ""))) {
if (!strcmp(k, "\tssid"))
{
v++; //skip quote
v = strsep(&v, "\"");
strncpy(staNetwork->ssid, v,32);
}
else if (!strcmp(k, "\tpsk"))
{
v++; //skip quote
v = strsep(&v, "\"");
strncpy(staNetwork->psk, v,128);
}
else if (!strcmp(k, "\tpairwise"))
strncpy(staNetwork->pairwise, v,64);
else if (!strcmp(k, "\tkey_mgmt"))
strncpy(staNetwork->key_mgmt, v,64);
else if (!strcmp(k, "\tproto"))
{
v = strsep(&v, "\n");
strncpy(staNetwork->proto, v,64);
}
else if (!strcmp(k, "\tbssid"))
sscanf(v, "%02x:%02x:%02x:%02x:%02x:%02x",
(unsigned int *)&staNetwork->bssid[0],
(unsigned int *)&staNetwork->bssid[1],
(unsigned int *)&staNetwork->bssid[2],
(unsigned int *)&staNetwork->bssid[3],
(unsigned int *)&staNetwork->bssid[4],
(unsigned int *)&staNetwork->bssid[5]);
else if (!strcmp(k, "multi_ap_backhaul_sta"))
staNetwork->multi_ap = atoi(v);
}
if((read = getline(&line, &len, fd)) == -1)
break;
}
id++;
if(id >= out_array_size)
goto close;
staNetwork++;
}
}
close:
fclose(fd);
return RETURN_OK;
}
INT wifi_setSTANetworks(INT apIndex, wifi_sta_network_t **staNetworks_array, INT array_size, BOOL scan_cur_freq)
{
FILE *fd = NULL;
char fname[100] = {0};
char cmd[128] = {0};
char out[64] = {0};
int ret = 0;
char zero[8] = {0};
bool enable_current = FALSE;
wifi_sta_network_t *sta = NULL;
if(array_size < 0)
return RETURN_ERR;
char ssid_ifname[128];
ret = wifi_getSTAName(apIndex, ssid_ifname);
if (ret != RETURN_OK)
return RETURN_ERR;
snprintf(fname, sizeof(fname), "/nvram/%s.conf", ssid_ifname);
sprintf(cmd, "touch %s && cp %s /nvram/%s.old", fname, fname, ssid_ifname);
_syscmd(cmd, out, 64);
fd = fopen(fname, "w");
if (!fd) {
return RETURN_ERR;
}
fprintf(fd, "ctrl_interface=%s\n", CTRL_INTERFACE);
fprintf(fd, "scan_cur_freq=%d\n", scan_cur_freq ? 1 : 0);
sta = *staNetworks_array;
for(int i=0; i<array_size; ++i, sta++) {
fprintf(fd, "network={\n");
fprintf(fd, "\tscan_ssid=1\n");
fprintf(fd, "\tbgscan=\"\"\n");
fprintf(fd, "\tssid=\"%s\"\n", sta->ssid);
if (sta->psk_len > 7 && sta->psk_len < 128)
fprintf(fd, "\tpsk=\"%s\"\n", sta->psk);
fprintf(fd, "\tkey_mgmt=%s\n", sta->key_mgmt);
if (!strncmp(sta->key_mgmt, "SAE", 3) || !strncmp(sta->key_mgmt, "OWE", 3)) { // wpa3-personal must use with ieee80211w
fprintf(fd, "\tieee80211w=2\n");
}
fprintf(fd, "\tpairwise=%s\n", sta->pairwise);
if (memcmp(sta->bssid, zero, sizeof(sta->bssid)) != 0) // Not 00:00:00:00:00:00 means need to specify ap mac address.
fprintf(fd, "bssid=%02x:%02x:%02x:%02x:%02x:%02x\n", sta->bssid[0],sta->bssid[1],sta->bssid[2],sta->bssid[3],sta->bssid[4],sta->bssid[5]);
fprintf(fd, "}\n");
}
fclose(fd);
wifi_getSTAEnabled(apIndex, &enable_current);
sprintf(cmd, "diff -q /nvram/%s.conf /nvram/%s.old", ssid_ifname, ssid_ifname);
if (enable_current && _syscmd(cmd, out, 64))
{
sprintf(cmd, "wpa_cli -B -i%s reconfigure", ssid_ifname);
_syscmd(cmd, out, 64);
}
sprintf(cmd, "rm /nvram/%s.old", ssid_ifname);
_syscmd(cmd, out, 64);
return RETURN_OK;
}
INT wifi_getSTAEnabled(INT ssidIndex, BOOL *enabled)
{
char ssid_ifname[128];
char cmd[128] = {0};
char out[64] = {0};
int ret = 0;
ret = wifi_getSTAName(ssidIndex, ssid_ifname);
if (ret != RETURN_OK)
{
return RETURN_ERR;
}
sprintf(cmd, "wpa_cli -g/var/run/wpa_supplicant-global -i global status | grep %s", ssid_ifname);
ret = _syscmd(cmd, out, 64);
*enabled = ret == 0 ? true : false;
return RETURN_OK;
}
INT wifi_setSTAEnabled(INT ssidIndex, BOOL enable)
{
char ssid_ifname[128];
char cmd[256] = {0};
char out[64] = {0};
int ret = 0;
int radioIndex = 0;
BOOL en;
wifi_getSTAEnabled(ssidIndex,&en);
if (enable == en)
return RETURN_OK;
ret = wifi_getSTAName(ssidIndex, ssid_ifname);
if (ret != RETURN_OK)
return RETURN_ERR;
if (enable) {
snprintf(cmd, sizeof(cmd), "wpa_cli -g/var/run/wpa_supplicant-global interface_add %s /nvram/%s.conf nl80211 /var/run/wpa_supplicant", ssid_ifname, ssid_ifname);
ret = _syscmd(cmd, out, 64);
} else {
snprintf(cmd, sizeof(cmd), "wpa_cli -g/var/run/wpa_supplicant-global -i global interface_remove %s", ssid_ifname);
ret = _syscmd(cmd, out, 64);
}
return ret == 0 ? RETURN_OK : RETURN_ERR;
}
INT wifi_createSTAInterface(INT ssidIndex, char *bssid, BOOL wds_flag)
{
char cmd[128] = {0};
char buf[128] = {0};
char ssid_ifname[128] = {0};
int radioIndex = 0;
int phyIndex = 0;
int ret = 0;
ret = wifi_getSTAName(ssidIndex, ssid_ifname);
if (ret != RETURN_OK)
return RETURN_ERR;
wifi_getSTARadioIndex(ssidIndex, &radioIndex);
phyIndex = radio_index_to_phy(radioIndex);
if (phyIndex == -1) {
fprintf(stderr, "%s: Invalid radio index %d.\n", __func__, radioIndex);
return RETURN_ERR;
}
snprintf(cmd, sizeof(cmd), "iw phy phy%d interface add %s type managed %s", phyIndex, ssid_ifname, wds_flag?"4addr on":"");
_syscmd(cmd, buf, sizeof(buf));
snprintf(cmd, sizeof(cmd), "ip link set dev %s address %s && ip link set dev %s up", ssid_ifname, bssid, ssid_ifname);
_syscmd(cmd, buf, sizeof(buf));
return RETURN_OK;
}