/* * Copyright (c) 2000-2003 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* ----------------------------------------------------------------------------- * * Theory of operation : * * PPPoE plugin for vpnd * ----------------------------------------------------------------------------- */ /* ----------------------------------------------------------------------------- Includes ----------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define APPLE 1 #include "../../../Family/ppp_defs.h" #include "../../../Family/if_ppp.h" #include "../../../Family/ppp_domain.h" #include "../../../Helpers/vpnd/vpnd.h" #include "../../../Helpers/vpnd/vpnplugins.h" #include "../../../Helpers/vpnd/RASSchemaDefinitions.h" #include "../../../Helpers/vpnd/cf_utils.h" #include "../PPPoE-extension/PPPoE.h" // ---------------------------------------------------------------------------- // ¥ Private Globals // ---------------------------------------------------------------------------- static CFBundleRef bundle = 0; static int listen_sockfd = -1; static char device[17]; static CFStringRef service = NULL; static CFStringRef access_concentrator = NULL; #define PPPOE_NKE "PPPoE.kext" int pppoevpn_get_pppd_args(struct vpn_params *params, int reload); int pppoevpn_listen(void); int pppoevpn_accept(void); int pppoevpn_refuse(void); void pppoevpn_close(void); static u_long load_kext(char *kext); /* ----------------------------------------------------------------------------- plugin entry point, called by vpnd ref is the vpn bundle reference pppref is the ppp bundle reference bundles can be layout in two different ways - As simple vpn bundles (bundle.vpn). the bundle contains the vpn bundle binary. - As full ppp bundles (bundle.ppp). The bundle contains the ppp bundle binary, and also the vpn kext and the vpn bundle binary in its Plugins directory. if a simple vpn bundle was used, pppref will be NULL. if a ppp bundle was used, the vpn plugin will be able to get access to the Plugins directory and load the vpn kext. ----------------------------------------------------------------------------- */ int start(struct vpn_channel* the_vpn_channel, CFBundleRef ref, CFBundleRef pppref, int debug, int log_verbose) { int s; char name[MAXPATHLEN]; CFURLRef url; /* first load the kext if we are loaded as part of a ppp bundle */ if (pppref) { s = socket(PF_PPP, SOCK_DGRAM, PPPPROTO_PPPOE); if (s < 0) { if (url = CFBundleCopyBundleURL(pppref)) { name[0] = 0; CFURLGetFileSystemRepresentation(url, 0, name, MAXPATHLEN - 1); CFRelease(url); strcat(name, "/"); if (url = CFBundleCopyBuiltInPlugInsURL(pppref)) { CFURLGetFileSystemRepresentation(url, 0, name + strlen(name), MAXPATHLEN - strlen(name) - strlen(PPPOE_NKE) - 1); CFRelease(url); strcat(name, "/"); strcat(name, PPPOE_NKE); if (!load_kext(name)) s = socket(PF_PPP, SOCK_DGRAM, PPPPROTO_PPPOE); } } if (s < 0) { vpnlog(LOG_ERR, "PPPoE plugin: Unable to load PPPoE kernel extension\n"); return -1; } } close (s); } /* retain reference */ bundle = ref; CFRetain(bundle); // hookup our socket handlers bzero(the_vpn_channel, sizeof(struct vpn_channel)); the_vpn_channel->get_pppd_args = pppoevpn_get_pppd_args; the_vpn_channel->listen = pppoevpn_listen; the_vpn_channel->accept = pppoevpn_accept; the_vpn_channel->refuse = pppoevpn_refuse; the_vpn_channel->close = pppoevpn_close; /* copy default interface */ strcpy(device, "en0"); return 0; } /* ----------------------------------------------------------------------------- pppoevpn_get_pppd_args ----------------------------------------------------------------------------- */ int pppoevpn_get_pppd_args(struct vpn_params *params, int reload) { CFStringRef string; if (params->serverRef) { /* arguments from the preferences file */ addstrparam(params->exec_args, ¶ms->next_arg_index, "pppoemode", "answer"); if (string = get_cfstr_option(params->serverRef, kRASEntPPPoE, kRASPropPPPoEDeviceName)) { if (!CFStringGetCString(string, device, sizeof(device), kCFStringEncodingUTF8)) { vpnlog(LOG_ERR, "PPPoE plugin: Could not get device name\n"); return -1; } } addstrparam(params->exec_args, ¶ms->next_arg_index, "device", device); if (service = get_cfstr_option(params->serverRef, kRASEntPPPoE, kRASPropPPPoEServiceName)) CFRetain(service); if (access_concentrator = get_cfstr_option(params->serverRef, kRASEntPPPoE, kRASPropPPPoEAccessConcentratorName)) CFRetain(access_concentrator); } return 0; } /* ----------------------------------------------------------------------------- system call wrappers ----------------------------------------------------------------------------- */ int pppoe_sys_accept(int sockfd, struct sockaddr *cliaddr, int *addrlen) { int fd; while ((fd = accept(sockfd, cliaddr, addrlen)) == -1) if (errno != EINTR) { vpnlog(LOG_ERR, "PPPoE plugin: error calling accept = %s\n", strerror(errno)); return -1; } return fd; } int pppoe_sys_close(int sockfd) { while (close(sockfd) == -1) if (errno != EINTR) { vpnlog(LOG_ERR, "PPPoE plugin: error calling close on socket = %s\n", strerror(errno)); return -1; } return 0; } /* ----------------------------------------------------------------------------- closeall ----------------------------------------------------------------------------- */ static void closeall() { int i; for (i = getdtablesize() - 1; i >= 0; i--) close(i); open("/dev/null", O_RDWR, 0); dup(0); dup(0); return; } /* ----------------------------------------------------------------------------- load_kext ----------------------------------------------------------------------------- */ static u_long load_kext(char *kext) { int pid; if ((pid = fork()) < 0) return 1; if (pid == 0) { closeall(); // PPP kernel extension not loaded, try load it... execl("/sbin/kextload", "kextload", kext, (char *)0); exit(1); } while (waitpid(pid, 0, 0) < 0) { if (errno == EINTR) continue; return 1; } return 0; } /* ----------------------------------------------------------------------------- pppoevpn_listen() called by vpnd to setup listening socket ----------------------------------------------------------------------------- */ int pppoevpn_listen(void) { struct sockaddr_pppoe addr; int s, error = -1; struct ifreq ifr; /* first, make sure the interface is UP */ error = -1; s = socket(PF_SYSTEM, SOCK_RAW, SYSPROTO_EVENT); if (s >= 0) { bzero(&ifr, sizeof(ifr)); bcopy(device, ifr.ifr_name, strlen(device)); if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) >= 0) { // ensure that the device is UP ifr.ifr_flags |= IFF_UP; if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) >= 0) error = 0 ; } close(s); } if (error) { vpnlog(LOG_ERR, "PPPoE plugin: Could not configure the interface UP - err = %s\n", strerror(errno)); goto fail; } // Create the requested socket while ((listen_sockfd = socket (PF_PPP, SOCK_DGRAM, PPPPROTO_PPPOE)) < 0) { if (errno != EINTR) { vpnlog(LOG_ERR, "PPPoE plugin: Could not create socket - err = %s\n", strerror(errno)); goto fail; } } if (setsockopt(listen_sockfd, PPPPROTO_PPPOE, PPPOE_OPT_INTERFACE, device, strlen(device))) { vpnlog(LOG_ERR, "PPPoE plugin: Could not specify listening interface '%s' - err = %s\n", device, strerror(errno)); goto fail; } if (access_concentrator || service) { bzero(&addr, sizeof(addr)); addr.ppp.ppp_len = sizeof(struct sockaddr_pppoe); addr.ppp.ppp_family = AF_PPP; addr.ppp.ppp_proto = PPPPROTO_PPPOE; if (access_concentrator) CFStringGetCString(access_concentrator, addr.pppoe_ac_name, sizeof(addr.pppoe_ac_name), kCFStringEncodingUTF8); if (service) CFStringGetCString(service, addr.pppoe_service, sizeof(addr.pppoe_service), kCFStringEncodingUTF8); if (bind(listen_sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_pppoe)) < 0) { vpnlog(LOG_ERR, "PPPoE plugin: bind failed for service = '%s', access concentrator = '%s'. Error = %s\n", service, access_concentrator, strerror(errno)); goto fail; } } while (listen(listen_sockfd, SOMAXCONN) < 0) { if (errno == EINTR) { vpnlog(LOG_ERR, "PPPoE plugin: error calling listen = %s\n", strerror(errno)); goto fail; } } return listen_sockfd; fail: if (listen_sockfd!= -1) { close(listen_sockfd); listen_sockfd = -1; } return -1; } /* ----------------------------------------------------------------------------- pppoevpn_accept() called by vpnd to listen for incomming connections. ----------------------------------------------------------------------------- */ int pppoevpn_accept(void) { int fdConn; struct sockaddr_storage ssSender; struct sockaddr *sapSender = (struct sockaddr *)&ssSender; int nSize = sizeof(ssSender); if ((fdConn = pppoe_sys_accept(listen_sockfd, sapSender, &nSize)) < 0) return -1; if (sapSender->sa_family != AF_PPP) { vpnlog(LOG_ERR, "PPPoE plugin: Unexpected protocol family!\n"); if (pppoe_sys_close(fdConn) < 0) return -1; return 0; } return fdConn; } /* ----------------------------------------------------------------------------- pppoevpn_refuse() called by vpnd to refuse incomming connections return values: -1 error 0 handled - do not launch pppd ----------------------------------------------------------------------------- */ int pppoevpn_refuse(void) { int fdConn; struct sockaddr_pppoe ssSender; struct sockaddr *sapSender = (struct sockaddr *)&ssSender; int nSize = sizeof (ssSender); if ((fdConn = pppoe_sys_accept(listen_sockfd, sapSender, &nSize)) < 0) return -1; if (pppoe_sys_close(fdConn) < 0) return -1; return 0; } /* ----------------------------------------------------------------------------- pppoevpn_close() called by vpnd to close listening socket and cleanup. ----------------------------------------------------------------------------- */ void pppoevpn_close(void) { if (listen_sockfd != -1) { if (pppoe_sys_close(listen_sockfd) < 0) ; // do nothing listen_sockfd = -1; } }