📄 pppoe.c
字号:
/************************************************************************* pppoe.c ** Implementation of user-space PPPoE redirector for Linux.** Copyright (C) 2000-2001 by Roaring Penguin Software Inc.** This program may be distributed according to the terms of the GNU* General Public License, version 2 or (at your option) any later version.************************************************************************/static char const RCSID[] ="$Id: pppoe.c,v 1.1 2002/03/15 23:37:31 m4 Exp $";#include <features.h>#include "pppoe.h"#include <stdlib.h>#ifdef HAVE_SYSLOG_H#include <syslog.h>#endif#ifdef HAVE_GETOPT_H#include <getopt.h>#endif#include <string.h>#include <errno.h>#ifdef HAVE_SYS_TIME_H#include <sys/time.h>#endif#ifdef HAVE_SYS_UIO_H#include <sys/uio.h>#endif#ifdef HAVE_UNISTD_H#include <unistd.h>#endif#ifdef USE_LINUX_PACKET#include <sys/ioctl.h>#include <fcntl.h>#endif#include <signal.h>#ifdef HAVE_N_HDLC#ifndef N_HDLC#include <linux/termios.h>#endif#endif/* Default interface if no -I option given */#define DEFAULT_IF "eth0"/* Global variables -- options */int optInactivityTimeout = 0; /* Inactivity timeout */int optClampMSS = 0; /* Clamp MSS to this value */int optSkipSession = 0; /* Perform discovery, print session info and exit */int optFloodDiscovery = 0; /* Flood server with discovery requests. USED FOR STRESS-TESTING ONLY. DO NOT USE THE -F OPTION AGAINST A REAL ISP */PPPoEConnection *Connection = NULL; /* Must be global -- used in signal handler *//************************************************************************%FUNCTION: sendSessionPacket*%ARGUMENTS:* conn -- PPPoE connection* packet -- the packet to send* len -- length of data to send*%RETURNS:* Nothing*%DESCRIPTION:* Transmits a session packet to the peer.***********************************************************************/voidsendSessionPacket(PPPoEConnection *conn, PPPoEPacket *packet, int len){ packet->length = htons(len); if (optClampMSS) { clampMSS(packet, "outgoing", optClampMSS); } if (sendPacket(conn, conn->sessionSocket, packet, len + HDR_SIZE) < 0) { exit(EXIT_FAILURE); } if (conn->debugFile) { dumpPacket(conn->debugFile, packet, "SENT"); fprintf(conn->debugFile, "\n"); fflush(conn->debugFile); }}#ifdef USE_BPF/***********************************************************************%FUNCTION: sessionDiscoveryPacket*%ARGUMENTS:* packet -- the discovery packet that was received*%RETURNS:* Nothing*%DESCRIPTION:* We got a discovery packet during the session stage. This most likely* means a PADT.** The BSD version uses a single socket for both discovery and session* packets. When a packet comes in over the wire once we are in* session mode, either syncReadFromEth() or asyncReadFromEth() will* have already read the packet and determined it to be a discovery* packet before passing it here.***********************************************************************/voidsessionDiscoveryPacket(PPPoEPacket *packet){ /* Sanity check */ if (packet->code != CODE_PADT) { return; } /* It's a PADT, all right. Is it for us? */ if (packet->session != Connection->session) { /* Nope, ignore it */ return; } if (memcmp(packet->ethHdr.h_dest, Connection->myEth, ETH_ALEN)) { return; } if (memcmp(packet->ethHdr.h_source, Connection->peerEth, ETH_ALEN)) { return; } syslog(LOG_INFO, "Session terminated -- received PADT from peer"); parsePacket(packet, parseLogErrs, NULL); exit(EXIT_SUCCESS);}#else/***********************************************************************%FUNCTION: sessionDiscoveryPacket*%ARGUMENTS:* conn -- PPPoE connection*%RETURNS:* Nothing*%DESCRIPTION:* We got a discovery packet during the session stage. This most likely* means a PADT.***********************************************************************/voidsessionDiscoveryPacket(PPPoEConnection *conn){ PPPoEPacket packet; int len; if (receivePacket(conn->discoverySocket, &packet, &len) < 0) { return; } /* Check length */ if (ntohs(packet.length) + HDR_SIZE > len) { syslog(LOG_ERR, "Bogus PPPoE length field (%u)", (unsigned int) ntohs(packet.length)); return; } if (packet.code != CODE_PADT) { /* Not PADT; ignore it */ return; } /* It's a PADT, all right. Is it for us? */ if (packet.session != conn->session) { /* Nope, ignore it */ return; } if (memcmp(packet.ethHdr.h_dest, conn->myEth, ETH_ALEN)) { return; } if (memcmp(packet.ethHdr.h_source, conn->peerEth, ETH_ALEN)) { return; } if (conn->debugFile) { dumpPacket(conn->debugFile, &packet, "RCVD"); fprintf(conn->debugFile, "\n"); fflush(conn->debugFile); } syslog(LOG_INFO, "Session terminated -- received PADT from peer"); parsePacket(&packet, parseLogErrs, NULL); exit(EXIT_SUCCESS);}#endif /* USE_BPF *//***********************************************************************%FUNCTION: session*%ARGUMENTS:* conn -- PPPoE connection info*%RETURNS:* Nothing*%DESCRIPTION:* Handles the "session" phase of PPPoE***********************************************************************/voidsession(PPPoEConnection *conn){ fd_set readable; PPPoEPacket packet; struct timeval tv; struct timeval *tvp = NULL; int maxFD = 0; int r; /* Open a session socket */ conn->sessionSocket = openInterface(conn->ifName, Eth_PPPOE_Session, conn->myEth); /* Prepare for select() */ if (conn->sessionSocket > maxFD) maxFD = conn->sessionSocket; if (conn->discoverySocket > maxFD) maxFD = conn->discoverySocket; maxFD++; /* Fill in the constant fields of the packet to save time */ memcpy(packet.ethHdr.h_dest, conn->peerEth, ETH_ALEN); memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN); packet.ethHdr.h_proto = htons(Eth_PPPOE_Session); packet.ver = 1; packet.type = 1; packet.code = CODE_SESS; packet.session = conn->session; initPPP();#ifdef USE_BPF /* check for buffered session data */ while (BPF_BUFFER_HAS_DATA) { if (conn->synchronous) { syncReadFromEth(conn, conn->sessionSocket, optClampMSS); } else { asyncReadFromEth(conn, conn->sessionSocket, optClampMSS); } }#endif for (;;) { if (optInactivityTimeout > 0) { tv.tv_sec = optInactivityTimeout; tv.tv_usec = 0; tvp = &tv; } FD_ZERO(&readable); FD_SET(0, &readable); /* ppp packets come from stdin */ if (conn->discoverySocket >= 0) { FD_SET(conn->discoverySocket, &readable); } FD_SET(conn->sessionSocket, &readable); while(1) { r = select(maxFD, &readable, NULL, NULL, tvp); if (r >= 0 || errno != EINTR) break; } if (r < 0) { fatalSys("select (session)"); } if (r == 0) { /* Inactivity timeout */ syslog(LOG_ERR, "Inactivity timeout... something wicked happened"); sendPADT(conn, "RP-PPPoE: Inactivity timeout"); exit(EXIT_FAILURE); } /* Handle ready sockets */ if (FD_ISSET(0, &readable)) { if (conn->synchronous) { syncReadFromPPP(conn, &packet); } else { asyncReadFromPPP(conn, &packet); } } if (FD_ISSET(conn->sessionSocket, &readable)) { do { if (conn->synchronous) { syncReadFromEth(conn, conn->sessionSocket, optClampMSS); } else { asyncReadFromEth(conn, conn->sessionSocket, optClampMSS); } } while (BPF_BUFFER_HAS_DATA); }#ifndef USE_BPF /* BSD uses a single socket, see *syncReadFromEth() */ /* for calls to sessionDiscoveryPacket() */ if (conn->discoverySocket >= 0) { if (FD_ISSET(conn->discoverySocket, &readable)) { sessionDiscoveryPacket(conn); } }#endif }}/************************************************************************%FUNCTION: sigPADT*%ARGUMENTS:* src -- signal received*%RETURNS:* Nothing*%DESCRIPTION:* If an established session exists send PADT to terminate from session* from our end***********************************************************************/voidsigPADT(int src){ syslog(LOG_DEBUG,"Received signal %d.",(int)src); sendPADT(Connection, "RP-PPPoE: Received signal"); exit(EXIT_SUCCESS);}/***********************************************************************%FUNCTION: usage*%ARGUMENTS:* argv0 -- program name*%RETURNS:* Nothing*%DESCRIPTION:* Prints usage information and exits.***********************************************************************/voidusage(char const *argv0){ fprintf(stderr, "Usage: %s [options]\n", argv0); fprintf(stderr, "Options:\n");#ifdef USE_BPF fprintf(stderr, " -I if_name -- Specify interface (REQUIRED)\n");#else fprintf(stderr, " -I if_name -- Specify interface (default %s).\n", DEFAULT_IF);#endif fprintf(stderr, " -T timeout -- Specify inactivity timeout in seconds.\n"); fprintf(stderr, " -D filename -- Log debugging information in filename.\n"); fprintf(stderr, " -V -- Print version and exit.\n"); fprintf(stderr, " -A -- Print access concentrator names and exit.\n"); fprintf(stderr, " -S name -- Set desired service name.\n"); fprintf(stderr, " -C name -- Set desired access concentrator name.\n"); fprintf(stderr, " -U -- Use Host-Unique to allow multiple PPPoE sessions.\n"); fprintf(stderr, " -s -- Use synchronous PPP encapsulation.\n"); fprintf(stderr, " -m MSS -- Clamp incoming and outgoing MSS options.\n"); fprintf(stderr, " -p pidfile -- Write process-ID to pidfile.\n"); fprintf(stderr, " -e sess:mac -- Skip discovery phase; use existing session.\n"); fprintf(stderr, " -n -- Do not open discovery socket.\n"); fprintf(stderr, " -k -- Kill a session with PADT (requires -e)\n"); fprintf(stderr, " -d -- Perform discovery, print session info and exit.\n"); fprintf(stderr, " -f disc:sess -- Set Ethernet frame types (hex).\n"); fprintf(stderr, " -h -- Print usage information.\n\n"); fprintf(stderr, "PPPoE Version %s, Copyright (C) 2001 Roaring Penguin Software Inc.\n", VERSION); fprintf(stderr, "PPPoE comes with ABSOLUTELY NO WARRANTY.\n"); fprintf(stderr, "This is free software, and you are welcome to redistribute it under the terms\n"); fprintf(stderr, "of the GNU General Public License, version 2 or any later version.\n"); fprintf(stderr, "http://www.roaringpenguin.com\n"); exit(EXIT_SUCCESS);}/***********************************************************************%FUNCTION: main*%ARGUMENTS:* argc, argv -- count and values of command-line arguments*%RETURNS:* Nothing*%DESCRIPTION:* Main program***********************************************************************/intmain(int argc, char *argv[]){ int opt; int n; unsigned int m[6]; /* MAC address in -e option */ unsigned int s; /* Temporary to hold session */ FILE *pidfile; unsigned int discoveryType, sessionType; PPPoEConnection conn;#ifdef HAVE_N_HDLC int disc = N_HDLC; long flags;#endif /* Initialize connection info */ memset(&conn, 0, sizeof(conn)); conn.discoverySocket = -1; conn.sessionSocket = -1; /* For signal handler */ Connection = &conn;#ifdef MACDUX /* Initialize syslog */ openlog("pppoe", LOG_PID, LOG_DAEMON);#endif while((opt = getopt(argc, argv, "I:VAT:D:hS:C:Usm:np:e:kdf:F:")) != -1) { switch(opt) { case 'F': if (sscanf(optarg, "%d", &optFloodDiscovery) != 1) { fprintf(stderr, "Illegal argument to -F: Should be -F numFloods\n"); exit(EXIT_FAILURE); } if (optFloodDiscovery < 1) optFloodDiscovery = 1; fprintf(stderr, "WARNING: DISCOVERY FLOOD IS MEANT FOR STRESS-TESTING\n"); fprintf(stderr, "A PPPOE SERVER WHICH YOU OWN. DO NOT USE IT AGAINST\n"); fprintf(stderr, "A REAL ISP. YOU HAVE 5 SECONDS TO ABORT.\n"); sleep(5); break; case 'f': if (sscanf(optarg, "%x:%x", &discoveryType, &sessionType) != 2) { fprintf(stderr, "Illegal argument to -f: Should be disc:sess in hex\n"); exit(EXIT_FAILURE); } Eth_PPPOE_Discovery = (UINT16_t) discoveryType; Eth_PPPOE_Session = (UINT16_t) sessionType; break; case 'd': optSkipSession = 1; break; case 'k': conn.killSession = 1; break; case 'n': /* Do not even open a discovery socket -- used when invoked by pppoe-server */ conn.noDiscoverySocket = 1; break; case 'e':
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -