summaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
authorReyk Floeter <reyk@cvs.openbsd.org>2006-05-15 20:53:03 +0000
committerReyk Floeter <reyk@cvs.openbsd.org>2006-05-15 20:53:03 +0000
commit35e8a265b9c183213211836eca2dfdd91922cd67 (patch)
tree5cfdca7d06eb274a04683e590b313068c181b18f /usr.sbin
parent492d6cdbb19de4eff70307b097ed5d80f49604f2 (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/Makefile5
-rw-r--r--usr.sbin/hostapd/apme.c12
-rw-r--r--usr.sbin/hostapd/hostapd.811
-rw-r--r--usr.sbin/hostapd/hostapd.conf.5104
-rw-r--r--usr.sbin/hostapd/hostapd.h24
-rw-r--r--usr.sbin/hostapd/iapp.c15
-rw-r--r--usr.sbin/hostapd/parse.y34
-rw-r--r--usr.sbin/hostapd/privsep.c47
-rw-r--r--usr.sbin/hostapd/roaming.c297
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));
+}
+