1 /*
2  * Wired Ethernet driver interface
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 #include <sys/ioctl.h>
12 #include <net/if.h>
13 #ifdef __linux__
14 #include <netpacket/packet.h>
15 #include <net/if_arp.h>
16 #include <net/if.h>
17 #endif /* __linux__ */
18 #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
19 #include <net/if_dl.h>
20 #include <net/if_media.h>
21 #endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) */
22 #ifdef __sun__
23 #include <sys/sockio.h>
24 #endif /* __sun__ */
25 
26 #include "common.h"
27 #include "eloop.h"
28 #include "driver.h"
29 
30 #ifdef _MSC_VER
31 #pragma pack(push, 1)
32 #endif /* _MSC_VER */
33 
34 struct ieee8023_hdr {
35 	u8 dest[6];
36 	u8 src[6];
37 	u16 ethertype;
38 } STRUCT_PACKED;
39 
40 #ifdef _MSC_VER
41 #pragma pack(pop)
42 #endif /* _MSC_VER */
43 
44 static const u8 pae_group_addr[ETH_ALEN] =
45 { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
46 
47 
48 struct wpa_driver_wired_data {
49 	char ifname[IFNAMSIZ + 1];
50 	void *ctx;
51 
52 	int sock; /* raw packet socket for driver access */
53 	int dhcp_sock; /* socket for dhcp packets */
54 	int use_pae_group_addr;
55 
56 	int pf_sock;
57 	int membership, multi, iff_allmulti, iff_up;
58 };
59 
60 
61 /* TODO: detecting new devices should eventually be changed from using DHCP
62  * snooping to trigger on any packet from a new layer 2 MAC address, e.g.,
63  * based on ebtables, etc. */
64 
65 struct dhcp_message {
66 	u_int8_t op;
67 	u_int8_t htype;
68 	u_int8_t hlen;
69 	u_int8_t hops;
70 	u_int32_t xid;
71 	u_int16_t secs;
72 	u_int16_t flags;
73 	u_int32_t ciaddr;
74 	u_int32_t yiaddr;
75 	u_int32_t siaddr;
76 	u_int32_t giaddr;
77 	u_int8_t chaddr[16];
78 	u_int8_t sname[64];
79 	u_int8_t file[128];
80 	u_int32_t cookie;
81 	u_int8_t options[308]; /* 312 - cookie */
82 };
83 
84 
85 static int wired_multicast_membership(int sock, int ifindex,
86 				      const u8 *addr, int add)
87 {
88 #ifdef __linux__
89 	struct packet_mreq mreq;
90 
91 	if (sock < 0)
92 		return -1;
93 
94 	os_memset(&mreq, 0, sizeof(mreq));
95 	mreq.mr_ifindex = ifindex;
96 	mreq.mr_type = PACKET_MR_MULTICAST;
97 	mreq.mr_alen = ETH_ALEN;
98 	os_memcpy(mreq.mr_address, addr, ETH_ALEN);
99 
100 	if (setsockopt(sock, SOL_PACKET,
101 		       add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP,
102 		       &mreq, sizeof(mreq)) < 0) {
103 		perror("setsockopt");
104 		return -1;
105 	}
106 	return 0;
107 #else /* __linux__ */
108 	return -1;
109 #endif /* __linux__ */
110 }
111 
112 
113 #ifdef __linux__
114 static void handle_data(void *ctx, unsigned char *buf, size_t len)
115 {
116 #ifdef HOSTAPD
117 	struct ieee8023_hdr *hdr;
118 	u8 *pos, *sa;
119 	size_t left;
120 	union wpa_event_data event;
121 
122 	/* must contain at least ieee8023_hdr 6 byte source, 6 byte dest,
123 	 * 2 byte ethertype */
124 	if (len < 14) {
125 		wpa_printf(MSG_MSGDUMP, "handle_data: too short (%lu)",
126 			   (unsigned long) len);
127 		return;
128 	}
129 
130 	hdr = (struct ieee8023_hdr *) buf;
131 
132 	switch (ntohs(hdr->ethertype)) {
133 		case ETH_P_PAE:
134 			wpa_printf(MSG_MSGDUMP, "Received EAPOL packet");
135 			sa = hdr->src;
136 			os_memset(&event, 0, sizeof(event));
137 			event.new_sta.addr = sa;
138 			wpa_supplicant_event(ctx, EVENT_NEW_STA, &event);
139 
140 			pos = (u8 *) (hdr + 1);
141 			left = len - sizeof(*hdr);
142 			drv_event_eapol_rx(ctx, sa, pos, left);
143 		break;
144 
145 	default:
146 		wpa_printf(MSG_DEBUG, "Unknown ethertype 0x%04x in data frame",
147 			   ntohs(hdr->ethertype));
148 		break;
149 	}
150 #endif /* HOSTAPD */
151 }
152 
153 
154 static void handle_read(int sock, void *eloop_ctx, void *sock_ctx)
155 {
156 	int len;
157 	unsigned char buf[3000];
158 
159 	len = recv(sock, buf, sizeof(buf), 0);
160 	if (len < 0) {
161 		perror("recv");
162 		return;
163 	}
164 
165 	handle_data(eloop_ctx, buf, len);
166 }
167 
168 
169 static void handle_dhcp(int sock, void *eloop_ctx, void *sock_ctx)
170 {
171 	int len;
172 	unsigned char buf[3000];
173 	struct dhcp_message *msg;
174 	u8 *mac_address;
175 	union wpa_event_data event;
176 
177 	len = recv(sock, buf, sizeof(buf), 0);
178 	if (len < 0) {
179 		perror("recv");
180 		return;
181 	}
182 
183 	/* must contain at least dhcp_message->chaddr */
184 	if (len < 44) {
185 		wpa_printf(MSG_MSGDUMP, "handle_dhcp: too short (%d)", len);
186 		return;
187 	}
188 
189 	msg = (struct dhcp_message *) buf;
190 	mac_address = (u8 *) &(msg->chaddr);
191 
192 	wpa_printf(MSG_MSGDUMP, "Got DHCP broadcast packet from " MACSTR,
193 		   MAC2STR(mac_address));
194 
195 	os_memset(&event, 0, sizeof(event));
196 	event.new_sta.addr = mac_address;
197 	wpa_supplicant_event(eloop_ctx, EVENT_NEW_STA, &event);
198 }
199 #endif /* __linux__ */
200 
201 
202 static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr)
203 {
204 #ifdef __linux__
205 	struct ifreq ifr;
206 	struct sockaddr_ll addr;
207 	struct sockaddr_in addr2;
208 	int n = 1;
209 
210 	drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE));
211 	if (drv->sock < 0) {
212 		perror("socket[PF_PACKET,SOCK_RAW]");
213 		return -1;
214 	}
215 
216 	if (eloop_register_read_sock(drv->sock, handle_read, drv->ctx, NULL)) {
217 		printf("Could not register read socket\n");
218 		return -1;
219 	}
220 
221 	os_memset(&ifr, 0, sizeof(ifr));
222 	os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
223 	if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) {
224 		perror("ioctl(SIOCGIFINDEX)");
225 		return -1;
226 	}
227 
228 	os_memset(&addr, 0, sizeof(addr));
229 	addr.sll_family = AF_PACKET;
230 	addr.sll_ifindex = ifr.ifr_ifindex;
231 	wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d",
232 		   addr.sll_ifindex);
233 
234 	if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
235 		perror("bind");
236 		return -1;
237 	}
238 
239 	/* filter multicast address */
240 	if (wired_multicast_membership(drv->sock, ifr.ifr_ifindex,
241 				       pae_group_addr, 1) < 0) {
242 		wpa_printf(MSG_ERROR, "wired: Failed to add multicast group "
243 			   "membership");
244 		return -1;
245 	}
246 
247 	os_memset(&ifr, 0, sizeof(ifr));
248 	os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
249 	if (ioctl(drv->sock, SIOCGIFHWADDR, &ifr) != 0) {
250 		perror("ioctl(SIOCGIFHWADDR)");
251 		return -1;
252 	}
253 
254 	if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
255 		printf("Invalid HW-addr family 0x%04x\n",
256 		       ifr.ifr_hwaddr.sa_family);
257 		return -1;
258 	}
259 	os_memcpy(own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
260 
261 	/* setup dhcp listen socket for sta detection */
262 	if ((drv->dhcp_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
263 		perror("socket call failed for dhcp");
264 		return -1;
265 	}
266 
267 	if (eloop_register_read_sock(drv->dhcp_sock, handle_dhcp, drv->ctx,
268 				     NULL)) {
269 		printf("Could not register read socket\n");
270 		return -1;
271 	}
272 
273 	os_memset(&addr2, 0, sizeof(addr2));
274 	addr2.sin_family = AF_INET;
275 	addr2.sin_port = htons(67);
276 	addr2.sin_addr.s_addr = INADDR_ANY;
277 
278 	if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_REUSEADDR, (char *) &n,
279 		       sizeof(n)) == -1) {
280 		perror("setsockopt[SOL_SOCKET,SO_REUSEADDR]");
281 		return -1;
282 	}
283 	if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BROADCAST, (char *) &n,
284 		       sizeof(n)) == -1) {
285 		perror("setsockopt[SOL_SOCKET,SO_BROADCAST]");
286 		return -1;
287 	}
288 
289 	os_memset(&ifr, 0, sizeof(ifr));
290 	os_strlcpy(ifr.ifr_ifrn.ifrn_name, drv->ifname, IFNAMSIZ);
291 	if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BINDTODEVICE,
292 		       (char *) &ifr, sizeof(ifr)) < 0) {
293 		perror("setsockopt[SOL_SOCKET,SO_BINDTODEVICE]");
294 		return -1;
295 	}
296 
297 	if (bind(drv->dhcp_sock, (struct sockaddr *) &addr2,
298 		 sizeof(struct sockaddr)) == -1) {
299 		perror("bind");
300 		return -1;
301 	}
302 
303 	return 0;
304 #else /* __linux__ */
305 	return -1;
306 #endif /* __linux__ */
307 }
308 
309 
310 static int wired_send_eapol(void *priv, const u8 *addr,
311 			    const u8 *data, size_t data_len, int encrypt,
312 			    const u8 *own_addr, u32 flags)
313 {
314 	struct wpa_driver_wired_data *drv = priv;
315 	struct ieee8023_hdr *hdr;
316 	size_t len;
317 	u8 *pos;
318 	int res;
319 
320 	len = sizeof(*hdr) + data_len;
321 	hdr = os_zalloc(len);
322 	if (hdr == NULL) {
323 		printf("malloc() failed for wired_send_eapol(len=%lu)\n",
324 		       (unsigned long) len);
325 		return -1;
326 	}
327 
328 	os_memcpy(hdr->dest, drv->use_pae_group_addr ? pae_group_addr : addr,
329 		  ETH_ALEN);
330 	os_memcpy(hdr->src, own_addr, ETH_ALEN);
331 	hdr->ethertype = htons(ETH_P_PAE);
332 
333 	pos = (u8 *) (hdr + 1);
334 	os_memcpy(pos, data, data_len);
335 
336 	res = send(drv->sock, (u8 *) hdr, len, 0);
337 	os_free(hdr);
338 
339 	if (res < 0) {
340 		perror("wired_send_eapol: send");
341 		printf("wired_send_eapol - packet len: %lu - failed\n",
342 		       (unsigned long) len);
343 	}
344 
345 	return res;
346 }
347 
348 
349 static void * wired_driver_hapd_init(struct hostapd_data *hapd,
350 				     struct wpa_init_params *params)
351 {
352 	struct wpa_driver_wired_data *drv;
353 
354 	drv = os_zalloc(sizeof(struct wpa_driver_wired_data));
355 	if (drv == NULL) {
356 		printf("Could not allocate memory for wired driver data\n");
357 		return NULL;
358 	}
359 
360 	drv->ctx = hapd;
361 	os_strlcpy(drv->ifname, params->ifname, sizeof(drv->ifname));
362 	drv->use_pae_group_addr = params->use_pae_group_addr;
363 
364 	if (wired_init_sockets(drv, params->own_addr)) {
365 		os_free(drv);
366 		return NULL;
367 	}
368 
369 	return drv;
370 }
371 
372 
373 static void wired_driver_hapd_deinit(void *priv)
374 {
375 	struct wpa_driver_wired_data *drv = priv;
376 
377 	if (drv->sock >= 0)
378 		close(drv->sock);
379 
380 	if (drv->dhcp_sock >= 0)
381 		close(drv->dhcp_sock);
382 
383 	os_free(drv);
384 }
385 
386 
387 static int wpa_driver_wired_get_ssid(void *priv, u8 *ssid)
388 {
389 	ssid[0] = 0;
390 	return 0;
391 }
392 
393 
394 static int wpa_driver_wired_get_bssid(void *priv, u8 *bssid)
395 {
396 	/* Report PAE group address as the "BSSID" for wired connection. */
397 	os_memcpy(bssid, pae_group_addr, ETH_ALEN);
398 	return 0;
399 }
400 
401 
402 static int wpa_driver_wired_get_capa(void *priv, struct wpa_driver_capa *capa)
403 {
404 	os_memset(capa, 0, sizeof(*capa));
405 	capa->flags = WPA_DRIVER_FLAGS_WIRED;
406 	return 0;
407 }
408 
409 
410 static int wpa_driver_wired_get_ifflags(const char *ifname, int *flags)
411 {
412 	struct ifreq ifr;
413 	int s;
414 
415 	s = socket(PF_INET, SOCK_DGRAM, 0);
416 	if (s < 0) {
417 		perror("socket");
418 		return -1;
419 	}
420 
421 	os_memset(&ifr, 0, sizeof(ifr));
422 	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
423 	if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
424 		perror("ioctl[SIOCGIFFLAGS]");
425 		close(s);
426 		return -1;
427 	}
428 	close(s);
429 	*flags = ifr.ifr_flags & 0xffff;
430 	return 0;
431 }
432 
433 
434 static int wpa_driver_wired_set_ifflags(const char *ifname, int flags)
435 {
436 	struct ifreq ifr;
437 	int s;
438 
439 	s = socket(PF_INET, SOCK_DGRAM, 0);
440 	if (s < 0) {
441 		perror("socket");
442 		return -1;
443 	}
444 
445 	os_memset(&ifr, 0, sizeof(ifr));
446 	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
447 	ifr.ifr_flags = flags & 0xffff;
448 	if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
449 		perror("ioctl[SIOCSIFFLAGS]");
450 		close(s);
451 		return -1;
452 	}
453 	close(s);
454 	return 0;
455 }
456 
457 
458 #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
459 static int wpa_driver_wired_get_ifstatus(const char *ifname, int *status)
460 {
461 	struct ifmediareq ifmr;
462 	int s;
463 
464 	s = socket(PF_INET, SOCK_DGRAM, 0);
465 	if (s < 0) {
466 		perror("socket");
467 		return -1;
468 	}
469 
470 	os_memset(&ifmr, 0, sizeof(ifmr));
471 	os_strlcpy(ifmr.ifm_name, ifname, IFNAMSIZ);
472 	if (ioctl(s, SIOCGIFMEDIA, (caddr_t) &ifmr) < 0) {
473 		perror("ioctl[SIOCGIFMEDIA]");
474 		close(s);
475 		return -1;
476 	}
477 	close(s);
478 	*status = (ifmr.ifm_status & (IFM_ACTIVE | IFM_AVALID)) ==
479 		(IFM_ACTIVE | IFM_AVALID);
480 
481 	return 0;
482 }
483 #endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
484 
485 
486 static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add)
487 {
488 	struct ifreq ifr;
489 	int s;
490 
491 #ifdef __sun__
492 	return -1;
493 #endif /* __sun__ */
494 
495 	s = socket(PF_INET, SOCK_DGRAM, 0);
496 	if (s < 0) {
497 		perror("socket");
498 		return -1;
499 	}
500 
501 	os_memset(&ifr, 0, sizeof(ifr));
502 	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
503 #ifdef __linux__
504 	ifr.ifr_hwaddr.sa_family = AF_UNSPEC;
505 	os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN);
506 #endif /* __linux__ */
507 #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
508 	{
509 		struct sockaddr_dl *dlp;
510 		dlp = (struct sockaddr_dl *) &ifr.ifr_addr;
511 		dlp->sdl_len = sizeof(struct sockaddr_dl);
512 		dlp->sdl_family = AF_LINK;
513 		dlp->sdl_index = 0;
514 		dlp->sdl_nlen = 0;
515 		dlp->sdl_alen = ETH_ALEN;
516 		dlp->sdl_slen = 0;
517 		os_memcpy(LLADDR(dlp), addr, ETH_ALEN);
518 	}
519 #endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
520 #if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
521 	{
522 		struct sockaddr *sap;
523 		sap = (struct sockaddr *) &ifr.ifr_addr;
524 		sap->sa_len = sizeof(struct sockaddr);
525 		sap->sa_family = AF_UNSPEC;
526 		os_memcpy(sap->sa_data, addr, ETH_ALEN);
527 	}
528 #endif /* defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) */
529 
530 	if (ioctl(s, add ? SIOCADDMULTI : SIOCDELMULTI, (caddr_t) &ifr) < 0) {
531 		perror("ioctl[SIOC{ADD/DEL}MULTI]");
532 		close(s);
533 		return -1;
534 	}
535 	close(s);
536 	return 0;
537 }
538 
539 
540 static void * wpa_driver_wired_init(void *ctx, const char *ifname)
541 {
542 	struct wpa_driver_wired_data *drv;
543 	int flags;
544 
545 	drv = os_zalloc(sizeof(*drv));
546 	if (drv == NULL)
547 		return NULL;
548 	os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
549 	drv->ctx = ctx;
550 
551 #ifdef __linux__
552 	drv->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
553 	if (drv->pf_sock < 0)
554 		perror("socket(PF_PACKET)");
555 #else /* __linux__ */
556 	drv->pf_sock = -1;
557 #endif /* __linux__ */
558 
559 	if (wpa_driver_wired_get_ifflags(ifname, &flags) == 0 &&
560 	    !(flags & IFF_UP) &&
561 	    wpa_driver_wired_set_ifflags(ifname, flags | IFF_UP) == 0) {
562 		drv->iff_up = 1;
563 	}
564 
565 	if (wired_multicast_membership(drv->pf_sock,
566 				       if_nametoindex(drv->ifname),
567 				       pae_group_addr, 1) == 0) {
568 		wpa_printf(MSG_DEBUG, "%s: Added multicast membership with "
569 			   "packet socket", __func__);
570 		drv->membership = 1;
571 	} else if (wpa_driver_wired_multi(ifname, pae_group_addr, 1) == 0) {
572 		wpa_printf(MSG_DEBUG, "%s: Added multicast membership with "
573 			   "SIOCADDMULTI", __func__);
574 		drv->multi = 1;
575 	} else if (wpa_driver_wired_get_ifflags(ifname, &flags) < 0) {
576 		wpa_printf(MSG_INFO, "%s: Could not get interface "
577 			   "flags", __func__);
578 		os_free(drv);
579 		return NULL;
580 	} else if (flags & IFF_ALLMULTI) {
581 		wpa_printf(MSG_DEBUG, "%s: Interface is already configured "
582 			   "for multicast", __func__);
583 	} else if (wpa_driver_wired_set_ifflags(ifname,
584 						flags | IFF_ALLMULTI) < 0) {
585 		wpa_printf(MSG_INFO, "%s: Failed to enable allmulti",
586 			   __func__);
587 		os_free(drv);
588 		return NULL;
589 	} else {
590 		wpa_printf(MSG_DEBUG, "%s: Enabled allmulti mode",
591 			   __func__);
592 		drv->iff_allmulti = 1;
593 	}
594 #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
595 	{
596 		int status;
597 		wpa_printf(MSG_DEBUG, "%s: waiting for link to become active",
598 			   __func__);
599 		while (wpa_driver_wired_get_ifstatus(ifname, &status) == 0 &&
600 		       status == 0)
601 			sleep(1);
602 	}
603 #endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
604 
605 	return drv;
606 }
607 
608 
609 static void wpa_driver_wired_deinit(void *priv)
610 {
611 	struct wpa_driver_wired_data *drv = priv;
612 	int flags;
613 
614 	if (drv->membership &&
615 	    wired_multicast_membership(drv->pf_sock,
616 				       if_nametoindex(drv->ifname),
617 				       pae_group_addr, 0) < 0) {
618 		wpa_printf(MSG_DEBUG, "%s: Failed to remove PAE multicast "
619 			   "group (PACKET)", __func__);
620 	}
621 
622 	if (drv->multi &&
623 	    wpa_driver_wired_multi(drv->ifname, pae_group_addr, 0) < 0) {
624 		wpa_printf(MSG_DEBUG, "%s: Failed to remove PAE multicast "
625 			   "group (SIOCDELMULTI)", __func__);
626 	}
627 
628 	if (drv->iff_allmulti &&
629 	    (wpa_driver_wired_get_ifflags(drv->ifname, &flags) < 0 ||
630 	     wpa_driver_wired_set_ifflags(drv->ifname,
631 					  flags & ~IFF_ALLMULTI) < 0)) {
632 		wpa_printf(MSG_DEBUG, "%s: Failed to disable allmulti mode",
633 			   __func__);
634 	}
635 
636 	if (drv->iff_up &&
637 	    wpa_driver_wired_get_ifflags(drv->ifname, &flags) == 0 &&
638 	    (flags & IFF_UP) &&
639 	    wpa_driver_wired_set_ifflags(drv->ifname, flags & ~IFF_UP) < 0) {
640 		wpa_printf(MSG_DEBUG, "%s: Failed to set the interface down",
641 			   __func__);
642 	}
643 
644 	if (drv->pf_sock != -1)
645 		close(drv->pf_sock);
646 
647 	os_free(drv);
648 }
649 
650 
651 const struct wpa_driver_ops wpa_driver_wired_ops = {
652 	.name = "wired",
653 	.desc = "Wired Ethernet driver",
654 	.hapd_init = wired_driver_hapd_init,
655 	.hapd_deinit = wired_driver_hapd_deinit,
656 	.hapd_send_eapol = wired_send_eapol,
657 	.get_ssid = wpa_driver_wired_get_ssid,
658 	.get_bssid = wpa_driver_wired_get_bssid,
659 	.get_capa = wpa_driver_wired_get_capa,
660 	.init = wpa_driver_wired_init,
661 	.deinit = wpa_driver_wired_deinit,
662 };
663