1/*
2 * Linux ioctl helper functions for driver wrappers
3 * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi>
4 *
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
7 */
8
9#include "utils/includes.h"
10#include <sys/ioctl.h>
11#include <net/if.h>
12#include <net/if_arp.h>
13
14#include "utils/common.h"
15#include "linux_ioctl.h"
16
17
18int linux_set_iface_flags(int sock, const char *ifname, int dev_up)
19{
20	struct ifreq ifr;
21	int ret;
22
23	if (sock < 0)
24		return -1;
25
26	os_memset(&ifr, 0, sizeof(ifr));
27	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
28
29	if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) {
30		ret = errno ? -errno : -999;
31		wpa_printf(MSG_ERROR, "Could not read interface %s flags: %s",
32			   ifname, strerror(errno));
33		return ret;
34	}
35
36	if (dev_up) {
37		if (ifr.ifr_flags & IFF_UP)
38			return 0;
39		ifr.ifr_flags |= IFF_UP;
40	} else {
41		if (!(ifr.ifr_flags & IFF_UP))
42			return 0;
43		ifr.ifr_flags &= ~IFF_UP;
44	}
45
46	if (ioctl(sock, SIOCSIFFLAGS, &ifr) != 0) {
47		ret = errno ? -errno : -999;
48		wpa_printf(MSG_ERROR, "Could not set interface %s flags (%s): "
49			   "%s",
50			   ifname, dev_up ? "UP" : "DOWN", strerror(errno));
51		return ret;
52	}
53
54	return 0;
55}
56
57
58int linux_iface_up(int sock, const char *ifname)
59{
60	struct ifreq ifr;
61	int ret;
62
63	if (sock < 0)
64		return -1;
65
66	os_memset(&ifr, 0, sizeof(ifr));
67	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
68
69	if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) {
70		ret = errno ? -errno : -999;
71		wpa_printf(MSG_ERROR, "Could not read interface %s flags: %s",
72			   ifname, strerror(errno));
73		return ret;
74	}
75
76	return !!(ifr.ifr_flags & IFF_UP);
77}
78
79
80int linux_get_ifhwaddr(int sock, const char *ifname, u8 *addr)
81{
82	struct ifreq ifr;
83
84	os_memset(&ifr, 0, sizeof(ifr));
85	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
86	if (ioctl(sock, SIOCGIFHWADDR, &ifr)) {
87		wpa_printf(MSG_ERROR, "Could not get interface %s hwaddr: %s",
88			   ifname, strerror(errno));
89		return -1;
90	}
91
92	if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
93		wpa_printf(MSG_ERROR, "%s: Invalid HW-addr family 0x%04x",
94			   ifname, ifr.ifr_hwaddr.sa_family);
95		return -1;
96	}
97	os_memcpy(addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
98
99	return 0;
100}
101
102
103int linux_set_ifhwaddr(int sock, const char *ifname, const u8 *addr)
104{
105	struct ifreq ifr;
106
107	os_memset(&ifr, 0, sizeof(ifr));
108	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
109	os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN);
110	ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
111
112	if (ioctl(sock, SIOCSIFHWADDR, &ifr)) {
113		wpa_printf(MSG_DEBUG, "Could not set interface %s hwaddr: %s",
114			   ifname, strerror(errno));
115		return -1;
116	}
117
118	return 0;
119}
120
121
122#ifndef SIOCBRADDBR
123#define SIOCBRADDBR 0x89a0
124#endif
125#ifndef SIOCBRDELBR
126#define SIOCBRDELBR 0x89a1
127#endif
128#ifndef SIOCBRADDIF
129#define SIOCBRADDIF 0x89a2
130#endif
131#ifndef SIOCBRDELIF
132#define SIOCBRDELIF 0x89a3
133#endif
134
135
136int linux_br_add(int sock, const char *brname)
137{
138	if (ioctl(sock, SIOCBRADDBR, brname) < 0) {
139		wpa_printf(MSG_DEBUG, "Could not add bridge %s: %s",
140			   brname, strerror(errno));
141		return -1;
142	}
143
144	return 0;
145}
146
147
148int linux_br_del(int sock, const char *brname)
149{
150	if (ioctl(sock, SIOCBRDELBR, brname) < 0) {
151		wpa_printf(MSG_DEBUG, "Could not remove bridge %s: %s",
152			   brname, strerror(errno));
153		return -1;
154	}
155
156	return 0;
157}
158
159
160int linux_br_add_if(int sock, const char *brname, const char *ifname)
161{
162	struct ifreq ifr;
163	int ifindex;
164
165	ifindex = if_nametoindex(ifname);
166	if (ifindex == 0)
167		return -1;
168
169	os_memset(&ifr, 0, sizeof(ifr));
170	os_strlcpy(ifr.ifr_name, brname, IFNAMSIZ);
171	ifr.ifr_ifindex = ifindex;
172	if (ioctl(sock, SIOCBRADDIF, &ifr) < 0) {
173		wpa_printf(MSG_DEBUG, "Could not add interface %s into bridge "
174			   "%s: %s", ifname, brname, strerror(errno));
175		return -1;
176	}
177
178	return 0;
179}
180
181
182int linux_br_del_if(int sock, const char *brname, const char *ifname)
183{
184	struct ifreq ifr;
185	int ifindex;
186
187	ifindex = if_nametoindex(ifname);
188	if (ifindex == 0)
189		return -1;
190
191	os_memset(&ifr, 0, sizeof(ifr));
192	os_strlcpy(ifr.ifr_name, brname, IFNAMSIZ);
193	ifr.ifr_ifindex = ifindex;
194	if (ioctl(sock, SIOCBRDELIF, &ifr) < 0) {
195		wpa_printf(MSG_DEBUG, "Could not remove interface %s from "
196			   "bridge %s: %s", ifname, brname, strerror(errno));
197		return -1;
198	}
199
200	return 0;
201}
202
203
204int linux_br_get(char *brname, const char *ifname)
205{
206	char path[128], brlink[128], *pos;
207	ssize_t res;
208
209	os_snprintf(path, sizeof(path), "/sys/class/net/%s/brport/bridge",
210		    ifname);
211	res = readlink(path, brlink, sizeof(brlink));
212	if (res < 0 || (size_t) res >= sizeof(brlink))
213		return -1;
214	brlink[res] = '\0';
215	pos = os_strrchr(brlink, '/');
216	if (pos == NULL)
217		return -1;
218	pos++;
219	os_strlcpy(brname, pos, IFNAMSIZ);
220	return 0;
221}
222
223
224int linux_master_get(char *master_ifname, const char *ifname)
225{
226	char buf[128], masterlink[128], *pos;
227	ssize_t res;
228
229	/* check whether there is a master */
230	os_snprintf(buf, sizeof(buf), "/sys/class/net/%s/master", ifname);
231
232	res = readlink(buf, masterlink, sizeof(masterlink));
233	if (res < 0 || (size_t) res >= sizeof(masterlink))
234		return -1;
235
236	masterlink[res] = '\0';
237
238	pos = os_strrchr(masterlink, '/');
239	if (pos == NULL)
240		return -1;
241	pos++;
242	os_strlcpy(master_ifname, pos, IFNAMSIZ);
243	return 0;
244}
245