1/* 2 * Common functions for Wired Ethernet driver interfaces 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 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 33static 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 58static 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 83static 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 140int 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 167int driver_wired_get_ssid(void *priv, u8 *ssid) 168{ 169 ssid[0] = 0; 170 return 0; 171} 172 173 174int 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 182int 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__) 191static 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 219int 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 284void 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