]> pilppa.org Git - linux-2.6-omap-h63xx.git/blob - net/ieee80211/softmac/ieee80211softmac_io.c
Merge branch 'audit.b3' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/audit...
[linux-2.6-omap-h63xx.git] / net / ieee80211 / softmac / ieee80211softmac_io.c
1 /* 
2  * Some parts based on code from net80211
3  * Copyright (c) 2001 Atsushi Onoe
4  * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. The name of the author may not be used to endorse or promote products
16  *    derived from this software without specific prior written permission.
17  *
18  * Alternatively, this software may be distributed under the terms of the
19  * GNU General Public License ("GPL") version 2 as published by the Free
20  * Software Foundation.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  * 
33  */
34
35 #include "ieee80211softmac_priv.h"
36
37 /* Helper functions for inserting data into the frames */
38
39 /* 
40  * Adds an ESSID element to the frame
41  *
42  */
43 static u8 *
44 ieee80211softmac_add_essid(u8 *dst, struct ieee80211softmac_essid *essid)
45 {
46         if (essid) {
47                 *dst++ = MFIE_TYPE_SSID;
48                 *dst++ = essid->len;
49                 memcpy(dst, essid->data, essid->len);
50                 return dst+essid->len;
51         } else {
52                 *dst++ = MFIE_TYPE_SSID;
53                 *dst++ = 0;
54                 return dst;
55         }
56 }     
57
58 /* Adds Supported Rates and if required Extended Rates Information Element
59  * to the frame, ASSUMES WE HAVE A SORTED LIST OF RATES */
60 static u8 *
61 ieee80211softmac_frame_add_rates(u8 *dst, const struct ieee80211softmac_ratesinfo *r)
62 {
63         int cck_len, ofdm_len;
64         *dst++ = MFIE_TYPE_RATES;
65
66         for(cck_len=0; ieee80211_is_cck_rate(r->rates[cck_len]) && (cck_len < r->count);cck_len++);
67
68         if(cck_len > IEEE80211SOFTMAC_MAX_RATES_LEN)
69                 cck_len = IEEE80211SOFTMAC_MAX_RATES_LEN;
70         *dst++ = cck_len;
71         memcpy(dst, r->rates, cck_len);
72         dst += cck_len;
73
74         if(cck_len < r->count){
75                 for (ofdm_len=0; ieee80211_is_ofdm_rate(r->rates[ofdm_len + cck_len]) && (ofdm_len + cck_len < r->count); ofdm_len++);
76                 if (ofdm_len > 0) {
77                         if (ofdm_len > IEEE80211SOFTMAC_MAX_EX_RATES_LEN)
78                                 ofdm_len = IEEE80211SOFTMAC_MAX_EX_RATES_LEN;
79                         *dst++ = MFIE_TYPE_RATES_EX;
80                         *dst++ = ofdm_len;
81                         memcpy(dst, r->rates + cck_len, ofdm_len);
82                         dst += ofdm_len;
83                 }
84         }       
85         return dst;
86 }
87
88 /* Allocate a management frame */
89 static u8 * 
90 ieee80211softmac_alloc_mgt(u32 size)
91 {
92         u8 * data;
93         
94         /* Add the header and FCS to the size */
95         size = size + IEEE80211_3ADDR_LEN;      
96         if(size > IEEE80211_DATA_LEN)
97                 return NULL;
98         /* Allocate the frame */
99         data = kmalloc(size, GFP_ATOMIC);
100         memset(data, 0, size);
101         return data;
102 }
103
104 /*
105  * Add a 2 Address Header
106  */
107 static void 
108 ieee80211softmac_hdr_2addr(struct ieee80211softmac_device *mac,
109         struct ieee80211_hdr_2addr *header, u32 type, u8 *dest)
110 {
111         /* Fill in the frame control flags */
112         header->frame_ctl = cpu_to_le16(type);
113         /* Control packets always have WEP turned off */        
114         if(type > IEEE80211_STYPE_CFENDACK && type < IEEE80211_STYPE_PSPOLL)
115                 header->frame_ctl |= mac->ieee->sec.level ? cpu_to_le16(IEEE80211_FCTL_PROTECTED) : 0;
116
117         /* Fill in the duration */
118         header->duration_id = 0;
119         /* FIXME: How do I find this?
120          * calculate. But most drivers just fill in 0 (except if it's a station id of course) */
121
122         /* Fill in the Destination Address */
123         if(dest == NULL)
124                 memset(header->addr1, 0xFF, ETH_ALEN);
125         else
126                 memcpy(header->addr1, dest, ETH_ALEN);
127         /* Fill in the Source Address */
128         memcpy(header->addr2, mac->ieee->dev->dev_addr, ETH_ALEN);
129
130 }
131
132
133 /* Add a 3 Address Header */
134 static void 
135 ieee80211softmac_hdr_3addr(struct ieee80211softmac_device *mac,
136         struct ieee80211_hdr_3addr *header, u32 type, u8 *dest, u8 *bssid)
137 {
138         /* This is common with 2addr, so use that instead */
139         ieee80211softmac_hdr_2addr(mac, (struct ieee80211_hdr_2addr *)header, type, dest);      
140         
141         /* Fill in the BSS ID */
142         if(bssid == NULL)
143                 memset(header->addr3, 0xFF, ETH_ALEN);
144         else
145                 memcpy(header->addr3, bssid, ETH_ALEN);
146
147         /* Fill in the sequence # */
148         /* FIXME: I need to add this to the softmac struct
149          * shouldn't the sequence number be in ieee80211? */
150 }
151
152
153 /*****************************************************************************
154  * Create Management packets
155  *****************************************************************************/ 
156
157 /* Creates an association request packet */
158 static u32
159 ieee80211softmac_assoc_req(struct ieee80211_assoc_request **pkt, 
160         struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net)
161 {
162         u8 *data;
163         (*pkt) = (struct ieee80211_assoc_request *)ieee80211softmac_alloc_mgt(
164                 2 +             /* Capability Info */
165                 2 +             /* Listen Interval */
166                 /* SSID IE */
167                 1 + 1 + IW_ESSID_MAX_SIZE +
168                 /* Rates IE */
169                 1 + 1 + IEEE80211SOFTMAC_MAX_RATES_LEN +
170                 /* Extended Rates IE */
171                 1 + 1 + IEEE80211SOFTMAC_MAX_EX_RATES_LEN +
172                 /* WPA IE if present */
173                 mac->wpa.IElen
174                 /* Other IE's?  Optional?
175                  * Yeah, probably need an extra IE parameter -- lots of vendors like to
176                  * fill in their own IEs */
177         );
178         if (unlikely((*pkt) == NULL))
179                 return 0;
180         ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), IEEE80211_STYPE_ASSOC_REQ, net->bssid, net->bssid);
181
182         /* Fill in capability Info */
183         (*pkt)->capability = (mac->ieee->iw_mode == IW_MODE_MASTER) || (mac->ieee->iw_mode == IW_MODE_INFRA) ?
184                 cpu_to_le16(WLAN_CAPABILITY_ESS) :
185                 cpu_to_le16(WLAN_CAPABILITY_IBSS);
186         /* Need to add this
187         (*pkt)->capability |= mac->ieee->short_slot ? 
188                         cpu_to_le16(WLAN_CAPABILITY_SHORT_SLOT_TIME) : 0;
189          */
190         (*pkt)->capability |= mac->ieee->sec.level ? cpu_to_le16(WLAN_CAPABILITY_PRIVACY) : 0;
191         /* Fill in Listen Interval (?) */
192         (*pkt)->listen_interval = cpu_to_le16(10);
193         
194         data = (u8 *)(*pkt)->info_element;
195         /* Add SSID */
196         data = ieee80211softmac_add_essid(data, &net->essid);
197         /* Add Rates */
198         data = ieee80211softmac_frame_add_rates(data, &mac->ratesinfo);
199         /* Add WPA IE */
200         if (mac->wpa.IElen && mac->wpa.IE) {
201                 memcpy(data, mac->wpa.IE, mac->wpa.IElen);
202                 data += mac->wpa.IElen;
203         }
204         /* Return the number of used bytes */
205         return (data - (u8*)(*pkt));
206 }
207
208 /* Create a reassociation request packet */
209 static u32
210 ieee80211softmac_reassoc_req(struct ieee80211_reassoc_request **pkt, 
211         struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net)
212 {
213         u8 *data;
214         (*pkt) = (struct ieee80211_reassoc_request *)ieee80211softmac_alloc_mgt(
215                 2 +             /* Capability Info */
216                 2 +             /* Listen Interval */
217                 ETH_ALEN +      /* AP MAC */
218                 /* SSID IE */
219                 1 + 1 + IW_ESSID_MAX_SIZE +
220                 /* Rates IE */
221                 1 + 1 + IEEE80211SOFTMAC_MAX_RATES_LEN +
222                 /* Extended Rates IE */
223                 1 + 1 + IEEE80211SOFTMAC_MAX_EX_RATES_LEN 
224                 /* Other IE's? */
225         );                              
226         if (unlikely((*pkt) == NULL))
227                 return 0;
228         ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), IEEE80211_STYPE_REASSOC_REQ, net->bssid, net->bssid);
229
230         /* Fill in capability Info */
231         (*pkt)->capability = mac->ieee->iw_mode == IW_MODE_MASTER ? 
232                                 cpu_to_le16(WLAN_CAPABILITY_ESS) :
233                                 cpu_to_le16(WLAN_CAPABILITY_IBSS);
234         /*
235         (*pkt)->capability |= mac->ieee->short_slot ? 
236                         cpu_to_le16(WLAN_CAPABILITY_SHORT_SLOT_TIME) : 0;
237          */
238         (*pkt)->capability |= mac->ieee->sec.level ?
239                         cpu_to_le16(WLAN_CAPABILITY_PRIVACY) : 0;
240                 
241         /* Fill in Listen Interval (?) */
242         (*pkt)->listen_interval = cpu_to_le16(10);
243         /* Fill in the current AP MAC */
244         memcpy((*pkt)->current_ap, mac->ieee->bssid, ETH_ALEN);
245         
246         data = (u8 *)(*pkt)->info_element;
247         /* Add SSID */
248         data = ieee80211softmac_add_essid(data, &net->essid); 
249         /* Add Rates */
250         data = ieee80211softmac_frame_add_rates(data, &mac->ratesinfo);
251         /* Return packet size */
252         return (data - (u8 *)(*pkt));
253 }
254
255 /* Create an authentication packet */
256 static u32
257 ieee80211softmac_auth(struct ieee80211_auth **pkt, 
258         struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net,
259         u16 transaction, u16 status)
260 {
261         u8 *data;
262         /* Allocate Packet */
263         (*pkt) = (struct ieee80211_auth *)ieee80211softmac_alloc_mgt(
264                 2 +             /* Auth Algorithm */
265                 2 +             /* Auth Transaction Seq */
266                 2 +             /* Status Code */
267                  /* Challenge Text IE */
268                 mac->ieee->open_wep ? 0 : 
269                 1 + 1 + WLAN_AUTH_CHALLENGE_LEN
270         );      
271         if (unlikely((*pkt) == NULL))
272                 return 0;
273         ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), IEEE80211_STYPE_AUTH, net->bssid, net->bssid);
274                 
275         /* Algorithm */
276         (*pkt)->algorithm = mac->ieee->open_wep ? 
277                 cpu_to_le16(WLAN_AUTH_OPEN) :
278                 cpu_to_le16(WLAN_AUTH_SHARED_KEY);
279         /* Transaction */
280         (*pkt)->transaction = cpu_to_le16(transaction);
281         /* Status */
282         (*pkt)->status = cpu_to_le16(status);
283         
284         data = (u8 *)(*pkt)->info_element;
285         /* Challenge Text */
286         if(!mac->ieee->open_wep){
287                 *data = MFIE_TYPE_CHALLENGE;
288                 data++;
289                 
290                 /* Copy the challenge in */
291                 // *data = challenge length
292                 // data += sizeof(u16);
293                 // memcpy(data, challenge, challenge length);
294                 // data += challenge length;
295                 
296                 /* Add the full size to the packet length */
297         }
298
299         /* Return the packet size */
300         return (data - (u8 *)(*pkt));
301 }
302
303 /* Create a disassocation or deauthentication packet */
304 static u32
305 ieee80211softmac_disassoc_deauth(struct ieee80211_disassoc **pkt,
306         struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net,
307         u16 type, u16 reason)
308 {
309         /* Allocate Packet */
310         (*pkt) = (struct ieee80211_disassoc *)ieee80211softmac_alloc_mgt(2);
311         if (unlikely((*pkt) == NULL))
312                 return 0;
313         ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), type, net->bssid, net->bssid);
314         /* Reason */
315         (*pkt)->reason = cpu_to_le16(reason);
316         /* Return the packet size */
317         return (2 + IEEE80211_3ADDR_LEN);
318 }
319
320 /* Create a probe request packet */
321 static u32
322 ieee80211softmac_probe_req(struct ieee80211_probe_request **pkt,
323         struct ieee80211softmac_device *mac, struct ieee80211softmac_essid *essid)
324 {
325         u8 *data;       
326         /* Allocate Packet */
327         (*pkt) = (struct ieee80211_probe_request *)ieee80211softmac_alloc_mgt(
328                 /* SSID of requested network */
329                 1 + 1 + IW_ESSID_MAX_SIZE +
330                 /* Rates IE */
331                 1 + 1 + IEEE80211SOFTMAC_MAX_RATES_LEN +
332                 /* Extended Rates IE */
333                 1 + 1 + IEEE80211SOFTMAC_MAX_EX_RATES_LEN 
334         );
335         if (unlikely((*pkt) == NULL))
336                 return 0;
337         ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), IEEE80211_STYPE_PROBE_REQ, NULL, NULL);
338                 
339         data = (u8 *)(*pkt)->info_element;
340         /* Add ESSID (can be NULL) */
341         data = ieee80211softmac_add_essid(data, essid);
342         /* Add Rates */
343         data = ieee80211softmac_frame_add_rates(data, &mac->ratesinfo);
344         /* Return packet size */
345         return (data - (u8 *)(*pkt));
346 }
347
348 /* Create a probe response packet */
349 /* FIXME: Not complete */
350 static u32
351 ieee80211softmac_probe_resp(struct ieee80211_probe_response **pkt,
352         struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net)
353 {
354         u8 *data;
355         /* Allocate Packet */
356         (*pkt) = (struct ieee80211_probe_response *)ieee80211softmac_alloc_mgt(
357                 8 +             /* Timestamp */
358                 2 +             /* Beacon Interval */
359                 2 +             /* Capability Info */
360                                 /* SSID IE */
361                 1 + 1 + IW_ESSID_MAX_SIZE +
362                 7 +             /* FH Parameter Set */
363                 2 +             /* DS Parameter Set */
364                 8 +             /* CF Parameter Set */
365                 4               /* IBSS Parameter Set */
366         );      
367         if (unlikely((*pkt) == NULL))
368                 return 0;
369         ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), IEEE80211_STYPE_PROBE_RESP, net->bssid, net->bssid);
370         data = (u8 *)(*pkt)->info_element;
371
372         /* Return the packet size */
373         return (data - (u8 *)(*pkt));
374 }
375
376
377 /* Sends a manangement packet
378  * FIXME: document the use of the arg parameter
379  * for _AUTH: (transaction #) | (status << 16)
380  */
381 int
382 ieee80211softmac_send_mgt_frame(struct ieee80211softmac_device *mac,
383         void *ptrarg, u32 type, u32 arg)
384 {
385         void *pkt = NULL;
386         u32 pkt_size = 0;
387
388         switch(type) {
389         case IEEE80211_STYPE_ASSOC_REQ:
390                 pkt_size = ieee80211softmac_assoc_req((struct ieee80211_assoc_request **)(&pkt), mac, (struct ieee80211softmac_network *)ptrarg);
391                 break;
392         case IEEE80211_STYPE_REASSOC_REQ:
393                 pkt_size = ieee80211softmac_reassoc_req((struct ieee80211_reassoc_request **)(&pkt), mac, (struct ieee80211softmac_network *)ptrarg);
394                 break;
395         case IEEE80211_STYPE_AUTH:
396                 pkt_size = ieee80211softmac_auth((struct ieee80211_auth **)(&pkt), mac, (struct ieee80211softmac_network *)ptrarg, (u16)(arg & 0xFFFF), (u16) (arg >> 16));
397                 break;
398         case IEEE80211_STYPE_DISASSOC:
399         case IEEE80211_STYPE_DEAUTH:
400                 pkt_size = ieee80211softmac_disassoc_deauth((struct ieee80211_disassoc **)(&pkt), mac, (struct ieee80211softmac_network *)ptrarg, type, (u16)(arg & 0xFFFF));
401                 break;
402         case IEEE80211_STYPE_PROBE_REQ:
403                 pkt_size = ieee80211softmac_probe_req((struct ieee80211_probe_request **)(&pkt), mac, (struct ieee80211softmac_essid *)ptrarg);
404                 break;
405         case IEEE80211_STYPE_PROBE_RESP:
406                 pkt_size = ieee80211softmac_probe_resp((struct ieee80211_probe_response **)(&pkt), mac, (struct ieee80211softmac_network *)ptrarg);
407                 break;
408         default:
409                 printkl(KERN_DEBUG PFX "Unsupported Management Frame type: %i\n", type);
410                 return -EINVAL;
411         };
412
413         if(pkt_size == 0 || pkt == NULL) {
414                 printkl(KERN_DEBUG PFX "Error, packet is nonexistant or 0 length\n");
415                 return -ENOMEM;
416         }
417         
418         /* Send the packet to the ieee80211 layer for tx */
419         /* we defined softmac->mgmt_xmit for this. Should we keep it
420          * as it is (that means we'd need to wrap this into a txb),
421          * modify the prototype (so it matches this function),
422          * or get rid of it alltogether?
423          * Does this work for you now?
424          */
425         ieee80211_tx_frame(mac->ieee, (struct ieee80211_hdr *)pkt, pkt_size);
426
427         kfree(pkt);
428         return 0;
429 }
430
431
432 /* Create an rts/cts frame */
433 static u32
434 ieee80211softmac_rts_cts(struct ieee80211_hdr_2addr **pkt,
435         struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net, 
436         u32 type)
437 {
438         /* Allocate Packet */
439         (*pkt) = kmalloc(IEEE80211_2ADDR_LEN, GFP_ATOMIC);      
440         memset(*pkt, 0, IEEE80211_2ADDR_LEN);
441         if((*pkt) == NULL)
442                 return 0;
443         ieee80211softmac_hdr_2addr(mac, (*pkt), type, net->bssid);
444         return IEEE80211_2ADDR_LEN;
445 }
446
447
448 /* Sends a control packet */
449 static int
450 ieee80211softmac_send_ctl_frame(struct ieee80211softmac_device *mac,
451         struct ieee80211softmac_network *net, u32 type, u32 arg)
452 {
453         void *pkt = NULL;
454         u32 pkt_size = 0;
455         
456         switch(type) {
457         case IEEE80211_STYPE_RTS:
458         case IEEE80211_STYPE_CTS:
459                 pkt_size = ieee80211softmac_rts_cts((struct ieee80211_hdr_2addr **)(&pkt), mac, net, type);
460                 break;
461         default:
462                 printkl(KERN_DEBUG PFX "Unsupported Control Frame type: %i\n", type);
463                 return -EINVAL;
464         }
465
466         if(pkt_size == 0)
467                 return -ENOMEM;
468         
469         /* Send the packet to the ieee80211 layer for tx */
470         ieee80211_tx_frame(mac->ieee, (struct ieee80211_hdr *) pkt, pkt_size);
471
472         kfree(pkt);
473         return 0;
474 }