📄 pppoe-server.c
字号:
}
/**********************************************************************
*%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;
}
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;
}
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, " -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, " -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 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");
}
/**********************************************************************
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -