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