Wireless Tools包含了一下工具:
iwconfig:设置基本无线参数
iwlist:扫描、列出频率,比特率,密钥等
iwspy:获取每个节点链接的质量(只能查阅与之相连的节点)
iwpriv:操作Wireless Extensions 特定驱动
ifrename: 基于各种静态标准命名接口
大多数 Linux 发行版本都在其网络初始化脚本中集成Wireless Extension,以便启动时配置无线接口。
iwconfig是Linux Wireless Extensions(LWE)的用户层配置工具之一。LWE是Linux下对无线网络配置的工具,包括内核的支持、用户层配置工具和驱动接口的支持三部分
ifconfig用法:
wireless-tools中iwconfig的main函数,内容如下:
/******************************* MAIN ********************************/
/*------------------------------------------------------------------*/
/*
* The main !
*/
int
main(int argc,
char ** argv)
{
int skfd; /* generic raw socket desc. */
int goterr = 0;
/* Create a channel to the NET kernel. */
if((skfd = iw_sockets_open()) < 0)
{
perror("socket");
exit(-1);
}
/* No argument : show the list of all device + info */
if(argc == 1)
iw_enum_devices(skfd, &print_info, NULL, 0);
else
/* Special case for help... */
if((!strcmp(argv[1], "-h")) || (!strcmp(argv[1], "--help")))
iw_usage();
else
/* Special case for version... */
if(!strcmp(argv[1], "-v") || !strcmp(argv[1], "--version"))
goterr = iw_print_version_info("iwconfig");
else
{
/* '--' escape device name */
if((argc > 2) && !strcmp(argv[1], "--"))
{
argv++;
argc--;
}
/* The device name must be the first argument */
if(argc == 2)
print_info(skfd, argv[1], NULL, 0);
else
/* The other args on the line specify options to be set... */
goterr = set_info(skfd, argv + 2, argc - 2, argv[1]);
}
/* Close the socket. */
iw_sockets_close(skfd);
return(goterr);
}
iw_sockets_open
函数根据不同的协议创建对应的socket,以便和无线设备驱动进行交互。
iw_enum_devices
函数,当输入的一个参入时,如果是网络接口,则输出网络接口状态信息。
print_info
函数,获取参数信息传入后,打印出与参数对应的需求信息。
set_info
函数,设置参数输入后,进行ioctl操作,向无线设备驱动传参,并生效。
/*------------------------------------------------------------------*/
/*
* Enumerate devices and call specified routine
* The new way just use /proc/net/wireless, so get all wireless interfaces,
* whether configured or not. This is the default if available.
* The old way use SIOCGIFCONF, so get only configured interfaces (wireless
* or not).
*/
void
iw_enum_devices(int skfd,
iw_enum_handler fn,
char * args[],
int count)
{
char buff[1024];
FILE * fh;
struct ifconf ifc;
struct ifreq *ifr;
int i;
#ifndef IW_RESTRIC_ENUM
/* Check if /proc/net/dev is available */
fh = fopen(PROC_NET_DEV, "r");
#else
/* Check if /proc/net/wireless is available */
fh = fopen(PROC_NET_WIRELESS, "r");
#endif
if(fh != NULL)
{
/* Success : use data from /proc/net/wireless */
/* Eat 2 lines of header */
fgets(buff, sizeof(buff), fh);
fgets(buff, sizeof(buff), fh);
/* Read each device line */
while(fgets(buff, sizeof(buff), fh))
{
char name[IFNAMSIZ + 1];
char *s;
/* Skip empty or almost empty lines. It seems that in some
* cases fgets return a line with only a newline. */
if((buff[0] == '\0') || (buff[1] == '\0'))
continue;
/* Extract interface name */
s = iw_get_ifname(name, sizeof(name), buff);
if(!s)
{
/* Failed to parse, complain and continue */
#ifndef IW_RESTRIC_ENUM
fprintf(stderr, "Cannot parse " PROC_NET_DEV "\n");
#else
fprintf(stderr, "Cannot parse " PROC_NET_WIRELESS "\n");
#endif
}
else
/* Got it, print info about this interface */
(*fn)(skfd, name, args, count);
}
fclose(fh);
}
else
{
/* Get list of configured devices using "traditional" way */
ifc.ifc_len = sizeof(buff);
ifc.ifc_buf = buff;
if(ioctl(skfd, SIOCGIFCONF, &ifc) < 0)
{
fprintf(stderr, "SIOCGIFCONF: %s\n", strerror(errno));
return;
}
ifr = ifc.ifc_req;
/* Print them */
for(i = ifc.ifc_len / sizeof(struct ifreq); --i >= 0; ifr++)
(*fn)(skfd, ifr->ifr_name, args, count);
}
}
其中iw_enum_handler
是函数参数,传入了print_info
函数:
/* Prototype for handling display of each single interface on the
* system - see iw_enum_devices() */
typedef int (*iw_enum_handler)(int skfd,
char * ifname,
char * args[],
int count);
iw_enum_devices
中根据相应的情况,可能执行print_info
来打印收集到的设备信息:
/*------------------------------------------------------------------*/
/*
* Print on the screen in a neat fashion all the info we have collected
* on a device.
*/
static int
print_info(int skfd,
char * ifname,
char * args[],
int count)
{
struct wireless_info info;
int rc;
/* Avoid "Unused parameter" warning */
args = args; count = count;
rc = get_info(skfd, ifname, &info);
switch(rc)
{
case 0: /* Success */
/* Display it ! */
display_info(&info, ifname);
break;
case -ENOTSUP:
fprintf(stderr, "%-8.16s no wireless extensions.\n\n",
ifname);
break;
default:
fprintf(stderr, "%-8.16s %s\n\n", ifname, strerror(-rc));
}
return(rc);
}
get_info
函数,可以从无线设备驱动中获取无线的配置参数和信息。
display_info
函数,从get_info
获取的无线配置参数和信息在display_info
中进行选择输出。
其中wireless_info
结构体定义如下
/* Structure for storing all wireless information for each device
* This is pretty exhaustive... */
typedef struct wireless_info
{
struct wireless_config b; /* Basic information */
int has_sens;
iwparam sens; /* sensitivity */
int has_nickname;
char nickname[IW_ESSID_MAX_SIZE + 1]; /* NickName */
int has_ap_addr;
sockaddr ap_addr; /* Access point address */
int has_bitrate;
iwparam bitrate; /* Bit rate in bps */
int has_rts;
iwparam rts; /* RTS threshold in bytes */
int has_frag;
iwparam frag; /* Fragmentation threshold in bytes */
int has_power;
iwparam power; /* Power management parameters */
int has_txpower;
iwparam txpower; /* Transmit Power in dBm */
int has_retry;
iwparam retry; /* Retry limit or lifetime */
/* Stats */
iwstats stats;
int has_stats;
iwrange range;
int has_range;
/* Auth params for WPA/802.1x/802.11i */
int auth_key_mgmt;
int has_auth_key_mgmt;
int auth_cipher_pairwise;
int has_auth_cipher_pairwise;
int auth_cipher_group;
int has_auth_cipher_group;
} wireless_info;
get_info
函数的内容如下:
/************************* DISPLAY ROUTINES **************************/
/*------------------------------------------------------------------*/
/*
* Get wireless informations & config from the device driver
* We will call all the classical wireless ioctl on the driver through
* the socket to know what is supported and to get the settings...
*/
static int
get_info(int skfd,
char * ifname,
struct wireless_info * info)
{
struct iwreq wrq;
memset((char *) info, 0, sizeof(struct wireless_info));
/* Get basic information */
if(iw_get_basic_config(skfd, ifname, &(info->b)) < 0)
{
/* If no wireless name : no wireless extensions */
/* But let's check if the interface exists at all */
struct ifreq ifr;
strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
if(ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0)
return(-ENODEV);
else
return(-ENOTSUP);
}
/* Get ranges */
if(iw_get_range_info(skfd, ifname, &(info->range)) >= 0)
info->has_range = 1;
/* Get AP address */
if(iw_get_ext(skfd, ifname, SIOCGIWAP, &wrq) >= 0)
{
info->has_ap_addr = 1;
memcpy(&(info->ap_addr), &(wrq.u.ap_addr), sizeof (sockaddr));
}
/* Get bit rate */
if(iw_get_ext(skfd, ifname, SIOCGIWRATE, &wrq) >= 0)
{
info->has_bitrate = 1;
memcpy(&(info->bitrate), &(wrq.u.bitrate), sizeof(iwparam));
}
/* Get Power Management settings */
wrq.u.power.flags = 0;
if(iw_get_ext(skfd, ifname, SIOCGIWPOWER, &wrq) >= 0)
{
info->has_power = 1;
memcpy(&(info->power), &(wrq.u.power), sizeof(iwparam));
}
/* Get stats */
if(iw_get_stats(skfd, ifname, &(info->stats),
&info->range, info->has_range) >= 0)
{
info->has_stats = 1;
}
#ifndef WE_ESSENTIAL
/* Get NickName */
wrq.u.essid.pointer = (caddr_t) info->nickname;
wrq.u.essid.length = IW_ESSID_MAX_SIZE + 1;
wrq.u.essid.flags = 0;
if(iw_get_ext(skfd, ifname, SIOCGIWNICKN, &wrq) >= 0)
if(wrq.u.data.length > 1)
info->has_nickname = 1;
if((info->has_range) && (info->range.we_version_compiled > 9))
{
/* Get Transmit Power */
if(iw_get_ext(skfd, ifname, SIOCGIWTXPOW, &wrq) >= 0)
{
info->has_txpower = 1;
memcpy(&(info->txpower), &(wrq.u.txpower), sizeof(iwparam));
}
}
/* Get sensitivity */
if(iw_get_ext(skfd, ifname, SIOCGIWSENS, &wrq) >= 0)
{
info->has_sens = 1;
memcpy(&(info->sens), &(wrq.u.sens), sizeof(iwparam));
}
if((info->has_range) && (info->range.we_version_compiled > 10))
{
/* Get retry limit/lifetime */
if(iw_get_ext(skfd, ifname, SIOCGIWRETRY, &wrq) >= 0)
{
info->has_retry = 1;
memcpy(&(info->retry), &(wrq.u.retry), sizeof(iwparam));
}
}
/* Get RTS threshold */
if(iw_get_ext(skfd, ifname, SIOCGIWRTS, &wrq) >= 0)
{
info->has_rts = 1;
memcpy(&(info->rts), &(wrq.u.rts), sizeof(iwparam));
}
/* Get fragmentation threshold */
if(iw_get_ext(skfd, ifname, SIOCGIWFRAG, &wrq) >= 0)
{
info->has_frag = 1;
memcpy(&(info->frag), &(wrq.u.frag), sizeof(iwparam));
}
#endif /* WE_ESSENTIAL */
return(0);
}
可以看到通过iw_get_ext
进行ioctl操作,并将获取的无线信息放入结构体iwreq,该结构体定义如下:
/*
* The structure to exchange data for ioctl.
* This structure is the same as 'struct ifreq', but (re)defined for
* convenience...
* Do I need to remind you about structure size (32 octets) ?
*/
struct iwreq
{
union
{
char ifrn_name[IFNAMSIZ]; /* if name, e.g. "eth0" */
} ifr_ifrn;
/* Data part (defined just above) */
union iwreq_data u;
};
iwconfig的无线设置,通过set_info
函数来实现,该函数内容如下:
/*------------------------------------------------------------------*/
/*
* Set the wireless options requested on command line
* Find the individual commands and call the appropriate subroutine
*/
static int
set_info(int skfd, /* The socket */
char * args[], /* Command line args */
int count, /* Args count */
char * ifname) /* Dev name */
{
const iwconfig_cmd * iwcmd;
int ret;
/* Loop until we run out of args... */
while(count > 0)
{
/* find the command matching the keyword */
iwcmd = find_command(args[0]);
if(iwcmd == NULL)
{
/* Here we have an unrecognised arg... Error already printed out. */
return(-1);
}
/* One arg is consumed (the command name) */
args++;
count--;
/* Check arg numbers */
if(count < iwcmd->min_count)
ret = IWERR_ARG_NUM;
else
ret = 0;
/* Call the command */
if(!ret)
ret = (*iwcmd->fn)(skfd, ifname, args, count);
/* Deal with various errors */
if(ret < 0)
{
int request = iwcmd->request;
if(ret == IWERR_GET_EXT)
request++; /* Transform the SET into GET */
fprintf(stderr, "Error for wireless request \"%s\" (%X) :\n",
iwcmd->name, request);
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;
}
/* Stop processing, we don't know if we are in a consistent state
* in reading the command line */
return(ret);
}
/* Substract consumed args from command line */
args += ret;
count -= ret;
/* Loop back */
}
/* Done, all done */
return(0);
}
iwconfig_cmd
结构体定义了一个数组,用来存放了无线设置项的固定参数名,以及参数类型,和设置调用函数。
find_command
函数通过对比已存的iwconfig_cmd
结构体数组,来匹配需要设置的无线参数。
iwconfig_cmd定义的数组如下:
static const struct iwconfig_entry iwconfig_cmds[] = {
{ "essid", set_essid_info, 1, SIOCSIWESSID,
"Set ESSID", "{NNN|any|on|off}" },
{ "mode", set_mode_info, 1, SIOCSIWMODE,
"Set Mode", "{managed|ad-hoc|master|...}" },
{ "freq", set_freq_info, 1, SIOCSIWFREQ,
"Set Frequency", "N.NNN[k|M|G]" },
{ "channel", set_freq_info, 1, SIOCSIWFREQ,
"Set Frequency", "N" },
{ "bit", set_bitrate_info, 1, SIOCSIWRATE,
"Set Bit Rate", "{N[k|M|G]|auto|fixed}" },
{ "rate", set_bitrate_info, 1, SIOCSIWRATE,
"Set Bit Rate", "{N[k|M|G]|auto|fixed}" },
{ "enc", set_enc_info, 1, SIOCSIWENCODE,
"Set Encode", "{NNNN-NNNN|off}" },
{ "key", set_enc_info, 1, SIOCSIWENCODE,
"Set Encode", "{NNNN-NNNN|off}" },
{ "power", set_power_info, 1, SIOCSIWPOWER,
"Set Power Management", "{period N|timeout N|saving N|off}" },
#ifndef WE_ESSENTIAL
{ "nickname", set_nick_info, 1, SIOCSIWNICKN,
"Set Nickname", "NNN" },
{ "nwid", set_nwid_info, 1, SIOCSIWNWID,
"Set NWID", "{NN|on|off}" },
{ "ap", set_apaddr_info, 1, SIOCSIWAP,
"Set AP Address", "{N|off|auto}" },
{ "txpower", set_txpower_info, 1, SIOCSIWTXPOW,
"Set Tx Power", "{NmW|NdBm|off|auto}" },
{ "sens", set_sens_info, 1, SIOCSIWSENS,
"Set Sensitivity", "N" },
{ "retry", set_retry_info, 1, SIOCSIWRETRY,
"Set Retry Limit", "{limit N|lifetime N}" },
{ "rts", set_rts_info, 1, SIOCSIWRTS,
"Set RTS Threshold", "{N|auto|fixed|off}" },
{ "frag", set_frag_info, 1, SIOCSIWFRAG,
"Set Fragmentation Threshold", "{N|auto|fixed|off}" },
{ "modulation", set_modulation_info, 1, SIOCGIWMODUL,
"Set Modulation", "{11g|11a|CCK|OFDMg|...}" },
#endif /* WE_ESSENTIAL */
{ "commit", set_commit_info, 0, SIOCSIWCOMMIT,
"Commit changes", "" },
{ NULL, NULL, 0, 0, NULL, NULL },
};
以设置ESSID为例,最后调用了set_essid_info
函数,并导入了参数SIOCSIWESSID
,内容如下:
/*********************** SETTING SUB-ROUTINES ***********************/
/*
* The following functions are use to set some wireless parameters and
* are called by the set dispatcher set_info().
* They take as arguments the remaining of the command line, with
* arguments processed already removed.
* An error is indicated by a negative return value.
* 0 and positive return values indicate the number of args consumed.
*/
/*------------------------------------------------------------------*/
/*
* Set ESSID
*/
static int
set_essid_info(int skfd,
char * ifname,
char * args[], /* Command line args */
int count) /* Args count */
{
struct iwreq wrq;
int i = 1;
char essid[IW_ESSID_MAX_SIZE + 1];
int we_kernel_version;
if((!strcasecmp(args[0], "off")) ||
(!strcasecmp(args[0], "any")))
{
wrq.u.essid.flags = 0;
essid[0] = '\0';
}
else
if(!strcasecmp(args[0], "on"))
{
/* Get old essid */
memset(essid, '\0', sizeof(essid));
wrq.u.essid.pointer = (caddr_t) essid;
wrq.u.essid.length = IW_ESSID_MAX_SIZE + 1;
wrq.u.essid.flags = 0;
if(iw_get_ext(skfd, ifname, SIOCGIWESSID, &wrq) < 0)
return(IWERR_GET_EXT);
wrq.u.essid.flags = 1;
}
else
{
i = 0;
/* '-' or '--' allow to escape the ESSID string, allowing
* to set it to the string "any" or "off".
* This is a big ugly, but it will do for now */
if((!strcmp(args[0], "-")) || (!strcmp(args[0], "--")))
{
if(++i >= count)
return(IWERR_ARG_NUM);
}
/* Check the size of what the user passed us to avoid
* buffer overflows */
if(strlen(args[i]) > IW_ESSID_MAX_SIZE)
{
errmax = IW_ESSID_MAX_SIZE;
return(IWERR_ARG_SIZE);
}
else
{
int temp;
wrq.u.essid.flags = 1;
strcpy(essid, args[i]); /* Size checked, all clear */
i++;
/* Check for ESSID index */
if((i < count) &&
(sscanf(args[i], "[%i]", &temp) == 1) &&
(temp > 0) && (temp < IW_ENCODE_INDEX))
{
wrq.u.essid.flags = temp;
++i;
}
}
}
/* Get version from kernel, device may not have range... */
we_kernel_version = iw_get_kernel_we_version();
/* Finally set the ESSID value */
wrq.u.essid.pointer = (caddr_t) essid;
wrq.u.essid.length = strlen(essid);
if(we_kernel_version < 21)
wrq.u.essid.length++;
if(iw_set_ext(skfd, ifname, SIOCSIWESSID, &wrq) < 0)
return(IWERR_SET_EXT);
/* Var args */
return(i);
}
可以看到最后调用iw_set_ext
函数,通过iwreq结构,实现对无线设备驱动的ioctl操作。
iwreq结构体中的成员u,是联合体iwreq_data
,存放了获取和设置的参数,定义如下:
/* ------------------------ IOCTL REQUEST ------------------------ */
/*
* This structure defines the payload of an ioctl, and is used
* below.
*
* Note that this structure should fit on the memory footprint
* of iwreq (which is the same as ifreq), which mean a max size of
* 16 octets = 128 bits. Warning, pointers might be 64 bits wide...
* You should check this when increasing the structures defined
* above in this file...
*/
union iwreq_data
{
/* Config - generic */
char name[IFNAMSIZ];
/* Name : used to verify the presence of wireless extensions.
* Name of the protocol/provider... */
struct iw_point essid; /* Extended network name */
struct iw_param nwid; /* network id (or domain - the cell) */
struct iw_freq freq; /* frequency or channel :
* 0-1000 = channel
* > 1000 = frequency in Hz */
struct iw_param sens; /* signal level threshold */
struct iw_param bitrate; /* default bit rate */
struct iw_param txpower; /* default transmit power */
struct iw_param rts; /* RTS threshold threshold */
struct iw_param frag; /* Fragmentation threshold */
__u32 mode; /* Operation mode */
struct iw_param retry; /* Retry limits & lifetime */
struct iw_point encoding; /* Encoding stuff : tokens */
struct iw_param power; /* PM duration/timeout */
struct iw_quality qual; /* Quality part of statistics */
struct sockaddr ap_addr; /* Access point address */
struct sockaddr addr; /* Destination address (hw/mac) */
struct iw_param param; /* Other small parameters */
struct iw_point data; /* Other large parameters */
};
联合体iwreq_data
中,结构体iw_point
用来存放16位的参数信息,结构体iw_quality
用来存放连接质量信息,其他信息使用结构体iw_param
。