Clean up some more binaries in wpa_supplicant 'make clean'
[libeap.git] / mac80211_hwsim / mac80211_hwsim.c
index bccb2f4..d976106 100644 (file)
@@ -9,12 +9,15 @@
 
 /*
  * TODO:
- * - periodic Beacon transmission in AP mode
- * - IBSS mode simulation (Beacon transmission with competion for "air time")
+ * - IBSS mode simulation (Beacon transmission with competition for "air time")
  * - IEEE 802.11a and 802.11n modes
  */
 
 #include <net/mac80211.h>
+#include <net/ieee80211_radiotap.h>
+#include <linux/if_arp.h>
+#include <linux/rtnetlink.h>
+#include <linux/etherdevice.h>
 
 MODULE_AUTHOR("Jouni Malinen");
 MODULE_DESCRIPTION("Software simulator of 802.11 radio(s) for mac80211");
@@ -29,6 +32,7 @@ static struct class *hwsim_class;
 
 static struct ieee80211_hw **hwsim_radios;
 static int hwsim_radio_count;
+static struct net_device *hwsim_mon; /* global monitor netdev */
 
 
 static const struct ieee80211_channel hwsim_channels[] = {
@@ -74,18 +78,89 @@ struct mac80211_hwsim_data {
        int channel;
        enum ieee80211_phymode phymode;
        int radio_enabled;
-       int beacon_int;
+       unsigned long beacon_int; /* in jiffies unit */
        unsigned int rx_filter;
+       int started;
+       struct timer_list beacon_timer;
 };
 
 
+struct hwsim_radiotap_hdr {
+       struct ieee80211_radiotap_header hdr;
+       u8 rt_flags;
+       u8 rt_rate;
+       __le16 rt_channel;
+       __le16 rt_chbitmask;
+} __attribute__ ((packed));
+
+
+static int hwsim_mon_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       /* TODO: allow packet injection */
+       dev_kfree_skb(skb);
+       return 0;
+}
+
+
+static void mac80211_hwsim_monitor_rx(struct mac80211_hwsim_data *data,
+                                     struct sk_buff *tx_skb,
+                                     struct ieee80211_tx_control *control)
+{
+       struct sk_buff *skb;
+       struct hwsim_radiotap_hdr *hdr;
+       u16 flags;
+
+       if (!netif_running(hwsim_mon))
+               return;
+
+       skb = skb_copy_expand(tx_skb, sizeof(*hdr), 0, GFP_ATOMIC);
+       if (skb == NULL)
+               return;
+
+       hdr = (struct hwsim_radiotap_hdr *) skb_push(skb, sizeof(*hdr));
+       hdr->hdr.it_version = PKTHDR_RADIOTAP_VERSION;
+       hdr->hdr.it_pad = 0;
+       hdr->hdr.it_len = cpu_to_le16(sizeof(*hdr));
+       hdr->hdr.it_present = __constant_cpu_to_le32(
+            (1 << IEEE80211_RADIOTAP_FLAGS) |
+            (1 << IEEE80211_RADIOTAP_RATE) |
+            (1 << IEEE80211_RADIOTAP_CHANNEL));
+       hdr->rt_flags = 0;
+       hdr->rt_rate = control->tx_rate / 5;
+       hdr->rt_channel = data->freq;
+       flags = IEEE80211_CHAN_2GHZ;
+       if (control->rate->flags & IEEE80211_RATE_OFDM)
+               flags |= IEEE80211_CHAN_OFDM;
+       if (control->rate->flags & IEEE80211_RATE_CCK)
+               flags |= IEEE80211_CHAN_CCK;
+       hdr->rt_chbitmask = cpu_to_le16(flags);
+
+       skb->dev = hwsim_mon;
+       skb_set_mac_header(skb, 0);
+       skb->ip_summed = CHECKSUM_UNNECESSARY;
+       skb->pkt_type = PACKET_OTHERHOST;
+       skb->protocol = __constant_htons(ETH_P_802_2);
+       memset(skb->cb, 0, sizeof(skb->cb));
+       netif_rx(skb);
+}
+
+
 static int mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb,
                             struct ieee80211_tx_control *control)
 {
        struct mac80211_hwsim_data *data = hw->priv;
        struct ieee80211_tx_status tx_status;
        struct ieee80211_rx_status rx_status;
-       int i;
+       int i, ack = 0;
+       struct ieee80211_hdr *hdr;
+
+       mac80211_hwsim_monitor_rx(data, skb, control);
+
+       if (skb->len < 10) {
+               /* Should not happen; just a sanity check for addr1 use */
+               dev_kfree_skb(skb);
+               return NETDEV_TX_OK;
+       }
 
        if (!data->radio_enabled) {
                printk(KERN_DEBUG "%s: dropped TX frame since radio "
@@ -94,6 +169,10 @@ static int mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb,
                return NETDEV_TX_OK;
        }
 
+       hdr = (struct ieee80211_hdr *) skb->data;
+       if (is_multicast_ether_addr(hdr->addr1))
+               ack = 1;
+
        memset(&rx_status, 0, sizeof(rx_status));
        /* TODO: set mactime */
        rx_status.freq = data->freq;
@@ -110,34 +189,41 @@ static int mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb,
                if (hwsim_radios[i] == NULL || hwsim_radios[i] == hw)
                        continue;
                data2 = hwsim_radios[i]->priv;
-               if (!data2->radio_enabled || data->freq != data2->freq)
+               if (!data2->started || !data2->radio_enabled ||
+                   data->freq != data2->freq)
                        continue;
 
                nskb = skb_copy(skb, GFP_ATOMIC);
                if (nskb == NULL)
                        continue;
 
-               ieee80211_rx(hwsim_radios[i], nskb, &rx_status);
+               if (memcmp(hdr->addr1, hwsim_radios[i]->wiphy->perm_addr,
+                          ETH_ALEN) == 0)
+                       ack = 1;
+               ieee80211_rx_irqsafe(hwsim_radios[i], nskb, &rx_status);
        }
 
        memset(&tx_status, 0, sizeof(tx_status));
        memcpy(&tx_status.control, control, sizeof(*control));
-       /* TODO: proper ACK determination */
-       tx_status.flags = IEEE80211_TX_STATUS_ACK;
-       ieee80211_tx_status(hw, skb, &tx_status);
+       tx_status.flags = ack ? IEEE80211_TX_STATUS_ACK : 0;
+       ieee80211_tx_status_irqsafe(hw, skb, &tx_status);
        return NETDEV_TX_OK;
 }
 
 
 static int mac80211_hwsim_start(struct ieee80211_hw *hw)
 {
+       struct mac80211_hwsim_data *data = hw->priv;
        printk(KERN_DEBUG "%s:%s\n", wiphy_name(hw->wiphy), __func__);
+       data->started = 1;
        return 0;
 }
 
 
 static void mac80211_hwsim_stop(struct ieee80211_hw *hw)
 {
+       struct mac80211_hwsim_data *data = hw->priv;
+       data->started = 0;
        printk(KERN_DEBUG "%s:%s\n", wiphy_name(hw->wiphy), __func__);
 }
 
@@ -145,7 +231,10 @@ static void mac80211_hwsim_stop(struct ieee80211_hw *hw)
 static int mac80211_hwsim_add_interface(struct ieee80211_hw *hw,
                                        struct ieee80211_if_init_conf *conf)
 {
-       printk(KERN_DEBUG "%s:%s\n", wiphy_name(hw->wiphy), __func__);
+       DECLARE_MAC_BUF(mac);
+       printk(KERN_DEBUG "%s:%s (type=%d mac_addr=%s)\n",
+              wiphy_name(hw->wiphy), __func__, conf->type,
+              print_mac(mac, conf->mac_addr));
        return 0;
 }
 
@@ -153,7 +242,75 @@ static int mac80211_hwsim_add_interface(struct ieee80211_hw *hw,
 static void mac80211_hwsim_remove_interface(
        struct ieee80211_hw *hw, struct ieee80211_if_init_conf *conf)
 {
-       printk(KERN_DEBUG "%s:%s\n", wiphy_name(hw->wiphy), __func__);
+       DECLARE_MAC_BUF(mac);
+       printk(KERN_DEBUG "%s:%s (type=%d mac_addr=%s)\n",
+              wiphy_name(hw->wiphy), __func__, conf->type,
+              print_mac(mac, conf->mac_addr));
+}
+
+
+static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac,
+                                    struct ieee80211_vif *vif)
+{
+       struct ieee80211_hw *hw = arg;
+       struct mac80211_hwsim_data *data = hw->priv;
+       struct ieee80211_tx_control control;
+       struct sk_buff *skb;
+       struct ieee80211_rx_status rx_status;
+       int i;
+
+       if (vif->type != IEEE80211_IF_TYPE_AP)
+               return;
+
+       skb = ieee80211_beacon_get(hw, vif, &control);
+       if (skb == NULL)
+               return;
+
+       mac80211_hwsim_monitor_rx(data, skb, &control);
+
+       memset(&rx_status, 0, sizeof(rx_status));
+       /* TODO: set mactime */
+       rx_status.freq = data->freq;
+       rx_status.channel = data->channel;
+       rx_status.phymode = data->phymode;
+       rx_status.rate = control.tx_rate;
+       /* TODO: simulate signal strength (and optional packet drop) */
+
+       /* Copy skb to all enabled radios that are on the current frequency */
+       for (i = 0; i < hwsim_radio_count; i++) {
+               struct mac80211_hwsim_data *data2;
+               struct sk_buff *nskb;
+
+               if (hwsim_radios[i] == NULL || hwsim_radios[i] == hw)
+                       continue;
+               data2 = hwsim_radios[i]->priv;
+               if (!data2->started || !data2->radio_enabled ||
+                   data->freq != data2->freq)
+                       continue;
+
+               nskb = skb_copy(skb, GFP_ATOMIC);
+               if (nskb == NULL)
+                       continue;
+
+               ieee80211_rx_irqsafe(hwsim_radios[i], nskb, &rx_status);
+       }
+
+       dev_kfree_skb(skb);
+}
+
+
+static void mac80211_hwsim_beacon(unsigned long arg)
+{
+       struct ieee80211_hw *hw = (struct ieee80211_hw *) arg;
+       struct mac80211_hwsim_data *data = hw->priv;
+
+       if (!data->started || !data->radio_enabled)
+               return;
+
+       ieee80211_iterate_active_interfaces(hw, mac80211_hwsim_beacon_tx, hw);
+
+       data->beacon_timer.expires = jiffies + data->beacon_int;
+       add_timer(&data->beacon_timer);
 }
 
 
@@ -170,7 +327,14 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw,
        data->channel = conf->channel;
        data->phymode = conf->phymode;
        data->radio_enabled = conf->radio_enabled;
-       data->beacon_int = conf->beacon_int;
+       data->beacon_int = 1024 * conf->beacon_int / 1000 * HZ / 1000;
+       if (data->beacon_int < 1)
+               data->beacon_int = 1;
+
+       if (!data->started || !data->radio_enabled)
+               del_timer(&data->beacon_timer);
+       else
+               mod_timer(&data->beacon_timer, jiffies + data->beacon_int);
 
        return 0;
 }
@@ -233,6 +397,18 @@ static struct device_driver mac80211_hwsim_driver = {
 };
 
 
+static void hwsim_mon_setup(struct net_device *dev)
+{
+       dev->hard_start_xmit = hwsim_mon_xmit;
+       dev->destructor = free_netdev;
+       ether_setup(dev);
+       dev->tx_queue_len = 0;
+       dev->type = ARPHRD_IEEE80211_RADIOTAP;
+       memset(dev->dev_addr, 0, ETH_ALEN);
+       dev->dev_addr[0] = 0x12;
+}
+
+
 static int __init init_mac80211_hwsim(void)
 {
        int i, err = 0;
@@ -315,10 +491,34 @@ static int __init init_mac80211_hwsim(void)
                printk(KERN_DEBUG "%s: hwaddr %s registered\n",
                       wiphy_name(hw->wiphy),
                       print_mac(mac, hw->wiphy->perm_addr));
+
+               setup_timer(&data->beacon_timer, mac80211_hwsim_beacon,
+                           (unsigned long) hw);
        }
 
+       hwsim_mon = alloc_netdev(0, "hwsim%d", hwsim_mon_setup);
+       if (hwsim_mon == NULL)
+               goto failed;
+
+       rtnl_lock();
+
+       err = dev_alloc_name(hwsim_mon, hwsim_mon->name);
+       if (err < 0) {
+               goto failed_mon;
+       }
+
+       err = register_netdevice(hwsim_mon);
+       if (err < 0)
+               goto failed_mon;
+
+       rtnl_unlock();
+
        return 0;
 
+failed_mon:
+       rtnl_unlock();
+       free_netdev(hwsim_mon);
+
 failed:
        mac80211_hwsim_free();
        return err;
@@ -330,6 +530,7 @@ static void __exit exit_mac80211_hwsim(void)
        printk(KERN_DEBUG "mac80211_hwsim: unregister %d radios\n",
               hwsim_radio_count);
 
+       unregister_netdev(hwsim_mon);
        mac80211_hwsim_free();
 }