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  
driver_wired_get_ifflags(const char * ifname,int * flags)33  static 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  
driver_wired_set_ifflags(const char * ifname,int flags)58  static 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  
driver_wired_multi(const char * ifname,const u8 * addr,int add)83  static 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  
wired_multicast_membership(int sock,int ifindex,const u8 * addr,int add)140  int 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  
driver_wired_get_ssid(void * priv,u8 * ssid)167  int driver_wired_get_ssid(void *priv, u8 *ssid)
168  {
169  	ssid[0] = 0;
170  	return 0;
171  }
172  
173  
driver_wired_get_bssid(void * priv,u8 * bssid)174  int 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  
driver_wired_get_capa(void * priv,struct wpa_driver_capa * capa)182  int 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__)
driver_wired_get_ifstatus(const char * ifname,int * status)191  static 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  
driver_wired_init_common(struct driver_wired_common_data * common,const char * ifname,void * ctx)219  int 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  
driver_wired_deinit_common(struct driver_wired_common_data * common)284  void 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