/*------------------------------------------------------------------------------ NAME iwlist - Get more detailed wireless information from a wireless interface SYNOPSIS iwlist [interface] scanning DESCRIPTION Iwlist is used to display some additional information from a wireless network interface that is not displayed by iwconfig(8). The main argument is used to select a category of information, iwlist displays in detailed form all information related to this category, including information already shown by iwconfig(8). PARAMETERS scan[ning] Give the list of Access Points and Ad-Hoc cells in range, and optionally a whole bunch of information about them (ESSID, Quality, Frequency, Mode...). The type of information returned depends on what the card supports. Triggering scanning is a privileged operation (root only) and normal users can only read left-over scan results. By default, the way scanning is done (the scope of the scan) is dependant on the card and card settings. This command takes optional arguments, however most drivers will ignore those. The option essid is used to specify a scan on a specific ESSID. With some card/driver, this enables to see hidden networks. The option last does not trigger a scan and read left-over scan results. SEE ALSO iwconfig(8), iwspy(8). iwevent(8), iwpriv(8), wireless(7). https://www.centurylink.com/home/help/internet/wireless/which-frequency-should-you-use.html Standard Frequency Theoretical Speed Real World Speed 802.11g 2.4Ghz 54 Mbps 10 -29 Mbps 802.11n 2.4Ghz 300 Mpbs 150 Mbps 802.11n 5Ghz 900 Mbps 450Mbps 802.11ac 5Ghz 433 Mbps - 1.7 Gbps 210 Mbps - 1 G ------------------------------------------------------------------------------*/ #include #include // Input/output #include // General utilities #include // String handling /*------------------------------------------------------------------------------ ------------------------------------------------------------------------------*/ typedef struct wifi_channel_data { double signal_level_dB; double frequency; double metric; char SSID_string[256]; char quality_string[256]; int quality_out_of_70; int channel; } CHANNEL_DATA; /*----------------------------------------------------------------------------- compare two channel entries -----------------------------------------------------------------------------*/ int compare_channels(const void *channel_a, const void *channel_b) { int comparison; if(((CHANNEL_DATA*)channel_a)->metric == ((CHANNEL_DATA*)channel_b)->metric) { comparison = 0; } else if(((CHANNEL_DATA*)channel_a)->metric < ((CHANNEL_DATA*)channel_b)->metric) { comparison = +1; } else { comparison = -1; } return comparison; } /*------------------------------------------------------------------------------ ------------------------------------------------------------------------------*/ void report_error(char *error) { printf("error\t: %s\n", error); printf("WiFi scan failed\n"); getchar(); exit(EXIT_FAILURE); } /*------------------------------------------------------------------------------ ------------------------------------------------------------------------------*/ void print_channel_data(CHANNEL_DATA *channel_data_list) { CHANNEL_DATA *channel_data; int count = 0; puts(""); channel_data = channel_data_list; while(channel_data->quality_out_of_70 > 0 && channel_data->channel != -1) { count++; printf ( "\t%2d \tSSID %-20s %3.0lfdB\t\tchannel %3d\tmetric %6.1lf\n", count, channel_data->SSID_string, channel_data->signal_level_dB, channel_data->channel, channel_data->metric ); channel_data++; } } /*------------------------------------------------------------------------------ a rather crude parsing of the output from iwlist ------------------------------------------------------------------------------*/ void parse_the_scan_line(char *scan_line, char *ssid, CHANNEL_DATA **channel_data) { char *position; if((position = strstr(scan_line, "Quality=")) != NULL) { // obtain both quality and signal strength // which are on the same line strcpy((*channel_data)->quality_string, scan_line); (*channel_data)->quality_out_of_70 = atoi(position + strlen("Quality=")); //printf("quality_out_of_70\t: %3d\n", (*channel_data)->quality_out_of_70); if((position = strstr(scan_line, "Signal level=")) != NULL) { (*channel_data)->signal_level_dB = atof(position + strlen("Signal level=")); } else { (*channel_data)->signal_level_dB = -100; } //printf("signal_level_dB\t\t: %3.0lfdB\n", (*channel_data)->signal_level_dB); } else if((position = strstr(scan_line, "Frequency:")) != NULL) { //printf("\n\t\"%s\"\n", scan_line); (*channel_data)->frequency = atof(position + strlen("Frequency:")); //printf("frequency\t\t: %7.3lf GHz\n", (*channel_data)->frequency); if((position = strstr(scan_line, "Channel:")) != NULL) { (*channel_data)->channel = atoi(position + strlen("Channel:")); //printf("\nchannel\t\t\t: %3d\n", (*channel_data)->channel); } } else if((position = strstr(scan_line, "Channel:")) != NULL) { (*channel_data)->channel = atoi(position + strlen("Channel:")); //printf("\nchannel\t\t\t: %3d\n", (*channel_data)->channel); } else if((position = strstr(scan_line, "ESSID:")) != NULL) { strcpy((*channel_data)->SSID_string, position + strlen("ESSID:")); //printf("SSID_string\t\t: %s\n", (*channel_data)->SSID_string); if(strcasecmp((*channel_data)->SSID_string, ssid) == 0) { /* Calculate a 'utility' metric based on signal strength and real world data rate. The balance between the two characteristics can be debated. The choice here is between a 5GHz channel at 450 Mbps and 2.4 GHz at ~20 Mbps. The power of the signal (in dBm) is logarithmic and can easily dominate the metric. */ if((*channel_data)->channel > 14) { // Standard Frequency Real World Speed // 802.11n 5 GHz 450 Mbps (*channel_data)->metric = 450 * 1000000 * pow(10, (*channel_data)->signal_level_dB / 10.0); } else { // Standard Frequency Real World Speed // 802.11g 2.4 GHz 10 - 29 Mbps (*channel_data)->metric = 10 * 1000000 * pow(10, (*channel_data)->signal_level_dB / 10.0); } //printf("\tour SSID %s\n", (*channel_data)->SSID_string); (*channel_data)++; // move on } else { // don't move on, not our SSID //printf("\tnot our SSID %s\n", (*channel_data)->SSID_string); } } else { printf("\n\t\"%s\"\n", scan_line); report_error("is not a recognized string in wifi_scan.txt"); } } /*------------------------------------------------------------------------------ ------------------------------------------------------------------------------*/ int main(int argc, char **argv) { CHANNEL_DATA channel_data_list[256]; CHANNEL_DATA *channel_data; FILE *fptr; char scan_line[256]; char command[256]; int count; char *ssid = "\"clive\""; // <-- set your SSD here //char *interface = "wlp1s0"; // <-- set your WiFi interface here char *interface = "wlp0s20f3"; // <-- set your WiFi interface here sprintf ( command, "iwlist scan | grep -e completed" ); printf("%s\n", command); system(command); // this reveals the WiFi interface sprintf ( command, "iwlist scan | " "grep -e ESSID -e Quality -e rate -e Channel " "> wifi_scan.txt" ); printf("\n%s\n", command); /* Gathers the iwlist channel information. Does not work if does not have super-user permissions. */ //system(command); if((fptr = fopen("wifi_scan.txt", "rt")) == NULL) { report_error("can't open wifi_scan.txt"); } else { channel_data = channel_data_list; while(!feof(fptr)) { fscanf(fptr, " %[^\r\n]s", scan_line); parse_the_scan_line(scan_line, ssid, &channel_data); } } fclose(fptr); channel_data->channel = -1; // terminate the list count = channel_data - channel_data_list - 1; print_channel_data(channel_data_list); qsort ( (void *)channel_data_list, count, sizeof(CHANNEL_DATA), compare_channels // by utility metric ); print_channel_data(channel_data_list); // connect to the 'best' AP available using iwconfig sprintf ( command, "iwconfig %s channel %d", interface, channel_data_list->channel ); printf("\n%s\n", command); //system(command); // un-comment this to make the change print_channel_data(channel_data_list); printf("\nWiFi scan is done\n"); getchar(); return EXIT_SUCCESS; } /*------------------------------------------------------------------------------ ------------------------------------------------------------------------------*/