wireless-tools源码分析-iwconfig

Wireless Tools包含了一下工具:

    iwconfig:设置基本无线参数
    iwlist:扫描、列出频率,比特率,密钥等
    iwspy:获取每个节点链接的质量(只能查阅与之相连的节点)
    iwpriv:操作Wireless Extensions 特定驱动
    ifrename: 基于各种静态标准命名接口

大多数 Linux 发行版本都在其网络初始化脚本中集成Wireless Extension,以便启动时配置无线接口。

ifconfig

iwconfig是Linux Wireless Extensions(LWE)的用户层配置工具之一。LWE是Linux下对无线网络配置的工具,包括内核的支持、用户层配置工具和驱动接口的支持三部分
ifconfig用法:
wireless-tools源码分析-iwconfig_第1张图片

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

你可能感兴趣的:(Linux,C,Linux,无线网络,linux网络,mac80211解析)