517 lines
15 KiB
C
517 lines
15 KiB
C
/*
|
|
* wireless_if.c
|
|
* This file is part of Network-inador
|
|
*
|
|
* Copyright (C) 2020 - Félix Arreola Rodríguez
|
|
*
|
|
* Network-inador is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Network-inador is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with Network-inador; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <netlink/genl/ctrl.h>
|
|
#include <netlink/socket.h>
|
|
#include <netlink/msg.h>
|
|
#include <netlink/genl/genl.h>
|
|
|
|
#include <linux/nl80211.h>
|
|
|
|
#include "network-inador-private.h"
|
|
#include "interfaces.h"
|
|
#include "netlink-events.h"
|
|
#include "wireless_if.h"
|
|
#include "wireless_bss.h"
|
|
|
|
uint16_t wireless_if_nl80211_id = 0;
|
|
static uint16_t wireless_if_nl80211_scan_multicast_group_id = 0;
|
|
|
|
int wireless_interface_new_scan_results (NetworkInadorHandle *handle, struct nl_msg *msg);
|
|
void wireless_interface_get_scan (NetworkInadorHandle *handle, Interface *iface);
|
|
void wireless_interface_trigger_scan (NetworkInadorHandle *handle, Interface *iface);
|
|
|
|
struct _wireless_iface_is_wifi {
|
|
int error;
|
|
int is_wifi;
|
|
int index;
|
|
WirelessInfo *info;
|
|
};
|
|
|
|
static void _wireless_if_process_bands (struct nlattr *list_bands, WirelessInfo *info) {
|
|
int remaining, remaining2, remaining3, remaining4;
|
|
struct nlattr *band, *band_attr, *freq, *freq_attr;
|
|
int freq_index;
|
|
|
|
printf ("%s: El info es: %p\n", __func__, info);
|
|
|
|
info->num_freqs = 0;
|
|
printf ("La lista de bandas tiene por tamaño: %i\n", nla_len (list_bands));
|
|
nla_for_each_nested (band, list_bands, remaining) {
|
|
|
|
nla_for_each_nested (band_attr, band, remaining2) {
|
|
if (nla_type (band_attr) != NL80211_BAND_ATTR_FREQS) continue;
|
|
nla_for_each_nested (freq, band_attr, remaining3) {
|
|
printf ("Dentro de BAND attr freqs, atributo es: %i (%i)\n", nla_type (freq), nla_len (freq));
|
|
nla_for_each_nested (freq_attr, freq, remaining4) {
|
|
printf ("Dentro de FREQ attr, atributo es: %i (%i)\n", nla_type (freq_attr), nla_len (freq_attr));
|
|
if (nla_type (freq_attr) != NL80211_FREQUENCY_ATTR_FREQ) continue;
|
|
info->num_freqs++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
printf ("La supuesta cantidad de frecuencias es: %i\n", info->num_freqs);
|
|
info->freqs = (uint32_t *) malloc (sizeof (uint32_t) * info->num_freqs);
|
|
freq_index = 0;
|
|
nla_for_each_nested (band, list_bands, remaining) {
|
|
|
|
nla_for_each_nested (band_attr, band, remaining2) {
|
|
if (nla_type (band_attr) != NL80211_BAND_ATTR_FREQS) continue;
|
|
nla_for_each_nested (freq, band_attr, remaining3) {
|
|
nla_for_each_nested (freq_attr, freq, remaining4) {
|
|
if (nla_type (freq_attr) != NL80211_FREQUENCY_ATTR_FREQ) continue;
|
|
|
|
info->freqs[freq_index] = nla_get_u32 (freq_attr);
|
|
|
|
info->caps |= WIFI_DEVICE_CAP_FREQ_VALID;
|
|
|
|
if (info->freqs[freq_index] > 2400 && info->freqs[freq_index] < 2500) {
|
|
info->caps |= WIFI_DEVICE_CAP_FREQ_2GHZ;
|
|
}
|
|
if (info->freqs[freq_index] > 4900 && info->freqs[freq_index] < 6000) {
|
|
info->caps |= WIFI_DEVICE_CAP_FREQ_5GHZ;
|
|
}
|
|
|
|
printf ("Frecuencia: %i\n", info->freqs[freq_index]);
|
|
freq_index++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static int _wireless_if_cb_valid_is_wifi (struct nl_msg *msg, void *arg) {
|
|
struct _wireless_iface_is_wifi *is_wifi = (struct _wireless_iface_is_wifi *) arg;
|
|
|
|
struct nlmsgerr *l_err;
|
|
struct nlmsghdr *reply;
|
|
struct genlmsghdr *gnlh;
|
|
struct nlattr *attr, *nest_attr;
|
|
int remaining, remaining2;
|
|
|
|
reply = nlmsg_hdr (msg);
|
|
printf ("CB Valid is wifi\n");
|
|
if (reply->nlmsg_type == NLMSG_ERROR) {
|
|
l_err = nlmsg_data (reply);
|
|
|
|
is_wifi->error = l_err->error;
|
|
printf ("---> (type = %i, NLMSG_ERROR = %i) Error %i", reply->nlmsg_type, NLMSG_ERROR, is_wifi->error);
|
|
return NL_SKIP;
|
|
}
|
|
|
|
if (reply->nlmsg_type != wireless_if_nl80211_id) {
|
|
return NL_SKIP;
|
|
}
|
|
gnlh = nlmsg_data (reply);
|
|
|
|
printf ("---> type = nl80211_id, ");
|
|
printf ("CMD: %i\n", gnlh->cmd);
|
|
|
|
if (gnlh->cmd != NL80211_CMD_NEW_WIPHY) {
|
|
/* Ignorar */
|
|
return NL_SKIP;
|
|
}
|
|
|
|
/* Como está la información de la interfaz, es una interfaz wifi */
|
|
is_wifi->is_wifi = 1;
|
|
|
|
nlmsg_for_each_attr(attr, reply, sizeof (struct genlmsghdr), remaining) {
|
|
printf ("Atributo de la interfaz wifi: %i\n", nla_type (attr));
|
|
switch (nla_type (attr)) {
|
|
case NL80211_ATTR_WIPHY:
|
|
is_wifi->info->phy = nla_get_u32 (attr);
|
|
break;
|
|
case NL80211_ATTR_SUPPORTED_COMMANDS:
|
|
nla_for_each_nested (nest_attr, attr, remaining2) {
|
|
switch (nla_get_u32 (nest_attr)) {
|
|
case NL80211_CMD_TRIGGER_SCAN:
|
|
is_wifi->info->can_scan = TRUE;
|
|
break;
|
|
case NL80211_CMD_CONNECT:
|
|
case NL80211_CMD_AUTHENTICATE:
|
|
/* Only devices that support CONNECT or AUTH actually support
|
|
* 802.11, unlike say ipw2x00 (up to at least kernel 3.4) which
|
|
* has minimal info support, but no actual command support.
|
|
* This check mirrors what wpa_supplicant does to determine
|
|
* whether or not to use the nl80211 driver.
|
|
*/
|
|
is_wifi->info->supported = TRUE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case NL80211_ATTR_WIPHY_BANDS:
|
|
printf ("Hay attr wiphy bands\n");
|
|
_wireless_if_process_bands (attr, is_wifi->info);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return NL_SKIP;
|
|
}
|
|
|
|
static int _wireless_if_wait_ack_or_error (struct nl_msg *msg, void *arg) {
|
|
int *ret = (int *) arg;
|
|
struct nlmsgerr *l_err;
|
|
struct nlmsghdr *reply;
|
|
|
|
reply = nlmsg_hdr (msg);
|
|
printf ("--> _wireless_if_wait_ack_or_error");
|
|
if (reply->nlmsg_type == NLMSG_ERROR) {
|
|
l_err = nlmsg_data (reply);
|
|
|
|
*ret = l_err->error;
|
|
printf ("---> (type = %i, NLMSG_ERROR = %i) Error %i", reply->nlmsg_type, NLMSG_ERROR, *ret);
|
|
} else {
|
|
printf ("---> type = %i (nl802 = %i).", reply->nlmsg_type, wireless_if_nl80211_id);
|
|
}
|
|
printf ("\n");
|
|
return NL_SKIP;
|
|
}
|
|
|
|
static int _wireless_if_wait_error (struct sockaddr_nl *nla, struct nlmsgerr *l_err, void *arg) {
|
|
int *ret = (int *) arg;
|
|
|
|
*ret = l_err->error;
|
|
printf ("--> _wireless_if_wait_error\n");
|
|
return NL_SKIP;
|
|
}
|
|
|
|
static int _wireless_if_valid (struct nl_msg *msg, void *arg) {
|
|
printf ("CB Valid\n");
|
|
return _wireless_if_wait_ack_or_error (msg, arg);
|
|
}
|
|
|
|
static int _wireless_if_invalid (struct nl_msg *msg, void *arg) {
|
|
printf ("CB INValid\n");
|
|
return _wireless_if_wait_ack_or_error (msg, arg);
|
|
}
|
|
|
|
static int _wireless_if_ack (struct nl_msg *msg, void *arg) {
|
|
printf ("CB ACK\n");
|
|
return _wireless_if_wait_ack_or_error (msg, arg);
|
|
}
|
|
|
|
#if 0
|
|
gboolean wireless_interface_timer_trigger (gpointer data) {
|
|
NetworkInadorHandle *handle = (NetworkInadorHandle *) data;
|
|
Interface *iface;
|
|
|
|
static int c = 0;
|
|
|
|
printf ("Ejecutando Trigger SCAN\n");
|
|
|
|
iface = _interfaces_locate_by_index (handle->interfaces, 3);
|
|
if (iface == NULL) return FALSE;
|
|
wireless_interface_trigger_scan (handle, iface);
|
|
|
|
if (c == 0) {
|
|
c++;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
static void wireless_interface_init_nl80211 (NetworkInadorHandle *handle) {
|
|
#if 0
|
|
int family_id;
|
|
int mcid;
|
|
struct nl_sock * sock_req;
|
|
|
|
sock_req = nl_socket_alloc ();
|
|
|
|
if (nl_connect (sock_req, NETLINK_GENERIC) != 0) {
|
|
perror ("Falló conectar netlink socket GENERIC\n");
|
|
|
|
return;
|
|
}
|
|
|
|
family_id = genl_ctrl_resolve (sock_req, "nl80211");
|
|
|
|
if (family_id < 0) {
|
|
nl_socket_free (sock_req);
|
|
return;
|
|
}
|
|
|
|
mcid = genl_ctrl_resolve_grp (sock_req, "nl80211", "scan");
|
|
|
|
if (mcid >= 0) {
|
|
/* Solo si tenemos el id del grupo multicast crear el generic de eventos */
|
|
netlink_events_create_pair (&handle->nl80211_scan, NETLINK_GENERIC);
|
|
if (handle->nl80211_scan.nl_sock != NULL) {
|
|
nl_socket_add_membership (handle->nl80211_scan.nl_sock, mcid);
|
|
nl_socket_modify_cb (handle->nl80211_scan.nl_sock, NL_CB_VALID, NL_CB_CUSTOM, wireless_events_dispatcher, handle);
|
|
}
|
|
wireless_if_nl80211_scan_multicast_group_id = mcid;
|
|
}
|
|
|
|
netlink_events_create_pair (&handle->nl80211_scan_results, NETLINK_GENERIC);
|
|
nl_socket_modify_cb (handle->nl80211_scan_results.nl_sock, NL_CB_VALID, NL_CB_CUSTOM, wireless_bss_parse_station_scan, handle);
|
|
nl_socket_modify_cb (handle->nl80211_scan_results.nl_sock, NL_CB_FINISH, NL_CB_CUSTOM, wireless_bss_finish_scan, handle);
|
|
|
|
/* Guardar la familia nl80211 */
|
|
wireless_if_nl80211_id = family_id;
|
|
handle->nl_sock_nl80211 = sock_req;
|
|
|
|
/* Instalar un timer para ejecutar TRIGGER_SCAN cada minuto */
|
|
//g_timeout_add_seconds (50, wireless_interface_timer_trigger, handle);
|
|
#endif
|
|
}
|
|
|
|
int wireless_events_dispatcher (struct nl_msg *msg, void *arg) {
|
|
#if 0
|
|
NetworkInadorHandle *handle = (NetworkInadorHandle *) arg;
|
|
struct nlmsghdr *reply;
|
|
struct genlmsghdr *gnlh;
|
|
struct nlattr *attr;
|
|
int remaining;
|
|
Interface *iface;
|
|
int has_bss;
|
|
int index;
|
|
|
|
reply = nlmsg_hdr (msg);
|
|
|
|
if (reply->nlmsg_type != wireless_if_nl80211_id) return NL_SKIP;
|
|
|
|
gnlh = nlmsg_data (reply);
|
|
|
|
switch (gnlh->cmd) {
|
|
case NL80211_CMD_NEW_SCAN_RESULTS:
|
|
/* Es un mensaje del grupo multicast "scan" */
|
|
has_bss = 0;
|
|
index = 0;
|
|
iface = NULL;
|
|
nlmsg_for_each_attr(attr, reply, sizeof (struct genlmsghdr), remaining) {
|
|
if (nla_type (attr) == NL80211_ATTR_BSS) {
|
|
has_bss = 1;
|
|
} else if (nla_type (attr) == NL80211_ATTR_IFINDEX) {
|
|
if (nla_len (attr) != 4) {
|
|
/* Tamaño incorrecto para el nuevo master */
|
|
continue;
|
|
}
|
|
index = nla_get_u32 (attr);
|
|
printf ("CMD_NEW_SCAN_RESULTS sobre interfaz %i\n", index);
|
|
}
|
|
}
|
|
|
|
if (index > 0) {
|
|
iface = _interfaces_locate_by_index (handle->interfaces, index);
|
|
}
|
|
|
|
if (iface == NULL) {
|
|
/* Si no hay interfaz, tenemos un gran problema */
|
|
break;
|
|
}
|
|
|
|
if (has_bss == 1) {
|
|
/* Es un mensaje de una estación */
|
|
printf ("Mensaje NEW RESULTS con estación\n");
|
|
} else {
|
|
/* Ejecutar el CMD_GET_SCAN */
|
|
printf ("Ejecutando GET_SCAN...\n");
|
|
wireless_interface_get_scan (handle, iface);
|
|
}
|
|
return NL_SKIP;
|
|
break;
|
|
}
|
|
|
|
return NL_SKIP;
|
|
#endif
|
|
}
|
|
|
|
void wireless_interface_trigger_scan (NetworkInadorHandle *handle, Interface *iface) {
|
|
#if 0
|
|
struct nl_msg *msg;
|
|
int error, ret;
|
|
struct nlattr *ssid;
|
|
|
|
msg = nlmsg_alloc ();
|
|
if (msg == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (iface->is_wireless == 0) {
|
|
/* No puedo ejecutar trigger scan sobre una interfaz que no he visto como wireless */
|
|
return;
|
|
}
|
|
|
|
printf ("Ejecutando NL80211_CMD_TRIGGER_SCAN\n");
|
|
|
|
genlmsg_put (msg, NL_AUTO_PORT, NL_AUTO_SEQ, wireless_if_nl80211_id, 0, 0, NL80211_CMD_TRIGGER_SCAN, 0);
|
|
|
|
nla_put_u32 (msg, NL80211_ATTR_IFINDEX, iface->index);
|
|
|
|
ssid = nla_nest_start (msg, NL80211_ATTR_SCAN_SSIDS);
|
|
nla_put (msg, 1, 0, ""); // Scan all SSIDs.
|
|
nla_nest_end (msg, ssid);
|
|
|
|
nl_complete_msg (handle->nl_sock_nl80211, msg);
|
|
|
|
ret = nl_send (handle->nl_sock_nl80211, msg);
|
|
|
|
nlmsg_free (msg);
|
|
if (ret <= 0) {
|
|
return;
|
|
}
|
|
|
|
error = 0;
|
|
nl_socket_modify_cb (handle->nl_sock_nl80211, NL_CB_VALID, NL_CB_CUSTOM, _wireless_if_valid, &error);
|
|
nl_socket_modify_cb (handle->nl_sock_nl80211, NL_CB_INVALID, NL_CB_CUSTOM, _wireless_if_invalid, &error);
|
|
nl_socket_modify_cb (handle->nl_sock_nl80211, NL_CB_ACK, NL_CB_CUSTOM, _wireless_if_ack, &error);
|
|
nl_socket_modify_err_cb (handle->nl_sock_nl80211, NL_CB_CUSTOM, _wireless_if_wait_error, &error);
|
|
|
|
nl_recvmsgs_default (handle->nl_sock_nl80211);
|
|
|
|
if (ret <= 0 || error < 0) {
|
|
printf ("Error al hacer NL80211_CMD_TRIGGER_SCAN error = %i\n", error);
|
|
return;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void wireless_interface_get_scan (NetworkInadorHandle *handle, Interface *iface) {
|
|
#if 0
|
|
struct nl_msg *msg;
|
|
int error, ret;
|
|
struct genlmsghdr *gnlh;
|
|
struct nlmsghdr *req;
|
|
|
|
msg = nlmsg_alloc ();
|
|
if (msg == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (iface->is_wireless == 0) {
|
|
/* No puedo ejecutar Get scan sobre una interfaz que no he visto como wireless */
|
|
return;
|
|
}
|
|
|
|
printf ("Ejecutando CMD_GET_SCAN\n");
|
|
|
|
req = nlmsg_put (msg, NL_AUTO_PORT, NL_AUTO_SEQ, wireless_if_nl80211_id, sizeof (struct genlmsghdr), NLM_F_DUMP);
|
|
gnlh = (struct genlmsghdr *) nlmsg_data (req);
|
|
|
|
gnlh->cmd = NL80211_CMD_GET_SCAN;
|
|
gnlh->version = 0;
|
|
|
|
nla_put_u32 (msg, NL80211_ATTR_IFINDEX, iface->index);
|
|
|
|
nl_complete_msg (handle->nl80211_scan_results.nl_sock, msg);
|
|
|
|
ret = nl_send (handle->nl80211_scan_results.nl_sock, msg);
|
|
|
|
nlmsg_free (msg);
|
|
if (ret <= 0) {
|
|
return;
|
|
}
|
|
|
|
//nl_socket_modify_cb (handle->nl80211_scan_results, NL_CB_VALID, NL_CB_CUSTOM, _wireless_if_valid, &error);
|
|
/*nl_socket_modify_cb (handle->nl80211_scan_results, NL_CB_INVALID, NL_CB_CUSTOM, _wireless_if_invalid, &error);
|
|
nl_socket_modify_cb (handle->nl80211_scan_results, NL_CB_ACK, NL_CB_CUSTOM, _wireless_if_ack, &error);
|
|
nl_socket_modify_err_cb (handle->nl80211_scan_results, NL_CB_CUSTOM, _wireless_if_wait_error, &error);*/
|
|
|
|
//nl_recvmsgs_default (handle->nl_sock_nl80211);
|
|
|
|
if (ret <= 0) {
|
|
printf ("Error al hacer CMD_GET_SCAN error = %i\n", error);
|
|
//return;
|
|
}
|
|
|
|
//nl_recvmsgs_default (handle->nl_sock_nl80211);
|
|
#endif
|
|
}
|
|
|
|
void wireless_interface_check (NetworkInadorHandle *handle, Interface *iface) {
|
|
#if 0
|
|
struct nl_msg * msg;
|
|
int ret;
|
|
struct _wireless_iface_is_wifi is_wifi;
|
|
WirelessInfo *info;
|
|
|
|
if (wireless_if_nl80211_id == 0) {
|
|
wireless_interface_init_nl80211 (handle);
|
|
}
|
|
|
|
info = (WirelessInfo *) malloc (sizeof (WirelessInfo));
|
|
|
|
if (info == NULL) {
|
|
return;
|
|
}
|
|
|
|
msg = nlmsg_alloc ();
|
|
if (msg == NULL) {
|
|
free (info);
|
|
return;
|
|
}
|
|
|
|
genlmsg_put (msg, NL_AUTO_PORT, NL_AUTO_SEQ, wireless_if_nl80211_id, 0, 0, NL80211_CMD_GET_WIPHY, 0); // Setup which command to run.
|
|
nla_put_u32 (msg, NL80211_ATTR_IFINDEX, iface->index);
|
|
|
|
nl_complete_msg (handle->nl_sock_nl80211, msg);
|
|
|
|
ret = nl_send (handle->nl_sock_nl80211, msg);
|
|
|
|
nlmsg_free (msg);
|
|
if (ret <= 0) {
|
|
free (info);
|
|
return;
|
|
}
|
|
|
|
memset (&is_wifi, 0, sizeof (is_wifi));
|
|
memset (info, 0, sizeof (WirelessInfo));
|
|
is_wifi.info = info;
|
|
|
|
nl_socket_modify_cb (handle->nl_sock_nl80211, NL_CB_VALID, NL_CB_CUSTOM, _wireless_if_cb_valid_is_wifi, &is_wifi);
|
|
nl_socket_modify_cb (handle->nl_sock_nl80211, NL_CB_INVALID, NL_CB_CUSTOM, _wireless_if_invalid, &is_wifi.error);
|
|
nl_socket_modify_cb (handle->nl_sock_nl80211, NL_CB_ACK, NL_CB_CUSTOM, _wireless_if_ack, &is_wifi.error);
|
|
nl_socket_modify_err_cb (handle->nl_sock_nl80211, NL_CB_CUSTOM, _wireless_if_wait_error, &is_wifi.error);
|
|
|
|
nl_recvmsgs_default (handle->nl_sock_nl80211);
|
|
|
|
if (ret <= 0 || is_wifi.error < 0) {
|
|
free (info);
|
|
return;
|
|
}
|
|
|
|
if (is_wifi.is_wifi != 0) {
|
|
/* Es una interfaz inalámbrica */
|
|
printf ("La interfaz %s es inalambrica\n", iface->name);
|
|
|
|
iface->is_wireless = 1;
|
|
iface->wireless = info;
|
|
} else {
|
|
free (info);
|
|
}
|
|
#endif
|
|
}
|