📄 relay.c
字号:
/***********************************************************************
*
* relay.c
*
* Implementation of PPPoE relay
*
* Copyright (C) 2001 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
*
* $Id: relay.c,v 1.24 2002/04/09 17:28:39 dfs Exp $
*
***********************************************************************/
static char const RCSID[] =
"$Id: relay.c,v 1.24 2002/04/09 17:28:39 dfs Exp $";
#define _GNU_SOURCE 1 /* For SA_RESTART */
#include "relay.h"
#include <signal.h>
#ifdef HAVE_SYSLOG_H
#include <syslog.h>
#endif
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
#include <stdlib.h>
#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
/* Interfaces (max MAX_INTERFACES) */
PPPoEInterface Interfaces[MAX_INTERFACES];
int NumInterfaces;
/* Relay info */
int NumSessions;
int MaxSessions;
PPPoESession *AllSessions;
PPPoESession *FreeSessions;
PPPoESession *ActiveSessions;
SessionHash *AllHashes;
SessionHash *FreeHashes;
SessionHash *Buckets[HASHTAB_SIZE];
volatile unsigned int Epoch = 0;
volatile unsigned int CleanCounter = 0;
/* How often to clean up stale sessions? */
#define MIN_CLEAN_PERIOD 30 /* Minimum period to run cleaner */
#define TIMEOUT_DIVISOR 20 /* How often to run cleaner per timeout period */
unsigned int CleanPeriod = MIN_CLEAN_PERIOD;
/* How long a session can be idle before it is cleaned up? */
unsigned int IdleTimeout = MIN_CLEAN_PERIOD * TIMEOUT_DIVISOR;
/* Pipe for breaking select() to initiate periodic cleaning */
int CleanPipe[2];
/* Our relay: if_index followed by peer_mac */
#define MY_RELAY_TAG_LEN (sizeof(int) + ETH_ALEN)
/* Hack for daemonizing */
#define CLOSEFD 64
/**********************************************************************
*%FUNCTION: keepDescriptor
*%ARGUMENTS:
* fd -- a file descriptor
*%RETURNS:
* 1 if descriptor should NOT be closed during daemonizing; 0 otherwise.
***********************************************************************/
static int
keepDescriptor(int fd)
{
int i;
if (fd == CleanPipe[0] || fd == CleanPipe[1]) return 1;
for (i=0; i<NumInterfaces; i++) {
if (fd == Interfaces[i].discoverySock ||
fd == Interfaces[i].sessionSock) return 1;
}
return 0;
}
/**********************************************************************
*%FUNCTION: addTag
*%ARGUMENTS:
* packet -- a PPPoE packet
* tag -- tag to add
*%RETURNS:
* -1 if no room in packet; number of bytes added otherwise.
*%DESCRIPTION:
* Inserts a tag as the first tag in a PPPoE packet.
***********************************************************************/
int
addTag(PPPoEPacket *packet, PPPoETag const *tag)
{
return insertBytes(packet, packet->payload, tag,
ntohs(tag->length) + TAG_HDR_SIZE);
}
/**********************************************************************
*%FUNCTION: insertBytes
*%ARGUMENTS:
* packet -- a PPPoE packet
* loc -- location at which to insert bytes of data
* bytes -- the data to insert
* len -- length of data to insert
*%RETURNS:
* -1 if no room in packet; len otherwise.
*%DESCRIPTION:
* Inserts "len" bytes of data at location "loc" in "packet", moving all
* other data up to make room.
***********************************************************************/
int
insertBytes(PPPoEPacket *packet,
unsigned char *loc,
void const *bytes,
int len)
{
int toMove;
int plen = ntohs(packet->length);
/* Sanity checks */
if (loc < packet->payload ||
loc > packet->payload + plen ||
len + plen > MAX_PPPOE_PAYLOAD) {
return -1;
}
toMove = (packet->payload + plen) - loc;
memmove(loc+len, loc, toMove);
memcpy(loc, bytes, len);
packet->length = htons(plen + len);
return len;
}
/**********************************************************************
*%FUNCTION: removeBytes
*%ARGUMENTS:
* packet -- a PPPoE packet
* loc -- location at which to remove bytes of data
* len -- length of data to remove
*%RETURNS:
* -1 if there was a problem, len otherwise
*%DESCRIPTION:
* Removes "len" bytes of data from location "loc" in "packet", moving all
* other data down to close the gap
***********************************************************************/
int
removeBytes(PPPoEPacket *packet,
unsigned char *loc,
int len)
{
int toMove;
int plen = ntohs(packet->length);
/* Sanity checks */
if (len < 0 || len > plen ||
loc < packet->payload ||
loc + len > packet->payload + plen) {
return -1;
}
toMove = ((packet->payload + plen) - loc) - len;
memmove(loc, loc+len, toMove);
packet->length = htons(plen - len);
return len;
}
/**********************************************************************
*%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");
fprintf(stderr, " -S if_name -- Specify interface for PPPoE Server\n");
fprintf(stderr, " -C if_name -- Specify interface for PPPoE Client\n");
fprintf(stderr, " -B if_name -- Specify interface for both clients and server\n");
fprintf(stderr, " -n nsess -- Maxmimum number of sessions to relay\n");
fprintf(stderr, " -i timeout -- Idle timeout in seconds (0 = no timeout)\n");
fprintf(stderr, " -F -- Do not fork into background\n");
fprintf(stderr, " -h -- Print this help message\n");
fprintf(stderr, "\nPPPoE 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 -- usual suspects
*%RETURNS:
* EXIT_SUCCESS or EXIT_FAILURE
*%DESCRIPTION:
* Main program. Options:
* -C ifname -- Use interface for PPPoE clients
* -S ifname -- Use interface for PPPoE servers
* -B ifname -- Use interface for both clients and servers
* -n sessions -- Maximum of "n" sessions
***********************************************************************/
int
main(int argc, char *argv[])
{
int opt;
int nsess = DEFAULT_SESSIONS;
struct sigaction sa;
int beDaemon = 1;
openlog("pppoe-relay", LOG_PID, LOG_DAEMON);
while((opt = getopt(argc, argv, "hC:S:B:n:i:F")) != -1) {
switch(opt) {
case 'h':
usage(argv[0]);
break;
case 'F':
beDaemon = 0;
break;
case 'C':
addInterface(optarg, 1, 0);
break;
case 'S':
addInterface(optarg, 0, 1);
break;
case 'B':
addInterface(optarg, 1, 1);
break;
case 'i':
if (sscanf(optarg, "%u", &IdleTimeout) != 1) {
fprintf(stderr, "Illegal argument to -i: should be -i timeout\n");
exit(EXIT_FAILURE);
}
CleanPeriod = IdleTimeout / TIMEOUT_DIVISOR;
if (CleanPeriod < MIN_CLEAN_PERIOD) CleanPeriod = MIN_CLEAN_PERIOD;
break;
case 'n':
if (sscanf(optarg, "%d", &nsess) != 1) {
fprintf(stderr, "Illegal argument to -n: should be -n #sessions\n");
exit(EXIT_FAILURE);
}
if (nsess < 1 || nsess > 65534) {
fprintf(stderr, "Illegal argument to -n: must range from 1 to 65534\n");
exit(EXIT_FAILURE);
}
break;
default:
usage(argv[0]);
}
}
#ifdef USE_LINUX_PACKET
#ifndef HAVE_STRUCT_SOCKADDR_LL
fprintf(stderr, "The PPPoE relay does not work on Linux 2.0 kernels.\n");
exit(EXIT_FAILURE);
#endif
#endif
/* Check that at least two interfaces were defined */
if (NumInterfaces < 2) {
fprintf(stderr, "%s: Must define at least two interfaces\n",
argv[0]);
exit(EXIT_FAILURE);
}
/* Make a pipe for the cleaner */
if (pipe(CleanPipe) < 0) {
fatalSys("pipe");
}
/* Set up alarm handler */
sa.sa_handler = alarmHandler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGALRM, &sa, NULL) < 0) {
fatalSys("sigaction");
}
/* Allocate memory for sessions, etc. */
initRelay(nsess);
/* Daemonize -- UNIX Network Programming, Vol. 1, Stevens */
if (beDaemon) {
int i;
i = fork();
if (i < 0) {
fatalSys("fork");
} else if (i != 0) {
/* parent */
exit(0);
}
setsid();
signal(SIGHUP, SIG_IGN);
i = fork();
if (i < 0) {
fatalSys("fork");
} else if (i != 0) {
exit(0);
}
chdir("/");
closelog();
for (i=0; i<CLOSEFD; i++) {
if (!keepDescriptor(i)) {
close(i);
}
}
/* We nuked our syslog descriptor... */
openlog("pppoe-relay", LOG_PID, LOG_DAEMON);
}
/* Kick off SIGALRM if there is an idle timeout */
if (IdleTimeout) alarm(1);
/* Enter the relay loop */
relayLoop();
/* Shouldn't ever get here... */
return EXIT_FAILURE;
}
/**********************************************************************
*%FUNCTION: addInterface
*%ARGUMENTS:
* ifname -- interface name
* clientOK -- true if this interface should relay PADI, PADR packets.
* acOK -- true if this interface should relay PADO, PADS packets.
*%RETURNS:
* Nothing
*%DESCRIPTION:
* Opens an interface; sets up discovery and session sockets.
***********************************************************************/
void
addInterface(char const *ifname,
int clientOK,
int acOK)
{
PPPoEInterface *i;
int j;
for (j=0; j<NumInterfaces; j++) {
if (!strncmp(Interfaces[j].name, ifname, IFNAMSIZ)) {
fprintf(stderr, "Interface %s specified more than once.\n", ifname);
exit(EXIT_FAILURE);
}
}
if (NumInterfaces >= MAX_INTERFACES) {
fprintf(stderr, "Too many interfaces (%d max)\n",
MAX_INTERFACES);
exit(EXIT_FAILURE);
}
i = &Interfaces[NumInterfaces++];
strncpy(i->name, ifname, IFNAMSIZ);
i->name[IFNAMSIZ] = 0;
i->discoverySock = openInterface(ifname, Eth_PPPOE_Discovery, i->mac);
i->sessionSock = openInterface(ifname, Eth_PPPOE_Session, NULL);
i->clientOK = clientOK;
i->acOK = acOK;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -