Home | History | Annotate | Download | only in drivers
      1 /*
      2  * Common functions for Wired Ethernet driver interfaces
      3  * Copyright (c) 2005-2009, Jouni Malinen <j (at) w1.fi>
      4  * Copyright (c) 2004, Gunter Burchardt <tira (at) 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 
     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 
     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 
     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 
    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 
    167 int driver_wired_get_ssid(void *priv, u8 *ssid)
    168 {
    169 	ssid[0] = 0;
    170 	return 0;
    171 }
    172 
    173 
    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 
    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__)
    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 
    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 
    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