Line data Source code
1 : /*
2 : * hostapd / IEEE 802.11F-2003 Inter-Access Point Protocol (IAPP)
3 : * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
4 : *
5 : * This software may be distributed under the terms of the BSD license.
6 : * See README for more details.
7 : *
8 : * Note: IEEE 802.11F-2003 was a experimental use specification. It has expired
9 : * and IEEE has withdrawn it. In other words, it is likely better to look at
10 : * using some other mechanism for AP-to-AP communication than extending the
11 : * implementation here.
12 : */
13 :
14 : /* TODO:
15 : * Level 1: no administrative or security support
16 : * (e.g., static BSSID to IP address mapping in each AP)
17 : * Level 2: support for dynamic mapping of BSSID to IP address
18 : * Level 3: support for encryption and authentication of IAPP messages
19 : * - add support for MOVE-notify and MOVE-response (this requires support for
20 : * finding out IP address for previous AP using RADIUS)
21 : * - add support for Send- and ACK-Security-Block to speedup IEEE 802.1X during
22 : * reassociation to another AP
23 : * - implement counters etc. for IAPP MIB
24 : * - verify endianness of fields in IAPP messages; are they big-endian as
25 : * used here?
26 : * - RADIUS connection for AP registration and BSSID to IP address mapping
27 : * - TCP connection for IAPP MOVE, CACHE
28 : * - broadcast ESP for IAPP ADD-notify
29 : * - ESP for IAPP MOVE messages
30 : * - security block sending/processing
31 : * - IEEE 802.11 context transfer
32 : */
33 :
34 : #include "utils/includes.h"
35 : #include <net/if.h>
36 : #include <sys/ioctl.h>
37 : #include <netpacket/packet.h>
38 :
39 : #include "utils/common.h"
40 : #include "utils/eloop.h"
41 : #include "common/ieee802_11_defs.h"
42 : #include "hostapd.h"
43 : #include "ap_config.h"
44 : #include "ieee802_11.h"
45 : #include "sta_info.h"
46 : #include "iapp.h"
47 :
48 :
49 : #define IAPP_MULTICAST "224.0.1.178"
50 : #define IAPP_UDP_PORT 3517
51 : #define IAPP_TCP_PORT 3517
52 :
53 : struct iapp_hdr {
54 : u8 version;
55 : u8 command;
56 : be16 identifier;
57 : be16 length;
58 : /* followed by length-6 octets of data */
59 : } __attribute__ ((packed));
60 :
61 : #define IAPP_VERSION 0
62 :
63 : enum IAPP_COMMAND {
64 : IAPP_CMD_ADD_notify = 0,
65 : IAPP_CMD_MOVE_notify = 1,
66 : IAPP_CMD_MOVE_response = 2,
67 : IAPP_CMD_Send_Security_Block = 3,
68 : IAPP_CMD_ACK_Security_Block = 4,
69 : IAPP_CMD_CACHE_notify = 5,
70 : IAPP_CMD_CACHE_response = 6,
71 : };
72 :
73 :
74 : /* ADD-notify - multicast UDP on the local LAN */
75 : struct iapp_add_notify {
76 : u8 addr_len; /* ETH_ALEN */
77 : u8 reserved;
78 : u8 mac_addr[ETH_ALEN];
79 : be16 seq_num;
80 : } __attribute__ ((packed));
81 :
82 :
83 : /* Layer 2 Update frame (802.2 Type 1 LLC XID Update response) */
84 : struct iapp_layer2_update {
85 : u8 da[ETH_ALEN]; /* broadcast */
86 : u8 sa[ETH_ALEN]; /* STA addr */
87 : be16 len; /* 6 */
88 : u8 dsap; /* null DSAP address */
89 : u8 ssap; /* null SSAP address, CR=Response */
90 : u8 control;
91 : u8 xid_info[3];
92 : } __attribute__ ((packed));
93 :
94 :
95 : /* MOVE-notify - unicast TCP */
96 : struct iapp_move_notify {
97 : u8 addr_len; /* ETH_ALEN */
98 : u8 reserved;
99 : u8 mac_addr[ETH_ALEN];
100 : u16 seq_num;
101 : u16 ctx_block_len;
102 : /* followed by ctx_block_len bytes */
103 : } __attribute__ ((packed));
104 :
105 :
106 : /* MOVE-response - unicast TCP */
107 : struct iapp_move_response {
108 : u8 addr_len; /* ETH_ALEN */
109 : u8 status;
110 : u8 mac_addr[ETH_ALEN];
111 : u16 seq_num;
112 : u16 ctx_block_len;
113 : /* followed by ctx_block_len bytes */
114 : } __attribute__ ((packed));
115 :
116 : enum {
117 : IAPP_MOVE_SUCCESSFUL = 0,
118 : IAPP_MOVE_DENIED = 1,
119 : IAPP_MOVE_STALE_MOVE = 2,
120 : };
121 :
122 :
123 : /* CACHE-notify */
124 : struct iapp_cache_notify {
125 : u8 addr_len; /* ETH_ALEN */
126 : u8 reserved;
127 : u8 mac_addr[ETH_ALEN];
128 : u16 seq_num;
129 : u8 current_ap[ETH_ALEN];
130 : u16 ctx_block_len;
131 : /* ctx_block_len bytes of context block followed by 16-bit context
132 : * timeout */
133 : } __attribute__ ((packed));
134 :
135 :
136 : /* CACHE-response - unicast TCP */
137 : struct iapp_cache_response {
138 : u8 addr_len; /* ETH_ALEN */
139 : u8 status;
140 : u8 mac_addr[ETH_ALEN];
141 : u16 seq_num;
142 : } __attribute__ ((packed));
143 :
144 : enum {
145 : IAPP_CACHE_SUCCESSFUL = 0,
146 : IAPP_CACHE_STALE_CACHE = 1,
147 : };
148 :
149 :
150 : /* Send-Security-Block - unicast TCP */
151 : struct iapp_send_security_block {
152 : u8 iv[8];
153 : u16 sec_block_len;
154 : /* followed by sec_block_len bytes of security block */
155 : } __attribute__ ((packed));
156 :
157 :
158 : /* ACK-Security-Block - unicast TCP */
159 : struct iapp_ack_security_block {
160 : u8 iv[8];
161 : u8 new_ap_ack_authenticator[48];
162 : } __attribute__ ((packed));
163 :
164 :
165 : struct iapp_data {
166 : struct hostapd_data *hapd;
167 : u16 identifier; /* next IAPP identifier */
168 : struct in_addr own, multicast;
169 : int udp_sock;
170 : int packet_sock;
171 : };
172 :
173 :
174 4 : static void iapp_send_add(struct iapp_data *iapp, u8 *mac_addr, u16 seq_num)
175 : {
176 : char buf[128];
177 : struct iapp_hdr *hdr;
178 : struct iapp_add_notify *add;
179 : struct sockaddr_in addr;
180 :
181 : /* Send IAPP ADD-notify to remove possible association from other APs
182 : */
183 :
184 4 : hdr = (struct iapp_hdr *) buf;
185 4 : hdr->version = IAPP_VERSION;
186 4 : hdr->command = IAPP_CMD_ADD_notify;
187 4 : hdr->identifier = host_to_be16(iapp->identifier++);
188 4 : hdr->length = host_to_be16(sizeof(*hdr) + sizeof(*add));
189 :
190 4 : add = (struct iapp_add_notify *) (hdr + 1);
191 4 : add->addr_len = ETH_ALEN;
192 4 : add->reserved = 0;
193 4 : os_memcpy(add->mac_addr, mac_addr, ETH_ALEN);
194 :
195 4 : add->seq_num = host_to_be16(seq_num);
196 :
197 4 : os_memset(&addr, 0, sizeof(addr));
198 4 : addr.sin_family = AF_INET;
199 4 : addr.sin_addr.s_addr = iapp->multicast.s_addr;
200 4 : addr.sin_port = htons(IAPP_UDP_PORT);
201 4 : if (sendto(iapp->udp_sock, buf, (char *) (add + 1) - buf, 0,
202 : (struct sockaddr *) &addr, sizeof(addr)) < 0)
203 0 : wpa_printf(MSG_INFO, "sendto[IAPP-ADD]: %s", strerror(errno));
204 4 : }
205 :
206 :
207 4 : static void iapp_send_layer2_update(struct iapp_data *iapp, u8 *addr)
208 : {
209 : struct iapp_layer2_update msg;
210 :
211 : /* Send Level 2 Update Frame to update forwarding tables in layer 2
212 : * bridge devices */
213 :
214 : /* 802.2 Type 1 Logical Link Control (LLC) Exchange Identifier (XID)
215 : * Update response frame; IEEE Std 802.2-1998, 5.4.1.2.1 */
216 :
217 4 : os_memset(msg.da, 0xff, ETH_ALEN);
218 4 : os_memcpy(msg.sa, addr, ETH_ALEN);
219 4 : msg.len = host_to_be16(6);
220 4 : msg.dsap = 0; /* NULL DSAP address */
221 4 : msg.ssap = 0x01; /* NULL SSAP address, CR Bit: Response */
222 4 : msg.control = 0xaf; /* XID response lsb.1111F101.
223 : * F=0 (no poll command; unsolicited frame) */
224 4 : msg.xid_info[0] = 0x81; /* XID format identifier */
225 4 : msg.xid_info[1] = 1; /* LLC types/classes: Type 1 LLC */
226 4 : msg.xid_info[2] = 1 << 1; /* XID sender's receive window size (RW)
227 : * FIX: what is correct RW with 802.11? */
228 :
229 4 : if (send(iapp->packet_sock, &msg, sizeof(msg), 0) < 0)
230 0 : wpa_printf(MSG_INFO, "send[L2 Update]: %s", strerror(errno));
231 4 : }
232 :
233 :
234 : /**
235 : * iapp_new_station - IAPP processing for a new STA
236 : * @iapp: IAPP data
237 : * @sta: The associated station
238 : */
239 4 : void iapp_new_station(struct iapp_data *iapp, struct sta_info *sta)
240 : {
241 4 : u16 seq = 0; /* TODO */
242 :
243 4 : if (iapp == NULL)
244 4 : return;
245 :
246 : /* IAPP-ADD.request(MAC Address, Sequence Number, Timeout) */
247 4 : hostapd_logger(iapp->hapd, sta->addr, HOSTAPD_MODULE_IAPP,
248 : HOSTAPD_LEVEL_DEBUG, "IAPP-ADD.request(seq=%d)", seq);
249 4 : iapp_send_layer2_update(iapp, sta->addr);
250 4 : iapp_send_add(iapp, sta->addr, seq);
251 :
252 : /* TODO: If this was reassociation:
253 : * IAPP-MOVE.request(MAC Address, Sequence Number, Old AP,
254 : * Context Block, Timeout)
255 : * TODO: Send IAPP-MOVE to the old AP; Map Old AP BSSID to
256 : * IP address */
257 : }
258 :
259 :
260 0 : static void iapp_process_add_notify(struct iapp_data *iapp,
261 : struct sockaddr_in *from,
262 : struct iapp_hdr *hdr, int len)
263 : {
264 0 : struct iapp_add_notify *add = (struct iapp_add_notify *) (hdr + 1);
265 : struct sta_info *sta;
266 :
267 0 : if (len != sizeof(*add)) {
268 0 : wpa_printf(MSG_INFO, "Invalid IAPP-ADD packet length %d (expected %lu)",
269 : len, (unsigned long) sizeof(*add));
270 0 : return;
271 : }
272 :
273 0 : sta = ap_get_sta(iapp->hapd, add->mac_addr);
274 :
275 : /* IAPP-ADD.indication(MAC Address, Sequence Number) */
276 0 : hostapd_logger(iapp->hapd, add->mac_addr, HOSTAPD_MODULE_IAPP,
277 : HOSTAPD_LEVEL_INFO,
278 : "Received IAPP ADD-notify (seq# %d) from %s:%d%s",
279 0 : be_to_host16(add->seq_num),
280 0 : inet_ntoa(from->sin_addr), ntohs(from->sin_port),
281 : sta ? "" : " (STA not found)");
282 :
283 0 : if (!sta)
284 0 : return;
285 :
286 : /* TODO: could use seq_num to try to determine whether last association
287 : * to this AP is newer than the one advertised in IAPP-ADD. Although,
288 : * this is not really a reliable verification. */
289 :
290 0 : hostapd_logger(iapp->hapd, add->mac_addr, HOSTAPD_MODULE_IAPP,
291 : HOSTAPD_LEVEL_DEBUG,
292 : "Removing STA due to IAPP ADD-notify");
293 0 : ap_sta_disconnect(iapp->hapd, sta, NULL, 0);
294 : }
295 :
296 :
297 : /**
298 : * iapp_receive_udp - Process IAPP UDP frames
299 : * @sock: File descriptor for the socket
300 : * @eloop_ctx: IAPP data (struct iapp_data *)
301 : * @sock_ctx: Not used
302 : */
303 6 : static void iapp_receive_udp(int sock, void *eloop_ctx, void *sock_ctx)
304 : {
305 6 : struct iapp_data *iapp = eloop_ctx;
306 : int len, hlen;
307 : unsigned char buf[128];
308 : struct sockaddr_in from;
309 : socklen_t fromlen;
310 : struct iapp_hdr *hdr;
311 :
312 : /* Handle incoming IAPP frames (over UDP/IP) */
313 :
314 6 : fromlen = sizeof(from);
315 6 : len = recvfrom(iapp->udp_sock, buf, sizeof(buf), 0,
316 : (struct sockaddr *) &from, &fromlen);
317 6 : if (len < 0) {
318 0 : wpa_printf(MSG_INFO, "iapp_receive_udp - recvfrom: %s",
319 0 : strerror(errno));
320 0 : return;
321 : }
322 :
323 6 : if (from.sin_addr.s_addr == iapp->own.s_addr)
324 6 : return; /* ignore own IAPP messages */
325 :
326 0 : hostapd_logger(iapp->hapd, NULL, HOSTAPD_MODULE_IAPP,
327 : HOSTAPD_LEVEL_DEBUG,
328 : "Received %d byte IAPP frame from %s%s\n",
329 : len, inet_ntoa(from.sin_addr),
330 : len < (int) sizeof(*hdr) ? " (too short)" : "");
331 :
332 0 : if (len < (int) sizeof(*hdr))
333 0 : return;
334 :
335 0 : hdr = (struct iapp_hdr *) buf;
336 0 : hlen = be_to_host16(hdr->length);
337 0 : hostapd_logger(iapp->hapd, NULL, HOSTAPD_MODULE_IAPP,
338 : HOSTAPD_LEVEL_DEBUG,
339 : "RX: version=%d command=%d id=%d len=%d\n",
340 0 : hdr->version, hdr->command,
341 0 : be_to_host16(hdr->identifier), hlen);
342 0 : if (hdr->version != IAPP_VERSION) {
343 0 : wpa_printf(MSG_INFO, "Dropping IAPP frame with unknown version %d",
344 0 : hdr->version);
345 0 : return;
346 : }
347 0 : if (hlen > len) {
348 0 : wpa_printf(MSG_INFO, "Underflow IAPP frame (hlen=%d len=%d)",
349 : hlen, len);
350 0 : return;
351 : }
352 0 : if (hlen < len) {
353 0 : wpa_printf(MSG_INFO, "Ignoring %d extra bytes from IAPP frame",
354 : len - hlen);
355 0 : len = hlen;
356 : }
357 :
358 0 : switch (hdr->command) {
359 : case IAPP_CMD_ADD_notify:
360 0 : iapp_process_add_notify(iapp, &from, hdr, len - sizeof(*hdr));
361 0 : break;
362 : case IAPP_CMD_MOVE_notify:
363 : /* TODO: MOVE is using TCP; so move this to TCP handler once it
364 : * is implemented.. */
365 : /* IAPP-MOVE.indication(MAC Address, New BSSID,
366 : * Sequence Number, AP Address, Context Block) */
367 : /* TODO: process */
368 0 : break;
369 : default:
370 0 : wpa_printf(MSG_INFO, "Unknown IAPP command %d", hdr->command);
371 0 : break;
372 : }
373 : }
374 :
375 :
376 2 : struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface)
377 : {
378 : struct ifreq ifr;
379 : struct sockaddr_ll addr;
380 : int ifindex;
381 : struct sockaddr_in *paddr, uaddr;
382 : struct iapp_data *iapp;
383 : struct ip_mreqn mreq;
384 2 : int reuseaddr = 1;
385 :
386 2 : iapp = os_zalloc(sizeof(*iapp));
387 2 : if (iapp == NULL)
388 0 : return NULL;
389 2 : iapp->hapd = hapd;
390 2 : iapp->udp_sock = iapp->packet_sock = -1;
391 :
392 : /* TODO:
393 : * open socket for sending and receiving IAPP frames over TCP
394 : */
395 :
396 2 : iapp->udp_sock = socket(PF_INET, SOCK_DGRAM, 0);
397 2 : if (iapp->udp_sock < 0) {
398 0 : wpa_printf(MSG_INFO, "iapp_init - socket[PF_INET,SOCK_DGRAM]: %s",
399 0 : strerror(errno));
400 0 : iapp_deinit(iapp);
401 0 : return NULL;
402 : }
403 :
404 2 : os_memset(&ifr, 0, sizeof(ifr));
405 2 : os_strlcpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
406 2 : if (ioctl(iapp->udp_sock, SIOCGIFINDEX, &ifr) != 0) {
407 0 : wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFINDEX): %s",
408 0 : strerror(errno));
409 0 : iapp_deinit(iapp);
410 0 : return NULL;
411 : }
412 2 : ifindex = ifr.ifr_ifindex;
413 :
414 2 : if (ioctl(iapp->udp_sock, SIOCGIFADDR, &ifr) != 0) {
415 0 : wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFADDR): %s",
416 0 : strerror(errno));
417 0 : iapp_deinit(iapp);
418 0 : return NULL;
419 : }
420 2 : paddr = (struct sockaddr_in *) &ifr.ifr_addr;
421 2 : if (paddr->sin_family != AF_INET) {
422 0 : wpa_printf(MSG_INFO, "IAPP: Invalid address family %i (SIOCGIFADDR)",
423 0 : paddr->sin_family);
424 0 : iapp_deinit(iapp);
425 0 : return NULL;
426 : }
427 2 : iapp->own.s_addr = paddr->sin_addr.s_addr;
428 :
429 2 : if (ioctl(iapp->udp_sock, SIOCGIFBRDADDR, &ifr) != 0) {
430 0 : wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFBRDADDR): %s",
431 0 : strerror(errno));
432 0 : iapp_deinit(iapp);
433 0 : return NULL;
434 : }
435 2 : paddr = (struct sockaddr_in *) &ifr.ifr_addr;
436 2 : if (paddr->sin_family != AF_INET) {
437 0 : wpa_printf(MSG_INFO, "Invalid address family %i (SIOCGIFBRDADDR)",
438 0 : paddr->sin_family);
439 0 : iapp_deinit(iapp);
440 0 : return NULL;
441 : }
442 2 : inet_aton(IAPP_MULTICAST, &iapp->multicast);
443 :
444 2 : os_memset(&uaddr, 0, sizeof(uaddr));
445 2 : uaddr.sin_family = AF_INET;
446 2 : uaddr.sin_port = htons(IAPP_UDP_PORT);
447 :
448 2 : if (setsockopt(iapp->udp_sock, SOL_SOCKET, SO_REUSEADDR, &reuseaddr,
449 : sizeof(reuseaddr)) < 0) {
450 0 : wpa_printf(MSG_INFO,
451 : "iapp_init - setsockopt[UDP,SO_REUSEADDR]: %s",
452 0 : strerror(errno));
453 : /*
454 : * Ignore this and try to continue. This is fine for single
455 : * BSS cases, but may fail if multiple BSSes enable IAPP.
456 : */
457 : }
458 :
459 2 : if (bind(iapp->udp_sock, (struct sockaddr *) &uaddr,
460 : sizeof(uaddr)) < 0) {
461 0 : wpa_printf(MSG_INFO, "iapp_init - bind[UDP]: %s",
462 0 : strerror(errno));
463 0 : iapp_deinit(iapp);
464 0 : return NULL;
465 : }
466 :
467 2 : os_memset(&mreq, 0, sizeof(mreq));
468 2 : mreq.imr_multiaddr = iapp->multicast;
469 2 : mreq.imr_address.s_addr = INADDR_ANY;
470 2 : mreq.imr_ifindex = 0;
471 2 : if (setsockopt(iapp->udp_sock, SOL_IP, IP_ADD_MEMBERSHIP, &mreq,
472 : sizeof(mreq)) < 0) {
473 0 : wpa_printf(MSG_INFO, "iapp_init - setsockopt[UDP,IP_ADD_MEMBERSHIP]: %s",
474 0 : strerror(errno));
475 0 : iapp_deinit(iapp);
476 0 : return NULL;
477 : }
478 :
479 2 : iapp->packet_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
480 2 : if (iapp->packet_sock < 0) {
481 0 : wpa_printf(MSG_INFO, "iapp_init - socket[PF_PACKET,SOCK_RAW]: %s",
482 0 : strerror(errno));
483 0 : iapp_deinit(iapp);
484 0 : return NULL;
485 : }
486 :
487 2 : os_memset(&addr, 0, sizeof(addr));
488 2 : addr.sll_family = AF_PACKET;
489 2 : addr.sll_ifindex = ifindex;
490 2 : if (bind(iapp->packet_sock, (struct sockaddr *) &addr,
491 : sizeof(addr)) < 0) {
492 0 : wpa_printf(MSG_INFO, "iapp_init - bind[PACKET]: %s",
493 0 : strerror(errno));
494 0 : iapp_deinit(iapp);
495 0 : return NULL;
496 : }
497 :
498 2 : if (eloop_register_read_sock(iapp->udp_sock, iapp_receive_udp,
499 : iapp, NULL)) {
500 0 : wpa_printf(MSG_INFO, "Could not register read socket for IAPP");
501 0 : iapp_deinit(iapp);
502 0 : return NULL;
503 : }
504 :
505 2 : wpa_printf(MSG_INFO, "IEEE 802.11F (IAPP) using interface %s", iface);
506 :
507 : /* TODO: For levels 2 and 3: send RADIUS Initiate-Request, receive
508 : * RADIUS Initiate-Accept or Initiate-Reject. IAPP port should actually
509 : * be openned only after receiving Initiate-Accept. If Initiate-Reject
510 : * is received, IAPP is not started. */
511 :
512 2 : return iapp;
513 : }
514 :
515 :
516 2046 : void iapp_deinit(struct iapp_data *iapp)
517 : {
518 : struct ip_mreqn mreq;
519 :
520 2046 : if (iapp == NULL)
521 4090 : return;
522 :
523 2 : if (iapp->udp_sock >= 0) {
524 2 : os_memset(&mreq, 0, sizeof(mreq));
525 2 : mreq.imr_multiaddr = iapp->multicast;
526 2 : mreq.imr_address.s_addr = INADDR_ANY;
527 2 : mreq.imr_ifindex = 0;
528 2 : if (setsockopt(iapp->udp_sock, SOL_IP, IP_DROP_MEMBERSHIP,
529 : &mreq, sizeof(mreq)) < 0) {
530 0 : wpa_printf(MSG_INFO, "iapp_deinit - setsockopt[UDP,IP_DEL_MEMBERSHIP]: %s",
531 0 : strerror(errno));
532 : }
533 :
534 2 : eloop_unregister_read_sock(iapp->udp_sock);
535 2 : close(iapp->udp_sock);
536 : }
537 2 : if (iapp->packet_sock >= 0) {
538 2 : eloop_unregister_read_sock(iapp->packet_sock);
539 2 : close(iapp->packet_sock);
540 : }
541 2 : os_free(iapp);
542 : }
|