From 0a121a8ecd6de894c14d60daf9da2022ec47405c Mon Sep 17 00:00:00 2001 From: Guillaume Cottenceau Date: Mon, 11 Jun 2001 13:49:39 +0000 Subject: Initial revision --- mdk-stage1/rp-pppoe/src/discovery.c | 629 ++++++++++++++++++++++++++++++++++++ 1 file changed, 629 insertions(+) create mode 100644 mdk-stage1/rp-pppoe/src/discovery.c (limited to 'mdk-stage1/rp-pppoe/src/discovery.c') diff --git a/mdk-stage1/rp-pppoe/src/discovery.c b/mdk-stage1/rp-pppoe/src/discovery.c new file mode 100644 index 000000000..fc3568db5 --- /dev/null +++ b/mdk-stage1/rp-pppoe/src/discovery.c @@ -0,0 +1,629 @@ +/*********************************************************************** +* +* discovery.c +* +* Perform PPPoE discovery +* +* Copyright (C) 1999 by Roaring Penguin Software Inc. +* +***********************************************************************/ + +static char const RCSID[] = +"$Id$"; + +#include "pppoe.h" + +#ifdef HAVE_SYSLOG_H +#include +#endif + +#include +#include +#include + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#ifdef HAVE_SYS_UIO_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef USE_LINUX_PACKET +#include +#include +#endif + +#include + +/********************************************************************** +*%FUNCTION: parseForHostUniq +*%ARGUMENTS: +* type -- tag type +* len -- tag length +* data -- tag data. +* extra -- user-supplied pointer. This is assumed to be a pointer to int. +*%RETURNS: +* Nothing +*%DESCRIPTION: +* If a HostUnique tag is found which matches our PID, sets *extra to 1. +***********************************************************************/ +void +parseForHostUniq(UINT16_t type, UINT16_t len, unsigned char *data, + void *extra) +{ + int *val = (int *) extra; + if (type == TAG_HOST_UNIQ && len == sizeof(pid_t)) { + pid_t tmp; + memcpy(&tmp, data, len); + if (tmp == getpid()) { + *val = 1; + } + } +} + +/********************************************************************** +*%FUNCTION: packetIsForMe +*%ARGUMENTS: +* conn -- PPPoE connection info +* packet -- a received PPPoE packet +*%RETURNS: +* 1 if packet is for this PPPoE daemon; 0 otherwise. +*%DESCRIPTION: +* If we are using the Host-Unique tag, verifies that packet contains +* our unique identifier. +***********************************************************************/ +int +packetIsForMe(PPPoEConnection *conn, PPPoEPacket *packet) +{ + int forMe = 0; + + /* If we're not using the Host-Unique tag, then accept the packet */ + if (!conn->useHostUniq) return 1; + + parsePacket(packet, parseForHostUniq, &forMe); + return forMe; +} + +/********************************************************************** +*%FUNCTION: parsePADOTags +*%ARGUMENTS: +* type -- tag type +* len -- tag length +* data -- tag data +* extra -- extra user data. Should point to a PacketCriteria structure +* which gets filled in according to selected AC name and service +* name. +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Picks interesting tags out of a PADO packet +***********************************************************************/ +void +parsePADOTags(UINT16_t type, UINT16_t len, unsigned char *data, + void *extra) +{ + struct PacketCriteria *pc = (struct PacketCriteria *) extra; + PPPoEConnection *conn = pc->conn; + int i; + + switch(type) { + case TAG_AC_NAME: + if (conn->printACNames) { + printf("Access-Concentrator: %.*s\n", (int) len, data); + } + if (conn->acName && len == strlen(conn->acName) && + !strncmp((char *) data, conn->acName, len)) { + pc->acNameOK = 1; + } + break; + case TAG_SERVICE_NAME: + if (conn->printACNames && len > 0) { + printf(" Service-Name: %.*s\n", (int) len, data); + } + if (conn->serviceName && len == strlen(conn->serviceName) && + !strncmp((char *) data, conn->serviceName, len)) { + pc->serviceNameOK = 1; + } + break; + case TAG_AC_COOKIE: + if (conn->printACNames) { + printf("Got a cookie:"); + /* Print first 20 bytes of cookie */ + for (i=0; icookie.type = htons(type); + conn->cookie.length = htons(len); + memcpy(conn->cookie.payload, data, len); + break; + case TAG_RELAY_SESSION_ID: + if (conn->printACNames) { + printf("Got a Relay-ID:"); + /* Print first 20 bytes of relay ID */ + for (i=0; irelayId.type = htons(type); + conn->relayId.length = htons(len); + memcpy(conn->relayId.payload, data, len); + break; + case TAG_SERVICE_NAME_ERROR: + if (conn->printACNames) { + printf("Got a Service-Name-Error tag: %.*s\n", (int) len, data); + } else { + syslog(LOG_ERR, "PADO: Service-Name-Error: %.*s", (int) len, data); + exit(1); + } + break; + case TAG_AC_SYSTEM_ERROR: + if (conn->printACNames) { + printf("Got a System-Error tag: %.*s\n", (int) len, data); + } else { + syslog(LOG_ERR, "PADO: System-Error: %.*s", (int) len, data); + exit(1); + } + break; + case TAG_GENERIC_ERROR: + if (conn->printACNames) { + printf("Got a Generic-Error tag: %.*s\n", (int) len, data); + } else { + syslog(LOG_ERR, "PADO: Generic-Error: %.*s", (int) len, data); + exit(1); + } + break; + } +} + +/********************************************************************** +*%FUNCTION: parsePADSTags +*%ARGUMENTS: +* type -- tag type +* len -- tag length +* data -- tag data +* extra -- extra user data (pointer to PPPoEConnection structure) +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Picks interesting tags out of a PADS packet +***********************************************************************/ +void +parsePADSTags(UINT16_t type, UINT16_t len, unsigned char *data, + void *extra) +{ + PPPoEConnection *conn = (PPPoEConnection *) extra; + switch(type) { + case TAG_SERVICE_NAME: + syslog(LOG_DEBUG, "PADS: Service-Name: '%.*s'", (int) len, data); + break; + case TAG_SERVICE_NAME_ERROR: + syslog(LOG_ERR, "PADS: Service-Name-Error: %.*s", (int) len, data); + fprintf(stderr, "PADS: Service-Name-Error: %.*s\n", (int) len, data); + exit(1); + case TAG_AC_SYSTEM_ERROR: + syslog(LOG_ERR, "PADS: System-Error: %.*s", (int) len, data); + fprintf(stderr, "PADS: System-Error: %.*s\n", (int) len, data); + exit(1); + case TAG_GENERIC_ERROR: + syslog(LOG_ERR, "PADS: Generic-Error: %.*s", (int) len, data); + fprintf(stderr, "PADS: Generic-Error: %.*s\n", (int) len, data); + exit(1); + case TAG_RELAY_SESSION_ID: + conn->relayId.type = htons(type); + conn->relayId.length = htons(len); + memcpy(conn->relayId.payload, data, len); + break; + } +} + +/*********************************************************************** +*%FUNCTION: sendPADI +*%ARGUMENTS: +* conn -- PPPoEConnection structure +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Sends a PADI packet +***********************************************************************/ +void +sendPADI(PPPoEConnection *conn) +{ + PPPoEPacket packet; + unsigned char *cursor = packet.payload; + PPPoETag *svc = (PPPoETag *) (&packet.payload); + UINT16_t namelen = 0; + UINT16_t plen; + + if (conn->serviceName) { + namelen = (UINT16_t) strlen(conn->serviceName); + } + plen = TAG_HDR_SIZE + namelen; + CHECK_ROOM(cursor, packet.payload, plen); + + /* Set destination to Ethernet broadcast address */ + memset(packet.ethHdr.h_dest, 0xFF, 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_PADI; + packet.session = 0; + + svc->type = TAG_SERVICE_NAME; + svc->length = htons(namelen); + CHECK_ROOM(cursor, packet.payload, namelen+TAG_HDR_SIZE); + + if (conn->serviceName) { + memcpy(svc->payload, conn->serviceName, strlen(conn->serviceName)); + } + cursor += namelen + TAG_HDR_SIZE; + + /* 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)); + CHECK_ROOM(cursor, packet.payload, sizeof(pid) + TAG_HDR_SIZE); + memcpy(cursor, &hostUniq, sizeof(pid) + TAG_HDR_SIZE); + cursor += sizeof(pid) + TAG_HDR_SIZE; + plen += sizeof(pid) + 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); + } +} + +/********************************************************************** +*%FUNCTION: waitForPADO +*%ARGUMENTS: +* conn -- PPPoEConnection structure +* timeout -- how long to wait (in seconds) +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Waits for a PADO packet and copies useful information +***********************************************************************/ +void +waitForPADO(PPPoEConnection *conn, int timeout) +{ + fd_set readable; + int r; + struct timeval tv; + PPPoEPacket packet; + int len; + + struct PacketCriteria pc; + pc.conn = conn; + pc.acNameOK = (conn->acName) ? 0 : 1; + pc.serviceNameOK = (conn->serviceName) ? 0 : 1; + + do { + if (BPF_BUFFER_IS_EMPTY) { + tv.tv_sec = timeout; + tv.tv_usec = 0; + + FD_ZERO(&readable); + FD_SET(conn->discoverySocket, &readable); + + while(1) { + r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv); + if (r >= 0 || errno != EINTR) break; + } + if (r < 0) { + fatalSys("select (waitForPADO)"); + } + if (r == 0) return; /* Timed out */ + } + + /* Get the packet */ + receivePacket(conn->discoverySocket, &packet, &len); + + /* Check length */ + if (ntohs(packet.length) + HDR_SIZE > len) { + syslog(LOG_ERR, "Bogus PPPoE length field (%u)", + (unsigned int) ntohs(packet.length)); + continue; + } + +#ifdef USE_BPF + /* If it's not a Discovery packet, loop again */ + if (etherType(&packet) != Eth_PPPOE_Discovery) continue; +#endif + + if (conn->debugFile) { + dumpPacket(conn->debugFile, &packet, "RCVD"); + fprintf(conn->debugFile, "\n"); + fflush(conn->debugFile); + } + /* If it's not for us, loop again */ + if (!packetIsForMe(conn, &packet)) continue; + + if (packet.code == CODE_PADO) { + if (NOT_UNICAST(packet.ethHdr.h_source)) { + printErr("Ignoring PADO packet from non-unicast MAC address"); + continue; + } + conn->numPADOs++; + if (conn->printACNames) { + printf("--------------------------------------------------\n"); + } + parsePacket(&packet, parsePADOTags, &pc); + if (pc.acNameOK && pc.serviceNameOK) { + memcpy(conn->peerEth, packet.ethHdr.h_source, ETH_ALEN); + if (conn->printACNames) { + printf("AC-Ethernet-Address: %02x:%02x:%02x:%02x:%02x:%02x\n", + (unsigned) conn->peerEth[0], + (unsigned) conn->peerEth[1], + (unsigned) conn->peerEth[2], + (unsigned) conn->peerEth[3], + (unsigned) conn->peerEth[4], + (unsigned) conn->peerEth[5]); + continue; + } + conn->discoveryState = STATE_RECEIVED_PADO; + break; + } + } + } while (conn->discoveryState != STATE_RECEIVED_PADO); +} + +/*********************************************************************** +*%FUNCTION: sendPADR +*%ARGUMENTS: +* conn -- PPPoE connection structur +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Sends a PADR packet +***********************************************************************/ +void +sendPADR(PPPoEConnection *conn) +{ + PPPoEPacket packet; + PPPoETag *svc = (PPPoETag *) packet.payload; + unsigned char *cursor = packet.payload; + + UINT16_t namelen = 0; + UINT16_t plen; + + if (conn->serviceName) { + namelen = (UINT16_t) strlen(conn->serviceName); + } + plen = TAG_HDR_SIZE + namelen; + CHECK_ROOM(cursor, packet.payload, plen); + + 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_PADR; + packet.session = 0; + + svc->type = TAG_SERVICE_NAME; + svc->length = htons(namelen); + if (conn->serviceName) { + memcpy(svc->payload, conn->serviceName, namelen); + } + cursor += namelen + TAG_HDR_SIZE; + + /* 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)); + CHECK_ROOM(cursor, packet.payload, sizeof(pid)+TAG_HDR_SIZE); + memcpy(cursor, &hostUniq, sizeof(pid) + TAG_HDR_SIZE); + cursor += sizeof(pid) + TAG_HDR_SIZE; + plen += sizeof(pid) + 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); + } +} + +/********************************************************************** +*%FUNCTION: waitForPADS +*%ARGUMENTS: +* conn -- PPPoE connection info +* timeout -- how long to wait (in seconds) +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Waits for a PADS packet and copies useful information +***********************************************************************/ +void +waitForPADS(PPPoEConnection *conn, int timeout) +{ + fd_set readable; + int r; + struct timeval tv; + PPPoEPacket packet; + int len; + + do { + if (BPF_BUFFER_IS_EMPTY) { + tv.tv_sec = timeout; + tv.tv_usec = 0; + + FD_ZERO(&readable); + FD_SET(conn->discoverySocket, &readable); + + while(1) { + r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv); + if (r >= 0 || errno != EINTR) break; + } + if (r < 0) { + fatalSys("select (waitForPADS)"); + } + if (r == 0) return; + } + + /* Get the packet */ + receivePacket(conn->discoverySocket, &packet, &len); + + /* Check length */ + if (ntohs(packet.length) + HDR_SIZE > len) { + syslog(LOG_ERR, "Bogus PPPoE length field (%u)", + (unsigned int) ntohs(packet.length)); + continue; + } + +#ifdef USE_BPF + /* If it's not a Discovery packet, loop again */ + if (etherType(&packet) != Eth_PPPOE_Discovery) continue; +#endif + if (conn->debugFile) { + dumpPacket(conn->debugFile, &packet, "RCVD"); + fprintf(conn->debugFile, "\n"); + fflush(conn->debugFile); + } + + /* If it's not from the AC, it's not for me */ + if (memcmp(packet.ethHdr.h_source, conn->peerEth, ETH_ALEN)) continue; + + /* If it's not for us, loop again */ + if (!packetIsForMe(conn, &packet)) continue; + + /* Is it PADS? */ + if (packet.code == CODE_PADS) { + /* Parse for goodies */ + parsePacket(&packet, parsePADSTags, conn); + conn->discoveryState = STATE_SESSION; + break; + } + } while (conn->discoveryState != STATE_SESSION); + + /* Don't bother with ntohs; we'll just end up converting it back... */ + conn->session = packet.session; + + syslog(LOG_INFO, "PPP session is %d", (int) ntohs(conn->session)); + + /* RFC 2516 says session id MUST NOT be zero or 0xFFFF */ + if (ntohs(conn->session) == 0 || ntohs(conn->session) == 0xFFFF) { + syslog(LOG_ERR, "Access concentrator used a session value of %x -- the AC is violating RFC 2516", (unsigned int) ntohs(conn->session)); + } +} + +/********************************************************************** +*%FUNCTION: discovery +*%ARGUMENTS: +* conn -- PPPoE connection info structure +*%RETURNS: +* Nothing +*%DESCRIPTION: +* Performs the PPPoE discovery phase +***********************************************************************/ +void +discovery(PPPoEConnection *conn) +{ + int padiAttempts = 0; + int padrAttempts = 0; + int timeout = PADI_TIMEOUT; + + /* Skip discovery and don't open discovery socket? */ + if (conn->skipDiscovery && conn->noDiscoverySocket) { + conn->discoveryState = STATE_SESSION; + return; + } + + conn->discoverySocket = + openInterface(conn->ifName, Eth_PPPOE_Discovery, conn->myEth); + + /* Skip discovery? */ + if (conn->skipDiscovery) { + conn->discoveryState = STATE_SESSION; + if (conn->killSession) { + sendPADT(conn, "RP-PPPoE: Session killed manually"); + exit(0); + } + return; + } + + do { + padiAttempts++; + if (padiAttempts > MAX_PADI_ATTEMPTS) { + rp_fatal("Timeout waiting for PADO packets"); + } + sendPADI(conn); + conn->discoveryState = STATE_SENT_PADI; + waitForPADO(conn, timeout); + + /* If we're just probing for access concentrators, don't do + exponential backoff. This reduces the time for an unsuccessful + probe to 15 seconds. */ + if (!conn->printACNames) { + timeout *= 2; + } + if (conn->printACNames && conn->numPADOs) { + break; + } + } while (conn->discoveryState == STATE_SENT_PADI); + + /* If we're only printing access concentrator names, we're done */ + if (conn->printACNames) { + printf("--------------------------------------------------\n"); + exit(0); + } + + timeout = PADI_TIMEOUT; + do { + padrAttempts++; + if (padrAttempts > MAX_PADI_ATTEMPTS) { + rp_fatal("Timeout waiting for PADS packets"); + } + sendPADR(conn); + conn->discoveryState = STATE_SENT_PADR; + waitForPADS(conn, timeout); + timeout *= 2; + } while (conn->discoveryState == STATE_SENT_PADR); + + /* We're done. */ + conn->discoveryState = STATE_SESSION; + return; +} + -- cgit v1.2.1