2 * Wired Ethernet driver interface
3 * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
4 * Copyright (c) 2004, Gunter Burchardt <tira@isx.de>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
10 * Alternatively, this software may be distributed under the terms of BSD
13 * See README and COPYING for more details.
17 #include <sys/ioctl.h>
20 #include <netpacket/packet.h>
21 #include <net/if_arp.h>
23 #endif /* __linux__ */
24 #if defined(__FreeBSD__) || defined(__DragonFly__)
25 #include <net/if_dl.h>
26 #endif /* defined(__FreeBSD__) || defined(__DragonFly__) */
46 static const u8 pae_group_addr
[ETH_ALEN
] =
47 { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
50 struct wpa_driver_wired_data
{
51 char ifname
[IFNAMSIZ
+ 1];
54 int sock
; /* raw packet socket for driver access */
55 int dhcp_sock
; /* socket for dhcp packets */
56 int use_pae_group_addr
;
59 int membership
, multi
, iff_allmulti
, iff_up
;
63 /* TODO: detecting new devices should eventually be changed from using DHCP
64 * snooping to trigger on any packet from a new layer 2 MAC address, e.g.,
65 * based on ebtables, etc. */
83 u_int8_t options
[308]; /* 312 - cookie */
87 static int wired_multicast_membership(int sock
, int ifindex
,
88 const u8
*addr
, int add
)
91 struct packet_mreq mreq
;
96 os_memset(&mreq
, 0, sizeof(mreq
));
97 mreq
.mr_ifindex
= ifindex
;
98 mreq
.mr_type
= PACKET_MR_MULTICAST
;
99 mreq
.mr_alen
= ETH_ALEN
;
100 os_memcpy(mreq
.mr_address
, addr
, ETH_ALEN
);
102 if (setsockopt(sock
, SOL_PACKET
,
103 add
? PACKET_ADD_MEMBERSHIP
: PACKET_DROP_MEMBERSHIP
,
104 &mreq
, sizeof(mreq
)) < 0) {
105 perror("setsockopt");
109 #else /* __linux__ */
111 #endif /* __linux__ */
115 static void handle_data(void *ctx
, unsigned char *buf
, size_t len
)
118 struct ieee8023_hdr
*hdr
;
122 /* must contain at least ieee8023_hdr 6 byte source, 6 byte dest,
123 * 2 byte ethertype */
125 wpa_printf(MSG_MSGDUMP
, "handle_data: too short (%lu)",
126 (unsigned long) len
);
130 hdr
= (struct ieee8023_hdr
*) buf
;
132 switch (ntohs(hdr
->ethertype
)) {
134 wpa_printf(MSG_MSGDUMP
, "Received EAPOL packet");
136 hostapd_notif_new_sta(ctx
, sa
);
138 pos
= (u8
*) (hdr
+ 1);
139 left
= len
- sizeof(*hdr
);
141 hostapd_eapol_receive(ctx
, sa
, pos
, left
);
145 wpa_printf(MSG_DEBUG
, "Unknown ethertype 0x%04x in data frame",
146 ntohs(hdr
->ethertype
));
153 static void handle_read(int sock
, void *eloop_ctx
, void *sock_ctx
)
156 unsigned char buf
[3000];
158 len
= recv(sock
, buf
, sizeof(buf
), 0);
164 handle_data(eloop_ctx
, buf
, len
);
168 static void handle_dhcp(int sock
, void *eloop_ctx
, void *sock_ctx
)
172 unsigned char buf
[3000];
173 struct dhcp_message
*msg
;
176 len
= recv(sock
, buf
, sizeof(buf
), 0);
182 /* must contain at least dhcp_message->chaddr */
184 wpa_printf(MSG_MSGDUMP
, "handle_dhcp: too short (%d)", len
);
188 msg
= (struct dhcp_message
*) buf
;
189 mac_address
= (u8
*) &(msg
->chaddr
);
191 wpa_printf(MSG_MSGDUMP
, "Got DHCP broadcast packet from " MACSTR
,
192 MAC2STR(mac_address
));
194 hostapd_notif_new_sta(eloop_ctx
, mac_address
);
199 static int wired_init_sockets(struct wpa_driver_wired_data
*drv
, u8
*own_addr
)
202 struct sockaddr_ll addr
;
203 struct sockaddr_in addr2
;
206 drv
->sock
= socket(PF_PACKET
, SOCK_RAW
, htons(ETH_P_PAE
));
208 perror("socket[PF_PACKET,SOCK_RAW]");
212 if (eloop_register_read_sock(drv
->sock
, handle_read
, drv
->ctx
, NULL
)) {
213 printf("Could not register read socket\n");
217 os_memset(&ifr
, 0, sizeof(ifr
));
218 os_strlcpy(ifr
.ifr_name
, drv
->ifname
, sizeof(ifr
.ifr_name
));
219 if (ioctl(drv
->sock
, SIOCGIFINDEX
, &ifr
) != 0) {
220 perror("ioctl(SIOCGIFINDEX)");
224 os_memset(&addr
, 0, sizeof(addr
));
225 addr
.sll_family
= AF_PACKET
;
226 addr
.sll_ifindex
= ifr
.ifr_ifindex
;
227 wpa_printf(MSG_DEBUG
, "Opening raw packet socket for ifindex %d",
230 if (bind(drv
->sock
, (struct sockaddr
*) &addr
, sizeof(addr
)) < 0) {
235 /* filter multicast address */
236 if (wired_multicast_membership(drv
->sock
, ifr
.ifr_ifindex
,
237 pae_group_addr
, 1) < 0) {
238 wpa_printf(MSG_ERROR
, "wired: Failed to add multicast group "
243 os_memset(&ifr
, 0, sizeof(ifr
));
244 os_strlcpy(ifr
.ifr_name
, drv
->ifname
, sizeof(ifr
.ifr_name
));
245 if (ioctl(drv
->sock
, SIOCGIFHWADDR
, &ifr
) != 0) {
246 perror("ioctl(SIOCGIFHWADDR)");
250 if (ifr
.ifr_hwaddr
.sa_family
!= ARPHRD_ETHER
) {
251 printf("Invalid HW-addr family 0x%04x\n",
252 ifr
.ifr_hwaddr
.sa_family
);
255 os_memcpy(own_addr
, ifr
.ifr_hwaddr
.sa_data
, ETH_ALEN
);
257 /* setup dhcp listen socket for sta detection */
258 if ((drv
->dhcp_sock
= socket(PF_INET
, SOCK_DGRAM
, IPPROTO_UDP
)) < 0) {
259 perror("socket call failed for dhcp");
263 if (eloop_register_read_sock(drv
->dhcp_sock
, handle_dhcp
, drv
->ctx
,
265 printf("Could not register read socket\n");
269 os_memset(&addr2
, 0, sizeof(addr2
));
270 addr2
.sin_family
= AF_INET
;
271 addr2
.sin_port
= htons(67);
272 addr2
.sin_addr
.s_addr
= INADDR_ANY
;
274 if (setsockopt(drv
->dhcp_sock
, SOL_SOCKET
, SO_REUSEADDR
, (char *) &n
,
276 perror("setsockopt[SOL_SOCKET,SO_REUSEADDR]");
279 if (setsockopt(drv
->dhcp_sock
, SOL_SOCKET
, SO_BROADCAST
, (char *) &n
,
281 perror("setsockopt[SOL_SOCKET,SO_BROADCAST]");
285 os_memset(&ifr
, 0, sizeof(ifr
));
286 os_strlcpy(ifr
.ifr_ifrn
.ifrn_name
, drv
->ifname
, IFNAMSIZ
);
287 if (setsockopt(drv
->dhcp_sock
, SOL_SOCKET
, SO_BINDTODEVICE
,
288 (char *) &ifr
, sizeof(ifr
)) < 0) {
289 perror("setsockopt[SOL_SOCKET,SO_BINDTODEVICE]");
293 if (bind(drv
->dhcp_sock
, (struct sockaddr
*) &addr2
,
294 sizeof(struct sockaddr
)) == -1) {
303 static int wired_send_eapol(void *priv
, const u8
*addr
,
304 const u8
*data
, size_t data_len
, int encrypt
,
307 struct wpa_driver_wired_data
*drv
= priv
;
308 struct ieee8023_hdr
*hdr
;
313 len
= sizeof(*hdr
) + data_len
;
314 hdr
= os_zalloc(len
);
316 printf("malloc() failed for wired_send_eapol(len=%lu)\n",
317 (unsigned long) len
);
321 os_memcpy(hdr
->dest
, drv
->use_pae_group_addr
? pae_group_addr
: addr
,
323 os_memcpy(hdr
->src
, own_addr
, ETH_ALEN
);
324 hdr
->ethertype
= htons(ETH_P_PAE
);
326 pos
= (u8
*) (hdr
+ 1);
327 os_memcpy(pos
, data
, data_len
);
329 res
= send(drv
->sock
, (u8
*) hdr
, len
, 0);
333 perror("wired_send_eapol: send");
334 printf("wired_send_eapol - packet len: %lu - failed\n",
335 (unsigned long) len
);
342 static void * wired_driver_hapd_init(struct hostapd_data
*hapd
,
343 struct wpa_init_params
*params
)
345 struct wpa_driver_wired_data
*drv
;
347 drv
= os_zalloc(sizeof(struct wpa_driver_wired_data
));
349 printf("Could not allocate memory for wired driver data\n");
354 os_strlcpy(drv
->ifname
, params
->ifname
, sizeof(drv
->ifname
));
355 drv
->use_pae_group_addr
= params
->use_pae_group_addr
;
357 if (wired_init_sockets(drv
, params
->own_addr
)) {
366 static void wired_driver_hapd_deinit(void *priv
)
368 struct wpa_driver_wired_data
*drv
= priv
;
373 if (drv
->dhcp_sock
>= 0)
374 close(drv
->dhcp_sock
);
380 static int wpa_driver_wired_get_ssid(void *priv
, u8
*ssid
)
387 static int wpa_driver_wired_get_bssid(void *priv
, u8
*bssid
)
389 /* Report PAE group address as the "BSSID" for wired connection. */
390 os_memcpy(bssid
, pae_group_addr
, ETH_ALEN
);
395 static int wpa_driver_wired_get_capa(void *priv
, struct wpa_driver_capa
*capa
)
397 os_memset(capa
, 0, sizeof(*capa
));
398 capa
->flags
= WPA_DRIVER_FLAGS_WIRED
;
403 static int wpa_driver_wired_get_ifflags(const char *ifname
, int *flags
)
408 s
= socket(PF_INET
, SOCK_DGRAM
, 0);
414 os_memset(&ifr
, 0, sizeof(ifr
));
415 os_strlcpy(ifr
.ifr_name
, ifname
, IFNAMSIZ
);
416 if (ioctl(s
, SIOCGIFFLAGS
, (caddr_t
) &ifr
) < 0) {
417 perror("ioctl[SIOCGIFFLAGS]");
422 *flags
= ifr
.ifr_flags
& 0xffff;
427 static int wpa_driver_wired_set_ifflags(const char *ifname
, int flags
)
432 s
= socket(PF_INET
, SOCK_DGRAM
, 0);
438 os_memset(&ifr
, 0, sizeof(ifr
));
439 os_strlcpy(ifr
.ifr_name
, ifname
, IFNAMSIZ
);
440 ifr
.ifr_flags
= flags
& 0xffff;
441 if (ioctl(s
, SIOCSIFFLAGS
, (caddr_t
) &ifr
) < 0) {
442 perror("ioctl[SIOCSIFFLAGS]");
451 static int wpa_driver_wired_multi(const char *ifname
, const u8
*addr
, int add
)
456 s
= socket(PF_INET
, SOCK_DGRAM
, 0);
462 os_memset(&ifr
, 0, sizeof(ifr
));
463 os_strlcpy(ifr
.ifr_name
, ifname
, IFNAMSIZ
);
465 ifr
.ifr_hwaddr
.sa_family
= AF_UNSPEC
;
466 os_memcpy(ifr
.ifr_hwaddr
.sa_data
, addr
, ETH_ALEN
);
467 #endif /* __linux__ */
468 #if defined(__FreeBSD__) || defined(__DragonFly__)
470 struct sockaddr_dl
*dlp
;
471 dlp
= (struct sockaddr_dl
*) &ifr
.ifr_addr
;
472 dlp
->sdl_len
= sizeof(struct sockaddr_dl
);
473 dlp
->sdl_family
= AF_LINK
;
476 dlp
->sdl_alen
= ETH_ALEN
;
478 os_memcpy(LLADDR(dlp
), addr
, ETH_ALEN
);
480 #endif /* defined(__FreeBSD__) || defined(__DragonFly__) */
481 #if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
483 struct sockaddr
*sap
;
484 sap
= (struct sockaddr
*) &ifr
.ifr_addr
;
485 sap
->sa_len
= sizeof(struct sockaddr
);
486 sap
->sa_family
= AF_UNSPEC
;
487 os_memcpy(sap
->sa_data
, addr
, ETH_ALEN
);
489 #endif /* defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) */
491 if (ioctl(s
, add
? SIOCADDMULTI
: SIOCDELMULTI
, (caddr_t
) &ifr
) < 0) {
492 perror("ioctl[SIOC{ADD/DEL}MULTI]");
501 static void * wpa_driver_wired_init(void *ctx
, const char *ifname
)
503 struct wpa_driver_wired_data
*drv
;
506 drv
= os_zalloc(sizeof(*drv
));
509 os_strlcpy(drv
->ifname
, ifname
, sizeof(drv
->ifname
));
513 drv
->pf_sock
= socket(PF_PACKET
, SOCK_DGRAM
, 0);
514 if (drv
->pf_sock
< 0)
515 perror("socket(PF_PACKET)");
516 #else /* __linux__ */
518 #endif /* __linux__ */
520 if (wpa_driver_wired_get_ifflags(ifname
, &flags
) == 0 &&
522 wpa_driver_wired_set_ifflags(ifname
, flags
| IFF_UP
) == 0) {
526 if (wired_multicast_membership(drv
->pf_sock
,
527 if_nametoindex(drv
->ifname
),
528 pae_group_addr
, 1) == 0) {
529 wpa_printf(MSG_DEBUG
, "%s: Added multicast membership with "
530 "packet socket", __func__
);
532 } else if (wpa_driver_wired_multi(ifname
, pae_group_addr
, 1) == 0) {
533 wpa_printf(MSG_DEBUG
, "%s: Added multicast membership with "
534 "SIOCADDMULTI", __func__
);
536 } else if (wpa_driver_wired_get_ifflags(ifname
, &flags
) < 0) {
537 wpa_printf(MSG_INFO
, "%s: Could not get interface "
541 } else if (flags
& IFF_ALLMULTI
) {
542 wpa_printf(MSG_DEBUG
, "%s: Interface is already configured "
543 "for multicast", __func__
);
544 } else if (wpa_driver_wired_set_ifflags(ifname
,
545 flags
| IFF_ALLMULTI
) < 0) {
546 wpa_printf(MSG_INFO
, "%s: Failed to enable allmulti",
551 wpa_printf(MSG_DEBUG
, "%s: Enabled allmulti mode",
553 drv
->iff_allmulti
= 1;
560 static void wpa_driver_wired_deinit(void *priv
)
562 struct wpa_driver_wired_data
*drv
= priv
;
565 if (drv
->membership
&&
566 wired_multicast_membership(drv
->pf_sock
,
567 if_nametoindex(drv
->ifname
),
568 pae_group_addr
, 0) < 0) {
569 wpa_printf(MSG_DEBUG
, "%s: Failed to remove PAE multicast "
570 "group (PACKET)", __func__
);
574 wpa_driver_wired_multi(drv
->ifname
, pae_group_addr
, 0) < 0) {
575 wpa_printf(MSG_DEBUG
, "%s: Failed to remove PAE multicast "
576 "group (SIOCDELMULTI)", __func__
);
579 if (drv
->iff_allmulti
&&
580 (wpa_driver_wired_get_ifflags(drv
->ifname
, &flags
) < 0 ||
581 wpa_driver_wired_set_ifflags(drv
->ifname
,
582 flags
& ~IFF_ALLMULTI
) < 0)) {
583 wpa_printf(MSG_DEBUG
, "%s: Failed to disable allmulti mode",
588 wpa_driver_wired_get_ifflags(drv
->ifname
, &flags
) == 0 &&
590 wpa_driver_wired_set_ifflags(drv
->ifname
, flags
& ~IFF_UP
) < 0) {
591 wpa_printf(MSG_DEBUG
, "%s: Failed to set the interface down",
595 if (drv
->pf_sock
!= -1)
602 const struct wpa_driver_ops wpa_driver_wired_ops
= {
604 .desc
= "Wired Ethernet driver",
605 .hapd_init
= wired_driver_hapd_init
,
606 .hapd_deinit
= wired_driver_hapd_deinit
,
607 .hapd_send_eapol
= wired_send_eapol
,
608 .get_ssid
= wpa_driver_wired_get_ssid
,
609 .get_bssid
= wpa_driver_wired_get_bssid
,
610 .get_capa
= wpa_driver_wired_get_capa
,
611 .init
= wpa_driver_wired_init
,
612 .deinit
= wpa_driver_wired_deinit
,