Skip to content

Instantly share code, notes, and snippets.

@an-roo
Created February 13, 2011 01:53
Show Gist options
  • Select an option

  • Save an-roo/824341 to your computer and use it in GitHub Desktop.

Select an option

Save an-roo/824341 to your computer and use it in GitHub Desktop.
/*
* Ultra_bcm_config - A utility that does one thing, pretty well.
*
* Andrew Robinson <andrew.robinson@gmail.com>
*
* Based off of the framework provided by iwconfig.c - the standard
* linux wireless driver configuration tool. Modified to pass a special
* structure to the custom Broadcom BCM4329 chipset driver running in the
* kernel to enable AccessPoint mode, broadcasting of the SSID, and WEP/WPA
* encryption.
*
* Also references a publically available implementation of the SHA1
* hashing algorthim for WPA key generation. The BCM driver requires a fully
* hashed key to enable WPA encryption. Function has been modified to return
* proper ASCII characters for sending in the ASCII_CMD structure.
*
* TODO:
* - Advanced error checking - We look for arg counts right now... more could
* be done.
* - TX Power Config - Easy call to bcm4329 driver, uses same offset as current.
*/
#include "iwlib.h"
#include <stdio.h>
#include <stdlib.h>
#include "wpa_supplicant/includes.h"
#include "wpa_supplicant/common.h"
#include "wpa_supplicant/sha1.h"
#define WSEC_MAX_PSK_LEN 64
/**************************** CONSTANTS ****************************/
/*
* Error codes defined for setting args
*/
#define IWERR_ARG_NUM -2
#define IWERR_ARG_TYPE -3
#define IWERR_ARG_SIZE -4
#define IWERR_ARG_CONFLICT -5
#define IWERR_SET_EXT -6
#define IWERR_GET_EXT -7
/**************************** VARIABLES ****************************/
/*
* Deal with errors in set_info() efficiently...
*/
static int errarg;
static int errmax;
static void
build_ap_string(char * output,
char * ssid,
char * sec,
char * key,
char * channel,
char secondString )
{
if(secondString)
{
strcpy(output, "ASCII_CMD=AP_BSS_START,SSID=");
output += 28;
}
else
{
strcpy(output, "ASCII_CMD=AP_CFG,SSID=");
output += 22;
}
strcpy(output, ssid);
output += strlen(ssid);
strcpy(output, ",SEC=");
output += 5;
strcpy(output, sec);
output += strlen(sec);
strcpy(output, ",KEY=");
output += 5;
unsigned char psk[32];
pbkdf2_sha1(key, ssid, strlen(ssid), 4096, psk, 32);
int i;
char* ptr;
char key_str_buf[WSEC_MAX_PSK_LEN+1];
ptr = key_str_buf;
for (i=0; i<(WSEC_MAX_PSK_LEN/8); i++){
sprintf(ptr, "%02x%02x%02x%02x", (uint)psk[i*4], (uint)psk[i*4+1], (uint)psk[i*4+2], (uint)psk[i*4+3]);
ptr += 8;
}
strcpy(output, key_str_buf);
output += strlen(key_str_buf);
strcpy(output, ",CHANNEL=");
output += 9;
strcpy(output, channel);
output += strlen(channel);
strcpy(output, ",PREAMBLE=0,MAX_SCB=8,END");
output += 25;
*output = 0;
}
/*
* set_mode_info
* This function is completely original code
*/
static int
set_mode_info(int skfd, /* Socket name */
char * ifname, /* Dev name */
char * args[], /* Command line args */
int count) /* Args count */
{
/* The wrq pointer struct is a mechanism to pass data from user-land to kernel-space memory.
* It has a allocatable field where we can stuff a struct of an arbitary size into it for
* passing custom code to the driver
*/
struct iwreq wrq;
unsigned int k; /* Must be unsigned */
/* Arguments to be passed:
1 - AP Name
2 - Encryption Type
3 - Encryption Key (unencrypted)
4 - Channel Number (0 is automatically scan) */
// Some length error checking. Lengths are defined in iwlib.h as part of the ap_profile struct
if(strlen(args[1]) > SSID_LEN)
{
errmax = IW_ESSID_MAX_SIZE;
return(IWERR_ARG_SIZE);
}
if(strlen(args[3]) > KEY_LEN)
{
errmax = IW_ESSID_MAX_SIZE;
return(IWERR_ARG_SIZE);
}
double freq;
char * unit;
freq = strtod(args[4], &unit);
/* This is the original functionality of set_mode in iwconfig.
* It is not strictly nessessary anymore but will
* make it look like the interface
* is an access point to the kernel and iwconfig utilities,
* instead of just making it
* an access point and not showing anything.
*/
wrq.u.mode = 3;
if(iw_set_ext(skfd, ifname, SIOCSIWMODE, &wrq) < 0)
return(IWERR_SET_EXT);
/* There are two different versions of the bcm driver on android phones. For some
it requires us to build an ASCII string in memory and then send that string to the driver
using io_ctl commands. The other version requires us to use a struct and send a more compact
binary representation of the data to the driver. I believe google will be looking to use the first
method because it's dead simple. We switch on arg[0] to determine
*/
if(!strcmp(args[0], "softap_htc"))
{
/* STANDARD STRUCT BUILDING MODE */
if(strlen(args[2]) > SEC_LEN)
{
errmax = IW_ESSID_MAX_SIZE;
return(IWERR_ARG_SIZE);
}
/* Data is a union member of what can be stored within the wrq pointer. In this case we set it to the maximum string
length and manually allocate memory for it.
*/
wrq.u.data.length = 162;
wrq.u.data.pointer = malloc(162);
/* Now that we have 162 bytes of free memory allocation is important. The way
the bcm driver hands it is it does a strcom on the first 31 characters and stores
the command string within this block of memory. We will now write the required
piece into memory
*/
strcpy(wrq.u.data.pointer, "AP_PROFILE_SET");
/* ap_profile is defined in iwlib by me.
* we toss it 32 bytes deep into the wrq pointer, because
* the bcm driver expects it there.
*/
/* Create a ap_profile pointer and assign it to a memory location 32 bytes deep within the wrq data struct */
struct ap_profile * ap = (struct ap_profile *)(wrq.u.data.pointer + 32);
/* Setup our payload for delivery */
strcpy(ap->ssid,args[1]);
strcpy(ap->sec ,args[2]);
strcpy(ap->key , args[3]);
/* 0 means auto config on the channel. I recommend using that one.*/
ap->channel = (int) freq;
ap->max_scb = 2;
/* Pass the wrq structure deep into kernel memory for copying and processing. */
if(iw_set_ext(skfd, ifname, SIOCSIWPRIV, &wrq) < 0)
return(IWERR_SET_EXT);
}
else
{
/* ASCII STRING BUILD */
/* maxmimum length of this string will be <300 bytes */
wrq.u.data.length = 300;
wrq.u.data.pointer = malloc(300);
/* Test commands + formatting template:
//char *cmd = "ASCII_CMD= ASCII_CMD=AP_CFG,SSID=AndroidTether,SEC=open,KEY=28e4636bb4e166ab752b859f7b2bc7b26db3d817b5ef79965f48fd76be8d2a19,CHANNEL=6,PREAMBLE=0,MAX_SCB=8,END";
//char *cmd2 = "ASCII_CMD= ASCII_CMD=AP_BSS_START,SSID=AndroidTether,SEC=open,KEY=28e4636bb4e166ab752b859f7b2bc7b26db3d817b5ef79965f48fd76be8d2a19,CHANNEL=6,PREAMBLE=0,MAX_SCB=8,END";
*/
/* Added 32 spaces and command text for command */
char cmd[267] = "ASCII_CMD= ";
/* Call string builder function */
build_ap_string(cmd+32,args[1],args[2],args[3],args[4],0);
fprintf(stderr, cmd);
strcpy(wrq.u.data.pointer, cmd);
if(iw_set_ext(skfd, ifname, SIOCSIWPRIV, &wrq) < 0)
return(IWERR_SET_EXT);
build_ap_string(cmd+32,args[1],args[2],args[3],args[4],1);
fprintf(stderr, cmd);
strcpy(wrq.u.data.pointer, cmd);
if(iw_set_ext(skfd, ifname, SIOCSIWPRIV, &wrq) < 0)
return(IWERR_SET_EXT);
}
/* Free allocated memory. */
free(wrq.u.data.pointer);
return(1);
}
/*
* set_info
* In the original iwconfig file this function acted as a branching conditional function that would ferry the input parameters to the
neccesary functions and deal with return values. Even though we only have one command I have left it in tact for future expansion.
*/
static int
set_info(int skfd, /* The socket */
char * args[], /* Command line args */
int count, /* Args count */
char * ifname) /* Dev name */
{
int ret;
/* Call the command */
ret = set_mode_info(skfd, ifname, args, count);
/* Deal with various errors, taken from iwconfig.c */
if(ret < 0)
{
switch(ret)
{
case IWERR_ARG_NUM:
fprintf(stderr, " too few arguments.\n");
break;
case IWERR_ARG_TYPE:
if(errarg < 0)
errarg = 0;
if(errarg >= count)
errarg = count - 1;
fprintf(stderr, " invalid argument \"%s\".\n", args[errarg]);
break;
case IWERR_ARG_SIZE:
fprintf(stderr, " argument too big (max %d)\n", errmax);
break;
case IWERR_ARG_CONFLICT:
if(errarg < 0)
errarg = 0;
if(errarg >= count)
errarg = count - 1;
fprintf(stderr, " conflicting argument \"%s\".\n", args[errarg]);
break;
case IWERR_SET_EXT:
fprintf(stderr, " SET failed on device %-1.16s ; %s.\n",
ifname, strerror(errno));
break;
case IWERR_GET_EXT:
fprintf(stderr, " GET failed on device %-1.16s ; %s.\n",
ifname, strerror(errno));
break;
}
return(ret);
}
/* Done, all done */
return(0);
}
/*
* MAIN Body. Returns 0 on success, -1 on failure to the system
*/
int
main(int argc,
char ** argv)
{
/* Raw socket descriptor, skfd is -1 on failure to open */
int skfd;
int goterr = 0;
/* Create a channel to the NET kernel using iw system call, defined in iwlib.h. */
/* All io_ctl will be carried out referencing this socket descripter*/
if((skfd = iw_sockets_open()) < 0)
{
perror("socket");
exit(-1);
}
/* Ensure the proper number of arguments has been passed */
if(argc == 7)
/* The other args on the line specify options to be set... */
goterr = set_info(skfd, argv + 2, argc - 2, argv[1]);
else
{
goterr = -1;
fprintf(stderr, "Wrong number of args.");
}
/* Close the NET socket. */
iw_sockets_close(skfd);
return(goterr);
}
@anpilog
Copy link

anpilog commented Jan 14, 2014

Thanks!
It saved me few hours 8)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment