xref: /openbsd/usr.sbin/hostapd/roaming.c (revision a6445c1d)
1 /*	$OpenBSD: roaming.c,v 1.5 2010/05/26 19:18:10 millert 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/tree.h>
23 #include <sys/ioctl.h>
24 
25 #include <net/if.h>
26 #include <net/if_dl.h>
27 #include <net/if_media.h>
28 #include <net/if_arp.h>
29 #include <net/if_llc.h>
30 #include <net/route.h>
31 
32 #include <netinet/in.h>
33 #include <netinet/if_ether.h>
34 #include <arpa/inet.h>
35 
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <unistd.h>
40 #include <fcntl.h>
41 #include <errno.h>
42 
43 #include "hostapd.h"
44 
45 int	 hostapd_roaming_addr(struct hostapd_apme *, struct hostapd_inaddr *, int);
46 int	 hostapd_roaming_rt(struct hostapd_apme *, struct hostapd_inaddr *, int);
47 
48 void
49 hostapd_roaming_init(struct hostapd_config *cfg)
50 {
51 	struct hostapd_iapp *iapp = &cfg->c_iapp;
52 	struct hostapd_apme *apme;
53 	struct ifreq ifr;
54 	int v;
55 
56 	if ((cfg->c_flags & HOSTAPD_CFG_F_APME) == 0 ||
57 	    (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING_ROUTE) == 0)
58 		return;
59 
60 	if ((cfg->c_rtsock = socket(AF_ROUTE, SOCK_RAW, AF_INET)) == -1)
61 		hostapd_fatal("failed to init inet socket: %s\n",
62 		    strerror(errno));
63 
64 	v = 0;
65 	if (setsockopt(cfg->c_rtsock, SOL_SOCKET, SO_USELOOPBACK,
66 	    &v, sizeof(v)) == -1)
67 		hostapd_fatal("failed to setup inet socket: %s\n",
68 		    strerror(errno));
69 
70 	TAILQ_FOREACH(apme, &cfg->c_apmes, a_entries) {
71 		bzero(&ifr, sizeof(ifr));
72 		(void)strlcpy(ifr.ifr_name, apme->a_iface, sizeof(ifr.ifr_name));
73 		if (ioctl(cfg->c_apme_ctl, SIOCGIFADDR, &ifr) == -1)
74 			hostapd_fatal("ioctl %s on \"%s\" failed: %s\n",
75 			    "SIOCGIFADDR", ifr.ifr_name, strerror(errno));
76 		bcopy(&ifr.ifr_addr, &apme->a_addr,
77 		    sizeof(struct sockaddr_in));
78 		hostapd_log(HOSTAPD_LOG_VERBOSE,
79 		    "%s/%s: using gateway address %s",
80 		    apme->a_iface, iapp->i_iface,
81 		    inet_ntoa(apme->a_addr.sin_addr), apme->a_iface);
82 	}
83 }
84 
85 void
86 hostapd_roaming_term(struct hostapd_apme *apme)
87 {
88 	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
89 	struct hostapd_iapp *iapp = &cfg->c_iapp;
90 	struct hostapd_entry *entry;
91 
92 	if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING_ROUTE &&
93 	    iapp->i_route_tbl != NULL) {
94 		RB_FOREACH(entry, hostapd_tree, &iapp->i_route_tbl->t_tree) {
95 			if ((entry->e_flags & HOSTAPD_ENTRY_F_INADDR) == 0)
96 				continue;
97 			(void)hostapd_roaming_rt(apme, &entry->e_inaddr, 0);
98 		}
99 	}
100 
101 	if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING_ADDRESS &&
102 	    iapp->i_addr_tbl != NULL) {
103 		RB_FOREACH(entry, hostapd_tree, &iapp->i_addr_tbl->t_tree) {
104 			if ((entry->e_flags & HOSTAPD_ENTRY_F_INADDR) == 0)
105 				continue;
106 			(void)hostapd_roaming_addr(apme, &entry->e_inaddr, 0);
107 		}
108 	}
109 }
110 
111 int
112 hostapd_roaming(struct hostapd_apme *apme, struct hostapd_node *node, int add)
113 {
114 	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
115 	struct hostapd_iapp *iapp = &cfg->c_iapp;
116 	struct hostapd_entry *entry;
117 	int ret;
118 
119 	if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING_ADDRESS &&
120 	    iapp->i_addr_tbl != NULL) {
121 		if ((entry = hostapd_entry_lookup(iapp->i_addr_tbl,
122 		    node->ni_macaddr)) == NULL ||
123 		    (entry->e_flags & HOSTAPD_ENTRY_F_INADDR) == 0)
124 			return (ESRCH);
125 		if ((ret = hostapd_roaming_addr(apme, &entry->e_inaddr,
126 		    add)) != 0)
127 			return (ret);
128 	}
129 
130 	if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING_ROUTE &&
131 	    iapp->i_route_tbl != NULL) {
132 		if ((entry = hostapd_entry_lookup(iapp->i_addr_tbl,
133 		    node->ni_macaddr)) == NULL ||
134 		    (entry->e_flags & HOSTAPD_ENTRY_F_INADDR) == 0)
135 			return (ESRCH);
136 		if ((ret = hostapd_roaming_rt(apme, &entry->e_inaddr,
137 		    add)) != 0)
138 			return (ret);
139 	}
140 
141 	return (0);
142 }
143 
144 
145 int
146 hostapd_roaming_addr(struct hostapd_apme *apme, struct hostapd_inaddr *addr,
147     int add)
148 {
149 	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
150 	struct hostapd_iapp *iapp = &cfg->c_iapp;
151 	struct sockaddr_in *ifaddr, *ifmask, *ifbroadaddr;
152 	struct ifaliasreq ifra;
153 
154 	bzero(&ifra, sizeof(ifra));
155 
156 	ifaddr = (struct sockaddr_in *)&ifra.ifra_addr;
157 	ifaddr->sin_family = AF_INET;
158 	ifaddr->sin_len = sizeof(struct sockaddr_in);
159 	ifaddr->sin_addr.s_addr = addr->in_v4.s_addr;
160 
161 	ifbroadaddr = (struct sockaddr_in *)&ifra.ifra_broadaddr;
162 	ifbroadaddr->sin_family = AF_INET;
163 	ifbroadaddr->sin_len = sizeof(struct sockaddr_in);
164 	ifbroadaddr->sin_addr.s_addr =
165 	    addr->in_v4.s_addr | htonl(0xffffffffUL >> addr->in_netmask);
166 
167 	if (add) {
168 		ifmask = (struct sockaddr_in *)&ifra.ifra_mask;
169 		ifmask->sin_family = AF_INET;
170 		ifmask->sin_len = sizeof(struct sockaddr_in);
171 		ifmask->sin_addr.s_addr =
172 		    htonl(0xffffffff << (32 - addr->in_netmask));
173 	}
174 
175 	(void)strlcpy(ifra.ifra_name, apme->a_iface, sizeof(ifra.ifra_name));
176 	if (ioctl(cfg->c_apme_ctl, SIOCDIFADDR, &ifra) < 0) {
177 		if (errno != EADDRNOTAVAIL) {
178 			hostapd_log(HOSTAPD_LOG_VERBOSE,
179 			    "%s/%s: failed to delete address %s",
180 			    apme->a_iface, iapp->i_iface,
181 			    inet_ntoa(addr->in_v4));
182 			return (errno);
183 		}
184 	}
185 	if (add && ioctl(cfg->c_apme_ctl, SIOCAIFADDR, &ifra) < 0) {
186 		if (errno != EEXIST) {
187 			hostapd_log(HOSTAPD_LOG_VERBOSE,
188 			    "%s/%s: failed to add address %s",
189 			    apme->a_iface, iapp->i_iface,
190 			    inet_ntoa(addr->in_v4));
191 			return (errno);
192 		}
193 	}
194 
195 	hostapd_log(HOSTAPD_LOG_VERBOSE,
196 	    "%s/%s: %s address %s",
197 	    apme->a_iface, iapp->i_iface,
198 	    add ? "added" : "deleted",
199 	    inet_ntoa(addr->in_v4));
200 
201 	return (0);
202 }
203 
204 int
205 hostapd_roaming_rt(struct hostapd_apme *apme, struct hostapd_inaddr *addr,
206     int add)
207 {
208 	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
209 	struct hostapd_iapp *iapp = &cfg->c_iapp;
210 	struct {
211 		struct rt_msghdr	rm_hdr;
212 		struct sockaddr_in	rm_dst;
213 		struct sockaddr_in	rm_gateway;
214 		struct sockaddr_in	rm_netmask;
215 		struct sockaddr_rtlabel	rm_label;
216 	} rm;
217 	size_t len = sizeof(rm);
218 
219 	bzero(&rm, len);
220 
221 	rm.rm_hdr.rtm_msglen = len;
222 	rm.rm_hdr.rtm_version = RTM_VERSION;
223 	rm.rm_hdr.rtm_type = add ? RTM_CHANGE : RTM_DELETE;
224 	rm.rm_hdr.rtm_flags = RTF_STATIC;
225 	rm.rm_hdr.rtm_seq = cfg->c_rtseq++;
226 	rm.rm_hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_LABEL;
227 	rm.rm_hdr.rtm_hdrlen = sizeof(struct rt_msghdr);
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