nexmon – Rev 1

Subversion Repositories:
Rev:
/*
 *      Wireless Tools
 *
 *              Jean II - HPL '01
 *
 * Just print the ESSID or NWID...
 *
 * This file is released under the GPL license.
 *     Copyright (c) 1997-2004 Jean Tourrilhes <jt@hpl.hp.com>
 */

#include "iwlib.h"              /* Header */

#include <getopt.h>

/*
 * Note on Pcmcia Schemes :
 * ----------------------
 *      The purpose of this tool is to use the ESSID discovery mechanism
 * to select the appropriate Pcmcia Scheme. The card tell us which
 * ESSID it has found, and we can then select the appropriate Pcmcia
 * Scheme for this ESSID (Wireless config (encrypt keys) and IP config).
 *      The way to do it is as follows :
 *                      cardctl scheme "essidany"
 *                      delay 100
 *                      $scheme = iwgetid --scheme
 *                      cardctl scheme $scheme
 *      Of course, you need to add a scheme called "essidany" with the
 * following setting :
 *                      essidany,*,*,*)
 *                              ESSID="any"
 *                              IPADDR="10.0.0.1"
 *
 *      This can also be integrated int he Pcmcia scripts.
 *      Some drivers don't activate the card up to "ifconfig up".
 * Therefore, they wont scan ESSID up to this point, so we can't
 * read it reliably in Pcmcia scripts.
 *      I guess the proper way to write the network script is as follows :
 *                      if($scheme == "iwgetid") {
 *                              iwconfig $name essid any
 *                              iwconfig $name nwid any
 *                              ifconfig $name up
 *                              delay 100
 *                              $scheme = iwgetid $name --scheme
 *                              ifconfig $name down
 *                      }
 *
 *      This is pseudo code, but you get an idea...
 *      The "ifconfig up" activate the card.
 *      The "delay" is necessary to let time for the card scan the
 * frequencies and associate with the AP.
 *      The "ifconfig down" is necessary to allow the driver to optimise
 * the wireless parameters setting (minimise number of card resets).
 *
 *      Another cute idea is to have a list of Pcmcia Schemes to try
 * and to keep the first one that associate (AP address != 0). This
 * would be necessary for closed networks and cards that can't
 * discover essid...
 *
 * Jean II - 29/3/01
 */

/**************************** CONSTANTS ****************************/

#define FORMAT_DEFAULT  0        /* Nice looking display for the user */
#define FORMAT_SCHEME   1       /* To be used as a Pcmcia Scheme */
#define FORMAT_RAW      2       /* Raw value, for shell scripts */
#define WTYPE_ESSID     0        /* Display ESSID or NWID */
#define WTYPE_AP        1       /* Display AP/Cell Address */
#define WTYPE_FREQ      2       /* Display frequency/channel */
#define WTYPE_CHANNEL   3       /* Display channel (converted from freq) */
#define WTYPE_MODE      4       /* Display mode */
#define WTYPE_PROTO     5       /* Display protocol name */

/************************ DISPLAY ESSID/NWID ************************/

/*------------------------------------------------------------------*/
/*
 * Display the ESSID if possible
 */
static int
print_essid(int                 skfd,
            const char *        ifname,
            int                 format)
{
  struct iwreq          wrq;
  char                  essid[IW_ESSID_MAX_SIZE + 1];   /* ESSID */
  char                  pessid[IW_ESSID_MAX_SIZE + 1];  /* Pcmcia format */
  unsigned int          i;
  unsigned int          j;

  /* Make sure ESSID is always NULL terminated */
  memset(essid, 0, sizeof(essid));

  /* Get 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(-1);

  switch(format)
    {
    case FORMAT_SCHEME:
      /* Strip all white space and stuff */
      j = 0;
      for(i = 0; i < strlen(essid); i++)
        if(isalnum(essid[i]))
          pessid[j++] = essid[i];
      pessid[j] = '\0';
      if((j == 0) || (j > 32))
        return(-2);
      printf("%s\n", pessid);
      break;
    case FORMAT_RAW:
      printf("%s\n", essid);
      break;
    default:
      printf("%-8.16s  ESSID:\"%s\"\n", ifname, essid);
      break;
    }

  return(0);
}

