1/* 2 * Copyright (c) 2001-2013 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24/* 25 * eapol_socket.c 26 * - wrapper for allocating an NDRV socket for use with 802.1X 27 */ 28 29/* 30 * Modification History 31 * 32 * October 26, 2001 Dieter Siegmund (dieter@apple) 33 * - created 34 * 35 * August 31, 2010 Dieter Siegmund (dieter@apple) 36 * - combined ndrv_socket.c/eapol_socket.c, moved to framework 37 */ 38 39#include <stdlib.h> 40#include <unistd.h> 41#include <string.h> 42#include <stdio.h> 43#include <sys/types.h> 44#include <sys/wait.h> 45#include <sys/errno.h> 46#include <sys/socket.h> 47#include <sys/ioctl.h> 48#include <net/if.h> 49#include <net/if_media.h> 50#include <net/if_types.h> 51#include <net/if_dl.h> 52#include <net/dlil.h> 53#include <net/ndrv.h> 54#include <net/ethernet.h> 55#include <sys/sockio.h> 56#include <fcntl.h> 57#include <sys/filio.h> 58#include <syslog.h> 59#include "EAPOL.h" 60#include "EAPLog.h" 61#include "symbol_scope.h" 62#include "eapol_socket.h" 63 64#define EAPOL_802_1_X_FAMILY 0x8021ec /* XXX needs official number! */ 65 66static const struct ether_addr eapol_multicast = { 67 EAPOL_802_1_X_GROUP_ADDRESS 68}; 69 70STATIC int 71ndrv_socket(const char * ifname) 72{ 73 struct sockaddr_ndrv ndrv; 74 int s; 75 76 s = socket(AF_NDRV, SOCK_RAW, 0); 77 if (s < 0) { 78 EAPLOG_FL(LOG_NOTICE, "socket() failed: %s", 79 strerror(errno)); 80 goto failed; 81 } 82 strlcpy((char *)ndrv.snd_name, ifname, sizeof(ndrv.snd_name)); 83 ndrv.snd_len = sizeof(ndrv); 84 ndrv.snd_family = AF_NDRV; 85 if (bind(s, (struct sockaddr *)&ndrv, sizeof(ndrv)) < 0) { 86 EAPLOG_FL(LOG_NOTICE, "bind() failed: %s", strerror(errno)); 87 goto failed; 88 } 89 return (s); 90 failed: 91 if (s >= 0) { 92 close(s); 93 } 94 return (-1); 95} 96 97STATIC int 98ndrv_socket_bind(int s, u_int32_t family, const u_int16_t * ether_types, 99 int ether_types_count) 100{ 101 int i; 102 struct ndrv_protocol_desc proto; 103 struct ndrv_demux_desc * demux; 104 int status; 105 106 demux = (struct ndrv_demux_desc *) 107 malloc(sizeof(*demux) * ether_types_count); 108 proto.version = NDRV_PROTOCOL_DESC_VERS; 109 proto.protocol_family = family; 110 proto.demux_count = ether_types_count; 111 proto.demux_list = demux; 112 for (i = 0; i < ether_types_count; i++) { 113 demux[i].type = NDRV_DEMUXTYPE_ETHERTYPE; 114 demux[i].length = sizeof(demux[i].data.ether_type); 115 demux[i].data.ether_type = htons(ether_types[i]); 116 } 117 status = setsockopt(s, SOL_NDRVPROTO, NDRV_SETDMXSPEC, 118 (caddr_t)&proto, sizeof(proto)); 119 free(demux); 120 if (status < 0) { 121 syslog(LOG_NOTICE, "setsockopt(NDRV_SETDMXSPEC) failed: %s", 122 strerror(errno)); 123 return (status); 124 } 125 return (0); 126} 127 128STATIC int 129ndrv_socket_add_multicast(int s, const struct sockaddr_dl * dl_p) 130{ 131 int status; 132 133 status = setsockopt(s, SOL_NDRVPROTO, NDRV_ADDMULTICAST, 134 dl_p, dl_p->sdl_len); 135 if (status < 0) { 136 syslog(LOG_NOTICE, "setsockopt(NDRV_ADDMULTICAST) failed: %s", 137 strerror(errno)); 138 return (status); 139 } 140 return (0); 141} 142 143STATIC bool 144eapol_socket_add_multicast(int s) 145{ 146 struct sockaddr_dl dl; 147 148 bzero(&dl, sizeof(dl)); 149 dl.sdl_len = sizeof(dl); 150 dl.sdl_family = AF_LINK; 151 dl.sdl_type = IFT_ETHER; 152 dl.sdl_nlen = 0; 153 dl.sdl_alen = sizeof(eapol_multicast); 154 bcopy(&eapol_multicast, 155 dl.sdl_data, 156 sizeof(eapol_multicast)); 157 if (ndrv_socket_add_multicast(s, &dl) < 0) { 158 syslog(LOG_NOTICE, "eapol_socket: ndrv_socket_add_multicast failed, %s", 159 strerror(errno)); 160 return (false); 161 } 162 return (true); 163} 164 165int 166eapol_socket(const char * ifname, bool is_wireless) 167{ 168 uint16_t ether_types[2] = { EAPOL_802_1_X_ETHERTYPE, 169 IEEE80211_PREAUTH_ETHERTYPE }; 170 int ether_types_count; 171 int opt = 1; 172 int s; 173 174 s = ndrv_socket(ifname); 175 if (s < 0) { 176 syslog(LOG_NOTICE, "eapol_socket: ndrv_socket failed"); 177 goto failed; 178 } 179 if (ioctl(s, FIONBIO, &opt) < 0) { 180 syslog(LOG_NOTICE, "eapol_socket: FIONBIO failed, %s", 181 strerror(errno)); 182 goto failed; 183 } 184 if (is_wireless == false) { 185 /* ethernet needs multicast */ 186 ether_types_count = 1; 187 if (eapol_socket_add_multicast(s) == false) { 188 goto failed; 189 } 190 } 191 else { 192 ether_types_count = 2; 193 } 194 if (ndrv_socket_bind(s, EAPOL_802_1_X_FAMILY, ether_types, 195 ether_types_count) < 0) { 196 syslog(LOG_NOTICE, "eapol_socket: ndrv_socket_bind failed, %s", 197 strerror(errno)); 198 goto failed; 199 } 200 return (s); 201 failed: 202 if (s >= 0) { 203 close(s); 204 } 205 return (-1); 206 207} 208 209#ifdef TEST_EAPOL_SOCKET 210 211static int 212get_ifm_type(const char * name) 213{ 214 int i; 215 struct ifmediareq ifm; 216 int media_static[20]; 217 int media_static_count = sizeof(media_static) / sizeof(media_static[0]); 218 int s; 219 int ifm_type = 0; 220 bool supports_full_duplex = false; 221 222 s = socket(AF_INET, SOCK_DGRAM, 0); 223 if (s < 0) { 224 perror("socket"); 225 goto done; 226 } 227 bzero(&ifm, sizeof(ifm)); 228 strlcpy(ifm.ifm_name, name, sizeof(ifm.ifm_name)); 229 if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifm) < 0) { 230 goto done; 231 } 232 ifm_type = IFM_TYPE(ifm.ifm_current); 233 if (ifm_type != IFM_ETHER) { 234 goto done; 235 } 236 if (ifm.ifm_count == 0) { 237 goto done; 238 } 239 if (ifm.ifm_count > media_static_count) { 240 ifm.ifm_ulist = (int *)malloc(ifm.ifm_count * sizeof(int)); 241 } 242 else { 243 ifm.ifm_ulist = media_static; 244 } 245 if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifm) == -1) { 246 goto done; 247 } 248 if (ifm.ifm_count == 1 249 && IFM_SUBTYPE(ifm.ifm_ulist[0]) == IFM_AUTO) { 250 /* only support autoselect, not really ethernet */ 251 goto done; 252 } 253 for (i = 0; i < ifm.ifm_count; i++) { 254 if ((ifm.ifm_ulist[i] & IFM_FDX) != 0) { 255 supports_full_duplex = true; 256 break; 257 } 258 } 259 260 done: 261 if (s >= 0) { 262 close(s); 263 } 264 if (ifm_type == IFM_ETHER && supports_full_duplex == false) { 265 /* not really ethernet */ 266 ifm_type = 0; 267 } 268 return (ifm_type); 269} 270 271#include <sys/stat.h> 272 273int 274main(int argc, const char * argv[]) 275{ 276 int fd; 277 int ifm_type; 278 279 if (argc < 2) { 280 fprintf(stderr, "usage: eapol <ifname>\n"); 281 exit(1); 282 } 283 ifm_type = get_ifm_type(argv[1]); 284 switch (ifm_type) { 285 case IFM_ETHER: 286 break; 287 case IFM_IEEE80211: 288 break; 289 default: 290 fprintf(stderr, "interface %s is not valid\n", argv[1]); 291 exit(1); 292 break; 293 } 294 fd = eapol_socket(argv[1], (ifm_type == IFM_IEEE80211)); 295 if (fd < 0) { 296 fprintf(stderr, "eapol_socket(%s) failed\n", argv[1]); 297 } 298 { 299 struct stat sb; 300 301 if (fstat(fd, &sb) == 0) { 302 if (S_ISSOCK(sb.st_mode)) { 303 fprintf(stderr, "%d a socket\n", fd); 304 } 305 } 306 } 307 308 close(fd); 309 exit(1); 310} 311 312#endif /* TEST_EAPOL_SOCKET */ 313