xref: /openbsd-src/usr.sbin/hostapd/roaming.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: roaming.c,v 1.3 2007/02/08 11:15:55 reyk 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/param.h>
20 #include <sys/types.h>
21 #include <sys/socket.h>
22 #include <sys/sysctl.h>
23 #include <sys/tree.h>
24 #include <sys/ioctl.h>
25 
26 #include <net/if.h>
27 #include <net/if_dl.h>
28 #include <net/if_media.h>
29 #include <net/if_arp.h>
30 #include <net/if_llc.h>
31 #include <net/route.h>
32 
33 #include <netinet/in.h>
34 #include <netinet/if_ether.h>
35 #include <arpa/inet.h>
36 
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <fcntl.h>
42 #include <errno.h>
43 
44 #include "hostapd.h"
45 
46 int	 hostapd_roaming_addr(struct hostapd_apme *, struct hostapd_inaddr *, int);
47 int	 hostapd_roaming_rt(struct hostapd_apme *, struct hostapd_inaddr *, int);
48 
49 void
50 hostapd_roaming_init(struct hostapd_config *cfg)
51 {
52 	struct hostapd_iapp *iapp = &cfg->c_iapp;
53 	struct hostapd_apme *apme;
54 	struct ifreq ifr;
55 	int v;
56 
57 	if ((cfg->c_flags & HOSTAPD_CFG_F_APME) == 0 ||
58 	    (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING_ROUTE) == 0)
59 		return;
60 
61 	if ((cfg->c_rtsock = socket(AF_ROUTE, SOCK_RAW, AF_INET)) == -1)
62 		hostapd_fatal("failed to init inet socket: %s\n",
63 		    strerror(errno));
64 
65 	v = 0;
66 	if (setsockopt(cfg->c_rtsock, SOL_SOCKET, SO_USELOOPBACK,
67 	    &v, sizeof(v)) == -1)
68 		hostapd_fatal("failed to setup inet socket: %s\n",
69 		    strerror(errno));
70 
71 	TAILQ_FOREACH(apme, &cfg->c_apmes, a_entries) {
72 		bzero(&ifr, sizeof(ifr));
73 		(void)strlcpy(ifr.ifr_name, apme->a_iface, sizeof(ifr.ifr_name));
74 		if (ioctl(cfg->c_apme_ctl, SIOCGIFADDR, &ifr) == -1)
75 			hostapd_fatal("ioctl %s on \"%s\" failed: %s\n",
76 			    "SIOCGIFADDR", ifr.ifr_name, strerror(errno));
77 		bcopy(&ifr.ifr_addr, &apme->a_addr,
78 		    sizeof(struct sockaddr_in));
79 		hostapd_log(HOSTAPD_LOG_VERBOSE,
80 		    "%s/%s: using gateway address %s",
81 		    apme->a_iface, iapp->i_iface,
82 		    inet_ntoa(apme->a_addr.sin_addr), apme->a_iface);
83 	}
84 }
85 
86 void
87 hostapd_roaming_term(struct hostapd_apme *apme)
88 {
89 	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
90 	struct hostapd_iapp *iapp = &cfg->c_iapp;
91 	struct hostapd_entry *entry;
92 
93 	if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING_ROUTE &&
94 	    iapp->i_route_tbl != NULL) {
95 		RB_FOREACH(entry, hostapd_tree, &iapp->i_route_tbl->t_tree) {
96 			if ((entry->e_flags & HOSTAPD_ENTRY_F_INADDR) == 0)
97 				continue;
98 			(void)hostapd_roaming_rt(apme, &entry->e_inaddr, 0);
99 		}
100 	}
101 
102 	if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING_ADDRESS &&
103 	    iapp->i_addr_tbl != NULL) {
104 		RB_FOREACH(entry, hostapd_tree, &iapp->i_addr_tbl->t_tree) {
105 			if ((entry->e_flags & HOSTAPD_ENTRY_F_INADDR) == 0)
106 				continue;
107 			(void)hostapd_roaming_addr(apme, &entry->e_inaddr, 0);
108 		}
109 	}
110 }
111 
112 int
113 hostapd_roaming(struct hostapd_apme *apme, struct hostapd_node *node, int add)
114 {
115 	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
116 	struct hostapd_iapp *iapp = &cfg->c_iapp;
117 	struct hostapd_entry *entry;
118 	int ret;
119 
120 	if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING_ADDRESS &&
121 	    iapp->i_addr_tbl != NULL) {
122 		if ((entry = hostapd_entry_lookup(iapp->i_addr_tbl,
123 		    node->ni_macaddr)) == NULL ||
124 		    (entry->e_flags & HOSTAPD_ENTRY_F_INADDR) == 0)
125 			return (ESRCH);
126 		if ((ret = hostapd_roaming_addr(apme, &entry->e_inaddr,
127 		    add)) != 0)
128 			return (ret);
129 	}
130 
131 	if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING_ROUTE &&
132 	    iapp->i_route_tbl != NULL) {
133 		if ((entry = hostapd_entry_lookup(iapp->i_addr_tbl,
134 		    node->ni_macaddr)) == NULL ||
135 		    (entry->e_flags & HOSTAPD_ENTRY_F_INADDR) == 0)
136 			return (ESRCH);
137 		if ((ret = hostapd_roaming_rt(apme, &entry->e_inaddr,
138 		    add)) != 0)
139 			return (ret);
140 	}
141 
142 	return (0);
143 }
144 
145 
146 int
147 hostapd_roaming_addr(struct hostapd_apme *apme, struct hostapd_inaddr *addr,
148     int add)
149 {
150 	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
151 	struct hostapd_iapp *iapp = &cfg->c_iapp;
152 	struct sockaddr_in *ifaddr, *ifmask, *ifbroadaddr;
153 	struct ifaliasreq ifra;
154 
155 	bzero(&ifra, sizeof(ifra));
156 
157 	ifaddr = (struct sockaddr_in *)&ifra.ifra_addr;
158 	ifaddr->sin_family = AF_INET;
159 	ifaddr->sin_len = sizeof(struct sockaddr_in);
160 	ifaddr->sin_addr.s_addr = addr->in_v4.s_addr;
161 
162 	ifbroadaddr = (struct sockaddr_in *)&ifra.ifra_broadaddr;
163 	ifbroadaddr->sin_family = AF_INET;
164 	ifbroadaddr->sin_len = sizeof(struct sockaddr_in);
165 	ifbroadaddr->sin_addr.s_addr =
166 	    addr->in_v4.s_addr | htonl(0xffffffffUL >> addr->in_netmask);
167 
168 	if (add) {
169 		ifmask = (struct sockaddr_in *)&ifra.ifra_mask;
170 		ifmask->sin_family = AF_INET;
171 		ifmask->sin_len = sizeof(struct sockaddr_in);
172 		ifmask->sin_addr.s_addr =
173 		    htonl(0xffffffff << (32 - addr->in_netmask));
174 	}
175 
176 	(void)strlcpy(ifra.ifra_name, apme->a_iface, sizeof(ifra.ifra_name));
177 	if (ioctl(cfg->c_apme_ctl, SIOCDIFADDR, &ifra) < 0) {
178 		if (errno != EADDRNOTAVAIL) {
179 			hostapd_log(HOSTAPD_LOG_VERBOSE,
180 			    "%s/%s: failed to delete address %s",
181 			    apme->a_iface, iapp->i_iface,
182 			    inet_ntoa(addr->in_v4));
183 			return (errno);
184 		}
185 	}
186 	if (add && ioctl(cfg->c_apme_ctl, SIOCAIFADDR, &ifra) < 0) {
187 		if (errno != EEXIST) {
188 			hostapd_log(HOSTAPD_LOG_VERBOSE,
189 			    "%s/%s: failed to add address %s",
190 			    apme->a_iface, iapp->i_iface,
191 			    inet_ntoa(addr->in_v4));
192 			return (errno);
193 		}
194 	}
195 
196 	hostapd_log(HOSTAPD_LOG_VERBOSE,
197 	    "%s/%s: %s address %s",
198 	    apme->a_iface, iapp->i_iface,
199 	    add ? "added" : "deleted",
200 	    inet_ntoa(addr->in_v4));
201 
202 	return (0);
203 }
204 
205 int
206 hostapd_roaming_rt(struct hostapd_apme *apme, struct hostapd_inaddr *addr,
207     int add)
208 {
209 	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
210 	struct hostapd_iapp *iapp = &cfg->c_iapp;
211 	struct {
212 		struct rt_msghdr	rm_hdr;
213 		struct sockaddr_in	rm_dst;
214 		struct sockaddr_in	rm_gateway;
215 		struct sockaddr_in	rm_netmask;
216 		struct sockaddr_rtlabel	rm_label;
217 	} rm;
218 	size_t len = sizeof(rm);
219 
220 	bzero(&rm, len);
221 
222 	rm.rm_hdr.rtm_msglen = len;
223 	rm.rm_hdr.rtm_version = RTM_VERSION;
224 	rm.rm_hdr.rtm_type = add ? RTM_CHANGE : RTM_DELETE;
225 	rm.rm_hdr.rtm_flags = RTF_STATIC;
226 	rm.rm_hdr.rtm_seq = cfg->c_rtseq++;
227 	rm.rm_hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_LABEL;
228 
229 	rm.rm_dst.sin_family = AF_INET;
230 	rm.rm_dst.sin_len = sizeof(rm.rm_dst);
231 	rm.rm_dst.sin_addr.s_addr = addr->in_v4.s_addr;
232 
233 	rm.rm_gateway.sin_family = AF_INET;
234 	rm.rm_gateway.sin_len = sizeof(rm.rm_gateway);
235 	rm.rm_gateway.sin_addr.s_addr = apme->a_addr.sin_addr.s_addr;
236 
237 	rm.rm_hdr.rtm_addrs |= RTA_NETMASK;
238 	rm.rm_netmask.sin_len = sizeof(rm.rm_netmask);
239 	rm.rm_netmask.sin_family = AF_INET;
240 	if (addr->in_netmask)
241 		rm.rm_netmask.sin_addr.s_addr =
242 		    htonl(0xffffffff << (32 - addr->in_netmask));
243 	else if (addr->in_netmask < 0)
244 		rm.rm_hdr.rtm_flags |= RTF_HOST;
245 
246 	rm.rm_label.sr_len = sizeof(rm.rm_label);
247 	if (snprintf(rm.rm_label.sr_label, sizeof(rm.rm_label.sr_label),
248 	    "apme-%s", apme->a_iface) == -1)
249 		goto bad;
250 
251  retry:
252 	if (write(cfg->c_rtsock, &rm, len) == -1) {
253 		switch (errno) {
254 		case ESRCH:
255 			if (rm.rm_hdr.rtm_type == RTM_CHANGE) {
256 				rm.rm_hdr.rtm_type = RTM_ADD;
257 				goto retry;
258 			} else if (rm.rm_hdr.rtm_type == RTM_DELETE) {
259 				/* Ignore */
260 				break;
261 			}
262 			/* FALLTHROUGH */
263 		default:
264 			goto bad;
265 		}
266 	}
267 
268 	hostapd_log(HOSTAPD_LOG_VERBOSE,
269 	    "%s/%s: %s route to %s",
270 	    apme->a_iface, iapp->i_iface,
271 	    add ? "added" : "deleted",
272 	    inet_ntoa(addr->in_v4));
273 
274 	return (0);
275 
276  bad:
277 	hostapd_log(HOSTAPD_LOG_VERBOSE,
278 	    "%s/%s: failed to %s route to %s: %s",
279 	    apme->a_iface, iapp->i_iface,
280 	    add ? "add" : "delete",
281 	    inet_ntoa(addr->in_v4),
282 	    strerror(errno));
283 
284 	return (ESRCH);
285 }
286 
287 int
288 hostapd_roaming_add(struct hostapd_apme *apme, struct hostapd_node *node)
289 {
290 	return (hostapd_priv_roaming(apme, node, 1));
291 }
292 
293 int
294 hostapd_roaming_del(struct hostapd_apme *apme, struct hostapd_node *node)
295 {
296 	return (hostapd_priv_roaming(apme, node, 0));
297 }
298 
299