/*------------------------------------------------------------------*/
/*
 * Display the NWID if possible
 */
static int
print_nwid(int          skfd,
           const char * ifname,
           int          format)
{
  struct iwreq          wrq;

  /* Get network ID */
  if(iw_get_ext(skfd, ifname, SIOCGIWNWID, &wrq) < 0)
    return(-1);

  switch(format)
    {
    case FORMAT_SCHEME:
      /* Prefix with nwid to avoid name space collisions */
      printf("nwid%X\n", wrq.u.nwid.value);
      break;
    case FORMAT_RAW:
      printf("%X\n", wrq.u.nwid.value);
      break;
    default:
      printf("%-8.16s  NWID:%X\n", ifname, wrq.u.nwid.value);
      break;
    }

  return(0);
}

/**************************** AP ADDRESS ****************************/

/*------------------------------------------------------------------*/
/*
 * Display the AP Address if possible
 */
static int
print_ap(int            skfd,
         const char *   ifname,
         int            format)
{
  struct iwreq          wrq;
  char                  buffer[64];

  /* Get AP Address */
  if(iw_get_ext(skfd, ifname, SIOCGIWAP, &wrq) < 0)
    return(-1);

  /* Print */
  iw_ether_ntop((const struct ether_addr *) wrq.u.ap_addr.sa_data, buffer);
  switch(format)
    {
    case FORMAT_SCHEME:
      /* I think ':' are not problematic, because Pcmcia scripts
       * seem to handle them properly... */
    case FORMAT_RAW:
      printf("%s\n", buffer);
      break;
    default:
      printf("%-8.16s  Access Point/Cell: %s\n", ifname, buffer);
      break;
    }

  return(0);
}

/****************************** OTHER ******************************/

/*------------------------------------------------------------------*/
/*
 * Display the frequency (or channel) if possible
 */
static int
print_freq(int          skfd,
           const char * ifname,
           int          format)
{
  struct iwreq          wrq;
  double                freq;
  char                  buffer[64];

  /* Get frequency / channel */
  if(iw_get_ext(skfd, ifname, SIOCGIWFREQ, &wrq) < 0)
    return(-1);

  /* Print */
  freq = iw_freq2float(&(wrq.u.freq));
  switch(format)
    {
    case FORMAT_SCHEME:
      /* Prefix with freq to avoid name space collisions */
      printf("freq%g\n", freq);
      break;
    case FORMAT_RAW:
      printf("%g\n", freq);
      break;
    default:
      iw_print_freq(buffer, sizeof(buffer), freq, -1, wrq.u.freq.flags);
      printf("%-8.16s  %s\n", ifname, buffer);
      break;
    }

  return(0);
}

/*------------------------------------------------------------------*/
/*
 * Display the channel (converted from frequency) if possible
 */
static int
print_channel(int               skfd,
              const char *      ifname,
              int               format)
{
  struct iwreq          wrq;
  struct iw_range       range;
  double                freq;
  int                   channel;

  /* Get frequency / channel */
  if(iw_get_ext(skfd, ifname, SIOCGIWFREQ, &wrq) < 0)
    return(-1);

  /* Convert to channel */
  if(iw_get_range_info(skfd, ifname, &range) < 0)
    return(-2);
  freq = iw_freq2float(&(wrq.u.freq));
  if(freq < KILO)
    channel = (int) freq;
  else
    {
      channel = iw_freq_to_channel(freq, &range);
      if(channel < 0)
        return(-3);
    }

  /* Print */
  switch(format)
    {
    case FORMAT_SCHEME:
      /* Prefix with freq to avoid name space collisions */
      printf("channel%d\n", channel);
      break;
    case FORMAT_RAW:
      printf("%d\n", channel);
      break;
    default:
      printf("%-8.16s  Channel:%d\n", ifname, channel);
      break;
    }

  return(0);
}

