⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pppoe.c

📁 PPPoE在Linux上的源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/***********************************************************************
*
* 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.
*
* LIC: GPL
*
***********************************************************************/

static char const RCSID[] =
"$Id: pppoe.c,v 1.35 2002/05/08 13:43:06 dfs Exp $";

#include "pppoe.h"

#ifdef HAVE_SYSLOG_H
#include <syslog.h>
#endif

#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif

#include <string.h>
#include <stdlib.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.
***********************************************************************/
void
sendSessionPacket(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) {
	if (errno == ENOBUFS) {
	    /* No buffer space is a transient error */
	    return;
	}
	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.
***********************************************************************/
void
sessionDiscoveryPacket(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 %d terminated -- received PADT from peer",
	   (int) ntohs(packet->session));
    parsePacket(packet, parseLogErrs, NULL);
    sendPADT(Connection, "Received PADT from peer");
    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.
***********************************************************************/
void
sessionDiscoveryPacket(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 %d terminated -- received PADT from peer",
	   (int) ntohs(packet.session));
    parsePacket(&packet, parseLogErrs, NULL);
    sendPADT(conn, "Received PADT from peer");
    exit(EXIT_SUCCESS);
}
#endif /* USE_BPF */

/**********************************************************************
*%FUNCTION: session
*%ARGUMENTS:
* conn -- PPPoE connection info
*%RETURNS:
* Nothing
*%DESCRIPTION:
* Handles the "session" phase of PPPoE
***********************************************************************/
void
session(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 on session %d",
		   (int) ntohs(conn->session));
	    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
***********************************************************************/
void
sigPADT(int src)
{
  syslog(LOG_DEBUG,"Received signal %d on session %d.",
	 (int)src, (int) ntohs(Connection->session));
  sendPADTf(Connection, "RP-PPPoE: Received signal %d", src);
  exit(EXIT_SUCCESS);
}

/**********************************************************************
*%FUNCTION: usage
*%ARGUMENTS:
* argv0 -- program name
*%RETURNS:
* Nothing
*%DESCRIPTION:
* Prints usage information and exits.
***********************************************************************/
void
usage(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
***********************************************************************/
int
main(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;

    /* Initialize syslog */
    openlog("pppoe", LOG_PID, LOG_DAEMON);

    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

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -