Original 20051017 tarball
[acx-mac80211.git] / conv.c
blob574daf5193719d8cd47f4fad51a993d55d8626d9
1 /***********************************************************************
2 ** Copyright (C) 2003 ACX100 Open Source Project
3 **
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/
8 **
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
26 ** made directly to:
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>
41 #endif
43 #include "acx.h"
46 /***********************************************************************
47 ** proto_is_stt
49 ** Searches the 802.1h Selective Translation Table for a given
50 ** protocol.
52 ** prottype - protocol number (in host order) to search for.
54 ** Returns:
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
60 static inline int
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 */
71 return 1;
73 return 0;
74 /* return ((prottype == ETH_P_AARP) || (prottype == ETH_P_IPX)); */
77 /* Helpers */
79 static inline void
80 store_llc_snap(struct wlan_llc *llc)
82 llc->dsap = 0xaa; /* SNAP, see IEEE 802 */
83 llc->ssap = 0xaa;
84 llc->ctl = 0x03;
86 static inline int
87 llc_is_snap(const struct wlan_llc *llc)
89 return (llc->dsap == 0xaa)
90 && (llc->ssap == 0xaa)
91 && (llc->ctl == 0x03);
93 static inline void
94 store_oui_rfc1042(struct wlan_snap *snap)
96 snap->oui[0] = 0;
97 snap->oui[1] = 0;
98 snap->oui[2] = 0;
100 static inline int
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);
107 static inline void
108 store_oui_8021h(struct wlan_snap *snap)
110 snap->oui[0] = 0;
111 snap->oui[1] = 0;
112 snap->oui[2] = 0xf8;
114 static inline int
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
127 ** the 802.11 frame.
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;
142 const u8 *a1, *a3;
143 int header_len, payload_len;
144 int result = -1;
145 /* protocol type or data length, depending on whether
146 * DIX or 802.3 ethernet format */
147 u16 proto;
148 u16 fc;
150 FN_ENTER;
152 if (unlikely(!skb->len)) {
153 acxlog(L_DEBUG, "zero-length skb!\n");
154 goto end;
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);
166 goto end;
168 memcpy(w_hdr, skb->data, skb->len);
169 result = skb->len;
170 goto end;
173 /* step 1: classify ether frame, DIX or 802.3? */
174 e_hdr = (wlan_ethhdr_t *)skb->data;
175 proto = ntohs(e_hdr->type);
176 if (proto <= 1500) {
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;
182 } else {
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);
202 } else {
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);
217 a1 = e_hdr->daddr;
218 a3 = priv->bssid;
219 break;
220 case ACX_MODE_2_STA:
221 fc = (WF_FTYPE_DATAi | WF_FSTYPE_DATAONLYi | WF_FC_TODSi);
222 a1 = priv->bssid;
223 a3 = e_hdr->daddr;
224 break;
225 case ACX_MODE_3_AP:
226 fc = (WF_FTYPE_DATAi | WF_FSTYPE_DATAONLYi | WF_FC_FROMDSi);
227 a1 = e_hdr->daddr;
228 a3 = e_hdr->saddr;
229 break;
230 default:
231 printk("%s: error - converting eth to wlan in unknown mode\n",
232 priv->netdev->name);
233 result = -1;
234 goto end;
236 if (priv->wep_enabled)
237 SET_BIT(fc, WF_FC_ISWEPi);
239 w_hdr->fc = fc;
240 w_hdr->dur = 0;
241 MAC_COPY(w_hdr->a1, a1);
242 MAC_COPY(w_hdr->a2, priv->dev_addr);
243 MAC_COPY(w_hdr->a3, a3);
244 w_hdr->seq = 0;
246 #ifdef DEBUG_CONVERT
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);
253 #endif
255 end:
256 FN_EXIT1(result);
257 return result;
261 /***********************************************************************
262 ** acx_rxbuf_to_ether
264 ** Uses the contents of a received 802.11 frame to build an ether
265 ** frame.
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
272 struct sk_buff*
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;
279 struct sk_buff *skb;
280 const u8 *daddr;
281 const u8 *saddr;
282 const u8 *e_payload;
283 int buflen, payload_length;
284 unsigned int payload_offset, mtu;
285 u16 fc;
287 FN_ENTER;
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 */
298 fc = w_hdr->fc;
299 switch (WF_FC_FROMTODSi & fc) {
300 case 0:
301 daddr = w_hdr->a1;
302 saddr = w_hdr->a2;
303 break;
304 case WF_FC_FROMDSi:
305 daddr = w_hdr->a1;
306 saddr = w_hdr->a3;
307 break;
308 case WF_FC_TODSi:
309 daddr = w_hdr->a3;
310 saddr = w_hdr->a2;
311 break;
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);
315 daddr = w_hdr->a3;
316 saddr = w_hdr->a4;
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);
329 goto ret_null;
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",
360 priv->netdev->name,
361 payload_length, mtu + ETH_HLEN);
362 goto ret_null;
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);
369 if (unlikely(!skb))
370 goto no_skb;
371 skb_reserve(skb, 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",
391 priv->netdev->name,
392 payload_length, mtu);
393 goto ret_null;
396 /* allocate space and setup host buffer */
397 buflen = payload_length + ETH_HLEN;
398 skb = dev_alloc_skb(buflen + 2);
399 if (unlikely(!skb))
400 goto no_skb;
401 skb_reserve(skb, 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);
416 } else {
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",
420 payload_length);
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",
427 priv->netdev->name,
428 payload_length, mtu);
429 goto ret_null;
432 /* allocate space and setup host buffer */
433 buflen = payload_length + ETH_HLEN;
434 skb = dev_alloc_skb(buflen + 2);
435 if (unlikely(!skb))
436 goto no_skb;
437 skb_reserve(skb, 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);
453 } else {
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 */
456 /* any NON-ENCAP */
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);
463 goto ret_null;
466 /* allocate space and setup host buffer */
467 buflen = payload_length + ETH_HLEN;
468 skb = dev_alloc_skb(buflen + 2);
469 if (unlikely(!skb))
470 goto no_skb;
471 skb_reserve(skb, 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);
487 #ifdef DEBUG_CONVERT
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);
494 #endif
496 FN_EXIT0;
497 return skb;
499 no_skb:
500 printk("%s: rx: no memory for skb (%d bytes)\n",
501 priv->netdev->name, buflen + 2);
502 ret_null:
503 FN_EXIT1((int)NULL);
504 return NULL;