/*------------------------------------------------------------------*/
/*
 * Display the mode if possible
 */
static int
print_mode(int          skfd,
           const char * ifname,
           int          format)
{
  struct iwreq          wrq;

  /* Get frequency / channel */
  if(iw_get_ext(skfd, ifname, SIOCGIWMODE, &wrq) < 0)
    return(-1);
  if(wrq.u.mode >= IW_NUM_OPER_MODE)
    return(-2);

  /* Print */
  switch(format)
    {
    case FORMAT_SCHEME:
      /* Strip all white space and stuff */
      if(wrq.u.mode == IW_MODE_ADHOC)
        printf("AdHoc\n");
      else
        printf("%s\n", iw_operation_mode[wrq.u.mode]);
      break;
    case FORMAT_RAW:
      printf("%d\n", wrq.u.mode);
      break;
    default:
      printf("%-8.16s  Mode:%s\n", ifname, iw_operation_mode[wrq.u.mode]);
      break;
    }

  return(0);
}

/*------------------------------------------------------------------*/
/*
 * Display the ESSID if possible
 */
static int
print_protocol(int              skfd,
               const char *     ifname,
               int              format)
{
  struct iwreq          wrq;
  char                  proto[IFNAMSIZ + 1];    /* Protocol */
  char                  pproto[IFNAMSIZ + 1];   /* Pcmcia format */
  unsigned int          i;
  unsigned int          j;

  /* Get Protocol name */
  if(iw_get_ext(skfd, ifname, SIOCGIWNAME, &wrq) < 0)
    return(-1);
  strncpy(proto, wrq.u.name, IFNAMSIZ);
  proto[IFNAMSIZ] = '\0';

  switch(format)
    {
    case FORMAT_SCHEME:
      /* Strip all white space and stuff */
      j = 0;
      for(i = 0; i < strlen(proto); i++)
        if(isalnum(proto[i]))
          pproto[j++] = proto[i];
      pproto[j] = '\0';
      if((j == 0) || (j > 32))
        return(-2);
      printf("%s\n", pproto);
      break;
    case FORMAT_RAW:
      printf("%s\n", proto);
      break;
    default:
      printf("%-8.16s  Protocol Name:\"%s\"\n", ifname, proto);
      break;
    }

  return(0);
}

/******************************* MAIN ********************************/

/*------------------------------------------------------------------*/
/*
 * Check options and call the proper handler
 */
static int
print_one_device(int            skfd,
                 int            format,
                 int            wtype,
                 const char*    ifname)
{
  int ret;

  /* Check wtype */
  switch(wtype)
    {
    case WTYPE_AP:
      /* Try to print an AP */
      ret = print_ap(skfd, ifname, format);
      break;

    case WTYPE_CHANNEL:
      /* Try to print channel */
      ret = print_channel(skfd, ifname, format);
      break;

    case WTYPE_FREQ:
      /* Try to print frequency */
      ret = print_freq(skfd, ifname, format);
      break;

    case WTYPE_MODE:
      /* Try to print the mode */
      ret = print_mode(skfd, ifname, format);
      break;

    case WTYPE_PROTO:
      /* Try to print the protocol */
      ret = print_protocol(skfd, ifname, format);
      break;

    default:
      /* Try to print an ESSID */
      ret = print_essid(skfd, ifname, format);
      if(ret < 0)
        {
          /* Try to print a nwid */
          ret = print_nwid(skfd, ifname, format);
        }
    }

  return(ret);
}

/*------------------------------------------------------------------*/
/*
 * Try the various devices until one return something we can use
 *
 * Note : we can't use iw_enum_devices() because we want a different
 * behaviour :
 *      1) Stop at the first valid wireless device
 *      2) Only go through active devices
 */
