File: [local] / src / usr.sbin / hostapd / roaming.c (download)
Revision 1.3, Thu Feb 8 11:15:55 2007 UTC (17 years, 3 months ago) by reyk
Branch: MAIN
CVS Tags: OPENBSD_4_5_BASE, OPENBSD_4_5, OPENBSD_4_4_BASE, OPENBSD_4_4, OPENBSD_4_3_BASE, OPENBSD_4_3, OPENBSD_4_2_BASE, OPENBSD_4_2, OPENBSD_4_1_BASE, OPENBSD_4_1 Changes since 1.2: +8 -7 lines
carefully check some return values and make lint happy. check for
truncation before feeding strings into the kernel.
|
/* $OpenBSD: roaming.c,v 1.3 2007/02/08 11:15:55 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));
(void)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",
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;
(void)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;
(void)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));
}
(void)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",
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",
apme->a_iface, iapp->i_iface,
inet_ntoa(addr->in_v4));
return (errno);
}
}
hostapd_log(HOSTAPD_LOG_VERBOSE,
"%s/%s: %s address %s",
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);
if (snprintf(rm.rm_label.sr_label, sizeof(rm.rm_label.sr_label),
"apme-%s", apme->a_iface) == -1)
goto bad;
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",
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",
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));
}