1/* 2 * Copyright (c) 2000 Apple Computer, 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 * 26 * Theory of operation : 27 * 28 * plugin to add a generic socket support to pppd, instead of tty. 29 * 30----------------------------------------------------------------------------- */ 31 32 33/* ----------------------------------------------------------------------------- 34 Includes 35----------------------------------------------------------------------------- */ 36 37#include <sys/param.h> 38#include <sys/types.h> 39#include <sys/wait.h> 40#include <sys/time.h> 41#include <sys/resource.h> 42#include <sys/socket.h> 43#include <sys/stat.h> 44#include <sys/socket.h> 45#include <netinet/in.h> 46#include <arpa/inet.h> 47#include <syslog.h> 48#include <sys/ioctl.h> 49#include <net/if.h> 50#include <sys/sys_domain.h> 51 52#include <stdio.h> 53#include <ctype.h> 54#include <stdlib.h> 55#include <string.h> 56#include <unistd.h> 57#include <signal.h> 58#include <errno.h> 59#include <fcntl.h> 60#include <syslog.h> 61#include <netdb.h> 62#include <pwd.h> 63#include <setjmp.h> 64 65#include <CoreFoundation/CFBundle.h> 66 67#define APPLE 1 68 69#include "../../../Family/ppp_defs.h" 70#include "../../../Family/if_ppp.h" 71#include "../../../Family/ppp_domain.h" 72#include "../PPPoE-extension/PPPoE.h" 73#include "../../../Helpers/pppd/pppd.h" 74#include "../../../Helpers/pppd/fsm.h" 75#include "../../../Helpers/pppd/lcp.h" 76 77 78/* ----------------------------------------------------------------------------- 79 Definitions 80----------------------------------------------------------------------------- */ 81 82#define MODE_CONNECT "connect" 83#define MODE_LISTEN "listen" 84#define MODE_ANSWER "answer" 85 86#define PPPOE_NKE "PPPoE.kext" 87#define PPPOE_NKE_ID "com.apple.nke.pppoe" 88 89/* ----------------------------------------------------------------------------- 90 Forward declarations 91----------------------------------------------------------------------------- */ 92void pppoe_process_extra_options(); 93void pppoe_check_options(); 94int pppoe_connect(int *errorcode); 95void pppoe_disconnect(); 96void pppoe_close(); 97void pppoe_cleanup(); 98int pppoe_establish_ppp(int); 99void pppoe_wait_input(); 100void pppoe_disestablish_ppp(int); 101void pppoe_link_down(void *arg, uintptr_t p); 102 103static int pppoe_dial(); 104static int pppoe_listen(); 105static void closeall(); 106static u_long load_kext(char *kext, int byBundleID); 107 108/* ----------------------------------------------------------------------------- 109 PPP globals 110----------------------------------------------------------------------------- */ 111 112 113static int sockfd = -1; /* socket file descriptor */ 114static CFBundleRef bundle = 0; /* our bundle ref */ 115 116/* option variables */ 117static char *mode = MODE_CONNECT; /* connect mode by default */ 118static bool loopback = 0; /* loop back mode */ 119static bool noload = 0; /* don't load the kernel extension */ 120static char *service = NULL; /* service selection to use */ 121static char *access_concentrator = NULL; /* access concentrator to connect to */ 122static int retrytimer = 0; /* retry timer (default is 3 seconds) */ 123static int connecttimer = 65; /* bump the connection timer from 20 to 65 seconds */ 124static bool linkdown = 0; /* flag set when we receive link down event */ 125 126extern int kill_link; 127 128/* option descriptors */ 129option_t pppoe_options[] = { 130 { "pppoeservicename", o_string, &service, 131 "PPPoE service to choose" }, 132 { "pppoeacname", o_string, &access_concentrator, 133 "PPPoE Access Concentrator to connect to" }, 134 { "pppoeloopback", o_bool, &loopback, 135 "Configure PPPoE in loopback mode, for single machine testing", 1 }, 136 { "nopppoeload", o_bool, &noload, 137 "Don't try to load the PPPoE kernel extension", 1 }, 138 { "pppoemode", o_string, &mode, 139 "Configure configuration mode [connect, listen, answer]" }, 140 { "pppoeconnecttimer", o_int, &connecttimer, 141 "Connect timer for outgoing call (default 65 seconds)" }, 142 { "pppoeretrytimer", o_int, &retrytimer, 143 "Retry timer for outgoing call (default 3 seconds)" }, 144 { NULL } 145}; 146 147 148/* ----------------------------------------------------------------------------- 149plugin entry point, called by pppd 150----------------------------------------------------------------------------- */ 151int start(CFBundleRef ref) 152{ 153 154 bundle = ref; 155 CFRetain(bundle); 156 157 // hookup our socket handlers 158 bzero(the_channel, sizeof(struct channel)); 159 the_channel->options = pppoe_options; 160 the_channel->process_extra_options = pppoe_process_extra_options; 161 the_channel->wait_input = pppoe_wait_input; 162 the_channel->check_options = pppoe_check_options; 163 the_channel->connect = pppoe_connect; 164 the_channel->disconnect = pppoe_disconnect; 165 the_channel->cleanup = pppoe_cleanup; 166 the_channel->close = pppoe_close; 167 the_channel->establish_ppp = pppoe_establish_ppp; 168 the_channel->disestablish_ppp = pppoe_disestablish_ppp; 169 // use the default config functions 170 the_channel->send_config = generic_send_config; 171 the_channel->recv_config = generic_recv_config; 172 173 add_notifier(&link_down_notifier, pppoe_link_down, 0); 174 return 0; 175} 176 177/* ----------------------------------------------------------------------------- 178----------------------------------------------------------------------------- */ 179void pppoe_link_down(void *arg, uintptr_t p) 180{ 181 linkdown = 1; 182} 183 184/* ----------------------------------------------------------------------------- 185work out which device we are using and read its options file 186----------------------------------------------------------------------------- */ 187void pppoe_process_extra_options() 188{ 189 190 if (!device) 191 device = "en0"; 192 193 if (!strcmp(mode, MODE_ANSWER)) { 194 // make sure we get a file descriptor > 2 so that pppd can detach and close 0,1,2 195 sockfd = dup(0); 196 } 197} 198 199/* ----------------------------------------------------------------------------- 200do consistency checks on the options we were given 201----------------------------------------------------------------------------- */ 202void pppoe_check_options() 203{ 204} 205 206/* ----------------------------------------------------------------------------- 207called back everytime we go out of select, and data needs to be read 208the hook is called and has a chance to get data out of its file descriptor 209in the case of PPPoE, we are not supposed to get data on the socket 210if our socket gets awaken, that's because is has been closed 211----------------------------------------------------------------------------- */ 212void pppoe_wait_input() 213{ 214 215 if (sockfd != -1 && is_ready_fd(sockfd)) { 216 // looks like we have been disconnected... 217 // the status is updated only if link is not already down 218 if (linkdown == 0) { 219 notice("PPPoE hangup"); 220 status = EXIT_HANGUP; 221 } 222 remove_fd(sockfd); 223 hungup = 1; 224 lcp_lowerdown(0); /* PPPoE link is no longer available */ 225 link_terminated(0); 226 } 227} 228 229/* ----------------------------------------------------------------------------- 230get the socket ready to start doing PPP. 231That is, open the socket and run the connector 232----------------------------------------------------------------------------- */ 233int pppoe_connect(int *errorcode) 234{ 235 char dev[32], name[MAXPATHLEN]; 236 int err = 0, len, s; 237 CFURLRef url; 238 struct ifreq ifr; 239 240 *errorcode = 0; 241 242 snprintf(dev, sizeof(dev), "socket[%d:%d]", PF_PPP, PPPPROTO_PPPOE); 243 strlcpy(ppp_devnam, dev, sizeof(ppp_devnam)); 244 245 hungup = 0; 246 kill_link = 0; 247 linkdown = 0; 248 249 err = -1; 250 s = socket(PF_SYSTEM, SOCK_RAW, SYSPROTO_EVENT); 251 if (s >= 0) { 252 253 len = strlen(device); 254 if (len <= sizeof(ifr.ifr_name)) { 255 256 bzero(&ifr, sizeof(ifr)); 257 bcopy(device, ifr.ifr_name, len); 258 if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) >= 0) { 259 // ensure that the device is UP 260 ifr.ifr_flags |= IFF_UP; 261 if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) >= 0) 262 err = 0; 263 } 264 } 265 266 close(s); 267 } 268 if (err) { 269 error("PPPoE cannot use interface '%s'.", device); 270 status = EXIT_OPEN_FAILED; 271 return -1; 272 } 273 274 if (strcmp(mode, MODE_ANSWER)) { 275 /* open the socket */ 276 sockfd = socket(PF_PPP, SOCK_DGRAM, PPPPROTO_PPPOE); 277 if (sockfd < 0) { 278 if (!noload) { 279 if ((url = CFBundleCopyBundleURL(bundle))) { 280 name[0] = 0; 281 CFURLGetFileSystemRepresentation(url, 0, (UInt8 *)name, MAXPATHLEN - 1); 282 CFRelease(url); 283 strlcat(name, "/", sizeof(name)); 284 if ((url = CFBundleCopyBuiltInPlugInsURL(bundle))) { 285 CFURLGetFileSystemRepresentation(url, 0, (UInt8 *)(name + strlen(name)), 286 MAXPATHLEN - strlen(name) - strlen(PPPOE_NKE) - 1); 287 CFRelease(url); 288 strlcat(name, "/", sizeof(name)); 289 strlcat(name, PPPOE_NKE, sizeof(name)); 290#if !TARGET_OS_EMBEDDED // This file is not built for Embedded 291 if (!load_kext(name, 0)) 292#else 293 if (!load_kext(PPPOE_NKE_ID, 1)) 294#endif 295 sockfd = socket(PF_PPP, SOCK_DGRAM, PPPPROTO_PPPOE); 296 } 297 } 298 } 299 if (sockfd < 0) { 300 error("Failed to open PPPoE socket: %m"); 301 status = EXIT_OPEN_FAILED; 302 return -1; 303 } 304 } 305 } 306 307 if (loopback || debug) { 308 u_int32_t flags; 309 flags = (loopback ? PPPOE_FLAG_LOOPBACK : 0) 310 + ((kdebugflag & 1) ? PPPOE_FLAG_DEBUG : 0); 311 if (setsockopt(sockfd, PPPPROTO_PPPOE, PPPOE_OPT_FLAGS, &flags, 4)) { 312 error("PPPoE can't set PPPoE flags...\n"); 313 return errno; 314 } 315 if (loopback) 316 notice("PPPoE loopback activated...\n"); 317 } 318 319 if (connecttimer) { 320 u_int16_t timer = connecttimer; 321 if (setsockopt(sockfd, PPPPROTO_PPPOE, PPPOE_OPT_CONNECT_TIMER, &timer, 2)) { 322 error("PPPoE can't set PPPoE connect timer...\n"); 323 return errno; 324 } 325 } 326 327 if (retrytimer) { 328 u_int16_t timer = retrytimer; 329 if (setsockopt(sockfd, PPPPROTO_PPPOE, PPPOE_OPT_RETRY_TIMER, &timer, 2)) { 330 error("PPPoE can't set PPPoE retry timer...\n"); 331 return errno; 332 } 333 } 334 335 if (setsockopt(sockfd, PPPPROTO_PPPOE, PPPOE_OPT_INTERFACE, device, strlen(device))) { 336 error("PPPoE can't specify interface...\n"); 337 return errno; 338 } 339 340 if (!strcmp(mode, MODE_ANSWER)) { 341 // nothing to do 342 } 343 else if (!strcmp(mode, MODE_LISTEN)) { 344 err = pppoe_listen(); 345 } 346 else if (!strcmp(mode, MODE_CONNECT)) { 347 err = pppoe_dial(); 348 } 349 else 350 fatal("PPPoE incorrect mode : '%s'", mode ? mode : ""); 351 352 if (err) { 353 if (err != -2) { 354 if (err != -1) 355 devstatus = err; 356 status = EXIT_CONNECT_FAILED; 357 } 358 return -1; 359 } 360 361 return sockfd; 362} 363 364/* ----------------------------------------------------------------------------- 365run the disconnector connector 366----------------------------------------------------------------------------- */ 367void pppoe_disconnect() 368{ 369 notice("PPPoE disconnecting...\n"); 370 371 if (shutdown(sockfd, SHUT_RDWR) < 0) { 372 error("PPPoE disconnection failed, error = %d.\n", errno); 373 return; 374 } 375 376 notice("PPPoE disconnected\n"); 377} 378 379/* ----------------------------------------------------------------------------- 380close the socket descriptors 381----------------------------------------------------------------------------- */ 382void pppoe_close() 383{ 384 if (sockfd >= 0) { 385 close(sockfd); 386 sockfd = -1; 387 } 388} 389 390/* ----------------------------------------------------------------------------- 391clean up before quitting 392----------------------------------------------------------------------------- */ 393void pppoe_cleanup() 394{ 395 pppoe_close(); 396} 397 398/* ----------------------------------------------------------------------------- 399establish the socket as a ppp link 400----------------------------------------------------------------------------- */ 401int pppoe_establish_ppp(int fd) 402{ 403 int x, new_fd; 404 405 if (ioctl(fd, PPPIOCATTACH, &x) < 0) { 406 error("Couldn't attach socket to the link layer: %m"); 407 return -1; 408 } 409 410 new_fd = generic_establish_ppp(fd, NULL); 411 if (new_fd == -1) 412 return -1; 413 414 /* add our pppoe socket to the select */ 415 add_fd(fd); 416 417 return new_fd; 418} 419 420/* ----------------------------------------------------------------------------- 421disestablish the socket as a ppp link 422----------------------------------------------------------------------------- */ 423void pppoe_disestablish_ppp(int fd) 424{ 425 int x; 426 427 remove_fd(fd); 428 429 if (ioctl(fd, PPPIOCDETACH, &x) < 0) 430 error("Couldn't detach socket from link layer: %m"); 431 432 generic_disestablish_ppp(fd); 433} 434 435/* ----------------------------------------------------------------------------- 436----------------------------------------------------------------------------- */ 437int pppoe_dial() 438{ 439 struct sockaddr_pppoe addr; 440 u_short len, i; 441 unsigned char ac_address[ETHER_ADDR_LEN]; 442 char ac_string[ETHER_ADDR_LEN * 3]; 443 socklen_t ac_len = ETHER_ADDR_LEN; 444 int err; 445 446 // if the specific pppoe option are not used, try to see 447 // if the remote address generic field can be decomposed 448 // in the form of service_name\access_concentrator 449 if (!service && !access_concentrator && remoteaddress) { 450 len = strlen(remoteaddress); 451 for (i = 0; i < len; i++) 452 if (remoteaddress[i] == '\\') 453 break; 454 if ((service = malloc(i + 1))) { 455 strncpy(service, remoteaddress, i); 456 service[i] = 0; 457 } 458 if (i < len) { 459 if ((access_concentrator = malloc(len - i))) 460 strlcpy(access_concentrator, &remoteaddress[i + 1], len - i); 461 } 462 } 463 464 notice("PPPoE connecting to service '%s' [access concentrator '%s']...\n", 465 service ? service : "", 466 access_concentrator ? access_concentrator : ""); 467 468 bzero(&addr, sizeof(addr)); 469 addr.ppp.ppp_len = sizeof(struct sockaddr_pppoe); 470 addr.ppp.ppp_family = AF_PPP; 471 addr.ppp.ppp_proto = PPPPROTO_PPPOE; 472 if (access_concentrator) 473 strncpy(addr.pppoe_ac_name, access_concentrator, sizeof(addr.pppoe_ac_name)); 474 if (service) 475 strncpy(addr.pppoe_service, service, sizeof(addr.pppoe_service)); 476 if (connect(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_pppoe)) < 0) { 477 if (errno == EINTR) 478 return -2; // user cancelled 479 error("PPPoE connection failed, %m"); 480 switch (errno) { 481 case EHOSTUNREACH: 482 if ((service && service[0]) && (access_concentrator && access_concentrator[0])) 483 return EXIT_PPPoE_NOACSERVICE; 484 else if (service && service[0]) 485 return EXIT_PPPoE_NOSERVICE; 486 else if (access_concentrator && access_concentrator[0]) 487 return EXIT_PPPoE_NOAC; 488 return EXIT_PPPoE_NOSERVER; 489 490 case ECONNREFUSED: 491 return EXIT_PPPoE_CONNREFUSED; 492 493 case ENXIO: 494 // Ethernet interface is detached 495 // fake a cancel to get a consistent 496 // error message with the HANGUP case 497 status = EXIT_HANGUP; 498 return -2; 499 500 } 501 return -1; 502 } 503 504 /* build network signature from the Access Concentrator address */ 505 err = getsockopt(sockfd, PPPPROTO_PPPOE, PPPOE_OPT_PEER_ENETADDR, ac_address, &ac_len); 506 if (err == -1) { 507 warning("PPPoE cannot retrieve access concentrator address, %m"); 508 } 509 else { 510 snprintf(ac_string, sizeof(ac_string), "%02X:%02X:%2X:%02X:%02X:%02X", ac_address[0], ac_address[1], ac_address[2], ac_address[3], ac_address[4], ac_address[5]); 511 set_network_signature("PPPoE.AccessConcentratorAddress", ac_string, 0, 0); 512 } 513 514 notice("PPPoE connection established."); 515 return 0; 516} 517 518 519/* ----------------------------------------------------------------------------- 520----------------------------------------------------------------------------- */ 521int pppoe_listen() 522{ 523 struct sockaddr_pppoe addr; 524 socklen_t len; 525 int fd; 526 527 notice("PPPoE listening on service '%s' [access concentrator '%s']...\n", 528 service ? service : "", 529 access_concentrator ? access_concentrator : ""); 530 531 bzero(&addr, sizeof(addr)); 532 addr.ppp.ppp_len = sizeof(struct sockaddr_pppoe); 533 addr.ppp.ppp_family = AF_PPP; 534 addr.ppp.ppp_proto = PPPPROTO_PPPOE; 535 if (access_concentrator) 536 strncpy(addr.pppoe_ac_name, access_concentrator, sizeof(addr.pppoe_ac_name)); 537 if (service) 538 strncpy(addr.pppoe_service, service, sizeof(addr.pppoe_service)); 539 if (bind(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_pppoe)) < 0) { 540 error("PPPoE bind failed, %m"); 541 return -1; 542 } 543 544 if (listen(sockfd, 10) < 0) { 545 error("PPPoE listen failed, %m"); 546 return -1; 547 } 548 549 len = sizeof(addr); 550 fd = accept(sockfd, (struct sockaddr *) &addr, &len); 551 if (fd < 0) { 552 error("PPPoE accept failed, %m"); 553 return -1; 554 } 555 556 close(sockfd); // close the socket used for listening 557 sockfd = fd; // use the accepted socket instead of 558 559 notice("PPPoE connection established in incoming call."); 560 return 0; 561} 562 563/* ----------------------------------------------------------------------------- 564----------------------------------------------------------------------------- */ 565void closeall() 566{ 567 int i; 568 569 for (i = getdtablesize() - 1; i >= 0; i--) close(i); 570 open("/dev/null", O_RDWR, 0); 571 dup(0); 572 dup(0); 573 return; 574} 575 576/* ----------------------------------------------------------------------------- 577----------------------------------------------------------------------------- */ 578u_long load_kext(char *kext, int byBundleID) 579{ 580 int pid; 581 582 if ((pid = fork()) < 0) 583 return 1; 584 585 if (pid == 0) { 586 closeall(); 587 // PPP kernel extension not loaded, try load it... 588 if (byBundleID) 589 execle("/sbin/kextload", "kextload", "-b", kext, (char *)0, (char *)0); 590 else 591 execle("/sbin/kextload", "kextload", kext, (char *)0, (char *)0); 592 exit(1); 593 } 594 595 while (waitpid(pid, 0, 0) < 0) { 596 if (errno == EINTR) 597 continue; 598 return 1; 599 } 600 return 0; 601} 602 603