1/*
2 * Common functions for Wired Ethernet driver interfaces
3 * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
4 * Copyright (c) 2004, Gunter Burchardt <tira@isx.de>
5 *
6 * This software may be distributed under the terms of the BSD license.
7 * See README for more details.
8 */
9
10#include "includes.h"
11
12#include "common.h"
13#include "eloop.h"
14#include "driver.h"
15#include "driver_wired_common.h"
16
17#include <sys/ioctl.h>
18#include <net/if.h>
19#ifdef __linux__
20#include <netpacket/packet.h>
21#include <net/if_arp.h>
22#include <net/if.h>
23#endif /* __linux__ */
24#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
25#include <net/if_dl.h>
26#include <net/if_media.h>
27#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) */
28#ifdef __sun__
29#include <sys/sockio.h>
30#endif /* __sun__ */
31
32
33static int driver_wired_get_ifflags(const char *ifname, int *flags)
34{
35	struct ifreq ifr;
36	int s;
37
38	s = socket(PF_INET, SOCK_DGRAM, 0);
39	if (s < 0) {
40		wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
41		return -1;
42	}
43
44	os_memset(&ifr, 0, sizeof(ifr));
45	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
46	if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
47		wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s",
48			   strerror(errno));
49		close(s);
50		return -1;
51	}
52	close(s);
53	*flags = ifr.ifr_flags & 0xffff;
54	return 0;
55}
56
57
58static int driver_wired_set_ifflags(const char *ifname, int flags)
59{
60	struct ifreq ifr;
61	int s;
62
63	s = socket(PF_INET, SOCK_DGRAM, 0);
64	if (s < 0) {
65		wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
66		return -1;
67	}
68
69	os_memset(&ifr, 0, sizeof(ifr));
70	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
71	ifr.ifr_flags = flags & 0xffff;
72	if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
73		wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s",
74			   strerror(errno));
75		close(s);
76		return -1;
77	}
78	close(s);
79	return 0;
80}
81
82
83static int driver_wired_multi(const char *ifname, const u8 *addr, int add)
84{
85	struct ifreq ifr;
86	int s;
87
88#ifdef __sun__
89	return -1;
90#endif /* __sun__ */
91
92	s = socket(PF_INET, SOCK_DGRAM, 0);
93	if (s < 0) {
94		wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
95		return -1;
96	}
97
98	os_memset(&ifr, 0, sizeof(ifr));
99	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
100#ifdef __linux__
101	ifr.ifr_hwaddr.sa_family = AF_UNSPEC;
102	os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN);
103#endif /* __linux__ */
104#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
105	{
106		struct sockaddr_dl *dlp;
107
108		dlp = (struct sockaddr_dl *) &ifr.ifr_addr;
109		dlp->sdl_len = sizeof(struct sockaddr_dl);
110		dlp->sdl_family = AF_LINK;
111		dlp->sdl_index = 0;
112		dlp->sdl_nlen = 0;
113		dlp->sdl_alen = ETH_ALEN;
114		dlp->sdl_slen = 0;
115		os_memcpy(LLADDR(dlp), addr, ETH_ALEN);
116	}
117#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
118#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
119	{
120		struct sockaddr *sap;
121
122		sap = (struct sockaddr *) &ifr.ifr_addr;
123		sap->sa_len = sizeof(struct sockaddr);
124		sap->sa_family = AF_UNSPEC;
125		os_memcpy(sap->sa_data, addr, ETH_ALEN);
126	}
127#endif /* defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) */
128
129	if (ioctl(s, add ? SIOCADDMULTI : SIOCDELMULTI, (caddr_t) &ifr) < 0) {
130		wpa_printf(MSG_ERROR, "ioctl[SIOC{ADD/DEL}MULTI]: %s",
131			   strerror(errno));
132		close(s);
133		return -1;
134	}
135	close(s);
136	return 0;
137}
138
139
140int wired_multicast_membership(int sock, int ifindex, const u8 *addr, int add)
141{
142#ifdef __linux__
143	struct packet_mreq mreq;
144
145	if (sock < 0)
146		return -1;
147
148	os_memset(&mreq, 0, sizeof(mreq));
149	mreq.mr_ifindex = ifindex;
150	mreq.mr_type = PACKET_MR_MULTICAST;
151	mreq.mr_alen = ETH_ALEN;
152	os_memcpy(mreq.mr_address, addr, ETH_ALEN);
153
154	if (setsockopt(sock, SOL_PACKET,
155		       add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP,
156		       &mreq, sizeof(mreq)) < 0) {
157		wpa_printf(MSG_ERROR, "setsockopt: %s", strerror(errno));
158		return -1;
159	}
160	return 0;
161#else /* __linux__ */
162	return -1;
163#endif /* __linux__ */
164}
165
166
167int driver_wired_get_ssid(void *priv, u8 *ssid)
168{
169	ssid[0] = 0;
170	return 0;
171}
172
173
174int driver_wired_get_bssid(void *priv, u8 *bssid)
175{
176	/* Report PAE group address as the "BSSID" for wired connection. */
177	os_memcpy(bssid, pae_group_addr, ETH_ALEN);
178	return 0;
179}
180
181
182int driver_wired_get_capa(void *priv, struct wpa_driver_capa *capa)
183{
184	os_memset(capa, 0, sizeof(*capa));
185	capa->flags = WPA_DRIVER_FLAGS_WIRED;
186	return 0;
187}
188
189
190#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
191static int driver_wired_get_ifstatus(const char *ifname, int *status)
192{
193	struct ifmediareq ifmr;
194	int s;
195
196	s = socket(PF_INET, SOCK_DGRAM, 0);
197	if (s < 0) {
198		wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
199		return -1;
200	}
201
202	os_memset(&ifmr, 0, sizeof(ifmr));
203	os_strlcpy(ifmr.ifm_name, ifname, IFNAMSIZ);
204	if (ioctl(s, SIOCGIFMEDIA, (caddr_t) &ifmr) < 0) {
205		wpa_printf(MSG_ERROR, "ioctl[SIOCGIFMEDIA]: %s",
206			   strerror(errno));
207		close(s);
208		return -1;
209	}
210	close(s);
211	*status = (ifmr.ifm_status & (IFM_ACTIVE | IFM_AVALID)) ==
212		(IFM_ACTIVE | IFM_AVALID);
213
214	return 0;
215}
216#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
217
218
219int driver_wired_init_common(struct driver_wired_common_data *common,
220			     const char *ifname, void *ctx)
221{
222	int flags;
223
224	os_strlcpy(common->ifname, ifname, sizeof(common->ifname));
225	common->ctx = ctx;
226
227#ifdef __linux__
228	common->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
229	if (common->pf_sock < 0)
230		wpa_printf(MSG_ERROR, "socket(PF_PACKET): %s", strerror(errno));
231#else /* __linux__ */
232	common->pf_sock = -1;
233#endif /* __linux__ */
234
235	if (driver_wired_get_ifflags(ifname, &flags) == 0 &&
236	    !(flags & IFF_UP) &&
237	    driver_wired_set_ifflags(ifname, flags | IFF_UP) == 0)
238		common->iff_up = 1;
239
240	if (wired_multicast_membership(common->pf_sock,
241				       if_nametoindex(common->ifname),
242				       pae_group_addr, 1) == 0) {
243		wpa_printf(MSG_DEBUG,
244			   "%s: Added multicast membership with packet socket",
245			   __func__);
246		common->membership = 1;
247	} else if (driver_wired_multi(ifname, pae_group_addr, 1) == 0) {
248		wpa_printf(MSG_DEBUG,
249			   "%s: Added multicast membership with SIOCADDMULTI",
250			   __func__);
251		common->multi = 1;
252	} else if (driver_wired_get_ifflags(ifname, &flags) < 0) {
253		wpa_printf(MSG_INFO, "%s: Could not get interface flags",
254			   __func__);
255		return -1;
256	} else if (flags & IFF_ALLMULTI) {
257		wpa_printf(MSG_DEBUG,
258			   "%s: Interface is already configured for multicast",
259			   __func__);
260	} else if (driver_wired_set_ifflags(ifname,
261						flags | IFF_ALLMULTI) < 0) {
262		wpa_printf(MSG_INFO, "%s: Failed to enable allmulti", __func__);
263		return -1;
264	} else {
265		wpa_printf(MSG_DEBUG, "%s: Enabled allmulti mode", __func__);
266		common->iff_allmulti = 1;
267	}
268#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
269	{
270		int status;
271
272		wpa_printf(MSG_DEBUG, "%s: waiting for link to become active",
273			   __func__);
274		while (driver_wired_get_ifstatus(ifname, &status) == 0 &&
275		       status == 0)
276			sleep(1);
277	}
278#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
279
280	return 0;
281}
282
283
284void driver_wired_deinit_common(struct driver_wired_common_data *common)
285{
286	int flags;
287
288	if (common->membership &&
289	    wired_multicast_membership(common->pf_sock,
290				       if_nametoindex(common->ifname),
291				       pae_group_addr, 0) < 0) {
292		wpa_printf(MSG_DEBUG,
293			   "%s: Failed to remove PAE multicast group (PACKET)",
294			   __func__);
295	}
296
297	if (common->multi &&
298	    driver_wired_multi(common->ifname, pae_group_addr, 0) < 0) {
299		wpa_printf(MSG_DEBUG,
300			   "%s: Failed to remove PAE multicast group (SIOCDELMULTI)",
301			   __func__);
302	}
303
304	if (common->iff_allmulti &&
305	    (driver_wired_get_ifflags(common->ifname, &flags) < 0 ||
306	     driver_wired_set_ifflags(common->ifname,
307				      flags & ~IFF_ALLMULTI) < 0)) {
308		wpa_printf(MSG_DEBUG, "%s: Failed to disable allmulti mode",
309			   __func__);
310	}
311
312	if (common->iff_up &&
313	    driver_wired_get_ifflags(common->ifname, &flags) == 0 &&
314	    (flags & IFF_UP) &&
315	    driver_wired_set_ifflags(common->ifname, flags & ~IFF_UP) < 0) {
316		wpa_printf(MSG_DEBUG, "%s: Failed to set the interface down",
317			   __func__);
318	}
319
320	if (common->pf_sock != -1)
321		close(common->pf_sock);
322}
323