GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/hostapd/roaming.c Lines: 0 141 0.0 %
Date: 2017-11-13 Branches: 0 74 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: roaming.c,v 1.6 2015/01/16 06:40:17 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_dl.h>
26
#include <net/if_media.h>
27
#include <net/if_arp.h>
28
#include <net/if_llc.h>
29
#include <net/route.h>
30
31
#include <netinet/in.h>
32
#include <netinet/if_ether.h>
33
#include <arpa/inet.h>
34
35
#include <stdlib.h>
36
#include <stdio.h>
37
#include <string.h>
38
#include <unistd.h>
39
#include <fcntl.h>
40
#include <errno.h>
41
#include <limits.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