This patch allows monitor interfaces to be set by SIOCSIWMODE and to receive frames.
Also, "soft" and "hard" monitor modes are introduced. Signed-off-by: Jiri Benc <[EMAIL PROTECTED]> --- include/net/d80211.h | 10 ++++++ net/d80211/ieee80211.c | 67 ++++++++++++++++++++++++++++++++++++++++-- net/d80211/ieee80211_i.h | 1 + net/d80211/ieee80211_iface.c | 7 ++++ 4 files changed, 81 insertions(+), 4 deletions(-) 738241025b5e0091237d8c9d809d9affbdfaee29 diff --git a/include/net/d80211.h b/include/net/d80211.h index 7bc66b5..23bcbfa 100644 --- a/include/net/d80211.h +++ b/include/net/d80211.h @@ -330,6 +330,8 @@ enum ieee80211_if_types { * @if_id: internal interface ID. This number has no particular meaning to * drivers and the only allowed usage is to pass it to * ieee80211_beacon_get() and ieee80211_get_buffered_bc() functions. + * This field is not valid for monitor interfaces + * (interfaces of %IEEE80211_IF_TYPE_MNTR type). * @type: one of &enum ieee80211_if_types constants. Determines the type of * added/removed interface. * @mac_addr: pointer to MAC address of the interface. This pointer is valid @@ -476,6 +478,10 @@ struct ieee80211_hw { */ unsigned int device_strips_mic:1; + /* Device is capable of performing full monitor mode even during + * normal operation. */ + unsigned int monitor_during_oper:1; + /* 1 = low-level driver supports skb fraglist (NETIF_F_FRAGLIST), i.e., * more than one skb per frame */ unsigned int fraglist; @@ -511,7 +517,9 @@ struct ieee80211_hw { * more exactly, set UP). If the handler returns zero, the interface * is added. Driver should perform any initialization it needs prior * to returning zero. By returning non-zero, adding of the interface - * is not permitted. The open() handler is called after + * is not permitted. Unless monitor_during_oper is set, it is + * guaranteed that monitor interfaces and normal interfaces are + * mutually exclusive. The open() handler is called after * add_interface() if this is the first device added. At least one * of open() and add_interface() handler has to be non-NULL. If * add_interface() is NULL, one STA interface is permitted only. */ diff --git a/net/d80211/ieee80211.c b/net/d80211/ieee80211.c index e2a42af..b199b9e 100644 --- a/net/d80211/ieee80211.c +++ b/net/d80211/ieee80211.c @@ -1891,7 +1891,9 @@ static struct net_device_stats *ieee8021 static inline int identical_mac_addr_allowed(int type1, int type2) { - return ((type1 == IEEE80211_IF_TYPE_AP && + 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 || @@ -1925,6 +1927,36 @@ static int ieee80211_master_stop(struct return 0; } +/* Check if running monitor interfaces should go to a "soft monitor" mode + * and switch them if necessary. */ +static inline void ieee80211_start_soft_monitor(struct ieee80211_local *local) +{ + struct ieee80211_if_init_conf conf; + + if (local->open_count && local->open_count == local->monitors && + !local->hw->monitor_during_oper && local->hw->remove_interface) { + conf.if_id = -1; + conf.type = IEEE80211_IF_TYPE_MNTR; + conf.mac_addr = NULL; + local->hw->remove_interface(local->mdev, &conf); + } +} + +/* Check if running monitor interfaces should go to a "hard monitor" mode + * and switch them if necessary. */ +static void ieee80211_start_hard_monitor(struct ieee80211_local *local) +{ + struct ieee80211_if_init_conf conf; + + if (local->open_count && local->open_count == local->monitors && + !local->hw->monitor_during_oper && local->hw->add_interface) { + conf.if_id = -1; + conf.type = IEEE80211_IF_TYPE_MNTR; + conf.mac_addr = NULL; + local->hw->add_interface(local->mdev, &conf); + } +} + static int ieee80211_open(struct net_device *dev) { struct ieee80211_sub_if_data *sdata, *nsdata; @@ -1948,13 +1980,25 @@ static int ieee80211_open(struct net_dev memcmp(sdata->u.wds.remote_addr, "\0\0\0\0\0\0", ETH_ALEN) == 0) return -ENOLINK; + if (sdata->type == IEEE80211_IF_TYPE_MNTR && local->open_count && + !local->hw->monitor_during_oper) { + /* run the interface in a "soft monitor" mode */ + local->monitors++; + local->open_count++; + return 0; + } + ieee80211_start_soft_monitor(local); + if (local->hw->add_interface) { conf.if_id = dev->ifindex; conf.type = sdata->type; conf.mac_addr = dev->dev_addr; res = local->hw->add_interface(sdata->master, &conf); - if (res) + if (res) { + if (sdata->type == IEEE80211_IF_TYPE_MNTR) + ieee80211_start_hard_monitor(local); return res; + } } else { if (sdata->type != IEEE80211_IF_TYPE_STA) return -EOPNOTSUPP; @@ -1980,6 +2024,9 @@ static int ieee80211_open(struct net_dev } local->open_count++; + if (sdata->type == IEEE80211_IF_TYPE_MNTR) + local->monitors++; + netif_start_queue(dev); return 0; } @@ -1992,8 +2039,19 @@ static int ieee80211_stop(struct net_dev sdata = IEEE80211_DEV_TO_SUB_IF(dev); + if (sdata->type == IEEE80211_IF_TYPE_MNTR && + local->open_count > 1 && !local->hw->monitor_during_oper) { + /* remove "soft monitor" interface */ + local->open_count--; + local->monitors--; + return 0; + } + netif_stop_queue(dev); + if (sdata->type == IEEE80211_IF_TYPE_MNTR) + local->monitors--; + local->open_count--; if (local->open_count == 0) { ieee80211_stop_scan(sdata->master); @@ -2010,6 +2068,8 @@ static int ieee80211_stop(struct net_dev local->hw->remove_interface(sdata->master, &conf); } + ieee80211_start_hard_monitor(local); + return 0; } @@ -2242,7 +2302,8 @@ ieee80211_rx_mgmt(struct net_device *dev size_t hlen; struct ieee80211_sub_if_data *sdata; - dev = local->apdev; + if (msg_type != ieee80211_msg_monitor) + dev = local->apdev; skb->dev = dev; sdata = IEEE80211_DEV_TO_SUB_IF(dev); diff --git a/net/d80211/ieee80211_i.h b/net/d80211/ieee80211_i.h index 37d533d..2dbb132 100644 --- a/net/d80211/ieee80211_i.h +++ b/net/d80211/ieee80211_i.h @@ -305,6 +305,7 @@ struct ieee80211_local { struct net_device *wdev; /* wlan# - default Ethernet (data) devide */ struct net_device *apdev; /* wlan#ap - management frames (hostapd) */ int open_count; + int monitors; struct ieee80211_conf conf; int dev_index; diff --git a/net/d80211/ieee80211_iface.c b/net/d80211/ieee80211_iface.c index 2151bd9..dd809c2 100644 --- a/net/d80211/ieee80211_iface.c +++ b/net/d80211/ieee80211_iface.c @@ -8,6 +8,7 @@ * published by the Free Software Foundation. */ #include <linux/kernel.h> +#include <linux/if_arp.h> #include <linux/netdevice.h> #include <linux/rtnetlink.h> #include <net/d80211.h> @@ -124,6 +125,9 @@ void ieee80211_if_set_type(struct net_de sdata->bss = &msdata->u.ap; break; } + case IEEE80211_IF_TYPE_MNTR: + dev->type = ARPHRD_IEEE80211_PRISM; + break; default: printk(KERN_WARNING "%s: %s: Unknown interface type 0x%x", dev->name, __FUNCTION__, type); @@ -211,6 +215,9 @@ #endif /* CONFIG_D80211_VERBOSE_DEBUG */ } break; + case IEEE80211_IF_TYPE_MNTR: + dev->type = ARPHRD_ETHER; + break; } /* remove all STAs that are bound to this virtual interface */ -- 1.3.0 - To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html