aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless
diff options
context:
space:
mode:
authorZhu Yi <yi.zhu@intel.com>2009-05-21 09:47:03 -0400
committerJohn W. Linville <linville@tuxdriver.com>2009-05-22 14:06:02 -0400
commite31a16d6f64ef0e324c6f54d5112703c3f13a9c4 (patch)
tree4ed30d0ebd1d948aaa14bcd4fb52f29d9bb2e7a8 /net/wireless
parenta971be223f243311a8014ddfc721f68e3ef2da9c (diff)
wireless: move some utility functions from mac80211 to cfg80211
The patch moves some utility functions from mac80211 to cfg80211. Because these functions are doing generic 802.11 operations so they are not mac80211 specific. The moving allows some fullmac drivers to be also benefit from these utility functions. Signed-off-by: Zhu Yi <yi.zhu@intel.com> Signed-off-by: Samuel Ortiz <samuel.ortiz@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/wireless')
-rw-r--r--net/wireless/util.c305
1 files changed, 305 insertions, 0 deletions
diff --git a/net/wireless/util.c b/net/wireless/util.c
index b94c8604ad7c..d072bff463aa 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -4,7 +4,9 @@
4 * Copyright 2007-2009 Johannes Berg <johannes@sipsolutions.net> 4 * Copyright 2007-2009 Johannes Berg <johannes@sipsolutions.net>
5 */ 5 */
6#include <linux/bitops.h> 6#include <linux/bitops.h>
7#include <linux/etherdevice.h>
7#include <net/cfg80211.h> 8#include <net/cfg80211.h>
9#include <net/ip.h>
8#include "core.h" 10#include "core.h"
9 11
10struct ieee80211_rate * 12struct ieee80211_rate *
@@ -198,3 +200,306 @@ int cfg80211_validate_key_settings(struct key_params *params, int key_idx,
198 200
199 return 0; 201 return 0;
200} 202}
203
204/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
205/* Ethernet-II snap header (RFC1042 for most EtherTypes) */
206const unsigned char rfc1042_header[] __aligned(2) =
207 { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
208EXPORT_SYMBOL(rfc1042_header);
209
210/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */
211const unsigned char bridge_tunnel_header[] __aligned(2) =
212 { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
213EXPORT_SYMBOL(bridge_tunnel_header);
214
215unsigned int ieee80211_hdrlen(__le16 fc)
216{
217 unsigned int hdrlen = 24;
218
219 if (ieee80211_is_data(fc)) {
220 if (ieee80211_has_a4(fc))
221 hdrlen = 30;
222 if (ieee80211_is_data_qos(fc))
223 hdrlen += IEEE80211_QOS_CTL_LEN;
224 goto out;
225 }
226
227 if (ieee80211_is_ctl(fc)) {
228 /*
229 * ACK and CTS are 10 bytes, all others 16. To see how
230 * to get this condition consider
231 * subtype mask: 0b0000000011110000 (0x00F0)
232 * ACK subtype: 0b0000000011010000 (0x00D0)
233 * CTS subtype: 0b0000000011000000 (0x00C0)
234 * bits that matter: ^^^ (0x00E0)
235 * value of those: 0b0000000011000000 (0x00C0)
236 */
237 if ((fc & cpu_to_le16(0x00E0)) == cpu_to_le16(0x00C0))
238 hdrlen = 10;
239 else
240 hdrlen = 16;
241 }
242out:
243 return hdrlen;
244}
245EXPORT_SYMBOL(ieee80211_hdrlen);
246
247unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb)
248{
249 const struct ieee80211_hdr *hdr =
250 (const struct ieee80211_hdr *)skb->data;
251 unsigned int hdrlen;
252
253 if (unlikely(skb->len < 10))
254 return 0;
255 hdrlen = ieee80211_hdrlen(hdr->frame_control);
256 if (unlikely(hdrlen > skb->len))
257 return 0;
258 return hdrlen;
259}
260EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb);
261
262int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
263{
264 int ae = meshhdr->flags & MESH_FLAGS_AE;
265 /* 7.1.3.5a.2 */
266 switch (ae) {
267 case 0:
268 return 6;
269 case 1:
270 return 12;
271 case 2:
272 return 18;
273 case 3:
274 return 24;
275 default:
276 return 6;
277 }
278}
279
280int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr,
281 enum nl80211_iftype iftype)
282{
283 struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
284 u16 hdrlen, ethertype;
285 u8 *payload;
286 u8 dst[ETH_ALEN];
287 u8 src[ETH_ALEN] __aligned(2);
288
289 if (unlikely(!ieee80211_is_data_present(hdr->frame_control)))
290 return -1;
291
292 hdrlen = ieee80211_hdrlen(hdr->frame_control);
293
294 /* convert IEEE 802.11 header + possible LLC headers into Ethernet
295 * header
296 * IEEE 802.11 address fields:
297 * ToDS FromDS Addr1 Addr2 Addr3 Addr4
298 * 0 0 DA SA BSSID n/a
299 * 0 1 DA BSSID SA n/a
300 * 1 0 BSSID SA DA n/a
301 * 1 1 RA TA DA SA
302 */
303 memcpy(dst, ieee80211_get_DA(hdr), ETH_ALEN);
304 memcpy(src, ieee80211_get_SA(hdr), ETH_ALEN);
305
306 switch (hdr->frame_control &
307 cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) {
308 case cpu_to_le16(IEEE80211_FCTL_TODS):
309 if (unlikely(iftype != NL80211_IFTYPE_AP &&
310 iftype != NL80211_IFTYPE_AP_VLAN))
311 return -1;
312 break;
313 case cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS):
314 if (unlikely(iftype != NL80211_IFTYPE_WDS &&
315 iftype != NL80211_IFTYPE_MESH_POINT))
316 return -1;
317 if (iftype == NL80211_IFTYPE_MESH_POINT) {
318 struct ieee80211s_hdr *meshdr =
319 (struct ieee80211s_hdr *) (skb->data + hdrlen);
320 hdrlen += ieee80211_get_mesh_hdrlen(meshdr);
321 if (meshdr->flags & MESH_FLAGS_AE_A5_A6) {
322 memcpy(dst, meshdr->eaddr1, ETH_ALEN);
323 memcpy(src, meshdr->eaddr2, ETH_ALEN);
324 }
325 }
326 break;
327 case cpu_to_le16(IEEE80211_FCTL_FROMDS):
328 if (iftype != NL80211_IFTYPE_STATION ||
329 (is_multicast_ether_addr(dst) &&
330 !compare_ether_addr(src, addr)))
331 return -1;
332 break;
333 case cpu_to_le16(0):
334 if (iftype != NL80211_IFTYPE_ADHOC)
335 return -1;
336 break;
337 }
338
339 if (unlikely(skb->len - hdrlen < 8))
340 return -1;
341
342 payload = skb->data + hdrlen;
343 ethertype = (payload[6] << 8) | payload[7];
344
345 if (likely((compare_ether_addr(payload, rfc1042_header) == 0 &&
346 ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
347 compare_ether_addr(payload, bridge_tunnel_header) == 0)) {
348 /* remove RFC1042 or Bridge-Tunnel encapsulation and
349 * replace EtherType */
350 skb_pull(skb, hdrlen + 6);
351 memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN);
352 memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN);
353 } else {
354 struct ethhdr *ehdr;
355 __be16 len;
356
357 skb_pull(skb, hdrlen);
358 len = htons(skb->len);
359 ehdr = (struct ethhdr *) skb_push(skb, sizeof(struct ethhdr));
360 memcpy(ehdr->h_dest, dst, ETH_ALEN);
361 memcpy(ehdr->h_source, src, ETH_ALEN);
362 ehdr->h_proto = len;
363 }
364 return 0;
365}
366EXPORT_SYMBOL(ieee80211_data_to_8023);
367
368int ieee80211_data_from_8023(struct sk_buff *skb, u8 *addr,
369 enum nl80211_iftype iftype, u8 *bssid, bool qos)
370{
371 struct ieee80211_hdr hdr;
372 u16 hdrlen, ethertype;
373 __le16 fc;
374 const u8 *encaps_data;
375 int encaps_len, skip_header_bytes;
376 int nh_pos, h_pos;
377 int head_need;
378
379 if (unlikely(skb->len < ETH_HLEN))
380 return -EINVAL;
381
382 nh_pos = skb_network_header(skb) - skb->data;
383 h_pos = skb_transport_header(skb) - skb->data;
384
385 /* convert Ethernet header to proper 802.11 header (based on
386 * operation mode) */
387 ethertype = (skb->data[12] << 8) | skb->data[13];
388 fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA);
389
390 switch (iftype) {
391 case NL80211_IFTYPE_AP:
392 case NL80211_IFTYPE_AP_VLAN:
393 fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
394 /* DA BSSID SA */
395 memcpy(hdr.addr1, skb->data, ETH_ALEN);
396 memcpy(hdr.addr2, addr, ETH_ALEN);
397 memcpy(hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN);
398 hdrlen = 24;
399 break;
400 case NL80211_IFTYPE_STATION:
401 fc |= cpu_to_le16(IEEE80211_FCTL_TODS);
402 /* BSSID SA DA */
403 memcpy(hdr.addr1, bssid, ETH_ALEN);
404 memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
405 memcpy(hdr.addr3, skb->data, ETH_ALEN);
406 hdrlen = 24;
407 break;
408 case NL80211_IFTYPE_ADHOC:
409 /* DA SA BSSID */
410 memcpy(hdr.addr1, skb->data, ETH_ALEN);
411 memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
412 memcpy(hdr.addr3, bssid, ETH_ALEN);
413 hdrlen = 24;
414 break;
415 default:
416 return -EOPNOTSUPP;
417 }
418
419 if (qos) {
420 fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA);
421 hdrlen += 2;
422 }
423
424 hdr.frame_control = fc;
425 hdr.duration_id = 0;
426 hdr.seq_ctrl = 0;
427
428 skip_header_bytes = ETH_HLEN;
429 if (ethertype == ETH_P_AARP || ethertype == ETH_P_IPX) {
430 encaps_data = bridge_tunnel_header;
431 encaps_len = sizeof(bridge_tunnel_header);
432 skip_header_bytes -= 2;
433 } else if (ethertype > 0x600) {
434 encaps_data = rfc1042_header;
435 encaps_len = sizeof(rfc1042_header);
436 skip_header_bytes -= 2;
437 } else {
438 encaps_data = NULL;
439 encaps_len = 0;
440 }
441
442 skb_pull(skb, skip_header_bytes);
443 nh_pos -= skip_header_bytes;
444 h_pos -= skip_header_bytes;
445
446 head_need = hdrlen + encaps_len - skb_headroom(skb);
447
448 if (head_need > 0 || skb_cloned(skb)) {
449 head_need = max(head_need, 0);
450 if (head_need)
451 skb_orphan(skb);
452
453 if (pskb_expand_head(skb, head_need, 0, GFP_ATOMIC)) {
454 printk(KERN_ERR "failed to reallocate Tx buffer\n");
455 return -ENOMEM;
456 }
457 skb->truesize += head_need;
458 }
459
460 if (encaps_data) {
461 memcpy(skb_push(skb, encaps_len), encaps_data, encaps_len);
462 nh_pos += encaps_len;
463 h_pos += encaps_len;
464 }
465
466 memcpy(skb_push(skb, hdrlen), &hdr, hdrlen);
467
468 nh_pos += hdrlen;
469 h_pos += hdrlen;
470
471 /* Update skb pointers to various headers since this modified frame
472 * is going to go through Linux networking code that may potentially
473 * need things like pointer to IP header. */
474 skb_set_mac_header(skb, 0);
475 skb_set_network_header(skb, nh_pos);
476 skb_set_transport_header(skb, h_pos);
477
478 return 0;
479}
480EXPORT_SYMBOL(ieee80211_data_from_8023);
481
482/* Given a data frame determine the 802.1p/1d tag to use. */
483unsigned int cfg80211_classify8021d(struct sk_buff *skb)
484{
485 unsigned int dscp;
486
487 /* skb->priority values from 256->263 are magic values to
488 * directly indicate a specific 802.1d priority. This is used
489 * to allow 802.1d priority to be passed directly in from VLAN
490 * tags, etc.
491 */
492 if (skb->priority >= 256 && skb->priority <= 263)
493 return skb->priority - 256;
494
495 switch (skb->protocol) {
496 case htons(ETH_P_IP):
497 dscp = ip_hdr(skb)->tos & 0xfc;
498 break;
499 default:
500 return 0;
501 }
502
503 return dscp >> 5;
504}
505EXPORT_SYMBOL(cfg80211_classify8021d);