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