?? ieee80211_linux.c
字號:
/*- * Copyright (c) 2003-2005 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $Id: ieee80211_linux.c 1690 2006-07-21 08:59:10Z kelmo $ */#ifndef EXPORT_SYMTAB#define EXPORT_SYMTAB#endif/* * IEEE 802.11 support (Linux-specific code) */#include <linux/config.h>#include <linux/version.h>#include <linux/module.h>#include <linux/kmod.h>#include <linux/init.h>#include <linux/skbuff.h>#include <linux/sysctl.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/if_vlan.h>#include <linux/vmalloc.h>#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)#include <linux/proc_fs.h>#endif#include <net/iw_handler.h>#include <linux/wireless.h>#include <linux/if_arp.h> /* XXX for ARPHRD_* */#include <asm/uaccess.h>#include "if_media.h"#include "if_ethersubr.h"#include <net80211/ieee80211_var.h>#include <net80211/ieee80211_monitor.h>/* * Print a console message with the device name prepended. */voidif_printf(struct net_device *dev, const char *fmt, ...){ va_list ap; char buf[512]; /* XXX */ va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); printk("%s: %s", dev->name, buf);}/* * Allocate and setup a management frame of the specified * size. We return the sk_buff and a pointer to the start * of the contiguous data area that's been reserved based * on the packet length. The data area is forced to 32-bit * alignment and the buffer length to a multiple of 4 bytes. * This is done mainly so beacon frames (that require this) * can use this interface too. */struct sk_buff *ieee80211_getmgtframe(u_int8_t **frm, u_int pktlen){ const u_int align = sizeof(u_int32_t); struct ieee80211_cb *cb; struct sk_buff *skb; u_int len; len = roundup(sizeof(struct ieee80211_frame) + pktlen, 4); skb = dev_alloc_skb(len + align-1); if (skb != NULL) { u_int off = ((unsigned long) skb->data) % align; if (off != 0) skb_reserve(skb, align - off); cb = (struct ieee80211_cb *)skb->cb; cb->ni = NULL; cb->flags = 0; cb->next = NULL; skb_reserve(skb, sizeof(struct ieee80211_frame)); *frm = skb_put(skb, pktlen); } return skb;}#if 0/* * Drain a queue of sk_buff's. */void__skb_queue_drain(struct sk_buff_head *q){ struct sk_buff *skb; while ((skb = __skb_dequeue(q)) != NULL) dev_kfree_skb(skb);}#endif#if IEEE80211_VLAN_TAG_USED/* * VLAN support. *//* * Register a vlan group. */static voidieee80211_vlan_register(struct net_device *dev, struct vlan_group *grp){ struct ieee80211vap *vap = dev->priv; vap->iv_vlgrp = grp;}/* * Add an rx vlan identifier */static voidieee80211_vlan_add_vid(struct net_device *dev, unsigned short vid){ struct ieee80211vap *vap = dev->priv; if (vap->iv_vlgrp != NULL) vap->iv_bss->ni_vlan = vid;}/* * Kill (i.e. delete) a vlan identifier. */static voidieee80211_vlan_kill_vid(struct net_device *dev, unsigned short vid){ struct ieee80211vap *vap = dev->priv; if (vap->iv_vlgrp != NULL) vap->iv_vlgrp->vlan_devices[vid] = NULL;}#endif /* IEEE80211_VLAN_TAG_USED */voidieee80211_vlan_vattach(struct ieee80211vap *vap){#if IEEE80211_VLAN_TAG_USED struct net_device *dev = vap->iv_dev; dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_FILTER; dev->vlan_rx_register = ieee80211_vlan_register; dev->vlan_rx_add_vid = ieee80211_vlan_add_vid; dev->vlan_rx_kill_vid = ieee80211_vlan_kill_vid;#endif /* IEEE80211_VLAN_TAG_USED */}voidieee80211_vlan_vdetach(struct ieee80211vap *vap){}voidieee80211_notify_node_join(struct ieee80211_node *ni, int newassoc){ struct ieee80211vap *vap = ni->ni_vap; struct net_device *dev = vap->iv_dev; union iwreq_data wreq; if (ni == vap->iv_bss) { if (newassoc) netif_carrier_on(dev); memset(&wreq, 0, sizeof(wreq)); IEEE80211_ADDR_COPY(wreq.addr.sa_data, ni->ni_bssid); wreq.addr.sa_family = ARPHRD_ETHER;#ifdef ATH_SUPERG_XR if (vap->iv_xrvap && vap->iv_flags & IEEE80211_F_XR) dev = vap->iv_xrvap->iv_dev;#endif wireless_send_event(dev, SIOCGIWAP, &wreq, NULL); } else { memset(&wreq, 0, sizeof(wreq)); IEEE80211_ADDR_COPY(wreq.addr.sa_data, ni->ni_macaddr); wreq.addr.sa_family = ARPHRD_ETHER;#ifdef ATH_SUPERG_XR if (vap->iv_xrvap && vap->iv_flags & IEEE80211_F_XR) dev = vap->iv_xrvap->iv_dev;#endif wireless_send_event(dev, IWEVREGISTERED, &wreq, NULL); }}voidieee80211_notify_node_leave(struct ieee80211_node *ni){ struct ieee80211vap *vap = ni->ni_vap; struct net_device *dev = vap->iv_dev; union iwreq_data wreq; if (ni == vap->iv_bss) { netif_carrier_off(dev); memset(wreq.ap_addr.sa_data, 0, ETHER_ADDR_LEN); wreq.ap_addr.sa_family = ARPHRD_ETHER; wireless_send_event(dev, SIOCGIWAP, &wreq, NULL); } else { /* fire off wireless event station leaving */ memset(&wreq, 0, sizeof(wreq)); IEEE80211_ADDR_COPY(wreq.addr.sa_data, ni->ni_macaddr); wreq.addr.sa_family = ARPHRD_ETHER; wireless_send_event(dev, IWEVEXPIRED, &wreq, NULL); }}voidieee80211_notify_scan_done(struct ieee80211vap *vap){ struct net_device *dev = vap->iv_dev; union iwreq_data wreq; IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s\n", "notify scan done"); /* dispatch wireless event indicating scan completed */ wreq.data.length = 0; wreq.data.flags = 0; wireless_send_event(dev, SIOCGIWSCAN, &wreq, NULL);}voidieee80211_notify_replay_failure(struct ieee80211vap *vap, const struct ieee80211_frame *wh, const struct ieee80211_key *k, u_int64_t rsc){ static const char *tag = "MLME-REPLAYFAILURE.indication"; struct net_device *dev = vap->iv_dev; union iwreq_data wrqu; char buf[128]; /* XXX */ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2, "%s replay detected <keyix %d, rsc %llu >", k->wk_cipher->ic_name, k->wk_keyix, rsc ); /* TODO: needed parameters: count, keyid, key type, src address, TSC */ snprintf(buf, sizeof(buf), "%s(keyid=%d %scast addr=%s)", tag, k->wk_keyix, IEEE80211_IS_MULTICAST(wh->i_addr1) ? "broad" : "uni", ether_sprintf(wh->i_addr1)); memset(&wrqu, 0, sizeof(wrqu)); wrqu.data.length = strlen(buf); wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf);}EXPORT_SYMBOL(ieee80211_notify_replay_failure);voidieee80211_notify_michael_failure(struct ieee80211vap *vap, const struct ieee80211_frame *wh, u_int keyix){ static const char *tag = "MLME-MICHAELMICFAILURE.indication"; struct net_device *dev = vap->iv_dev; union iwreq_data wrqu; char buf[128]; /* XXX */ IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_CRYPTO, wh->i_addr2, "Michael MIC verification failed <keyix %d>", keyix); vap->iv_stats.is_rx_tkipmic++; /* TODO: needed parameters: count, keyid, key type, src address, TSC */ snprintf(buf, sizeof(buf), "%s(keyid=%d %scast addr=%s)", tag, keyix, IEEE80211_IS_MULTICAST(wh->i_addr1) ? "broad" : "uni", ether_sprintf(wh->i_addr1)); memset(&wrqu, 0, sizeof(wrqu)); wrqu.data.length = strlen(buf); wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf);}EXPORT_SYMBOL(ieee80211_notify_michael_failure);intieee80211_load_module(const char *modname){ int rv; rv = request_module(modname); if (rv < 0) printk(KERN_ERR "couldn't load module '%s' (%d)\n", modname, rv); /* * XXX Further checking needed if module has been loaded successfully: * * "Note that a successful module load does not mean the module did not * then unload and exit on an error of its own. Callers must check that * the service they requested is now available not blindly invoke it." * http://kernelnewbies.org/documents/kdoc/kernel-api/r7338.html */ return rv;}#ifdef CONFIG_SYSCTLstatic struct proc_dir_entry *proc_madwifi;static int proc_madwifi_count = 0;static intproc_read_nodes(struct ieee80211vap *vap, char *buf, int space){ char *p = buf; struct ieee80211_node *ni; struct ieee80211_node_table *nt = (struct ieee80211_node_table *) &vap->iv_ic->ic_sta; //IEEE80211_NODE_LOCK(nt); TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { /* Assume each node needs 500 bytes */ if (buf + space < p + 500) break; if (ni->ni_vap == vap && 0 != memcmp(vap->iv_myaddr, ni->ni_macaddr, IEEE80211_ADDR_LEN)) { struct timespec t; jiffies_to_timespec(jiffies - ni->ni_last_rx, &t); p += sprintf(p, "macaddr: <%s>\n", ether_sprintf(ni->ni_macaddr)); p += sprintf(p, " rssi %d\n", ni->ni_rssi); p += sprintf(p, " last_rx %ld.%06ld\n", t.tv_sec, t.tv_nsec / 1000); } } //IEEE80211_NODE_UNLOCK(nt); return (p - buf);}static ssize_tproc_ieee80211_read(struct file *file, char __user *buf, size_t len, loff_t *offset){ loff_t pos = *offset; struct proc_ieee80211_priv *pv = (struct proc_ieee80211_priv *) file->private_data; if (!pv->rbuf) return -EINVAL; if (pos < 0) return -EINVAL; if (pos > pv->rlen) return -EFAULT; if (len > pv->rlen - pos) len = pv->rlen - pos; if (copy_to_user(buf, pv->rbuf + pos, len)) return -EFAULT; *offset = pos + len; return len;}static intproc_ieee80211_open(struct inode *inode, struct file *file){ struct proc_ieee80211_priv *pv = NULL; struct proc_dir_entry *dp = PDE(inode); struct ieee80211vap *vap = dp->data; if (!(file->private_data = kmalloc(sizeof(struct proc_ieee80211_priv), GFP_KERNEL))) return -ENOMEM; /* intially allocate both read and write buffers */ pv = (struct proc_ieee80211_priv *) file->private_data; memset(pv, 0, sizeof(struct proc_ieee80211_priv)); pv->rbuf = vmalloc(MAX_PROC_IEEE80211_SIZE); if (!pv->rbuf) { kfree(pv); return -ENOMEM; } pv->wbuf = vmalloc(MAX_PROC_IEEE80211_SIZE); if (!pv->wbuf) { vfree(pv->rbuf); kfree(pv); return -ENOMEM; } memset(pv->wbuf, 0, MAX_PROC_IEEE80211_SIZE); memset(pv->rbuf, 0, MAX_PROC_IEEE80211_SIZE); pv->max_wlen = MAX_PROC_IEEE80211_SIZE; pv->max_rlen = MAX_PROC_IEEE80211_SIZE; /* now read the data into the buffer */ pv->rlen = proc_read_nodes(vap, pv->rbuf, MAX_PROC_IEEE80211_SIZE); return 0;}static ssize_tproc_ieee80211_write(struct file *file, const char __user *buf, size_t len, loff_t *offset){ loff_t pos = *offset; struct proc_ieee80211_priv *pv = (struct proc_ieee80211_priv *) file->private_data; if (!pv->wbuf) return -EINVAL; if (pos < 0) return -EINVAL; if (pos >= pv->max_wlen) return 0; if (len > pv->max_wlen - pos) len = pv->max_wlen - pos; if (copy_from_user(pv->wbuf + pos, buf, len)) return -EFAULT; if (pos + len > pv->wlen) pv->wlen = pos + len; *offset = pos + len; return len;}static intproc_ieee80211_close(struct inode *inode, struct file *file){ struct proc_ieee80211_priv *pv = (struct proc_ieee80211_priv *) file->private_data;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -