diff options
author | Michael Wu <flamingice@sourmilk.net> | 2008-01-31 13:48:27 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-02-29 15:37:03 -0500 |
commit | 3d30d949cf3f9763393f3457721bca3ac2426e42 (patch) | |
tree | d80d1490f8a5263d74b4ed105835a7ef21eb6b80 /net/mac80211 | |
parent | 8944b79fe9b1fe249c599e7e51f1bfad539aab6d (diff) |
mac80211: Add cooked monitor mode support
This adds "cooked" monitor mode to mac80211. A monitor interface
in "cooked" mode will see all frames that mac80211 has not used
internally.
Signed-off-by: Michael Wu <flamingice@sourmilk.net>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211')
-rw-r--r-- | net/mac80211/ieee80211.c | 67 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 3 | ||||
-rw-r--r-- | net/mac80211/rx.c | 87 |
3 files changed, 126 insertions, 31 deletions
diff --git a/net/mac80211/ieee80211.c b/net/mac80211/ieee80211.c index 78fd91895c80..91f06c3f4a7c 100644 --- a/net/mac80211/ieee80211.c +++ b/net/mac80211/ieee80211.c | |||
@@ -239,6 +239,11 @@ static int ieee80211_open(struct net_device *dev) | |||
239 | /* no need to tell driver */ | 239 | /* no need to tell driver */ |
240 | break; | 240 | break; |
241 | case IEEE80211_IF_TYPE_MNTR: | 241 | case IEEE80211_IF_TYPE_MNTR: |
242 | if (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) { | ||
243 | local->cooked_mntrs++; | ||
244 | break; | ||
245 | } | ||
246 | |||
242 | /* must be before the call to ieee80211_configure_filter */ | 247 | /* must be before the call to ieee80211_configure_filter */ |
243 | local->monitors++; | 248 | local->monitors++; |
244 | if (local->monitors == 1) | 249 | if (local->monitors == 1) |
@@ -370,6 +375,11 @@ static int ieee80211_stop(struct net_device *dev) | |||
370 | /* no need to tell driver */ | 375 | /* no need to tell driver */ |
371 | break; | 376 | break; |
372 | case IEEE80211_IF_TYPE_MNTR: | 377 | case IEEE80211_IF_TYPE_MNTR: |
378 | if (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) { | ||
379 | local->cooked_mntrs--; | ||
380 | break; | ||
381 | } | ||
382 | |||
373 | local->monitors--; | 383 | local->monitors--; |
374 | if (local->monitors == 0) | 384 | if (local->monitors == 0) |
375 | local->hw.conf.flags &= ~IEEE80211_CONF_RADIOTAP; | 385 | local->hw.conf.flags &= ~IEEE80211_CONF_RADIOTAP; |
@@ -1177,7 +1187,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb, | |||
1177 | u16 frag, type; | 1187 | u16 frag, type; |
1178 | struct ieee80211_tx_status_rtap_hdr *rthdr; | 1188 | struct ieee80211_tx_status_rtap_hdr *rthdr; |
1179 | struct ieee80211_sub_if_data *sdata; | 1189 | struct ieee80211_sub_if_data *sdata; |
1180 | int monitors; | 1190 | struct net_device *prev_dev = NULL; |
1181 | 1191 | ||
1182 | if (!status) { | 1192 | if (!status) { |
1183 | printk(KERN_ERR | 1193 | printk(KERN_ERR |
@@ -1290,7 +1300,11 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb, | |||
1290 | /* this was a transmitted frame, but now we want to reuse it */ | 1300 | /* this was a transmitted frame, but now we want to reuse it */ |
1291 | skb_orphan(skb); | 1301 | skb_orphan(skb); |
1292 | 1302 | ||
1293 | if (!local->monitors) { | 1303 | /* |
1304 | * This is a bit racy but we can avoid a lot of work | ||
1305 | * with this test... | ||
1306 | */ | ||
1307 | if (!local->monitors && !local->cooked_mntrs) { | ||
1294 | dev_kfree_skb(skb); | 1308 | dev_kfree_skb(skb); |
1295 | return; | 1309 | return; |
1296 | } | 1310 | } |
@@ -1324,42 +1338,37 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb, | |||
1324 | 1338 | ||
1325 | rthdr->data_retries = status->retry_count; | 1339 | rthdr->data_retries = status->retry_count; |
1326 | 1340 | ||
1341 | /* XXX: is this sufficient for BPF? */ | ||
1342 | skb_set_mac_header(skb, 0); | ||
1343 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
1344 | skb->pkt_type = PACKET_OTHERHOST; | ||
1345 | skb->protocol = htons(ETH_P_802_2); | ||
1346 | memset(skb->cb, 0, sizeof(skb->cb)); | ||
1347 | |||
1327 | rcu_read_lock(); | 1348 | rcu_read_lock(); |
1328 | monitors = local->monitors; | ||
1329 | list_for_each_entry_rcu(sdata, &local->interfaces, list) { | 1349 | list_for_each_entry_rcu(sdata, &local->interfaces, list) { |
1330 | /* | ||
1331 | * Using the monitors counter is possibly racy, but | ||
1332 | * if the value is wrong we simply either clone the skb | ||
1333 | * once too much or forget sending it to one monitor iface | ||
1334 | * The latter case isn't nice but fixing the race is much | ||
1335 | * more complicated. | ||
1336 | */ | ||
1337 | if (!monitors || !skb) | ||
1338 | goto out; | ||
1339 | |||
1340 | if (sdata->vif.type == IEEE80211_IF_TYPE_MNTR) { | 1350 | if (sdata->vif.type == IEEE80211_IF_TYPE_MNTR) { |
1341 | if (!netif_running(sdata->dev)) | 1351 | if (!netif_running(sdata->dev)) |
1342 | continue; | 1352 | continue; |
1343 | monitors--; | 1353 | |
1344 | if (monitors) | 1354 | if (prev_dev) { |
1345 | skb2 = skb_clone(skb, GFP_ATOMIC); | 1355 | skb2 = skb_clone(skb, GFP_ATOMIC); |
1346 | else | 1356 | if (skb2) { |
1347 | skb2 = NULL; | 1357 | skb2->dev = prev_dev; |
1348 | skb->dev = sdata->dev; | 1358 | netif_rx(skb2); |
1349 | /* XXX: is this sufficient for BPF? */ | 1359 | } |
1350 | skb_set_mac_header(skb, 0); | 1360 | } |
1351 | skb->ip_summed = CHECKSUM_UNNECESSARY; | 1361 | |
1352 | skb->pkt_type = PACKET_OTHERHOST; | 1362 | prev_dev = sdata->dev; |
1353 | skb->protocol = htons(ETH_P_802_2); | ||
1354 | memset(skb->cb, 0, sizeof(skb->cb)); | ||
1355 | netif_rx(skb); | ||
1356 | skb = skb2; | ||
1357 | } | 1363 | } |
1358 | } | 1364 | } |
1359 | out: | 1365 | if (prev_dev) { |
1366 | skb->dev = prev_dev; | ||
1367 | netif_rx(skb); | ||
1368 | skb = NULL; | ||
1369 | } | ||
1360 | rcu_read_unlock(); | 1370 | rcu_read_unlock(); |
1361 | if (skb) | 1371 | dev_kfree_skb(skb); |
1362 | dev_kfree_skb(skb); | ||
1363 | } | 1372 | } |
1364 | EXPORT_SYMBOL(ieee80211_tx_status); | 1373 | EXPORT_SYMBOL(ieee80211_tx_status); |
1365 | 1374 | ||
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 1129a4299de7..1b4a4497030d 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -131,6 +131,7 @@ typedef unsigned __bitwise__ ieee80211_rx_result; | |||
131 | #define IEEE80211_TXRXD_RXRA_MATCH BIT(5) | 131 | #define IEEE80211_TXRXD_RXRA_MATCH BIT(5) |
132 | #define IEEE80211_TXRXD_TX_INJECTED BIT(6) | 132 | #define IEEE80211_TXRXD_TX_INJECTED BIT(6) |
133 | #define IEEE80211_TXRXD_RX_AMSDU BIT(7) | 133 | #define IEEE80211_TXRXD_RX_AMSDU BIT(7) |
134 | #define IEEE80211_TXRXD_RX_CMNTR_REPORTED BIT(8) | ||
134 | struct ieee80211_txrx_data { | 135 | struct ieee80211_txrx_data { |
135 | struct sk_buff *skb; | 136 | struct sk_buff *skb; |
136 | struct net_device *dev; | 137 | struct net_device *dev; |
@@ -419,7 +420,7 @@ struct ieee80211_local { | |||
419 | 420 | ||
420 | struct net_device *mdev; /* wmaster# - "master" 802.11 device */ | 421 | struct net_device *mdev; /* wmaster# - "master" 802.11 device */ |
421 | int open_count; | 422 | int open_count; |
422 | int monitors; | 423 | int monitors, cooked_mntrs; |
423 | /* number of interfaces with corresponding FIF_ flags */ | 424 | /* number of interfaces with corresponding FIF_ flags */ |
424 | int fif_fcsfail, fif_plcpfail, fif_control, fif_other_bss; | 425 | int fif_fcsfail, fif_plcpfail, fif_control, fif_other_bss; |
425 | unsigned int filter_flags; /* FIF_* */ | 426 | unsigned int filter_flags; /* FIF_* */ |
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 3a3112f17832..b1fc112152c9 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c | |||
@@ -223,6 +223,9 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, | |||
223 | if (sdata->vif.type != IEEE80211_IF_TYPE_MNTR) | 223 | if (sdata->vif.type != IEEE80211_IF_TYPE_MNTR) |
224 | continue; | 224 | continue; |
225 | 225 | ||
226 | if (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) | ||
227 | continue; | ||
228 | |||
226 | if (prev_dev) { | 229 | if (prev_dev) { |
227 | skb2 = skb_clone(skb, GFP_ATOMIC); | 230 | skb2 = skb_clone(skb, GFP_ATOMIC); |
228 | if (skb2) { | 231 | if (skb2) { |
@@ -1520,6 +1523,86 @@ static void ieee80211_rx_michael_mic_report(struct net_device *dev, | |||
1520 | rx->skb = NULL; | 1523 | rx->skb = NULL; |
1521 | } | 1524 | } |
1522 | 1525 | ||
1526 | static void ieee80211_rx_cooked_monitor(struct ieee80211_txrx_data *rx) | ||
1527 | { | ||
1528 | struct ieee80211_sub_if_data *sdata; | ||
1529 | struct ieee80211_local *local = rx->local; | ||
1530 | struct ieee80211_rtap_hdr { | ||
1531 | struct ieee80211_radiotap_header hdr; | ||
1532 | u8 flags; | ||
1533 | u8 rate; | ||
1534 | __le16 chan_freq; | ||
1535 | __le16 chan_flags; | ||
1536 | } __attribute__ ((packed)) *rthdr; | ||
1537 | struct sk_buff *skb = rx->skb, *skb2; | ||
1538 | struct net_device *prev_dev = NULL; | ||
1539 | struct ieee80211_rx_status *status = rx->u.rx.status; | ||
1540 | |||
1541 | if (rx->flags & IEEE80211_TXRXD_RX_CMNTR_REPORTED) | ||
1542 | goto out_free_skb; | ||
1543 | |||
1544 | if (skb_headroom(skb) < sizeof(*rthdr) && | ||
1545 | pskb_expand_head(skb, sizeof(*rthdr), 0, GFP_ATOMIC)) | ||
1546 | goto out_free_skb; | ||
1547 | |||
1548 | rthdr = (void *)skb_push(skb, sizeof(*rthdr)); | ||
1549 | memset(rthdr, 0, sizeof(*rthdr)); | ||
1550 | rthdr->hdr.it_len = cpu_to_le16(sizeof(*rthdr)); | ||
1551 | rthdr->hdr.it_present = | ||
1552 | cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) | | ||
1553 | (1 << IEEE80211_RADIOTAP_RATE) | | ||
1554 | (1 << IEEE80211_RADIOTAP_CHANNEL)); | ||
1555 | |||
1556 | rthdr->rate = rx->u.rx.rate->bitrate / 5; | ||
1557 | rthdr->chan_freq = cpu_to_le16(status->freq); | ||
1558 | |||
1559 | if (status->band == IEEE80211_BAND_5GHZ) | ||
1560 | rthdr->chan_flags = cpu_to_le16(IEEE80211_CHAN_OFDM | | ||
1561 | IEEE80211_CHAN_5GHZ); | ||
1562 | else | ||
1563 | rthdr->chan_flags = cpu_to_le16(IEEE80211_CHAN_DYN | | ||
1564 | IEEE80211_CHAN_2GHZ); | ||
1565 | |||
1566 | skb_set_mac_header(skb, 0); | ||
1567 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
1568 | skb->pkt_type = PACKET_OTHERHOST; | ||
1569 | skb->protocol = htons(ETH_P_802_2); | ||
1570 | |||
1571 | list_for_each_entry_rcu(sdata, &local->interfaces, list) { | ||
1572 | if (!netif_running(sdata->dev)) | ||
1573 | continue; | ||
1574 | |||
1575 | if (sdata->vif.type != IEEE80211_IF_TYPE_MNTR || | ||
1576 | !(sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES)) | ||
1577 | continue; | ||
1578 | |||
1579 | if (prev_dev) { | ||
1580 | skb2 = skb_clone(skb, GFP_ATOMIC); | ||
1581 | if (skb2) { | ||
1582 | skb2->dev = prev_dev; | ||
1583 | netif_rx(skb2); | ||
1584 | } | ||
1585 | } | ||
1586 | |||
1587 | prev_dev = sdata->dev; | ||
1588 | sdata->dev->stats.rx_packets++; | ||
1589 | sdata->dev->stats.rx_bytes += skb->len; | ||
1590 | } | ||
1591 | |||
1592 | if (prev_dev) { | ||
1593 | skb->dev = prev_dev; | ||
1594 | netif_rx(skb); | ||
1595 | skb = NULL; | ||
1596 | } else | ||
1597 | goto out_free_skb; | ||
1598 | |||
1599 | rx->flags |= IEEE80211_TXRXD_RX_CMNTR_REPORTED; | ||
1600 | return; | ||
1601 | |||
1602 | out_free_skb: | ||
1603 | dev_kfree_skb(skb); | ||
1604 | } | ||
1605 | |||
1523 | typedef ieee80211_rx_result (*ieee80211_rx_handler)(struct ieee80211_txrx_data *); | 1606 | typedef ieee80211_rx_result (*ieee80211_rx_handler)(struct ieee80211_txrx_data *); |
1524 | static ieee80211_rx_handler ieee80211_rx_handlers[] = | 1607 | static ieee80211_rx_handler ieee80211_rx_handlers[] = |
1525 | { | 1608 | { |
@@ -1574,9 +1657,11 @@ static void ieee80211_invoke_rx_handlers(struct ieee80211_sub_if_data *sdata, | |||
1574 | } | 1657 | } |
1575 | 1658 | ||
1576 | switch (res) { | 1659 | switch (res) { |
1660 | case RX_CONTINUE: | ||
1577 | case RX_DROP_MONITOR: | 1661 | case RX_DROP_MONITOR: |
1662 | ieee80211_rx_cooked_monitor(rx); | ||
1663 | break; | ||
1578 | case RX_DROP_UNUSABLE: | 1664 | case RX_DROP_UNUSABLE: |
1579 | case RX_CONTINUE: | ||
1580 | dev_kfree_skb(rx->skb); | 1665 | dev_kfree_skb(rx->skb); |
1581 | break; | 1666 | break; |
1582 | } | 1667 | } |