xref: /freebsd/contrib/wpa/src/drivers/driver_wired.c (revision 3157ba2193f225049c28b3527f499567dae6ad14)
1*3157ba21SRui Paulo /*
2*3157ba21SRui Paulo  * WPA Supplicant - wired Ethernet driver interface
3*3157ba21SRui Paulo  * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
4*3157ba21SRui Paulo  *
5*3157ba21SRui Paulo  * This program is free software; you can redistribute it and/or modify
6*3157ba21SRui Paulo  * it under the terms of the GNU General Public License version 2 as
7*3157ba21SRui Paulo  * published by the Free Software Foundation.
8*3157ba21SRui Paulo  *
9*3157ba21SRui Paulo  * Alternatively, this software may be distributed under the terms of BSD
10*3157ba21SRui Paulo  * license.
11*3157ba21SRui Paulo  *
12*3157ba21SRui Paulo  * See README and COPYING for more details.
13*3157ba21SRui Paulo  */
14*3157ba21SRui Paulo 
15*3157ba21SRui Paulo #include "includes.h"
16*3157ba21SRui Paulo #include <sys/ioctl.h>
17*3157ba21SRui Paulo #include <net/if.h>
18*3157ba21SRui Paulo #ifdef __linux__
19*3157ba21SRui Paulo #include <netpacket/packet.h>
20*3157ba21SRui Paulo #endif /* __linux__ */
21*3157ba21SRui Paulo #if defined(__FreeBSD__) || defined(__DragonFly__)
22*3157ba21SRui Paulo #include <net/if_dl.h>
23*3157ba21SRui Paulo #endif /* defined(__FreeBSD__) || defined(__DragonFly__) */
24*3157ba21SRui Paulo 
25*3157ba21SRui Paulo #include "common.h"
26*3157ba21SRui Paulo #include "driver.h"
27*3157ba21SRui Paulo 
28*3157ba21SRui Paulo 
29*3157ba21SRui Paulo static const u8 pae_group_addr[ETH_ALEN] =
30*3157ba21SRui Paulo { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
31*3157ba21SRui Paulo 
32*3157ba21SRui Paulo 
33*3157ba21SRui Paulo struct wpa_driver_wired_data {
34*3157ba21SRui Paulo 	void *ctx;
35*3157ba21SRui Paulo 	int pf_sock;
36*3157ba21SRui Paulo 	char ifname[IFNAMSIZ + 1];
37*3157ba21SRui Paulo 	int membership, multi, iff_allmulti, iff_up;
38*3157ba21SRui Paulo };
39*3157ba21SRui Paulo 
40*3157ba21SRui Paulo 
41*3157ba21SRui Paulo static int wpa_driver_wired_get_ssid(void *priv, u8 *ssid)
42*3157ba21SRui Paulo {
43*3157ba21SRui Paulo 	ssid[0] = 0;
44*3157ba21SRui Paulo 	return 0;
45*3157ba21SRui Paulo }
46*3157ba21SRui Paulo 
47*3157ba21SRui Paulo 
48*3157ba21SRui Paulo static int wpa_driver_wired_get_bssid(void *priv, u8 *bssid)
49*3157ba21SRui Paulo {
50*3157ba21SRui Paulo 	/* Report PAE group address as the "BSSID" for wired connection. */
51*3157ba21SRui Paulo 	os_memcpy(bssid, pae_group_addr, ETH_ALEN);
52*3157ba21SRui Paulo 	return 0;
53*3157ba21SRui Paulo }
54*3157ba21SRui Paulo 
55*3157ba21SRui Paulo 
56*3157ba21SRui Paulo static int wpa_driver_wired_get_ifflags(const char *ifname, int *flags)
57*3157ba21SRui Paulo {
58*3157ba21SRui Paulo 	struct ifreq ifr;
59*3157ba21SRui Paulo 	int s;
60*3157ba21SRui Paulo 
61*3157ba21SRui Paulo 	s = socket(PF_INET, SOCK_DGRAM, 0);
62*3157ba21SRui Paulo 	if (s < 0) {
63*3157ba21SRui Paulo 		perror("socket");
64*3157ba21SRui Paulo 		return -1;
65*3157ba21SRui Paulo 	}
66*3157ba21SRui Paulo 
67*3157ba21SRui Paulo 	os_memset(&ifr, 0, sizeof(ifr));
68*3157ba21SRui Paulo 	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
69*3157ba21SRui Paulo 	if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
70*3157ba21SRui Paulo 		perror("ioctl[SIOCGIFFLAGS]");
71*3157ba21SRui Paulo 		close(s);
72*3157ba21SRui Paulo 		return -1;
73*3157ba21SRui Paulo 	}
74*3157ba21SRui Paulo 	close(s);
75*3157ba21SRui Paulo 	*flags = ifr.ifr_flags & 0xffff;
76*3157ba21SRui Paulo 	return 0;
77*3157ba21SRui Paulo }
78*3157ba21SRui Paulo 
79*3157ba21SRui Paulo 
80*3157ba21SRui Paulo static int wpa_driver_wired_set_ifflags(const char *ifname, int flags)
81*3157ba21SRui Paulo {
82*3157ba21SRui Paulo 	struct ifreq ifr;
83*3157ba21SRui Paulo 	int s;
84*3157ba21SRui Paulo 
85*3157ba21SRui Paulo 	s = socket(PF_INET, SOCK_DGRAM, 0);
86*3157ba21SRui Paulo 	if (s < 0) {
87*3157ba21SRui Paulo 		perror("socket");
88*3157ba21SRui Paulo 		return -1;
89*3157ba21SRui Paulo 	}
90*3157ba21SRui Paulo 
91*3157ba21SRui Paulo 	os_memset(&ifr, 0, sizeof(ifr));
92*3157ba21SRui Paulo 	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
93*3157ba21SRui Paulo 	ifr.ifr_flags = flags & 0xffff;
94*3157ba21SRui Paulo 	if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
95*3157ba21SRui Paulo 		perror("ioctl[SIOCSIFFLAGS]");
96*3157ba21SRui Paulo 		close(s);
97*3157ba21SRui Paulo 		return -1;
98*3157ba21SRui Paulo 	}
99*3157ba21SRui Paulo 	close(s);
100*3157ba21SRui Paulo 	return 0;
101*3157ba21SRui Paulo }
102*3157ba21SRui Paulo 
103*3157ba21SRui Paulo 
104*3157ba21SRui Paulo static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add)
105*3157ba21SRui Paulo {
106*3157ba21SRui Paulo 	struct ifreq ifr;
107*3157ba21SRui Paulo 	int s;
108*3157ba21SRui Paulo 
109*3157ba21SRui Paulo 	s = socket(PF_INET, SOCK_DGRAM, 0);
110*3157ba21SRui Paulo 	if (s < 0) {
111*3157ba21SRui Paulo 		perror("socket");
112*3157ba21SRui Paulo 		return -1;
113*3157ba21SRui Paulo 	}
114*3157ba21SRui Paulo 
115*3157ba21SRui Paulo 	os_memset(&ifr, 0, sizeof(ifr));
116*3157ba21SRui Paulo 	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
117*3157ba21SRui Paulo #ifdef __linux__
118*3157ba21SRui Paulo 	ifr.ifr_hwaddr.sa_family = AF_UNSPEC;
119*3157ba21SRui Paulo 	os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN);
120*3157ba21SRui Paulo #endif /* __linux__ */
121*3157ba21SRui Paulo #if defined(__FreeBSD__) || defined(__DragonFly__)
122*3157ba21SRui Paulo 	{
123*3157ba21SRui Paulo 		struct sockaddr_dl *dlp;
124*3157ba21SRui Paulo 		dlp = (struct sockaddr_dl *) &ifr.ifr_addr;
125*3157ba21SRui Paulo 		dlp->sdl_len = sizeof(struct sockaddr_dl);
126*3157ba21SRui Paulo 		dlp->sdl_family = AF_LINK;
127*3157ba21SRui Paulo 		dlp->sdl_index = 0;
128*3157ba21SRui Paulo 		dlp->sdl_nlen = 0;
129*3157ba21SRui Paulo 		dlp->sdl_alen = ETH_ALEN;
130*3157ba21SRui Paulo 		dlp->sdl_slen = 0;
131*3157ba21SRui Paulo 		os_memcpy(LLADDR(dlp), addr, ETH_ALEN);
132*3157ba21SRui Paulo 	}
133*3157ba21SRui Paulo #endif /* defined(__FreeBSD__) || defined(__DragonFly__) */
134*3157ba21SRui Paulo #if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
135*3157ba21SRui Paulo 	{
136*3157ba21SRui Paulo 		struct sockaddr *sap;
137*3157ba21SRui Paulo 		sap = (struct sockaddr *) &ifr.ifr_addr;
138*3157ba21SRui Paulo 		sap->sa_len = sizeof(struct sockaddr);
139*3157ba21SRui Paulo 		sap->sa_family = AF_UNSPEC;
140*3157ba21SRui Paulo 		os_memcpy(sap->sa_data, addr, ETH_ALEN);
141*3157ba21SRui Paulo 	}
142*3157ba21SRui Paulo #endif /* defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) */
143*3157ba21SRui Paulo 
144*3157ba21SRui Paulo 	if (ioctl(s, add ? SIOCADDMULTI : SIOCDELMULTI, (caddr_t) &ifr) < 0) {
145*3157ba21SRui Paulo 		perror("ioctl[SIOC{ADD/DEL}MULTI]");
146*3157ba21SRui Paulo 		close(s);
147*3157ba21SRui Paulo 		return -1;
148*3157ba21SRui Paulo 	}
149*3157ba21SRui Paulo 	close(s);
150*3157ba21SRui Paulo 	return 0;
151*3157ba21SRui Paulo }
152*3157ba21SRui Paulo 
153*3157ba21SRui Paulo 
154*3157ba21SRui Paulo static int wpa_driver_wired_membership(struct wpa_driver_wired_data *drv,
155*3157ba21SRui Paulo 				       const u8 *addr, int add)
156*3157ba21SRui Paulo {
157*3157ba21SRui Paulo #ifdef __linux__
158*3157ba21SRui Paulo 	struct packet_mreq mreq;
159*3157ba21SRui Paulo 
160*3157ba21SRui Paulo 	if (drv->pf_sock == -1)
161*3157ba21SRui Paulo 		return -1;
162*3157ba21SRui Paulo 
163*3157ba21SRui Paulo 	os_memset(&mreq, 0, sizeof(mreq));
164*3157ba21SRui Paulo 	mreq.mr_ifindex = if_nametoindex(drv->ifname);
165*3157ba21SRui Paulo 	mreq.mr_type = PACKET_MR_MULTICAST;
166*3157ba21SRui Paulo 	mreq.mr_alen = ETH_ALEN;
167*3157ba21SRui Paulo 	os_memcpy(mreq.mr_address, addr, ETH_ALEN);
168*3157ba21SRui Paulo 
169*3157ba21SRui Paulo 	if (setsockopt(drv->pf_sock, SOL_PACKET,
170*3157ba21SRui Paulo 		       add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP,
171*3157ba21SRui Paulo 		       &mreq, sizeof(mreq)) < 0) {
172*3157ba21SRui Paulo 		perror("setsockopt");
173*3157ba21SRui Paulo 		return -1;
174*3157ba21SRui Paulo 	}
175*3157ba21SRui Paulo 	return 0;
176*3157ba21SRui Paulo #else /* __linux__ */
177*3157ba21SRui Paulo 	return -1;
178*3157ba21SRui Paulo #endif /* __linux__ */
179*3157ba21SRui Paulo }
180*3157ba21SRui Paulo 
181*3157ba21SRui Paulo 
182*3157ba21SRui Paulo static void * wpa_driver_wired_init(void *ctx, const char *ifname)
183*3157ba21SRui Paulo {
184*3157ba21SRui Paulo 	struct wpa_driver_wired_data *drv;
185*3157ba21SRui Paulo 	int flags;
186*3157ba21SRui Paulo 
187*3157ba21SRui Paulo 	drv = os_zalloc(sizeof(*drv));
188*3157ba21SRui Paulo 	if (drv == NULL)
189*3157ba21SRui Paulo 		return NULL;
190*3157ba21SRui Paulo 	os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
191*3157ba21SRui Paulo 	drv->ctx = ctx;
192*3157ba21SRui Paulo 
193*3157ba21SRui Paulo #ifdef __linux__
194*3157ba21SRui Paulo 	drv->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
195*3157ba21SRui Paulo 	if (drv->pf_sock < 0)
196*3157ba21SRui Paulo 		perror("socket(PF_PACKET)");
197*3157ba21SRui Paulo #else /* __linux__ */
198*3157ba21SRui Paulo 	drv->pf_sock = -1;
199*3157ba21SRui Paulo #endif /* __linux__ */
200*3157ba21SRui Paulo 
201*3157ba21SRui Paulo 	if (wpa_driver_wired_get_ifflags(ifname, &flags) == 0 &&
202*3157ba21SRui Paulo 	    !(flags & IFF_UP) &&
203*3157ba21SRui Paulo 	    wpa_driver_wired_set_ifflags(ifname, flags | IFF_UP) == 0) {
204*3157ba21SRui Paulo 		drv->iff_up = 1;
205*3157ba21SRui Paulo 	}
206*3157ba21SRui Paulo 
207*3157ba21SRui Paulo 	if (wpa_driver_wired_membership(drv, pae_group_addr, 1) == 0) {
208*3157ba21SRui Paulo 		wpa_printf(MSG_DEBUG, "%s: Added multicast membership with "
209*3157ba21SRui Paulo 			   "packet socket", __func__);
210*3157ba21SRui Paulo 		drv->membership = 1;
211*3157ba21SRui Paulo 	} else if (wpa_driver_wired_multi(ifname, pae_group_addr, 1) == 0) {
212*3157ba21SRui Paulo 		wpa_printf(MSG_DEBUG, "%s: Added multicast membership with "
213*3157ba21SRui Paulo 			   "SIOCADDMULTI", __func__);
214*3157ba21SRui Paulo 		drv->multi = 1;
215*3157ba21SRui Paulo 	} else if (wpa_driver_wired_get_ifflags(ifname, &flags) < 0) {
216*3157ba21SRui Paulo 		wpa_printf(MSG_INFO, "%s: Could not get interface "
217*3157ba21SRui Paulo 			   "flags", __func__);
218*3157ba21SRui Paulo 		os_free(drv);
219*3157ba21SRui Paulo 		return NULL;
220*3157ba21SRui Paulo 	} else if (flags & IFF_ALLMULTI) {
221*3157ba21SRui Paulo 		wpa_printf(MSG_DEBUG, "%s: Interface is already configured "
222*3157ba21SRui Paulo 			   "for multicast", __func__);
223*3157ba21SRui Paulo 	} else if (wpa_driver_wired_set_ifflags(ifname,
224*3157ba21SRui Paulo 						flags | IFF_ALLMULTI) < 0) {
225*3157ba21SRui Paulo 		wpa_printf(MSG_INFO, "%s: Failed to enable allmulti",
226*3157ba21SRui Paulo 			   __func__);
227*3157ba21SRui Paulo 		os_free(drv);
228*3157ba21SRui Paulo 		return NULL;
229*3157ba21SRui Paulo 	} else {
230*3157ba21SRui Paulo 		wpa_printf(MSG_DEBUG, "%s: Enabled allmulti mode",
231*3157ba21SRui Paulo 			   __func__);
232*3157ba21SRui Paulo 		drv->iff_allmulti = 1;
233*3157ba21SRui Paulo 	}
234*3157ba21SRui Paulo 
235*3157ba21SRui Paulo 	return drv;
236*3157ba21SRui Paulo }
237*3157ba21SRui Paulo 
238*3157ba21SRui Paulo 
239*3157ba21SRui Paulo static void wpa_driver_wired_deinit(void *priv)
240*3157ba21SRui Paulo {
241*3157ba21SRui Paulo 	struct wpa_driver_wired_data *drv = priv;
242*3157ba21SRui Paulo 	int flags;
243*3157ba21SRui Paulo 
244*3157ba21SRui Paulo 	if (drv->membership &&
245*3157ba21SRui Paulo 	    wpa_driver_wired_membership(drv, pae_group_addr, 0) < 0) {
246*3157ba21SRui Paulo 		wpa_printf(MSG_DEBUG, "%s: Failed to remove PAE multicast "
247*3157ba21SRui Paulo 			   "group (PACKET)", __func__);
248*3157ba21SRui Paulo 	}
249*3157ba21SRui Paulo 
250*3157ba21SRui Paulo 	if (drv->multi &&
251*3157ba21SRui Paulo 	    wpa_driver_wired_multi(drv->ifname, pae_group_addr, 0) < 0) {
252*3157ba21SRui Paulo 		wpa_printf(MSG_DEBUG, "%s: Failed to remove PAE multicast "
253*3157ba21SRui Paulo 			   "group (SIOCDELMULTI)", __func__);
254*3157ba21SRui Paulo 	}
255*3157ba21SRui Paulo 
256*3157ba21SRui Paulo 	if (drv->iff_allmulti &&
257*3157ba21SRui Paulo 	    (wpa_driver_wired_get_ifflags(drv->ifname, &flags) < 0 ||
258*3157ba21SRui Paulo 	     wpa_driver_wired_set_ifflags(drv->ifname,
259*3157ba21SRui Paulo 					  flags & ~IFF_ALLMULTI) < 0)) {
260*3157ba21SRui Paulo 		wpa_printf(MSG_DEBUG, "%s: Failed to disable allmulti mode",
261*3157ba21SRui Paulo 			   __func__);
262*3157ba21SRui Paulo 	}
263*3157ba21SRui Paulo 
264*3157ba21SRui Paulo 	if (drv->iff_up &&
265*3157ba21SRui Paulo 	    wpa_driver_wired_get_ifflags(drv->ifname, &flags) == 0 &&
266*3157ba21SRui Paulo 	    (flags & IFF_UP) &&
267*3157ba21SRui Paulo 	    wpa_driver_wired_set_ifflags(drv->ifname, flags & ~IFF_UP) < 0) {
268*3157ba21SRui Paulo 		wpa_printf(MSG_DEBUG, "%s: Failed to set the interface down",
269*3157ba21SRui Paulo 			   __func__);
270*3157ba21SRui Paulo 	}
271*3157ba21SRui Paulo 
272*3157ba21SRui Paulo 	if (drv->pf_sock != -1)
273*3157ba21SRui Paulo 		close(drv->pf_sock);
274*3157ba21SRui Paulo 
275*3157ba21SRui Paulo 	os_free(drv);
276*3157ba21SRui Paulo }
277*3157ba21SRui Paulo 
278*3157ba21SRui Paulo 
279*3157ba21SRui Paulo const struct wpa_driver_ops wpa_driver_wired_ops = {
280*3157ba21SRui Paulo 	.name = "wired",
281*3157ba21SRui Paulo 	.desc = "wpa_supplicant wired Ethernet driver",
282*3157ba21SRui Paulo 	.get_ssid = wpa_driver_wired_get_ssid,
283*3157ba21SRui Paulo 	.get_bssid = wpa_driver_wired_get_bssid,
284*3157ba21SRui Paulo 	.init = wpa_driver_wired_init,
285*3157ba21SRui Paulo 	.deinit = wpa_driver_wired_deinit,
286*3157ba21SRui Paulo };
287