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
hostapd_roaming_init(struct hostapd_config * cfg)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
hostapd_roaming_term(struct hostapd_apme * apme)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
hostapd_roaming(struct hostapd_apme * apme,struct hostapd_node * node,int add)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
hostapd_roaming_addr(struct hostapd_apme * apme,struct hostapd_inaddr * addr,int add)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
hostapd_roaming_rt(struct hostapd_apme * apme,struct hostapd_inaddr * addr,int add)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
hostapd_roaming_add(struct hostapd_apme * apme,struct hostapd_node * node)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
hostapd_roaming_del(struct hostapd_apme * apme,struct hostapd_node * node)293 hostapd_roaming_del(struct hostapd_apme *apme, struct hostapd_node *node)
294 {
295 return (hostapd_priv_roaming(apme, node, 0));
296 }
297
298