1/*	$OpenBSD: roaming.c,v 1.8 2019/06/28 13:32:47 deraadt Exp $	*/
2
3/*
4 * Copyright (c) 2005, 2006 Reyk Floeter <reyk@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/types.h>
20#include <sys/socket.h>
21#include <sys/tree.h>
22#include <sys/ioctl.h>
23
24#include <net/if.h>
25#include <net/if_media.h>
26#include <net/if_arp.h>
27#include <net/if_llc.h>
28#include <net/route.h>
29
30#include <netinet/in.h>
31#include <netinet/if_ether.h>
32#include <arpa/inet.h>
33
34#include <stdlib.h>
35#include <stdio.h>
36#include <string.h>
37#include <unistd.h>
38#include <fcntl.h>
39#include <errno.h>
40#include <limits.h>
41
42#include "hostapd.h"
43
44int	 hostapd_roaming_addr(struct hostapd_apme *, struct hostapd_inaddr *, int);
45int	 hostapd_roaming_rt(struct hostapd_apme *, struct hostapd_inaddr *, int);
46
47void
48hostapd_roaming_init(struct hostapd_config *cfg)
49{
50	struct hostapd_iapp *iapp = &cfg->c_iapp;
51	struct hostapd_apme *apme;
52	struct ifreq ifr;
53	int v;
54
55	if ((cfg->c_flags & HOSTAPD_CFG_F_APME) == 0 ||
56	    (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING_ROUTE) == 0)
57		return;
58
59	if ((cfg->c_rtsock = socket(AF_ROUTE, SOCK_RAW, AF_INET)) == -1)
60		hostapd_fatal("failed to init inet socket: %s\n",
61		    strerror(errno));
62
63	v = 0;
64	if (setsockopt(cfg->c_rtsock, SOL_SOCKET, SO_USELOOPBACK,
65	    &v, sizeof(v)) == -1)
66		hostapd_fatal("failed to setup inet socket: %s\n",
67		    strerror(errno));
68
69	TAILQ_FOREACH(apme, &cfg->c_apmes, a_entries) {
70		bzero(&ifr, sizeof(ifr));
71		(void)strlcpy(ifr.ifr_name, apme->a_iface, sizeof(ifr.ifr_name));
72		if (ioctl(cfg->c_apme_ctl, SIOCGIFADDR, &ifr) == -1)
73			hostapd_fatal("ioctl %s on \"%s\" failed: %s\n",
74			    "SIOCGIFADDR", ifr.ifr_name, strerror(errno));
75		bcopy(&ifr.ifr_addr, &apme->a_addr,
76		    sizeof(struct sockaddr_in));
77		hostapd_log(HOSTAPD_LOG_VERBOSE,
78		    "%s/%s: using gateway address %s",
79		    apme->a_iface, iapp->i_iface,
80		    inet_ntoa(apme->a_addr.sin_addr), apme->a_iface);
81	}
82}
83
84void
85hostapd_roaming_term(struct hostapd_apme *apme)
86{
87	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
88	struct hostapd_iapp *iapp = &cfg->c_iapp;
89	struct hostapd_entry *entry;
90
91	if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING_ROUTE &&
92	    iapp->i_route_tbl != NULL) {
93		RB_FOREACH(entry, hostapd_tree, &iapp->i_route_tbl->t_tree) {
94			if ((entry->e_flags & HOSTAPD_ENTRY_F_INADDR) == 0)
95				continue;
96			(void)hostapd_roaming_rt(apme, &entry->e_inaddr, 0);
97		}
98	}
99
100	if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING_ADDRESS &&
101	    iapp->i_addr_tbl != NULL) {
102		RB_FOREACH(entry, hostapd_tree, &iapp->i_addr_tbl->t_tree) {
103			if ((entry->e_flags & HOSTAPD_ENTRY_F_INADDR) == 0)
104				continue;
105			(void)hostapd_roaming_addr(apme, &entry->e_inaddr, 0);
106		}
107	}
108}
109
110int
111hostapd_roaming(struct hostapd_apme *apme, struct hostapd_node *node, int add)
112{
113	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
114	struct hostapd_iapp *iapp = &cfg->c_iapp;
115	struct hostapd_entry *entry;
116	int ret;
117
118	if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING_ADDRESS &&
119	    iapp->i_addr_tbl != NULL) {
120		if ((entry = hostapd_entry_lookup(iapp->i_addr_tbl,
121		    node->ni_macaddr)) == NULL ||
122		    (entry->e_flags & HOSTAPD_ENTRY_F_INADDR) == 0)
123			return (ESRCH);
124		if ((ret = hostapd_roaming_addr(apme, &entry->e_inaddr,
125		    add)) != 0)
126			return (ret);
127	}
128
129	if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING_ROUTE &&
130	    iapp->i_route_tbl != NULL) {
131		if ((entry = hostapd_entry_lookup(iapp->i_addr_tbl,
132		    node->ni_macaddr)) == NULL ||
133		    (entry->e_flags & HOSTAPD_ENTRY_F_INADDR) == 0)
134			return (ESRCH);
135		if ((ret = hostapd_roaming_rt(apme, &entry->e_inaddr,
136		    add)) != 0)
137			return (ret);
138	}
139
140	return (0);
141}
142
143
144int
145hostapd_roaming_addr(struct hostapd_apme *apme, struct hostapd_inaddr *addr,
146    int add)
147{
148	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
149	struct hostapd_iapp *iapp = &cfg->c_iapp;
150	struct sockaddr_in *ifaddr, *ifmask, *ifbroadaddr;
151	struct ifaliasreq ifra;
152
153	bzero(&ifra, sizeof(ifra));
154
155	ifaddr = (struct sockaddr_in *)&ifra.ifra_addr;
156	ifaddr->sin_family = AF_INET;
157	ifaddr->sin_len = sizeof(struct sockaddr_in);
158	ifaddr->sin_addr.s_addr = addr->in_v4.s_addr;
159
160	ifbroadaddr = (struct sockaddr_in *)&ifra.ifra_broadaddr;
161	ifbroadaddr->sin_family = AF_INET;
162	ifbroadaddr->sin_len = sizeof(struct sockaddr_in);
163	ifbroadaddr->sin_addr.s_addr =
164	    addr->in_v4.s_addr | htonl(0xffffffffUL >> addr->in_netmask);
165
166	if (add) {
167		ifmask = (struct sockaddr_in *)&ifra.ifra_mask;
168		ifmask->sin_family = AF_INET;
169		ifmask->sin_len = sizeof(struct sockaddr_in);
170		ifmask->sin_addr.s_addr =
171		    htonl(0xffffffff << (32 - addr->in_netmask));
172	}
173
174	(void)strlcpy(ifra.ifra_name, apme->a_iface, sizeof(ifra.ifra_name));
175	if (ioctl(cfg->c_apme_ctl, SIOCDIFADDR, &ifra) == -1) {
176		if (errno != EADDRNOTAVAIL) {
177			hostapd_log(HOSTAPD_LOG_VERBOSE,
178			    "%s/%s: failed to delete address %s",
179			    apme->a_iface, iapp->i_iface,
180			    inet_ntoa(addr->in_v4));
181			return (errno);
182		}
183	}
184	if (add && ioctl(cfg->c_apme_ctl, SIOCAIFADDR, &ifra) == -1) {
185		if (errno != EEXIST) {
186			hostapd_log(HOSTAPD_LOG_VERBOSE,
187			    "%s/%s: failed to add address %s",
188			    apme->a_iface, iapp->i_iface,
189			    inet_ntoa(addr->in_v4));
190			return (errno);
191		}
192	}
193
194	hostapd_log(HOSTAPD_LOG_VERBOSE,
195	    "%s/%s: %s address %s",
196	    apme->a_iface, iapp->i_iface,
197	    add ? "added" : "deleted",
198	    inet_ntoa(addr->in_v4));
199
200	return (0);
201}
202
203int
204hostapd_roaming_rt(struct hostapd_apme *apme, struct hostapd_inaddr *addr,
205    int add)
206{
207	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
208	struct hostapd_iapp *iapp = &cfg->c_iapp;
209	struct {
210		struct rt_msghdr	rm_hdr;
211		struct sockaddr_in	rm_dst;
212		struct sockaddr_in	rm_gateway;
213		struct sockaddr_in	rm_netmask;
214		struct sockaddr_rtlabel	rm_label;
215	} rm;
216	size_t len = sizeof(rm);
217
218	bzero(&rm, len);
219
220	rm.rm_hdr.rtm_msglen = len;
221	rm.rm_hdr.rtm_version = RTM_VERSION;
222	rm.rm_hdr.rtm_type = add ? RTM_CHANGE : RTM_DELETE;
223	rm.rm_hdr.rtm_flags = RTF_STATIC;
224	rm.rm_hdr.rtm_seq = cfg->c_rtseq++;
225	rm.rm_hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_LABEL;
226	rm.rm_hdr.rtm_hdrlen = sizeof(struct rt_msghdr);
227
228	rm.rm_dst.sin_family = AF_INET;
229	rm.rm_dst.sin_len = sizeof(rm.rm_dst);
230	rm.rm_dst.sin_addr.s_addr = addr->in_v4.s_addr;
231
232	rm.rm_gateway.sin_family = AF_INET;
233	rm.rm_gateway.sin_len = sizeof(rm.rm_gateway);
234	rm.rm_gateway.sin_addr.s_addr = apme->a_addr.sin_addr.s_addr;
235
236	rm.rm_hdr.rtm_addrs |= RTA_NETMASK;
237	rm.rm_netmask.sin_len = sizeof(rm.rm_netmask);
238	rm.rm_netmask.sin_family = AF_INET;
239	if (addr->in_netmask)
240		rm.rm_netmask.sin_addr.s_addr =
241		    htonl(0xffffffff << (32 - addr->in_netmask));
242	else if (addr->in_netmask < 0)
243		rm.rm_hdr.rtm_flags |= RTF_HOST;
244
245	rm.rm_label.sr_len = sizeof(rm.rm_label);
246	if (snprintf(rm.rm_label.sr_label, sizeof(rm.rm_label.sr_label),
247	    "apme-%s", apme->a_iface) == -1)
248		goto bad;
249
250 retry:
251	if (write(cfg->c_rtsock, &rm, len) == -1) {
252		switch (errno) {
253		case ESRCH:
254			if (rm.rm_hdr.rtm_type == RTM_CHANGE) {
255				rm.rm_hdr.rtm_type = RTM_ADD;
256				goto retry;
257			} else if (rm.rm_hdr.rtm_type == RTM_DELETE) {
258				/* Ignore */
259				break;
260			}
261			/* FALLTHROUGH */
262		default:
263			goto bad;
264		}
265	}
266
267	hostapd_log(HOSTAPD_LOG_VERBOSE,
268	    "%s/%s: %s route to %s",
269	    apme->a_iface, iapp->i_iface,
270	    add ? "added" : "deleted",
271	    inet_ntoa(addr->in_v4));
272
273	return (0);
274
275 bad:
276	hostapd_log(HOSTAPD_LOG_VERBOSE,
277	    "%s/%s: failed to %s route to %s: %s",
278	    apme->a_iface, iapp->i_iface,
279	    add ? "add" : "delete",
280	    inet_ntoa(addr->in_v4),
281	    strerror(errno));
282
283	return (ESRCH);
284}
285
286int
287hostapd_roaming_add(struct hostapd_apme *apme, struct hostapd_node *node)
288{
289	return (hostapd_priv_roaming(apme, node, 1));
290}
291
292int
293hostapd_roaming_del(struct hostapd_apme *apme, struct hostapd_node *node)
294{
295	return (hostapd_priv_roaming(apme, node, 0));
296}
297
298