xref: /openbsd/usr.sbin/hostapd/roaming.c (revision 952f302f)
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 
44 int	 hostapd_roaming_addr(struct hostapd_apme *, struct hostapd_inaddr *, int);
45 int	 hostapd_roaming_rt(struct hostapd_apme *, struct hostapd_inaddr *, int);
46 
47 void
48 hostapd_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 
84 void
85 hostapd_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 
110 int
111 hostapd_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 
144 int
145 hostapd_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 
203 int
204 hostapd_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 
286 int
287 hostapd_roaming_add(struct hostapd_apme *apme, struct hostapd_node *node)
288 {
289 	return (hostapd_priv_roaming(apme, node, 1));
290 }
291 
292 int
293 hostapd_roaming_del(struct hostapd_apme *apme, struct hostapd_node *node)
294 {
295 	return (hostapd_priv_roaming(apme, node, 0));
296 }
297 
298