66 files changed, 0 insertions, 15542 deletions
diff --git a/src/pppoe-server.c b/src/pppoe-server.c deleted file mode 100755 index b59cd3c..0000000 --- a/src/pppoe-server.c +++ b/dev/null @@ -1,2137 +0,0 @@ -/*********************************************************************** -* -* pppoe-server.c -* -* Implementation of a user-space PPPoE server -* -* Copyright (C) 2000 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. -* -* $Id$ -* -* LIC: GPL -* -***********************************************************************/ - -static char const RCSID[] = -"$Id$"; - -#include "config.h" - -#if defined(HAVE_NETPACKET_PACKET_H) || defined(HAVE_LINUX_IF_PACKET_H) -#define _POSIX_SOURCE 1 /* For sigaction defines */ -#endif - -#define _BSD_SOURCE 1 /* for gethostname */ - -#include "pppoe-server.h" -#include "md5.h" - -#ifdef HAVE_SYSLOG_H -#include <syslog.h> -#endif - -#include <errno.h> -#include <string.h> -#include <stdlib.h> -#include <fcntl.h> - -#ifdef HAVE_UNISTD_H -#include <unistd.h> -#endif - -#ifdef HAVE_GETOPT_H -#include <getopt.h> -#endif - -#ifdef HAVE_SYS_WAIT_H -#include <sys/wait.h> -#endif - -#ifdef HAVE_SYS_TIME_H -#include <sys/time.h> -#endif - -#include <time.h> - -#include <signal.h> - -#ifdef HAVE_LICENSE -#include "license.h" -#include "licensed-only/servfuncs.h" -static struct License const *ServerLicense; -static struct License const *ClusterLicense; -#else -#define control_session_started(x) (void) 0 -#define control_session_terminated(x) (void) 0 -#define control_exit() (void) 0 -#define realpeerip peerip -#endif - -#ifdef HAVE_L2TP -extern PppoeSessionFunctionTable L2TPSessionFunctionTable; -extern void pppoe_to_l2tp_add_interface(EventSelector *es, - Interface *interface); -#endif - -static void InterfaceHandler(EventSelector *es, - int fd, unsigned int flags, void *data); -static void startPPPD(ClientSession *sess); -static void sendErrorPADS(int sock, unsigned char *source, unsigned char *dest, - int errorTag, char *errorMsg); - -#define CHECK_ROOM(cursor, start, len) \ -do {\ - if (((cursor)-(start))+(len) > MAX_PPPOE_PAYLOAD) { \ - syslog(LOG_ERR, "Would create too-long packet"); \ - return; \ - } \ -} while(0) - -static void PppoeStopSession(ClientSession *ses, char const *reason); -static int PppoeSessionIsActive(ClientSession *ses); - -/* Service-Names we advertise */ -#define MAX_SERVICE_NAMES 64 -static int NumServiceNames = 0; -static char const *ServiceNames[MAX_SERVICE_NAMES]; - -PppoeSessionFunctionTable DefaultSessionFunctionTable = { - PppoeStopSession, - PppoeSessionIsActive, - NULL -}; - -/* An array of client sessions */ -ClientSession *Sessions = NULL; -ClientSession *FreeSessions = NULL; -ClientSession *LastFreeSession = NULL; -ClientSession *BusySessions = NULL; - -/* Interfaces we're listening on */ -Interface interfaces[MAX_INTERFACES]; -int NumInterfaces = 0; - -/* The number of session slots */ -size_t NumSessionSlots; - -/* Maximum number of sessions per MAC address */ -int MaxSessionsPerMac; - -/* Number of active sessions */ -size_t NumActiveSessions = 0; - -/* Offset of first session */ -size_t SessOffset = 0; - -/* Event Selector */ -EventSelector *event_selector; - -/* Use Linux kernel-mode PPPoE? */ -static int UseLinuxKernelModePPPoE = 0; - -/* File with PPPD options */ -static char *pppoptfile = NULL; - -static int Debug = 0; -static int CheckPoolSyntax = 0; - -/* Synchronous mode */ -static int Synchronous = 0; - -/* Random seed for cookie generation */ -#define SEED_LEN 16 -#define MD5_LEN 16 -#define COOKIE_LEN (MD5_LEN + sizeof(pid_t)) /* Cookie is 16-byte MD5 + PID of server */ - -static unsigned char CookieSeed[SEED_LEN]; - -#define MAXLINE 512 - -/* Default interface if no -I option given */ -#define DEFAULT_IF "eth0" - -/* Access concentrator name */ -char *ACName = NULL; - -/* Options to pass to pppoe process */ -char PppoeOptions[SMALLBUF] = ""; - -/* Our local IP address */ -unsigned char LocalIP[IPV4ALEN] = {10, 0, 0, 1}; /* Counter optionally STARTS here */ -unsigned char RemoteIP[IPV4ALEN] = {10, 67, 15, 1}; /* Counter STARTS here */ - -/* Do we increment local IP for each connection? */ -int IncrLocalIP = 0; - -/* Do we randomize session numbers? */ -int RandomizeSessionNumbers = 0; - -/* Do we pass the "unit" option to pppd? (2.4 or greater) */ -int PassUnitOptionToPPPD = 0; - -static PPPoETag hostUniq; -static PPPoETag relayId; -static PPPoETag receivedCookie; -static PPPoETag requestedService; - -#define HOSTNAMELEN 256 - -static int -count_sessions_from_mac(unsigned char *eth) -{ - int n=0; - ClientSession *s = BusySessions; - while(s) { - if (!memcmp(eth, s->eth, ETH_ALEN)) n++; - s = s->next; - } - return n; -} - -/********************************************************************** -*%FUNCTION: childHandler -*%ARGUMENTS: -* pid -- pid of child -* status -- exit status -* ses -- which session terminated -*%RETURNS: -* Nothing -*%DESCRIPTION: -* Called synchronously when a child dies. Remove from busy list. -***********************************************************************/ -static void -childHandler(pid_t pid, int status, void *s) -{ - ClientSession *session = s; - - /* Temporary structure for sending PADT's. */ - PPPoEConnection conn; - -#ifdef HAVE_L2TP - /* We're acting as LAC, so when child exits, become a PPPoE <-> L2TP - relay */ - if (session->flags & FLAG_ACT_AS_LAC) { - syslog(LOG_INFO, "Session %u for client " - "%02x:%02x:%02x:%02x:%02x:%02x handed off to LNS %s", - (unsigned int) ntohs(session->sess), - session->eth[0], session->eth[1], session->eth[2], - session->eth[3], session->eth[4], session->eth[5], - inet_ntoa(session->tunnel_endpoint.sin_addr)); - session->pid = 0; - session->funcs = &L2TPSessionFunctionTable; - return; - } -#endif - - memset(&conn, 0, sizeof(conn)); - conn.useHostUniq = 0; - - syslog(LOG_INFO, - "Session %u closed for client " - "%02x:%02x:%02x:%02x:%02x:%02x (%d.%d.%d.%d) on %s", - (unsigned int) ntohs(session->sess), - session->eth[0], session->eth[1], session->eth[2], - session->eth[3], session->eth[4], session->eth[5], - (int) session->realpeerip[0], (int) session->realpeerip[1], - (int) session->realpeerip[2], (int) session->realpeerip[3], - session->ethif->name); - memcpy(conn.myEth, session->ethif->mac, ETH_ALEN); - conn.discoverySocket = session->ethif->sock; - conn.session = session->sess; - memcpy(conn.peerEth, session->eth, ETH_ALEN); - if (!(session->flags & FLAG_SENT_PADT)) { - if (session->flags & FLAG_RECVD_PADT) { - sendPADT(&conn, "RP-PPPoE: Received PADT from peer"); - } else { - sendPADT(&conn, "RP-PPPoE: Child pppd process terminated"); - } - session->flags |= FLAG_SENT_PADT; - } - - session->serviceName = ""; - control_session_terminated(session); - if (pppoe_free_session(session) < 0) { - return; - } - -} - -/********************************************************************** -*%FUNCTION: incrementIPAddress (static) -*%ARGUMENTS: -* addr -- a 4-byte array representing IP address -*%RETURNS: -* Nothing -*%DESCRIPTION: -* Increments addr in-place -***********************************************************************/ -static void -incrementIPAddress(unsigned char ip[IPV4ALEN]) -{ - ip[3]++; - if (!ip[3]) { - ip[2]++; - if (!ip[2]) { - ip[1]++; - if (!ip[1]) { - ip[0]++; - } - } - } -} - -/********************************************************************** -*%FUNCTION: killAllSessions -*%ARGUMENTS: -* None -*%RETURNS: -* Nothing -*%DESCRIPTION: -* Kills all pppd processes (and hence all PPPoE sessions) -***********************************************************************/ -void -killAllSessions(void) -{ - ClientSession *sess = BusySessions; - while(sess) { - sess->funcs->stop(sess, "Shutting Down"); - sess = sess->next; - } -#ifdef HAVE_L2TP - pppoe_close_l2tp_tunnels(); -#endif -} - -/********************************************************************** -*%FUNCTION: parseAddressPool -*%ARGUMENTS: -* fname -- name of file containing IP address pool. -* install -- if true, install IP addresses in sessions. -*%RETURNS: -* Number of valid IP addresses found. -*%DESCRIPTION: -* Reads a list of IP addresses from a file. -***********************************************************************/ -static int -parseAddressPool(char const *fname, int install) -{ - FILE *fp = fopen(fname, "r"); - int numAddrs = 0; - unsigned int a, b, c, d; - unsigned int e, f, g, h; - char line[MAXLINE]; - - if (!fp) { - sysErr("Cannot open address pool file"); - exit(1); - } - - while (!feof(fp)) { - if (!fgets(line, MAXLINE, fp)) { - break; - } - if ((sscanf(line, "%u.%u.%u.%u:%u.%u.%u.%u", - &a, &b, &c, &d, &e, &f, &g, &h) == 8) && - a < 256 && b < 256 && c < 256 && d < 256 && - e < 256 && f < 256 && g < 256 && h < 256) { - - /* Both specified (local:remote) */ - if (install) { - Sessions[numAddrs].myip[0] = (unsigned char) a; - Sessions[numAddrs].myip[1] = (unsigned char) b; - Sessions[numAddrs].myip[2] = (unsigned char) c; - Sessions[numAddrs].myip[3] = (unsigned char) d; - Sessions[numAddrs].peerip[0] = (unsigned char) e; - Sessions[numAddrs].peerip[1] = (unsigned char) f; - Sessions[numAddrs].peerip[2] = (unsigned char) g; - Sessions[numAddrs].peerip[3] = (unsigned char) h; -#ifdef HAVE_LICENSE - memcpy(Sessions[numAddrs].realpeerip, - Sessions[numAddrs].peerip, IPV4ALEN); -#endif - } - numAddrs++; - } else if ((sscanf(line, "%u.%u.%u.%u-%u", &a, &b, &c, &d, &e) == 5) && - a < 256 && b < 256 && c < 256 && d < 256 && e < 256) { - /* Remote specied as a.b.c.d-e. Example: 1.2.3.4-8 yields: - 1.2.3.4, 1.2.3.5, 1.2.3.6, 1.2.3.7, 1.2.3.8 */ - /* Swap d and e so that e >= d */ - if (e < d) { - f = d; - d = e; - e = f; - } - if (install) { - while (d <= e) { - Sessions[numAddrs].peerip[0] = (unsigned char) a; - Sessions[numAddrs].peerip[1] = (unsigned char) b; - Sessions[numAddrs].peerip[2] = (unsigned char) c; - Sessions[numAddrs].peerip[3] = (unsigned char) d; -#ifdef HAVE_LICENSE - memcpy(Sessions[numAddrs].realpeerip, - Sessions[numAddrs].peerip, IPV4ALEN); -#endif - d++; - numAddrs++; - } - } else { - numAddrs += (e-d) + 1; - } - } else if ((sscanf(line, "%u.%u.%u.%u", &a, &b, &c, &d) == 4) && - a < 256 && b < 256 && c < 256 && d < 256) { - /* Only remote specified */ - if (install) { - Sessions[numAddrs].peerip[0] = (unsigned char) a; - Sessions[numAddrs].peerip[1] = (unsigned char) b; - Sessions[numAddrs].peerip[2] = (unsigned char) c; - Sessions[numAddrs].peerip[3] = (unsigned char) d; -#ifdef HAVE_LICENSE - memcpy(Sessions[numAddrs].realpeerip, - Sessions[numAddrs].peerip, IPV4ALEN); -#endif - } - numAddrs++; - } - } - fclose(fp); - if (!numAddrs) { - rp_fatal("No valid ip addresses found in pool file"); - } - return numAddrs; -} - -/********************************************************************** -*%FUNCTION: parsePADITags -*%ARGUMENTS: -* type -- tag type -* len -- tag length -* data -- tag data -* extra -- extra user data. -*%RETURNS: -* Nothing -*%DESCRIPTION: -* Picks interesting tags out of a PADI packet -***********************************************************************/ -void -parsePADITags(UINT16_t type, UINT16_t len, unsigned char *data, - void *extra) -{ - switch(type) { - case TAG_SERVICE_NAME: - /* Copy requested service name */ - requestedService.type = htons(type); - requestedService.length = htons(len); - memcpy(requestedService.payload, data, len); - break; - case TAG_RELAY_SESSION_ID: - relayId.type = htons(type); - relayId.length = htons(len); - memcpy(relayId.payload, data, len); - break; - case TAG_HOST_UNIQ: - hostUniq.type = htons(type); - hostUniq.length = htons(len); - memcpy(hostUniq.payload, data, len); - break; - } -} - -/********************************************************************** -*%FUNCTION: parsePADRTags -*%ARGUMENTS: -* type -- tag type -* len -- tag length -* data -- tag data -* extra -- extra user data. -*%RETURNS: -* Nothing -*%DESCRIPTION: -* Picks interesting tags out of a PADR packet -***********************************************************************/ -void -parsePADRTags(UINT16_t type, UINT16_t len, unsigned char *data, - void *extra) -{ - switch(type) { - case TAG_RELAY_SESSION_ID: - relayId.type = htons(type); - relayId.length = htons(len); - memcpy(relayId.payload, data, len); - break; - case TAG_HOST_UNIQ: - hostUniq.type = htons(type); - hostUniq.length = htons(len); - memcpy(hostUniq.payload, data, len); - break; - case TAG_AC_COOKIE: - receivedCookie.type = htons(type); - receivedCookie.length = htons(len); - memcpy(receivedCookie.payload, data, len); - break; - case TAG_SERVICE_NAME: - requestedService.type = htons(type); - requestedService.length = htons(len); - memcpy(requestedService.payload, data, len); - break; - } -} - -/********************************************************************** -*%FUNCTION: fatalSys -*%ARGUMENTS: -* str -- error message -*%RETURNS: -* Nothing -*%DESCRIPTION: -* Prints a message plus the errno value to stderr and syslog and exits. -***********************************************************************/ -void -fatalSys(char const *str) -{ - char buf[SMALLBUF]; - snprintf(buf, SMALLBUF, "%s: %s", str, strerror(errno)); - printErr(buf); - control_exit(); - exit(EXIT_FAILURE); -} - -/********************************************************************** -*%FUNCTION: sysErr -*%ARGUMENTS: -* str -- error message -*%RETURNS: -* Nothing -*%DESCRIPTION: -* Prints a message plus the errno value to syslog. -***********************************************************************/ -void -sysErr(char const *str) -{ - char buf[1024]; - sprintf(buf, "%.256s: %.256s", str, strerror(errno)); - printErr(buf); -} - -/********************************************************************** -*%FUNCTION: rp_fatal -*%ARGUMENTS: -* str -- error message -*%RETURNS: -* Nothing -*%DESCRIPTION: -* Prints a message to stderr and syslog and exits. -***********************************************************************/ -void -rp_fatal(char const *str) -{ - printErr(str); - control_exit(); - exit(EXIT_FAILURE); -} - -/********************************************************************** -*%FUNCTION: genCookie -*%ARGUMENTS: -* peerEthAddr -- peer Ethernet address (6 bytes) -* myEthAddr -- my Ethernet address (6 bytes) -* seed -- random cookie seed to make things tasty (16 bytes) -* cookie -- buffer which is filled with server PID and -* md5 sum of previous items -*%RETURNS: -* Nothing -*%DESCRIPTION: -* Forms the md5 sum of peer MAC address, our MAC address and seed, useful -* in a PPPoE Cookie tag. -***********************************************************************/ -void -genCookie(unsigned char const *peerEthAddr, - unsigned char const *myEthAddr, - unsigned char const *seed, - unsigned char *cookie) -{ - struct MD5Context ctx; - pid_t pid = getpid(); - - MD5Init(&ctx); - MD5Update(&ctx, peerEthAddr, ETH_ALEN); - MD5Update(&ctx, myEthAddr, ETH_ALEN); - MD5Update(&ctx, seed, SEED_LEN); - MD5Final(cookie, &ctx); - memcpy(cookie+MD5_LEN, &pid, sizeof(pid)); -} - -/********************************************************************** -*%FUNCTION: processPADI -*%ARGUMENTS: -* ethif -- Interface -* packet -- PPPoE PADI packet -* len -- length of received packet -*%RETURNS: -* Nothing -*%DESCRIPTION: -* Sends a PADO packet back to client -***********************************************************************/ -void -processPADI(Interface *ethif, PPPoEPacket *packet, int len) -{ - PPPoEPacket pado; - PPPoETag acname; - PPPoETag servname; - PPPoETag cookie; - size_t acname_len; - unsigned char *cursor = pado.payload; - UINT16_t plen; - - int sock = ethif->sock; - int i; - int ok = 0; - unsigned char *myAddr = ethif->mac; - - /* Ignore PADI's which don't come from a unicast address */ - if (NOT_UNICAST(packet->ethHdr.h_source)) { - syslog(LOG_ERR, "PADI packet from non-unicast source address"); - return; - } - - /* If number of sessions per MAC is limited, check here and don't - send PADO if already max number of sessions. */ - if (MaxSessionsPerMac) { - if (count_sessions_from_mac(packet->ethHdr.h_source) >= MaxSessionsPerMac) { - syslog(LOG_INFO, "PADI: Client %02x:%02x:%02x:%02x:%02x:%02x attempted to create more than %d session(s)", - packet->ethHdr.h_source[0], - packet->ethHdr.h_source[1], - packet->ethHdr.h_source[2], - packet->ethHdr.h_source[3], - packet->ethHdr.h_source[4], - packet->ethHdr.h_source[5], - MaxSessionsPerMac); - return; - } - } - - acname.type = htons(TAG_AC_NAME); - acname_len = strlen(ACName); - acname.length = htons(acname_len); - memcpy(acname.payload, ACName, acname_len); - - relayId.type = 0; - hostUniq.type = 0; - requestedService.type = 0; - parsePacket(packet, parsePADITags, NULL); - - /* If PADI specified non-default service name, and we do not offer - that service, DO NOT send PADO */ - if (requestedService.type) { - int slen = ntohs(requestedService.length); - if (slen) { - for (i=0; i<NumServiceNames; i++) { - if (slen == strlen(ServiceNames[i]) && - !memcmp(ServiceNames[i], &requestedService.payload, slen)) { - ok = 1; - break; - } - } - } else { - ok = 1; /* Default service requested */ - } - } else { - ok = 1; /* No Service-Name tag in PADI */ - } - - if (!ok) { - /* PADI asked for unsupported service */ - return; - } - - /* Generate a cookie */ - cookie.type = htons(TAG_AC_COOKIE); - cookie.length = htons(COOKIE_LEN); - genCookie(packet->ethHdr.h_source, myAddr, CookieSeed, cookie.payload); - - /* Construct a PADO packet */ - memcpy(pado.ethHdr.h_dest, packet->ethHdr.h_source, ETH_ALEN); - memcpy(pado.ethHdr.h_source, myAddr, ETH_ALEN); - pado.ethHdr.h_proto = htons(Eth_PPPOE_Discovery); - pado.ver = 1; - pado.type = 1; - pado.code = CODE_PADO; - pado.session = 0; - plen = TAG_HDR_SIZE + acname_len; - - CHECK_ROOM(cursor, pado.payload, acname_len+TAG_HDR_SIZE); - memcpy(cursor, &acname, acname_len + TAG_HDR_SIZE); - cursor += acname_len + TAG_HDR_SIZE; - - /* If no service-names specified on command-line, just send default - zero-length name. Otherwise, add all service-name tags */ - servname.type = htons(TAG_SERVICE_NAME); - if (!NumServiceNames) { - servname.length = 0; - CHECK_ROOM(cursor, pado.payload, TAG_HDR_SIZE); - memcpy(cursor, &servname, TAG_HDR_SIZE); - cursor += TAG_HDR_SIZE; - plen += TAG_HDR_SIZE; - } else { - for (i=0; i<NumServiceNames; i++) { - int slen = strlen(ServiceNames[i]); - servname.length = htons(slen); - CHECK_ROOM(cursor, pado.payload, TAG_HDR_SIZE+slen); - memcpy(cursor, &servname, TAG_HDR_SIZE); - memcpy(cursor+TAG_HDR_SIZE, ServiceNames[i], slen); - cursor += TAG_HDR_SIZE+slen; - plen += TAG_HDR_SIZE+slen; - } - } - - CHECK_ROOM(cursor, pado.payload, TAG_HDR_SIZE + COOKIE_LEN); - memcpy(cursor, &cookie, TAG_HDR_SIZE + COOKIE_LEN); - cursor += TAG_HDR_SIZE + COOKIE_LEN; - plen += TAG_HDR_SIZE + COOKIE_LEN; - - if (relayId.type) { - CHECK_ROOM(cursor, pado.payload, ntohs(relayId.length) + TAG_HDR_SIZE); - memcpy(cursor, &relayId, ntohs(relayId.length) + TAG_HDR_SIZE); - cursor += ntohs(relayId.length) + TAG_HDR_SIZE; - plen += ntohs(relayId.length) + TAG_HDR_SIZE; - } - if (hostUniq.type) { - CHECK_ROOM(cursor, pado.payload, ntohs(hostUniq.length)+TAG_HDR_SIZE); - memcpy(cursor, &hostUniq, ntohs(hostUniq.length) + TAG_HDR_SIZE); - cursor += ntohs(hostUniq.length) + TAG_HDR_SIZE; - plen += ntohs(hostUniq.length) + TAG_HDR_SIZE; - } - pado.length = htons(plen); - sendPacket(NULL, sock, &pado, (int) (plen + HDR_SIZE)); -} - -/********************************************************************** -*%FUNCTION: processPADT -*%ARGUMENTS: -* ethif -- interface -* packet -- PPPoE PADT packet -* len -- length of received packet -*%RETURNS: -* Nothing -*%DESCRIPTION: -* Kills session whose session-ID is in PADT packet. -***********************************************************************/ -void -processPADT(Interface *ethif, PPPoEPacket *packet, int len) -{ - size_t i; - - unsigned char *myAddr = ethif->mac; - - /* Ignore PADT's not directed at us */ - if (memcmp(packet->ethHdr.h_dest, myAddr, ETH_ALEN)) return; - - /* Get session's index */ - i = ntohs(packet->session) - 1 - SessOffset; - if (i >= NumSessionSlots) return; - if (Sessions[i].sess != packet->session) { - syslog(LOG_ERR, "Session index %u doesn't match session number %u", - (unsigned int) i, (unsigned int) ntohs(packet->session)); - return; - } - - - /* If source MAC does not match, do not kill session */ - if (memcmp(packet->ethHdr.h_source, Sessions[i].eth, ETH_ALEN)) { - syslog(LOG_WARNING, "PADT for session %u received from " - "%02X:%02X:%02X:%02X:%02X:%02X; should be from " - "%02X:%02X:%02X:%02X:%02X:%02X", - (unsigned int) ntohs(packet->session), - packet->ethHdr.h_source[0], - packet->ethHdr.h_source[1], - packet->ethHdr.h_source[2], - packet->ethHdr.h_source[3], - packet->ethHdr.h_source[4], - packet->ethHdr.h_source[5], - Sessions[i].eth[0], - Sessions[i].eth[1], - Sessions[i].eth[2], - Sessions[i].eth[3], - Sessions[i].eth[4], - Sessions[i].eth[5]); - return; - } - Sessions[i].flags |= FLAG_RECVD_PADT; - parsePacket(packet, parseLogErrs, NULL); - Sessions[i].funcs->stop(&Sessions[i], "Received PADT"); -} - -/********************************************************************** -*%FUNCTION: processPADR -*%ARGUMENTS: -* ethif -- Ethernet interface -* packet -- PPPoE PADR packet -* len -- length of received packet -*%RETURNS: -* Nothing -*%DESCRIPTION: -* Sends a PADS packet back to client and starts a PPP session if PADR -* packet is OK. -***********************************************************************/ -void -processPADR(Interface *ethif, PPPoEPacket *packet, int len) -{ - unsigned char cookieBuffer[COOKIE_LEN]; - ClientSession *cliSession; - pid_t child; - PPPoEPacket pads; - unsigned char *cursor = pads.payload; - UINT16_t plen; - int i; - int sock = ethif->sock; - unsigned char *myAddr = ethif->mac; - int slen = 0; - char const *serviceName = NULL; - -#ifdef HAVE_LICENSE - int freemem; -#endif - - /* Initialize some globals */ - relayId.type = 0; - hostUniq.type = 0; - receivedCookie.type = 0; - requestedService.type = 0; - - /* Ignore PADR's not directed at us */ - if (memcmp(packet->ethHdr.h_dest, myAddr, ETH_ALEN)) return; - - /* Ignore PADR's from non-unicast addresses */ - if (NOT_UNICAST(packet->ethHdr.h_source)) { - syslog(LOG_ERR, "PADR packet from non-unicast source address"); - return; - } - - /* If number of sessions per MAC is limited, check here and don't - send PADS if already max number of sessions. */ - if (MaxSessionsPerMac) { - if (count_sessions_from_mac(packet->ethHdr.h_source) >= MaxSessionsPerMac) { - syslog(LOG_INFO, "PADR: Client %02x:%02x:%02x:%02x:%02x:%02x attempted to create more than %d session(s)", - packet->ethHdr.h_source[0], - packet->ethHdr.h_source[1], - packet->ethHdr.h_source[2], - packet->ethHdr.h_source[3], - packet->ethHdr.h_source[4], - packet->ethHdr.h_source[5], - MaxSessionsPerMac); - return; - } - } - parsePacket(packet, parsePADRTags, NULL); - - /* Check that everything's cool */ - if (!receivedCookie.type) { - /* Drop it -- do not send error PADS */ - return; - } - - /* Is cookie kosher? */ - if (receivedCookie.length != htons(COOKIE_LEN)) { - /* Drop it -- do not send error PADS */ - return; - } - - genCookie(packet->ethHdr.h_source, myAddr, CookieSeed, cookieBuffer); - if (memcmp(receivedCookie.payload, cookieBuffer, COOKIE_LEN)) { - /* Drop it -- do not send error PADS */ - return; - } - - /* Check service name */ - if (!requestedService.type) { - syslog(LOG_ERR, "Received PADR packet with no SERVICE_NAME tag"); - sendErrorPADS(sock, myAddr, packet->ethHdr.h_source, - TAG_SERVICE_NAME_ERROR, "RP-PPPoE: Server: No service name tag"); - return; - } - - slen = ntohs(requestedService.length); - if (slen) { - /* Check supported services */ - for(i=0; i<NumServiceNames; i++) { - if (slen == strlen(ServiceNames[i]) && - !memcmp(ServiceNames[i], &requestedService.payload, slen)) { - serviceName = ServiceNames[i]; - break; - } - } - - if (!serviceName) { - syslog(LOG_ERR, "Received PADR packet asking for unsupported service %.*s", (int) ntohs(requestedService.length), requestedService.payload); - sendErrorPADS(sock, myAddr, packet->ethHdr.h_source, - TAG_SERVICE_NAME_ERROR, "RP-PPPoE: Server: Invalid service name tag"); - return; - } - } else { - serviceName = ""; - } - - -#ifdef HAVE_LICENSE - /* Are we licensed for this many sessions? */ - if (License_NumLicenses("PPPOE-SESSIONS") <= NumActiveSessions) { - syslog(LOG_ERR, "Insufficient session licenses (%02x:%02x:%02x:%02x:%02x:%02x)", - (unsigned int) packet->ethHdr.h_source[0], - (unsigned int) packet->ethHdr.h_source[1], - (unsigned int) packet->ethHdr.h_source[2], - (unsigned int) packet->ethHdr.h_source[3], - (unsigned int) packet->ethHdr.h_source[4], - (unsigned int) packet->ethHdr.h_source[5]); - sendErrorPADS(sock, myAddr, packet->ethHdr.h_source, - TAG_AC_SYSTEM_ERROR, "RP-PPPoE: Server: No session licenses available"); - return; - } -#endif - /* Enough free memory? */ -#ifdef HAVE_LICENSE - freemem = getFreeMem(); - if (freemem < MIN_FREE_MEMORY) { - syslog(LOG_WARNING, - "Insufficient free memory to create session: Want %d, have %d", - MIN_FREE_MEMORY, freemem); - sendErrorPADS(sock, myAddr, packet->ethHdr.h_source, - TAG_AC_SYSTEM_ERROR, "RP-PPPoE: Insufficient free RAM"); - return; - } -#endif - /* Looks cool... find a slot for the session */ - cliSession = pppoe_alloc_session(); - if (!cliSession) { - syslog(LOG_ERR, "No client slots available (%02x:%02x:%02x:%02x:%02x:%02x)", - (unsigned int) packet->ethHdr.h_source[0], - (unsigned int) packet->ethHdr.h_source[1], - (unsigned int) packet->ethHdr.h_source[2], - (unsigned int) packet->ethHdr.h_source[3], - (unsigned int) packet->ethHdr.h_source[4], - (unsigned int) packet->ethHdr.h_source[5]); - sendErrorPADS(sock, myAddr, packet->ethHdr.h_source, - TAG_AC_SYSTEM_ERROR, "RP-PPPoE: Server: No client slots available"); - return; - } - - /* Set up client session peer Ethernet address */ - memcpy(cliSession->eth, packet->ethHdr.h_source, ETH_ALEN); - cliSession->ethif = ethif; - cliSession->flags = 0; - cliSession->funcs = &DefaultSessionFunctionTable; - cliSession->startTime = time(NULL); - cliSession->serviceName = serviceName; - - /* Create child process, send PADS packet back */ - child = fork(); - if (child < 0) { - sendErrorPADS(sock, myAddr, packet->ethHdr.h_source, - TAG_AC_SYSTEM_ERROR, "RP-PPPoE: Server: Unable to start session process"); - pppoe_free_session(cliSession); - return; - } - if (child != 0) { - /* In the parent process. Mark pid in session slot */ - cliSession->pid = child; - Event_HandleChildExit(event_selector, child, - childHandler, cliSession); - control_session_started(cliSession); - return; - } - - /* In the child process. */ - - /* Close all file descriptors except for socket */ - closelog(); - for (i=0; i<CLOSEFD; i++) { - if (i != sock) { - close(i); - } - } - - openlog("pppoe-server", LOG_PID, LOG_DAEMON); - /* pppd has a nasty habit of killing all processes in its process group. - Start a new session to stop pppd from killing us! */ - setsid(); - - /* Send PADS and Start pppd */ - memcpy(pads.ethHdr.h_dest, packet->ethHdr.h_source, ETH_ALEN); - memcpy(pads.ethHdr.h_source, myAddr, ETH_ALEN); - pads.ethHdr.h_proto = htons(Eth_PPPOE_Discovery); - pads.ver = 1; - pads.type = 1; - pads.code = CODE_PADS; - - pads.session = cliSession->sess; - plen = 0; - - /* Copy requested service name tag back in. If requested-service name - length is zero, and we have non-zero services, use first service-name - as default */ - if (!slen && NumServiceNames) { - slen = strlen(ServiceNames[0]); - memcpy(&requestedService.payload, ServiceNames[0], slen); - requestedService.length = htons(slen); - } - memcpy(cursor, &requestedService, TAG_HDR_SIZE+slen); - cursor += TAG_HDR_SIZE+slen; - plen += TAG_HDR_SIZE+slen; - - if (relayId.type) { - memcpy(cursor, &relayId, ntohs(relayId.length) + TAG_HDR_SIZE); - cursor += ntohs(relayId.length) + TAG_HDR_SIZE; - plen += ntohs(relayId.length) + TAG_HDR_SIZE; - } - if (hostUniq.type) { - memcpy(cursor, &hostUniq, ntohs(hostUniq.length) + TAG_HDR_SIZE); - cursor += ntohs(hostUniq.length) + TAG_HDR_SIZE; - plen += ntohs(hostUniq.length) + TAG_HDR_SIZE; - } - pads.length = htons(plen); - sendPacket(NULL, sock, &pads, (int) (plen + HDR_SIZE)); - - /* Close sock; don't need it any more */ - close(sock); - - startPPPD(cliSession); -} - -/********************************************************************** -*%FUNCTION: termHandler -*%ARGUMENTS: -* sig -- signal number -*%RETURNS: -* Nothing -*%DESCRIPTION: -* Called by SIGTERM or SIGINT. Causes all sessions to be killed! -***********************************************************************/ -static void -termHandler(int sig) -{ - syslog(LOG_INFO, - "Terminating on signal %d -- killing all PPPoE sessions", - sig); - killAllSessions(); - control_exit(); - exit(0); -} - -/********************************************************************** -*%FUNCTION: usage -*%ARGUMENTS: -* argv0 -- argv[0] from main -*%RETURNS: -* Nothing -*%DESCRIPTION: -* Prints usage instructions -***********************************************************************/ -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, " -C name -- Set access concentrator name.\n"); - fprintf(stderr, " -m MSS -- Clamp incoming and outgoing MSS options.\n"); - fprintf(stderr, " -L ip -- Set local IP address.\n"); - fprintf(stderr, " -l -- Increment local IP address for each session.\n"); - fprintf(stderr, " -R ip -- Set start address of remote IP pool.\n"); - fprintf(stderr, " -S name -- Advertise specified service-name.\n"); - fprintf(stderr, " -O fname -- Use PPPD options from specified file\n"); - fprintf(stderr, " (default %s).\n", PPPOE_SERVER_OPTIONS); - fprintf(stderr, " -p fname -- Optain IP address pool from specified file.\n"); - fprintf(stderr, " -N num -- Allow 'num' concurrent sessions.\n"); - fprintf(stderr, " -o offset -- Assign session numbers starting at offset+1.\n"); - fprintf(stderr, " -f disc:sess -- Set Ethernet frame types (hex).\n"); - fprintf(stderr, " -s -- Use synchronous PPP mode.\n"); -#ifdef HAVE_LINUX_KERNEL_PPPOE - fprintf(stderr, " -k -- Use kernel-mode PPPoE.\n"); -#endif - fprintf(stderr, " -u -- Pass 'unit' option to pppd.\n"); - fprintf(stderr, " -r -- Randomize session numbers.\n"); - fprintf(stderr, " -d -- Debug session creation.\n"); - fprintf(stderr, " -x n -- Limit to 'n' sessions/MAC address.\n"); - fprintf(stderr, " -P -- Check pool file for correctness and exit.\n"); -#ifdef HAVE_LICENSE - fprintf(stderr, " -c secret:if:port -- Enable clustering on interface 'if'.\n"); - fprintf(stderr, " -1 -- Allow only one session per user.\n"); -#endif - - fprintf(stderr, " -h -- Print usage information.\n\n"); - fprintf(stderr, "PPPoE-Server Version %s, Copyright (C) 2001-2006 Roaring Penguin Software Inc.\n", VERSION); - -#ifndef HAVE_LICENSE - fprintf(stderr, "PPPoE-Server comes with ABSOLUTELY NO WARRANTY.\n"); - fprintf(stderr, "This is free software, and you are welcome to redistribute it\n"); - fprintf(stderr, "under the terms of the GNU General Public License, version 2\n"); - fprintf(stderr, "or (at your option) any later version.\n"); -#endif - fprintf(stderr, "http://www.roaringpenguin.com\n"); -} - -/********************************************************************** -*%FUNCTION: main -*%ARGUMENTS: -* argc, argv -- usual suspects -*%RETURNS: -* Exit status -*%DESCRIPTION: -* Main program of PPPoE server -***********************************************************************/ -int -main(int argc, char **argv) -{ - - FILE *fp; - int i, j; - int opt; - int d[IPV4ALEN]; - int beDaemon = 1; - int found; - unsigned int discoveryType, sessionType; - char *addressPoolFname = NULL; -#ifdef HAVE_LICENSE - int use_clustering = 0; -#endif - -#ifndef HAVE_LINUX_KERNEL_PPPOE - char *options = "x:hI:C:L:R:T:m:FN:f:O:o:sp:lrudPc:S:1"; -#else - char *options = "x:hI:C:L:R:T:m:FN:f:O:o:skp:lrudPc:S:1"; -#endif - - if (getuid() != geteuid() || - getgid() != getegid()) { - fprintf(stderr, "SECURITY WARNING: pppoe-server will NOT run suid or sgid. Fix your installation.\n"); - exit(1); - } - - memset(interfaces, 0, sizeof(interfaces)); - - /* Initialize syslog */ - openlog("pppoe-server", LOG_PID, LOG_DAEMON); - - /* Default number of session slots */ - NumSessionSlots = DEFAULT_MAX_SESSIONS; - MaxSessionsPerMac = 0; /* No limit */ - NumActiveSessions = 0; - - /* Parse command-line options */ - while((opt = getopt(argc, argv, options)) != -1) { - switch(opt) { - case 'x': - if (sscanf(optarg, "%d", &MaxSessionsPerMac) != 1) { - usage(argv[0]); - exit(EXIT_FAILURE); - } - if (MaxSessionsPerMac < 0) { - MaxSessionsPerMac = 0; - } - break; - -#ifdef HAVE_LINUX_KERNEL_PPPOE - case 'k': - UseLinuxKernelModePPPoE = 1; - break; -#endif - case 'S': - if (NumServiceNames == MAX_SERVICE_NAMES) { - fprintf(stderr, "Too many '-S' options (%d max)", - MAX_SERVICE_NAMES); - exit(1); - } - ServiceNames[NumServiceNames] = strdup(optarg); - if (!ServiceNames[NumServiceNames]) { - fprintf(stderr, "Out of memory"); - exit(1); - } - NumServiceNames++; - break; - case 'c': -#ifndef HAVE_LICENSE - fprintf(stderr, "Clustering capability not available.\n"); - exit(1); -#else - cluster_handle_option(optarg); - use_clustering = 1; - break; -#endif - - case 'd': - Debug = 1; - break; - case 'P': - CheckPoolSyntax = 1; - break; - case 'u': - PassUnitOptionToPPPD = 1; - break; - - case 'r': - RandomizeSessionNumbers = 1; - break; - - case 'l': - IncrLocalIP = 1; - break; - - case 'p': - SET_STRING(addressPoolFname, optarg); - break; - - case 's': - Synchronous = 1; - /* Pass the Synchronous option on to pppoe */ - snprintf(PppoeOptions + strlen(PppoeOptions), - SMALLBUF-strlen(PppoeOptions), - " -s"); - 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; - /* This option gets passed to pppoe */ - snprintf(PppoeOptions + strlen(PppoeOptions), - SMALLBUF-strlen(PppoeOptions), - " -%c %s", opt, optarg); - break; - - case 'F': - beDaemon = 0; - break; - - case 'N': - if (sscanf(optarg, "%d", &opt) != 1) { - usage(argv[0]); - exit(EXIT_FAILURE); - } - if (opt <= 0) { - fprintf(stderr, "-N: Value must be positive\n"); - exit(EXIT_FAILURE); - } - NumSessionSlots = opt; - break; - - case 'O': - SET_STRING(pppoptfile, optarg); - break; - - case 'o': - if (sscanf(optarg, "%d", &opt) != 1) { - usage(argv[0]); - exit(EXIT_FAILURE); - } - if (opt < 0) { - fprintf(stderr, "-o: Value must be non-negative\n"); - exit(EXIT_FAILURE); - } - SessOffset = (size_t) opt; - break; - - case 'I': - if (NumInterfaces >= MAX_INTERFACES) { - fprintf(stderr, "Too many -I options (max %d)\n", - MAX_INTERFACES); - exit(EXIT_FAILURE); - } - found = 0; - for (i=0; i<NumInterfaces; i++) { - if (!strncmp(interfaces[i].name, optarg, IFNAMSIZ)) { - found = 1; - break; - } - } - if (!found) { - strncpy(interfaces[NumInterfaces].name, optarg, IFNAMSIZ); - NumInterfaces++; - } - break; - - case 'C': - SET_STRING(ACName, optarg); - break; - - case 'L': - case 'R': - /* Get local/remote IP address */ - if (sscanf(optarg, "%d.%d.%d.%d", &d[0], &d[1], &d[2], &d[3]) != 4) { - usage(argv[0]); - exit(EXIT_FAILURE); - } - for (i=0; i<IPV4ALEN; i++) { - if (d[i] < 0 || d[i] > 255) { - usage(argv[0]); - exit(EXIT_FAILURE); - } - if (opt == 'L') { - LocalIP[i] = (unsigned char) d[i]; - } else { - RemoteIP[i] = (unsigned char) d[i]; - } - } - break; - - case 'T': - case 'm': - /* These just get passed to pppoe */ - snprintf(PppoeOptions + strlen(PppoeOptions), - SMALLBUF-strlen(PppoeOptions), - " -%c %s", opt, optarg); - break; - - case 'h': - usage(argv[0]); - exit(EXIT_SUCCESS); - case '1': -#ifdef HAVE_LICENSE - MaxSessionsPerUser = 1; -#else - fprintf(stderr, "-1 option not valid.\n"); - exit(1); -#endif - break; - } - } - - if (!pppoptfile) { - pppoptfile = PPPOE_SERVER_OPTIONS; - } - -#ifdef HAVE_LICENSE - License_SetVersion(SERVPOET_VERSION); - License_ReadBundleFile("/etc/rp/bundle.txt"); - License_ReadFile("/etc/rp/license.txt"); - ServerLicense = License_GetFeature("PPPOE-SERVER"); - if (!ServerLicense) { - fprintf(stderr, "License: GetFeature failed: %s\n", - License_ErrorMessage()); - exit(1); - } -#endif - -#ifdef USE_LINUX_PACKET -#ifndef HAVE_STRUCT_SOCKADDR_LL - fprintf(stderr, "The PPPoE server does not work on Linux 2.0 kernels.\n"); - exit(EXIT_FAILURE); -#endif -#endif - - if (!NumInterfaces) { - strcpy(interfaces[0].name, DEFAULT_IF); - NumInterfaces = 1; - } - - if (!ACName) { - ACName = malloc(HOSTNAMELEN); - if (gethostname(ACName, HOSTNAMELEN) < 0) { - fatalSys("gethostname"); - } - } - - /* If address pool filename given, count number of addresses */ - if (addressPoolFname) { - NumSessionSlots = parseAddressPool(addressPoolFname, 0); - if (CheckPoolSyntax) { - printf("%lu\n", (unsigned long) NumSessionSlots); - exit(0); - } - } - - /* Max 65534 - SessOffset sessions */ - if (NumSessionSlots + SessOffset > 65534) { - fprintf(stderr, "-N and -o options must add up to at most 65534\n"); - exit(EXIT_FAILURE); - } - - /* Allocate memory for sessions */ - Sessions = calloc(NumSessionSlots, sizeof(ClientSession)); - if (!Sessions) { - rp_fatal("Cannot allocate memory for session slots"); - } - - /* Fill in local addresses first (let pool file override later */ - for (i=0; i<NumSessionSlots; i++) { - memcpy(Sessions[i].myip, LocalIP, sizeof(LocalIP)); - if (IncrLocalIP) { - incrementIPAddress(LocalIP); - } - } - - /* Fill in remote IP addresses from pool (may also overwrite local ips) */ - if (addressPoolFname) { - (void) parseAddressPool(addressPoolFname, 1); - } - - /* For testing -- generate sequential remote IP addresses */ - for (i=0; i<NumSessionSlots; i++) { - Sessions[i].pid = 0; - Sessions[i].funcs = &DefaultSessionFunctionTable; - Sessions[i].sess = htons(i+1+SessOffset); - - if (!addressPoolFname) { - memcpy(Sessions[i].peerip, RemoteIP, sizeof(RemoteIP)); -#ifdef HAVE_LICENSE - memcpy(Sessions[i].realpeerip, RemoteIP, sizeof(RemoteIP)); -#endif - incrementIPAddress(RemoteIP); - } - } - - /* Initialize our random cookie. Try /dev/urandom; if that fails, - use PID and rand() */ - fp = fopen("/dev/urandom", "r"); - if (fp) { - unsigned int x; - fread(&x, 1, sizeof(x), fp); - srand(x); - fread(&CookieSeed, 1, SEED_LEN, fp); - fclose(fp); - } else { - srand((unsigned int) getpid() * (unsigned int) time(NULL)); - CookieSeed[0] = getpid() & 0xFF; - CookieSeed[1] = (getpid() >> 8) & 0xFF; - for (i=2; i<SEED_LEN; i++) { - CookieSeed[i] = (rand() >> (i % 9)) & 0xFF; - } - } - - if (RandomizeSessionNumbers) { - int *permutation; - int tmp; - permutation = malloc(sizeof(int) * NumSessionSlots); - if (!permutation) { - fprintf(stderr, "Could not allocate memory to randomize session numbers\n"); - exit(EXIT_FAILURE); - } - for (i=0; i<NumSessionSlots; i++) { - permutation[i] = i; - } - for (i=0; i<NumSessionSlots-1; i++) { - j = i + rand() % (NumSessionSlots - i); - if (j != i) { - tmp = permutation[j]; - permutation[j] = permutation[i]; - permutation[i] = tmp; - } - } - /* Link sessions together */ - FreeSessions = &Sessions[permutation[0]]; - LastFreeSession = &Sessions[permutation[NumSessionSlots-1]]; - for (i=0; i<NumSessionSlots-1; i++) { - Sessions[permutation[i]].next = &Sessions[permutation[i+1]]; - } - Sessions[permutation[NumSessionSlots-1]].next = NULL; - free(permutation); - } else { - /* Link sessions together */ - FreeSessions = &Sessions[0]; - LastFreeSession = &Sessions[NumSessionSlots - 1]; - for (i=0; i<NumSessionSlots-1; i++) { - Sessions[i].next = &Sessions[i+1]; - } - Sessions[NumSessionSlots-1].next = NULL; - } - - if (Debug) { - /* Dump session array and exit */ - ClientSession *ses = FreeSessions; - while(ses) { - printf("Session %u local %d.%d.%d.%d remote %d.%d.%d.%d\n", - (unsigned int) (ntohs(ses->sess)), - ses->myip[0], ses->myip[1], - ses->myip[2], ses->myip[3], - ses->peerip[0], ses->peerip[1], - ses->peerip[2], ses->peerip[3]); - ses = ses->next; - } - exit(0); - } - - /* Open all the interfaces */ - for (i=0; i<NumInterfaces; i++) { - interfaces[i].sock = openInterface(interfaces[i].name, Eth_PPPOE_Discovery, interfaces[i].mac); - } - - /* Ignore SIGPIPE */ - signal(SIGPIPE, SIG_IGN); - - /* Create event selector */ - event_selector = Event_CreateSelector(); - if (!event_selector) { - rp_fatal("Could not create EventSelector -- probably out of memory"); - } - - /* Set signal handlers for SIGTERM and SIGINT */ - if (Event_HandleSignal(event_selector, SIGTERM, termHandler) < 0 || - Event_HandleSignal(event_selector, SIGINT, termHandler) < 0) { - fatalSys("Event_HandleSignal"); - } - - /* Control channel */ -#ifdef HAVE_LICENSE - if (control_init(argc, argv, event_selector)) { - rp_fatal("control_init failed"); - } -#endif - - /* Create event handler for each interface */ - for (i = 0; i<NumInterfaces; i++) { - interfaces[i].eh = Event_AddHandler(event_selector, - interfaces[i].sock, - EVENT_FLAG_READABLE, - InterfaceHandler, - &interfaces[i]); -#ifdef HAVE_L2TP - interfaces[i].session_sock = -1; -#endif - if (!interfaces[i].eh) { - rp_fatal("Event_AddHandler failed"); - } - } - -#ifdef HAVE_LICENSE - if (use_clustering) { - ClusterLicense = License_GetFeature("PPPOE-CLUSTER"); - if (!ClusterLicense) { - fprintf(stderr, "License: GetFeature failed: %s\n", - License_ErrorMessage()); - exit(1); - } - if (!License_Expired(ClusterLicense)) { - if (cluster_init(event_selector) < 0) { - rp_fatal("cluster_init failed"); - } - } - } -#endif - -#ifdef HAVE_L2TP - for (i=0; i<NumInterfaces; i++) { - pppoe_to_l2tp_add_interface(event_selector, - &interfaces[i]); - } -#endif - - /* Daemonize -- UNIX Network Programming, Vol. 1, Stevens */ - if (beDaemon) { - i = fork(); - if (i < 0) { - fatalSys("fork"); - } else if (i != 0) { - /* parent */ - exit(EXIT_SUCCESS); - } - setsid(); - signal(SIGHUP, SIG_IGN); - i = fork(); - if (i < 0) { - fatalSys("fork"); - } else if (i != 0) { - exit(EXIT_SUCCESS); - } - - chdir("/"); - - /* Point stdin/stdout/stderr to /dev/null */ - for (i=0; i<3; i++) { - close(i); - } - i = open("/dev/null", O_RDWR); - if (i >= 0) { - dup2(i, 0); - dup2(i, 1); - dup2(i, 2); - if (i > 2) close(i); - } - } - - for(;;) { - i = Event_HandleEvent(event_selector); - if (i < 0) { - fatalSys("Event_HandleEvent"); - } - -#ifdef HAVE_LICENSE - if (License_Expired(ServerLicense)) { - syslog(LOG_INFO, "Server license has expired -- killing all PPPoE sessions"); - killAllSessions(); - control_exit(); - exit(0); - } -#endif - } - return 0; -} - -void -serverProcessPacket(Interface *i) -{ - int len; - PPPoEPacket packet; - int sock = i->sock; - - if (receivePacket(sock, &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; - } - - /* Sanity check on packet */ - if (packet.ver != 1 || packet.type != 1) { - /* Syslog an error */ - return; - } - switch(packet.code) { - case CODE_PADI: - processPADI(i, &packet, len); - break; - case CODE_PADR: - processPADR(i, &packet, len); - break; - case CODE_PADT: - /* Kill the child */ - processPADT(i, &packet, len); - break; - case CODE_SESS: - /* Ignore SESS -- children will handle them */ - break; - case CODE_PADO: - case CODE_PADS: - /* Ignore PADO and PADS totally */ - break; - default: - /* Syslog an error */ - break; - } -} - -/********************************************************************** -*%FUNCTION: sendErrorPADS -*%ARGUMENTS: -* sock -- socket to write to -* source -- source Ethernet address -* dest -- destination Ethernet address -* errorTag -- error tag -* errorMsg -- error message -*%RETURNS: -* Nothing -*%DESCRIPTION: -* Sends a PADS packet with an error message -***********************************************************************/ -void -sendErrorPADS(int sock, - unsigned char *source, - unsigned char *dest, - int errorTag, - char *errorMsg) -{ - PPPoEPacket pads; - unsigned char *cursor = pads.payload; - UINT16_t plen; - PPPoETag err; - int elen = strlen(errorMsg); - - memcpy(pads.ethHdr.h_dest, dest, ETH_ALEN); - memcpy(pads.ethHdr.h_source, source, ETH_ALEN); - pads.ethHdr.h_proto = htons(Eth_PPPOE_Discovery); - pads.ver = 1; - pads.type = 1; - pads.code = CODE_PADS; - - pads.session = htons(0); - plen = 0; - - err.type = htons(errorTag); - err.length = htons(elen); - - memcpy(err.payload, errorMsg, elen); - memcpy(cursor, &err, TAG_HDR_SIZE+elen); - cursor += TAG_HDR_SIZE + elen; - plen += TAG_HDR_SIZE + elen; - - if (relayId.type) { - memcpy(cursor, &relayId, ntohs(relayId.length) + TAG_HDR_SIZE); - cursor += ntohs(relayId.length) + TAG_HDR_SIZE; - plen += ntohs(relayId.length) + TAG_HDR_SIZE; - } - if (hostUniq.type) { - memcpy(cursor, &hostUniq, ntohs(hostUniq.length) + TAG_HDR_SIZE); - cursor += ntohs(hostUniq.length) + TAG_HDR_SIZE; - plen += ntohs(hostUniq.length) + TAG_HDR_SIZE; - } - pads.length = htons(plen); - sendPacket(NULL, sock, &pads, (int) (plen + HDR_SIZE)); -} - - -/********************************************************************** -*%FUNCTION: startPPPDUserMode -*%ARGUMENTS: -* session -- client session record -*%RETURNS: -* Nothing -*%DESCRIPTION: -* Starts PPPD for user-mode PPPoE -***********************************************************************/ -void -startPPPDUserMode(ClientSession *session) -{ - /* Leave some room */ - char *argv[32]; - - char buffer[SMALLBUF]; - - int c = 0; - - argv[c++] = "pppd"; - argv[c++] = "pty"; - - /* Let's hope service-name does not have ' in it... */ - snprintf(buffer, SMALLBUF, "%s -n -I %s -e %u:%02x:%02x:%02x:%02x:%02x:%02x%s -S '%s'", - PPPOE_PATH, session->ethif->name, - (unsigned int) ntohs(session->sess), - session->eth[0], session->eth[1], session->eth[2], - session->eth[3], session->eth[4], session->eth[5], - PppoeOptions, session->serviceName); - argv[c++] = strdup(buffer); - if (!argv[c-1]) { - /* TODO: Send a PADT */ - exit(EXIT_FAILURE); - } - - argv[c++] = "file"; - argv[c++] = pppoptfile; - - snprintf(buffer, SMALLBUF, "%d.%d.%d.%d:%d.%d.%d.%d", - (int) session->myip[0], (int) session->myip[1], - (int) session->myip[2], (int) session->myip[3], - (int) session->peerip[0], (int) session->peerip[1], - (int) session->peerip[2], (int) session->peerip[3]); - syslog(LOG_INFO, - "Session %u created for client %02x:%02x:%02x:%02x:%02x:%02x (%d.%d.%d.%d) on %s using Service-Name '%s'", - (unsigned int) ntohs(session->sess), - session->eth[0], session->eth[1], session->eth[2], - session->eth[3], session->eth[4], session->eth[5], - (int) session->peerip[0], (int) session->peerip[1], - (int) session->peerip[2], (int) session->peerip[3], - session->ethif->name, - session->serviceName); - argv[c++] = strdup(buffer); - if (!argv[c-1]) { - /* TODO: Send a PADT */ - exit(EXIT_FAILURE); - } - argv[c++] = "nodetach"; - argv[c++] = "noaccomp"; - argv[c++] = "nobsdcomp"; - argv[c++] = "nodeflate"; - argv[c++] = "nopcomp"; - argv[c++] = "novj"; - argv[c++] = "novjccomp"; - argv[c++] = "default-asyncmap"; - if (Synchronous) { - argv[c++] = "sync"; - } - if (PassUnitOptionToPPPD) { - argv[c++] = "unit"; - sprintf(buffer, "%u", (unsigned int) (ntohs(session->sess) - 1 - SessOffset)); - argv[c++] = buffer; - } - argv[c++] = NULL; - - execv(PPPD_PATH, argv); - exit(EXIT_FAILURE); -} - -/********************************************************************** -*%FUNCTION: startPPPDLinuxKernelMode -*%ARGUMENTS: -* session -- client session record -*%RETURNS: -* Nothing -*%DESCRIPTION: -* Starts PPPD for kernel-mode PPPoE on Linux -***********************************************************************/ -void -startPPPDLinuxKernelMode(ClientSession *session) -{ - /* Leave some room */ - char *argv[32]; - - int c = 0; - - char buffer[SMALLBUF]; - - argv[c++] = "pppd"; - argv[c++] = "plugin"; - argv[c++] = PLUGIN_PATH; - - /* Add "nic-" to interface name */ - snprintf(buffer, SMALLBUF, "nic-%s", session->ethif->name); - argv[c++] = strdup(buffer); - if (!argv[c-1]) { - exit(EXIT_FAILURE); - } - - snprintf(buffer, SMALLBUF, "%u:%02x:%02x:%02x:%02x:%02x:%02x", - (unsigned int) ntohs(session->sess), - session->eth[0], session->eth[1], session->eth[2], - session->eth[3], session->eth[4], session->eth[5]); - argv[c++] = "rp_pppoe_sess"; - argv[c++] = strdup(buffer); - if (!argv[c-1]) { - /* TODO: Send a PADT */ - exit(EXIT_FAILURE); - } - argv[c++] = "rp_pppoe_service"; - argv[c++] = (char *) session->serviceName; - argv[c++] = "file"; - argv[c++] = pppoptfile; - - snprintf(buffer, SMALLBUF, "%d.%d.%d.%d:%d.%d.%d.%d", - (int) session->myip[0], (int) session->myip[1], - (int) session->myip[2], (int) session->myip[3], - (int) session->peerip[0], (int) session->peerip[1], - (int) session->peerip[2], (int) session->peerip[3]); - syslog(LOG_INFO, - "Session %u created for client %02x:%02x:%02x:%02x:%02x:%02x (%d.%d.%d.%d) on %s using Service-Name '%s'", - (unsigned int) ntohs(session->sess), - session->eth[0], session->eth[1], session->eth[2], - session->eth[3], session->eth[4], session->eth[5], - (int) session->peerip[0], (int) session->peerip[1], - (int) session->peerip[2], (int) session->peerip[3], - session->ethif->name, - session->serviceName); - argv[c++] = strdup(buffer); - if (!argv[c-1]) { - /* TODO: Send a PADT */ - exit(EXIT_FAILURE); - } - argv[c++] = "nodetach"; - argv[c++] = "noaccomp"; - argv[c++] = "nobsdcomp"; - argv[c++] = "nodeflate"; - argv[c++] = "nopcomp"; - argv[c++] = "novj"; - argv[c++] = "novjccomp"; - argv[c++] = "default-asyncmap"; - if (PassUnitOptionToPPPD) { - argv[c++] = "unit"; - sprintf(buffer, "%u", (unsigned int) (ntohs(session->sess) - 1 - SessOffset)); - argv[c++] = buffer; - } - argv[c++] = NULL; - execv(PPPD_PATH, argv); - exit(EXIT_FAILURE); -} - -/********************************************************************** -*%FUNCTION: startPPPD -*%ARGUMENTS: -* session -- client session record -*%RETURNS: -* Nothing -*%DESCRIPTION: -* Starts PPPD -***********************************************************************/ -void -startPPPD(ClientSession *session) -{ - if (UseLinuxKernelModePPPoE) startPPPDLinuxKernelMode(session); - else startPPPDUserMode(session); -} - -/********************************************************************** -* %FUNCTION: InterfaceHandler -* %ARGUMENTS: -* es -- event selector (ignored) -* fd -- file descriptor which is readable -* flags -- ignored -* data -- Pointer to the Interface structure -* %RETURNS: -* Nothing -* %DESCRIPTION: -* Handles a packet ready at an interface -***********************************************************************/ -void -InterfaceHandler(EventSelector *es, - int fd, - unsigned int flags, - void *data) -{ - serverProcessPacket((Interface *) data); -} - -/********************************************************************** -* %FUNCTION: PppoeStopSession -* %ARGUMENTS: -* ses -- the session -* reason -- reason session is being stopped. -* %RETURNS: -* Nothing -* %DESCRIPTION: -* Kills pppd. -***********************************************************************/ -static void -PppoeStopSession(ClientSession *ses, - char const *reason) -{ - /* Temporary structure for sending PADT's. */ - PPPoEConnection conn; - - memset(&conn, 0, sizeof(conn)); - conn.useHostUniq = 0; - - memcpy(conn.myEth, ses->ethif->mac, ETH_ALEN); - conn.discoverySocket = ses->ethif->sock; - conn.session = ses->sess; - memcpy(conn.peerEth, ses->eth, ETH_ALEN); - sendPADT(&conn, reason); - ses->flags |= FLAG_SENT_PADT; - - if (ses->pid) { - kill(ses->pid, SIGTERM); - } - ses->funcs = &DefaultSessionFunctionTable; -} - -/********************************************************************** -* %FUNCTION: PppoeSessionIsActive -* %ARGUMENTS: -* ses -- the session -* %RETURNS: -* True if session is active, false if not. -***********************************************************************/ -static int -PppoeSessionIsActive(ClientSession *ses) -{ - return (ses->pid != 0); -} - -#ifdef HAVE_LICENSE -/********************************************************************** -* %FUNCTION: getFreeMem -* %ARGUMENTS: -* None -* %RETURNS: -* The amount of free RAM in kilobytes, or -1 if it could not be -* determined -* %DESCRIPTION: -* Reads Linux-specific /proc/meminfo file and extracts free RAM -***********************************************************************/ -int -getFreeMem(void) -{ - char buf[512]; - int memfree=0, buffers=0, cached=0; - FILE *fp = fopen("/proc/meminfo", "r"); - if (!fp) return -1; - - while (fgets(buf, sizeof(buf), fp)) { - if (!strncmp(buf, "MemFree:", 8)) { - if (sscanf(buf, "MemFree: %d", &memfree) != 1) { - fclose(fp); - return -1; - } - } else if (!strncmp(buf, "Buffers:", 8)) { - if (sscanf(buf, "Buffers: %d", &buffers) != 1) { - fclose(fp); - return -1; - } - } else if (!strncmp(buf, "Cached:", 7)) { - if (sscanf(buf, "Cached: %d", &cached) != 1) { - fclose(fp); - return -1; - } - } - } - fclose(fp); - /* return memfree + buffers + cached; */ - return memfree; -} -#endif - -/********************************************************************** -* %FUNCTION: pppoe_alloc_session -* %ARGUMENTS: -* None -* %RETURNS: -* NULL if no session is available, otherwise a ClientSession structure. -* %DESCRIPTION: -* Allocates a ClientSession structure and removes from free list, puts -* on busy list -***********************************************************************/ -ClientSession * -pppoe_alloc_session(void) -{ - ClientSession *ses = FreeSessions; - if (!ses) return NULL; - - /* Remove from free sessions list */ - if (ses == LastFreeSession) { - LastFreeSession = NULL; - } - FreeSessions = ses->next; - - /* Put on busy sessions list */ - ses->next = BusySessions; - BusySessions = ses; - - /* Initialize fields to sane values */ - ses->funcs = &DefaultSessionFunctionTable; - ses->pid = 0; - ses->ethif = NULL; - memset(ses->eth, 0, ETH_ALEN); - ses->flags = 0; - ses->startTime = time(NULL); - ses->serviceName = ""; -#ifdef HAVE_LICENSE - memset(ses->user, 0, MAX_USERNAME_LEN+1); - memset(ses->realm, 0, MAX_USERNAME_LEN+1); - memset(ses->realpeerip, 0, IPV4ALEN); -#endif -#ifdef HAVE_L2TP - ses->l2tp_ses = NULL; -#endif - NumActiveSessions++; - return ses; -} - -/********************************************************************** -* %FUNCTION: pppoe_free_session -* %ARGUMENTS: -* ses -- session to free -* %RETURNS: -* 0 if OK, -1 if error -* %DESCRIPTION: -* Places a ClientSession on the free list. -***********************************************************************/ -int -pppoe_free_session(ClientSession *ses) -{ - ClientSession *cur, *prev; - - cur = BusySessions; - prev = NULL; - while (cur) { - if (ses == cur) break; - prev = cur; - cur = cur->next; - } - - if (!cur) { - syslog(LOG_ERR, "pppoe_free_session: Could not find session %p on busy list", (void *) ses); - return -1; - } - - /* Remove from busy sessions list */ - if (prev) { - prev->next = ses->next; - } else { - BusySessions = ses->next; - } - - /* Add to end of free sessions */ - ses->next = NULL; - if (LastFreeSession) { - LastFreeSession->next = ses; - LastFreeSession = ses; - } else { - FreeSessions = ses; - LastFreeSession = ses; - } - - /* Initialize fields to sane values */ - ses->funcs = &DefaultSessionFunctionTable; - ses->pid = 0; - ses->flags = 0; -#ifdef HAVE_L2TP - ses->l2tp_ses = NULL; -#endif - NumActiveSessions--; - return 0; -} - -/********************************************************************** -* %FUNCTION: sendHURLorMOTM -* %ARGUMENTS: -* conn -- PPPoE connection -* url -- a URL, which *MUST* begin with "http://" or it won't be sent, or -* a message. -* tag -- one of TAG_HURL or TAG_MOTM -* %RETURNS: -* Nothing -* %DESCRIPTION: -* Sends a PADM packet contaning a HURL or MOTM tag to the victim...er, peer. -***********************************************************************/ -void -sendHURLorMOTM(PPPoEConnection *conn, char const *url, UINT16_t tag) -{ - PPPoEPacket packet; - PPPoETag hurl; - size_t elen; - unsigned char *cursor = packet.payload; - UINT16_t plen = 0; - - if (!conn->session) return; - if (conn->discoverySocket < 0) return; - - if (tag == TAG_HURL) { - if (strncmp(url, "http://", 7)) { - syslog(LOG_WARNING, "sendHURL(%s): URL must begin with http://", url); - return; - } - } else { - tag = TAG_MOTM; - } - - 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_Discovery); - packet.ver = 1; - packet.type = 1; - packet.code = CODE_PADM; - packet.session = conn->session; - - elen = strlen(url); - if (elen > 256) { - syslog(LOG_WARNING, "MOTM or HURL too long: %d", (int) elen); - return; - } - - hurl.type = htons(tag); - hurl.length = htons(elen); - strcpy((char *) hurl.payload, url); - memcpy(cursor, &hurl, elen + TAG_HDR_SIZE); - cursor += elen + TAG_HDR_SIZE; - plen += elen + TAG_HDR_SIZE; - - packet.length = htons(plen); - - sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE)); -#ifdef DEBUGGING_ENABLED - if (conn->debugFile) { - dumpPacket(conn->debugFile, &packet, "SENT"); - fprintf(conn->debugFile, "\n"); - fflush(conn->debugFile); - } -#endif -} |