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/ieee80211.c | |
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/ieee80211.c')
-rw-r--r-- | net/mac80211/ieee80211.c | 67 |
1 files changed, 38 insertions, 29 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 | ||