The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/net80211/ieee80211_ioctl.c

Version: -  FREEBSD  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-2  -  FREEBSD-11-1  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-4  -  FREEBSD-10-3  -  FREEBSD-10-2  -  FREEBSD-10-1  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-3  -  FREEBSD-9-2  -  FREEBSD-9-1  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-4  -  FREEBSD-8-3  -  FREEBSD-8-2  -  FREEBSD-8-1  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-4  -  FREEBSD-7-3  -  FREEBSD-7-2  -  FREEBSD-7-1  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-4  -  FREEBSD-6-3  -  FREEBSD-6-2  -  FREEBSD-6-1  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-5  -  FREEBSD-5-4  -  FREEBSD-5-3  -  FREEBSD-5-2  -  FREEBSD-5-1  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  linux-2.6  -  linux-2.4.22  -  MK83  -  MK84  -  PLAN9  -  DFBSD  -  NETBSD  -  NETBSD5  -  NETBSD4  -  NETBSD3  -  NETBSD20  -  OPENBSD  -  xnu-517  -  xnu-792  -  xnu-792.6.70  -  xnu-1228  -  xnu-1456.1.26  -  xnu-1699.24.8  -  xnu-2050.18.24  -  OPENSOLARIS  -  minix-3-1-1 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2001 Atsushi Onoe
    3  * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
    4  * All rights reserved.
    5  *
    6  * Redistribution and use in source and binary forms, with or without
    7  * modification, are permitted provided that the following conditions
    8  * are met:
    9  * 1. Redistributions of source code must retain the above copyright
   10  *    notice, this list of conditions and the following disclaimer.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  * 3. The name of the author may not be used to endorse or promote products
   15  *    derived from this software without specific prior written permission.
   16  *
   17  * Alternatively, this software may be distributed under the terms of the
   18  * GNU General Public License ("GPL") version 2 as published by the Free
   19  * Software Foundation.
   20  *
   21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   31  */
   32 
   33 #include <sys/cdefs.h>
   34 __FBSDID("$FreeBSD: releng/5.2/sys/net80211/ieee80211_ioctl.c 122600 2003-11-13 05:23:58Z sam $");
   35 
   36 /*
   37  * IEEE 802.11 ioctl support (FreeBSD-specific)
   38  */
   39 
   40 #include <sys/endian.h>
   41 #include <sys/param.h>
   42 #include <sys/kernel.h>
   43 #include <sys/socket.h>
   44 #include <sys/sockio.h>
   45 #include <sys/systm.h>
   46  
   47 #include <net/if.h>
   48 #include <net/if_arp.h>
   49 #include <net/if_media.h>
   50 #include <net/ethernet.h>
   51 
   52 #include <net80211/ieee80211_var.h>
   53 #include <net80211/ieee80211_ioctl.h>
   54 
   55 #include <dev/wi/if_wavelan_ieee.h>
   56 
   57 /*
   58  * XXX
   59  * Wireless LAN specific configuration interface, which is compatible
   60  * with wicontrol(8).
   61  */
   62 
   63 int
   64 ieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data)
   65 {
   66         struct ieee80211com *ic = (void *)ifp;
   67         int i, j, error;
   68         struct ifreq *ifr = (struct ifreq *)data;
   69         struct wi_req wreq;
   70         struct wi_ltv_keys *keys;
   71         struct wi_apinfo *ap;
   72         struct ieee80211_node *ni;
   73         struct ieee80211_rateset *rs;
   74         struct wi_sigcache wsc;
   75         struct wi_scan_p2_hdr *p2;
   76         struct wi_scan_res *res;
   77 
   78         error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
   79         if (error)
   80                 return error;
   81         wreq.wi_len = 0;
   82         switch (wreq.wi_type) {
   83         case WI_RID_SERIALNO:
   84                 /* nothing appropriate */
   85                 break;
   86         case WI_RID_NODENAME:
   87                 strcpy((char *)&wreq.wi_val[1], hostname);
   88                 wreq.wi_val[0] = htole16(strlen(hostname));
   89                 wreq.wi_len = (1 + strlen(hostname) + 1) / 2;
   90                 break;
   91         case WI_RID_CURRENT_SSID:
   92                 if (ic->ic_state != IEEE80211_S_RUN) {
   93                         wreq.wi_val[0] = 0;
   94                         wreq.wi_len = 1;
   95                         break;
   96                 }
   97                 wreq.wi_val[0] = htole16(ic->ic_bss->ni_esslen);
   98                 memcpy(&wreq.wi_val[1], ic->ic_bss->ni_essid,
   99                     ic->ic_bss->ni_esslen);
  100                 wreq.wi_len = (1 + ic->ic_bss->ni_esslen + 1) / 2;
  101                 break;
  102         case WI_RID_OWN_SSID:
  103         case WI_RID_DESIRED_SSID:
  104                 wreq.wi_val[0] = htole16(ic->ic_des_esslen);
  105                 memcpy(&wreq.wi_val[1], ic->ic_des_essid, ic->ic_des_esslen);
  106                 wreq.wi_len = (1 + ic->ic_des_esslen + 1) / 2;
  107                 break;
  108         case WI_RID_CURRENT_BSSID:
  109                 if (ic->ic_state == IEEE80211_S_RUN)
  110                         IEEE80211_ADDR_COPY(wreq.wi_val, ic->ic_bss->ni_bssid);
  111                 else
  112                         memset(wreq.wi_val, 0, IEEE80211_ADDR_LEN);
  113                 wreq.wi_len = IEEE80211_ADDR_LEN / 2;
  114                 break;
  115         case WI_RID_CHANNEL_LIST:
  116                 memset(wreq.wi_val, 0, sizeof(wreq.wi_val));
  117                 /*
  118                  * Since channel 0 is not available for DS, channel 1
  119                  * is assigned to LSB on WaveLAN.
  120                  */
  121                 if (ic->ic_phytype == IEEE80211_T_DS)
  122                         i = 1;
  123                 else
  124                         i = 0;
  125                 for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++)
  126                         if (isset(ic->ic_chan_active, i)) {
  127                                 setbit((u_int8_t *)wreq.wi_val, j);
  128                                 wreq.wi_len = j / 16 + 1;
  129                         }
  130                 break;
  131         case WI_RID_OWN_CHNL:
  132                 wreq.wi_val[0] = htole16(
  133                         ieee80211_chan2ieee(ic, ic->ic_ibss_chan));
  134                 wreq.wi_len = 1;
  135                 break;
  136         case WI_RID_CURRENT_CHAN:
  137                 wreq.wi_val[0] = htole16(
  138                         ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan));
  139                 wreq.wi_len = 1;
  140                 break;
  141         case WI_RID_COMMS_QUALITY:
  142                 wreq.wi_val[0] = 0;                             /* quality */
  143                 wreq.wi_val[1] =
  144                         htole16((*ic->ic_node_getrssi)(ic, ic->ic_bss));
  145                 wreq.wi_val[2] = 0;                             /* noise */
  146                 wreq.wi_len = 3;
  147                 break;
  148         case WI_RID_PROMISC:
  149                 wreq.wi_val[0] = htole16((ifp->if_flags & IFF_PROMISC) ? 1 : 0);
  150                 wreq.wi_len = 1;
  151                 break;
  152         case WI_RID_PORTTYPE:
  153                 wreq.wi_val[0] = htole16(ic->ic_opmode);
  154                 wreq.wi_len = 1;
  155                 break;
  156         case WI_RID_MAC_NODE:
  157                 IEEE80211_ADDR_COPY(wreq.wi_val, ic->ic_myaddr);
  158                 wreq.wi_len = IEEE80211_ADDR_LEN / 2;
  159                 break;
  160         case WI_RID_TX_RATE:
  161                 if (ic->ic_fixed_rate == -1)
  162                         wreq.wi_val[0] = 0;     /* auto */
  163                 else
  164                         wreq.wi_val[0] = htole16(
  165                             (ic->ic_sup_rates[ic->ic_curmode].rs_rates[ic->ic_fixed_rate] &
  166                             IEEE80211_RATE_VAL) / 2);
  167                 wreq.wi_len = 1;
  168                 break;
  169         case WI_RID_CUR_TX_RATE:
  170                 wreq.wi_val[0] = htole16(
  171                     (ic->ic_bss->ni_rates.rs_rates[ic->ic_bss->ni_txrate] &
  172                     IEEE80211_RATE_VAL) / 2);
  173                 wreq.wi_len = 1;
  174                 break;
  175         case WI_RID_RTS_THRESH:
  176                 wreq.wi_val[0] = htole16(ic->ic_rtsthreshold);
  177                 wreq.wi_len = 1;
  178                 break;
  179         case WI_RID_CREATE_IBSS:
  180                 wreq.wi_val[0] =
  181                     htole16((ic->ic_flags & IEEE80211_F_IBSSON) ? 1 : 0);
  182                 wreq.wi_len = 1;
  183                 break;
  184         case WI_RID_MICROWAVE_OVEN:
  185                 wreq.wi_val[0] = 0;     /* no ... not supported */
  186                 wreq.wi_len = 1;
  187                 break;
  188         case WI_RID_ROAMING_MODE:
  189                 wreq.wi_val[0] = htole16(1);    /* enabled ... not supported */
  190                 wreq.wi_len = 1;
  191                 break;
  192         case WI_RID_SYSTEM_SCALE:
  193                 wreq.wi_val[0] = htole16(1);    /* low density ... not supp */
  194                 wreq.wi_len = 1;
  195                 break;
  196         case WI_RID_PM_ENABLED:
  197                 wreq.wi_val[0] =
  198                     htole16((ic->ic_flags & IEEE80211_F_PMGTON) ? 1 : 0);
  199                 wreq.wi_len = 1;
  200                 break;
  201         case WI_RID_MAX_SLEEP:
  202                 wreq.wi_val[0] = htole16(ic->ic_lintval);
  203                 wreq.wi_len = 1;
  204                 break;
  205         case WI_RID_CUR_BEACON_INT:
  206                 wreq.wi_val[0] = htole16(ic->ic_bss->ni_intval);
  207                 wreq.wi_len = 1;
  208                 break;
  209         case WI_RID_WEP_AVAIL:
  210                 wreq.wi_val[0] =
  211                     htole16((ic->ic_caps & IEEE80211_C_WEP) ? 1 : 0);
  212                 wreq.wi_len = 1;
  213                 break;
  214         case WI_RID_CNFAUTHMODE:
  215                 wreq.wi_val[0] = htole16(1);    /* TODO: open system only */
  216                 wreq.wi_len = 1;
  217                 break;
  218         case WI_RID_ENCRYPTION:
  219                 wreq.wi_val[0] =
  220                     htole16((ic->ic_flags & IEEE80211_F_WEPON) ? 1 : 0);
  221                 wreq.wi_len = 1;
  222                 break;
  223         case WI_RID_TX_CRYPT_KEY:
  224                 wreq.wi_val[0] = htole16(ic->ic_wep_txkey);
  225                 wreq.wi_len = 1;
  226                 break;
  227         case WI_RID_DEFLT_CRYPT_KEYS:
  228                 keys = (struct wi_ltv_keys *)&wreq;
  229                 /* do not show keys to non-root user */
  230                 error = suser(curthread);
  231                 if (error) {
  232                         memset(keys, 0, sizeof(*keys));
  233                         error = 0;
  234                         break;
  235                 }
  236                 for (i = 0; i < IEEE80211_WEP_NKID; i++) {
  237                         keys->wi_keys[i].wi_keylen =
  238                             htole16(ic->ic_nw_keys[i].wk_len);
  239                         memcpy(keys->wi_keys[i].wi_keydat,
  240                             ic->ic_nw_keys[i].wk_key, ic->ic_nw_keys[i].wk_len);
  241                 }
  242                 wreq.wi_len = sizeof(*keys) / 2;
  243                 break;
  244         case WI_RID_MAX_DATALEN:
  245                 wreq.wi_val[0] = htole16(IEEE80211_MAX_LEN);    /* TODO: frag */
  246                 wreq.wi_len = 1;
  247                 break;
  248         case WI_RID_IFACE_STATS:
  249                 /* XXX: should be implemented in lower drivers */
  250                 break;
  251         case WI_RID_READ_APS:
  252                 if (ic->ic_opmode != IEEE80211_M_HOSTAP) {
  253                         /*
  254                          * Don't return results until active scan completes.
  255                          */
  256                         if (ic->ic_state == IEEE80211_S_SCAN &&
  257                             (ic->ic_flags & IEEE80211_F_ASCAN)) {
  258                                 error = EINPROGRESS;
  259                                 break;
  260                         }
  261                 }
  262                 i = 0;
  263                 ap = (void *)((char *)wreq.wi_val + sizeof(i));
  264                 TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
  265                         if ((caddr_t)(ap + 1) > (caddr_t)(&wreq + 1))
  266                                 break;
  267                         memset(ap, 0, sizeof(*ap));
  268                         if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
  269                                 IEEE80211_ADDR_COPY(ap->bssid, ni->ni_macaddr);
  270                                 ap->namelen = ic->ic_des_esslen;
  271                                 if (ic->ic_des_esslen)
  272                                         memcpy(ap->name, ic->ic_des_essid,
  273                                             ic->ic_des_esslen);
  274                         } else {
  275                                 IEEE80211_ADDR_COPY(ap->bssid, ni->ni_bssid);
  276                                 ap->namelen = ni->ni_esslen;
  277                                 if (ni->ni_esslen)
  278                                         memcpy(ap->name, ni->ni_essid,
  279                                             ni->ni_esslen);
  280                         }
  281                         ap->channel = ieee80211_chan2ieee(ic, ni->ni_chan);
  282                         ap->signal = (*ic->ic_node_getrssi)(ic, ni);
  283                         ap->capinfo = ni->ni_capinfo;
  284                         ap->interval = ni->ni_intval;
  285                         rs = &ni->ni_rates;
  286                         for (j = 0; j < rs->rs_nrates; j++) {
  287                                 if (rs->rs_rates[j] & IEEE80211_RATE_BASIC) {
  288                                         ap->rate = (rs->rs_rates[j] &
  289                                             IEEE80211_RATE_VAL) * 5; /* XXX */
  290                                 }
  291                         }
  292                         i++;
  293                         ap++;
  294                 }
  295                 memcpy(wreq.wi_val, &i, sizeof(i));
  296                 wreq.wi_len = (sizeof(int) + sizeof(*ap) * i) / 2;
  297                 break;
  298         case WI_RID_PRISM2:
  299                 wreq.wi_val[0] = 1;     /* XXX lie so SCAN_RES can give rates */
  300                 wreq.wi_len = sizeof(u_int16_t) / 2;
  301                 break;
  302         case WI_RID_SCAN_RES:                   /* compatibility interface */
  303                 if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
  304                     ic->ic_state == IEEE80211_S_SCAN) {
  305                         error = EINPROGRESS;
  306                         break;
  307                 }
  308                 /* NB: we use the Prism2 format so we can return rate info */
  309                 p2 = (struct wi_scan_p2_hdr *)wreq.wi_val;
  310                 res = (void *)&p2[1];
  311                 i = 0;
  312                 TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
  313                         if ((caddr_t)(res + 1) > (caddr_t)(&wreq + 1))
  314                                 break;
  315                         res->wi_chan = ieee80211_chan2ieee(ic, ni->ni_chan);
  316                         res->wi_noise = 0;
  317                         res->wi_signal = (*ic->ic_node_getrssi)(ic, ni);
  318                         IEEE80211_ADDR_COPY(res->wi_bssid, ni->ni_bssid);
  319                         res->wi_interval = ni->ni_intval;
  320                         res->wi_capinfo = ni->ni_capinfo;
  321                         res->wi_ssid_len = ni->ni_esslen;
  322                         memcpy(res->wi_ssid, ni->ni_essid, IEEE80211_NWID_LEN);
  323                         /* NB: assumes wi_srates holds <= ni->ni_rates */
  324                         memcpy(res->wi_srates, ni->ni_rates.rs_rates,
  325                                 sizeof(res->wi_srates));
  326                         if (ni->ni_rates.rs_nrates < 10)
  327                                 res->wi_srates[ni->ni_rates.rs_nrates] = 0;
  328                         res->wi_rate = ni->ni_rates.rs_rates[ni->ni_txrate];
  329                         res->wi_rsvd = 0;
  330                         res++, i++;
  331                 }
  332                 p2->wi_rsvd = 0;
  333                 p2->wi_reason = i;
  334                 wreq.wi_len = (sizeof(*p2) + sizeof(*res) * i) / 2;
  335                 break;
  336         case WI_RID_READ_CACHE:
  337                 i = 0;
  338                 TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
  339                         if (i == (WI_MAX_DATALEN/sizeof(struct wi_sigcache))-1)
  340                                 break;
  341                         IEEE80211_ADDR_COPY(wsc.macsrc, ni->ni_macaddr);
  342                         memset(&wsc.ipsrc, 0, sizeof(wsc.ipsrc));
  343                         wsc.signal = (*ic->ic_node_getrssi)(ic, ni);
  344                         wsc.noise = 0;
  345                         wsc.quality = 0;
  346                         memcpy((caddr_t)wreq.wi_val + sizeof(wsc) * i,
  347                             &wsc, sizeof(wsc));
  348                         i++;
  349                 }
  350                 wreq.wi_len = sizeof(wsc) * i / 2;
  351                 break;
  352         case WI_RID_SCAN_APS:
  353                 error = EINVAL;
  354                 break;
  355         default:
  356                 error = EINVAL;
  357                 break;
  358         }
  359         if (error == 0) {
  360                 wreq.wi_len++;
  361                 error = copyout(&wreq, ifr->ifr_data, sizeof(wreq));
  362         }
  363         return error;
  364 }
  365 
  366 static int
  367 findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate)
  368 {
  369 #define IEEERATE(_ic,_m,_i) \
  370         ((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL)
  371         int i, nrates = ic->ic_sup_rates[mode].rs_nrates;
  372         for (i = 0; i < nrates; i++)
  373                 if (IEEERATE(ic, mode, i) == rate)
  374                         return i;
  375         return -1;
  376 #undef IEEERATE
  377 }
  378 
  379 /*
  380  * Prepare to do a user-initiated scan for AP's.  If no
  381  * current/default channel is setup or the current channel
  382  * is invalid then pick the first available channel from
  383  * the active list as the place to start the scan.
  384  */
  385 static int
  386 ieee80211_setupscan(struct ieee80211com *ic)
  387 {
  388         u_char *chanlist = ic->ic_chan_active;
  389         int i;
  390 
  391         if (ic->ic_ibss_chan == NULL ||
  392             isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) {
  393                 for (i = 0; i <= IEEE80211_CHAN_MAX; i++)
  394                         if (isset(chanlist, i)) {
  395                                 ic->ic_ibss_chan = &ic->ic_channels[i];
  396                                 goto found;
  397                         }
  398                 return EINVAL;                  /* no active channels */
  399 found:
  400                 ;
  401         }
  402         if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC ||
  403             isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan)))
  404                 ic->ic_bss->ni_chan = ic->ic_ibss_chan;
  405         /*
  406          * XXX don't permit a scan to be started unless we
  407          * know the device is ready.  For the moment this means
  408          * the device is marked up as this is the required to
  409          * initialize the hardware.  It would be better to permit
  410          * scanning prior to being up but that'll require some
  411          * changes to the infrastructure.
  412          */
  413         return (ic->ic_if.if_flags & IFF_UP) ? 0 : ENETRESET;
  414 }
  415 
  416 int
  417 ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
  418 {
  419         struct ieee80211com *ic = (void *)ifp;
  420         int i, j, len, error, rate;
  421         struct ifreq *ifr = (struct ifreq *)data;
  422         struct wi_ltv_keys *keys;
  423         struct wi_req wreq;
  424         u_char chanlist[roundup(IEEE80211_CHAN_MAX, NBBY)];
  425 
  426         error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
  427         if (error)
  428                 return error;
  429         len = wreq.wi_len ? (wreq.wi_len - 1) * 2 : 0;
  430         switch (wreq.wi_type) {
  431         case WI_RID_SERIALNO:
  432         case WI_RID_NODENAME:
  433                 return EPERM;
  434         case WI_RID_CURRENT_SSID:
  435                 return EPERM;
  436         case WI_RID_OWN_SSID:
  437         case WI_RID_DESIRED_SSID:
  438                 if (le16toh(wreq.wi_val[0]) * 2 > len ||
  439                     le16toh(wreq.wi_val[0]) > IEEE80211_NWID_LEN) {
  440                         error = ENOSPC;
  441                         break;
  442                 }
  443                 memset(ic->ic_des_essid, 0, sizeof(ic->ic_des_essid));
  444                 ic->ic_des_esslen = le16toh(wreq.wi_val[0]) * 2;
  445                 memcpy(ic->ic_des_essid, &wreq.wi_val[1], ic->ic_des_esslen);
  446                 error = ENETRESET;
  447                 break;
  448         case WI_RID_CURRENT_BSSID:
  449                 return EPERM;
  450         case WI_RID_OWN_CHNL:
  451                 if (len != 2)
  452                         return EINVAL;
  453                 i = le16toh(wreq.wi_val[0]);
  454                 if (i < 0 ||
  455                     i > IEEE80211_CHAN_MAX ||
  456                     isclr(ic->ic_chan_active, i))
  457                         return EINVAL;
  458                 ic->ic_ibss_chan = &ic->ic_channels[i];
  459                 if (ic->ic_flags & IEEE80211_F_SIBSS)
  460                         error = ENETRESET;
  461                 break;
  462         case WI_RID_CURRENT_CHAN:
  463                 return EPERM;
  464         case WI_RID_COMMS_QUALITY:
  465                 return EPERM;
  466         case WI_RID_PROMISC:
  467                 if (len != 2)
  468                         return EINVAL;
  469                 if (ifp->if_flags & IFF_PROMISC) {
  470                         if (wreq.wi_val[0] == 0) {
  471                                 ifp->if_flags &= ~IFF_PROMISC;
  472                                 error = ENETRESET;
  473                         }
  474                 } else {
  475                         if (wreq.wi_val[0] != 0) {
  476                                 ifp->if_flags |= IFF_PROMISC;
  477                                 error = ENETRESET;
  478                         }
  479                 }
  480                 break;
  481         case WI_RID_PORTTYPE:
  482                 if (len != 2)
  483                         return EINVAL;
  484                 switch (le16toh(wreq.wi_val[0])) {
  485                 case IEEE80211_M_STA:
  486                         break;
  487                 case IEEE80211_M_IBSS:
  488                         if (!(ic->ic_caps & IEEE80211_C_IBSS))
  489                                 return EINVAL;
  490                         break;
  491                 case IEEE80211_M_AHDEMO:
  492                         if (ic->ic_phytype != IEEE80211_T_DS ||
  493                             !(ic->ic_caps & IEEE80211_C_AHDEMO))
  494                                 return EINVAL;
  495                         break;
  496                 case IEEE80211_M_HOSTAP:
  497                         if (!(ic->ic_caps & IEEE80211_C_HOSTAP))
  498                                 return EINVAL;
  499                         break;
  500                 default:
  501                         return EINVAL;
  502                 }
  503                 if (le16toh(wreq.wi_val[0]) != ic->ic_opmode) {
  504                         ic->ic_opmode = le16toh(wreq.wi_val[0]);
  505                         error = ENETRESET;
  506                 }
  507                 break;
  508 #if 0
  509         case WI_RID_MAC_NODE:
  510                 if (len != IEEE80211_ADDR_LEN)
  511                         return EINVAL;
  512                 IEEE80211_ADDR_COPY(LLADDR(ifp->if_sadl), wreq.wi_val);
  513                 /* if_init will copy lladdr into ic_myaddr */
  514                 error = ENETRESET;
  515                 break;
  516 #endif
  517         case WI_RID_TX_RATE:
  518                 if (len != 2)
  519                         return EINVAL;
  520                 if (wreq.wi_val[0] == 0) {
  521                         /* auto */
  522                         ic->ic_fixed_rate = -1;
  523                         break;
  524                 }
  525                 rate = 2 * le16toh(wreq.wi_val[0]);
  526                 if (ic->ic_curmode == IEEE80211_MODE_AUTO) {
  527                         /*
  528                          * In autoselect mode search for the rate.  We take
  529                          * the first instance which may not be right, but we
  530                          * are limited by the interface.  Note that we also
  531                          * lock the mode to insure the rate is meaningful
  532                          * when it is used.
  533                          */
  534                         for (j = IEEE80211_MODE_11A;
  535                              j < IEEE80211_MODE_MAX; j++) {
  536                                 if ((ic->ic_modecaps & (1<<j)) == 0)
  537                                         continue;
  538                                 i = findrate(ic, j, rate);
  539                                 if (i != -1) {
  540                                         /* lock mode too */
  541                                         ic->ic_curmode = j;
  542                                         goto setrate;
  543                                 }
  544                         }
  545                 } else {
  546                         i = findrate(ic, ic->ic_curmode, rate);
  547                         if (i != -1)
  548                                 goto setrate;
  549                 }
  550                 return EINVAL;
  551         setrate:
  552                 ic->ic_fixed_rate = i;
  553                 error = ENETRESET;
  554                 break;
  555         case WI_RID_CUR_TX_RATE:
  556                 return EPERM;
  557         case WI_RID_RTS_THRESH:
  558                 if (len != 2)
  559                         return EINVAL;
  560                 if (le16toh(wreq.wi_val[0]) != IEEE80211_MAX_LEN)
  561                         return EINVAL;          /* TODO: RTS */
  562                 break;
  563         case WI_RID_CREATE_IBSS:
  564                 if (len != 2)
  565                         return EINVAL;
  566                 if (wreq.wi_val[0] != 0) {
  567                         if ((ic->ic_caps & IEEE80211_C_IBSS) == 0)
  568                                 return EINVAL;
  569                         if ((ic->ic_flags & IEEE80211_F_IBSSON) == 0) {
  570                                 ic->ic_flags |= IEEE80211_F_IBSSON;
  571                                 if (ic->ic_opmode == IEEE80211_M_IBSS &&
  572                                     ic->ic_state == IEEE80211_S_SCAN)
  573                                         error = ENETRESET;
  574                         }
  575                 } else {
  576                         if (ic->ic_flags & IEEE80211_F_IBSSON) {
  577                                 ic->ic_flags &= ~IEEE80211_F_IBSSON;
  578                                 if (ic->ic_flags & IEEE80211_F_SIBSS) {
  579                                         ic->ic_flags &= ~IEEE80211_F_SIBSS;
  580                                         error = ENETRESET;
  581                                 }
  582                         }
  583                 }
  584                 break;
  585         case WI_RID_MICROWAVE_OVEN:
  586                 if (len != 2)
  587                         return EINVAL;
  588                 if (wreq.wi_val[0] != 0)
  589                         return EINVAL;          /* not supported */
  590                 break;
  591         case WI_RID_ROAMING_MODE:
  592                 if (len != 2)
  593                         return EINVAL;
  594                 if (le16toh(wreq.wi_val[0]) != 1)
  595                         return EINVAL;          /* not supported */
  596                 break;
  597         case WI_RID_SYSTEM_SCALE:
  598                 if (len != 2)
  599                         return EINVAL;
  600                 if (le16toh(wreq.wi_val[0]) != 1)
  601                         return EINVAL;          /* not supported */
  602                 break;
  603         case WI_RID_PM_ENABLED:
  604                 if (len != 2)
  605                         return EINVAL;
  606                 if (wreq.wi_val[0] != 0) {
  607                         if ((ic->ic_caps & IEEE80211_C_PMGT) == 0)
  608                                 return EINVAL;
  609                         if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) {
  610                                 ic->ic_flags |= IEEE80211_F_PMGTON;
  611                                 error = ENETRESET;
  612                         }
  613                 } else {
  614                         if (ic->ic_flags & IEEE80211_F_PMGTON) {
  615                                 ic->ic_flags &= ~IEEE80211_F_PMGTON;
  616                                 error = ENETRESET;
  617                         }
  618                 }
  619                 break;
  620         case WI_RID_MAX_SLEEP:
  621                 if (len != 2)
  622                         return EINVAL;
  623                 ic->ic_lintval = le16toh(wreq.wi_val[0]);
  624                 if (ic->ic_flags & IEEE80211_F_PMGTON)
  625                         error = ENETRESET;
  626                 break;
  627         case WI_RID_CUR_BEACON_INT:
  628                 return EPERM;
  629         case WI_RID_WEP_AVAIL:
  630                 return EPERM;
  631         case WI_RID_CNFAUTHMODE:
  632                 if (len != 2)
  633                         return EINVAL;
  634                 if (le16toh(wreq.wi_val[0]) != 1)
  635                         return EINVAL;          /* TODO: shared key auth */
  636                 break;
  637         case WI_RID_ENCRYPTION:
  638                 if (len != 2)
  639                         return EINVAL;
  640                 if (wreq.wi_val[0] != 0) {
  641                         if ((ic->ic_caps & IEEE80211_C_WEP) == 0)
  642                                 return EINVAL;
  643                         if ((ic->ic_flags & IEEE80211_F_WEPON) == 0) {
  644                                 ic->ic_flags |= IEEE80211_F_WEPON;
  645                                 error = ENETRESET;
  646                         }
  647                 } else {
  648                         if (ic->ic_flags & IEEE80211_F_WEPON) {
  649                                 ic->ic_flags &= ~IEEE80211_F_WEPON;
  650                                 error = ENETRESET;
  651                         }
  652                 }
  653                 break;
  654         case WI_RID_TX_CRYPT_KEY:
  655                 if (len != 2)
  656                         return EINVAL;
  657                 i = le16toh(wreq.wi_val[0]);
  658                 if (i >= IEEE80211_WEP_NKID)
  659                         return EINVAL;
  660                 ic->ic_wep_txkey = i;
  661                 break;
  662         case WI_RID_DEFLT_CRYPT_KEYS:
  663                 if (len != sizeof(struct wi_ltv_keys))
  664                         return EINVAL;
  665                 keys = (struct wi_ltv_keys *)&wreq;
  666                 for (i = 0; i < IEEE80211_WEP_NKID; i++) {
  667                         len = le16toh(keys->wi_keys[i].wi_keylen);
  668                         if (len != 0 && len < IEEE80211_WEP_KEYLEN)
  669                                 return EINVAL;
  670                         if (len > sizeof(ic->ic_nw_keys[i].wk_key))
  671                                 return EINVAL;
  672                 }
  673                 memset(ic->ic_nw_keys, 0, sizeof(ic->ic_nw_keys));
  674                 for (i = 0; i < IEEE80211_WEP_NKID; i++) {
  675                         len = le16toh(keys->wi_keys[i].wi_keylen);
  676                         ic->ic_nw_keys[i].wk_len = len;
  677                         memcpy(ic->ic_nw_keys[i].wk_key,
  678                             keys->wi_keys[i].wi_keydat, len);
  679                 }
  680                 error = ENETRESET;
  681                 break;
  682         case WI_RID_MAX_DATALEN:
  683                 if (len != 2)
  684                         return EINVAL;
  685                 len = le16toh(wreq.wi_val[0]);
  686                 if (len < 350 /* ? */ || len > IEEE80211_MAX_LEN)
  687                         return EINVAL;
  688                 if (len != IEEE80211_MAX_LEN)
  689                         return EINVAL;          /* TODO: fragment */
  690                 ic->ic_fragthreshold = len;
  691                 error = ENETRESET;
  692                 break;
  693         case WI_RID_IFACE_STATS:
  694                 error = EPERM;
  695                 break;
  696         case WI_RID_SCAN_REQ:                   /* XXX wicontrol */
  697                 if (ic->ic_opmode == IEEE80211_M_HOSTAP)
  698                         break;
  699                 error = ieee80211_setupscan(ic);
  700                 if (error == 0)
  701                         error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
  702                 break;
  703         case WI_RID_SCAN_APS:
  704                 if (ic->ic_opmode == IEEE80211_M_HOSTAP)
  705                         break;
  706                 len--;                  /* XXX: tx rate? */
  707                 /* FALLTHRU */
  708         case WI_RID_CHANNEL_LIST:
  709                 memset(chanlist, 0, sizeof(chanlist));
  710                 /*
  711                  * Since channel 0 is not available for DS, channel 1
  712                  * is assigned to LSB on WaveLAN.
  713                  */
  714                 if (ic->ic_phytype == IEEE80211_T_DS)
  715                         i = 1;
  716                 else
  717                         i = 0;
  718                 for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) {
  719                         if ((j / 8) >= len)
  720                                 break;
  721                         if (isclr((u_int8_t *)wreq.wi_val, j))
  722                                 continue;
  723                         if (isclr(ic->ic_chan_active, i)) {
  724                                 if (wreq.wi_type != WI_RID_CHANNEL_LIST)
  725                                         continue;
  726                                 if (isclr(ic->ic_chan_avail, i))
  727                                         return EPERM;
  728                         }
  729                         setbit(chanlist, i);
  730                 }
  731                 memcpy(ic->ic_chan_active, chanlist,
  732                     sizeof(ic->ic_chan_active));
  733                 error = ieee80211_setupscan(ic);
  734                 if (wreq.wi_type == WI_RID_CHANNEL_LIST) {
  735                         /* NB: ignore error from ieee80211_setupscan */
  736                         error = ENETRESET;
  737                 } else if (error == 0)
  738                         error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
  739                 break;
  740         default:
  741                 error = EINVAL;
  742                 break;
  743         }
  744         return error;
  745 }
  746 
  747 int
  748 ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
  749 {
  750         struct ieee80211com *ic = (void *)ifp;
  751         int error = 0;
  752         u_int kid, len;
  753         struct ieee80211req *ireq;
  754         struct ifreq *ifr;
  755         u_int8_t tmpkey[IEEE80211_KEYBUF_SIZE];
  756         char tmpssid[IEEE80211_NWID_LEN];
  757         struct ieee80211_channel *chan;
  758 
  759         switch (cmd) {
  760         case SIOCSIFMEDIA:
  761         case SIOCGIFMEDIA:
  762                 error = ifmedia_ioctl(ifp, (struct ifreq *) data,
  763                                 &ic->ic_media, cmd);
  764                 break;
  765         case SIOCG80211:
  766                 ireq = (struct ieee80211req *) data;
  767                 switch (ireq->i_type) {
  768                 case IEEE80211_IOC_SSID:
  769                         switch (ic->ic_state) {
  770                         case IEEE80211_S_INIT:
  771                         case IEEE80211_S_SCAN:
  772                                 ireq->i_len = ic->ic_des_esslen;
  773                                 memcpy(tmpssid, ic->ic_des_essid, ireq->i_len);
  774                                 break;
  775                         default:
  776                                 ireq->i_len = ic->ic_bss->ni_esslen;
  777                                 memcpy(tmpssid, ic->ic_bss->ni_essid,
  778                                         ireq->i_len);
  779                                 break;
  780                         }
  781                         error = copyout(tmpssid, ireq->i_data, ireq->i_len);
  782                         break;
  783                 case IEEE80211_IOC_NUMSSIDS:
  784                         ireq->i_val = 1;
  785                         break;
  786                 case IEEE80211_IOC_WEP:
  787                         if ((ic->ic_caps & IEEE80211_C_WEP) == 0) {
  788                                 ireq->i_val = IEEE80211_WEP_NOSUP; 
  789                         } else {
  790                                 if (ic->ic_flags & IEEE80211_F_WEPON) {
  791                                         ireq->i_val =
  792                                             IEEE80211_WEP_MIXED;
  793                                 } else {
  794                                         ireq->i_val =
  795                                             IEEE80211_WEP_OFF;
  796                                 }
  797                         }
  798                         break;
  799                 case IEEE80211_IOC_WEPKEY:
  800                         if ((ic->ic_caps & IEEE80211_C_WEP) == 0) {
  801                                 error = EINVAL;
  802                                 break;
  803                         }
  804                         kid = (u_int) ireq->i_val;
  805                         if (kid >= IEEE80211_WEP_NKID) {
  806                                 error = EINVAL;
  807                                 break;
  808                         }
  809                         len = (u_int) ic->ic_nw_keys[kid].wk_len;
  810                         /* NB: only root can read WEP keys */
  811                         if (suser(curthread) == 0) {
  812                                 bcopy(ic->ic_nw_keys[kid].wk_key, tmpkey, len);
  813                         } else {
  814                                 bzero(tmpkey, len);
  815                         }
  816                         ireq->i_len = len;
  817                         error = copyout(tmpkey, ireq->i_data, len);
  818                         break;
  819                 case IEEE80211_IOC_NUMWEPKEYS:
  820                         if ((ic->ic_caps & IEEE80211_C_WEP) == 0)
  821                                 error = EINVAL;
  822                         else
  823                                 ireq->i_val = IEEE80211_WEP_NKID;
  824                         break;
  825                 case IEEE80211_IOC_WEPTXKEY:
  826                         if ((ic->ic_caps & IEEE80211_C_WEP) == 0)
  827                                 error = EINVAL;
  828                         else
  829                                 ireq->i_val = ic->ic_wep_txkey;
  830                         break;
  831                 case IEEE80211_IOC_AUTHMODE:
  832                         ireq->i_val = IEEE80211_AUTH_OPEN;
  833                         break;
  834                 case IEEE80211_IOC_CHANNEL:
  835                         switch (ic->ic_state) {
  836                         case IEEE80211_S_INIT:
  837                         case IEEE80211_S_SCAN:
  838                                 if (ic->ic_opmode == IEEE80211_M_STA)
  839                                         chan = ic->ic_des_chan;
  840                                 else
  841                                         chan = ic->ic_ibss_chan;
  842                                 break;
  843                         default:
  844                                 chan = ic->ic_bss->ni_chan;
  845                                 break;
  846                         }
  847                         ireq->i_val = ieee80211_chan2ieee(ic, chan);
  848                         break;
  849                 case IEEE80211_IOC_POWERSAVE:
  850                         if (ic->ic_flags & IEEE80211_F_PMGTON)
  851                                 ireq->i_val = IEEE80211_POWERSAVE_ON;
  852                         else
  853                                 ireq->i_val = IEEE80211_POWERSAVE_OFF;
  854                         break;
  855                 case IEEE80211_IOC_POWERSAVESLEEP:
  856                         ireq->i_val = ic->ic_lintval;
  857                         break;
  858                 case IEEE80211_IOC_RTSTHRESHOLD:
  859                         ireq->i_val = ic->ic_rtsthreshold;
  860                         break;
  861                 default:
  862                         error = EINVAL;
  863                 }
  864                 break;
  865         case SIOCS80211:
  866                 error = suser(curthread);
  867                 if (error)
  868                         break;
  869                 ireq = (struct ieee80211req *) data;
  870                 switch (ireq->i_type) {
  871                 case IEEE80211_IOC_SSID:
  872                         if (ireq->i_val != 0 ||
  873                             ireq->i_len > IEEE80211_NWID_LEN) {
  874                                 error = EINVAL;
  875                                 break;
  876                         }
  877                         error = copyin(ireq->i_data, tmpssid, ireq->i_len);
  878                         if (error)
  879                                 break;
  880                         memset(ic->ic_des_essid, 0, IEEE80211_NWID_LEN);
  881                         ic->ic_des_esslen = ireq->i_len;
  882                         memcpy(ic->ic_des_essid, tmpssid, ireq->i_len);
  883                         error = ENETRESET;
  884                         break;
  885                 case IEEE80211_IOC_WEP:
  886                         /*
  887                          * These cards only support one mode so
  888                          * we just turn wep on if what ever is
  889                          * passed in is not OFF.
  890                          */
  891                         if (ireq->i_val == IEEE80211_WEP_OFF) {
  892                                 ic->ic_flags &= ~IEEE80211_F_WEPON;
  893                         } else {
  894                                 ic->ic_flags |= IEEE80211_F_WEPON;
  895                         }
  896                         error = ENETRESET;
  897                         break;
  898                 case IEEE80211_IOC_WEPKEY:
  899                         if ((ic->ic_caps & IEEE80211_C_WEP) == 0) {
  900                                 error = EINVAL;
  901                                 break;
  902                         }
  903                         kid = (u_int) ireq->i_val;
  904                         if (kid >= IEEE80211_WEP_NKID) {
  905                                 error = EINVAL;
  906                                 break;
  907                         } 
  908                         if (ireq->i_len > sizeof(tmpkey)) {
  909                                 error = EINVAL;
  910                                 break;
  911                         }
  912                         memset(tmpkey, 0, sizeof(tmpkey));
  913                         error = copyin(ireq->i_data, tmpkey, ireq->i_len);
  914                         if (error)
  915                                 break;
  916                         memcpy(ic->ic_nw_keys[kid].wk_key, tmpkey,
  917                                 sizeof(tmpkey));
  918                         ic->ic_nw_keys[kid].wk_len = ireq->i_len;
  919                         error = ENETRESET;
  920                         break;
  921                 case IEEE80211_IOC_WEPTXKEY:
  922                         kid = (u_int) ireq->i_val;
  923                         if (kid >= IEEE80211_WEP_NKID) {
  924                                 error = EINVAL;
  925                                 break;
  926                         }
  927                         ic->ic_wep_txkey = kid;
  928                         error = ENETRESET;
  929                         break;
  930 #if 0
  931                 case IEEE80211_IOC_AUTHMODE:
  932                         sc->wi_authmode = ireq->i_val;
  933                         break;
  934 #endif
  935                 case IEEE80211_IOC_CHANNEL:
  936                         /* XXX 0xffff overflows 16-bit signed */
  937                         if (ireq->i_val == 0 ||
  938                             ireq->i_val == (int16_t) IEEE80211_CHAN_ANY)
  939                                 ic->ic_des_chan = IEEE80211_CHAN_ANYC;
  940                         else if ((u_int) ireq->i_val > IEEE80211_CHAN_MAX ||
  941                             isclr(ic->ic_chan_active, ireq->i_val)) {
  942                                 error = EINVAL;
  943                                 break;
  944                         } else
  945                                 ic->ic_ibss_chan = ic->ic_des_chan =
  946                                         &ic->ic_channels[ireq->i_val];
  947                         switch (ic->ic_state) {
  948                         case IEEE80211_S_INIT:
  949                         case IEEE80211_S_SCAN:
  950                                 error = ENETRESET;
  951                                 break;
  952                         default:
  953                                 if (ic->ic_opmode == IEEE80211_M_STA) {
  954                                         if (ic->ic_des_chan != IEEE80211_CHAN_ANYC &&
  955                                             ic->ic_bss->ni_chan != ic->ic_des_chan)
  956                                                 error = ENETRESET;
  957                                 } else {
  958                                         if (ic->ic_bss->ni_chan != ic->ic_ibss_chan)
  959                                                 error = ENETRESET;
  960                                 }
  961                                 break;
  962                         }
  963                         break;
  964                 case IEEE80211_IOC_POWERSAVE:
  965                         switch (ireq->i_val) {
  966                         case IEEE80211_POWERSAVE_OFF:
  967                                 if (ic->ic_flags & IEEE80211_F_PMGTON) {
  968                                         ic->ic_flags &= ~IEEE80211_F_PMGTON;
  969                                         error = ENETRESET;
  970                                 }
  971                                 break;
  972                         case IEEE80211_POWERSAVE_ON:
  973                                 if ((ic->ic_caps & IEEE80211_C_PMGT) == 0)
  974                                         error = EINVAL;
  975                                 else if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) {
  976                                         ic->ic_flags |= IEEE80211_F_PMGTON;
  977                                         error = ENETRESET;
  978                                 }
  979                                 break;
  980                         default:
  981                                 error = EINVAL;
  982                                 break;
  983                         }
  984                         break;
  985                 case IEEE80211_IOC_POWERSAVESLEEP:
  986                         if (ireq->i_val < 0) {
  987                                 error = EINVAL;
  988                                 break;
  989                         }
  990                         ic->ic_lintval = ireq->i_val;
  991                         error = ENETRESET;
  992                         break;
  993                 case IEEE80211_IOC_RTSTHRESHOLD:
  994                         if (!(IEEE80211_RTS_MIN < ireq->i_val &&
  995                               ireq->i_val < IEEE80211_RTS_MAX)) {
  996                                 error = EINVAL;
  997                                 break;
  998                         }
  999                         ic->ic_rtsthreshold = ireq->i_val;
 1000                         error = ENETRESET;
 1001                         break;
 1002                 default:
 1003                         error = EINVAL;
 1004                         break;
 1005                 }
 1006                 break;
 1007         case SIOCGIFGENERIC:
 1008                 error = ieee80211_cfgget(ifp, cmd, data);
 1009                 break;
 1010         case SIOCSIFGENERIC:
 1011                 error = suser(curthread);
 1012                 if (error)
 1013                         break;
 1014                 error = ieee80211_cfgset(ifp, cmd, data);
 1015                 break;
 1016         case SIOCG80211STATS:
 1017                 ifr = (struct ifreq *)data;
 1018                 copyout(&ic->ic_stats, ifr->ifr_data, sizeof (ic->ic_stats));
 1019                 break;
 1020         default:
 1021                 error = ether_ioctl(ifp, cmd, data);
 1022                 break;
 1023         }
 1024         return error;
 1025 }

Cache object: f7eddfed5932e684152fec7b5f390ca3


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.