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