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