1*df69c215Sderaadt /* $OpenBSD: roaming.c,v 1.8 2019/06/28 13:32:47 deraadt Exp $ */
274ff1540Sreyk
374ff1540Sreyk /*
474ff1540Sreyk * Copyright (c) 2005, 2006 Reyk Floeter <reyk@openbsd.org>
574ff1540Sreyk *
674ff1540Sreyk * Permission to use, copy, modify, and distribute this software for any
774ff1540Sreyk * purpose with or without fee is hereby granted, provided that the above
874ff1540Sreyk * copyright notice and this permission notice appear in all copies.
974ff1540Sreyk *
1074ff1540Sreyk * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1174ff1540Sreyk * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1274ff1540Sreyk * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1374ff1540Sreyk * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1474ff1540Sreyk * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1574ff1540Sreyk * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1674ff1540Sreyk * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1774ff1540Sreyk */
1874ff1540Sreyk
1974ff1540Sreyk #include <sys/types.h>
2074ff1540Sreyk #include <sys/socket.h>
2174ff1540Sreyk #include <sys/tree.h>
2274ff1540Sreyk #include <sys/ioctl.h>
2374ff1540Sreyk
2474ff1540Sreyk #include <net/if.h>
2574ff1540Sreyk #include <net/if_media.h>
2674ff1540Sreyk #include <net/if_arp.h>
2774ff1540Sreyk #include <net/if_llc.h>
2874ff1540Sreyk #include <net/route.h>
2974ff1540Sreyk
3074ff1540Sreyk #include <netinet/in.h>
3174ff1540Sreyk #include <netinet/if_ether.h>
3274ff1540Sreyk #include <arpa/inet.h>
3374ff1540Sreyk
3474ff1540Sreyk #include <stdlib.h>
3574ff1540Sreyk #include <stdio.h>
3674ff1540Sreyk #include <string.h>
3774ff1540Sreyk #include <unistd.h>
3874ff1540Sreyk #include <fcntl.h>
3974ff1540Sreyk #include <errno.h>
40b9fc9a72Sderaadt #include <limits.h>
4174ff1540Sreyk
4274ff1540Sreyk #include "hostapd.h"
4374ff1540Sreyk
4474ff1540Sreyk int hostapd_roaming_addr(struct hostapd_apme *, struct hostapd_inaddr *, int);
4574ff1540Sreyk int hostapd_roaming_rt(struct hostapd_apme *, struct hostapd_inaddr *, int);
4674ff1540Sreyk
4774ff1540Sreyk void
hostapd_roaming_init(struct hostapd_config * cfg)4874ff1540Sreyk hostapd_roaming_init(struct hostapd_config *cfg)
4974ff1540Sreyk {
5074ff1540Sreyk struct hostapd_iapp *iapp = &cfg->c_iapp;
5174ff1540Sreyk struct hostapd_apme *apme;
5274ff1540Sreyk struct ifreq ifr;
5374ff1540Sreyk int v;
5474ff1540Sreyk
5574ff1540Sreyk if ((cfg->c_flags & HOSTAPD_CFG_F_APME) == 0 ||
5674ff1540Sreyk (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING_ROUTE) == 0)
5774ff1540Sreyk return;
5874ff1540Sreyk
5974ff1540Sreyk if ((cfg->c_rtsock = socket(AF_ROUTE, SOCK_RAW, AF_INET)) == -1)
6074ff1540Sreyk hostapd_fatal("failed to init inet socket: %s\n",
6174ff1540Sreyk strerror(errno));
6274ff1540Sreyk
6374ff1540Sreyk v = 0;
6474ff1540Sreyk if (setsockopt(cfg->c_rtsock, SOL_SOCKET, SO_USELOOPBACK,
6574ff1540Sreyk &v, sizeof(v)) == -1)
6674ff1540Sreyk hostapd_fatal("failed to setup inet socket: %s\n",
6774ff1540Sreyk strerror(errno));
6874ff1540Sreyk
6974ff1540Sreyk TAILQ_FOREACH(apme, &cfg->c_apmes, a_entries) {
7074ff1540Sreyk bzero(&ifr, sizeof(ifr));
71ae7a93f5Sreyk (void)strlcpy(ifr.ifr_name, apme->a_iface, sizeof(ifr.ifr_name));
7274ff1540Sreyk if (ioctl(cfg->c_apme_ctl, SIOCGIFADDR, &ifr) == -1)
7374ff1540Sreyk hostapd_fatal("ioctl %s on \"%s\" failed: %s\n",
7474ff1540Sreyk "SIOCGIFADDR", ifr.ifr_name, strerror(errno));
7574ff1540Sreyk bcopy(&ifr.ifr_addr, &apme->a_addr,
7674ff1540Sreyk sizeof(struct sockaddr_in));
7774ff1540Sreyk hostapd_log(HOSTAPD_LOG_VERBOSE,
78f83005a6Sreyk "%s/%s: using gateway address %s",
7974ff1540Sreyk apme->a_iface, iapp->i_iface,
8074ff1540Sreyk inet_ntoa(apme->a_addr.sin_addr), apme->a_iface);
8174ff1540Sreyk }
8274ff1540Sreyk }
8374ff1540Sreyk
8474ff1540Sreyk void
hostapd_roaming_term(struct hostapd_apme * apme)8574ff1540Sreyk hostapd_roaming_term(struct hostapd_apme *apme)
8674ff1540Sreyk {
8774ff1540Sreyk struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
8874ff1540Sreyk struct hostapd_iapp *iapp = &cfg->c_iapp;
8974ff1540Sreyk struct hostapd_entry *entry;
9074ff1540Sreyk
9174ff1540Sreyk if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING_ROUTE &&
9274ff1540Sreyk iapp->i_route_tbl != NULL) {
9374ff1540Sreyk RB_FOREACH(entry, hostapd_tree, &iapp->i_route_tbl->t_tree) {
9474ff1540Sreyk if ((entry->e_flags & HOSTAPD_ENTRY_F_INADDR) == 0)
9574ff1540Sreyk continue;
96ae7a93f5Sreyk (void)hostapd_roaming_rt(apme, &entry->e_inaddr, 0);
9774ff1540Sreyk }
9874ff1540Sreyk }
9974ff1540Sreyk
10074ff1540Sreyk if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING_ADDRESS &&
10174ff1540Sreyk iapp->i_addr_tbl != NULL) {
10274ff1540Sreyk RB_FOREACH(entry, hostapd_tree, &iapp->i_addr_tbl->t_tree) {
10374ff1540Sreyk if ((entry->e_flags & HOSTAPD_ENTRY_F_INADDR) == 0)
10474ff1540Sreyk continue;
105ae7a93f5Sreyk (void)hostapd_roaming_addr(apme, &entry->e_inaddr, 0);
10674ff1540Sreyk }
10774ff1540Sreyk }
10874ff1540Sreyk }
10974ff1540Sreyk
11074ff1540Sreyk int
hostapd_roaming(struct hostapd_apme * apme,struct hostapd_node * node,int add)11174ff1540Sreyk hostapd_roaming(struct hostapd_apme *apme, struct hostapd_node *node, int add)
11274ff1540Sreyk {
11374ff1540Sreyk struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
11474ff1540Sreyk struct hostapd_iapp *iapp = &cfg->c_iapp;
11574ff1540Sreyk struct hostapd_entry *entry;
11674ff1540Sreyk int ret;
11774ff1540Sreyk
11874ff1540Sreyk if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING_ADDRESS &&
11974ff1540Sreyk iapp->i_addr_tbl != NULL) {
12074ff1540Sreyk if ((entry = hostapd_entry_lookup(iapp->i_addr_tbl,
12174ff1540Sreyk node->ni_macaddr)) == NULL ||
12274ff1540Sreyk (entry->e_flags & HOSTAPD_ENTRY_F_INADDR) == 0)
12374ff1540Sreyk return (ESRCH);
12474ff1540Sreyk if ((ret = hostapd_roaming_addr(apme, &entry->e_inaddr,
12574ff1540Sreyk add)) != 0)
12674ff1540Sreyk return (ret);
12774ff1540Sreyk }
12874ff1540Sreyk
12974ff1540Sreyk if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING_ROUTE &&
13074ff1540Sreyk iapp->i_route_tbl != NULL) {
13174ff1540Sreyk if ((entry = hostapd_entry_lookup(iapp->i_addr_tbl,
13274ff1540Sreyk node->ni_macaddr)) == NULL ||
13374ff1540Sreyk (entry->e_flags & HOSTAPD_ENTRY_F_INADDR) == 0)
13474ff1540Sreyk return (ESRCH);
13574ff1540Sreyk if ((ret = hostapd_roaming_rt(apme, &entry->e_inaddr,
13674ff1540Sreyk add)) != 0)
13774ff1540Sreyk return (ret);
13874ff1540Sreyk }
13974ff1540Sreyk
14074ff1540Sreyk return (0);
14174ff1540Sreyk }
14274ff1540Sreyk
14374ff1540Sreyk
14474ff1540Sreyk int
hostapd_roaming_addr(struct hostapd_apme * apme,struct hostapd_inaddr * addr,int add)14574ff1540Sreyk hostapd_roaming_addr(struct hostapd_apme *apme, struct hostapd_inaddr *addr,
14674ff1540Sreyk int add)
14774ff1540Sreyk {
14874ff1540Sreyk struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
14974ff1540Sreyk struct hostapd_iapp *iapp = &cfg->c_iapp;
15074ff1540Sreyk struct sockaddr_in *ifaddr, *ifmask, *ifbroadaddr;
15174ff1540Sreyk struct ifaliasreq ifra;
15274ff1540Sreyk
15374ff1540Sreyk bzero(&ifra, sizeof(ifra));
15474ff1540Sreyk
15574ff1540Sreyk ifaddr = (struct sockaddr_in *)&ifra.ifra_addr;
15674ff1540Sreyk ifaddr->sin_family = AF_INET;
15774ff1540Sreyk ifaddr->sin_len = sizeof(struct sockaddr_in);
15874ff1540Sreyk ifaddr->sin_addr.s_addr = addr->in_v4.s_addr;
15974ff1540Sreyk
16074ff1540Sreyk ifbroadaddr = (struct sockaddr_in *)&ifra.ifra_broadaddr;
16174ff1540Sreyk ifbroadaddr->sin_family = AF_INET;
16274ff1540Sreyk ifbroadaddr->sin_len = sizeof(struct sockaddr_in);
16374ff1540Sreyk ifbroadaddr->sin_addr.s_addr =
16474ff1540Sreyk addr->in_v4.s_addr | htonl(0xffffffffUL >> addr->in_netmask);
16574ff1540Sreyk
16674ff1540Sreyk if (add) {
16774ff1540Sreyk ifmask = (struct sockaddr_in *)&ifra.ifra_mask;
16874ff1540Sreyk ifmask->sin_family = AF_INET;
16974ff1540Sreyk ifmask->sin_len = sizeof(struct sockaddr_in);
17074ff1540Sreyk ifmask->sin_addr.s_addr =
17174ff1540Sreyk htonl(0xffffffff << (32 - addr->in_netmask));
17274ff1540Sreyk }
17374ff1540Sreyk
174ae7a93f5Sreyk (void)strlcpy(ifra.ifra_name, apme->a_iface, sizeof(ifra.ifra_name));
175*df69c215Sderaadt if (ioctl(cfg->c_apme_ctl, SIOCDIFADDR, &ifra) == -1) {
17674ff1540Sreyk if (errno != EADDRNOTAVAIL) {
17774ff1540Sreyk hostapd_log(HOSTAPD_LOG_VERBOSE,
178f83005a6Sreyk "%s/%s: failed to delete address %s",
17974ff1540Sreyk apme->a_iface, iapp->i_iface,
18074ff1540Sreyk inet_ntoa(addr->in_v4));
18174ff1540Sreyk return (errno);
18274ff1540Sreyk }
18374ff1540Sreyk }
184*df69c215Sderaadt if (add && ioctl(cfg->c_apme_ctl, SIOCAIFADDR, &ifra) == -1) {
18574ff1540Sreyk if (errno != EEXIST) {
18674ff1540Sreyk hostapd_log(HOSTAPD_LOG_VERBOSE,
187f83005a6Sreyk "%s/%s: failed to add address %s",
18874ff1540Sreyk apme->a_iface, iapp->i_iface,
18974ff1540Sreyk inet_ntoa(addr->in_v4));
19074ff1540Sreyk return (errno);
19174ff1540Sreyk }
19274ff1540Sreyk }
19374ff1540Sreyk
19474ff1540Sreyk hostapd_log(HOSTAPD_LOG_VERBOSE,
195f83005a6Sreyk "%s/%s: %s address %s",
19674ff1540Sreyk apme->a_iface, iapp->i_iface,
19774ff1540Sreyk add ? "added" : "deleted",
19874ff1540Sreyk inet_ntoa(addr->in_v4));
19974ff1540Sreyk
20074ff1540Sreyk return (0);
20174ff1540Sreyk }
20274ff1540Sreyk
20374ff1540Sreyk int
hostapd_roaming_rt(struct hostapd_apme * apme,struct hostapd_inaddr * addr,int add)20474ff1540Sreyk hostapd_roaming_rt(struct hostapd_apme *apme, struct hostapd_inaddr *addr,
20574ff1540Sreyk int add)
20674ff1540Sreyk {
20774ff1540Sreyk struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
20874ff1540Sreyk struct hostapd_iapp *iapp = &cfg->c_iapp;
20974ff1540Sreyk struct {
21074ff1540Sreyk struct rt_msghdr rm_hdr;
21174ff1540Sreyk struct sockaddr_in rm_dst;
21274ff1540Sreyk struct sockaddr_in rm_gateway;
21374ff1540Sreyk struct sockaddr_in rm_netmask;
21474ff1540Sreyk struct sockaddr_rtlabel rm_label;
21574ff1540Sreyk } rm;
21674ff1540Sreyk size_t len = sizeof(rm);
21774ff1540Sreyk
21874ff1540Sreyk bzero(&rm, len);
21974ff1540Sreyk
22074ff1540Sreyk rm.rm_hdr.rtm_msglen = len;
22174ff1540Sreyk rm.rm_hdr.rtm_version = RTM_VERSION;
22274ff1540Sreyk rm.rm_hdr.rtm_type = add ? RTM_CHANGE : RTM_DELETE;
22374ff1540Sreyk rm.rm_hdr.rtm_flags = RTF_STATIC;
22474ff1540Sreyk rm.rm_hdr.rtm_seq = cfg->c_rtseq++;
22574ff1540Sreyk rm.rm_hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_LABEL;
226587cee7fSchris rm.rm_hdr.rtm_hdrlen = sizeof(struct rt_msghdr);
22774ff1540Sreyk
22874ff1540Sreyk rm.rm_dst.sin_family = AF_INET;
22974ff1540Sreyk rm.rm_dst.sin_len = sizeof(rm.rm_dst);
23074ff1540Sreyk rm.rm_dst.sin_addr.s_addr = addr->in_v4.s_addr;
23174ff1540Sreyk
23274ff1540Sreyk rm.rm_gateway.sin_family = AF_INET;
23374ff1540Sreyk rm.rm_gateway.sin_len = sizeof(rm.rm_gateway);
23474ff1540Sreyk rm.rm_gateway.sin_addr.s_addr = apme->a_addr.sin_addr.s_addr;
23574ff1540Sreyk
23674ff1540Sreyk rm.rm_hdr.rtm_addrs |= RTA_NETMASK;
23774ff1540Sreyk rm.rm_netmask.sin_len = sizeof(rm.rm_netmask);
23874ff1540Sreyk rm.rm_netmask.sin_family = AF_INET;
23974ff1540Sreyk if (addr->in_netmask)
24074ff1540Sreyk rm.rm_netmask.sin_addr.s_addr =
24174ff1540Sreyk htonl(0xffffffff << (32 - addr->in_netmask));
24274ff1540Sreyk else if (addr->in_netmask < 0)
24374ff1540Sreyk rm.rm_hdr.rtm_flags |= RTF_HOST;
24474ff1540Sreyk
24574ff1540Sreyk rm.rm_label.sr_len = sizeof(rm.rm_label);
246ae7a93f5Sreyk if (snprintf(rm.rm_label.sr_label, sizeof(rm.rm_label.sr_label),
247ae7a93f5Sreyk "apme-%s", apme->a_iface) == -1)
248ae7a93f5Sreyk goto bad;
24974ff1540Sreyk
25074ff1540Sreyk retry:
25174ff1540Sreyk if (write(cfg->c_rtsock, &rm, len) == -1) {
25274ff1540Sreyk switch (errno) {
25374ff1540Sreyk case ESRCH:
25474ff1540Sreyk if (rm.rm_hdr.rtm_type == RTM_CHANGE) {
25574ff1540Sreyk rm.rm_hdr.rtm_type = RTM_ADD;
25674ff1540Sreyk goto retry;
25774ff1540Sreyk } else if (rm.rm_hdr.rtm_type == RTM_DELETE) {
25874ff1540Sreyk /* Ignore */
25974ff1540Sreyk break;
26074ff1540Sreyk }
26174ff1540Sreyk /* FALLTHROUGH */
26274ff1540Sreyk default:
26374ff1540Sreyk goto bad;
26474ff1540Sreyk }
26574ff1540Sreyk }
26674ff1540Sreyk
26774ff1540Sreyk hostapd_log(HOSTAPD_LOG_VERBOSE,
268f83005a6Sreyk "%s/%s: %s route to %s",
26974ff1540Sreyk apme->a_iface, iapp->i_iface,
27074ff1540Sreyk add ? "added" : "deleted",
27174ff1540Sreyk inet_ntoa(addr->in_v4));
27274ff1540Sreyk
27374ff1540Sreyk return (0);
27474ff1540Sreyk
27574ff1540Sreyk bad:
27674ff1540Sreyk hostapd_log(HOSTAPD_LOG_VERBOSE,
277f83005a6Sreyk "%s/%s: failed to %s route to %s: %s",
27874ff1540Sreyk apme->a_iface, iapp->i_iface,
27974ff1540Sreyk add ? "add" : "delete",
28074ff1540Sreyk inet_ntoa(addr->in_v4),
28174ff1540Sreyk strerror(errno));
28274ff1540Sreyk
28374ff1540Sreyk return (ESRCH);
28474ff1540Sreyk }
28574ff1540Sreyk
28674ff1540Sreyk int
hostapd_roaming_add(struct hostapd_apme * apme,struct hostapd_node * node)28774ff1540Sreyk hostapd_roaming_add(struct hostapd_apme *apme, struct hostapd_node *node)
28874ff1540Sreyk {
28974ff1540Sreyk return (hostapd_priv_roaming(apme, node, 1));
29074ff1540Sreyk }
29174ff1540Sreyk
29274ff1540Sreyk int
hostapd_roaming_del(struct hostapd_apme * apme,struct hostapd_node * node)29374ff1540Sreyk hostapd_roaming_del(struct hostapd_apme *apme, struct hostapd_node *node)
29474ff1540Sreyk {
29574ff1540Sreyk return (hostapd_priv_roaming(apme, node, 0));
29674ff1540Sreyk }
29774ff1540Sreyk
298