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 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 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 	hdr = (struct iapp_hdr *) buf;
    185 	hdr->version = IAPP_VERSION;
    186 	hdr->command = IAPP_CMD_ADD_notify;
    187 	hdr->identifier = host_to_be16(iapp->identifier++);
    188 	hdr->length = host_to_be16(sizeof(*hdr) + sizeof(*add));
    189 
    190 	add = (struct iapp_add_notify *) (hdr + 1);
    191 	add->addr_len = ETH_ALEN;
    192 	add->reserved = 0;
    193 	os_memcpy(add->mac_addr, mac_addr, ETH_ALEN);
    194 
    195 	add->seq_num = host_to_be16(seq_num);
    196 
    197 	os_memset(&addr, 0, sizeof(addr));
    198 	addr.sin_family = AF_INET;
    199 	addr.sin_addr.s_addr = iapp->multicast.s_addr;
    200 	addr.sin_port = htons(IAPP_UDP_PORT);
    201 	if (sendto(iapp->udp_sock, buf, (char *) (add + 1) - buf, 0,
    202 		   (struct sockaddr *) &addr, sizeof(addr)) < 0)
    203 		wpa_printf(MSG_INFO, "sendto[IAPP-ADD]: %s", strerror(errno));
    204 }
    205 
    206 
    207 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 	os_memset(msg.da, 0xff, ETH_ALEN);
    218 	os_memcpy(msg.sa, addr, ETH_ALEN);
    219 	msg.len = host_to_be16(6);
    220 	msg.dsap = 0; /* NULL DSAP address */
    221 	msg.ssap = 0x01; /* NULL SSAP address, CR Bit: Response */
    222 	msg.control = 0xaf; /* XID response lsb.1111F101.
    223 			     * F=0 (no poll command; unsolicited frame) */
    224 	msg.xid_info[0] = 0x81; /* XID format identifier */
    225 	msg.xid_info[1] = 1; /* LLC types/classes: Type 1 LLC */
    226 	msg.xid_info[2] = 1 << 1; /* XID sender's receive window size (RW)
    227 				   * FIX: what is correct RW with 802.11? */
    228 
    229 	if (send(iapp->packet_sock, &msg, sizeof(msg), 0) < 0)
    230 		wpa_printf(MSG_INFO, "send[L2 Update]: %s", strerror(errno));
    231 }
    232 
    233 
    234 /**
    235  * iapp_new_station - IAPP processing for a new STA
    236  * @iapp: IAPP data
    237  * @sta: The associated station
    238  */
    239 void iapp_new_station(struct iapp_data *iapp, struct sta_info *sta)
    240 {
    241 	u16 seq = 0; /* TODO */
    242 
    243 	if (iapp == NULL)
    244 		return;
    245 
    246 	/* IAPP-ADD.request(MAC Address, Sequence Number, Timeout) */
    247 	hostapd_logger(iapp->hapd, sta->addr, HOSTAPD_MODULE_IAPP,
    248 		       HOSTAPD_LEVEL_DEBUG, "IAPP-ADD.request(seq=%d)", seq);
    249 	iapp_send_layer2_update(iapp, sta->addr);
    250 	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 static void iapp_process_add_notify(struct iapp_data *iapp,
    261 				    struct sockaddr_in *from,
    262 				    struct iapp_hdr *hdr, int len)
    263 {
    264 	struct iapp_add_notify *add = (struct iapp_add_notify *) (hdr + 1);
    265 	struct sta_info *sta;
    266 
    267 	if (len != sizeof(*add)) {
    268 		wpa_printf(MSG_INFO, "Invalid IAPP-ADD packet length %d (expected %lu)",
    269 			   len, (unsigned long) sizeof(*add));
    270 		return;
    271 	}
    272 
    273 	sta = ap_get_sta(iapp->hapd, add->mac_addr);
    274 
    275 	/* IAPP-ADD.indication(MAC Address, Sequence Number) */
    276 	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 		       be_to_host16(add->seq_num),
    280 		       inet_ntoa(from->sin_addr), ntohs(from->sin_port),
    281 		       sta ? "" : " (STA not found)");
    282 
    283 	if (!sta)
    284 		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 	hostapd_logger(iapp->hapd, add->mac_addr, HOSTAPD_MODULE_IAPP,
    291 		       HOSTAPD_LEVEL_DEBUG,
    292 		       "Removing STA due to IAPP ADD-notify");
    293 	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 static void iapp_receive_udp(int sock, void *eloop_ctx, void *sock_ctx)
    304 {
    305 	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 	fromlen = sizeof(from);
    315 	len = recvfrom(iapp->udp_sock, buf, sizeof(buf), 0,
    316 		       (struct sockaddr *) &from, &fromlen);
    317 	if (len < 0) {
    318 		wpa_printf(MSG_INFO, "iapp_receive_udp - recvfrom: %s",
    319 			   strerror(errno));
    320 		return;
    321 	}
    322 
    323 	if (from.sin_addr.s_addr == iapp->own.s_addr)
    324 		return; /* ignore own IAPP messages */
    325 
    326 	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 	if (len < (int) sizeof(*hdr))
    333 		return;
    334 
    335 	hdr = (struct iapp_hdr *) buf;
    336 	hlen = be_to_host16(hdr->length);
    337 	hostapd_logger(iapp->hapd, NULL, HOSTAPD_MODULE_IAPP,
    338 		       HOSTAPD_LEVEL_DEBUG,
    339 		       "RX: version=%d command=%d id=%d len=%d\n",
    340 		       hdr->version, hdr->command,
    341 		       be_to_host16(hdr->identifier), hlen);
    342 	if (hdr->version != IAPP_VERSION) {
    343 		wpa_printf(MSG_INFO, "Dropping IAPP frame with unknown version %d",
    344 			   hdr->version);
    345 		return;
    346 	}
    347 	if (hlen > len) {
    348 		wpa_printf(MSG_INFO, "Underflow IAPP frame (hlen=%d len=%d)",
    349 			   hlen, len);
    350 		return;
    351 	}
    352 	if (hlen < len) {
    353 		wpa_printf(MSG_INFO, "Ignoring %d extra bytes from IAPP frame",
    354 			   len - hlen);
    355 		len = hlen;
    356 	}
    357 
    358 	switch (hdr->command) {
    359 	case IAPP_CMD_ADD_notify:
    360 		iapp_process_add_notify(iapp, &from, hdr, len - sizeof(*hdr));
    361 		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 		break;
    369 	default:
    370 		wpa_printf(MSG_INFO, "Unknown IAPP command %d", hdr->command);
    371 		break;
    372 	}
    373 }
    374 
    375 
    376 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 
    385 	iapp = os_zalloc(sizeof(*iapp));
    386 	if (iapp == NULL)
    387 		return NULL;
    388 	iapp->hapd = hapd;
    389 	iapp->udp_sock = iapp->packet_sock = -1;
    390 
    391 	/* TODO:
    392 	 * open socket for sending and receiving IAPP frames over TCP
    393 	 */
    394 
    395 	iapp->udp_sock = socket(PF_INET, SOCK_DGRAM, 0);
    396 	if (iapp->udp_sock < 0) {
    397 		wpa_printf(MSG_INFO, "iapp_init - socket[PF_INET,SOCK_DGRAM]: %s",
    398 			   strerror(errno));
    399 		iapp_deinit(iapp);
    400 		return NULL;
    401 	}
    402 
    403 	os_memset(&ifr, 0, sizeof(ifr));
    404 	os_strlcpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
    405 	if (ioctl(iapp->udp_sock, SIOCGIFINDEX, &ifr) != 0) {
    406 		wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFINDEX): %s",
    407 			   strerror(errno));
    408 		iapp_deinit(iapp);
    409 		return NULL;
    410 	}
    411 	ifindex = ifr.ifr_ifindex;
    412 
    413 	if (ioctl(iapp->udp_sock, SIOCGIFADDR, &ifr) != 0) {
    414 		wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFADDR): %s",
    415 			   strerror(errno));
    416 		iapp_deinit(iapp);
    417 		return NULL;
    418 	}
    419 	paddr = (struct sockaddr_in *) &ifr.ifr_addr;
    420 	if (paddr->sin_family != AF_INET) {
    421 		wpa_printf(MSG_INFO, "IAPP: Invalid address family %i (SIOCGIFADDR)",
    422 			   paddr->sin_family);
    423 		iapp_deinit(iapp);
    424 		return NULL;
    425 	}
    426 	iapp->own.s_addr = paddr->sin_addr.s_addr;
    427 
    428 	if (ioctl(iapp->udp_sock, SIOCGIFBRDADDR, &ifr) != 0) {
    429 		wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFBRDADDR): %s",
    430 			   strerror(errno));
    431 		iapp_deinit(iapp);
    432 		return NULL;
    433 	}
    434 	paddr = (struct sockaddr_in *) &ifr.ifr_addr;
    435 	if (paddr->sin_family != AF_INET) {
    436 		wpa_printf(MSG_INFO, "Invalid address family %i (SIOCGIFBRDADDR)",
    437 			   paddr->sin_family);
    438 		iapp_deinit(iapp);
    439 		return NULL;
    440 	}
    441 	inet_aton(IAPP_MULTICAST, &iapp->multicast);
    442 
    443 	os_memset(&uaddr, 0, sizeof(uaddr));
    444 	uaddr.sin_family = AF_INET;
    445 	uaddr.sin_port = htons(IAPP_UDP_PORT);
    446 	if (bind(iapp->udp_sock, (struct sockaddr *) &uaddr,
    447 		 sizeof(uaddr)) < 0) {
    448 		wpa_printf(MSG_INFO, "iapp_init - bind[UDP]: %s",
    449 			   strerror(errno));
    450 		iapp_deinit(iapp);
    451 		return NULL;
    452 	}
    453 
    454 	os_memset(&mreq, 0, sizeof(mreq));
    455 	mreq.imr_multiaddr = iapp->multicast;
    456 	mreq.imr_address.s_addr = INADDR_ANY;
    457 	mreq.imr_ifindex = 0;
    458 	if (setsockopt(iapp->udp_sock, SOL_IP, IP_ADD_MEMBERSHIP, &mreq,
    459 		       sizeof(mreq)) < 0) {
    460 		wpa_printf(MSG_INFO, "iapp_init - setsockopt[UDP,IP_ADD_MEMBERSHIP]: %s",
    461 			   strerror(errno));
    462 		iapp_deinit(iapp);
    463 		return NULL;
    464 	}
    465 
    466 	iapp->packet_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    467 	if (iapp->packet_sock < 0) {
    468 		wpa_printf(MSG_INFO, "iapp_init - socket[PF_PACKET,SOCK_RAW]: %s",
    469 			   strerror(errno));
    470 		iapp_deinit(iapp);
    471 		return NULL;
    472 	}
    473 
    474 	os_memset(&addr, 0, sizeof(addr));
    475 	addr.sll_family = AF_PACKET;
    476 	addr.sll_ifindex = ifindex;
    477 	if (bind(iapp->packet_sock, (struct sockaddr *) &addr,
    478 		 sizeof(addr)) < 0) {
    479 		wpa_printf(MSG_INFO, "iapp_init - bind[PACKET]: %s",
    480 			   strerror(errno));
    481 		iapp_deinit(iapp);
    482 		return NULL;
    483 	}
    484 
    485 	if (eloop_register_read_sock(iapp->udp_sock, iapp_receive_udp,
    486 				     iapp, NULL)) {
    487 		wpa_printf(MSG_INFO, "Could not register read socket for IAPP");
    488 		iapp_deinit(iapp);
    489 		return NULL;
    490 	}
    491 
    492 	wpa_printf(MSG_INFO, "IEEE 802.11F (IAPP) using interface %s", iface);
    493 
    494 	/* TODO: For levels 2 and 3: send RADIUS Initiate-Request, receive
    495 	 * RADIUS Initiate-Accept or Initiate-Reject. IAPP port should actually
    496 	 * be openned only after receiving Initiate-Accept. If Initiate-Reject
    497 	 * is received, IAPP is not started. */
    498 
    499 	return iapp;
    500 }
    501 
    502 
    503 void iapp_deinit(struct iapp_data *iapp)
    504 {
    505 	struct ip_mreqn mreq;
    506 
    507 	if (iapp == NULL)
    508 		return;
    509 
    510 	if (iapp->udp_sock >= 0) {
    511 		os_memset(&mreq, 0, sizeof(mreq));
    512 		mreq.imr_multiaddr = iapp->multicast;
    513 		mreq.imr_address.s_addr = INADDR_ANY;
    514 		mreq.imr_ifindex = 0;
    515 		if (setsockopt(iapp->udp_sock, SOL_IP, IP_DROP_MEMBERSHIP,
    516 			       &mreq, sizeof(mreq)) < 0) {
    517 			wpa_printf(MSG_INFO, "iapp_deinit - setsockopt[UDP,IP_DEL_MEMBERSHIP]: %s",
    518 				   strerror(errno));
    519 		}
    520 
    521 		eloop_unregister_read_sock(iapp->udp_sock);
    522 		close(iapp->udp_sock);
    523 	}
    524 	if (iapp->packet_sock >= 0) {
    525 		eloop_unregister_read_sock(iapp->packet_sock);
    526 		close(iapp->packet_sock);
    527 	}
    528 	os_free(iapp);
    529 }
    530