Skip to content
Permalink
master
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time
#include "main.h"
#include <QDebug>
#include <string>
#include <netinet/ether.h>
#include <sstream>
using namespace std;
#define HARDWARE_STATUS_FIFO "/tmp/nickel-hardware-status"
static const int SIGNAL_STRENGTH_UPDATE_INTERVAL = 3000000;
static const int SCANNING_NETWORKS_WAIT_INTERVAL = 1000000;
int main(int argc, char *argv[])
{
pid_t pid;
// Fork off!
pid = fork();
if (pid < 0)
{
exit(1);
}
if (pid > 0)
{
exit(0);
}
// Initialize variables
openSocket = iw_sockets_open();
interface = string(getenv("INTERFACE"));
networkName = "";
networkMode = 0;
networkMacAddress = "";
networkSecurityType = "";
networkLinkQuality = 0;
monitoringLinkQuality = false;
scanning = false;
rescanOnEachIteration = false;
totalScanIterations = 20; // 5 seconds
// Open FIFO pipe.
fd = open(HARDWARE_STATUS_FIFO, O_WRONLY);
// Read the arguments passed to us.
for(int i = 0; i != argc; ++i)
{
string argument(argv[i]);
if(argument.substr(0, 4) == "scan")
{
startScanningForNetworks();
}
else if (argument.substr(0, 11) == "forcerescan")
{
rescanOnEachIteration = true;
totalScanIterations = 7;
}
else if(argument.substr(0, 4) == "info")
{
string message = "echo network details:NAME:" + getCurrentNetworkName() + "BITRATE:" + getBitRateDescription()
+ ">>" + HARDWARE_STATUS_FIFO;
system(message.c_str());
}
else if(argument.substr(0, 11) == "linkquality")
{
monitoringLinkQuality = true;
}
}
int intervalsSinceScan = 0;
int intervalsSinceLinkQuality = 0;
// Start our event loop.
while(monitoringLinkQuality || scanning)
{
if (monitoringLinkQuality && (intervalsSinceLinkQuality >= SIGNAL_STRENGTH_UPDATE_INTERVAL || intervalsSinceLinkQuality == 0))
{
fireLinkQualityUpdate();
intervalsSinceLinkQuality = 0;
}
if (scanning)
{
continueScan();
intervalsSinceScan = 0;
intervalsSinceScan += SCANNING_NETWORKS_WAIT_INTERVAL;
if (!scanning) {
string scanFinished = "echo scan finished >> " + string(HARDWARE_STATUS_FIFO);
system(scanFinished.c_str());
}
}
intervalsSinceLinkQuality += SCANNING_NETWORKS_WAIT_INTERVAL;
usleep(SCANNING_NETWORKS_WAIT_INTERVAL);
}
}
std::string escapeForShell(const std::string& s)
{
std::string specialChars (" !#$%&'()*+,-./}~:;>=<?@[\\]^_`{|\""); /* might not need to escape ALL of these; taken as-is from dbck-2484 test case */
std::string ret;
for (unsigned int i = 0; i < s.length(); ++i)
{
char c = s.at(i);
if (specialChars.find(c) != specialChars.npos)
{
ret += '\\';
}
ret += c;
}
return ret;
}
float linkQuality()
{
float quality = -1;
if (openSocket <= 0) {
return -1;
}
iwstats stats;
iwrange range;
const char *iface = interface.c_str();
if (iw_get_range_info(openSocket, iface, &range) >= 0) {
if (iw_get_stats(openSocket, iface, &stats, &range, 1) >= 0) {
// These magic numbers mean we're not connected anymore.
if (stats.qual.qual == 255 && stats.qual.level == 160) {
return -1;
}
signed char level = stats.qual.level;
signed char noise = stats.qual.noise;
signed char ratio = level - noise;
if (ratio > 40) {
quality = 0.8;
}
else if (ratio > 25) {
quality = 0.6;
}
else if (ratio > 15) {
quality = 0.4;
}
else if (ratio > 10) {
quality = 0.2;
}
else {
quality = 0.0;
}
}
}
return quality;
}
int getBitRate()
{
int bitRate = -1;
if (openSocket <= 0) {
return -1;
}
struct iwreq wrq;
memset(&wrq, 0, sizeof(struct iwreq));
int err = iw_get_ext(openSocket, interface.c_str(), SIOCGIWRATE, &wrq);
if (err >= 0) {
bitRate = wrq.u.bitrate.value;
}
return bitRate;
}
std::string getBitRateDescription()
{
int bitRate = getBitRate();
if (bitRate == -1) {
return "";
} else {
char buffer[32];
iw_print_bitrate(buffer, sizeof(buffer), bitRate);
return string(buffer);
}
}
std::string getCurrentNetworkName()
{
string name;
char essid[4 * IW_ESSID_MAX_SIZE + 1];
if (openSocket > 0)
{
struct iwreq wrq;
memset(&wrq, 0x00, sizeof(struct iwreq));
memset(essid, '\0', sizeof(essid));
wrq.u.essid.pointer = (caddr_t) essid;
wrq.u.essid.length = IW_ESSID_MAX_SIZE + 2;
wrq.u.essid.flags = 0;
if(iw_get_ext(openSocket, interface.c_str(), SIOCGIWESSID, &wrq) >= 0) {
name = essid;
}
}
return name;
}
void printInformationElement(unsigned char* buffer, int length)
{
printf("Dumping IE of length %d\n", length);
int offset = 0;
int part = 0;
while (offset < (length - 2))
{
unsigned char partType = buffer[offset + 0];
unsigned char partLength = buffer[offset + 1];
offset += 2;
printf(" Part #%.2d of length %d\n", partType, partLength);
//unsigned char* buffer = &buffer[offset];
for (int i = 0; i < partLength; i++) {
printf(" #%.4d 0x%.2x\n", i, buffer[offset++]);
}
++part;
}
}
bool find8021XAuthenticationSuiteInIE(unsigned char* buffer)
{
unsigned char type = buffer[0];
unsigned char length = buffer[1];
buffer += 2;
bool found = false;
// Only interested in IEs of type WPA or WPA2 (not sure what the official names for those types are)
if (type == 0x30 || type == 0xdd)
{
// Only interested if the version, group cipher, pairwise ciphers are there. Otherwise it is PSK
if ((type == 0x30 && length > 14) || (type == 0xdd && length > 17))
{
// WPA has the OUI first, so skip that
if (type == 0xdd)
{
static unsigned char wpa1_oui[3] = {0x00, 0x50, 0xf2};
// But not all IEs that start with 0xdd are WPA. So check that the OUI is valid.
if (memcmp(&buffer[0], wpa1_oui, 3) != 0 || buffer[3] != 0x01) {
return false;
}
buffer += 4;
}
// Skip the Version Number and the Group Cipher
buffer += 2 + 4;
// Skip the Pairwise Ciphers
uint16_t count = buffer[0] | (buffer[1] << 8);
buffer += 2 + (count * 4);
// Loop over the Authentication Suites and see if 802.1X is in there
count = buffer[0] | (buffer[1] << 8);
buffer += 2;
for (int i = 0; i < count; i++) {
if (buffer[3] == IW_AUTH_KEY_MGMT_802_1X) {
found = true;
break;
}
buffer += 4;
}
}
}
return found;
}
bool find8021XAuthenticationSuite(unsigned char* buffer, int length)
{
int offset = 0;
while (offset < (length - 2))
{
unsigned char partType = buffer[offset + 0];
unsigned char partLength = buffer[offset + 1];
if (partType == 0x30 || partType == 0xdd) {
if (find8021XAuthenticationSuiteInIE(&buffer[offset])) {
return true;
}
}
offset += 2 + partLength;
}
return false;
}
bool findWPS(unsigned char* buffer, int length)
{
static unsigned char wps_oui[4] = {0x00, 0x50, 0xf2, 0x04};
int offset = 0;
while (offset < (length - 2))
{
switch (buffer[offset])
{
case 0xdd:
if (memcmp(&buffer[offset+2], wps_oui, 4) == 0) {
return true;
}
break;
default:
break;
}
// Jump to the next IE.
offset += buffer[offset+1] + 2;
}
return false;
}
unsigned char findWPAType(unsigned char *buffer, int length)
{
static unsigned char wpa1_oui[3] = {0x00, 0x50, 0xf2};
int offset = 0;
// Scan the IEs until we find a WPA one. We assume that they are listed in order of
// preference. If that is truly the case I do not know. Worst case is that we will
// pick WPA1 over WPA2 while both are available. Should just work though.
while (offset < (length - 2))
{
switch (buffer[offset])
{
case 0xdd:
{
// Yay. Magic. There is no API for this. We should probably buy the WPA spec.
// Not all UEs that start with 0xdd are WPA. So check that the OUI is valid.
// WPA1 IE looks like this: 0xdd 0xNN 0x00 0x50 0xf2 (NN is length .. 3?)
if (memcmp(&buffer[offset+2], wpa1_oui, 3) == 0 && buffer[offset+5] == 0x01) {
return 0xdd;
}
break;
}
case 0x30:
{
return 0x30;
break;
}
}
// Jump to the next IE.
offset += buffer[offset+1] + 2;
}
return 0;
}
bool processScan()
{
struct iw_event event;
struct stream_descr stream;
iw_init_event_stream(&stream, scanBuffer, _wrq.u.data.length);
bool skipNetwork = false;
bool foundWPAType = false;
bool supportsWPS = false;
if (iw_extract_event_stream(&stream, &event, _range.we_version_compiled) <= 0) {
return false;
}
do {
switch (event.cmd)
{
case SIOCGIWAP:
{
if (skipNetwork == false && networkMacAddress.length() > 0 && networkName.length() > 0) {
string scannedNetwork;
ostringstream o;
o << networkLinkQuality;
ostringstream p;
p << networkMode;
scannedNetwork = "echo network scanned:NAME:" + escapeForShell(networkName) + "MACADDRESS:" + networkMacAddress + "SECURITYTYPE:" + networkSecurityType + "LINKQUALITY:" + o.str() + "MODE:" + p.str() + "WPS:" + (supportsWPS ? "1" : "0") + " >> " + HARDWARE_STATUS_FIFO;
system(scannedNetwork.c_str());
networkName = "";
networkMode = 0;
networkMacAddress = "";
networkSecurityType = "";
networkLinkQuality = 0;
}
foundWPAType = false;
skipNetwork = false;
supportsWPS = false;
char buffer[32];
networkMacAddress = string(ether_ntoa_r((const struct ether_addr *) &event.u.ap_addr.sa_data, buffer));
break;
}
case SIOCGIWESSID:
{
char essid[4*IW_ESSID_MAX_SIZE+1];
memset(essid, '\0', sizeof(essid));
if((event.u.essid.pointer) && (event.u.essid.length)) {
memcpy(essid, event.u.essid.pointer, event.u.essid.length);
}
networkName = essid;
break;
}
case IWEVQUAL:
{
float linkQuality = 0.0;
signed char level = event.u.qual.level;
signed char noise = event.u.qual.noise;
signed char ratio = level - noise;
if (ratio > 40) {
linkQuality = 0.8;
}
else if (ratio > 25) {
linkQuality = 0.6;
}
else if (ratio > 15) {
linkQuality = 0.4;
}
else if (ratio > 10) {
linkQuality = 0.2;
}
else {
linkQuality = 0.0;
}
networkLinkQuality = linkQuality;
break;
}
case SIOCGIWENCODE:
{
// There is no explicit WEP identifier so we default to WEP if security is enabled. We
// will change the security type to WPA or WPA2 when the IE events are coming in.
if (foundWPAType) {
break;
}
if (event.u.data.flags & IW_ENCODE_DISABLED) {
networkSecurityType = "NONE";
}
else {
networkSecurityType = "WEP";
}
break;
}
case IWEVGENIE:
{
if (!supportsWPS) {
supportsWPS = findWPS((unsigned char*)event.u.data.pointer,event.u.data.length);
}
// We sometimes get multiple IE events. Let's only take the first correct one.
if (foundWPAType) {
break;
}
// Parse the IE to find out if this AP uses WPA or WPA2.
unsigned char type = findWPAType((unsigned char*) event.u.data.pointer, event.u.data.length);
switch (type) {
case 0x30:
foundWPAType = true;
networkSecurityType = "WPA2";
break;
case 0xdd:
foundWPAType = true;
networkSecurityType = "WPA";
break;
}
// If this is WPA then parse the IE again to see if it is 802.1x
if (type != 0x00) {
skipNetwork = find8021XAuthenticationSuite((unsigned char*)event.u.data.pointer,event.u.data.length);
}
break;
}
}
}
while (iw_extract_event_stream(&stream, &event, _range.we_version_compiled) > 0);
// The stream just stops, there is no 'end of network info' event. So add the last network.
if (skipNetwork == false && networkMacAddress.length()) {
string scannedNetwork;
ostringstream o;
o << networkLinkQuality;
ostringstream p;
p << networkMode;
scannedNetwork = "echo network scanned:NAME:" + escapeForShell(networkName) + "MACADDRESS:" + networkMacAddress +
"SECURITYTYPE:" + networkSecurityType + "LINKQUALITY:" + o.str() + "MODE:" + p.str() + "WPS:" + (supportsWPS ? "1" : "0") + " >> " + HARDWARE_STATUS_FIFO;
system(scannedNetwork.c_str());
foundWPAType = false;
networkName = "";
networkMode = 0;
networkMacAddress = "";
networkSecurityType = "";
networkLinkQuality = 0;
}
return true;
}
void continueScan()
{
// Setup a results buffer if we don't have one yet.
if (scanBuffer == NULL) {
scanBufferSize = 65535;
scanBuffer = (char*) malloc(scanBufferSize);
_wrq.u.data.pointer = scanBuffer;
_wrq.u.data.flags = 0;
_wrq.u.data.length = scanBufferSize;
}
// Try to read the results
int ret = iw_get_ext(openSocket, interface.c_str(), SIOCGIWSCAN, &_wrq);
if (ret < 0) {
if (errno == EAGAIN) {
// Not all results are available s wait for event loop to check back again...
}
else {
scanning = false;
}
} else {
bool ok = processScan();
scanCount++;
if (scanCount == totalScanIterations) {
scanning = false;
return;
}
if (!ok || rescanOnEachIteration) {
if (initiateScan() == false) {
scanning = false;
return;
}
}
}
}
bool initiateScan()
{
// Initiate a new scan
if (scanBuffer != NULL) {
free(scanBuffer);
scanBuffer = NULL;
}
_wrq.u.data.pointer = NULL;
_wrq.u.data.flags = 0;
_wrq.u.data.length = 0;
if (iw_set_ext(openSocket, interface.c_str(), SIOCSIWSCAN, &_wrq) < 0) {
printf("(2) Interface does not support scanning (iw_set_ext failed)");
return false;
}
// We will check back by the event loop...
return true;
}
void startScanningForNetworks()
{
if (openSocket == -1) {
openSocket = iw_sockets_open();
}
if (openSocket == -1) {
printf("Didn't open socket!\n");
return;
}
// Get range stuff
if (iw_get_range_info(openSocket, interface.c_str(), &_range) < 0) {
printf("(1) Interface does not support scanning (iw_get_range_info failed)");
string scanFinished = "echo scan finished >> " + string(HARDWARE_STATUS_FIFO);
system(scanFinished.c_str());
return;
}
if (initiateScan()) {
scanning = true;
scanCount = 0;
}
}
void fireLinkQualityUpdate()
{
float quality = linkQuality();
if (quality != -1) {
ostringstream o;
o << quality;
string linkQualityUpdate = "echo link quality:" +o.str() + ">>" + HARDWARE_STATUS_FIFO;
system(linkQualityUpdate.c_str());
}
else {
string networkDeconfig = string("echo link lost >> ") + HARDWARE_STATUS_FIFO;
system(networkDeconfig.c_str());
}
}