📄 ieee80211.c
字号:
/* * Copyright 2002-2005, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */#include <net/mac80211.h>#include <net/ieee80211_radiotap.h>#include <linux/module.h>#include <linux/init.h>#include <linux/netdevice.h>#include <linux/types.h>#include <linux/slab.h>#include <linux/skbuff.h>#include <linux/etherdevice.h>#include <linux/if_arp.h>#include <linux/wireless.h>#include <linux/rtnetlink.h>#include <linux/bitmap.h>#include <net/net_namespace.h>#include <net/cfg80211.h>#include "ieee80211_i.h"#include "ieee80211_rate.h"#include "wep.h"#include "wme.h"#include "aes_ccm.h"#include "ieee80211_led.h"#include "cfg.h"#include "debugfs.h"#include "debugfs_netdev.h"/* * For seeing transmitted packets on monitor interfaces * we have a radiotap header too. */struct ieee80211_tx_status_rtap_hdr { struct ieee80211_radiotap_header hdr; __le16 tx_flags; u8 data_retries;} __attribute__ ((packed));/* common interface routines */static int header_parse_80211(const struct sk_buff *skb, unsigned char *haddr){ memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN); /* addr2 */ return ETH_ALEN;}/* must be called under mdev tx lock */static void ieee80211_configure_filter(struct ieee80211_local *local){ unsigned int changed_flags; unsigned int new_flags = 0; if (atomic_read(&local->iff_promiscs)) new_flags |= FIF_PROMISC_IN_BSS; if (atomic_read(&local->iff_allmultis)) new_flags |= FIF_ALLMULTI; if (local->monitors) new_flags |= FIF_CONTROL | FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC; changed_flags = local->filter_flags ^ new_flags; /* be a bit nasty */ new_flags |= (1<<31); local->ops->configure_filter(local_to_hw(local), changed_flags, &new_flags, local->mdev->mc_count, local->mdev->mc_list); WARN_ON(new_flags & (1<<31)); local->filter_flags = new_flags & ~(1<<31);}/* master interface */static int ieee80211_master_open(struct net_device *dev){ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_sub_if_data *sdata; int res = -EOPNOTSUPP; /* we hold the RTNL here so can safely walk the list */ list_for_each_entry(sdata, &local->interfaces, list) { if (sdata->dev != dev && netif_running(sdata->dev)) { res = 0; break; } } return res;}static int ieee80211_master_stop(struct net_device *dev){ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_sub_if_data *sdata; /* we hold the RTNL here so can safely walk the list */ list_for_each_entry(sdata, &local->interfaces, list) if (sdata->dev != dev && netif_running(sdata->dev)) dev_close(sdata->dev); return 0;}static void ieee80211_master_set_multicast_list(struct net_device *dev){ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); ieee80211_configure_filter(local);}/* regular interfaces */static int ieee80211_change_mtu(struct net_device *dev, int new_mtu){ /* FIX: what would be proper limits for MTU? * This interface uses 802.3 frames. */ if (new_mtu < 256 || new_mtu > IEEE80211_MAX_DATA_LEN - 24 - 6) { printk(KERN_WARNING "%s: invalid MTU %d\n", dev->name, new_mtu); return -EINVAL; }#ifdef CONFIG_MAC80211_VERBOSE_DEBUG printk(KERN_DEBUG "%s: setting MTU %d\n", dev->name, new_mtu);#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ dev->mtu = new_mtu; return 0;}static inline int identical_mac_addr_allowed(int type1, int type2){ return (type1 == IEEE80211_IF_TYPE_MNTR || type2 == IEEE80211_IF_TYPE_MNTR || (type1 == IEEE80211_IF_TYPE_AP && type2 == IEEE80211_IF_TYPE_WDS) || (type1 == IEEE80211_IF_TYPE_WDS && (type2 == IEEE80211_IF_TYPE_WDS || type2 == IEEE80211_IF_TYPE_AP)) || (type1 == IEEE80211_IF_TYPE_AP && type2 == IEEE80211_IF_TYPE_VLAN) || (type1 == IEEE80211_IF_TYPE_VLAN && (type2 == IEEE80211_IF_TYPE_AP || type2 == IEEE80211_IF_TYPE_VLAN)));}static int ieee80211_open(struct net_device *dev){ struct ieee80211_sub_if_data *sdata, *nsdata; struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_if_init_conf conf; int res; sdata = IEEE80211_DEV_TO_SUB_IF(dev); /* we hold the RTNL here so can safely walk the list */ list_for_each_entry(nsdata, &local->interfaces, list) { struct net_device *ndev = nsdata->dev; if (ndev != dev && ndev != local->mdev && netif_running(ndev) && compare_ether_addr(dev->dev_addr, ndev->dev_addr) == 0) { /* * check whether it may have the same address */ if (!identical_mac_addr_allowed(sdata->type, nsdata->type)) return -ENOTUNIQ; /* * can only add VLANs to enabled APs */ if (sdata->type == IEEE80211_IF_TYPE_VLAN && nsdata->type == IEEE80211_IF_TYPE_AP && netif_running(nsdata->dev)) sdata->u.vlan.ap = nsdata; } } switch (sdata->type) { case IEEE80211_IF_TYPE_WDS: if (is_zero_ether_addr(sdata->u.wds.remote_addr)) return -ENOLINK; break; case IEEE80211_IF_TYPE_VLAN: if (!sdata->u.vlan.ap) return -ENOLINK; break; case IEEE80211_IF_TYPE_AP: case IEEE80211_IF_TYPE_STA: case IEEE80211_IF_TYPE_MNTR: case IEEE80211_IF_TYPE_IBSS: /* no special treatment */ break; case IEEE80211_IF_TYPE_INVALID: /* cannot happen */ WARN_ON(1); break; } if (local->open_count == 0) { res = 0; if (local->ops->start) res = local->ops->start(local_to_hw(local)); if (res) return res; ieee80211_hw_config(local); } switch (sdata->type) { case IEEE80211_IF_TYPE_VLAN: list_add(&sdata->u.vlan.list, &sdata->u.vlan.ap->u.ap.vlans); /* no need to tell driver */ break; case IEEE80211_IF_TYPE_MNTR: /* must be before the call to ieee80211_configure_filter */ local->monitors++; if (local->monitors == 1) { netif_tx_lock_bh(local->mdev); ieee80211_configure_filter(local); netif_tx_unlock_bh(local->mdev); local->hw.conf.flags |= IEEE80211_CONF_RADIOTAP; } break; case IEEE80211_IF_TYPE_STA: case IEEE80211_IF_TYPE_IBSS: sdata->u.sta.flags &= ~IEEE80211_STA_PREV_BSSID_SET; /* fall through */ default: conf.if_id = dev->ifindex; conf.type = sdata->type; conf.mac_addr = dev->dev_addr; res = local->ops->add_interface(local_to_hw(local), &conf); if (res && !local->open_count && local->ops->stop) local->ops->stop(local_to_hw(local)); if (res) return res; ieee80211_if_config(dev); ieee80211_reset_erp_info(dev); ieee80211_enable_keys(sdata); if (sdata->type == IEEE80211_IF_TYPE_STA && !(sdata->flags & IEEE80211_SDATA_USERSPACE_MLME)) netif_carrier_off(dev); else netif_carrier_on(dev); } if (local->open_count == 0) { res = dev_open(local->mdev); WARN_ON(res); tasklet_enable(&local->tx_pending_tasklet); tasklet_enable(&local->tasklet); } /* * set_multicast_list will be invoked by the networking core * which will check whether any increments here were done in * error and sync them down to the hardware as filter flags. */ if (sdata->flags & IEEE80211_SDATA_ALLMULTI) atomic_inc(&local->iff_allmultis); if (sdata->flags & IEEE80211_SDATA_PROMISC) atomic_inc(&local->iff_promiscs); local->open_count++; netif_start_queue(dev); return 0;}static int ieee80211_stop(struct net_device *dev){ struct ieee80211_sub_if_data *sdata; struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_if_init_conf conf; sdata = IEEE80211_DEV_TO_SUB_IF(dev); netif_stop_queue(dev); /* * Don't count this interface for promisc/allmulti while it * is down. dev_mc_unsync() will invoke set_multicast_list * on the master interface which will sync these down to the * hardware as filter flags. */ if (sdata->flags & IEEE80211_SDATA_ALLMULTI) atomic_dec(&local->iff_allmultis); if (sdata->flags & IEEE80211_SDATA_PROMISC) atomic_dec(&local->iff_promiscs); dev_mc_unsync(local->mdev, dev); /* down all dependent devices, that is VLANs */ if (sdata->type == IEEE80211_IF_TYPE_AP) { struct ieee80211_sub_if_data *vlan, *tmp; list_for_each_entry_safe(vlan, tmp, &sdata->u.ap.vlans, u.vlan.list) dev_close(vlan->dev); WARN_ON(!list_empty(&sdata->u.ap.vlans)); } local->open_count--; switch (sdata->type) { case IEEE80211_IF_TYPE_VLAN: list_del(&sdata->u.vlan.list); sdata->u.vlan.ap = NULL; /* no need to tell driver */ break; case IEEE80211_IF_TYPE_MNTR: local->monitors--; if (local->monitors == 0) { netif_tx_lock_bh(local->mdev); ieee80211_configure_filter(local); netif_tx_unlock_bh(local->mdev); local->hw.conf.flags &= ~IEEE80211_CONF_RADIOTAP; } break; case IEEE80211_IF_TYPE_STA: case IEEE80211_IF_TYPE_IBSS: sdata->u.sta.state = IEEE80211_DISABLED; del_timer_sync(&sdata->u.sta.timer); /* * When we get here, the interface is marked down. * Call synchronize_rcu() to wait for the RX path * should it be using the interface and enqueuing * frames at this very time on another CPU. */ synchronize_rcu(); skb_queue_purge(&sdata->u.sta.skb_queue); if (!local->ops->hw_scan && local->scan_dev == sdata->dev) { local->sta_scanning = 0; cancel_delayed_work(&local->scan_work); } flush_workqueue(local->hw.workqueue); sdata->u.sta.flags &= ~IEEE80211_STA_PRIVACY_INVOKED; kfree(sdata->u.sta.extra_ie); sdata->u.sta.extra_ie = NULL; sdata->u.sta.extra_ie_len = 0; /* fall through */ default: conf.if_id = dev->ifindex; conf.type = sdata->type; conf.mac_addr = dev->dev_addr; /* disable all keys for as long as this netdev is down */ ieee80211_disable_keys(sdata); local->ops->remove_interface(local_to_hw(local), &conf); } if (local->open_count == 0) { if (netif_running(local->mdev)) dev_close(local->mdev); if (local->ops->stop) local->ops->stop(local_to_hw(local)); tasklet_disable(&local->tx_pending_tasklet); tasklet_disable(&local->tasklet); } return 0;}static void ieee80211_set_multicast_list(struct net_device *dev){ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); int allmulti, promisc, sdata_allmulti, sdata_promisc; allmulti = !!(dev->flags & IFF_ALLMULTI); promisc = !!(dev->flags & IFF_PROMISC); sdata_allmulti = !!(sdata->flags & IEEE80211_SDATA_ALLMULTI); sdata_promisc = !!(sdata->flags & IEEE80211_SDATA_PROMISC); if (allmulti != sdata_allmulti) { if (dev->flags & IFF_ALLMULTI) atomic_inc(&local->iff_allmultis); else atomic_dec(&local->iff_allmultis); sdata->flags ^= IEEE80211_SDATA_ALLMULTI; } if (promisc != sdata_promisc) { if (dev->flags & IFF_PROMISC) atomic_inc(&local->iff_promiscs); else atomic_dec(&local->iff_promiscs); sdata->flags ^= IEEE80211_SDATA_PROMISC; } dev_mc_sync(local->mdev, dev);}static const struct header_ops ieee80211_header_ops = { .create = eth_header, .parse = header_parse_80211, .rebuild = eth_rebuild_header, .cache = eth_header_cache, .cache_update = eth_header_cache_update,};/* Must not be called for mdev */void ieee80211_if_setup(struct net_device *dev){ ether_setup(dev); dev->hard_start_xmit = ieee80211_subif_start_xmit; dev->wireless_handlers = &ieee80211_iw_handler_def; dev->set_multicast_list = ieee80211_set_multicast_list; dev->change_mtu = ieee80211_change_mtu; dev->open = ieee80211_open;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -