📄 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.***********************************************************************/voidgenCookie(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***********************************************************************/voidprocessPADI(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.***********************************************************************/voidprocessPADT(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.***********************************************************************/voidprocessPADR(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 voidtermHandler(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***********************************************************************/voidusage(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-2005 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:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -