📄 relay.c
字号:
/************************************************************************* relay.c** Implementation of PPPoE relay** Copyright (C) 2001-2005 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.27 2005/08/10 00:25:21 dfs Exp $************************************************************************/static char const RCSID[] ="$Id: relay.c,v 1.27 2005/08/10 00:25:21 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 intkeepDescriptor(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.***********************************************************************/intaddTag(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.***********************************************************************/intinsertBytes(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***********************************************************************/intremoveBytes(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.***********************************************************************/voidusage(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-2005 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***********************************************************************/intmain(int argc, char *argv[]){ int opt; int nsess = DEFAULT_SESSIONS; struct sigaction sa; int beDaemon = 1; if (getuid() != geteuid() || getgid() != getegid()) { fprintf(stderr, "SECURITY WARNING: pppoe-relay will NOT run suid or sgid. Fix your installation.\n"); exit(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.***********************************************************************/voidaddInterface(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;}/***********************************************************************%FUNCTION: initRelay*%ARGUMENTS:* nsess -- maximum allowable number of sessions*%RETURNS:* Nothing*%DESCRIPTION:* Initializes relay hash table and session tables.***********************************************************************/voidinitRelay(int nsess){ int i; NumSessions = 0; MaxSessions = nsess; AllSessions = calloc(MaxSessions, sizeof(PPPoESession)); if (!AllSessions) { rp_fatal("Unable to allocate memory for PPPoE session table"); } AllHashes = calloc(MaxSessions*2, sizeof(SessionHash)); if (!AllHashes) { rp_fatal("Unable to allocate memory for PPPoE hash table"); } /* Initialize sessions in a linked list */ AllSessions[0].prev = NULL; if (MaxSessions > 1) { AllSessions[0].next = &AllSessions[1]; } else { AllSessions[0].next = NULL; } for (i=1; i<MaxSessions-1; i++) { AllSessions[i].prev = &AllSessions[i-1]; AllSessions[i].next = &AllSessions[i+1]; } if (MaxSessions > 1) { AllSessions[MaxSessions-1].prev = &AllSessions[MaxSessions-2]; AllSessions[MaxSessions-1].next = NULL; } FreeSessions = AllSessions; ActiveSessions = NULL; /* Initialize session numbers which we hand out */ for (i=0; i<MaxSessions; i++) { AllSessions[i].sesNum = htons((UINT16_t) i+1); } /* Initialize hashes in a linked list */ AllHashes[0].prev = NULL; AllHashes[0].next = &AllHashes[1]; for (i=1; i<2*MaxSessions-1; i++) { AllHashes[i].prev = &AllHashes[i-1]; AllHashes[i].next = &AllHashes[i+1]; } AllHashes[2*MaxSessions-1].prev = &AllHashes[2*MaxSessions-2]; AllHashes[2*MaxSessions-1].next = NULL; FreeHashes = AllHashes;}/***********************************************************************%FUNCTION: createSession*%ARGUMENTS:* ac -- Ethernet interface on access-concentrator side* cli -- Ethernet interface on client side* acMac -- Access concentrator's MAC address* cliMac -- Client's MAC address* acSess -- Access concentrator's session ID.*%RETURNS:* PPPoESession structure; NULL if one could not be allocated*%DESCRIPTION:* Initializes relay hash table and session tables.***********************************************************************/PPPoESession *createSession(PPPoEInterface const *ac, PPPoEInterface const *cli, unsigned char const *acMac, unsigned char const *cliMac, UINT16_t acSes){ PPPoESession *sess; SessionHash *acHash, *cliHash; if (NumSessions >= MaxSessions) { printErr("Maximum number of sessions reached -- cannot create new session"); return NULL; } /* Grab a free session */ sess = FreeSessions; FreeSessions = sess->next; NumSessions++; /* Link it to the active list */ sess->next = ActiveSessions; if (sess->next) { sess->next->prev = sess; } ActiveSessions = sess; sess->prev = NULL; sess->epoch = Epoch; /* Get two hash entries */ acHash = FreeHashes; cliHash = acHash->next; FreeHashes = cliHash->next; acHash->peer = cliHash; cliHash->peer = acHash; sess->acHash = acHash; sess->clientHash = cliHash; acHash->interface = ac; cliHash->interface = cli; memcpy(acHash->peerMac, acMac, ETH_ALEN); acHash->sesNum = acSes; acHash->ses = sess;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -