diff options
Diffstat (limited to 'mdk-stage1/rp-pppoe/src/common.c')
-rw-r--r-- | mdk-stage1/rp-pppoe/src/common.c | 485 |
1 files changed, 485 insertions, 0 deletions
diff --git a/mdk-stage1/rp-pppoe/src/common.c b/mdk-stage1/rp-pppoe/src/common.c new file mode 100644 index 000000000..b27302104 --- /dev/null +++ b/mdk-stage1/rp-pppoe/src/common.c @@ -0,0 +1,485 @@ +/*********************************************************************** +* +* common.c +* +* Implementation of user-space PPPoE redirector for Linux. +* +* Common functions used by PPPoE client and server +* +* Copyright (C) 2000 by 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. +* +***********************************************************************/ + +static char const RCSID[] = +"$Id$"; + +#include "pppoe.h" + +#ifdef HAVE_SYSLOG_H +#include <syslog.h> +#endif + +#include <string.h> +#include <errno.h> +#include <stdlib.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +/********************************************************************** +*%FUNCTION: parsePacket +*%ARGUMENTS: +* packet -- the PPPoE discovery packet to parse +* func -- function called for each tag in the packet +* extra -- an opaque data pointer supplied to parsing function +*%RETURNS: +* 0 if everything went well; -1 if there was an error +*%DESCRIPTION: +* Parses a PPPoE discovery packet, calling "func" for each tag in the packet. +* "func" is passed the additional argument "extra". +***********************************************************************/ +int +parsePacket(PPPoEPacket *packet, ParseFunc *func, void *extra) +{ + UINT16_t len = ntohs(packet->length); + unsigned char *curTag; + UINT16_t tagType, tagLen; + + if (packet->ver != 1) { + syslog(LOG_ERR, "Invalid PPPoE version (%d)", (int) packet->ver); + return -1; + } + if (packet->type != 1) { + syslog(LOG_ERR, "Invalid PPPoE type (%d)", (int) packet->type); + return -1; + } + + /* Do some sanity checks on packet */ + if (len > ETH_DATA_LEN - 6) { /* 6-byte overhead for PPPoE header */ + syslog(LOG_ERR, "Invalid PPPoE packet length (%u)", len); + return -1; + } + + /* Step through the tags */ + curTag = packet->payload; + while(curTag - packet->payload < len) { + /* Alignment is not guaranteed, so do this by hand... */ + tagType = (((UINT16_t) curTag[0]) << 8) + + (UINT16_t) curTag[1]; + tagLen = (((UINT16_t) curTag[2]) << 8) + + (UINT16_t) curTag[3]; + if (tagType == TAG_END_OF_LIST) { + return 0; + } + if ((curTag - packet->payload) + tagLen + TAG_HDR_SIZE > len) { + syslog(LOG_ERR, "Invalid PPPoE tag length (%u)", tagLen); + return -1; + } + func(tagType, tagLen, curTag+TAG_HDR_SIZE, extra); + curTag = curTag + TAG_HDR_SIZE + tagLen; + } + return 0; +} + +/********************************************************************** +*%FUNCTION: findTag +*%ARGUMENTS: +* packet -- the PPPoE discovery packet to parse +* type -- the type of the tag to look for +* tag -- will be filled in with tag contents +*%RETURNS: +* A pointer to the tag if one of the specified type is found; NULL +* otherwise. +*%DESCRIPTION: +* Looks for a specific tag type. +***********************************************************************/ +unsigned char * +findTag(PPPoEPacket *packet, UINT16_t type, PPPoETag *tag) +{ + UINT16_t len = ntohs(packet->length); + unsigned char *curTag; + UINT16_t tagType, tagLen; + + if (packet->ver != 1) { + syslog(LOG_ERR, "Invalid PPPoE version (%d)", (int) packet->ver); + return NULL; + } + if (packet->type != 1) { + syslog(LOG_ERR, "Invalid PPPoE type (%d)", (int) packet->type); + return NULL; + } + + /* Do some sanity checks on packet */ + if (len > ETH_DATA_LEN - 6) { /* 6-byte overhead for PPPoE header */ + syslog(LOG_ERR, "Invalid PPPoE packet length (%u)", len); + return NULL; + } + + /* Step through the tags */ + curTag = packet->payload; + while(curTag - packet->payload < len) { + /* Alignment is not guaranteed, so do this by hand... */ + tagType = (((UINT16_t) curTag[0]) << 8) + + (UINT16_t) curTag[1]; + tagLen = (((UINT16_t) curTag[2]) << 8) + + (UINT16_t) curTag[3]; + if (tagType == TAG_END_OF_LIST) { + return NULL; + } + if ((curTag - packet->payload) + tagLen + TAG_HDR_SIZE > len) { + syslog(LOG_ERR, "Invalid PPPoE tag length (%u)", tagLen); + return NULL; + } + if (tagType == type) { + memcpy(tag, curTag, tagLen + TAG_HDR_SIZE); + return curTag; + } + curTag = curTag + TAG_HDR_SIZE + tagLen; + } + return NULL; +} + +/********************************************************************** +*%FUNCTION: printErr +*%ARGUMENTS: +* str -- error message +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Prints a message to stderr and syslog. +***********************************************************************/ +void +printErr(char const *str) +{ + fprintf(stderr, "pppoe: %s\n", str); + syslog(LOG_ERR, "%s", str); +} + + +/********************************************************************** +*%FUNCTION: strDup +*%ARGUMENTS: +* str -- string to copy +*%RETURNS: +* A malloc'd copy of str. Exits if malloc fails. +***********************************************************************/ +char * +strDup(char const *str) +{ + char *copy = malloc(strlen(str)+1); + if (!copy) { + rp_fatal("strdup failed"); + } + strcpy(copy, str); + return copy; +} + +/********************************************************************** +*%FUNCTION: computeTCPChecksum +*%ARGUMENTS: +* ipHdr -- pointer to IP header +* tcpHdr -- pointer to TCP header +*%RETURNS: +* The computed TCP checksum +***********************************************************************/ +UINT16_t +computeTCPChecksum(unsigned char *ipHdr, unsigned char *tcpHdr) +{ + UINT32_t sum = 0; + UINT16_t count = ipHdr[2] * 256 + ipHdr[3]; + unsigned char *addr = tcpHdr; + unsigned char pseudoHeader[12]; + + /* Count number of bytes in TCP header and data */ + count -= (ipHdr[0] & 0x0F) * 4; + + memcpy(pseudoHeader, ipHdr+12, 8); + pseudoHeader[8] = 0; + pseudoHeader[9] = ipHdr[9]; + pseudoHeader[10] = (count >> 8) & 0xFF; + pseudoHeader[11] = (count & 0xFF); + + /* Checksum the pseudo-header */ + sum += * (UINT16_t *) pseudoHeader; + sum += * ((UINT16_t *) (pseudoHeader+2)); + sum += * ((UINT16_t *) (pseudoHeader+4)); + sum += * ((UINT16_t *) (pseudoHeader+6)); + sum += * ((UINT16_t *) (pseudoHeader+8)); + sum += * ((UINT16_t *) (pseudoHeader+10)); + + /* Checksum the TCP header and data */ + while (count > 1) { + sum += * (UINT16_t *) addr; + addr += 2; + count -= 2; + } + if (count > 0) { + sum += *addr; + } + + while(sum >> 16) { + sum = (sum & 0xffff) + (sum >> 16); + } + return (UINT16_t) (~sum & 0xFFFF); +} + +/********************************************************************** +*%FUNCTION: clampMSS +*%ARGUMENTS: +* packet -- PPPoE session packet +* dir -- either "incoming" or "outgoing" +* clampMss -- clamp value +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Clamps MSS option if TCP SYN flag is set. +***********************************************************************/ +void +clampMSS(PPPoEPacket *packet, char const *dir, int clampMss) +{ + unsigned char *tcpHdr; + unsigned char *ipHdr; + unsigned char *opt; + unsigned char *endHdr; + unsigned char *mssopt = NULL; + UINT16_t csum; + + int len; + + /* Is it IPv4? */ + if (packet->payload[0] != 0x00 || + packet->payload[1] != 0x21) { + /* Nope, ignore it */ + return; + } + + ipHdr = packet->payload + 2; + + /* Is it too short? */ + len = (int) ntohs(packet->length); + if (len < 42) { + /* 20 byte IP header; 20 byte TCP header; 2 byte PPP protocol */ + return; + } + + /* Verify once more that it's IPv4 */ + if ((ipHdr[0] & 0xF0) != 0x40) { + return; + } + + /* Is it a fragment that's not at the beginning of the packet? */ + if ((ipHdr[6] & 0x1F) || ipHdr[7]) { + /* Yup, don't touch! */ + return; + } + /* Is it TCP? */ + if (ipHdr[9] != 0x06) { + return; + } + + /* Get start of TCP header */ + tcpHdr = ipHdr + (ipHdr[0] & 0x0F) * 4; + + /* Is SYN set? */ + if (!(tcpHdr[13] & 0x02)) { + return; + } + + /* Compute and verify TCP checksum -- do not touch a packet with a bad + checksum */ + csum = computeTCPChecksum(ipHdr, tcpHdr); + if (csum) { + syslog(LOG_ERR, "Bad TCP checksum %x", (unsigned int) csum); + + /* Upper layers will drop it */ + return; + } + + /* Look for existing MSS option */ + endHdr = tcpHdr + ((tcpHdr[12] & 0xF0) >> 2); + opt = tcpHdr + 20; + while (opt < endHdr) { + if (!*opt) break; /* End of options */ + switch(*opt) { + case 1: + opt++; + break; + + case 2: + if (opt[1] != 4) { + /* Something fishy about MSS option length. */ + syslog(LOG_ERR, + "Bogus length for MSS option (%u) from %u.%u.%u.%u", + (unsigned int) opt[1], + (unsigned int) ipHdr[12], + (unsigned int) ipHdr[13], + (unsigned int) ipHdr[14], + (unsigned int) ipHdr[15]); + return; + } + mssopt = opt; + break; + default: + if (opt[1] < 2) { + /* Someone's trying to attack us? */ + syslog(LOG_ERR, + "Bogus TCP option length (%u) from %u.%u.%u.%u", + (unsigned int) opt[1], + (unsigned int) ipHdr[12], + (unsigned int) ipHdr[13], + (unsigned int) ipHdr[14], + (unsigned int) ipHdr[15]); + return; + } + opt += (opt[1]); + break; + } + /* Found existing MSS option? */ + if (mssopt) break; + } + + /* If MSS exists and it's low enough, do nothing */ + if (mssopt) { + unsigned mss = mssopt[2] * 256 + mssopt[3]; + if (mss <= clampMss) { + return; + } + + mssopt[2] = (((unsigned) clampMss) >> 8) & 0xFF; + mssopt[3] = ((unsigned) clampMss) & 0xFF; + } else { + /* No MSS option. Don't add one; we'll have to use 536. */ + return; + } + + /* Recompute TCP checksum */ + tcpHdr[16] = 0; + tcpHdr[17] = 0; + csum = computeTCPChecksum(ipHdr, tcpHdr); + (* (UINT16_t *) (tcpHdr+16)) = csum; +} + +/*********************************************************************** +*%FUNCTION: sendPADT +*%ARGUMENTS: +* conn -- PPPoE connection +* msg -- if non-NULL, extra error message to include in PADT packet. +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Sends a PADT packet +***********************************************************************/ +void +sendPADT(PPPoEConnection *conn, char const *msg) +{ + PPPoEPacket packet; + unsigned char *cursor = packet.payload; + + UINT16_t plen = 0; + + /* Do nothing if no session established yet */ + if (!conn->session) return; + + /* Do nothing if no discovery socket */ + if (conn->discoverySocket < 0) return; + + 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_PADT; + packet.session = conn->session; + + /* Reset Session to zero so there is no possibility of + recursive calls to this function by any signal handler */ + conn->session = 0; + + /* If we're using Host-Uniq, copy it over */ + if (conn->useHostUniq) { + PPPoETag hostUniq; + pid_t pid = getpid(); + hostUniq.type = htons(TAG_HOST_UNIQ); + hostUniq.length = htons(sizeof(pid)); + memcpy(hostUniq.payload, &pid, sizeof(pid)); + memcpy(cursor, &hostUniq, sizeof(pid) + TAG_HDR_SIZE); + cursor += sizeof(pid) + TAG_HDR_SIZE; + plen += sizeof(pid) + TAG_HDR_SIZE; + } + + /* Copy error message */ + if (msg) { + PPPoETag err; + size_t elen = strlen(msg); + err.type = htons(TAG_GENERIC_ERROR); + err.length = htons(elen); + strcpy(err.payload, msg); + memcpy(cursor, &err, elen + TAG_HDR_SIZE); + cursor += elen + TAG_HDR_SIZE; + plen += elen + TAG_HDR_SIZE; + } + + /* Copy cookie and relay-ID if needed */ + if (conn->cookie.type) { + CHECK_ROOM(cursor, packet.payload, + ntohs(conn->cookie.length) + TAG_HDR_SIZE); + memcpy(cursor, &conn->cookie, ntohs(conn->cookie.length) + TAG_HDR_SIZE); + cursor += ntohs(conn->cookie.length) + TAG_HDR_SIZE; + plen += ntohs(conn->cookie.length) + TAG_HDR_SIZE; + } + + if (conn->relayId.type) { + CHECK_ROOM(cursor, packet.payload, + ntohs(conn->relayId.length) + TAG_HDR_SIZE); + memcpy(cursor, &conn->relayId, ntohs(conn->relayId.length) + TAG_HDR_SIZE); + cursor += ntohs(conn->relayId.length) + TAG_HDR_SIZE; + plen += ntohs(conn->relayId.length) + TAG_HDR_SIZE; + } + + packet.length = htons(plen); + sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE)); + if (conn->debugFile) { + dumpPacket(conn->debugFile, &packet, "SENT"); + fprintf(conn->debugFile, "\n"); + fflush(conn->debugFile); + } + syslog(LOG_INFO,"Sent PADT"); +} + +/********************************************************************** +*%FUNCTION: parseLogErrs +*%ARGUMENTS: +* type -- tag type +* len -- tag length +* data -- tag data +* extra -- extra user data +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Picks error tags out of a packet and logs them. +***********************************************************************/ +void +parseLogErrs(UINT16_t type, UINT16_t len, unsigned char *data, + void *extra) +{ + switch(type) { + case TAG_SERVICE_NAME_ERROR: + syslog(LOG_ERR, "PADT: Service-Name-Error: %.*s", (int) len, data); + fprintf(stderr, "PADT: Service-Name-Error: %.*s\n", (int) len, data); + break; + case TAG_AC_SYSTEM_ERROR: + syslog(LOG_ERR, "PADT: System-Error: %.*s", (int) len, data); + fprintf(stderr, "PADT: System-Error: %.*s\n", (int) len, data); + break; + case TAG_GENERIC_ERROR: + syslog(LOG_ERR, "PADT: Generic-Error: %.*s", (int) len, data); + fprintf(stderr, "PADT: Generic-Error: %.*s\n", (int) len, data); + break; + } +} + |