📄 pppoe-server.c
字号:
*%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 = "hI:C:L:R:T:m:FN:f:o:sp:lrudPc:S:1";
#else
char *options = "hI:C:L:R:T:m:FN:f:o:skp:lrudPc:S:1";
#endif
memset(interfaces, 0, sizeof(interfaces));
/* Initialize syslog */
openlog("pppoe-server", LOG_PID, LOG_DAEMON);
/* Default number of session slots */
NumSessionSlots = DEFAULT_MAX_SESSIONS;
NumActiveSessions = 0;
/* Parse command-line options */
while((opt = getopt(argc, argv, options)) != -1) {
switch(opt) {
#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':
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':
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;
}
}
#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("%d\n", 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 %d local %d.%d.%d.%d remote %d.%d.%d.%d\n",
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:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -