1
/* src/p80211conv.c - conversion between 802.11 and ethernet
3
* --------------------------------------------------------------------
5
* Copyright (C) 2003 ACX100 Open Source Project
7
* The contents of this file are subject to the Mozilla Public
8
* License Version 1.1 (the "License"); you may not use this file
9
* except in compliance with the License. You may obtain a copy of
10
* the License at http://www.mozilla.org/MPL/
12
* Software distributed under the License is distributed on an "AS
13
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
14
* implied. See the License for the specific language governing
15
* rights and limitations under the License.
17
* Alternatively, the contents of this file may be used under the
18
* terms of the GNU Public License version 2 (the "GPL"), in which
19
* case the provisions of the GPL are applicable instead of the
20
* above. If you wish to allow the use of your version of this file
21
* only under the terms of the GPL and not to allow others to use
22
* your version of this file under the MPL, indicate your decision
23
* by deleting the provisions above and replace them with the notice
24
* and other provisions required by the GPL. If you do not delete
25
* the provisions above, a recipient may use your version of this
26
* file under either the MPL or the GPL.
28
* --------------------------------------------------------------------
30
* This code is based on elements which are
31
* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
33
* http://www.linux-wlan.com
35
* --------------------------------------------------------------------
37
* Inquiries regarding the ACX100 Open Source Project can be
40
* acx100-users@lists.sf.net
41
* http://acx100.sf.net
43
* --------------------------------------------------------------------
46
#ifdef S_SPLINT_S /* some crap that splint needs to not crap out */
47
#define __signed__ signed
48
#define __u64 unsigned long long
49
#define loff_t unsigned long
50
#define sigval_t unsigned long
51
#define siginfo_t unsigned long
52
#define stack_t unsigned long
53
#define __s64 signed long long
55
#include <linux/config.h>
56
#include <linux/version.h>
58
#include <linux/skbuff.h>
59
#include <linux/if_arp.h>
60
#include <linux/etherdevice.h>
61
#include <linux/wireless.h>
62
#if WIRELESS_EXT >= 13
63
#include <net/iw_handler.h>
68
/*----------------------------------------------------------------
69
* acx_rxdesc_to_txdesc
71
* Converts a rx descriptor to a tx descriptor.
86
*----------------------------------------------------------------*/
88
void acx_rxdesc_to_txdesc(const struct rxhostdescriptor *rxhostdesc,
89
struct txdescriptor *txdesc)
91
struct txhostdescriptor *payload;
92
struct txhostdescriptor *header;
94
header = txdesc->fixed_size.s.host_desc;
97
payload->data_offset = 0;
98
header->data_offset = 0;
100
memcpy(header->data, &rxhostdesc->data->hdr_a3, WLAN_HDR_A3_LEN);
102
/* BUG??? rxhostdesc->data->data was 12 bytes farther that
103
** in reality because wlan_hdr was erroneously defined to have 3 extra void*
104
** fields... Bug should have been mangling AP bridged packets. Fixed */
105
memcpy(payload->data, &rxhostdesc->data->data_a3,
106
MAC_CNT_RCVD(rxhostdesc->data) - WLAN_HDR_A3_LEN);
110
/*----------------------------------------------------------------
113
* Searches the 802.1h Selective Translation Table for a given
117
* prottype protocol number (in host order) to search for.
120
* 1 - if the table is empty or a match is found.
121
* 0 - if the table is non-empty and a match is not found.
126
* May be called in interrupt or non-interrupt context
131
* Based largely on p80211conv.c of the linux-wlan-ng project
133
*----------------------------------------------------------------*/
135
static inline int proto_is_stt(unsigned int proto)
137
/* Always return found for now. This is the behavior used by the */
138
/* Zoom Win95 driver when 802.1h mode is selected */
139
/* TODO: If necessary, add an actual search we'll probably
140
need this to match the CMAC's way of doing things.
141
Need to do some testing to confirm.
144
if (proto == 0x80f3) /* APPLETALK */
148
/* return ((prottype == ETH_P_AARP) || (prottype == ETH_P_IPX)); */
153
static inline void store_llc_snap(struct wlan_llc *llc)
155
llc->dsap = 0xaa; /* SNAP, see IEEE 802 */
159
static inline int llc_is_snap(const struct wlan_llc *llc)
161
return (llc->dsap == 0xaa)
162
&& (llc->ssap == 0xaa)
163
&& (llc->ctl == 0x03);
165
static inline void store_oui_rfc1042(struct wlan_snap *snap)
171
static inline int oui_is_rfc1042(const struct wlan_snap *snap)
173
return (snap->oui[0] == 0)
174
&& (snap->oui[1] == 0)
175
&& (snap->oui[2] == 0);
177
static inline void store_oui_8021h(struct wlan_snap *snap)
183
static inline int oui_is_8021h(const struct wlan_snap *snap)
185
return (snap->oui[0] == 0)
186
&& (snap->oui[1] == 0)
187
&& (snap->oui[2] == 0xf8);
190
/*----------------------------------------------------------------
191
* acx_ether_to_txdesc
193
* Uses the contents of the ether frame to build the elements of
196
* We don't actually set up the frame header here. That's the
197
* MAC's job. We're only handling conversion of DIXII or 802.3+LLC
198
* frames to something that works with 802.11.
212
* Based largely on p80211conv.c of the linux-wlan-ng project
214
*----------------------------------------------------------------*/
216
int acx_ether_to_txdesc(wlandevice_t *priv,
217
struct txdescriptor *tx_desc,
218
const struct sk_buff *skb)
220
struct txhostdescriptor *header;
221
struct txhostdescriptor *payload;
222
union p80211_hdr *w_hdr;
223
struct wlan_ethhdr *e_hdr;
224
struct wlan_llc *e_llc;
225
struct wlan_snap *e_snap;
226
const u8 *a1,*a2,*a3;
227
int header_len,payload_len;
228
u16 proto; /* protocol type or data length, depending on whether DIX or 802.3 ethernet format */
233
if (unlikely(0 == skb->len)) {
234
acxlog(L_DEBUG, "zero-length skb!\n");
239
header = tx_desc->fixed_size.s.host_desc;
240
if ((unsigned long)0xffffffff == (unsigned long)header) /* FIXME: happens on card eject; better method? */
242
payload = header + 1;
244
switch (priv->mode) {
245
case ACX_MODE_MONITOR:
246
/* FIXME: skb size check... */
248
header->data_offset = 0;
249
memcpy(payload->data, skb->data, skb->len);
250
payload->length = cpu_to_le16(skb->len);
251
payload->data_offset = 0;
252
tx_desc->total_length = cpu_to_le16(skb->len);
257
/* step 1: classify ether frame, DIX or 802.3? */
258
e_hdr = (wlan_ethhdr_t *)skb->data;
259
proto = ntohs(e_hdr->type);
261
acxlog(L_DEBUG, "tx: 802.3 len: %d\n", skb->len);
262
/* codes <= 1500 reserved for 802.3 lengths */
263
/* it's 802.3, pass ether payload unchanged, */
264
/* trim off ethernet header and copy payload to tx_desc */
265
header_len = WLAN_HDR_A3_LEN;
266
/* TODO: must be equal to skb->len - sizeof(wlan_ethhdr_t), no? */
267
/* then we can do payload_len = ... after this big if() */
270
/* it's DIXII, time for some conversion */
271
/* Create 802.11 packet. Header also contains llc and snap. */
273
acxlog(L_DEBUG, "tx: DIXII len: %d\n", skb->len);
275
/* size of header is 802.11 header + llc + snap */
276
header_len = WLAN_HDR_A3_LEN + sizeof(wlan_llc_t) + sizeof(wlan_snap_t);
277
/* llc is located behind the 802.11 header */
278
e_llc = (wlan_llc_t*)(header->data + WLAN_HDR_A3_LEN);
279
/* snap is located behind the llc */
280
e_snap = (wlan_snap_t*)((u8*)e_llc + sizeof(wlan_llc_t));
282
/* setup the LLC header */
283
store_llc_snap(e_llc);
285
/* setup the SNAP header */
286
e_snap->type = htons(proto);
287
if (proto_is_stt(proto)) {
288
store_oui_8021h(e_snap);
290
store_oui_rfc1042(e_snap);
292
/* trim off ethernet header and copy payload to tx_desc */
293
payload_len = skb->len - sizeof(wlan_ethhdr_t);
295
/* TODO: can we just let acx DMA payload from skb instead? */
296
memcpy(payload->data, skb->data + sizeof(wlan_ethhdr_t), payload_len);
297
payload->length = cpu_to_le16(payload_len);
298
header->length = cpu_to_le16(header_len);
299
payload->data_offset = 0;
300
header->data_offset = 0;
302
/* calculate total tx_desc length */
303
tx_desc->total_length = cpu_to_le16(payload_len + header_len);
305
/* Set up the 802.11 header */
306
w_hdr = (p80211_hdr_t*)header->data;
308
/* It's a data frame */
309
fc = (WF_FTYPE_DATAi | WF_FSTYPE_DATAONLYi);
311
switch (priv->mode) {
312
case ACX_MODE_0_ADHOC:
318
SET_BIT(fc, WF_FC_TODSi);
324
SET_BIT(fc, WF_FC_FROMDSi);
330
acxlog(L_STD, "Error: Converting eth to wlan in unknown mode\n");
333
MAC_COPY(w_hdr->a3.a1, a1);
334
MAC_COPY(w_hdr->a3.a2, a2);
335
MAC_COPY(w_hdr->a3.a3, a3);
337
if (priv->wep_enabled)
338
SET_BIT(fc, WF_FC_ISWEPi);
345
if (debug & L_DATA) {
347
printk("Original eth frame [%d]: ", skb->len);
348
for (i = 0; i < skb->len; i++)
349
printk(" %02x", ((u8 *) skb->data)[i]);
352
printk("802.11 header [%d]: ", header_len));
353
for (i = 0; i < header_len; i++)
354
printk(" %02x", header->data[i]);
357
printk("802.11 payload [%d]: ", payload_len);
358
for (i = 0; i < payload_len); i++)
359
printk(" %02x", payload->data[i]);
369
/*----------------------------------------------------------------
370
* acx_rxdesc_to_ether
372
* Uses the contents of a received 802.11 frame to build an ether
375
* This function extracts the src and dest address from the 802.11
376
* frame to use in the construction of the eth frame.
390
* Based largely on p80211conv.c of the linux-wlan-ng project
392
*----------------------------------------------------------------*/
394
/*@null@*/ struct sk_buff *acx_rxdesc_to_ether(wlandevice_t *priv, const struct
395
rxhostdescriptor *rx_desc)
397
union p80211_hdr *w_hdr;
398
struct wlan_ethhdr *e_hdr;
399
struct wlan_llc *e_llc;
400
struct wlan_snap *e_snap;
407
unsigned int payload_offset;
412
payload_length = MAC_CNT_RCVD(rx_desc->data) - WLAN_HDR_A3_LEN;
413
payload_offset = WLAN_HDR_A3_LEN;
415
w_hdr = (p80211_hdr_t*)&rx_desc->data->hdr_a3;
417
/* check if additional header is included */
418
if (priv->rx_config_1 & RX_CFG1_INCLUDE_ADDIT_HDR) {
419
/* Mmm, strange, when receiving a packet, 4 bytes precede the packet. Is it the CRC ? */
420
w_hdr = (p80211_hdr_t*)(((u8*)w_hdr) + WLAN_CRC_LEN);
421
payload_length -= WLAN_CRC_LEN;
424
/* setup some vars for convenience */
426
switch (WF_FC_FROMTODSi & fc) {
428
daddr = w_hdr->a3.a1;
429
saddr = w_hdr->a3.a2;
432
daddr = w_hdr->a3.a1;
433
saddr = w_hdr->a3.a3;
436
daddr = w_hdr->a3.a3;
437
saddr = w_hdr->a3.a2;
439
default: /* WF_FC_FROMTODSi */
440
payload_offset = WLAN_HDR_A4_LEN;
441
payload_length -= ( WLAN_HDR_A4_LEN - WLAN_HDR_A3_LEN );
442
if (0 > payload_length) {
443
acxlog(L_STD, "A4 frame too short!\n");
447
daddr = w_hdr->a4.a3;
448
saddr = w_hdr->a4.a4;
451
if ((WF_FC_ISWEPi & fc) && (CHIPTYPE_ACX100 == priv->chip_type)) {
452
/* chop off the IV+ICV WEP header and footer */
453
acxlog(L_DATA | L_DEBUG, "rx: it's a WEP packet, chopping off IV and ICV.\n");
458
e_hdr = (wlan_ethhdr_t*) ((u8*) w_hdr + payload_offset);
460
e_llc = (wlan_llc_t*) e_hdr;
461
e_snap = (wlan_snap_t*) (e_llc+1);
462
e_payload = (u8*) (e_snap+1);
464
acxlog(L_DATA, "rx: payload_offset %i, payload_length %i\n", payload_offset, payload_length);
465
acxlog(L_XFER | L_DATA,
466
"rx: frame info: llc.dsap %X, llc.ssap %X, llc.ctl %X, snap.oui %02X%02X%02X, snap.type %X\n",
467
e_llc->dsap, e_llc->ssap,
468
e_llc->ctl, e_snap->oui[0],
469
e_snap->oui[1], e_snap->oui[2],
472
/* Test for the various encodings */
473
if ((payload_length >= sizeof(wlan_ethhdr_t))
474
&& ((e_llc->dsap != 0xaa) || (e_llc->ssap != 0xaa))
475
&& ( (mac_is_equal(daddr, e_hdr->daddr))
476
|| (mac_is_equal(saddr, e_hdr->saddr))
479
acxlog(L_DEBUG | L_DATA, "rx: 802.3 ENCAP len: %d\n", payload_length);
480
/* 802.3 Encapsulated */
481
/* Test for an overlength frame */
483
if (unlikely(payload_length > WLAN_MAX_ETHFRM_LEN)) {
484
/* A bogus length ethfrm has been encap'd. */
485
/* Is someone trying an oflow attack? */
486
acxlog(L_STD, "rx: ENCAP frame too large (%d > %d)\n",
487
payload_length, WLAN_MAX_ETHFRM_LEN);
492
/* allocate space and setup host buffer */
493
buflen = payload_length;
494
/* FIXME: implement skb ring buffer similar to
495
* xircom_tulip_cb.c? */
496
skb = dev_alloc_skb(buflen + 2); /* +2 is attempt to align IP header */
497
if (unlikely(!skb)) {
498
acxlog(L_STD, "rx: FAILED to allocate skb\n");
503
skb_put(skb, buflen); /* make room */
505
/* now copy the data from the 80211 frame */
506
memcpy(skb->data, e_hdr, payload_length); /* copy the data */
508
} else if ( (payload_length >= sizeof(wlan_llc_t)+sizeof(wlan_snap_t)) && llc_is_snap(e_llc) ) {
511
if ( !oui_is_rfc1042(e_snap) || (proto_is_stt(ieee2host16(e_snap->type)) /* && (ethconv == WLAN_ETHCONV_8021h) */)) {
512
acxlog(L_DEBUG | L_DATA, "rx: SNAP+RFC1042 len: %d\n", payload_length);
513
/* it's a SNAP + RFC1042 frame && protocol is in STT */
514
/* build 802.3 + RFC1042 */
516
/* Test for an overlength frame */
517
if (unlikely(payload_length + WLAN_ETHHDR_LEN > WLAN_MAX_ETHFRM_LEN)) {
518
/* A bogus length ethfrm has been sent. */
519
/* Is someone trying an oflow attack? */
520
acxlog(L_STD, "rx: SNAP frame too large (%d > %d)\n",
521
payload_length, WLAN_MAX_ETHFRM_LEN);
526
/* allocate space and setup host buffer */
527
buflen = payload_length + WLAN_ETHHDR_LEN;
528
skb = dev_alloc_skb(buflen + 2); /* +2 is attempt to align IP header */
529
if (unlikely(!skb)) {
530
acxlog(L_STD, "rx: FAILED to allocate skb\n");
535
skb_put(skb, buflen); /* make room */
537
/* create 802.3 header */
538
e_hdr = (wlan_ethhdr_t*) skb->data;
539
MAC_COPY(e_hdr->daddr, daddr);
540
MAC_COPY(e_hdr->saddr, saddr);
541
e_hdr->type = htons(payload_length);
543
/* Now copy the data from the 80211 frame.
544
Make room in front for the eth header, and keep the
545
llc and snap from the 802.11 payload */
546
memcpy(skb->data + WLAN_ETHHDR_LEN,
551
acxlog(L_DEBUG | L_DATA, "rx: 802.1h/RFC1042 len: %d\n", payload_length);
552
/* it's an 802.1h frame (an RFC1042 && protocol is not in STT) */
553
/* build a DIXII + RFC894 */
555
/* Test for an overlength frame */
556
if (unlikely(payload_length - sizeof(wlan_llc_t) - sizeof(wlan_snap_t) + WLAN_ETHHDR_LEN > WLAN_MAX_ETHFRM_LEN)) {
557
/* A bogus length ethfrm has been sent. */
558
/* Is someone trying an oflow attack? */
559
acxlog(L_STD, "rx: DIXII frame too large (%d > %d)\n",
560
payload_length - sizeof(wlan_llc_t) - sizeof(wlan_snap_t),
561
WLAN_MAX_ETHFRM_LEN - WLAN_ETHHDR_LEN);
566
/* allocate space and setup host buffer */
567
buflen = payload_length + WLAN_ETHHDR_LEN - sizeof(wlan_llc_t) - sizeof(wlan_snap_t);
568
skb = dev_alloc_skb(buflen + 2); /* +2 is attempt to align IP header */
569
if (unlikely(!skb)) {
570
acxlog(L_STD, "rx: FAILED to allocate skb\n");
575
skb_put(skb, buflen); /* make room */
577
/* create 802.3 header */
578
e_hdr = (wlan_ethhdr_t *) skb->data;
579
MAC_COPY(e_hdr->daddr, daddr);
580
MAC_COPY(e_hdr->saddr, saddr);
581
e_hdr->type = e_snap->type;
583
/* Now copy the data from the 80211 frame.
584
Make room in front for the eth header, and cut off the
585
llc and snap from the 802.11 payload */
586
memcpy(skb->data + WLAN_ETHHDR_LEN, e_payload,
587
payload_length - sizeof(wlan_llc_t) - sizeof(wlan_snap_t));
591
acxlog(L_DEBUG | L_DATA, "rx: NON-ENCAP len: %d\n", payload_length);
593
/* it's a generic 80211+LLC or IPX 'Raw 802.3' */
594
/* build an 802.3 frame */
595
/* allocate space and setup hostbuf */
597
/* Test for an overlength frame */
598
if (unlikely(payload_length + WLAN_ETHHDR_LEN > WLAN_MAX_ETHFRM_LEN)) {
599
/* A bogus length ethfrm has been sent. */
600
/* Is someone trying an oflow attack? */
601
acxlog(L_STD, "rx: OTHER frame too large (%d > %d)\n",
603
WLAN_MAX_ETHFRM_LEN - WLAN_ETHHDR_LEN);
608
/* allocate space and setup host buffer */
609
buflen = payload_length + WLAN_ETHHDR_LEN;
610
skb = dev_alloc_skb(buflen + 2); /* +2 is attempt to align IP header */
611
if (unlikely(!skb)) {
612
acxlog(L_STD, "rx: FAILED to allocate skb\n");
617
skb_put(skb, buflen); /* make room */
619
/* set up the 802.3 header */
620
e_hdr = (wlan_ethhdr_t *) skb->data;
621
MAC_COPY(e_hdr->daddr, daddr);
622
MAC_COPY(e_hdr->saddr, saddr);
623
e_hdr->type = htons(payload_length);
625
/* now copy the data from the 80211 frame */
626
memcpy(skb->data + WLAN_ETHHDR_LEN, e_llc, payload_length);
629
skb->dev = priv->netdev;
630
skb->protocol = eth_type_trans(skb, priv->netdev);
633
if (debug & L_DATA) {
635
printk("p802.11 frame [%d]:", MAC_CNT_RCVD(rx_desc->data));
636
for (i = 0; i < MAC_CNT_RCVD(rx_desc->data); i++)
637
printk(" %02x", ((u8 *) w_hdr)[i]);
640
printk("eth frame [%d]:", skb->len);
641
for (i = 0; i < skb->len; i++)
642
printk(" %02x", ((u8 *) skb->data)[i]);