static int
scan_devices(int                skfd,
             int                format,
             int                wtype)
{
  char          buff[1024];
  struct ifconf ifc;
  struct ifreq *ifr;
  int           i;

  /* Get list of active devices */
  ifc.ifc_len = sizeof(buff);
  ifc.ifc_buf = buff;
  if(ioctl(skfd, SIOCGIFCONF, &ifc) < 0)
    {
      perror("SIOCGIFCONF");
      return(-1);
    }
  ifr = ifc.ifc_req;

  /* Print the first match */
  for(i = ifc.ifc_len / sizeof(struct ifreq); --i >= 0; ifr++)
    {
      if(print_one_device(skfd, format, wtype, ifr->ifr_name) >= 0)
        return 0;
    }
  return(-1);
}

/*------------------------------------------------------------------*/
/*
 * helper
 */
static void
iw_usage(int status)
{
  fputs("Usage iwgetid [OPTIONS] [ifname]\n"
        "  Options are:\n"
        "    -a,--ap       Print the access point address\n"
        "    -c,--channel  Print the current channel\n"
        "    -f,--freq     Print the current frequency\n"
        "    -m,--mode     Print the current mode\n"
        "    -p,--protocol Print the protocol name\n"
        "    -r,--raw      Format the output as raw value for shell scripts\n"
        "    -s,--scheme   Format the output as a PCMCIA scheme identifier\n"
        "    -h,--help     Print this message\n",
        status ? stderr : stdout);
  exit(status);
}

static const struct option long_opts[] = {
  { "ap", no_argument, NULL, 'a' },
  { "channel", no_argument, NULL, 'c' },
  { "freq", no_argument, NULL, 'f' },
  { "mode", no_argument, NULL, 'm' },
  { "protocol", no_argument, NULL, 'p' },
  { "help", no_argument, NULL, 'h' },
  { "raw", no_argument, NULL, 'r' },
  { "scheme", no_argument, NULL, 's' },
  { NULL, 0, NULL, 0 }
};

/*------------------------------------------------------------------*/
/*
 * The main !
 */
int
main(int        argc,
     char **    argv)
{
  int   skfd;                   /* generic raw socket desc.     */
  int   format = FORMAT_DEFAULT;
  int   wtype = WTYPE_ESSID;
  int   opt;
  int   ret = -1;

  /* Check command line arguments */
  while((opt = getopt_long(argc, argv, "acfhmprs", long_opts, NULL)) > 0)
    {
      switch(opt)
        {
        case 'a':
          /* User wants AP/Cell Address */
          wtype = WTYPE_AP;
          break;

        case 'c':
          /* User wants channel only */
          wtype = WTYPE_CHANNEL;
          break;

        case 'f':
          /* User wants frequency/channel */
          wtype = WTYPE_FREQ;
          break;

        case 'm':
          /* User wants the mode */
          wtype = WTYPE_MODE;
          break;

        case 'p':
          /* User wants the protocol */
          wtype = WTYPE_PROTO;
          break;

        case 'h':
          iw_usage(0);
          break;

        case 'r':
          /* User wants a Raw format */
          format = FORMAT_RAW;
          break;

        case 's':
          /* User wants a Scheme format */
          format = FORMAT_SCHEME;
          break;

        default:
          iw_usage(1);
          break;
        }
    }
  if(optind + 1 < argc) {
    fputs("Too many arguments.\n", stderr);
    iw_usage(1);
  }

  /* Create a channel to the NET kernel. */
  if((skfd = iw_sockets_open()) < 0)
    {
      perror("socket");
      return(-1);
    }

  /* Check if first argument is a device name */
  if(optind < argc)
    {
      /* Yes : query only this device */
      ret = print_one_device(skfd, format, wtype, argv[optind]);
    }
  else
    {
      /* No : query all devices and print first found */
      ret = scan_devices(skfd, format, wtype);
    }

  fflush(stdout);
  iw_sockets_close(skfd);
  return(ret);
}