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