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  
linux_set_iface_flags(int sock,const char * ifname,int dev_up)19  int 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  
linux_iface_up(int sock,const char * ifname)59  int 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  
linux_get_ifhwaddr(int sock,const char * ifname,u8 * addr)81  int 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  
linux_set_ifhwaddr(int sock,const char * ifname,const u8 * addr)104  int 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  
linux_br_add(int sock,const char * brname)123  int 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  
linux_br_del(int sock,const char * brname)138  int 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  
linux_br_add_if(int sock,const char * brname,const char * ifname)150  int 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  		char in_br[IFNAMSIZ];
165  
166  		wpa_printf(MSG_DEBUG, "Could not add interface %s into bridge "
167  			   "%s: %s", ifname, brname, strerror(errno));
168  		errno = saved_errno;
169  
170  		/* If ioctl() returns EBUSY when adding an interface into the
171  		 * bridge, the interface might have already been added by an
172  		 * external operation, so check whether the interface is
173  		 * currently on the right bridge and ignore the error if it is.
174  		 */
175  		if (errno != EBUSY || linux_br_get(in_br, ifname) != 0 ||
176  		    os_strcmp(in_br, brname) != 0)
177  			return -1;
178  	}
179  
180  	return 0;
181  }
182  
183  
linux_br_del_if(int sock,const char * brname,const char * ifname)184  int linux_br_del_if(int sock, const char *brname, const char *ifname)
185  {
186  	struct ifreq ifr;
187  	int ifindex;
188  
189  	ifindex = if_nametoindex(ifname);
190  	if (ifindex == 0)
191  		return -1;
192  
193  	os_memset(&ifr, 0, sizeof(ifr));
194  	os_strlcpy(ifr.ifr_name, brname, IFNAMSIZ);
195  	ifr.ifr_ifindex = ifindex;
196  	if (ioctl(sock, SIOCBRDELIF, &ifr) < 0) {
197  		wpa_printf(MSG_DEBUG, "Could not remove interface %s from "
198  			   "bridge %s: %s", ifname, brname, strerror(errno));
199  		return -1;
200  	}
201  
202  	return 0;
203  }
204  
205  
linux_br_get(char * brname,const char * ifname)206  int linux_br_get(char *brname, const char *ifname)
207  {
208  	char path[128], brlink[128], *pos;
209  	ssize_t res;
210  
211  	os_snprintf(path, sizeof(path), "/sys/class/net/%s/brport/bridge",
212  		    ifname);
213  	res = readlink(path, brlink, sizeof(brlink));
214  	if (res < 0 || (size_t) res >= sizeof(brlink))
215  		return -1;
216  	brlink[res] = '\0';
217  	pos = os_strrchr(brlink, '/');
218  	if (pos == NULL)
219  		return -1;
220  	pos++;
221  	os_strlcpy(brname, pos, IFNAMSIZ);
222  	return 0;
223  }
224  
225  
linux_master_get(char * master_ifname,const char * ifname)226  int linux_master_get(char *master_ifname, const char *ifname)
227  {
228  	char buf[128], masterlink[128], *pos;
229  	ssize_t res;
230  
231  	/* check whether there is a master */
232  	os_snprintf(buf, sizeof(buf), "/sys/class/net/%s/master", ifname);
233  
234  	res = readlink(buf, masterlink, sizeof(masterlink));
235  	if (res < 0 || (size_t) res >= sizeof(masterlink))
236  		return -1;
237  
238  	masterlink[res] = '\0';
239  
240  	pos = os_strrchr(masterlink, '/');
241  	if (pos == NULL)
242  		return -1;
243  	pos++;
244  	os_strlcpy(master_ifname, pos, IFNAMSIZ);
245  	return 0;
246  }
247