diff options
author | Reyk Floeter <reyk@cvs.openbsd.org> | 2006-05-15 20:53:03 +0000 |
---|---|---|
committer | Reyk Floeter <reyk@cvs.openbsd.org> | 2006-05-15 20:53:03 +0000 |
commit | 35e8a265b9c183213211836eca2dfdd91922cd67 (patch) | |
tree | 5cfdca7d06eb274a04683e590b313068c181b18f /usr.sbin | |
parent | 492d6cdbb19de4eff70307b097ed5d80f49604f2 (diff) |
initial implementation of "IP Roaming" in hostapd, see hostapd.conf(5).
ok dlg@
Diffstat (limited to 'usr.sbin')
-rw-r--r-- | usr.sbin/hostapd/Makefile | 5 | ||||
-rw-r--r-- | usr.sbin/hostapd/apme.c | 12 | ||||
-rw-r--r-- | usr.sbin/hostapd/hostapd.8 | 11 | ||||
-rw-r--r-- | usr.sbin/hostapd/hostapd.conf.5 | 104 | ||||
-rw-r--r-- | usr.sbin/hostapd/hostapd.h | 24 | ||||
-rw-r--r-- | usr.sbin/hostapd/iapp.c | 15 | ||||
-rw-r--r-- | usr.sbin/hostapd/parse.y | 34 | ||||
-rw-r--r-- | usr.sbin/hostapd/privsep.c | 47 | ||||
-rw-r--r-- | usr.sbin/hostapd/roaming.c | 297 |
9 files changed, 533 insertions, 16 deletions
diff --git a/usr.sbin/hostapd/Makefile b/usr.sbin/hostapd/Makefile index 5b4774d7242..69e1c7d45c4 100644 --- a/usr.sbin/hostapd/Makefile +++ b/usr.sbin/hostapd/Makefile @@ -1,7 +1,8 @@ -# $OpenBSD: Makefile,v 1.4 2005/07/30 17:18:24 reyk Exp $ +# $OpenBSD: Makefile,v 1.5 2006/05/15 20:53:02 reyk Exp $ PROG= hostapd -SRCS= privsep.c apme.c handle.c iapp.c llc.c hostapd.c print-802_11.c parse.y +SRCS= privsep.c apme.c handle.c iapp.c llc.c hostapd.c \ + print-802_11.c roaming.c parse.y MAN= hostapd.8 hostapd.conf.5 LDADD= ${LIBEVENT} CFLAGS+= -Wall -I${.CURDIR} diff --git a/usr.sbin/hostapd/apme.c b/usr.sbin/hostapd/apme.c index b5ff5e6ead4..6854da2fbf5 100644 --- a/usr.sbin/hostapd/apme.c +++ b/usr.sbin/hostapd/apme.c @@ -1,4 +1,4 @@ -/* $OpenBSD: apme.c,v 1.11 2006/01/31 10:55:02 reyk Exp $ */ +/* $OpenBSD: apme.c,v 1.12 2006/05/15 20:53:02 reyk Exp $ */ /* * Copyright (c) 2004, 2005 Reyk Floeter <reyk@openbsd.org> @@ -126,6 +126,10 @@ hostapd_apme_term(struct hostapd_apme *apme) TAILQ_REMOVE(&cfg->c_apmes, apme, a_entries); + /* Remove all dynamic roaming addresses */ + if (cfg->c_flags & HOSTAPD_CFG_F_PRIV) + hostapd_roaming_term(apme); + hostapd_log(HOSTAPD_LOG_DEBUG, "%s: Host AP interface removed\n", apme->a_iface); @@ -254,6 +258,7 @@ void hostapd_apme_frame(struct hostapd_apme *apme, u_int8_t *buf, u_int len) { struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg; + struct hostapd_iapp *iapp = &cfg->c_iapp; struct hostapd_apme *other_apme; struct hostapd_node node; struct ieee80211_frame *wh; @@ -311,10 +316,15 @@ hostapd_apme_frame(struct hostapd_apme *apme, u_int8_t *buf, u_int len) TAILQ_FOREACH(other_apme, &cfg->c_apmes, a_entries) { if (apme == other_apme) continue; + if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING) + hostapd_roaming_del(other_apme, &node); if (hostapd_apme_delnode(other_apme, &node) == 0) cfg->c_stats.cn_tx_apme++; } + if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING) + hostapd_roaming_add(apme, &node); + hostapd_iapp_add_notify(apme, &node); } diff --git a/usr.sbin/hostapd/hostapd.8 b/usr.sbin/hostapd/hostapd.8 index 214f5b198a2..c0af54503f3 100644 --- a/usr.sbin/hostapd/hostapd.8 +++ b/usr.sbin/hostapd/hostapd.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: hostapd.8,v 1.10 2005/12/18 17:54:12 reyk Exp $ +.\" $OpenBSD: hostapd.8,v 1.11 2006/05/15 20:53:02 reyk Exp $ .\" .\" Copyright (c) 2004, 2005 Reyk Floeter <reyk@openbsd.org> .\" @@ -69,6 +69,15 @@ If the receives ADD.notify messages it will request the Host AP remove a station which has been associated to another access point. .Pp +.Nm +may also handle dynamic roaming of IP addresses and routes in +addition to the standard IAPP ADD.notify behaviour. +See the section called +.Em IP Roaming +in +.Xr hostapd.conf 5 +for details. +.Pp The options are as follows: .Bl -tag -width Ds .It Fl D Ar macro Ns = Ns Ar value diff --git a/usr.sbin/hostapd/hostapd.conf.5 b/usr.sbin/hostapd/hostapd.conf.5 index ba4c557cfc4..0053f0e212d 100644 --- a/usr.sbin/hostapd/hostapd.conf.5 +++ b/usr.sbin/hostapd/hostapd.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: hostapd.conf.5,v 1.30 2006/03/10 18:10:16 reyk Exp $ +.\" $OpenBSD: hostapd.conf.5,v 1.31 2006/05/15 20:53:02 reyk Exp $ .\" .\" Copyright (c) 2004, 2005 Reyk Floeter <reyk@openbsd.org> .\" @@ -39,9 +39,12 @@ addresses easily, with increased performance and flexibility. .It Sy Global Configuration Global runtime settings for .Xr hostapd 8 . -.It Sy Event rules +.It Sy Event Rules Event rules provide a powerful mechanism to trigger certain actions when receiving specified IEEE 802.11 frames. +.It Sy IP Roaming +The concepts and details about the optional IP based roaming in +.Xr hostapd 8 . .El .Pp Comments can be put anywhere in the file using a hash mark @@ -147,6 +150,15 @@ It is important that the IAPP interface is on a trusted network because there is no authentication and an attacker could force disassociation of selected stations on all listening access points. .It Xo +.Ic set iapp +.Op Ic address \*(Ba\ route +.Ic roaming table +.Aq Ar table +.Xc +Specify a table used for +.Em IP Roaming +lookups of link layer address to IP address or subnet assignments. +.It Xo .Ic set iapp handle subtype .Ar subtype \*(Ba\ \& .Pf { Ar subtype0 , subtype1 , ... No } @@ -170,6 +182,13 @@ Receive .Em radiotap messages. This option is enabled by default. +.It Xo +.Op Ic not +.Op Ic address \*(Ba\ route +.Ic roaming +.Xc +Enable dynamic roaming of IP addresses or routes. +These options are disabled by default. .El .Pp .It Ic set iapp mode Ar mode @@ -649,6 +668,82 @@ hostap handle on ath0 type management subtype auth with resend hostap handle type management subtype auth from <blacklist> \e with node delete &from .Ed +.Sh IP ROAMING +In a traditional wireless network, multiple access points are +members of a single layer 3 broadcast domain. +The traffic is bridged between physical collision domains, +as with the +.Xr bridge 4 +interface in +.Ox . +This may cause problems in large wireless networks with a heavy load +of broadcast traffic, like broadcasted ARP, DHCP or ICMP requests. +.Pp +.Xr hostapd 8 +implements IP based roaming to build wireless networks +without the requirement of a single broadcast domain. +This works as follows: +.Pp +.Bl -enum -compact +.It +Every access point running +.Xr hostapd 8 +is a router to an individual internal broadcast domain, +.Em without +using the +.Xr bridge 4 +interface. +.It +An increased multicast TTL is used for IAPP communication +between access points in multiple network segments. +Multicast routing is required in the network infrastructure, +like an +.Ox +router running +.Xr mrouted 8 . +.It +The configuration file +.Nm +is used to assign IP subnets to link layer addresses. +If a station with the specified link layer address successfully +associates to the access point, +.Xr hostapd 8 +will configure the specified IP address and subnet on +the wireless interface. +.It +The +IAPP +.Em ADD.notify +message is used to notify other access points running +.Xr hostapd 8 +to remove the station and any assigned IP addresses or subnets from +the wireless interface. +.It +A dynamic routing daemon like +.Xr ospfd 8 +or +.Xr bgpd 8 +running on the access point will be used to announce the +new IP route to the internal network and routers. +.El +.Pp +For example: +.Bd -literal -offset indent +# Assign IP addresses to layer 2 addresses +table <clients> { + 00:02:6f:42:d0:01 -> 172.23.5.1/30 + 00:05:4e:45:d3:b8 -> 172.23.5.4/30 + 00:04:2e:12:03:e0 -> 172.23.5.8/30 +} + +# Global options +set hostap interface ath0 +set hostap mode radiotap +set iapp interface sis0 +set iapp address roaming table <clients> +set iapp handle subtype address roaming +set iapp mode multicast ttl 2 +.Ed .Sh FILES .Bl -tag -width "/etc/hostapd.conf" -compact .It Pa /etc/hostapd.conf @@ -661,3 +756,8 @@ The .Xr hostapd 8 program was written by .An Reyk Floeter Aq reyk@openbsd.org . +.Sh CAVEATS +.Em IP Roaming +requires statically assigned IP addresses of stations and does +not support DHCP at present. + diff --git a/usr.sbin/hostapd/hostapd.h b/usr.sbin/hostapd/hostapd.h index adf2f4e4c38..1d5c439c3e9 100644 --- a/usr.sbin/hostapd/hostapd.h +++ b/usr.sbin/hostapd/hostapd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: hostapd.h,v 1.16 2005/12/18 17:54:12 reyk Exp $ */ +/* $OpenBSD: hostapd.h,v 1.17 2006/05/15 20:53:02 reyk Exp $ */ /* * Copyright (c) 2004, 2005 Reyk Floeter <reyk@openbsd.org> @@ -268,8 +268,17 @@ struct hostapd_iapp { #define HOSTAPD_IAPP_F_ADD_NOTIFY 0x01 #define HOSTAPD_IAPP_F_RADIOTAP 0x02 +#define HOSTAPD_IAPP_F_ROAMING_ADDRESS 0x04 +#define HOSTAPD_IAPP_F_ROAMING_ROUTE 0x08 #define HOSTAPD_IAPP_F_DEFAULT \ (HOSTAPD_IAPP_F_ADD_NOTIFY | HOSTAPD_IAPP_F_RADIOTAP) +#define HOSTAPD_IAPP_F_ROAMING \ + (HOSTAPD_IAPP_F_ROAMING_ROUTE | HOSTAPD_IAPP_F_ROAMING_ADDRESS) +#define HOSTAPD_IAPP_F_ADD \ + (HOSTAPD_IAPP_F_ADD_NOTIFY | HOSTAPD_IAPP_F_ROAMING) + + struct hostapd_table *i_addr_tbl; + struct hostapd_table *i_route_tbl; }; struct hostapd_config { @@ -278,6 +287,9 @@ struct hostapd_config { struct hostapd_iapp c_iapp; + int c_rtsock; + int c_rtseq; + u_int8_t c_flags; #define HOSTAPD_CFG_F_APME 0x01 @@ -355,6 +367,8 @@ int hostapd_priv_apme_getnode(struct hostapd_apme *, struct hostapd_node *); int hostapd_priv_apme_setnode(struct hostapd_apme *, struct hostapd_node *node, int); +int hostapd_priv_roaming(struct hostapd_apme *, struct hostapd_node *, + int); void hostapd_apme_init(struct hostapd_apme *); int hostapd_apme_deauth(struct hostapd_apme *); @@ -387,6 +401,14 @@ int hostapd_handle_input(struct hostapd_apme *, u_int8_t *, u_int); void hostapd_print_ieee80211(u_int, u_int, u_int8_t *, u_int); +void hostapd_roaming_init(struct hostapd_config *); +void hostapd_roaming_term(struct hostapd_apme *); +int hostapd_roaming(struct hostapd_apme *, struct hostapd_node *, int); +int hostapd_roaming_add(struct hostapd_apme *, + struct hostapd_node *node); +int hostapd_roaming_del(struct hostapd_apme *, + struct hostapd_node *node); + __END_DECLS #endif /* _HOSTAPD_H */ diff --git a/usr.sbin/hostapd/iapp.c b/usr.sbin/hostapd/iapp.c index d31bd3bbba8..07f4758d6a0 100644 --- a/usr.sbin/hostapd/iapp.c +++ b/usr.sbin/hostapd/iapp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: iapp.c,v 1.14 2005/12/18 17:54:12 reyk Exp $ */ +/* $OpenBSD: iapp.c,v 1.15 2006/05/15 20:53:02 reyk Exp $ */ /* * Copyright (c) 2004, 2005 Reyk Floeter <reyk@openbsd.org> @@ -258,17 +258,22 @@ hostapd_iapp_input(int fd, short sig, void *arg) * any allocated resources. Otherwise the received * ADD.notify message will be ignored. */ - if (cfg->c_flags & HOSTAPD_CFG_F_APME) { + if (iapp->i_flags & HOSTAPD_IAPP_F_ADD && + cfg->c_flags & HOSTAPD_CFG_F_APME) { TAILQ_FOREACH(apme, &cfg->c_apmes, a_entries) { - if ((ret = hostapd_apme_delnode(apme, + if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING) + hostapd_roaming_del(apme, &node); + if (iapp->i_flags & HOSTAPD_IAPP_F_ADD_NOTIFY && + (ret = hostapd_apme_delnode(apme, &node)) == 0) cfg->c_stats.cn_tx_apme++; } } else ret = 0; - hostapd_log(HOSTAPD_LOG, "%s: %s ADD notification " - "for %s at %s\n", + hostapd_log(iapp->i_flags & HOSTAPD_IAPP_F_ADD ? + HOSTAPD_LOG : HOSTAPD_LOG_VERBOSE, + "%s: %s ADD notification for %s at %s\n", iapp->i_iface, ret == 0 ? "received" : "ignored", etheraddr_string(node.ni_macaddr), diff --git a/usr.sbin/hostapd/parse.y b/usr.sbin/hostapd/parse.y index 29717bcab1c..e2e8504e010 100644 --- a/usr.sbin/hostapd/parse.y +++ b/usr.sbin/hostapd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.20 2005/12/29 04:33:58 reyk Exp $ */ +/* $OpenBSD: parse.y,v 1.21 2006/05/15 20:53:02 reyk Exp $ */ /* * Copyright (c) 2004, 2005 Reyk Floeter <reyk@openbsd.org> @@ -134,7 +134,7 @@ u_int negative; %token ERROR CONST TABLE NODE DELETE ADD LOG VERBOSE LIMIT QUICK SKIP %token REASON UNSPECIFIED EXPIRE LEAVE ASSOC TOOMANY NOT AUTHED ASSOCED %token RESERVED RSN REQUIRED INCONSISTENT IE INVALID MIC FAILURE OPEN -%token ADDRESS PORT ON NOTIFY TTL INCLUDE +%token ADDRESS PORT ON NOTIFY TTL INCLUDE ROUTE ROAMING %token <v.string> STRING %token <v.val> VALUE %type <v.val> number @@ -196,6 +196,26 @@ option : SET HOSTAP INTERFACE hostapifaces free($4); } | SET IAPP MODE iappmode + | SET IAPP ADDRESS ROAMING TABLE table + { + if ((hostapd_cfg.c_iapp.i_addr_tbl = + hostapd_table_lookup(&hostapd_cfg, $6)) == NULL) { + yyerror("undefined table <%s>", $6); + free($6); + YYERROR; + } + free($6); + } + | SET IAPP ROUTE ROAMING TABLE table + { + if ((hostapd_cfg.c_iapp.i_route_tbl = + hostapd_table_lookup(&hostapd_cfg, $6)) == NULL) { + yyerror("undefined table <%s>", $6); + free($6); + YYERROR; + } + free($6); + } | SET IAPP HANDLE SUBTYPE iappsubtypes ; @@ -314,6 +334,14 @@ iappsubtype : not ADD NOTIFY { HOSTAPD_IAPP_FLAG(RADIOTAP); } + | not ROUTE ROAMING + { + HOSTAPD_IAPP_FLAG(ROAMING_ROUTE); + } + | not ADDRESS ROAMING + { + HOSTAPD_IAPP_FLAG(ROAMING_ADDRESS); + } ; eventopt : /* empty */ @@ -1050,6 +1078,8 @@ lookup(char *token) { "resend", RESEND }, { "reserved", RESERVED }, { "response", RESPONSE }, + { "roaming", ROAMING }, + { "route", ROUTE }, { "rsn", RSN }, { "sec", SEC }, { "set", SET }, diff --git a/usr.sbin/hostapd/privsep.c b/usr.sbin/hostapd/privsep.c index 519b580369e..d1f2f134edd 100644 --- a/usr.sbin/hostapd/privsep.c +++ b/usr.sbin/hostapd/privsep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: privsep.c,v 1.18 2005/12/18 17:54:12 reyk Exp $ */ +/* $OpenBSD: privsep.c,v 1.19 2006/05/15 20:53:02 reyk Exp $ */ /* * Copyright (c) 2004, 2005 Reyk Floeter <reyk@openbsd.org> @@ -57,6 +57,8 @@ enum hostapd_cmd_types { PRIV_APME_GETNODE, /* Get a node from the Host AP */ PRIV_APME_ADDNODE, /* Delete a node from the Host AP */ PRIV_APME_DELNODE, /* Delete a node from the Host AP */ + PRIV_APME_ADDROAMING, /* Add a route to the kernel */ + PRIV_APME_DELROAMING, /* Delete a route from the kernel */ PRIV_LLC_SEND_XID /* Send IEEE 802.3 LLC XID frame */ }; @@ -152,6 +154,8 @@ hostapd_priv_init(struct hostapd_config *cfg) hostapd_fatal("unable to open ioctl socket\n"); } + hostapd_roaming_init(cfg); + setproctitle("[priv]"); /* Start a new event listener */ @@ -193,7 +197,7 @@ hostapd_priv(int fd, short sig, void *arg) struct ieee80211_nodereq nr; struct ifreq ifr; unsigned long request; - int ret, cmd; + int ret = 0, cmd; /* Terminate the event if we got an invalid signal */ if (sig != EV_READ) @@ -287,6 +291,19 @@ hostapd_priv(int fd, short sig, void *arg) hostapd_must_write(fd, &ret, sizeof(int)); break; + case PRIV_APME_ADDROAMING: + case PRIV_APME_DELROAMING: + hostapd_log(HOSTAPD_LOG_DEBUG, + "[priv]: msg PRIV_APME_[ADD|DEL]ROAMING received\n"); + + hostapd_must_read(fd, &node, sizeof(struct hostapd_node)); + + if ((apme = hostapd_priv_getapme(fd, cfg)) == NULL) + break; + ret = hostapd_roaming(apme, &node, cmd == PRIV_APME_ADDROAMING); + hostapd_must_write(fd, &ret, sizeof(int)); + break; + default: hostapd_fatal("[priv]: unknown command %d\n", cmd); } @@ -396,6 +413,32 @@ hostapd_priv_llc_xid(struct hostapd_config *cfg, struct hostapd_node *node) return (ret); } +int +hostapd_priv_roaming(struct hostapd_apme *apme, struct hostapd_node *node, + int add) +{ + struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg; + int ret, cmd; + + if (priv_fd < 0) + hostapd_fatal("%s: called from privileged portion\n", __func__); + + if ((cfg->c_flags & HOSTAPD_CFG_F_APME) == 0) + hostapd_fatal("%s: Host AP is not available\n", __func__); + + if (add) + cmd = PRIV_APME_ADDROAMING; + else + cmd = PRIV_APME_DELROAMING; + hostapd_must_write(priv_fd, &cmd, sizeof(int)); + hostapd_must_write(priv_fd, node, sizeof(struct hostapd_node)); + hostapd_must_write(priv_fd, &apme->a_iface, IFNAMSIZ); + + hostapd_must_read(priv_fd, &ret, sizeof(int)); + + return (ret); +} + /* * If priv parent gets a TERM or HUP, pass it through to child instead. */ diff --git a/usr.sbin/hostapd/roaming.c b/usr.sbin/hostapd/roaming.c new file mode 100644 index 00000000000..40274251681 --- /dev/null +++ b/usr.sbin/hostapd/roaming.c @@ -0,0 +1,297 @@ +/* $OpenBSD: roaming.c,v 1.1 2006/05/15 20:53:02 reyk Exp $ */ + +/* + * Copyright (c) 2005, 2006 Reyk Floeter <reyk@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <sys/tree.h> +#include <sys/ioctl.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_arp.h> +#include <net/if_llc.h> +#include <net/route.h> + +#include <netinet/in.h> +#include <netinet/if_ether.h> +#include <arpa/inet.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> + +#include "hostapd.h" + +int hostapd_roaming_addr(struct hostapd_apme *, struct hostapd_inaddr *, int); +int hostapd_roaming_rt(struct hostapd_apme *, struct hostapd_inaddr *, int); + +void +hostapd_roaming_init(struct hostapd_config *cfg) +{ + struct hostapd_iapp *iapp = &cfg->c_iapp; + struct hostapd_apme *apme; + struct ifreq ifr; + int v; + + if ((cfg->c_flags & HOSTAPD_CFG_F_APME) == 0 || + (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING_ROUTE) == 0) + return; + + if ((cfg->c_rtsock = socket(AF_ROUTE, SOCK_RAW, AF_INET)) == -1) + hostapd_fatal("failed to init inet socket: %s\n", + strerror(errno)); + + v = 0; + if (setsockopt(cfg->c_rtsock, SOL_SOCKET, SO_USELOOPBACK, + &v, sizeof(v)) == -1) + hostapd_fatal("failed to setup inet socket: %s\n", + strerror(errno)); + + TAILQ_FOREACH(apme, &cfg->c_apmes, a_entries) { + bzero(&ifr, sizeof(ifr)); + strlcpy(ifr.ifr_name, apme->a_iface, sizeof(ifr.ifr_name)); + if (ioctl(cfg->c_apme_ctl, SIOCGIFADDR, &ifr) == -1) + hostapd_fatal("ioctl %s on \"%s\" failed: %s\n", + "SIOCGIFADDR", ifr.ifr_name, strerror(errno)); + bcopy(&ifr.ifr_addr, &apme->a_addr, + sizeof(struct sockaddr_in)); + hostapd_log(HOSTAPD_LOG_VERBOSE, + "%s/%s: using gateway address %s\n", + apme->a_iface, iapp->i_iface, + inet_ntoa(apme->a_addr.sin_addr), apme->a_iface); + } +} + +void +hostapd_roaming_term(struct hostapd_apme *apme) +{ + struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg; + struct hostapd_iapp *iapp = &cfg->c_iapp; + struct hostapd_entry *entry; + + if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING_ROUTE && + iapp->i_route_tbl != NULL) { + RB_FOREACH(entry, hostapd_tree, &iapp->i_route_tbl->t_tree) { + if ((entry->e_flags & HOSTAPD_ENTRY_F_INADDR) == 0) + continue; + hostapd_roaming_rt(apme, &entry->e_inaddr, 0); + } + } + + if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING_ADDRESS && + iapp->i_addr_tbl != NULL) { + RB_FOREACH(entry, hostapd_tree, &iapp->i_addr_tbl->t_tree) { + if ((entry->e_flags & HOSTAPD_ENTRY_F_INADDR) == 0) + continue; + hostapd_roaming_addr(apme, &entry->e_inaddr, 0); + } + } +} + +int +hostapd_roaming(struct hostapd_apme *apme, struct hostapd_node *node, int add) +{ + struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg; + struct hostapd_iapp *iapp = &cfg->c_iapp; + struct hostapd_entry *entry; + int ret; + + if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING_ADDRESS && + iapp->i_addr_tbl != NULL) { + if ((entry = hostapd_entry_lookup(iapp->i_addr_tbl, + node->ni_macaddr)) == NULL || + (entry->e_flags & HOSTAPD_ENTRY_F_INADDR) == 0) + return (ESRCH); + if ((ret = hostapd_roaming_addr(apme, &entry->e_inaddr, + add)) != 0) + return (ret); + } + + if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING_ROUTE && + iapp->i_route_tbl != NULL) { + if ((entry = hostapd_entry_lookup(iapp->i_addr_tbl, + node->ni_macaddr)) == NULL || + (entry->e_flags & HOSTAPD_ENTRY_F_INADDR) == 0) + return (ESRCH); + if ((ret = hostapd_roaming_rt(apme, &entry->e_inaddr, + add)) != 0) + return (ret); + } + + return (0); +} + + +int +hostapd_roaming_addr(struct hostapd_apme *apme, struct hostapd_inaddr *addr, + int add) +{ + struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg; + struct hostapd_iapp *iapp = &cfg->c_iapp; + struct sockaddr_in *ifaddr, *ifmask, *ifbroadaddr; + struct ifaliasreq ifra; + + bzero(&ifra, sizeof(ifra)); + + ifaddr = (struct sockaddr_in *)&ifra.ifra_addr; + ifaddr->sin_family = AF_INET; + ifaddr->sin_len = sizeof(struct sockaddr_in); + ifaddr->sin_addr.s_addr = addr->in_v4.s_addr; + + ifbroadaddr = (struct sockaddr_in *)&ifra.ifra_broadaddr; + ifbroadaddr->sin_family = AF_INET; + ifbroadaddr->sin_len = sizeof(struct sockaddr_in); + ifbroadaddr->sin_addr.s_addr = + addr->in_v4.s_addr | htonl(0xffffffffUL >> addr->in_netmask); + + if (add) { + ifmask = (struct sockaddr_in *)&ifra.ifra_mask; + ifmask->sin_family = AF_INET; + ifmask->sin_len = sizeof(struct sockaddr_in); + ifmask->sin_addr.s_addr = + htonl(0xffffffff << (32 - addr->in_netmask)); + } + + strlcpy(ifra.ifra_name, apme->a_iface, sizeof(ifra.ifra_name)); + if (ioctl(cfg->c_apme_ctl, SIOCDIFADDR, &ifra) < 0) { + if (errno != EADDRNOTAVAIL) { + hostapd_log(HOSTAPD_LOG_VERBOSE, + "%s/%s: failed to delete address %s\n", + apme->a_iface, iapp->i_iface, + inet_ntoa(addr->in_v4)); + return (errno); + } + } + if (add && ioctl(cfg->c_apme_ctl, SIOCAIFADDR, &ifra) < 0) { + if (errno != EEXIST) { + hostapd_log(HOSTAPD_LOG_VERBOSE, + "%s/%s: failed to add address %s\n", + apme->a_iface, iapp->i_iface, + inet_ntoa(addr->in_v4)); + return (errno); + } + } + + hostapd_log(HOSTAPD_LOG_VERBOSE, + "%s/%s: %s address %s\n", + apme->a_iface, iapp->i_iface, + add ? "added" : "deleted", + inet_ntoa(addr->in_v4)); + + return (0); +} + +int +hostapd_roaming_rt(struct hostapd_apme *apme, struct hostapd_inaddr *addr, + int add) +{ + struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg; + struct hostapd_iapp *iapp = &cfg->c_iapp; + struct { + struct rt_msghdr rm_hdr; + struct sockaddr_in rm_dst; + struct sockaddr_in rm_gateway; + struct sockaddr_in rm_netmask; + struct sockaddr_rtlabel rm_label; + } rm; + size_t len = sizeof(rm); + + bzero(&rm, len); + + rm.rm_hdr.rtm_msglen = len; + rm.rm_hdr.rtm_version = RTM_VERSION; + rm.rm_hdr.rtm_type = add ? RTM_CHANGE : RTM_DELETE; + rm.rm_hdr.rtm_flags = RTF_STATIC; + rm.rm_hdr.rtm_seq = cfg->c_rtseq++; + rm.rm_hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_LABEL; + + rm.rm_dst.sin_family = AF_INET; + rm.rm_dst.sin_len = sizeof(rm.rm_dst); + rm.rm_dst.sin_addr.s_addr = addr->in_v4.s_addr; + + rm.rm_gateway.sin_family = AF_INET; + rm.rm_gateway.sin_len = sizeof(rm.rm_gateway); + rm.rm_gateway.sin_addr.s_addr = apme->a_addr.sin_addr.s_addr; + + rm.rm_hdr.rtm_addrs |= RTA_NETMASK; + rm.rm_netmask.sin_len = sizeof(rm.rm_netmask); + rm.rm_netmask.sin_family = AF_INET; + if (addr->in_netmask) + rm.rm_netmask.sin_addr.s_addr = + htonl(0xffffffff << (32 - addr->in_netmask)); + else if (addr->in_netmask < 0) + rm.rm_hdr.rtm_flags |= RTF_HOST; + + rm.rm_label.sr_len = sizeof(rm.rm_label); + snprintf(rm.rm_label.sr_label, sizeof(rm.rm_label.sr_label), + "apme-%s", apme->a_iface); + + retry: + if (write(cfg->c_rtsock, &rm, len) == -1) { + switch (errno) { + case ESRCH: + if (rm.rm_hdr.rtm_type == RTM_CHANGE) { + rm.rm_hdr.rtm_type = RTM_ADD; + goto retry; + } else if (rm.rm_hdr.rtm_type == RTM_DELETE) { + /* Ignore */ + break; + } + /* FALLTHROUGH */ + default: + goto bad; + } + } + + hostapd_log(HOSTAPD_LOG_VERBOSE, + "%s/%s: %s route to %s\n", + apme->a_iface, iapp->i_iface, + add ? "added" : "deleted", + inet_ntoa(addr->in_v4)); + + return (0); + + bad: + hostapd_log(HOSTAPD_LOG_VERBOSE, + "%s/%s: failed to %s route to %s: %s\n", + apme->a_iface, iapp->i_iface, + add ? "add" : "delete", + inet_ntoa(addr->in_v4), + strerror(errno)); + + return (ESRCH); +} + +int +hostapd_roaming_add(struct hostapd_apme *apme, struct hostapd_node *node) +{ + return (hostapd_priv_roaming(apme, node, 1)); +} + +int +hostapd_roaming_del(struct hostapd_apme *apme, struct hostapd_node *node) +{ + return (hostapd_priv_roaming(apme, node, 0)); +} + |