1/*********************************************************************** 2* 3* pppoe.c 4* 5* Implementation of user-space PPPoE redirector for Linux. 6* 7* Copyright (C) 2000-2012 by Roaring Penguin Software Inc. 8* 9* This program may be distributed according to the terms of the GNU 10* General Public License, version 2 or (at your option) any later version. 11* 12* LIC: GPL 13* 14***********************************************************************/ 15 16static char const RCSID[] = 17"$Id$"; 18 19#include "pppoe.h" 20 21#ifdef HAVE_SYSLOG_H 22#include <syslog.h> 23#endif 24 25#ifdef HAVE_GETOPT_H 26#include <getopt.h> 27#endif 28 29#include <string.h> 30#include <stdlib.h> 31#include <errno.h> 32 33#ifdef HAVE_SYS_TIME_H 34#include <sys/time.h> 35#endif 36 37#ifdef HAVE_SYS_UIO_H 38#include <sys/uio.h> 39#endif 40 41#ifdef HAVE_UNISTD_H 42#include <unistd.h> 43#endif 44 45#ifdef USE_LINUX_PACKET 46#include <sys/ioctl.h> 47#include <fcntl.h> 48#endif 49 50#include <signal.h> 51 52#ifdef HAVE_N_HDLC 53#ifndef N_HDLC 54#include <linux/termios.h> 55#endif 56#endif 57 58/* Default interface if no -I option given */ 59#define DEFAULT_IF "eth0" 60 61/* Global variables -- options */ 62int optInactivityTimeout = 0; /* Inactivity timeout */ 63int optClampMSS = 0; /* Clamp MSS to this value */ 64int optSkipSession = 0; /* Perform discovery, print session info 65 and exit */ 66int optFloodDiscovery = 0; /* Flood server with discovery requests. 67 USED FOR STRESS-TESTING ONLY. DO NOT 68 USE THE -F OPTION AGAINST A REAL ISP */ 69 70PPPoEConnection *Connection = NULL; /* Must be global -- used 71 in signal handler */ 72 73/*********************************************************************** 74*%FUNCTION: sendSessionPacket 75*%ARGUMENTS: 76* conn -- PPPoE connection 77* packet -- the packet to send 78* len -- length of data to send 79*%RETURNS: 80* Nothing 81*%DESCRIPTION: 82* Transmits a session packet to the peer. 83***********************************************************************/ 84void 85sendSessionPacket(PPPoEConnection *conn, PPPoEPacket *packet, int len) 86{ 87 packet->length = htons(len); 88 if (optClampMSS) { 89 clampMSS(packet, "outgoing", optClampMSS); 90 } 91 if (sendPacket(conn, conn->sessionSocket, packet, len + HDR_SIZE) < 0) { 92 if (errno == ENOBUFS) { 93 /* No buffer space is a transient error */ 94 return; 95 } 96 exit(EXIT_FAILURE); 97 } 98#ifdef DEBUGGING_ENABLED 99 if (conn->debugFile) { 100 dumpPacket(conn->debugFile, packet, "SENT"); 101 fprintf(conn->debugFile, "\n"); 102 fflush(conn->debugFile); 103 } 104#endif 105 106} 107 108#ifdef USE_BPF 109/********************************************************************** 110*%FUNCTION: sessionDiscoveryPacket 111*%ARGUMENTS: 112* packet -- the discovery packet that was received 113*%RETURNS: 114* Nothing 115*%DESCRIPTION: 116* We got a discovery packet during the session stage. This most likely 117* means a PADT. 118* 119* The BSD version uses a single socket for both discovery and session 120* packets. When a packet comes in over the wire once we are in 121* session mode, either syncReadFromEth() or asyncReadFromEth() will 122* have already read the packet and determined it to be a discovery 123* packet before passing it here. 124***********************************************************************/ 125static void 126sessionDiscoveryPacket(PPPoEPacket *packet) 127{ 128 /* Sanity check */ 129 if (packet->code != CODE_PADT) { 130 return; 131 } 132 133 /* It's a PADT, all right. Is it for us? */ 134 if (packet->session != Connection->session) { 135 /* Nope, ignore it */ 136 return; 137 } 138 if (memcmp(packet->ethHdr.h_dest, Connection->myEth, ETH_ALEN)) { 139 return; 140 } 141 142 if (memcmp(packet->ethHdr.h_source, Connection->peerEth, ETH_ALEN)) { 143 return; 144 } 145 146 syslog(LOG_INFO, 147 "Session %d terminated -- received PADT from peer", 148 (int) ntohs(packet->session)); 149 parsePacket(packet, parseLogErrs, NULL); 150 sendPADT(Connection, "Received PADT from peer"); 151 exit(EXIT_SUCCESS); 152} 153#else 154/********************************************************************** 155*%FUNCTION: sessionDiscoveryPacket 156*%ARGUMENTS: 157* conn -- PPPoE connection 158*%RETURNS: 159* Nothing 160*%DESCRIPTION: 161* We got a discovery packet during the session stage. This most likely 162* means a PADT. 163***********************************************************************/ 164static void 165sessionDiscoveryPacket(PPPoEConnection *conn) 166{ 167 PPPoEPacket packet; 168 int len; 169 170 if (receivePacket(conn->discoverySocket, &packet, &len) < 0) { 171 return; 172 } 173 174 /* Check length */ 175 if (ntohs(packet.length) + HDR_SIZE > len) { 176 syslog(LOG_ERR, "Bogus PPPoE length field (%u)", 177 (unsigned int) ntohs(packet.length)); 178 return; 179 } 180 181 if (packet.code != CODE_PADT) { 182 /* Not PADT; ignore it */ 183 return; 184 } 185 186 /* It's a PADT, all right. Is it for us? */ 187 if (packet.session != conn->session) { 188 /* Nope, ignore it */ 189 return; 190 } 191 192 if (memcmp(packet.ethHdr.h_dest, conn->myEth, ETH_ALEN)) { 193 return; 194 } 195 196 if (memcmp(packet.ethHdr.h_source, conn->peerEth, ETH_ALEN)) { 197 return; 198 } 199#ifdef DEBUGGING_ENABLED 200 if (conn->debugFile) { 201 dumpPacket(conn->debugFile, &packet, "RCVD"); 202 fprintf(conn->debugFile, "\n"); 203 fflush(conn->debugFile); 204 } 205#endif 206 syslog(LOG_INFO, 207 "Session %d terminated -- received PADT from peer", 208 (int) ntohs(packet.session)); 209 parsePacket(&packet, parseLogErrs, NULL); 210 sendPADT(conn, "Received PADT from peer"); 211 exit(EXIT_SUCCESS); 212} 213#endif /* USE_BPF */ 214 215/********************************************************************** 216*%FUNCTION: session 217*%ARGUMENTS: 218* conn -- PPPoE connection info 219*%RETURNS: 220* Nothing 221*%DESCRIPTION: 222* Handles the "session" phase of PPPoE 223***********************************************************************/ 224void 225session(PPPoEConnection *conn) 226{ 227 fd_set readable; 228 PPPoEPacket packet; 229 struct timeval tv; 230 struct timeval *tvp = NULL; 231 int maxFD = 0; 232 int r; 233 234 /* Drop privileges */ 235 dropPrivs(); 236 237 /* Prepare for select() */ 238 if (conn->sessionSocket > maxFD) maxFD = conn->sessionSocket; 239 if (conn->discoverySocket > maxFD) maxFD = conn->discoverySocket; 240 maxFD++; 241 242 /* Fill in the constant fields of the packet to save time */ 243 memcpy(packet.ethHdr.h_dest, conn->peerEth, ETH_ALEN); 244 memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN); 245 packet.ethHdr.h_proto = htons(Eth_PPPOE_Session); 246 packet.ver = 1; 247 packet.type = 1; 248 packet.code = CODE_SESS; 249 packet.session = conn->session; 250 251 initPPP(); 252 253#ifdef USE_BPF 254 /* check for buffered session data */ 255 while (BPF_BUFFER_HAS_DATA) { 256 if (conn->synchronous) { 257 syncReadFromEth(conn, conn->sessionSocket, optClampMSS); 258 } else { 259 asyncReadFromEth(conn, conn->sessionSocket, optClampMSS); 260 } 261 } 262#endif 263 264 for (;;) { 265 if (optInactivityTimeout > 0) { 266 tv.tv_sec = optInactivityTimeout; 267 tv.tv_usec = 0; 268 tvp = &tv; 269 } 270 FD_ZERO(&readable); 271 FD_SET(0, &readable); /* ppp packets come from stdin */ 272 if (conn->discoverySocket >= 0) { 273 FD_SET(conn->discoverySocket, &readable); 274 } 275 FD_SET(conn->sessionSocket, &readable); 276 while(1) { 277 r = select(maxFD, &readable, NULL, NULL, tvp); 278 if (r >= 0 || errno != EINTR) break; 279 } 280 if (r < 0) { 281 fatalSys("select (session)"); 282 } 283 if (r == 0) { /* Inactivity timeout */ 284 syslog(LOG_ERR, "Inactivity timeout... something wicked happened on session %d", 285 (int) ntohs(conn->session)); 286 sendPADT(conn, "RP-PPPoE: Inactivity timeout"); 287 exit(EXIT_FAILURE); 288 } 289 290 /* Handle ready sockets */ 291 if (FD_ISSET(0, &readable)) { 292 if (conn->synchronous) { 293 syncReadFromPPP(conn, &packet); 294 } else { 295 asyncReadFromPPP(conn, &packet); 296 } 297 } 298 299 if (FD_ISSET(conn->sessionSocket, &readable)) { 300 do { 301 if (conn->synchronous) { 302 syncReadFromEth(conn, conn->sessionSocket, optClampMSS); 303 } else { 304 asyncReadFromEth(conn, conn->sessionSocket, optClampMSS); 305 } 306 } while (BPF_BUFFER_HAS_DATA); 307 } 308 309#ifndef USE_BPF 310 /* BSD uses a single socket, see *syncReadFromEth() */ 311 /* for calls to sessionDiscoveryPacket() */ 312 if (conn->discoverySocket >= 0) { 313 if (FD_ISSET(conn->discoverySocket, &readable)) { 314 sessionDiscoveryPacket(conn); 315 } 316 } 317#endif 318 319 } 320} 321 322 323/*********************************************************************** 324*%FUNCTION: sigPADT 325*%ARGUMENTS: 326* src -- signal received 327*%RETURNS: 328* Nothing 329*%DESCRIPTION: 330* If an established session exists send PADT to terminate from session 331* from our end 332***********************************************************************/ 333static void 334sigPADT(int src) 335{ 336 syslog(LOG_DEBUG,"Received signal %d on session %d.", 337 (int)src, (int) ntohs(Connection->session)); 338 sendPADTf(Connection, "RP-PPPoE: Received signal %d", src); 339 exit(EXIT_SUCCESS); 340} 341 342/********************************************************************** 343*%FUNCTION: usage 344*%ARGUMENTS: 345* argv0 -- program name 346*%RETURNS: 347* Nothing 348*%DESCRIPTION: 349* Prints usage information and exits. 350***********************************************************************/ 351void 352usage(char const *argv0) 353{ 354 fprintf(stderr, "Usage: %s [options]\n", argv0); 355 fprintf(stderr, "Options:\n"); 356#ifdef USE_BPF 357 fprintf(stderr, " -I if_name -- Specify interface (REQUIRED)\n"); 358#else 359 fprintf(stderr, " -I if_name -- Specify interface (default %s.)\n", 360 DEFAULT_IF); 361#endif 362#ifdef DEBUGGING_ENABLED 363 fprintf(stderr, " -D filename -- Log debugging information in filename.\n"); 364#endif 365 fprintf(stderr, 366 " -T timeout -- Specify inactivity timeout in seconds.\n" 367 " -t timeout -- Initial timeout for discovery packets in seconds\n" 368 " -V -- Print version and exit.\n" 369 " -A -- Print access concentrator names and exit.\n" 370 " -S name -- Set desired service name.\n" 371 " -C name -- Set desired access concentrator name.\n" 372 " -U -- Use Host-Unique to allow multiple PPPoE sessions.\n" 373 " -s -- Use synchronous PPP encapsulation.\n" 374 " -m MSS -- Clamp incoming and outgoing MSS options.\n" 375 " -p pidfile -- Write process-ID to pidfile.\n" 376 " -e sess:mac -- Skip discovery phase; use existing session.\n" 377 " -n -- Do not open discovery socket.\n" 378 " -k -- Kill a session with PADT (requires -e)\n" 379 " -d -- Perform discovery, print session info and exit.\n" 380 " -f disc:sess -- Set Ethernet frame types (hex).\n" 381 " -h -- Print usage information.\n\n" 382 "PPPoE Version %s, Copyright (C) 2001-2006 Roaring Penguin Software Inc.\n" 383 "PPPoE comes with ABSOLUTELY NO WARRANTY.\n" 384 "This is free software, and you are welcome to redistribute it under the terms\n" 385 "of the GNU General Public License, version 2 or any later version.\n" 386 "http://www.roaringpenguin.com\n", VERSION); 387 exit(EXIT_SUCCESS); 388} 389 390/********************************************************************** 391*%FUNCTION: main 392*%ARGUMENTS: 393* argc, argv -- count and values of command-line arguments 394*%RETURNS: 395* Nothing 396*%DESCRIPTION: 397* Main program 398***********************************************************************/ 399int 400main(int argc, char *argv[]) 401{ 402 int opt; 403 int n; 404 unsigned int m[6]; /* MAC address in -e option */ 405 unsigned int s; /* Temporary to hold session */ 406 FILE *pidfile; 407 unsigned int discoveryType, sessionType; 408 char const *options; 409 410 PPPoEConnection conn; 411 412#ifdef HAVE_N_HDLC 413 int disc = N_HDLC; 414 long flags; 415#endif 416 417 if (getuid() != geteuid() || 418 getgid() != getegid()) { 419 IsSetID = 1; 420 } 421 422 /* Initialize connection info */ 423 memset(&conn, 0, sizeof(conn)); 424 conn.discoverySocket = -1; 425 conn.sessionSocket = -1; 426 conn.discoveryTimeout = PADI_TIMEOUT; 427 428 /* For signal handler */ 429 Connection = &conn; 430 431 /* Initialize syslog */ 432 openlog("pppoe", LOG_PID, LOG_DAEMON); 433 434#ifdef DEBUGGING_ENABLED 435 options = "I:VAT:D:hS:C:Usm:np:e:kdf:F:t:"; 436#else 437 options = "I:VAT:hS:C:Usm:np:e:kdf:F:t:"; 438#endif 439 while((opt = getopt(argc, argv, options)) != -1) { 440 switch(opt) { 441 case 't': 442 if (sscanf(optarg, "%d", &conn.discoveryTimeout) != 1) { 443 fprintf(stderr, "Illegal argument to -t: Should be -t timeout\n"); 444 exit(EXIT_FAILURE); 445 } 446 if (conn.discoveryTimeout < 1) { 447 conn.discoveryTimeout = 1; 448 } 449 break; 450 case 'F': 451 if (sscanf(optarg, "%d", &optFloodDiscovery) != 1) { 452 fprintf(stderr, "Illegal argument to -F: Should be -F numFloods\n"); 453 exit(EXIT_FAILURE); 454 } 455 if (optFloodDiscovery < 1) optFloodDiscovery = 1; 456 fprintf(stderr, 457 "WARNING: DISCOVERY FLOOD IS MEANT FOR STRESS-TESTING\n" 458 "A PPPOE SERVER WHICH YOU OWN. DO NOT USE IT AGAINST\n" 459 "A REAL ISP. YOU HAVE 5 SECONDS TO ABORT.\n"); 460 sleep(5); 461 break; 462 case 'f': 463 if (sscanf(optarg, "%x:%x", &discoveryType, &sessionType) != 2) { 464 fprintf(stderr, "Illegal argument to -f: Should be disc:sess in hex\n"); 465 exit(EXIT_FAILURE); 466 } 467 Eth_PPPOE_Discovery = (UINT16_t) discoveryType; 468 Eth_PPPOE_Session = (UINT16_t) sessionType; 469 break; 470 case 'd': 471 optSkipSession = 1; 472 break; 473 474 case 'k': 475 conn.killSession = 1; 476 break; 477 478 case 'n': 479 /* Do not even open a discovery socket -- used when invoked 480 by pppoe-server */ 481 conn.noDiscoverySocket = 1; 482 break; 483 484 case 'e': 485 /* Existing session: "sess:xx:yy:zz:aa:bb:cc" where "sess" is 486 session-ID, and xx:yy:zz:aa:bb:cc is MAC-address of peer */ 487 n = sscanf(optarg, "%u:%2x:%2x:%2x:%2x:%2x:%2x", 488 &s, &m[0], &m[1], &m[2], &m[3], &m[4], &m[5]); 489 if (n != 7) { 490 fprintf(stderr, "Illegal argument to -e: Should be sess:xx:yy:zz:aa:bb:cc\n"); 491 exit(EXIT_FAILURE); 492 } 493 494 /* Copy MAC address of peer */ 495 for (n=0; n<6; n++) { 496 conn.peerEth[n] = (unsigned char) m[n]; 497 } 498 499 /* Convert session */ 500 conn.session = htons(s); 501 502 /* Skip discovery phase! */ 503 conn.skipDiscovery = 1; 504 break; 505 506 case 'p': 507 switchToRealID(); 508 pidfile = fopen(optarg, "w"); 509 if (pidfile) { 510 fprintf(pidfile, "%lu\n", (unsigned long) getpid()); 511 fclose(pidfile); 512 } 513 switchToEffectiveID(); 514 break; 515 case 'S': 516 SET_STRING(conn.serviceName, optarg); 517 break; 518 case 'C': 519 SET_STRING(conn.acName, optarg); 520 break; 521 case 's': 522 conn.synchronous = 1; 523 break; 524 case 'U': 525 conn.useHostUniq = 1; 526 break; 527#ifdef DEBUGGING_ENABLED 528 case 'D': 529 switchToRealID(); 530 conn.debugFile = fopen(optarg, "w"); 531 switchToEffectiveID(); 532 if (!conn.debugFile) { 533 fprintf(stderr, "Could not open %s: %s\n", 534 optarg, strerror(errno)); 535 exit(EXIT_FAILURE); 536 } 537 fprintf(conn.debugFile, "rp-pppoe-%s\n", VERSION); 538 fflush(conn.debugFile); 539 break; 540#endif 541 case 'T': 542 optInactivityTimeout = (int) strtol(optarg, NULL, 10); 543 if (optInactivityTimeout < 0) { 544 optInactivityTimeout = 0; 545 } 546 break; 547 case 'm': 548 optClampMSS = (int) strtol(optarg, NULL, 10); 549 if (optClampMSS < 536) { 550 fprintf(stderr, "-m: %d is too low (min 536)\n", optClampMSS); 551 exit(EXIT_FAILURE); 552 } 553 if (optClampMSS > 1452) { 554 fprintf(stderr, "-m: %d is too high (max 1452)\n", optClampMSS); 555 exit(EXIT_FAILURE); 556 } 557 break; 558 case 'I': 559 SET_STRING(conn.ifName, optarg); 560 break; 561 case 'V': 562 printf("Roaring Penguin PPPoE Version %s\n", VERSION); 563 exit(EXIT_SUCCESS); 564 case 'A': 565 conn.printACNames = 1; 566 break; 567 case 'h': 568 usage(argv[0]); 569 break; 570 default: 571 usage(argv[0]); 572 } 573 } 574 575 /* Pick a default interface name */ 576 if (!conn.ifName) { 577#ifdef USE_BPF 578 fprintf(stderr, "No interface specified (-I option)\n"); 579 exit(EXIT_FAILURE); 580#else 581 SET_STRING(conn.ifName, DEFAULT_IF); 582#endif 583 } 584 585 if (!conn.printACNames) { 586 587#ifdef HAVE_N_HDLC 588 if (conn.synchronous) { 589 if (ioctl(0, TIOCSETD, &disc) < 0) { 590 printErr("Unable to set line discipline to N_HDLC. Make sure your kernel supports the N_HDLC line discipline, or do not use the SYNCHRONOUS option. Quitting."); 591 exit(EXIT_FAILURE); 592 } else { 593 syslog(LOG_INFO, 594 "Changed pty line discipline to N_HDLC for synchronous mode"); 595 } 596 /* There is a bug in Linux's select which returns a descriptor 597 * as readable if N_HDLC line discipline is on, even if 598 * it isn't really readable. This return happens only when 599 * select() times out. To avoid blocking forever in read(), 600 * make descriptor 0 non-blocking */ 601 flags = fcntl(0, F_GETFL); 602 if (flags < 0) fatalSys("fcntl(F_GETFL)"); 603 if (fcntl(0, F_SETFL, (long) flags | O_NONBLOCK) < 0) { 604 fatalSys("fcntl(F_SETFL)"); 605 } 606 } 607#endif 608 609 } 610 611 if (optFloodDiscovery) { 612 for (n=0; n < optFloodDiscovery; n++) { 613 if (conn.printACNames) { 614 fprintf(stderr, "Sending discovery flood %d\n", n+1); 615 } 616 conn.discoverySocket = 617 openInterface(conn.ifName, Eth_PPPOE_Discovery, conn.myEth, NULL); 618 discovery(&conn); 619 conn.discoveryState = STATE_SENT_PADI; 620 close(conn.discoverySocket); 621 } 622 exit(EXIT_SUCCESS); 623 } 624 625 /* Open session socket before discovery phase, to avoid losing session */ 626 /* packets sent by peer just after PADS packet (noted on some Cisco */ 627 /* server equipment). */ 628 /* Opening this socket just before waitForPADS in the discovery() */ 629 /* function would be more appropriate, but it would mess-up the code */ 630 if (!optSkipSession) 631 conn.sessionSocket = openInterface(conn.ifName, Eth_PPPOE_Session, conn.myEth, NULL); 632 633 /* Skip discovery and don't open discovery socket? */ 634 if (conn.skipDiscovery && conn.noDiscoverySocket) { 635 conn.discoveryState = STATE_SESSION; 636 } else { 637 conn.discoverySocket = 638 openInterface(conn.ifName, Eth_PPPOE_Discovery, conn.myEth, NULL); 639 discovery(&conn); 640 } 641 if (optSkipSession) { 642 printf("%u:%02x:%02x:%02x:%02x:%02x:%02x\n", 643 ntohs(conn.session), 644 conn.peerEth[0], 645 conn.peerEth[1], 646 conn.peerEth[2], 647 conn.peerEth[3], 648 conn.peerEth[4], 649 conn.peerEth[5]); 650 exit(EXIT_SUCCESS); 651 } 652 653 /* Set signal handlers: send PADT on HUP; ignore TERM and INT */ 654 signal(SIGTERM, SIG_IGN); 655 signal(SIGINT, SIG_IGN); 656 signal(SIGHUP, sigPADT); 657 session(&conn); 658 return 0; 659} 660 661/********************************************************************** 662*%FUNCTION: fatalSys 663*%ARGUMENTS: 664* str -- error message 665*%RETURNS: 666* Nothing 667*%DESCRIPTION: 668* Prints a message plus the errno value to stderr and syslog and exits. 669***********************************************************************/ 670void 671fatalSys(char const *str) 672{ 673 char buf[1024]; 674 sprintf(buf, "%.256s: Session %d: %.256s", 675 str, (int) ntohs(Connection->session), strerror(errno)); 676 printErr(buf); 677 sendPADTf(Connection, "RP-PPPoE: System call error: %s", 678 strerror(errno)); 679 exit(EXIT_FAILURE); 680} 681 682/********************************************************************** 683*%FUNCTION: sysErr 684*%ARGUMENTS: 685* str -- error message 686*%RETURNS: 687* Nothing 688*%DESCRIPTION: 689* Prints a message plus the errno value to syslog. 690***********************************************************************/ 691void 692sysErr(char const *str) 693{ 694 char buf[1024]; 695 sprintf(buf, "%.256s: %.256s", str, strerror(errno)); 696 printErr(buf); 697} 698 699/********************************************************************** 700*%FUNCTION: rp_fatal 701*%ARGUMENTS: 702* str -- error message 703*%RETURNS: 704* Nothing 705*%DESCRIPTION: 706* Prints a message to stderr and syslog and exits. 707***********************************************************************/ 708void 709rp_fatal(char const *str) 710{ 711 printErr(str); 712 sendPADTf(Connection, "RP-PPPoE: Session %d: %.256s", 713 (int) ntohs(Connection->session), str); 714 exit(EXIT_FAILURE); 715} 716 717/********************************************************************** 718*%FUNCTION: asyncReadFromEth 719*%ARGUMENTS: 720* conn -- PPPoE connection info 721* sock -- Ethernet socket 722* clampMss -- if non-zero, do MSS-clamping 723*%RETURNS: 724* Nothing 725*%DESCRIPTION: 726* Reads a packet from the Ethernet interface and sends it to async PPP 727* device. 728***********************************************************************/ 729void 730asyncReadFromEth(PPPoEConnection *conn, int sock, int clampMss) 731{ 732 PPPoEPacket packet; 733 int len; 734 int plen; 735 int i; 736 unsigned char pppBuf[4096]; 737 unsigned char *ptr = pppBuf; 738 unsigned char c; 739 UINT16_t fcs; 740 unsigned char header[2] = {FRAME_ADDR, FRAME_CTRL}; 741 unsigned char tail[2]; 742#ifdef USE_BPF 743 int type; 744#endif 745 746 if (receivePacket(sock, &packet, &len) < 0) { 747 return; 748 } 749 750 /* Check length */ 751 if (ntohs(packet.length) + HDR_SIZE > len) { 752 syslog(LOG_ERR, "Bogus PPPoE length field (%u)", 753 (unsigned int) ntohs(packet.length)); 754 return; 755 } 756#ifdef DEBUGGING_ENABLED 757 if (conn->debugFile) { 758 dumpPacket(conn->debugFile, &packet, "RCVD"); 759 fprintf(conn->debugFile, "\n"); 760 fflush(conn->debugFile); 761 } 762#endif 763 764#ifdef USE_BPF 765 /* Make sure this is a session packet before processing further */ 766 type = etherType(&packet); 767 if (type == Eth_PPPOE_Discovery) { 768 sessionDiscoveryPacket(&packet); 769 } else if (type != Eth_PPPOE_Session) { 770 return; 771 } 772#endif 773 774 /* Sanity check */ 775 if (packet.code != CODE_SESS) { 776 syslog(LOG_ERR, "Unexpected packet code %d", (int) packet.code); 777 return; 778 } 779 if (packet.ver != 1) { 780 syslog(LOG_ERR, "Unexpected packet version %d", (int) packet.ver); 781 return; 782 } 783 if (packet.type != 1) { 784 syslog(LOG_ERR, "Unexpected packet type %d", (int) packet.type); 785 return; 786 } 787 if (memcmp(packet.ethHdr.h_dest, conn->myEth, ETH_ALEN)) { 788 return; 789 } 790 if (memcmp(packet.ethHdr.h_source, conn->peerEth, ETH_ALEN)) { 791 /* Not for us -- must be another session. This is not an error, 792 so don't log anything. */ 793 return; 794 } 795 796 if (packet.session != conn->session) { 797 /* Not for us -- must be another session. This is not an error, 798 so don't log anything. */ 799 return; 800 } 801 plen = ntohs(packet.length); 802 if (plen + HDR_SIZE > len) { 803 syslog(LOG_ERR, "Bogus length field in session packet %d (%d)", 804 (int) plen, (int) len); 805 return; 806 } 807 808 /* Clamp MSS */ 809 if (clampMss) { 810 clampMSS(&packet, "incoming", clampMss); 811 } 812 813 /* Compute FCS */ 814 fcs = pppFCS16(PPPINITFCS16, header, 2); 815 fcs = pppFCS16(fcs, packet.payload, plen) ^ 0xffff; 816 tail[0] = fcs & 0x00ff; 817 tail[1] = (fcs >> 8) & 0x00ff; 818 819 /* Build a buffer to send to PPP */ 820 *ptr++ = FRAME_FLAG; 821 *ptr++ = FRAME_ADDR; 822 *ptr++ = FRAME_ESC; 823 *ptr++ = FRAME_CTRL ^ FRAME_ENC; 824 825 for (i=0; i<plen; i++) { 826 c = packet.payload[i]; 827 if (c == FRAME_FLAG || c == FRAME_ADDR || c == FRAME_ESC || c < 0x20) { 828 *ptr++ = FRAME_ESC; 829 *ptr++ = c ^ FRAME_ENC; 830 } else { 831 *ptr++ = c; 832 } 833 } 834 for (i=0; i<2; i++) { 835 c = tail[i]; 836 if (c == FRAME_FLAG || c == FRAME_ADDR || c == FRAME_ESC || c < 0x20) { 837 *ptr++ = FRAME_ESC; 838 *ptr++ = c ^ FRAME_ENC; 839 } else { 840 *ptr++ = c; 841 } 842 } 843 *ptr++ = FRAME_FLAG; 844 845 /* Ship it out */ 846 if (write(1, pppBuf, (ptr-pppBuf)) < 0) { 847 fatalSys("asyncReadFromEth: write"); 848 } 849} 850 851/********************************************************************** 852*%FUNCTION: syncReadFromEth 853*%ARGUMENTS: 854* conn -- PPPoE connection info 855* sock -- Ethernet socket 856* clampMss -- if true, clamp MSS. 857*%RETURNS: 858* Nothing 859*%DESCRIPTION: 860* Reads a packet from the Ethernet interface and sends it to sync PPP 861* device. 862***********************************************************************/ 863void 864syncReadFromEth(PPPoEConnection *conn, int sock, int clampMss) 865{ 866 PPPoEPacket packet; 867 int len; 868 int plen; 869 struct iovec vec[2]; 870 unsigned char dummy[2]; 871#ifdef USE_BPF 872 int type; 873#endif 874 875 if (receivePacket(sock, &packet, &len) < 0) { 876 return; 877 } 878 879 /* Check length */ 880 if (ntohs(packet.length) + HDR_SIZE > len) { 881 syslog(LOG_ERR, "Bogus PPPoE length field (%u)", 882 (unsigned int) ntohs(packet.length)); 883 return; 884 } 885#ifdef DEBUGGING_ENABLED 886 if (conn->debugFile) { 887 dumpPacket(conn->debugFile, &packet, "RCVD"); 888 fprintf(conn->debugFile, "\n"); 889 fflush(conn->debugFile); 890 } 891#endif 892 893#ifdef USE_BPF 894 /* Make sure this is a session packet before processing further */ 895 type = etherType(&packet); 896 if (type == Eth_PPPOE_Discovery) { 897 sessionDiscoveryPacket(&packet); 898 } else if (type != Eth_PPPOE_Session) { 899 return; 900 } 901#endif 902 903 /* Sanity check */ 904 if (packet.code != CODE_SESS) { 905 syslog(LOG_ERR, "Unexpected packet code %d", (int) packet.code); 906 return; 907 } 908 if (packet.ver != 1) { 909 syslog(LOG_ERR, "Unexpected packet version %d", (int) packet.ver); 910 return; 911 } 912 if (packet.type != 1) { 913 syslog(LOG_ERR, "Unexpected packet type %d", (int) packet.type); 914 return; 915 } 916 if (memcmp(packet.ethHdr.h_dest, conn->myEth, ETH_ALEN)) { 917 /* Not for us -- must be another session. This is not an error, 918 so don't log anything. */ 919 return; 920 } 921 if (memcmp(packet.ethHdr.h_source, conn->peerEth, ETH_ALEN)) { 922 /* Not for us -- must be another session. This is not an error, 923 so don't log anything. */ 924 return; 925 } 926 if (packet.session != conn->session) { 927 /* Not for us -- must be another session. This is not an error, 928 so don't log anything. */ 929 return; 930 } 931 plen = ntohs(packet.length); 932 if (plen + HDR_SIZE > len) { 933 syslog(LOG_ERR, "Bogus length field in session packet %d (%d)", 934 (int) plen, (int) len); 935 return; 936 } 937 938 /* Clamp MSS */ 939 if (clampMss) { 940 clampMSS(&packet, "incoming", clampMss); 941 } 942 943 /* Ship it out */ 944 vec[0].iov_base = (void *) dummy; 945 dummy[0] = FRAME_ADDR; 946 dummy[1] = FRAME_CTRL; 947 vec[0].iov_len = 2; 948 vec[1].iov_base = (void *) packet.payload; 949 vec[1].iov_len = plen; 950 951 if (writev(1, vec, 2) < 0) { 952 fatalSys("syncReadFromEth: write"); 953 } 954} 955