1 /***********************************************************************
2 ** Copyright (C) 2003 ACX100 Open Source Project
4 ** The contents of this file are subject to the Mozilla Public
5 ** License Version 1.1 (the "License"); you may not use this file
6 ** except in compliance with the License. You may obtain a copy of
7 ** the License at http://www.mozilla.org/MPL/
9 ** Software distributed under the License is distributed on an "AS
10 ** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
11 ** implied. See the License for the specific language governing
12 ** rights and limitations under the License.
14 ** Alternatively, the contents of this file may be used under the
15 ** terms of the GNU Public License version 2 (the "GPL"), in which
16 ** case the provisions of the GPL are applicable instead of the
17 ** above. If you wish to allow the use of your version of this file
18 ** only under the terms of the GPL and not to allow others to use
19 ** your version of this file under the MPL, indicate your decision
20 ** by deleting the provisions above and replace them with the notice
21 ** and other provisions required by the GPL. If you do not delete
22 ** the provisions above, a recipient may use your version of this
23 ** file under either the MPL or the GPL.
24 ** ---------------------------------------------------------------------
25 ** Inquiries regarding the ACX100 Open Source Project can be
28 ** acx100-users@lists.sf.net
29 ** http://acx100.sf.net
30 ** ---------------------------------------------------------------------
33 #include <linux/config.h>
34 #include <linux/version.h>
35 #include <linux/skbuff.h>
36 #include <linux/if_arp.h>
37 #include <linux/etherdevice.h>
38 #include <linux/wireless.h>
39 #if WIRELESS_EXT >= 13
40 #include <net/iw_handler.h>
46 /***********************************************************************
49 ** Searches the 802.1h Selective Translation Table for a given
52 ** prottype - protocol number (in host order) to search for.
55 ** 1 - if the table is empty or a match is found.
56 ** 0 - if the table is non-empty and a match is not found.
58 ** Based largely on p80211conv.c of the linux-wlan-ng project
61 proto_is_stt(unsigned int proto
)
63 /* Always return found for now. This is the behavior used by the */
64 /* Zoom Win95 driver when 802.1h mode is selected */
65 /* TODO: If necessary, add an actual search we'll probably
66 need this to match the CMAC's way of doing things.
67 Need to do some testing to confirm.
70 if (proto
== 0x80f3) /* APPLETALK */
74 /* return ((prottype == ETH_P_AARP) || (prottype == ETH_P_IPX)); */
80 store_llc_snap(struct wlan_llc
*llc
)
82 llc
->dsap
= 0xaa; /* SNAP, see IEEE 802 */
87 llc_is_snap(const struct wlan_llc
*llc
)
89 return (llc
->dsap
== 0xaa)
90 && (llc
->ssap
== 0xaa)
91 && (llc
->ctl
== 0x03);
94 store_oui_rfc1042(struct wlan_snap
*snap
)
101 oui_is_rfc1042(const struct wlan_snap
*snap
)
103 return (snap
->oui
[0] == 0)
104 && (snap
->oui
[1] == 0)
105 && (snap
->oui
[2] == 0);
108 store_oui_8021h(struct wlan_snap
*snap
)
115 oui_is_8021h(const struct wlan_snap
*snap
)
117 return (snap
->oui
[0] == 0)
118 && (snap
->oui
[1] == 0)
119 && (snap
->oui
[2] == 0xf8);
123 /***********************************************************************
124 ** acx_ether_to_txbuf
126 ** Uses the contents of the ether frame to build the elements of
129 ** We don't actually set up the frame header here. That's the
130 ** MAC's job. We're only handling conversion of DIXII or 802.3+LLC
131 ** frames to something that works with 802.11.
133 ** Based largely on p80211conv.c of the linux-wlan-ng project
136 acx_ether_to_txbuf(wlandevice_t
*priv
, void *txbuf
, const struct sk_buff
*skb
)
138 struct wlan_hdr_a3
*w_hdr
;
139 struct wlan_ethhdr
*e_hdr
;
140 struct wlan_llc
*e_llc
;
141 struct wlan_snap
*e_snap
;
143 int header_len
, payload_len
;
145 /* protocol type or data length, depending on whether
146 * DIX or 802.3 ethernet format */
152 if (unlikely(!skb
->len
)) {
153 acxlog(L_DEBUG
, "zero-length skb!\n");
157 w_hdr
= (struct wlan_hdr_a3
*)txbuf
;
159 switch (priv
->mode
) {
160 case ACX_MODE_MONITOR
:
161 /* NB: one day we might want to play with DESC_CTL2_FCS
162 ** Will need to stop doing "- WLAN_FCS_LEN" here then */
163 if (skb
->len
>= WLAN_A4FR_MAXLEN_WEP_FCS
- WLAN_FCS_LEN
) {
164 printk("%s: can't tx oversized frame (%d bytes)\n",
165 priv
->netdev
->name
, skb
->len
);
168 memcpy(w_hdr
, skb
->data
, skb
->len
);
173 /* step 1: classify ether frame, DIX or 802.3? */
174 e_hdr
= (wlan_ethhdr_t
*)skb
->data
;
175 proto
= ntohs(e_hdr
->type
);
177 acxlog(L_DEBUG
, "tx: 802.3 len: %d\n", skb
->len
);
178 /* codes <= 1500 reserved for 802.3 lengths */
179 /* it's 802.3, pass ether payload unchanged, */
180 /* trim off ethernet header and copy payload to txdesc */
181 header_len
= WLAN_HDR_A3_LEN
;
183 /* it's DIXII, time for some conversion */
184 /* Create 802.11 packet. Header also contains llc and snap. */
186 acxlog(L_DEBUG
, "tx: DIXII len: %d\n", skb
->len
);
188 /* size of header is 802.11 header + llc + snap */
189 header_len
= WLAN_HDR_A3_LEN
+ sizeof(wlan_llc_t
) + sizeof(wlan_snap_t
);
190 /* llc is located behind the 802.11 header */
191 e_llc
= (wlan_llc_t
*)(w_hdr
+ 1);
192 /* snap is located behind the llc */
193 e_snap
= (wlan_snap_t
*)(e_llc
+ 1);
195 /* setup the LLC header */
196 store_llc_snap(e_llc
);
198 /* setup the SNAP header */
199 e_snap
->type
= htons(proto
);
200 if (proto_is_stt(proto
)) {
201 store_oui_8021h(e_snap
);
203 store_oui_rfc1042(e_snap
);
206 /* trim off ethernet header and copy payload to txbuf */
207 payload_len
= skb
->len
- sizeof(wlan_ethhdr_t
);
208 /* TODO: can we just let acx DMA payload from skb instead? */
209 memcpy((u8
*)txbuf
+ header_len
, skb
->data
+ sizeof(wlan_ethhdr_t
), payload_len
);
210 payload_len
+= header_len
;
211 result
= payload_len
;
213 /* Set up the 802.11 header */
214 switch (priv
->mode
) {
215 case ACX_MODE_0_ADHOC
:
216 fc
= (WF_FTYPE_DATAi
| WF_FSTYPE_DATAONLYi
);
221 fc
= (WF_FTYPE_DATAi
| WF_FSTYPE_DATAONLYi
| WF_FC_TODSi
);
226 fc
= (WF_FTYPE_DATAi
| WF_FSTYPE_DATAONLYi
| WF_FC_FROMDSi
);
231 printk("%s: error - converting eth to wlan in unknown mode\n",
236 if (priv
->wep_enabled
)
237 SET_BIT(fc
, WF_FC_ISWEPi
);
241 MAC_COPY(w_hdr
->a1
, a1
);
242 MAC_COPY(w_hdr
->a2
, priv
->dev_addr
);
243 MAC_COPY(w_hdr
->a3
, a3
);
247 if (acx_debug
& L_DATA
) {
248 printk("original eth frame [%d]: ", skb
->len
);
249 acx_dump_bytes(skb
->data
, skb
->len
);
250 printk("802.11 frame [%d]: ", payload_len
);
251 acx_dump_bytes(w_hdr
, payload_len
);
261 /***********************************************************************
262 ** acx_rxbuf_to_ether
264 ** Uses the contents of a received 802.11 frame to build an ether
267 ** This function extracts the src and dest address from the 802.11
268 ** frame to use in the construction of the eth frame.
270 ** Based largely on p80211conv.c of the linux-wlan-ng project
273 acx_rxbuf_to_ether(wlandevice_t
*priv
, rxbuffer_t
*rxbuf
)
275 struct wlan_hdr
*w_hdr
;
276 struct wlan_ethhdr
*e_hdr
;
277 struct wlan_llc
*e_llc
;
278 struct wlan_snap
*e_snap
;
283 int buflen
, payload_length
;
284 unsigned int payload_offset
, mtu
;
289 /* This looks complex because it must handle possible
290 ** phy header in rxbuff */
291 w_hdr
= acx_get_wlan_hdr(priv
, rxbuf
);
292 payload_offset
= WLAN_HDR_A3_LEN
; /* it is relative to w_hdr */
293 payload_length
= RXBUF_BYTES_USED(rxbuf
) /* entire rxbuff... */
294 - ((u8
*)w_hdr
- (u8
*)rxbuf
) /* minus space before 802.11 frame */
295 - WLAN_HDR_A3_LEN
; /* minus 802.11 header */
297 /* setup some vars for convenience */
299 switch (WF_FC_FROMTODSi
& fc
) {
312 default: /* WF_FC_FROMTODSi */
313 payload_offset
+= (WLAN_HDR_A4_LEN
- WLAN_HDR_A3_LEN
);
314 payload_length
-= (WLAN_HDR_A4_LEN
- WLAN_HDR_A3_LEN
);
319 if ((WF_FC_ISWEPi
& fc
) && IS_ACX100(priv
)) {
320 /* chop off the IV+ICV WEP header and footer */
321 acxlog(L_DATA
|L_DEBUG
, "rx: WEP packet, "
322 "chopping off IV and ICV\n");
323 payload_offset
+= WLAN_WEP_IV_LEN
;
324 payload_length
-= WLAN_WEP_IV_LEN
+ WLAN_WEP_ICV_LEN
;
327 if (unlikely(payload_length
< 0)) {
328 printk("%s: rx frame too short, ignored\n", priv
->netdev
->name
);
332 e_hdr
= (wlan_ethhdr_t
*) ((u8
*) w_hdr
+ payload_offset
);
333 e_llc
= (wlan_llc_t
*) e_hdr
;
334 e_snap
= (wlan_snap_t
*) (e_llc
+ 1);
335 e_payload
= (u8
*) (e_snap
+ 1);
336 mtu
= priv
->netdev
->mtu
;
338 acxlog(L_DATA
, "rx: payload_offset %d, payload_length %d\n",
339 payload_offset
, payload_length
);
340 acxlog(L_XFER
|L_DATA
,
341 "rx: frame info: llc=%02X%02X%02X "
342 "snap.oui=%02X%02X%02X snap.type=%04X\n",
343 e_llc
->dsap
, e_llc
->ssap
, e_llc
->ctl
,
344 e_snap
->oui
[0], e_snap
->oui
[1], e_snap
->oui
[2],
345 ntohs(e_snap
->type
));
347 /* Test for the various encodings */
348 if ((payload_length
>= sizeof(wlan_ethhdr_t
))
349 && ((e_llc
->dsap
!= 0xaa) || (e_llc
->ssap
!= 0xaa))
350 && ( (mac_is_equal(daddr
, e_hdr
->daddr
))
351 || (mac_is_equal(saddr
, e_hdr
->saddr
))
354 /* 802.3 Encapsulated: */
355 /* wlan frame body contains complete eth frame (header+body) */
356 acxlog(L_DEBUG
|L_DATA
, "rx: 802.3 ENCAP len=%d\n", payload_length
);
358 if (unlikely(payload_length
> (mtu
+ ETH_HLEN
))) {
359 printk("%s: rx: ENCAP frame too large (%d > %d)\n",
361 payload_length
, mtu
+ ETH_HLEN
);
365 /* allocate space and setup host buffer */
366 buflen
= payload_length
;
367 /* Attempt to align IP header (14 bytes eth header + 2 = 16) */
368 skb
= dev_alloc_skb(buflen
+ 2);
372 skb_put(skb
, buflen
); /* make room */
374 /* now copy the data from the 80211 frame */
375 memcpy(skb
->data
, e_hdr
, payload_length
);
377 } else if ( (payload_length
>= sizeof(wlan_llc_t
)+sizeof(wlan_snap_t
))
378 && llc_is_snap(e_llc
) ) {
379 /* wlan frame body contains: AA AA 03 ... (it's a SNAP) */
381 if ( !oui_is_rfc1042(e_snap
)
382 || (proto_is_stt(ieee2host16(e_snap
->type
)) /* && (ethconv == WLAN_ETHCONV_8021h) */)) {
383 acxlog(L_DEBUG
|L_DATA
, "rx: SNAP+RFC1042 len=%d\n", payload_length
);
384 /* wlan frame body contains: AA AA 03 !(00 00 00) ... -or- */
385 /* wlan frame body contains: AA AA 03 00 00 00 0x80f3 ... */
386 /* build eth hdr, type = len, copy AA AA 03... as eth body */
387 /* it's a SNAP + RFC1042 frame && protocol is in STT */
389 if (unlikely(payload_length
> mtu
)) {
390 printk("%s: rx: SNAP frame too large (%d > %d)\n",
392 payload_length
, mtu
);
396 /* allocate space and setup host buffer */
397 buflen
= payload_length
+ ETH_HLEN
;
398 skb
= dev_alloc_skb(buflen
+ 2);
402 skb_put(skb
, buflen
); /* make room */
404 /* create 802.3 header */
405 e_hdr
= (wlan_ethhdr_t
*) skb
->data
;
406 MAC_COPY(e_hdr
->daddr
, daddr
);
407 MAC_COPY(e_hdr
->saddr
, saddr
);
408 e_hdr
->type
= htons(payload_length
);
410 /* Now copy the data from the 80211 frame.
411 Make room in front for the eth header, and keep the
412 llc and snap from the 802.11 payload */
413 memcpy(skb
->data
+ ETH_HLEN
,
414 e_llc
, payload_length
);
417 /* wlan frame body contains: AA AA 03 00 00 00 [type] [tail] */
418 /* build eth hdr, type=[type], copy [tail] as eth body */
419 acxlog(L_DEBUG
|L_DATA
, "rx: 802.1h/RFC1042 len=%d\n",
421 /* it's an 802.1h frame (an RFC1042 && protocol is not in STT) */
422 /* build a DIXII + RFC894 */
424 payload_length
-= sizeof(wlan_llc_t
) + sizeof(wlan_snap_t
);
425 if (unlikely(payload_length
> mtu
)) {
426 printk("%s: rx: DIXII frame too large (%d > %d)\n",
428 payload_length
, mtu
);
432 /* allocate space and setup host buffer */
433 buflen
= payload_length
+ ETH_HLEN
;
434 skb
= dev_alloc_skb(buflen
+ 2);
438 skb_put(skb
, buflen
); /* make room */
440 /* create 802.3 header */
441 e_hdr
= (wlan_ethhdr_t
*) skb
->data
;
442 MAC_COPY(e_hdr
->daddr
, daddr
);
443 MAC_COPY(e_hdr
->saddr
, saddr
);
444 e_hdr
->type
= e_snap
->type
;
446 /* Now copy the data from the 80211 frame.
447 Make room in front for the eth header, and cut off the
448 llc and snap from the 802.11 payload */
449 memcpy(skb
->data
+ ETH_HLEN
,
450 e_payload
, payload_length
);
454 acxlog(L_DEBUG
|L_DATA
, "rx: NON-ENCAP len=%d\n", payload_length
);
455 /* build eth hdr, type=len, copy wlan body as eth body */
457 /* it's a generic 80211+LLC or IPX 'Raw 802.3' */
458 /* build an 802.3 frame */
460 if (unlikely(payload_length
> mtu
)) {
461 printk("%s: rx: OTHER frame too large (%d > %d)\n",
462 priv
->netdev
->name
, payload_length
, mtu
);
466 /* allocate space and setup host buffer */
467 buflen
= payload_length
+ ETH_HLEN
;
468 skb
= dev_alloc_skb(buflen
+ 2);
472 skb_put(skb
, buflen
); /* make room */
474 /* set up the 802.3 header */
475 e_hdr
= (wlan_ethhdr_t
*) skb
->data
;
476 MAC_COPY(e_hdr
->daddr
, daddr
);
477 MAC_COPY(e_hdr
->saddr
, saddr
);
478 e_hdr
->type
= htons(payload_length
);
480 /* now copy the data from the 80211 frame */
481 memcpy(skb
->data
+ ETH_HLEN
, e_llc
, payload_length
);
484 skb
->dev
= priv
->netdev
;
485 skb
->protocol
= eth_type_trans(skb
, priv
->netdev
);
488 if (acx_debug
& L_DATA
) {
489 printk("p802.11 frame [%d]: ", RXBUF_BYTES_RCVD(rxbuf
));
490 acx_dump_bytes(w_hdr
, RXBUF_BYTES_RCVD(rxbuf
));
491 printk("eth frame [%d]: ", skb
->len
);
492 acx_dump_bytes(skb
->data
, skb
->len
);
500 printk("%s: rx: no memory for skb (%d bytes)\n",
501 priv
->netdev
->name
, buflen
+ 2);